NFC Reader Firmware Development

Building Custom NFC Reader Applications

| 5 min read

NFC Reader Firmware Development

Firmware for NFC readers must handle RF state machines, protocol timing, NDEF parsing, error recovery, and host communication — often on resource-constrained MCUs. This guide covers the firmware architecture, critical timing requirements, and common pitfalls for embedded NFC development.

Firmware Architecture Layers

A well-structured NFC firmware stack follows a layered model:

┌─────────────────────────────────┐
│   Application Layer             │  Business logic, cloud sync
├─────────────────────────────────┤
│   NDEF / NFC Forum Layer        │  NDEF parse, tag type dispatch
├─────────────────────────────────┤
│   NFC Protocol Layer            │  ISO 14443 T=CL, ISO 15693, FeliCa
├─────────────────────────────────┤
│   RF Abstraction Layer (RFAL)   │  IC-independent RF commands
├─────────────────────────────────┤
│   NFC IC Driver                 │  SPI/I2C register access (PN532, ST25R)
├─────────────────────────────────┤
│   MCU HAL                       │  GPIO, SPI, I2C, Timer, UART
└─────────────────────────────────┘

Each layer should only depend on the layer below. This allows swapping the NFC IC (e.g., PN532 → ST25R3916) by only replacing the IC driver layer.

Critical Timing Requirements

NFC protocols specify tight timing windows that cannot be violated. Missing these in firmware causes intermittent failures that are hard to reproduce:

Protocol Event Timing Requirement Consequence of Violation
ISO 14443 Frame Delay Time (FDT) 1172/128 × bit time ≈ 1.2 ms min Reader sends before tag ready → NAK
ISO 14443 Guard Time ≥ 5 ms between field off and on Tag loses power, resets state
T=CL WTX (Waiting Time eXtension) Must respond within FWTI window Transaction timeout
ISO 15693 t1 (SOF to EOF response) 320.9 µs iso-15693 tag ignores command
NTAG PWD_AUTH timeout ≤ 2 s from activation Tag locks out after N failed attempts

Use hardware timers, not software delay loops, for all NFC timing. On ARM Cortex-M, use SysTick or a dedicated TIM peripheral. Interrupt-driven SPI with DMA is essential — blocking SPI transfers cannot guarantee timing.

Discovery Loop State Machine

The polling discovery loop is the heart of NFC reader firmware:

typedef enum {
    NFC_STATE_IDLE,
    NFC_STATE_POLLING_A,
    NFC_STATE_POLLING_B,
    NFC_STATE_POLLING_V,
    NFC_STATE_TAG_ACTIVE,
    NFC_STATE_TAG_LOST,
    NFC_STATE_ERROR
} NfcReaderState;

void nfc_discovery_loop(void) {
    switch (state) {
        case NFC_STATE_IDLE:
            rf_field_on();
            state = NFC_STATE_POLLING_A;
            break;
        case NFC_STATE_POLLING_A:
            if (iso14443a_poll(&tag_a) == ERR_NONE) {
                state = NFC_STATE_TAG_ACTIVE;
            } else {
                state = NFC_STATE_POLLING_B;
            }
            break;
        // ... additional states
        case NFC_STATE_TAG_ACTIVE:
            ndef_read_message(&tag_a, &ndef_buf);
            on_tag_detected_callback(&ndef_buf);
            state = NFC_STATE_TAG_LOST;
            break;
    }
}

The loop should be called from a periodic timer ISR or a dedicated RTOS task, not from the main loop with blocking delays.

NDEF Parsing in C

For resource-constrained MCUs, implement a minimal NDEF parser that avoids dynamic memory allocation:

typedef struct {
    uint8_t tnf;
    uint8_t type_len;
    uint32_t payload_len;
    uint8_t id_len;
    const uint8_t *type;
    const uint8_t *payload;
    bool is_mb;  // Message Begin
    bool is_me;  // Message End
    bool is_sr;  // Short Record
} NdefRecord;

NfcStatus ndef_parse_record(const uint8_t *buf, size_t buf_len,
                             NdefRecord *record, size_t *consumed) {
    if (buf_len < 3) return NFC_ERR_BUFFER_TOO_SMALL;
    uint8_t flags = buf[0];
    record->tnf       = flags & 0x07;
    record->is_mb     = (flags >> 7) & 1;
    record->is_me     = (flags >> 6) & 1;
    record->is_sr     = (flags >> 4) & 1;
    record->type_len  = buf[1];
    record->payload_len = record->is_sr ? buf[2] :
                         ((uint32_t)buf[2]<<24|(uint32_t)buf[3]<<16|
                          (uint32_t)buf[4]<<8|buf[5]);
    // ... offset calculation and pointer assignment
    return NFC_OK;
}

Avoid using malloc — pre-allocate a fixed-size NDEF buffer (typically 256–1024 bytes depending on your tag memory) and parse in-place.

Anti-Collision Handling

When multiple tags are in the RF field, the anti-collision loop must arbitrate cleanly. For ISO 14443-3A (NFC-A):

  1. Reader sends REQA (0x26, 7-bit frame)
  2. All tags respond simultaneously — a collision occurs if UIDs differ
  3. Reader sends SELECT with a bit string covering only non-colliding prefix bits
  4. Colliding tags that don't match the prefix go silent
  5. Repeat until single uid resolved

Most NFC IC drivers handle this automatically. The firmware only needs to handle the case where a second tag appears mid-transaction (causing the active tag to lose power in some configurations).

Power Management

NFC readers consume ~100 mA with RF on. In battery-powered devices, duty-cycle the RF field:

Mode RF Field Current Use Case
Continuous poll Always on ~100 mA Mains-powered reader
Low-power poll 100 ms on / 900 ms off ~12 mA avg Battery, ≤ 1 s latency
Wake-on-approach Low-power sensor triggers RF ~2 mA avg Long battery life

For wake-on-approach, some ICs (ST25R3916) offer a dedicated low-power card detection mode that draws < 1 mA and asserts an interrupt when a tag enters the field.

Error Handling and Recovery

Error Recovery Action Retry Limit
RF transmission error Retry command once 2 retries
No response (timeout) Cycle RF field off/on 3 cycles
Protocol error (wrong response) De-select and re-select tag 2 retries
FIFO overflow Increase buffer; log warning No retry
I2C/SPI bus error Reinitialise IC 3 attempts, then alarm

Log all errors with structlog or a minimal UART debug stream in development. In production, report error counts to your backend via the application layer.

Use the Chip Selector to evaluate which nfc-controller IC fits your firmware environment, and the Read Range Estimator to predict field strength from your chosen nfc-antenna design. The Compatibility Checker verifies that your firmware-controlled reader handles all required passive-tag types.

See Also

Terms in This Guide