Skip to main content
Explorer
June 9, 2025
Solved

ST25R3920B NDEF Handover Record (Bluetooth OOB Pairing)

  • June 9, 2025
  • 19 replies
  • 2570 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

    Technical Moderator
    June 10, 2025

    Hi,

    I guess this post is the continuation of https://community.st.com/t5/st25-nfc-rfid-tags-and-readers/st25r3920b-for-oob-bluetooth-pairing/td-p/762600.

    Please confirm that the pairing demo from the ST25 Embedded lib is the basis for your development and that ndefBluetoothLeInit (or ndefBluetoothSecureLeInit?) is being used instead of ndefBluetoothBrEdrInit and that the following mandatory AD are present in the pairing record:

     

    Value (1 byte) Description
    0x1B LE Bluetooth Device Address
    0x1C LE Role

    Using an Handover select ('Hs') record in addition to the pairing record will not improve the compatibility with other phones.

    The development team has recently found that the length of the LE oob record may be incorrect. A fix is available in ndef_type_bluetooth.c attached file.

    With this fix, the following LE oob record can be built:

    # Record #1: MIME type (RFC 2046) record:
    Type Name Format: MIME type (RFC 2046)
    Short Record
    type: "application/vnd.bluetooth.le.oob"
    
    Payload length: 26 bytes
    Payload data:
    
    [00] 08 1B 50 51 52 53 54 55 AA 02 1C 00 0D 09 53 54 |..PQRSTU......ST|
    [10] 32 35 52 20 64 65 76 69 63 65 |25R device |
    
    # NDEF message:
    [00] D2 20 1A 61 70 70 6C 69 63 61 74 69 6F 6E 2F 76 |. .application/v|
    [10] 6E 64 2E 62 6C 75 65 74 6F 6F 74 68 2E 6C 65 2E |nd.bluetooth.le.|
    [20] 6F 6F 62 08 1B 50 51 52 53 54 55 AA 02 1C 00 0D |oob..PQRSTU.....|
    [30] 09 53 54 32 35 52 20 64 65 76 69 63 65 |.ST25R device |
    
    

    and the phone properly proposes to pair the device once the record has been read.

    Let me know whether this fixes your issue.

    Rgds

    BT

    JPortilhaAuthor
    Explorer
    June 11, 2025

    Hello, 

    Yes this thread is the follow up of the post you have mentioned. I can show you the code that I am using to fill the BLE information:

    int BLEHandler::SetOOBPairData(uint8_t *ndefBuf, uint16_t ndefBufSize)
    {
     pcomm::ble::BLEHandler& BLE = pcomm::ble::BLEHandler::Instance();
     ndefTypeBluetooth bluetooth;
     ndefRecord leOobRecord;
     ndefType ndefBt;
     ndefStatus rc;
     static uint8_t tkBuf[BLE_TK_LEN];
    
     if (ndefBuf == NULL)
     return ERR_PARAM;
    
     /* Reset bluetooth record */
     rc = ndefBluetoothReset(&bluetooth);
     if (rc != ERR_NONE)
     return rc;
    
     /* Get bluetooth informations - Address, Address Type, Random and Confirm value */
     bt_le_oob_get_local(0, &oob_data);
    
     /* EIR Field 1: LE Device Address: Mandatory - 6 LSB bytes: Device address, 7th byte: Address type (Public/Random) */
     uint8_t eirLeAddress[9] = { 8, NDEF_BT_EIR_LE_DEVICE_ADDRESS };
     memcpy(&eirLeAddress[2], oob_data.addr.a.val, 6);
     eirLeAddress[8] = oob_data.addr.type;
    
     /* EIR Field 2: LE Role: Mandatory */
     uint8_t eirLeRole[3] = { 2, NDEF_BT_EIR_LE_ROLE, NDEF_BLE_ROLE_PERIPH_ONLY };
    
     /* Generate random TK */
     rc = BLE.GenerateRandomTK(tkBuf, BLE_TK_LEN);
     if (rc != 0)
     return rc;
     /* EIR Field 3: Temporary Key (TK) for secure pairing */
     uint8_t eirTk[18] = { 17, NDEF_BT_EIR_SECURITY_MANAGER_TK_VALUE };
     memcpy(&eirTk[2], tkBuf, BLE_TK_LEN);
    
     /* EIR Field 4: Confirmation Value - Needed for LE Secure Connections */
     uint8_t eirSecureConnConfirmation[18] = { 17, NDEF_BT_EIR_LE_SECURE_CONN_CONFIRMATION_VALUE };
     memcpy(&eirSecureConnConfirmation[2], oob_data.le_sc_data.c, BLE_SC_CONFIRMATION_VALUE_SIZE);
    
     /* EIR Field 5: Randomizer Value - Needed for LE Secure Connections */
     uint8_t eirSecureConnRandom[18] = { 17, NDEF_BT_EIR_LE_SECURE_CONN_RANDOM_VALUE };
     memcpy(&eirSecureConnRandom[2], oob_data.le_sc_data.r, BLE_SC_RANDOM_VALUE_SIZE);
    
     /* EIR Field 7: LE Flags: BR/EDR not supported */
     uint8_t eirLeFlags[3] = { 2, NDEF_BT_EIR_FLAGS, 0x04 };
    
     /* EIR Field 8: Device local name */
     const char *localName = bt_get_name();
     uint8_t eirLocalName[2 + strlen(localName)] = { (uint8_t)(1 + strlen(localName)), NDEF_BT_EIR_COMPLETE_LOCAL_NAME };
     memcpy(&eirLocalName[2], localName, strlen(localName));
    
     /* Set all EIR */
     rc = ndefBluetoothSetEir(&bluetooth, (uint8_t *) &eirLeAddress);
     if (rc != ERR_NONE)
     return rc;
    
     rc = ndefBluetoothSetEir(&bluetooth, (uint8_t *) &eirLeRole);
     if (rc != ERR_NONE)
     return rc;
    
     rc = ndefBluetoothSetEir(&bluetooth, (uint8_t *) &eirTk);
     if (rc != ERR_NONE)
     return rc;
    
     rc = ndefBluetoothSetEir(&bluetooth, (uint8_t *) &eirSecureConnConfirmation);
     if (rc != ERR_NONE)
     return rc;
    
     rc = ndefBluetoothSetEir(&bluetooth, (uint8_t *) &eirSecureConnRandom);
     if (rc != ERR_NONE)
     return rc;
    
     rc = ndefBluetoothSetEir(&bluetooth, (uint8_t *) &eirLeFlags);
     if (rc != ERR_NONE)
     return rc;
    
     rc = ndefBluetoothSetEir(&bluetooth, (uint8_t *) &eirLocalName);
     if (rc != ERR_NONE)
     return rc;
    
     /* Init BLE record */
     rc = ndefBluetoothLeInit(&ndefBt, &bluetooth);
     if (rc != ERR_NONE)
     return rc;
    
     rc = ndefTypeToRecord(&ndefBt, &leOobRecord);
     if (rc != ERR_NONE)
     return rc;
    
     /* Encode the record starting after the first two bytes that are saved to store the NDEF file length */
     ndefBuffer bufRecord = { &ndefBuf[2], (uint32_t)(ndefBufSize - 2) };
     rc = ndefRecordEncode(&leOobRecord, &bufRecord);
     if (rc != ERR_NONE)
     return rc;
    
     /* Store the NDEF file length on the two first bytes */
     ndefBuf[0] = bufRecord.length >> 8;
     ndefBuf[1] = bufRecord.length & 0xFF;
    
     CCC_Log("NDEF size: %d\n", bufRecord.length);
     CCC_Log("NDEF :%s\n", hex2Str(bufRecord.buffer, bufRecord.length));
    
     return rc;
    }

    This seems to work well for some phones (like the Samsung A12) where the pairing always occurs. However with other phones like Xiaomi or Huawei the pairing procedure is never triggered. This seems strange to me because with other manufacturer' example I was able to complete the pairing. I can also show you what the NDEF message of the above code looks like:
      Screenshot_20250611_113943_NFC Tools_114114.jpg

    Technical Moderator
    June 11, 2025

    Hi,

    Have you updated the ndef_type_bluetooth.c file with the one I've attached?

    By the way, why do you pair for LE rather than BR/EDR? Note that Bluetooth Low Energy (LE) and Bluetooth Classic (BR/EDR) are two are distinct Bluetooth technologies designed for different use cases.

    Are you sure those Xiaomi or Huawei phones support Bluetooth LE?

    From your January post, it seems that Felica/NFC T3T technology is being used. Do you still use this technology? 

    Regarding your question about Handover select ('Hs') record, see section 4.3 of the Bluetooth® Secure Simple Pairing Using NFC Application Document

    When using LE OOB pairing, only LE Bluetooth Device Address and LE Role are mandatory. Can you try to populate only LeAddress, LeRole and LocalName?

    Can you do a full scan of the tag using TagInfo android app and share with me the resulting text and xml files?

    Rgds

    BT

    JPortilhaAuthor
    Explorer
    June 11, 2025

    Hello,

    Yes I have replaced that file with the one that you have sent me, however it seems that the only difference is in line 347:

    /* For BR/EDR only, but no test needed because length is 0 in that case */
    if (type->id == NDEF_TYPE_ID_BLUETOOTH_BREDR)
    {
    length += ndefData->bufDeviceAddress.length;
    }

    I am using Bluetooth Low Energy because it is a requirement for the application I am developping. Additionaly, also LE Secure Connections (Level 4 of Security) is required, so the fields of Confirmation and Random values generated by the bluetooth stack are also required.

    Yes Xiaomi 14T that I am currently using, is one of the most recent Xiaomi smartphones and so it supports Bluetooth Low Energy. The most weird thing to me is that it follows perfectly with Samsung A12 but with my Xiaomi it doesn't even trigger the procedure. However when I run this example from Nordic works on all smarphones. Nordic NFC Pair 

    Yes, actually the technology being used is T4T (Type 4 Tag), that comes from your example of card emulation for ST25R3920B, as this chip needs to be working as a tag in the moment of pairing.

    For some reason, I am not able to read the ST25R3920B with the TagInfo application, it only works with NFC tools. However I can read nordic tag and show you the all the details. As theirs is working, maybe the information on that tag can help you understanding if something is missing.

    Technical Moderator
    June 11, 2025

    Hi,

    can you make sure that only T4T is emulated (set RFAL_FEATURE_NFCF to false in rfal_platform.h)? When using TagInfo, make sure to wait enough time to complete the reading. If this is still failing, can you set a breakpoint after demoEncodeBluetoothRecord and dump the memory content of ndefFile and share the memory content with me? 

    Can you provide information about Xiaomi or Huawei phone (model and Android version)?

    The NDEF message from Nordic has an extra Tnep record ('Tp') that uses the  well-known service name urn:nfc:sn:handover. Can you clone the Nordic NDEF message on a physical tag (or simply as the ndefFile content of the ST25R3920B emulated tag) and remove the Tnep record (of course the overall NDEF length needs to be reduced)? Still working with Xiaomi or Huawei phone? Make sure to double check the content of the NDEF with TagInfo or NFC tools. Can you remove the Handover select record and keep only the LE oob record? Still working with Xiaomi or Huawei phone?

    Compared with your LE OOB record, the Nordic LE OOB record has an extra Appearance (0x19) field. this should not make a difference but can you try to add it?

    Rgds

    BT

    JPortilhaAuthor
    Explorer
    June 11, 2025

    Hello, I cannot disable the NFCF technology because the other part of my application needs it make data exchange with a smartphone application (in a P2P fashion). I really can't use the TagInfo application, no matter how long I want but I can dump the memory of NDEF file after the encode function:

    NDEF size: 116

    D220516170706C69636174696F6E2F766E642E626C7565746F6F74682E6C652E6F6F62081B8EA7995C2A6901021C0011107FEA09E3F1EB30BF1D080D88ADED7B4B11228E6E28862CA1E8937AB6CB2BC6E2C2D711235FBE39E671EAB8CCECD3E97CF76DFD710201040B094454785F4343435F444B

    This should match the payload I have sent earlier in NFC Tools, plus the header information that should be also correct.

    The Xiaomi phone is a Xiaomi 14T, Android 15; the Huawei is a P40-Lite I am not really sure about the android version of this one but I think its 10. The basepoint is that with nordic example they both work, so both should support BLE and OOB Pair. I dont́ think that cloning the tag will do much, as then the phone needs to interact with the board bluetooth stack in order to paitr.

    I had already tried to "clone" the nordic information into my application bnut without success. However I was putting all in (the "tp" record included). I can try to add manually the rest of the information and see if it works.

    Technical Moderator
    June 11, 2025

    Hi,

    what is the value of the 2 first bytes of the ndefFile after the encode function (this should be the length of the NDEF message i.e. 00h 74h)?

    "I dont́ think that cloning the tag will do much, as then the phone needs to interact " Well, the phone is supposed to display "do you want to pair with xxxx?" once the oob record has been read and before any other interactions. The goal is to check whether the phone reacts on the Tnep handover record or on the oob record or on Hs record + oob record.

    "I cannot disable the NFCF technology because the other part of my application needs it make data exchange with a smartphone application (in a P2P fashion)". Do you mean that RFAL_NFC_POLL_TECH_AP2P and RFAL_NFC_LISTEN_TECH_AP2P are present in your discParam.techs2Find and that DEMO_CARD_EMULATION_ONLY has been removed? Can you share more details on the value of your discParam.techs2Find? If AP2P is configured in your application, some phones will enter into P2P and will not read the NDEF message. In order to debug your issue, please make sure to have only CE mode in NFCA. This will help to understand the pairing problem. And for sure when having only NFCA CE, TagInfo can read the card emulation.

    Rgds

    BT

    JPortilhaAuthor
    Explorer
    June 12, 2025

    Hello,

    I think I haven't understand the first part of the reply. After the encode function we get a raw buffer as the one I have sent you. I can decode the payload for you:

    D2h - Header - 5 LSB -> Flags (1101 0) - This means the bits of Message Begin, Message End and Short Record are set; TNF (010) - MIME Record Type;
    20h - Type Length
    51h - Payload Legth

    61h to 62h - Type: application/vnd.bluetooth.le.oob
    08h to end - Payload: Bluetooth data

    For the tag clonation, I have tried to clone on the ST25R3920B while was in card emulation but did not work. In a normal passive tag, I was able to sucessfully clone the tag and it worked with all smartphones I currently have for test (Xiaomi, Huawei, Google and Samsung), the pairing prompt appeared on all of them.

    As for my NFC technologies I have 2 distinct functions, one for the P2P data exchange with the smartphone and other for the card emulation and BLE Pairing. I will leave the code snippets here:


    flags = RFAL_NFC_POLL_TECH_A | RFAL_NFC_LISTEN_TECH_A
    
    int NFCHandler::PollTargets(int flags, uint32_t duration)
    {
     if (m_Initialized == false)
     return -ENODEV;
    
     if (flags == 0u)
     return -EINVAL;
    
     if (rfalNfcGetState() != RFAL_NFC_STATE_IDLE)
     rfalNfcDeactivate(RFAL_NFC_DEACTIVATE_IDLE);
    
     /* Retrieve default discovery parameters */
     rfalNfcDefaultDiscParams(&m_discParam);
    
     //m_discParam.notifyCb = m_fncPollNotification;
    
     m_discParam.p2pNfcaPrio = true;
     m_discParam.totalDuration = 1000;
     m_discParam.techs2Find = flags;
     
     m_discParam.wakeupEnabled = true;
     m_discParam.wakeupConfigDefault = false;
     /* Wake up mode configurations */
     m_discParam.wakeupConfig.period = RFAL_WUM_PERIOD_500MS; 
     m_discParam.wakeupConfig.swTagDetect = false;
     m_discParam.wakeupConfig.irqTout = false;
     /* ST25R3920B does not support capacitive sensing */
     m_discParam.wakeupConfig.cap.enabled = false;
     m_discParam.wakeupConfig.indPha.enabled = false;
     m_discParam.wakeupConfig.indAmp.enabled = true;
     m_discParam.wakeupConfig.indAmp.autoAvg = true;
     m_discParam.wakeupConfig.indAmp.delta = 50;
     m_discParam.wakeupConfig.indAmp.aaWeight = RFAL_WUM_AA_WEIGHT_8;
     m_discParam.wakeupConfig.indAmp.reference = 200;
     m_discParam.wakeupConfig.cap.reference = RFAL_WUM_REFERENCE_AUTO;
     m_discParam.wakeupConfig.indPha.reference = RFAL_WUM_REFERENCE_AUTO;
    
     m_pollDuration = duration;
    
     ReturnCode rc = rfalNfcDiscover(&m_discParam); 
    
     if (rc != ERR_NONE)
     {
     platformLog("rfalNfcDiscover, with ret=0x%x\n", (uint8_t) rc);
     return -EBUSY;
     }
    
     rfalNfcWorker();
    }
    
    ​

    As for my card emulation function I got this:

    int NFCHandler::InitCardEmulationMode(uint32_t duration)
    {
     int rc = 0;
    
     k_thread_suspend(m_pollThreadId);
    
     if (!m_Initialized)
     return -ENODEV;
    
     if (rfalNfcGetState() != RFAL_NFC_STATE_IDLE)
     rfalNfcDeactivate(RFAL_NFC_DEACTIVATE_IDLE);
    
     rfalNfcDefaultDiscParams(&m_discParam);
    
     m_discParam.devLimit = 1U;
     m_discParam.notifyCb = m_fncPollNotification;
     m_discParam.totalDuration = 60000U;
     m_discParam.techs2Find = RFAL_NFC_TECH_NONE;
    
     #if RFAL_SUPPORT_CE && RFAL_FEATURE_LISTEN_MODE
     memcpy( gNfcfNfcid, ceNFCF_nfcid2, RFAL_NFCF_NFCID2_LEN );
     memcpy( ndefFile, (uint8_t *)demoNdefFile, demoNdefFileLen );
     /* Update AIB Ln with actual NDEF length */
     InformationBlock[12] = demoNdefFile[0];
     InformationBlock[13] = demoNdefFile[1];
     uint16_t checksum = 0;
     for (int i = 0; i < 14; i++)
     {
     checksum += InformationBlock[i];
     }
     InformationBlock[14] = checksum >> 8;
     InformationBlock[15] = checksum & 0xFF;
    
     #if RFAL_SUPPORT_MODE_LISTEN_NFCA
     /* Set configuration for NFC-A CE */
     memcpy(m_discParam.lmConfigPA.SENS_RES, ceNFCA_SENS_RES, RFAL_LM_SENS_RES_LEN); /* Set SENS_RES / ATQA */
     memcpy(m_discParam.lmConfigPA.nfcid, ceNFCA_NFCID, RFAL_LM_NFCID_LEN_04); /* Set NFCID / UID */
     m_discParam.lmConfigPA.nfcidLen = RFAL_LM_NFCID_LEN_04; /* Set NFCID length to 7 bytes */
     m_discParam.lmConfigPA.SEL_RES = ceNFCA_SEL_RES; /* Set SEL_RES / SAK */
    
     m_discParam.techs2Find |= RFAL_NFC_LISTEN_TECH_A;
     #endif /* RFAL_SUPPORT_MODE_LISTEN_NFCA */
    
     #if RFAL_SUPPORT_MODE_LISTEN_NFCF
     /* Set configuration for NFC-F CE */
     memcpy(m_discParam.lmConfigPF.SC, ceNFCF_SC, RFAL_LM_SENSF_SC_LEN); /* Set System Code */
     memcpy(&ceNFCF_SENSF_RES[RFAL_NFCF_CMD_LEN], ceNFCF_nfcid2, RFAL_NFCID2_LEN); /* Load NFCID2 on SENSF_RES */
     memcpy(m_discParam.lmConfigPF.SENSF_RES, ceNFCF_SENSF_RES, RFAL_LM_SENSF_RES_LEN); /* Set SENSF_RES / Poll Response */
    
     m_discParam.techs2Find |= RFAL_NFC_LISTEN_TECH_F;
     #endif /* RFAL_SUPPORT_MODE_LISTEN_NFCF */
     #endif /* RFAL_SUPPORT_CE && RFAL_FEATURE_LISTEN_MODE */
    
     rc = rfalNfcDiscover(&m_discParam);
    
     /* Start timeout timer */
     k_timer_start(&m_cardEmulationTimer, K_SECONDS(duration), K_NO_WAIT); 
    
     k_thread_resume(m_cardEmulationThreadId);
    
     return rc;
    }

    Finally for my technologies defines:

     
    #define RFAL_SUPPORT_MODE_POLL_NFCA true /*!< RFAL Poll NFCA mode support switch */
    #define RFAL_SUPPORT_MODE_POLL_NFCB true /*!< RFAL Poll NFCB mode support switch */
    #define RFAL_SUPPORT_MODE_POLL_NFCF true /*!< RFAL Poll NFCF mode support switch */
    #define RFAL_SUPPORT_MODE_POLL_NFCV true /*!< RFAL Poll NFCV mode support switch */
    #define RFAL_SUPPORT_MODE_POLL_ACTIVE_P2P true /*!< RFAL Poll AP2P mode support switch */
    #define RFAL_SUPPORT_MODE_LISTEN_NFCA true /*!< RFAL Listen NFCA mode support switch */
    #define RFAL_SUPPORT_MODE_LISTEN_NFCB false /*!< RFAL Listen NFCB mode support switch */
    #define RFAL_SUPPORT_MODE_LISTEN_NFCF true /*!< RFAL Listen NFCF mode support switch */
    #define RFAL_SUPPORT_MODE_LISTEN_ACTIVE_P2P true /*!< RFAL Listen AP2P mode support switch */
    
    
    /*******************************************************************************/
    /*! RFAL supported Card Emulation (CE) */
    #define RFAL_SUPPORT_CE ( RFAL_SUPPORT_MODE_LISTEN_NFCA || RFAL_SUPPORT_MODE_LISTEN_NFCB || RFAL_SUPPORT_MODE_LISTEN_NFCF )
    
    /*! RFAL supported Reader/Writer (RW) */
    #define RFAL_SUPPORT_RW ( RFAL_SUPPORT_MODE_POLL_NFCA || RFAL_SUPPORT_MODE_POLL_NFCB || RFAL_SUPPORT_MODE_POLL_NFCF || RFAL_SUPPORT_MODE_POLL_NFCV )
    
    /*! RFAL support for Active P2P (AP2P) */
    #define RFAL_SUPPORT_AP2P ( RFAL_SUPPORT_MODE_POLL_ACTIVE_P2P || RFAL_SUPPORT_MODE_LISTEN_ACTIVE_P2
    Technical Moderator
    June 12, 2025

    Hi,

    "I think I haven't understand the first part of the reply. After the encode function we get a raw buffer as the one I have sent you. I can decode the payload for you". Well, I am used to decode NDEF records :-).  The demoEncodeBluetoothRecord fills the ndefFile buffer. The 2 first bytes of this buffer are supposed to be the NDEF length. I just want to verify that this length is correct. The dump (D2 20 51 etc.) is basically the content of &ndefFile[2] whereas I want to see the full file i.e. &ndefFile[0] starting with the NDEF length.

    "In a normal passive tag, I was able to successfully clone the tag and it worked with all smartphones": can you remove the Tnep Tp record: is it still working on all phones? can you remove the Handover Select Hs record: is it still working on all phones? Make sure to update the ndef length accordingly and to update the Message Begin/Message End in the record header.

    "As for my NFC technologies I have 2 distinct functions, one for the P2P data exchange with the smartphone and other for the card emulation and BLE Pairing. I will leave the code snippets here".

    • It seems Zephyr RTOS is being used (please confirm).
    • How do you handle the ST25_IRQ and how is sequenced the NFC task. Make sure the handling of the ST25_IRQ is not delayed and the rfalNfcWorker is running in a dense loop
    • How do you switch between the pollTargets and the CardEmulation mode? How can you make sure that you are in the CardEmulation mode and not in the pollTargets mode?
    • pollTargets is not using AP2P. Why is RFAL_NFC_LISTEN_TECH_A being used in this mode? This enables the card emulation... 
    • What do you mean by P2P on your side? When in pollTargets mode what is your application doing?
    • see https://community.st.com/t5/st25-nfc-rfid-tags-and-readers/how-to-send-ndef-packet-with-rfal-hl/td-p/204399 for support of AP2P with Android phones.
    • RFAL_SUPPORT_MODE_xxx: please do not modify those flags, do not modify rfal_features.h. You can modify rfal_platform.h flags (i.e. RFAL_FEATURE_xxx)
    • NFCF is not used in pollTargets and CardEmulationMode needs only RFAL_NFC_LISTEN_TECH_A. Thus should not be needed in your application. So RFAL_FEATURE_NFCF can be set to false.

    Rgds

    BT

     

    JPortilhaAuthor
    Explorer
    June 12, 2025

    Hello, 

    Sorry you are write. Currently I got: NDEF[0] = 0; NDEF[1] = 126 (0x7E), matching the previous kogs I have sent.

    I am trying to understand how can I remove those parts from the nordic example to test if it stills works without those records.

    Yes, Zephyr RTOS is being used.

    Basically I am using the ST25R3920B in wake-up mode, and whenever I got a ST25R IRQ I have a callback that should "awake" the system.

    When I said P2P I was not refering to any P2P example. I basically have a smarphone application that will send a message frame (like 0x00, 0x01, 0x02..) to my board and the board will reply. This is done sometimes until there is no more data exchange. I will leave my code snippets, maybe that helps:

    Should I remove RFAL_NFC_LISTEN_TECH_A from here?

    flags = RFAL_NFC_POLL_TECH_A | RFAL_NFC_LISTEN_TECH_A
    int NFCHandler::PollTargets(int flags, uint32_t duration)
    {
     if (m_Initialized == false)
     return -ENODEV;
    
     if (flags == 0u)
     return -EINVAL;
    
     if (rfalNfcGetState() != RFAL_NFC_STATE_IDLE)
     rfalNfcDeactivate(RFAL_NFC_DEACTIVATE_IDLE);
    
     /* Retrieve default discovery parameters */
     rfalNfcDefaultDiscParams(&m_discParam);
    
     //m_discParam.notifyCb = m_fncPollNotification;
    
     m_discParam.p2pNfcaPrio = true;
     m_discParam.totalDuration = 1000;
     m_discParam.techs2Find = flags;
     
     m_discParam.wakeupEnabled = true;
     m_discParam.wakeupConfigDefault = false;
     /* Wake up mode configurations */
     m_discParam.wakeupConfig.period = RFAL_WUM_PERIOD_500MS; 
     m_discParam.wakeupConfig.swTagDetect = false;
     m_discParam.wakeupConfig.irqTout = false;
     /* ST25R3920B does not support capacitive sensing */
     m_discParam.wakeupConfig.cap.enabled = false;
     m_discParam.wakeupConfig.indPha.enabled = false;
     m_discParam.wakeupConfig.indAmp.enabled = true;
     m_discParam.wakeupConfig.indAmp.autoAvg = true;
     m_discParam.wakeupConfig.indAmp.delta = 50;
     m_discParam.wakeupConfig.indAmp.aaWeight = RFAL_WUM_AA_WEIGHT_8;
     m_discParam.wakeupConfig.indAmp.reference = 200;
     m_discParam.wakeupConfig.cap.reference = RFAL_WUM_REFERENCE_AUTO;
     m_discParam.wakeupConfig.indPha.reference = RFAL_WUM_REFERENCE_AUTO;
    
     m_pollDuration = duration;
    
     ReturnCode rc = rfalNfcDiscover(&m_discParam); 
    
     if (rc != ERR_NONE)
     {
     platformLog("rfalNfcDiscover, with ret=0x%x\n", (uint8_t) rc);
     return -EBUSY;
     }
    
     rfalNfcWorker();
    
     int ret = gpio_pin_interrupt_configure(NFC_IRQ_PIN.port, NFC_IRQ_PIN.pin, GPIO_INT_ENABLE);
     if(ret != 0)
     return ret;
     gpio_init_callback(&callbackDataNFC, NFCGpioCallback, BIT(NFC_IRQ_PIN.pin));
     ret = gpio_add_callback(NFC_IRQ_PIN.port, &callbackDataNFC);
    
     /* Sleep to allow simultaneous NFC and BLE operations */
     k_msleep(200);
     
     return ret;
    }

    This thread will check if there was actually any field "activity" to enable the data exchange:

    void NFCHandler::m_fncPollThread(void *arg1, void *arg2, void *arg3)
    {
     if (arg1 == nullptr)
     return;
    
     NFCHandler *nfcDev = static_cast<NFCHandler *>(arg1);
     uint32_t duration = (uint32_t) (*(uint32_t *) (arg2));
     uint64_t duration_ms = duration * 1000;
    
     rfalNfcState state;
    
     uint32_t start = k_uptime_get();
    
     while (true)
     {
     rfalNfcWorker(); /* Run RFAL worker periodically */
     state = rfalNfcGetState();
    
     /* Check for timeout */
     if ((uint64_t) k_uptime_get() - start > duration_ms)
     {
     rfalNfcDeactivate(RFAL_NFC_DEACTIVATE_IDLE);
     if (nfcDev->m_fncUserCb != nullptr)
     nfcDev->m_fncUserCb(nfcDev->m_nfcDevice, rfalNfcGetState());
     k_thread_suspend(nfcDev->m_pollThreadId);
     start = k_uptime_get();
     }
     /* Verify if a device was detected */
     if (rfalNfcIsDevActivated(state))
     {
     /* Lock mutex for data update */
     k_mutex_lock(&nfcDev->m_pollThreadMutex, K_FOREVER);
     /* Get the current active tag / device */
     rfalNfcGetActiveDevice(&nfcDev->m_nfcDevice);
     /* Unlock update data mutex */
     k_mutex_unlock(&nfcDev->m_pollThreadMutex);
     /* Call for used defined callback */
     if (nfcDev->m_fncUserCb != nullptr)
     nfcDev->m_fncUserCb(nfcDev->m_nfcDevice, state);
     k_thread_suspend(nfcDev->m_pollThreadId);
     }
     /* Yield task to next one */
     k_yield();
     }
    }

    And for card emulation I got this:

    int NFCHandler::InitCardEmulationMode(uint32_t duration)
    {
     int rc = 0;
    
     k_thread_suspend(m_pollThreadId);
    
     if (!m_Initialized)
     return -ENODEV;
    
     if (rfalNfcGetState() != RFAL_NFC_STATE_IDLE)
     rfalNfcDeactivate(RFAL_NFC_DEACTIVATE_IDLE);
    
     rfalNfcDefaultDiscParams(&m_discParam);
    
     m_discParam.devLimit = 1U;
     m_discParam.notifyCb = m_fncPollNotification;
     m_discParam.totalDuration = 60000U;
     m_discParam.techs2Find = RFAL_NFC_TECH_NONE;
    
     #if RFAL_SUPPORT_CE && RFAL_FEATURE_LISTEN_MODE
     memcpy( gNfcfNfcid, ceNFCF_nfcid2, RFAL_NFCF_NFCID2_LEN );
     memcpy( ndefFile, (uint8_t *)demoNdefFile, demoNdefFileLen );
     /* Update AIB Ln with actual NDEF length */
     InformationBlock[12] = demoNdefFile[0];
     InformationBlock[13] = demoNdefFile[1];
     uint16_t checksum = 0;
     for (int i = 0; i < 14; i++)
     {
     checksum += InformationBlock[i];
     }
     InformationBlock[14] = checksum >> 8;
     InformationBlock[15] = checksum & 0xFF;
    
     #if RFAL_SUPPORT_MODE_LISTEN_NFCA
     /* Set configuration for NFC-A CE */
     memcpy(m_discParam.lmConfigPA.SENS_RES, ceNFCA_SENS_RES, RFAL_LM_SENS_RES_LEN); /* Set SENS_RES / ATQA */
     memcpy(m_discParam.lmConfigPA.nfcid, ceNFCA_NFCID, RFAL_LM_NFCID_LEN_04); /* Set NFCID / UID */
     m_discParam.lmConfigPA.nfcidLen = RFAL_LM_NFCID_LEN_04; /* Set NFCID length to 7 bytes */
     m_discParam.lmConfigPA.SEL_RES = ceNFCA_SEL_RES; /* Set SEL_RES / SAK */
    
     m_discParam.techs2Find |= RFAL_NFC_LISTEN_TECH_A;
     #endif /* RFAL_SUPPORT_MODE_LISTEN_NFCA */
    
     #if RFAL_SUPPORT_MODE_LISTEN_NFCF
     /* Set configuration for NFC-F CE */
     memcpy(m_discParam.lmConfigPF.SC, ceNFCF_SC, RFAL_LM_SENSF_SC_LEN); /* Set System Code */
     memcpy(&ceNFCF_SENSF_RES[RFAL_NFCF_CMD_LEN], ceNFCF_nfcid2, RFAL_NFCID2_LEN); /* Load NFCID2 on SENSF_RES */
     memcpy(m_discParam.lmConfigPF.SENSF_RES, ceNFCF_SENSF_RES, RFAL_LM_SENSF_RES_LEN); /* Set SENSF_RES / Poll Response */
    
     m_discParam.techs2Find |= RFAL_NFC_LISTEN_TECH_F;
     #endif /* RFAL_SUPPORT_MODE_LISTEN_NFCF */
     #endif /* RFAL_SUPPORT_CE && RFAL_FEATURE_LISTEN_MODE */
    
     rc = rfalNfcDiscover(&m_discParam);
    
     /* Start timeout timer */
     k_timer_start(&m_cardEmulationTimer, K_SECONDS(duration), K_NO_WAIT); 
    
     k_thread_resume(m_cardEmulationThreadId);
    
     return rc;
    }


    And this thread will run for X seconds:

    void NFCHandler::m_fncCardEmulationThread(void *arg1, void *arg2, void *arg3)
    {
     if (arg1 == nullptr)
     return;
    
     NFCHandler *nfcDev = static_cast<NFCHandler *>(arg1);
     rfalNfcState state;
     
     while(true)
     {
     /* Run RFAL worker periodically */
     rfalNfcWorker(); 
     state = rfalNfcGetState();
    
     /* Verify if a the smartphone detected the card */
     if(rfalNfcIsDevActivated(state))
     {
     /* Lock mutex for data update */
     k_mutex_lock(&nfcDev->m_cardEmulationThreadMutex, K_FOREVER);
     /* Get the current active tag / device */
     rfalNfcGetActiveDevice(&nfcDev->m_nfcDevice);
     /* Unlock update data mutex */
     k_mutex_unlock(&nfcDev->m_cardEmulationThreadMutex);
     switch(nfcDev->m_nfcDevice->type)
     {
     case RFAL_NFC_POLL_TYPE_NFCA:
     case RFAL_NFC_POLL_TYPE_NFCF:
     CCC_Log("Activated in CE %s mode.\r\n", (nfcDev->m_nfcDevice->type == RFAL_NFC_POLL_TYPE_NFCA) ? "NFC-A" : "NFC-F");
     nfcDev->HandleCardEmulation(nfcDev->m_nfcDevice);
     break;
     default:
     break;
     } 
     rfalNfcDeactivate(RFAL_NFC_DEACTIVATE_DISCOVERY);
     }
     } 
    }


     Finally, the HandleCardEmulation is based on your example with demoCE:

    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))));
     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 */
    }

    Maybe I am doing something wrong, but it is strange to work with one smartphone and others don't

    Technical Moderator
    June 12, 2025

    Hi,

    D220516170706C69636174696F6E2F766E642E626C7565746F6F74682E6C652E6F6F62081B8EA7995C2A6901021C0011107FEA09E3F1EB30BF1D080D88ADED7B4B11228E6E28862CA1E8937AB6CB2BC6E2C2D711235FBE39E671EAB8CCECD3E97CF76DFD710201040B094454785F4343435F444B has a length of 74h not 0x7E!

    I believe the updated ndef_type_bluetooth.c has not been taken into account in your build system. Please double check. If the NDEF length is incorrect, the behavior of the different phone can be random. Please provide the full content of the ndefFile (length and data).

    "When I said P2P I was not refering to any P2P example. I basically have a smarphone application that will send a message frame (like 0x00, 0x01, 0x02..) to my board and the board will reply" This is very unclear to me. Is your smartphone application acting as a reader or as an emulated tag? If your smartphone app acts as an emulated tag, then RFAL_NFC_LISTEN_TECH_A is useless in your pollTargets.

    Rgds

    BT

     

    JPortilhaAuthor
    Explorer
    June 12, 2025

    In the meantime, I have replicated manually the nordic payload into the ST25R20B but replacing BLE data with the one from by board:

    00A791020D487315C102000000046163010130001A2051016170706C69636174696F6E2F766E642E626C7565746F6F74682E6C652E6F6F6230081B61DD5891834901021C001110CAD9E5D0F32F5456D692E1EEB28171D31122278E54E8CE938ACF8FD3262327411E851123C59703E0B6C650BE92CA782093DF69310201040B094454785F4343435F444B51021A5470101375726E3A6E66633A736E3A68616E646F766572002C02.

    This payload has exactly the same record: Hs (with carrier), LE OOB and "Tp". The issue is the same, the pairing is trigger on Samsung and Google Pixel but not on Xiaomi. This seems very strange to me. Could it be the tag "functioning" mode that is different from nordic.

    My smartphone application should behave as a reader, it is not passive like a tag. During a transaction there should be multiple data exchanges, and the smartphone always initiate, for example:

    Smarphone sends 0x01;

    Board receives and replies 0x02;

    Smartphone receives and replies 0x03;

    Technical Moderator
    June 12, 2025

    Hi,

    I suspect with techs2Find containing RFAL_NFC_POLL_TECH_A your device is also polling and sometimes finding the device and not vice versa. Please remove it and then it should work more reliably.

    Regards, Ulysses

    JPortilhaAuthor
    Explorer
    June 12, 2025

    The PollTargets is not used for bluetooth pairing, does that make a difference?

    Technical Moderator
    June 13, 2025

    Yes, a big one.

    If your board acts as reader and discovers the smartphone as tag device then none of your pairing will work.

    Both acting in both roles makes it more or less random who discovers whom.

    Ulysses