NFC Wake Up and TNEP

Context : I'm building an embedded application using the nrf52832, that can turn on/off and be configured via NFC. These functionnality are implemented using the t4t library in R/W mode and are functional, I am able to read and write text NDEF messages using a generic nfc app on my phone, and interpret the modified NDEF on the embedded device. The device also wakes up successfuly when an NFC field is detected.

My problem is the following, when the device wakeup from an nfc field detection, the phone seemingly does not detect it until I move it way from the device and then put it back. What I would like is to be able to place the phone on a turned off device, and be able to "directly" edit the NDEF, without further actions.

I have tried to restart the emulation from the device, but no changes have been noted. If I turn on and off the nfc functionnality on my phone it does works, but I don't think I can do that from a custom app.

Is there a way to achieve my goal ? Can it be affected by the t4t library ? Is it compatible with NFC ? Any leads or informations would be greatly appreciated.

Parents
  • Hi

    Do you have the project you're working on here so we can try reproduce it on our end? Will it also run on the nRF52832 DK for example? I have asked internally if anyone has any idea of what's going on here, as this sounds very strange indeed.

    Best regards,

    Simon

  • Here is a cutdown version using only the NFC and LEDs, I tested it and it exhibits the same symptom as the full version.

    I do use a custom board, but it should work with the nRF52832DK, you may have to rewrite some values for the LEDs assignment.

    APP_main.h

    void APP_main_scheduleSystemOff(int delay_s);
    void APP_main_cancelSystemOff  (void);

    APP_main.c

    //--------- HEADER INCLUDE  ---------------
    #include "APP_main.h"
    //--------- C STANDARD INCLUDES -----------
    #include <stdbool.h>
    #include <stddef.h>
    #include <stdint.h>
    //--------- µC INCLUDES -------------------
    //--------- OS INCLUDES -------------------
    #include <zephyr/kernel.h>
    #include <zephyr/logging/log.h>
    #include <hal/nrf_power.h>
    #if !NRF_POWER_HAS_RESETREAS
    #include <hal/nrf_reset.h>
    #endif
    #include <zephyr/sys/poweroff.h>
    //--------- EXTERNALS INCLUDES ------------
    //--------- PROJECT INCLUDES --------------
    #include "APP_led.h"
    #include "APP_nfc.h"
    //--------- MACROS ------------------------
    #define APP_MAIN_SYSTEM_OFF_FROM_RESTART_DELAY_S    15       ///< Delay to set a value in nfc record before shutting off after a restart on nfc field detection
    //--------- TYPES AND ENUMERATIONS --------
    
    //--------- GLOBAL VARIABLES --------------
    
    //--------- PRIVATE VARIABLES -------------
    
    LOG_MODULE_REGISTER(APP_main);
    
    K_SEM_DEFINE(l_continueStartSemaphore, 0, 1);
    
    static struct k_work_delayable work_systemOff; //< Delayed work that enters system off. @ref cbWorkerSystemOff
    //--------- PRIVATE FUNCTION PROTOTYPES ---
    static void                   printResetReason(void);
    static void                   cbWorkerSystemOff(struct k_work* pWork);
    //--------- PUBLIC FUNCTIONS --------------
    
    /**
     * @brief Schedule the system power off after a delay
     * @param delay_s delay before power off
     */
    void APP_main_scheduleSystemOff(int delay_s)
    {
    	k_work_reschedule(&work_systemOff, K_SECONDS(delay_s));
    }
    
    void APP_main_cancelSystemOff()
    {
        LOG_INF("SystemOff canceled");
    	k_work_cancel_delayable(&work_systemOff); 
        k_sem_give(&l_continueStartSemaphore);
    }
    //--------- PRIVATE FUNCTIONS -------------
    
    
    /**
     * @brief Function entering system off.
     * System off is delayed to make sure that NFC tag was correctly read.
     * @param pWork unused
     */
    static void cbWorkerSystemOff(struct k_work* pWork)
    {
    	LOG_INF("Powering off system...");
    	sys_poweroff();
    }
    
    /**
     * @brief  Helper function for printing the reason of the last reset.
     * Can be used to confirm that NCF field actually woke up the system.
     */
    static void printResetReason(void)
    {
    	uint32_t reas;
    
    #if NRF_POWER_HAS_RESETREAS
    
    	reas = nrf_power_resetreas_get(NRF_POWER);
    	nrf_power_resetreas_clear(NRF_POWER, reas);
    	if (reas & NRF_POWER_RESETREAS_NFC_MASK) {
    		LOG_INF("Wake up by NFC field detect\n");
    	} else if (reas & NRF_POWER_RESETREAS_RESETPIN_MASK) {
    		LOG_INF("Reset by pin-reset\n");
    	} else if (reas & NRF_POWER_RESETREAS_SREQ_MASK) {
    		LOG_INF("Reset by soft-reset\n");
    	} else if (reas) {
    		LOG_INF("Reset by a different source (0x%08X)\n", reas);
    	} else {
    		LOG_INF("Power-on-reset\n");
    	}
    
    #else
    
    	reas = nrf_reset_resetreas_get(NRF_RESET);
    	nrf_reset_resetreas_clear(NRF_RESET, reas);
    	if (reas & NRF_RESET_RESETREAS_NFC_MASK) {
    		printk("Wake up by NFC field detect\n");
    	} else if (reas & NRF_RESET_RESETREAS_RESETPIN_MASK) {
    		printk("Reset by pin-reset\n");
    	} else if (reas & NRF_RESET_RESETREAS_SREQ_MASK) {
    		printk("Reset by soft-reset\n");
    	} else if (reas) {
    		printk("Reset by a different source (0x%08X)\n", reas);
    	} else {
    		printk("Power-on-reset\n");
    	}
    
    #endif
    }
    
    /**
     * Initialize all modules and timers, read and/or apply configuration, start advertising, then go to idle forever.
     */
    int main(void)
    {
        int status;
    
        printResetReason();
        
        k_work_init_delayable(&work_systemOff, cbWorkerSystemOff);
    
        APP_main_scheduleSystemOff(APP_MAIN_SYSTEM_OFF_FROM_RESTART_DELAY_S);
    
        status = APP_nfc_init();
        if (0 != status)
        {
            LOG_ERR("NFC initialization failed");
            return status;
        }
        LOG_DBG("NFC initialized successfuly");
    
        status = APP_led_init();
        if (0 != status)
        {
            LOG_ERR("Leds initialization failed");
            return status;
        }
        LOG_DBG("Leds initialized successfuly");
    
        APP_led_blueOn();
        k_sleep(K_MSEC(500));
        APP_led_blueOff();
    
        k_sem_take(&l_continueStartSemaphore, K_FOREVER); // Wait until the correct values have been written in nfc records before resuming the start sequence
    
        LOG_INF("START APP");
        
        // LEDS
        APP_led_blueOn();
        k_sleep(K_MSEC(1000));
        APP_led_blueOff();
        k_sleep(K_MSEC(800));
        for(int i = 0; i < 3; i++)
        {
            k_sleep(K_MSEC(200));
            APP_led_blueOn();
            k_sleep(K_MSEC(200));
            APP_led_blueOff();
        }
    
        for (;;) {
            k_sleep(K_FOREVER);
        }
        return status;
    }
    
    //--------- END OF FILE -------------------

    APP_nfc.h

    int APP_nfc_init();

    APP_nfc.c

    //--------- HEADER INCLUDE  ----------------
    #include "APP_nfc.h"
    //--------- C STANDARD INCLUDES ------------
    #include <stdlib.h>
    //--------- MCU INCLUDES -------------------
    //--------- OS INCLUDES --------------------
    #include <zephyr/kernel.h>
    #include <zephyr/logging/log.h>
    //--------- EXTERNALS INCLUDES -------------
    #include <nfc_t4t_lib.h>
    #include <nfc/ndef/msg.h>
    #include <nfc/ndef/text_rec.h>
    #include <nfc/tnep/tag.h>
    #include <nfc/ndef/msg_parser.h>
    #include <nfc/ndef/record_parser.h>
    #include <nfc/t4t/ndef_file.h>
    //--------- PROJECT INCLUDES ---------------
    #include "APP_main.h"
    //--------- MACROS -------------------------
    #define MAX_REC_COUNT (2)
    #define MAX_SVC_COUNT (1)
    #define NDEF_BUF_SIZE (128)
    
    #define ON_OFF_RECORD_INDEX (0)
    #define TIME_RECORD_INDEX   (1)
    
    #define APP_NFC_SYSTEM_OFF_DELAY_S                 3  ///< Delay before shutting off when device is turned off from NFC
    //--------- TYPES AND ENUMERATIONS ---------
    //--------- PRIVATE FUNCTION PROTOTYPES ----
    static void cbNfc(void *pContext, nfc_t4t_event_t event, const uint8_t *pBuffer, size_t bufferLength, uint32_t flags);
    static int  tnep_initial_msg_encode(struct nfc_ndef_msg_desc *msg);;
    static void cbWorkerHandlePayload();
    static int  getUsefulPayloadFromRecord(const struct nfc_ndef_record_desc *pRecord, uint8_t *pBuffer);
    //--------- PRIVATE VARIABLES --------------
    LOG_MODULE_REGISTER(APP_nfc);
    
    static struct k_poll_event events[NFC_TNEP_EVENTS_NUMBER];
    
    static uint8_t ndef_msg_buf[NDEF_BUF_SIZE] = {0};
    static uint8_t tnep_swap_buf[NDEF_BUF_SIZE] = {0};
    static uint8_t test_buf[NDEF_BUF_SIZE] = {0};
    static uint8_t l_aOnOffRecordText[] = "OFF";
    static uint8_t l_aTimeRecordText[sizeof("4294967295")] = "0";
    
    
    static size_t nfc_data_len = 0;
    static const uint8_t en_code[] = {'e', 'n'};
    static const uint8_t aSvcUri[] = "LXS";
    static struct k_work work_test;
    
    NFC_TNEP_TAG_SERVICE_DEF(nfcService, aSvcUri, sizeof(aSvcUri), NFC_TNEP_COMM_MODE_SINGLE_RESPONSE, 20, 10, 0, NULL, NULL, NULL, NULL);
    //--------- PUBLIC VARIABLES ---------------
    //--------- PUBLIC FUNCTIONS ---------------
    int APP_nfc_init()
    {
        int err = 0;
        size_t len = sizeof(ndef_msg_buf);
    
        k_work_init(&work_test, cbWorkerHandlePayload);
    
        err = nfc_tnep_tag_tx_msg_buffer_register(ndef_msg_buf, tnep_swap_buf, len);
        if (0 != err)
        {
            LOG_ERR("Cannot register tnep buffer, err: %d\n", err);
            return 0;
        }
    
        err = nfc_tnep_tag_init(events, ARRAY_SIZE(events), nfc_t4t_ndef_rwpayload_set);
        if (0 != err)
        {
            LOG_ERR("Cannot initialize TNEP protocol, err: %d\n", err);
            return 0;
        }
    
    
    
        err = nfc_t4t_setup(cbNfc, NULL);
    
        if(0 == err)
        {
            /* Set created message as the NFC payload */
            err = nfc_tnep_tag_initial_msg_create(MAX_REC_COUNT + MAX_SVC_COUNT, tnep_initial_msg_encode);
            if (0 == err) {
                err = nfc_t4t_emulation_start();
                if (0 != err)
                {
                    LOG_ERR("NFC T4T emulation start failed with %d", err);
                }
            }
            else
            {
                LOG_ERR("Cannot set payload! %d", err);
            }
        }
        else
        {
            LOG_ERR("NFC T4T SETUP failed with %d", err);
        }
    
        return err;
    }
    //--------- PRIVATE FUNCTIONS --------------
    static void cbNfc(void *pContext, nfc_t4t_event_t event, const uint8_t *pBuffer, size_t bufferLength, uint32_t flags)
    {
        switch (event) {
            case NFC_T4T_EVENT_NDEF_UPDATED:
                LOG_INF("NFC NDEF_UPDT");
                if(0 < bufferLength)
                {
                    nfc_data_len = bufferLength;
                    nfc_tnep_tag_rx_msg_indicate(nfc_t4t_ndef_file_msg_get(pBuffer), bufferLength);
                    memcpy(test_buf, nfc_t4t_ndef_file_msg_get(pBuffer), bufferLength);
                    k_work_submit(&work_test);
                }
                break;
            case NFC_T4T_EVENT_FIELD_ON:
                LOG_INF("NFC field on");
                break;
            case NFC_T4T_EVENT_FIELD_OFF:
                LOG_INF("NFC field off");
                break;
            default:
                break;
        }
    }
    
    static int tnep_initial_msg_encode(struct nfc_ndef_msg_desc *msg)
    {
        
        // Use Text Record to test with generic app
        NFC_NDEF_TEXT_RECORD_DESC_DEF(onOffRecord, UTF_8, en_code,
                          sizeof(en_code), l_aOnOffRecordText,
                          strlen(l_aOnOffRecordText));
    
        NFC_NDEF_TEXT_RECORD_DESC_DEF(timeRecord, UTF_8, en_code,
                          sizeof(en_code), l_aTimeRecordText,
                          strlen(l_aTimeRecordText));
    
        struct nfc_ndef_record_desc records[] = { NFC_NDEF_TEXT_RECORD_DESC(onOffRecord), NFC_NDEF_TEXT_RECORD_DESC(timeRecord) };
        
    
        return nfc_tnep_initial_msg_encode(msg,
                           records,
                           MAX_REC_COUNT);
    
    }
    
    static void cbWorkerHandlePayload()
    {
        uint32_t data_len = nfc_data_len;
        uint8_t res_buf[NDEF_BUF_SIZE * MAX_REC_COUNT];
        uint32_t res_buf_size = sizeof(res_buf);
        
        int err = nfc_ndef_msg_parse(res_buf, &res_buf_size, test_buf, &data_len);
        if (0 == err)
        {
            struct nfc_ndef_msg_desc* msg_desc = (struct nfc_ndef_msg_desc*)(res_buf);
            nfc_ndef_msg_printout(msg_desc); //DEBUG
            if(0 != getUsefulPayloadFromRecord(msg_desc->record[ON_OFF_RECORD_INDEX], l_aOnOffRecordText))
            {
                if (0 == strcmp(l_aOnOffRecordText, "OFF"))
                {
                    LOG_INF("NFC REC OFF");
                    APP_main_scheduleSystemOff(APP_NFC_SYSTEM_OFF_DELAY_S);
                }
                else if (0 == strcmp(l_aOnOffRecordText, "ON"))
                {
                    LOG_INF("NFC REC ON");
                    APP_main_cancelSystemOff();
                }
            }
            if(0 != getUsefulPayloadFromRecord(msg_desc->record[TIME_RECORD_INDEX], l_aTimeRecordText))
            {
                LOG_INF("NFC REC TIME");
                //APP_time_setReferenceTime(strtoul(l_aTimeRecordText, NULL, 10));
            }
            
        }
        else
        {
            LOG_ERR("nfc ndef msg parse failed with %d", err);
        }
    }
    
    static int getUsefulPayloadFromRecord(const struct nfc_ndef_record_desc *pRecord, uint8_t *pBuffer)
    {
        const struct nfc_ndef_bin_payload_desc* pBinPayloadDescriptor = pRecord->payload_descriptor;
        const uint8_t* pUsefulRecBuf = pBinPayloadDescriptor->payload + sizeof(en_code) + 1;
        const uint8_t usefulRecSize = pBinPayloadDescriptor->payload_length - sizeof(en_code) - 1;
        const uint8_t currentSize   = strlen(pBuffer);
        int comparisonValue = 0xFF;
    
        if (usefulRecSize == currentSize){ comparisonValue = memcmp(pBuffer, pUsefulRecBuf, currentSize); }
    
        LOG_HEXDUMP_INF(pBuffer      , currentSize  , "existing record");
        LOG_HEXDUMP_INF(pUsefulRecBuf, usefulRecSize, "incoming record");
    
        memcpy(pBuffer, pUsefulRecBuf, usefulRecSize);
        pBuffer[usefulRecSize] = 0x00;  //Add null char at end of array, not found in NDEF messages.
        LOG_HEXDUMP_INF(pBuffer, strlen(pBuffer) , "new record");
    
        return comparisonValue;
    }
    //--------- END OF FILE --------------------

    APP_led.h

    int  APP_led_init   (void);
    void APP_led_blueOn (void);
    void APP_led_blueOff(void);
    void APP_led_redOn  (void);
    void APP_led_redOff (void);

    APP_led.c

    //--------- PRIVATE VARIABLES -------------
    LOG_MODULE_REGISTER(APP_led);
    
    static const struct gpio_dt_spec l_blueLed = GPIO_DT_SPEC_GET(DT_PATH(leds, led_0), gpios); //< Zephyr pointer to blue led
    static const struct gpio_dt_spec l_redLed  = GPIO_DT_SPEC_GET(DT_PATH(leds, led_1), gpios); //< Zephyr pointer to red led
    
    //--------- PRIVATE FUNCTION PROTOTYPES ---
    //--------- PUBLIC FUNCTIONS --------------
    
    /**
     * @brief initialize LEDs (and configure unused GPIOs TEST IF USEFUL)
     * @retval 0   = OK
     * @retval 1   = Device not ready
     * @retval < 0 = Zephyr error code
     */
    int APP_led_init()
    {
        int status = 0;
        if(false == device_is_ready(l_blueLed.port))
        {
            LOG_ERR("Blue led is not ready");
            return 1;
        }
        if(false == device_is_ready(l_redLed.port))
        {
            LOG_ERR("Red led is not ready");
            return 1;
        }
    
        status = gpio_pin_configure_dt(&l_blueLed, GPIO_OUTPUT_ACTIVE);
        if(0 != status) {
            LOG_ERR("Blue led pin configuration failed with %d", status);
            return status;
        }
    
        status = gpio_pin_configure_dt(&l_redLed , GPIO_OUTPUT_ACTIVE);
        if(0 != status)
        {
            LOG_ERR("Red led pin configuration failed with %d", status);
            return status;
        }
    
        return 0;
    }
    
    
    /// @brief Turn on blue LED
    void APP_led_blueOn ()     { gpio_pin_set_dt   (&l_blueLed, 0); }
    /// @brief Turn off blue LED
    void APP_led_blueOff()     { gpio_pin_set_dt   (&l_blueLed, 1); }
    /// @brief Turn on red LED
    void APP_led_redOn  ()     { gpio_pin_set_dt   (&l_redLed , 0); }
    /// @brief Turn off red LED
    void APP_led_redOff ()     { gpio_pin_set_dt   (&l_redLed , 1); }
    
    //--------- PRIVATE FUNCTIONS -------------
    
    //--------- END OF FILE -------------------

    CMakeLists.txt

    cmake_minimum_required(VERSION 3.20.0)
    set(BOARD_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/)
    find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
    
    project(IoTag)
    
    include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src)
    target_sources(app PRIVATE 
        src/APP_main.c
        src/APP_led.c
        src/APP_nfc.c
        )
    

    prj.conf

    CONFIG_USE_SEGGER_RTT=y
    CONFIG_CBPRINTF_FP_SUPPORT=y
    CONFIG_GPIO=y
    
    CONFIG_LOG=y
    
    CONFIG_NFC_T4T_NRFXLIB=y
    
    CONFIG_NFC_NDEF=y
    CONFIG_NFC_NDEF_MSG=y
    CONFIG_NFC_NDEF_RECORD=y
    CONFIG_NFC_NDEF_TEXT_RECORD=y
    CONFIG_POWEROFF=y
    CONFIG_NFC_TNEP_TAG=y
    CONFIG_POLL=y

    iotag.dts

    /dts-v1/;
    #include <nordic/nrf52832_qfaa.dtsi>
    #include "IoTag-pinctrl.dtsi"
    
    / {
        model = "Linxens IoTag";
        compatible = "Linxens,IoTag";
    
        chosen {
            zephyr,sram = &sram0;
            zephyr,flash = &flash0;
        };
    
        leds {
            compatible = "gpio-leds";
    
            led0: led_0 {
                gpios = <&gpio0 17 GPIO_ACTIVE_LOW>;
                label = "Blue LED 0";
            };
    
            led1: led_1 {
                gpios = <&gpio0 18 GPIO_ACTIVE_LOW>;
                label = "Red LED 1";
            };
        };
    
    };
    
    &gpio0 {
        status = "okay";
    };
    
    &gpiote {
        status = "okay";
    };
    
    &nfct {
    	status = "okay";
    };

  • Thanks, I also see I haven't asked which NCS version you're working on. Can you confirm that as well? There were some known NFC issues prior to NCS v3.2.0, so if you're on an older version, we strongly recommend migrating to NCS v3.2.0.

    Best regards,

    Simon

  • I've been using ncs 3.1.1 until now, but I've just tried with v3.2.1 and the behavior is the same.

Reply Children
Related