Skip to main content
Associate III
June 9, 2025
Solved

ST25R3920B NDEF Handover Record (Bluetooth OOB Pairing)

  • June 9, 2025
  • 19 replies
  • 2571 views

Hello everyone,

I am currently using a custom NFC board based on the X-Nucleo-NFC08A1 that has the ST25R3920B MCU. I am able to perform OOB pairing (LE Secure Connections) using the example provided by ST in the software tools example. I can sucessfuly complete the bluetooth pair using a Samsung A12, however if I use other smartphones like Google Pixel 6, Xiaomi 14T or even Huawei the pairing does not complete.

I have tried this with other manufacturers libraries and software and it works for all the smartphones. After inspecting the memory of the tag I found out they use a "Handover Select" record with an "Alternative Carrier" inside. I dont't know if this is why it is not working with other smartphones.

Could someone explain this better, and if so, how do I set this additional records?

Best regards.

This topic has been closed for replies.
Best answer by Ulysses HERNIOSUS

You have the demo cycle code. Please adhere to rule 3 of https://debuggingrules.com/download-the-poster/

BR, Ulysses

19 replies

Brian TIDAL
Technical Moderator
June 13, 2025

Hi,

FYI, on our side "do you want to pair..." is properly displayed with Xiaomi 14T with our card emulation on NUCLEO_L476RG+X-NUCLEO-NFC08A1 and with a single LE OOB record (and with fix for the NDEF length).

I let you check on your side that:

  • the fix for the NDEF length (ndef_type_bluetooth.c) is properly applied (check that the NDEF length  is correct in ndefFile[0] and ndefFile[1])
  • "do you want to pair..." is displayed when using a single LE OOB record with a physical tag
  • only T4T CE is enabled in the CardEmulation mode of your application
  • tagInfo can properly read the CardEmulation (having tagInfo not able to read the cardEmulation is probably an indication that something is going wrong)

Rgds

BT

In order to give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
JPortilhaAuthor
Associate III
June 16, 2025

Thank you for testing.
Can you provide the example program so I can replicate on my side?

Thank you for your help,
JP

JPortilhaAuthor
Associate III
June 13, 2025

Hello, 

From the file you have sent me the only change on ndef_type_bluetooth.c is this:

if (type->id == NDEF_TYPE_ID_BLUETOOTH_BREDR)
{
length += ndefData->bufDeviceAddress.length;
}

How can I enable only T4T CE? Or how can I check that?

I really can't make it work.
Brian TIDAL
Technical Moderator
June 16, 2025

Hi,

"From the file you have sent me the only change on ndef_type_bluetooth.c is this" ==> yes and this avoids to wrongly increase the NDEF length in case of NDEF_TYPE_ID_BLUETOOTH_LE. 

 

"How can I enable only T4T CE?" ==> In InitCardEmulationMode, just comment the line 

m_discParam.techs2Find |= RFAL_NFC_LISTEN_TECH_F;

(actually line 50 of your code snipped in one of your previous answer)

 

"Can you provide the example program so I can replicate on my side?" ==> We have used our bare metal pairing demo running on NUCLEO-L476RG. The demoEncodeBluetoothRecord has been modified to create a LE OOB record instead of BR/EDR OOB record. Le Address and Le Role mandatory fields are encoded + the optional device local name field. See "// LE paining test" in the code snipped. This is enough to trig the "Do you want to pair?" message on the phone

