Files
xblink/docs/DEVELOPMENT_PLAN.md
michael e4195d2583 Add LP5562 driver, M1 smoke test, and milestone-based plan
- Integrate LP5562 driver from sibling project (ntag5-samd21-lp562)
  with full doc comments restored
- Update main.rs with complete M1 test: I2C init, GPIO EN, RGBW cycle
- Rewrite DEVELOPMENT_PLAN.md from waterfall to milestone-based groups
  organized by hardware availability (Groups A-E)
- Rewrite STATUS.md with milestone checklist tracking
- Add LP5562 timing constraints and flash script docs to CLAUDE.md
- Add flash_when_ready.sh for auto-flash dev workflow
- Add brainstorm design doc for plan redesign rationale

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

12 KiB
Raw Blame History

xblink Development Plan

Overview

xblink is an NFC-powered LED implant. Development is organized into small milestones grouped by hardware requirements. Each milestone is scoped to roughly one session of work. Abstractions (traits, pattern formats) are deferred until we have hands-on experience with the hardware they'll abstract.

Guiding Principles

  • Hardware-first: Get real LEDs blinking before designing abstractions
  • YAGNI: No traits, formats, or protocols until we need them
  • Small milestones: Each should produce a flashable, testable result
  • Test equipment: Multimeter for power budget, logic analyzer (optional) for I2C debugging

Group A — LP5562 Only

Hardware needed: XIAO M0 + LP5562EVM + mini-USB cable

M1: LED Smoke Test

Goal: Verify LP5562 direct PWM control works over I2C.

Copy the proven LP5562 driver from ../ntag5-samd21-lp562/src/lp5562.rs into the project. Wire up I2C and GPIO EN in main.rs. Light up each RGBW channel to confirm wiring and driver work.

Tasks:

  1. Copy lp5562.rssrc/led/lp5562.rs
  2. Create src/led/mod.rs with pub mod lp5562; re-export
  3. Update main.rs: I2C init (A4/A5, 400kHz), GPIO EN on D0/A0, LP5562 enable → 1ms delay → init_direct_control(Internal)set_all_current(100,100,100,100) → cycle through RGBW
  4. Flash and verify all 4 LED channels light up

Reference: Sibling project main.rs lines 40-114 — proven working sequence with LP5562EVM.

Done when: All 4 RGBW LEDs cycle on the LP5562EVM.

M2: Engine Patterns

Goal: Program LP5562 execution engines to run patterns autonomously — the core value proposition.

After engine programming, the MCU can sleep while LP5562 keeps running patterns on its own internal oscillator. This milestone proves the architecture works.

Tasks:

  1. Build engine programs using EngineCommand builder (already in driver):
    • Breathing: set_pwm(0) → ramp up → ramp down → branch(0, 0) (infinite loop)
    • Heartbeat: set_pwm(0) → fast ramp up → slow ramp down → wait → branch(0, 0)
    • Color cycle: 3 engines with staggered phases, synchronized via trigger()
  2. Map LED channels to engines via set_led_mapping()
  3. Load programs, set engines to Run, verify LEDs animate without MCU loop
  4. Verify MCU can stop_engine() and resume with a different pattern

Done when: LP5562 runs a breathing pattern while main() sits in an idle loop (no delay_ms driving the LEDs).

M3: Hardcoded Pattern Library

Goal: Build a set of const patterns in firmware, selectable at boot.

This gives us real pattern data to inform the EEPROM storage format later (M5), without designing that format prematurely.

Tasks:

  1. Define 4-5 patterns as const or static data:
    • Solid RGBW (direct PWM, no engines)
    • Breathing (single channel, 1 engine)
    • Heartbeat pulse (1 engine, asymmetric ramp)
    • RGB color cycle (3 engines, trigger sync)
    • Rainbow fade (3 engines, staggered offset)
  2. Simple pattern selector in main.rs (e.g., cycle through patterns on each boot, or use a compile-time const)
  3. Reduce LED current to 20-50 (2-5mA/ch) to simulate EH power budget

Done when: Can flash different patterns by changing a const, each runs autonomously on LP5562.


Hardware needed: + NTAG5Link Click board + I2C jumper wire

M4: NTAG5 EEPROM Read/Write

Goal: Communicate with NTAG5Link over I2C as a slave device.

