Trouble generating NFC T4T messages in Zephyr

I'm attempting to create an NFC type 4 message to allow certain information to be read using a phone or other NFC reader.  I've studied a number of examples, but none that does exactly what I want, i.e. Type 4 text records.  The nearest I've found is the writable_ndef_msg example, but this uses file-based records, which I don't need, and I can't see the proper way to create records programatically without using the filesystem.

I've made some headway by scanning another known-good type 4 device using an ACR122 USB reader and a script tool to dump the raw data, and then I've crafted a packet that exactly matches this.

However, when I attempt to read using a phone I can't get any response at all.

I'm not an expert on NFC.  I suspect that there's something in the preamble of the protocol (i.e. something that the NFC script tool doesn't show me) that's not right, but I can't see anything in the NFC API that would allow me to experiment.

I've included below the code I'm using, if you're able to offer any advice on what I should try or how I should be going about it, I would be most grateful to hear.

Cheers,

  Frog


#include <zephyr.h>
#include <sys/reboot.h>
#include <stdbool.h>
#include <nfc_t4t_lib.h>
#include <stdio.h>

#include <nfc/ndef/msg.h>
#include <nfc/t4t/ndef_file.h>

#include "mac_addr.h"
#include "logger.h"
#include "meas.h"
#include "nfc.h"

#define NFC_BUF_SIZE 256
static uint8_t ndef_msg_buf[NFC_BUF_SIZE]; /**< Buffer for NDEF file. */

/**
 * @brief Callback function for handling NFC events.
 */
static void nfc_callback(void *context,
             nfc_t4t_event_t event,
             const uint8_t *data,
             size_t data_length,
             uint32_t flags)
{
    ARG_UNUSED(context);
    ARG_UNUSED(data);
    ARG_UNUSED(flags);

    switch (event) {
    case NFC_T4T_EVENT_FIELD_ON:
        break;

    case NFC_T4T_EVENT_FIELD_OFF:
        break;

    case NFC_T4T_EVENT_NDEF_READ:
        logger_dbg("NFC read\n");
        break;

    case NFC_T4T_EVENT_NDEF_UPDATED:
        logger_dbg("NFC write\n");
        if (data_length > 0)
        {
        }
        break;

    default:
        logger_dbg("NFC unknown\n");
        break;
    }
}

void append_text_record(uint8_t *dest, uint8_t * code, uint8_t * text, int8_t flags)
{
    int initial_len = dest[1] + dest[0] * 256;
    int idx = initial_len + 2;
    int id_length = strlen(code);
    int text_length = strlen(text);
    int rec_length = text_length + 3;

    dest[idx++] = flags;
    dest[idx++] = 1; // type length

    dest[idx++] = rec_length>>24; // record length
    dest[idx++] = rec_length>>16;
    dest[idx++] = rec_length>>8;
    dest[idx++] = rec_length;

    dest[idx++] = id_length;

    dest[idx++] = 'T'; // text record

    strcpy(&dest[idx], code);
    idx += id_length;

    dest[idx++] = 2;
    dest[idx++] = 'e';
    dest[idx++] = 'n';

    strcpy(&dest[idx], text);
    idx += rec_length;
    idx -= 5;

    dest[0] = idx / 256;
    dest[1] = idx % 256;
}


/**
 * @brief   Function for application main entry.
 */
int nfc_init(void)
{
    /* Set up NFC */
    int err = nfc_t4t_setup(nfc_callback, NULL);

    if (err < 0)
    {
        printk("Cannot setup t4t library!\n");
    }
    /* Run Read-Write mode for Type 4 Tag platform */

    nfc_update();

    int len = ndef_msg_buf[1] + ndef_msg_buf[0]*256;

    if (nfc_t4t_ndef_rwpayload_set(ndef_msg_buf, len) < 0)
    {
        printk("Cannot set payload!\n");
    }

    /* Start sensing NFC field */
    if (nfc_t4t_emulation_start() < 0)
    {
        printk("Cannot start emulation!\n");
    }

    return 0;
}
/** @} */

void nfc_update(void)
{
    char content[20];

    memset(ndef_msg_buf, 0, sizeof(ndef_msg_buf));

    uint8_t * pMac = mac_addr_get();
    sprintf(content, "%02x%02x%02x%02x%02x%02x", pMac[5],pMac[4],pMac[3],pMac[2],pMac[1],pMac[0]);
    append_text_record(ndef_msg_buf, "ID", content, 0x89);
    sprintf(content, "%02x:%02x:%02x:%02x:%02x:%02x", pMac[5],pMac[4],pMac[3],pMac[2],pMac[1],pMac[0]);
    append_text_record(ndef_msg_buf, "MAC", content, 0x09);
    sprintf(content, "%d%%", meas_get_battery_percent());
    append_text_record(ndef_msg_buf, "BAT", content, 0x09);
    sprintf(content, "%4.2fC", 0.01f * (float)meas_get_temperature_hundredths_celsius());
    append_text_record(ndef_msg_buf, "TEMP", content, 0x09);
    sprintf(content, "%dmlx", meas_get_light_millilux());
    append_text_record(ndef_msg_buf, "MLUX", content, 0x49);
}
  • Update: I'm using an iPhone 12 and have installed the slightly more helpful NXP TagInfo app.  This tells me 'No NDEF Records', although reading the device using the ACR122 reader and script tool outputs what appears to be valid NDEF records.

    I know that this isn't much to go on but it's all I have at this point.

  • Further update: I've resolved this; it turns out that calling

    nfc_t4t_ndef_rwpayload_set()

    with a buffer_length that's not at least a few bytes more than the actual length of the NDEF message causes the problem.  I haven't got to the bottom of exactly how many bytes extra are needed, but with a message of about 120 bytes I have found that specifying 256 bytes (in my case the size of the available buffer rather than the content) is successful.

Related