ndefStatus demoEncodeBluetoothRecord(uint8_t *buffer, uint32_t length)
{
 ndefTypeBluetooth bluetooth;
 ndefRecord record;
 ndefType ndefBt;
 ndefStatus err;

 if( buffer == NULL )
 {
 return ERR_PARAM;
 }

 err = ndefBluetoothReset(&bluetooth);
 if( err != ERR_NONE )
 {
 return err;
 }

 /* Device address, in reverse order */
 uint8_t deviceAddress[] = { 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 };
 ndefConstBuffer bufDeviceAddress = { (uint8_t*)deviceAddress, sizeof(deviceAddress) };

 bluetooth.bufDeviceAddress = bufDeviceAddress;

 uint8_t localName[] = { 'S', 'T', '2', '5', 'R', ' ', 'd', 'e', 'v', 'i', 'c', 'e' };
 uint8_t eirLocalName[] = { sizeof(uint8_t) + sizeof(localName),
 NDEF_BT_EIR_COMPLETE_LOCAL_NAME,
 'S', 'T', '2', '5', 'R', ' ', 'd', 'e', 'v', 'i', 'c', 'e' };

 uint8_t eirLeAddress[9] = { 8, NDEF_BT_EIR_LE_DEVICE_ADDRESS, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0xAA }; // LE pairing test
 uint8_t eirLeRole[] = { 2, NDEF_BT_EIR_LE_ROLE, NDEF_BLE_ROLE_PERIPH_ONLY }; // LE pairing test
 // LE pairing test
 err = ndefBluetoothSetEir(&bluetooth, (uint8_t *) &eirLeAddress); // LE pairing test
 if (err != ERR_NONE) // LE pairing test
 { // LE pairing test
 return err; // LE pairing test
 } // LE pairing test
 err = ndefBluetoothSetEir(&bluetooth, (uint8_t *) &eirLeRole); // LE pairing test
 if (err != ERR_NONE) // LE pairing test
 { // LE pairing test
 return err; // LE pairing test
 } // LE pairing test
 err = ndefBluetoothSetEir(&bluetooth, (uint8_t*)&eirLocalName); // LE pairing test
 if( err != ERR_NONE ) // LE pairing test
 { // LE pairing test
 return err; // LE pairing test
 } // LE pairing test
 // LE pairing test
 err = ndefBluetoothLeInit(&ndefBt, &bluetooth); // LE pairing test
 if( err != ERR_NONE ) // LE pairing test
 { // LE pairing test
 return err; // LE pairing test
 } // LE pairing test
 // LE pairing test
 err = ndefTypeToRecord(&ndefBt, &record);
 if( err != ERR_NONE )
 {
 return err;
 }

 /* Encode the record starting after the first two bytes that are saved
 to store the NDEF file length */
 ndefBuffer bufRecord = { &buffer[2], length - 2 };
 err = ndefRecordEncode(&record, &bufRecord);
 if( err != ERR_NONE )
 {
 return err;
 }

 /* Store the NDEF file length on the two first bytes */
 buffer[0] = bufRecord.length >> 8;
 buffer[1] = bufRecord.length & 0xFF;

 return err;
}

The NDEF file has the following content:

# NDEF file contents:
[00] 00 3D D2 20 1A 61 70 70 6C 69 63 61 74 69 6F 6E |.=. .application|
[10] 2F 76 6E 64 2E 62 6C 75 65 74 6F 6F 74 68 2E 6C |/vnd.bluetooth.l|
[20] 65 2E 6F 6F 62 08 1B 50 51 52 53 54 55 AA 02 1C |e.oob..PQRSTU...|
[30] 00 0D 09 53 54 32 35 52 20 64 65 76 69 63 65 |...ST25R device |

 

Rgds

BT

In order to give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
JPortilhaAuthor
Associate III
June 16, 2025

Hello,

Did you really test that with a Xiaomi 14T? With mine nothing happens, I added all the changes you provided, I can see a log saying "Activated in CE NFC-A mode" but thats all. I can't even read with NFC tools using Xiaomi 14T.

Why does this happens?

 

BR

JP

Ulysses HERNIOSUS
Technical Moderator
June 17, 2025

Hi, 

do you have a nucleo (e.g. NUCLEO-L476) at hand? Then it might be easiest to share a working binary.

BR, Ulysses

JPortilhaAuthor
Associate III
June 17, 2025

Hello, unfortunately I dont́. However, I have noticed something in the HandleCardEmulation function that is extracted from your demo:

void NFCHandler::HandleCardEmulation(rfalNfcDevice *nfcDev)
{
 #if RFAL_SUPPORT_CE && RFAL_FEATURE_LISTEN_MODE
 
 ReturnCode err = ERR_INTERNAL;
 uint8_t *rxData;
 uint16_t *rcvLen;
 uint8_t txBuf[150];
 uint16_t txLen;

 do
 {
 switch( rfalNfcGetState() )
 {
 case RFAL_NFC_STATE_ACTIVATED:
 err = this->TransceiveBlocking(NULL, 0, &rxData, &rcvLen, 0); 
 
 case RFAL_NFC_STATE_DATAEXCHANGE:
 case RFAL_NFC_STATE_DATAEXCHANGE_DONE:
 txLen = ((nfcDev->type == RFAL_NFC_POLL_TYPE_NFCA) ? cardEmulationT4T(rxData, *rcvLen, txBuf, sizeof(txBuf)) : rfalConvBytesToBits(cardEmulationT3T(rxData, rfalConvBitsToBytes(*rcvLen), txBuf, sizeof(txBuf))));
 CCC_Log("TX Len: %d\n", txLen);
 err = this->TransceiveBlocking(txBuf, txLen, &rxData, &rcvLen, RFAL_FWT_NONE);
 break;
 
 case RFAL_NFC_STATE_START_DISCOVERY:
 return;
 
 case RFAL_NFC_STATE_LISTEN_SLEEP:
 default:
 break;
 }
 }
 while( (err == ERR_NONE) || (err == ERR_SLEEP_REQ) );

 #else
 NO_WARNING(nfcDev);
 #endif /* RFAL_SUPPORT_CE && RFAL_FEATURE_LISTEN_MODE */
}

For the Samsung A12 I get these logs:
Screenshot from 2025-06-16 17-56-08.png 

It goes through T4T Select and T4T Read and has different sizes including 118 (NDEF payload size); However for Xiaomi 14T I got this:
Screenshot from 2025-06-16 17-57-36.png

It seems like the communication does not advance and is always stuck at the same stage.

BR

Ulysses HERNIOSUS
Technical Moderator
June 17, 2025

Hi,

would be useful to see the received payloads and the sent payloads as well. However I would be suspecting a timing issue here. So it might be better to disable the logs and do a logic analyzer trace which allows to see timings, payloads and many more things.

Regards, Ulysses

JPortilhaAuthor
Associate III
June 17, 2025

I think I got the problem. The Samsung is sending the first message correctly, starts by 0x00 (CLA) and then is 0xA4 (Select), as you can see by these logs:

 

Screenshot from 2025-06-17 09-59-37.png

Howver, with Xiaomi the first byte sent by the smartphone is 0x80 that does not correspond to anything:

Screenshot from 2025-06-17 09-58-23.png

Ulysses HERNIOSUS
Technical Moderator
June 17, 2025

Hi,

a 0x80 byte does not really make sense to me. Not sure what it might meant or that it really is coming from RF or rather a porting artifact. Still recommend to do a logic analyzer trace to be able to investigate deeper.

Ulysses

JPortilhaAuthor
Associate III
June 17, 2025

I tested with the Huawei and the behaviour is the same, chinese brands seem to send this byte for some reason. Do you have any smartphone of this brand that can confirm this behaviour on your side?

BR

Ulysses HERNIOSUS
Technical Moderator
June 17, 2025

You are jumping to conclusions!

Brian has tested with the same Xiaomi you are testing and everything was ok.

You see the 0x80 byte but this does not mean it is sent by the phones. With even Pixel not working I am pretty sure that this is an artifact of your Zephyr port.

You need to either debug yourself into your code on what is happening. Where this 0x80 actually appears. I doubt it is actually coming from the phone. As said if you are providing a logic analyzer trace we may give your more educated hints. 

BR, Ulysses

 

JPortilhaAuthor
Associate III
June 17, 2025

Can you provide me your demoCycle code please?

Maybe that helps.

BR

Ulysses HERNIOSUS
Technical Moderator
June 17, 2025

You have the demo cycle code. Please adhere to rule 3 of https://debuggingrules.com/download-the-poster/

BR, Ulysses