Important: The MCU accesses NTAG5Link as a standard I2C slave at address 0x54 — plain register read/write. This is NOT the NFC-side ISO15693 custom commands used by the Python ntag5sensor tooling.

Tasks:

  1. Write src/ntag5/mod.rsNtag5Link<I2C> struct, generic over embedded_hal::i2c::I2c
  2. Write src/ntag5/registers.rs — register map constants from ntag5link.py
  3. Implement EEPROM block read/write (2-byte block address, 4 bytes per block)
  4. Implement session register read (single-byte address)
  5. Test: write known data to EEPROM via PCSC reader (ntag5sensor), read back on MCU, display result as LP5562 colors (visual verification)

NTAG5Link config (one-time setup via PCSC reader):

Setting Value Constant
Use case I2C slave CONFIG_1_USE_CASE_CONF_I2C_SLAVE = 0 << 4
Arbiter mode SRAM pass-through CONFIG_1_ARBITER_MODE_SRAM_PASSTHROUGH = 2 << 2
SRAM enable On CONFIG_1_SRAM_ENABLE = 1 << 1
EH mode Low field strength CONFIG_0_EH_MODE_LOW_FIELD_STRENGTH = 2 << 2
EH voltage 3.0V EH_CONFIG bits 2:1 = 10

Done when: MCU reads EEPROM data written by PCSC reader and displays it as LED colors.

M5: Boot-from-EEPROM

Goal: Design pattern binary format (informed by M2-M3 experience) and boot from stored patterns.

By now we know exactly what engine programs look like in practice, so the format design is grounded in real usage.

Tasks:

  1. Design binary pattern format — header + engine programs + LED mapping (see format spec below)
  2. Write src/pattern/mod.rs — deserializer (parse EEPROM bytes → engine programs)
  3. Write src/pattern/engine.rs — convert parsed pattern → LP5562 engine program load sequence
  4. Update main.rs boot sequence: read EEPROM → parse pattern → program LP5562 → idle
  5. Write a Python script (or extend ntag5sensor) to serialize patterns to EEPROM via PCSC
  6. Test: write pattern via PCSC, power-cycle, verify LP5562 runs the pattern

Pattern format (preliminary — will be refined based on M2-M3 learnings):

Header (16 bytes):
  [0-3]   Magic: "XBLK" (0x58 0x42 0x4C 0x4B)
  [4]     Version: 0x01
  [5]     Flags (which engines used, direct color mode)
  [6]     Number of pattern entries
  [7]     LED current setting (0-255 → 0-25.5mA)
  [8-13]  Reserved / direct RGBW color values
  [14-15] CRC-16

Entry (2 + N*2 bytes):
  [0]     Type (0x01 = engine program)
  [1]     Length in 16-bit words (1-16)
  [2..]   Engine command words (big-endian)

Assignment (4 bytes, after entries):
  [0-2]   Engine 1/2/3 → entry index (0xFF = unused)
  [3]     LED_MAP register value

Typical 3-engine pattern: 16 + 3×34 + 4 = 122 bytes (fits easily in 2048B EEPROM).

Done when: MCU boots, reads pattern from EEPROM, LP5562 runs it autonomously.

M6: SRAM Mailbox

Goal: Phone writes a new pattern via NFC, MCU picks it up and reprograms LP5562.

Tasks:

  1. Write src/ntag5/sram.rs — SRAM block read/write (64 blocks × 4 bytes at 0xF8-0xFF)
  2. Design SRAM mailbox command/response protocol:
    Command (NFC → MCU):  [cmd, seq, len_lo, len_hi, payload...]
    Response (MCU → NFC): [status, seq, len_lo, len_hi, payload...]
    Commands: 0x01=write_pattern, 0x02=get_info, 0x03=set_color, 0x04=get_version
    
  3. Configure NTAG5Link FD pin for SRAM write indication
  4. Implement polling or interrupt-based command detection (EIC wake comes in M7)
  5. MCU receives pattern → stores to EEPROM → reprograms LP5562
  6. Test with PCSC reader first, then VivoKey RawNFC app

Done when: Pattern update works via NFC without power-cycling the MCU.


Group C — Power + Recovery

Hardware needed: + Hall effect sensor + multimeter

M7: Sleep/Wake

Goal: MCU enters STANDBY after programming LP5562, wakes on NFC activity.

