Files
xblink/CLAUDE.md
michael 5cd728c298 Pivot from LP5562 to GPIO-direct PWM LEDs, add NTAG5 EH provisioning
LP5562 requires 2.7V min but NFC energy harvesting produces only 1.8V.
New architecture: SAMD21 drives 6 red LEDs directly via TCC0/TCC1
hardware PWM through current-limiting resistors.

Key changes:
- Add TCC PWM driver (src/led/pwm.rs) for 6 GPIO-direct LED channels
- Rewrite pattern engine: software waveform LUTs (sine/triangle/square/
  heartbeat) replace LP5562 hardware execution engines
- Add XBLK v2 EEPROM format with 16-byte pattern entries + playlist
- Add TC4 50Hz ISR for animation, power governor for current budget
- Add NTAG5 EH provisioning: persistent config + session trigger
- Critical finding: SRAM passthrough in persistent EEPROM blocks all
  NFC access when MCU unpowered — CONFIG_1 must be session-only
- Add provision_eh.py PCSC tool for ACR1552 reader
- Update CLAUDE.md and STATUS.md for new architecture

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 10:44:12 -08:00

6.5 KiB

xBlink Development Guide

Embedded Rust firmware for a battery-free, NFC-powered LED implant. The NTAG5Link harvests energy from an NFC field (1.8V) and provides EEPROM storage for LED patterns. A SAMD21E MCU reads patterns from EEPROM, drives 6 red LEDs directly via TCC hardware PWM through current-limiting resistors, and updates duty cycles from a 50Hz timer ISR. The MCU stays in IDLE sleep during animation.

Build Commands

# Build release firmware
cargo build --release

# Flash via UF2 bootloader (XIAO M0: double-tap RST to enter bootloader)
cargo hf2 --release

# Auto-flash: watches for bootloader USB device, flashes when detected
./flash_when_ready.sh

Target

  • Architecture: thumbv6m-none-eabi (ARM Cortex-M0+)
  • Dev board: Seeed XIAO M0 (SAMD21G18A) — will port to bare SAMD21E18A later
  • LEDs: 6x low-Vf red LEDs (Kingbright APTD1608, Vf ~1.7V) driven via TCC PWM + resistors
  • NFC: NTAG5Link Click board (MikroElektronika)
  • LP5562: removed from design (requires 2.7V min, incompatible with 1.8V EH)

Dependencies

Crate Version Purpose
xiao_m0 0.13 Board support package (dev board phase)
panic-halt 0.2 Minimal panic handler for no_std
cortex-m 0.7 Cortex-M runtime, critical-section-single-core feature
embedded-hal 1.0.0 Hardware abstraction traits (I2C, GPIO, delay)

When porting to bare SAMD21E: replace xiao_m0 with atsamd-hal = { version = "0.17", features = ["samd21e"] } and add cortex-m-rt = "0.7".

Project Structure

src/
  main.rs          # Entry point: boot, EEPROM load, TC4 ISR animation, sleep/wake loop
  led/
    mod.rs         # LED module re-exports
    pwm.rs         # TCC0/TCC1 hardware PWM driver for 6 GPIO-direct LEDs
    lp5562.rs      # LP5562 driver (legacy, kept for reference)
  ntag5/           # NTAG5Link I2C slave driver
  pattern/         # XBLK v2 format, software pattern engine, waveform LUTs

Architecture Conventions

  • no_std only: No heap allocation. Fixed-size arrays and stack-only data structures.
  • embedded-hal generics: All hardware drivers must be generic over embedded_hal::i2c::I2c (1.0 traits). Never use concrete HAL types in driver code.
  • Hardware-first: Get hardware working before extracting abstractions. Traits and formats are designed after hands-on experience, not before.
  • Error types: Use Error<E> enum wrapping underlying I2C error with From<E> impl (see LP5562 driver pattern).
  • Register addresses: Place in a reg submodule as pub const values.
  • const fn: Prefer const fn where possible (see EngineCommand builder in LP5562 driver).
  • No unsafe: Avoid unless absolutely necessary for hardware access.

LED PWM Architecture

6 LEDs driven directly by SAMD21 TCC hardware PWM through current-limiting resistors (10-47 ohm).

LED TCC Channel SAMD21E Pin XIAO Pin
0 TCC0 WO[0] PA04 A1
1 TCC0 WO[1] PA05 A2
2 TCC0 WO[2] PA06 A3*
3 TCC0 WO[3] PA07 A4*
4 TCC1 WO[0] PA10 D2
5 TCC1 WO[1] PA11 D3

*PA06/PA07 conflict with I2C on XIAO M0. Dev board uses 4 LEDs (0,1,4,5).

  • Animation: TC4 ISR at 50Hz computes brightness from PatternEngine, writes TCC CC registers
  • Power governor: Proportional scaling ensures total LED current stays within NTAG5 EH budget
  • Sleep: IDLE mode during animation (~0.5mA MCU), STANDBY when no pattern active (~2uA)

Key I2C Address

I2C bus (A4/A5) is used for NTAG5 only (LP5562 removed from design).

Device Address Notes
NTAG5Link 0x54 Default NTP53x2 I2C slave address

The MCU accesses NTAG5Link as a standard I2C slave — plain register read/write, NOT the NFC-side ISO15693 custom commands used by the Python ntag5sensor tooling.

Region Address Range Size Notes
Session registers 0x00-0x06 7 bytes Volatile, runtime config
Configuration 0x37-0x3F Varies Persistent, EEPROM-backed
EH config 0x3D 1 byte Energy harvesting voltage/current
SRAM 0xF8-0xFF 256 bytes 64 x 4-byte blocks, volatile
EEPROM user memory blocks 0x00-0x1FF 2048 bytes 512 x 4-byte blocks

Key config constants (from ../ntag5sensor/vicinity/ntag5link.py):

  • CONFIG_1_USE_CASE_CONF_I2C_SLAVE = 0 << 4 — NTAG5 as I2C slave (our mode)
  • CONFIG_1_ARBITER_MODE_SRAM_PASSTHROUGH = 2 << 2 — SRAM pass-through for NFC↔I2C
  • CONFIG_1_SRAM_ENABLE = 1 << 1 — Enable SRAM access
  • CONFIG_0_EH_MODE_LOW_FIELD_STRENGTH = 2 << 2 — EH mode for phone NFC

Hardware Wiring (Dev Board)

XIAO Pin Connection Function
A1 (PA04) LED 0 + resistor TCC0/WO[0] PWM output
A2 (PA05) LED 1 + resistor TCC0/WO[1] PWM output
A4 (SDA) NTAG5 SDA I2C data
A5 (SCL) NTAG5 SCL I2C clock
D2 (PA10) LED 4 + resistor TCC1/WO[0] PWM output
D3 (PA11) LED 5 + resistor TCC1/WO[1] PWM output
A1 (PA04) NTAG5 FD pin Field detect / SRAM write indication (EIC)
TBD Hall sensor output EIC wake, pattern cycling

Testing

  • Hardware: XIAO M0 + NTAG5Link Click + 4 LEDs with resistors on A1/A2/D2/D3
  • PCSC reader: Use ../ntag5sensor/ Python tooling with ACR1552 reader
  • Phone NFC: VivoKey RawNFC app for SRAM mailbox testing
  • Phone app: DT NFC Identifier for basic tag info

Sibling Projects

  • ../ntag5-samd21-lp562/ — Original LP5562 driver (legacy reference)
  • ../ntag5sensor/ — NTAG5Link command reference (vicinity/ntag5link.py), I2C patterns (vicinity/i2cbase.py)