Tasks:

  1. Configure SAMD21 EIC for NTAG5Link FD pin (wake source)
  2. After LP5562 engine programming, enter STANDBY (~5µA)
  3. On EIC interrupt (FD pin), wake, process SRAM mailbox, return to sleep
  4. Verify LP5562 keeps running patterns during MCU sleep
  5. Measure current with multimeter: active vs standby vs total system

Power budget reference:

Component Active Standby
SAMD21E 3-5mA ~5µA
LP5562 quiescent ~0.5mA ~0.5mA
LP5562 LEDs (4ch @ 3mA) ~12mA ~12mA
NTAG5Link ~100µA ~1µA
Total ~17mA ~12.5mA

Key insight: LED current dominates. MCU sleep saves ~4mA. Total must stay under EH max (~12.5mA), so LED current must be limited.

Done when: MCU sleeps after boot, LP5562 runs patterns, MCU wakes on NFC to accept new pattern.

M8: Recovery Mode

Goal: Hall sensor provides safe mode entry for post-implant recovery.

Tasks:

  1. Wire hall sensor to EIC-capable GPIO pin
  2. Boot-time check: if hall sensor asserted → recovery mode
  3. Recovery behavior: load hardcoded solid white (low brightness), skip EEPROM, stay awake
  4. Set recovery flag in SRAM so phone app can detect it
  5. Add hall sensor as runtime EIC wake source (e.g., cycle patterns)

Done when: Holding magnet near hall sensor at boot triggers recovery mode with default LED pattern.

M9: Power Characterization

Goal: Measure real power consumption and tune EH settings.

Tasks:

  1. Measure current draw: MCU active, MCU standby, LP5562 at various current settings
  2. Test NTAG5Link EH output with phone NFC at different current thresholds
  3. Find optimal balance: maximum LED brightness within EH budget
  4. Document actual power budget (replaces estimates in this plan)

Done when: We have real numbers and documented EH configuration that works reliably.


Group D — Abstraction + Polish

Prerequisite: Groups A-C working. Now we know the real interface needs.

M10: LedController Trait

Goal: Extract trait from proven LP5562 usage patterns.

After M1-M9, we know exactly what set_channel, load_pattern, and stop_pattern actually need to look like. Design the trait from real experience, not speculation.

Tasks:

  1. Define LedController trait in src/led/mod.rs based on actual usage in main.rs
  2. Implement for LP5562 wrapper (including GPIO EN pin)
  3. Refactor main.rs to use trait-based API
  4. Verify everything still works

M11: SimplePwm Driver

Goal: Single-color LED support via GPIO PWM — only if we have hardware to test.

Implement LedController for direct GPIO PWM. Skip this milestone entirely if we don't have single-color LEDs to validate against.


Group E — Future

Deferred until Groups A-D are solid.

Custom PCB (SAMD21E)

  • Port from xiao_m0 BSP to atsamd-hal with samd21e feature
  • Add cortex-m-rt = "0.7", custom memory.x linker script
  • Remap SERCOM and pin assignments for 32-pin QFN
  • PCB design: SAMD21E (5×5mm) + LP5562 (3×3mm) + NTAG5Link (1.45×1mm) + hall sensor + antenna
  • Target: disc <15mm diameter or capsule <12×30mm

Companion App (React Native)

  • NFC via platform APIs (Android NFC, iOS Core NFC)
  • ISO15693 access to NTAG5Link SRAM mailbox
  • Pattern editor UI + upload
  • Status display (firmware version, current pattern, recovery state)

Firmware Update (NFC OTA)

  • Custom bootloader in protected flash (~8KB, BOOTPROT fuse)
  • Chunked firmware transfer via SRAM mailbox (256B per chunk)
  • CRC verification, fallback to known-good firmware
  • Development: use UF2 via USB; NFC OTA only if needed post-implant

Technical Risks

Risk Severity Mitigation
Power budget too tight for 4ch LED High Limit current to 2-3mA/ch, use pulsed patterns, run fewer channels
I2C bus contention (LP5562 + NTAG5) Medium Pause LP5562 engines before NTAG5 I2C access, resume after
EEPROM write endurance (~100K cycles) Low SRAM for transient comms, EEPROM only for pattern persistence
Boot time too slow Low Estimated ~15-20ms — appears instant to user
NFC coupling distance too short Medium Optimize antenna, low field strength EH mode, accept 1-3cm
Firmware bug post-implant High Hall sensor recovery, thorough pre-implant testing, pattern-only updates