# xblink Development Plan ## Overview xblink is an NFC-powered LED implant. Development proceeds in phases, starting with dev board prototyping (XIAO M0 + LP5562EVM + NTAG5Link Click) and progressing to a custom SAMD21E PCB suitable for implantation. LED controller code is modular via a `LedController` trait — the LP5562 is the primary target, but the architecture supports other LED driver ICs and simple GPIO PWM for single-color LEDs. --- ## Phase 1: LP5562 Driver Integration **Goal**: Get LP5562EVM running RGBW LED patterns from the XIAO M0. **Hardware**: XIAO M0 ↔ LP5562EVM via I2C (A4/A5) + EN on D0/A0. ### Tasks 1. **Copy LP5562 driver** from `../ntag5-samd21-lp562/src/lp5562.rs` into `src/led/lp5562.rs`. This is a complete 757-line driver covering all LP5562 features: direct PWM, current control, engine programming, LED mapping, power-save, and clock config. It is generic over `embedded_hal::i2c::I2c` (1.0). 2. **Define `LedController` trait** in `src/led/mod.rs`: ```rust pub trait LedController { type Error; fn set_channel(&mut self, channel: u8, brightness: u8) -> Result<(), Self::Error>; fn set_all_channels(&mut self, values: &[u8]) -> Result<(), Self::Error>; fn all_off(&mut self) -> Result<(), Self::Error>; fn channel_count(&self) -> u8; fn supports_engines(&self) -> bool; fn load_pattern(&mut self, pattern: &[u8]) -> Result<(), Self::Error>; fn stop_pattern(&mut self) -> Result<(), Self::Error>; } ``` 3. **Add GPIO EN pin** to LP5562 wrapper. The LP5562EVM EN pin is connected to D0/A0 for hardware power control: ```rust pub struct Lp5562Gpio { lp: Lp5562, en_pin: EN, } ``` This wrapper manages the hardware enable alongside the I2C driver, allowing complete power-down for energy savings. 4. **Test sequences**: - Direct PWM: set individual channel colors, verify RGBW output - Current control: set 2-5mA per channel (suitable for EH power budget) - Engine programs: breathing pattern (ramp up/down with branch loop) - Engine programs: RGB color cycle (3 engines with synchronized triggers) - Engine programs: heartbeat pulse (fast ramp, slow decay) ### Reference Code The sibling project's `main.rs` (lines 49-114) provides a complete working example: - LP5562 init: `enable()` → 1ms delay → `init_direct_control(Internal)` → `set_all_current(100, 100, 100, 100)` - Color cycling loop with `set_all_pwm()` and delay - This exact pattern works with the LP5562EVM --- ## Phase 2: NTAG5Link Driver (MCU-side I2C) **Goal**: Read and write NTAG5Link EEPROM and SRAM from the SAMD21 over I2C. **Hardware**: XIAO M0 ↔ NTAG5Link Click via I2C (shared bus with LP5562). **Important**: The MCU accesses the NTAG5Link as a standard I2C slave at address 0x54. This is plain register read/write — completely different from the NFC-side ISO15693 custom commands (0xD2 READ_SRAM, 0xD4 WRITE_I2C, etc.) used by the Python `ntag5sensor` tooling. ### Driver Design ```rust pub struct Ntag5Link { i2c: I2C, addr: u8, // 0x54 default } ``` **EEPROM access** (2048 bytes, 512 blocks of 4 bytes each): - Read: I2C write 2-byte block address, then I2C read 4 bytes - Write: I2C write 2-byte block address + 4 bytes data - Bulk read: iterate blocks into caller-provided buffer **SRAM access** (256 bytes, 64 blocks of 4 bytes): - Same read/write pattern as EEPROM, different address range (0xF8-0xFF) - Used as NFC ↔ I2C mailbox for phone communication **Session registers** (7 bytes at 0x00-0x06): - Single-byte address, read/write 1 byte - Include EH status, field detection, arbiter mode control ### NTAG5Link Configuration Must configure NTAG5Link for our use case (via NFC-side config write before first use, or via I2C config registers): | Setting | Value | Constant (from ntag5link.py) | |---------|-------|------------------------------| | 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 register bits 2:1 = 10 | | EH current | 9.0mA+ | EH_CONFIG register bits 6:4 | --- ## Phase 3: Pattern Storage Format **Goal**: Define a compact binary format for LED patterns stored in NTAG5Link EEPROM. ### Format Specification ``` Pattern Header (16 bytes): [0-3] Magic: 0x58 0x42 0x4C 0x4B ("XBLK") [4] Version: 0x01 [5] Flags: bit 0: has engine 1 program bit 1: has engine 2 program bit 2: has engine 3 program bit 3: has direct color values bit 4-7: reserved [6] Number of pattern entries (1-255) [7] Current setting for all channels (0-255 → 0-25.5mA) [8-9] Reserved [10-13] Direct color values (R, G, B, W) — used when flag bit 3 set [14-15] CRC-16 of bytes 0-13 + all entry data Pattern Entry (2 + N*2 bytes each): [0] Entry type: 0x01 = Engine program (LP5562 engine commands) 0x02 = Direct PWM keyframes [1] Length in 16-bit words (1-16 for engine programs) [2..N] Command words (big-endian, LP5562 engine format) Engine Assignment Block (4 bytes, after all entries): [0] Engine 1 → entry index (0xFF = unused) [1] Engine 2 → entry index (0xFF = unused) [2] Engine 3 → entry index (0xFF = unused) [3] LED mapping byte (LP5562 LED_MAP register 0x70 format) ``` **Size**: A typical 3-engine pattern with 16 commands each: 16 + 3×(2 + 32) + 4 = **122 bytes**. Even complex multi-pattern configs fit comfortably in the 2048-byte EEPROM. ### Built-in Default Patterns Hardcoded in firmware as fallbacks: - **Solid color**: Direct PWM, no engines needed - **Breathing**: 1 engine — ramp up, ramp down, branch to start - **RGB cycle**: 3 engines — staggered phase with trigger synchronization - **Heartbeat**: 1 engine — fast ramp up, slow exponential decay - **Rainbow fade**: 3 engines — slow ramp with offset phases --- ## Phase 4: Power Management **Goal**: Minimize MCU power draw so LP5562 gets maximum current budget from energy harvesting. ### Sleep Architecture 1. After boot and LP5562 engine programming, enter SAMD21E **STANDBY** mode (~5µA) 2. LP5562 runs autonomously using its internal 256Hz oscillator — no MCU involvement 3. Wake sources via External Interrupt Controller (EIC): - **NTAG5Link FD pin**: NFC field detection or SRAM write indication - **Hall sensor GPIO**: Recovery trigger (magnet proximity) ### Power Budget Analysis | Component | Active | Sleep/Idle | Notes | |-----------|--------|------------|-------| | SAMD21E | 3-5mA @ 48MHz | ~5µA standby | Sleep ASAP after boot | | LP5562 | 0.5mA quiescent + LED current | Power-save mode | LEDs dominate budget | | LP5562 LEDs (4ch) | 2-5mA × 4 = 8-20mA | 0mA (PS_EN) | Must stay within EH budget | | NTAG5Link | ~100µA | ~1µA standby | Minimal contributor | | **Total (steady state)** | **9-21mA** | **~5µA** | EH max: ~12.5mA | **Key insight**: The system budget is tight. At 12.5mA EH and ~0.6mA quiescent (LP5562 + NTAG5), that leaves ~12mA for LEDs. At 3mA per channel, 4 channels = 12mA — right at the limit. Consider: - Running fewer channels simultaneously - Using LP5562 engine PWM duty cycling to reduce average current - Pulsed patterns (breathing, heartbeat) inherently use less average current than solid-on ### Energy Harvesting Configuration Set via NTAG5Link config registers (one-time setup via PCSC reader): - Voltage: 3.0V (EH_CONFIG bits 2:1 = 10) - Current threshold: start at 9.0mA, increase if patterns are dim - Mode: Low field strength (CONFIG_0 bits 3:2 = 10) — better for phone NFC - FD pin: SRAM write indication mode (for NFC communication wake) --- ## Phase 5: NFC Communication Protocol **Goal**: Enable phone-to-MCU communication through NTAG5Link SRAM mailbox. ### SRAM Mailbox Protocol The NTAG5Link's 256-byte SRAM is accessible from both NFC (phone) and I2C (MCU) sides. We use it as a command/response mailbox. ``` Command Frame (NFC → MCU, written to SRAM by phone): [0] Command byte: 0x01 = Write pattern (followed by pattern data) 0x02 = Read current pattern info 0x03 = Set direct color (4 bytes RGBW follow) 0x04 = Get firmware version 0x05 = Enter bootloader (reserved) [1] Sequence number (for request/response matching) [2-3] Payload length (little-endian, max 252) [4-255] Payload data Response Frame (MCU → NFC, written to SRAM by MCU): [0] Status: 0x00 = OK 0x01 = Error (error code in payload) 0x02 = Busy 0x03 = Ready for next chunk [1] Sequence number (echoed from command) [2-3] Payload length (little-endian) [4-255] Payload data ``` ### Chunked Transfer For patterns larger than 252 bytes (unlikely but supported): - Command 0x01 payload starts with chunk header: `[chunk_index: u8, total_chunks: u8, ...]` - MCU accumulates chunks in RAM - After final chunk, MCU writes complete pattern to EEPROM - MCU responds with 0x03 (ready for next chunk) or 0x00 (complete) ### Communication Flow 1. Phone writes command frame to NTAG5Link SRAM via NFC 2. NTAG5Link asserts FD pin (SRAM write indication) 3. SAMD21E wakes from STANDBY via EIC interrupt 4. MCU reads SRAM via I2C, processes command 5. MCU pauses LP5562 if needed (set engines to Hold) 6. MCU performs action (update pattern, read info, etc.) 7. MCU writes response frame to SRAM via I2C 8. MCU resumes LP5562 engines if paused 9. MCU returns to STANDBY 10. Phone reads response from SRAM via NFC --- ## Phase 6: Recovery and Safe Mode **Goal**: Provide a way to recover from bad patterns or firmware issues post-implantation. ### Hall Sensor Recovery A hall effect sensor connected to a SAMD21E GPIO pin (EIC-capable) provides recovery triggering by holding a magnet near the implant. **Boot-time check**: 1. On every boot, before reading EEPROM, check hall sensor GPIO state 2. If asserted (magnet present): enter recovery mode 3. If not asserted: normal boot, read EEPROM pattern **Recovery mode behavior**: - Load hardcoded default pattern: solid white at 25% brightness (~1.5mA/ch) - Do NOT read EEPROM pattern (in case it's corrupted or causes issues) - Set recovery flag in NTAG5Link SRAM so phone app can detect recovery state - Remain awake (do not enter STANDBY) to allow firmware update operations - Optionally: blink onboard LED in recognizable recovery pattern **Runtime trigger**: Hall sensor also serves as EIC wake source during sleep. Possible uses: - Cycle to next stored pattern - Enter diagnostic mode - Force re-read of EEPROM pattern --- ## Phase 7: Firmware Update Strategy ### Analysis | Approach | Pros | Cons | Feasibility | |----------|------|------|-------------| | **NFC pass-through bootloader** | True OTA, no physical access | Complex, slow (256B SRAM chunks for 256KB flash = thousands of transactions), custom bootloader needed | Possible but hard | | **UF2 bootloader (USB)** | Fast, well-established, standard tooling | Requires USB access — impossible once implanted | Dev phase only | | **Hybrid (recommended)** | Best of both: UF2 for dev, NFC OTA deferred | Two bootloader systems to maintain | Practical | ### Recommended Approach 1. **Development phase**: Use UF2 bootloader on XIAO M0 for rapid iteration. Flash with `cargo hf2 --release`. 2. **Pre-implant**: Thoroughly test final firmware via UF2. Ensure pattern system works perfectly since that's the primary update mechanism post-implant. 3. **Post-implant updates**: The pattern storage in NTAG5Link EEPROM provides the most common update path — new patterns without new firmware. This covers the majority of use cases. 4. **Future NFC OTA**: Implement minimal NFC pass-through bootloader in a later phase if needed. Would require: - ~8KB reserved flash for bootloader code (BOOTPROT fuse) - Custom chunked transfer protocol over SRAM mailbox - CRC verification before committing new firmware - Fallback to known-good firmware on verification failure --- ## Phase 8: Custom PCB (SAMD21E) ### Porting from XIAO M0 to SAMD21E18A | Change | From (XIAO M0) | To (Custom PCB) | |--------|----------------|------------------| | BSP crate | `xiao_m0 = "0.13"` | `atsamd-hal = { version = "0.17", features = ["samd21e"] }` | | Runtime | Included in BSP | Add `cortex-m-rt = "0.7"` | | Linker script | Included in BSP | Custom `memory.x` for SAMD21E18A | | Package | SAMD21G18A (48-pin TQFP) | SAMD21E18A (32-pin QFN, 5×5mm) | | SERCOM | SERCOM0 (BSP-configured) | Remap to available SERCOM on E variant | | Pins | BSP pin names (a0, a4, a5, led0) | Direct PAC pin references | ### PCB Design Targets | Component | Package | Size | |-----------|---------|------| | SAMD21E18A | QFN-32 | 5×5mm | | LP5562 | QFN-16 | 3×3mm | | NTAG5Link (NTP53x2) | XSON-8 | 1.45×1mm | | Hall sensor | SOT-23 | 2.9×1.6mm | | NFC antenna | PCB trace loop | ~12-15mm diameter | Target form factor: disc under 15mm diameter or capsule under 12×30mm. --- ## Phase 9: Companion App ### React Native App - NFC communication via platform-native APIs (Android NFC, iOS Core NFC) - ISO15693 NTAG5Link access for SRAM mailbox protocol - Pattern editor: visual LED color/pattern picker - Pattern upload: serialize to XBLK format, send via SRAM mailbox - Status display: firmware version, current pattern info, recovery state - Testing: use VivoKey RawNFC and DT NFC Identifier as reference --- ## 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 | Always pause LP5562 before NTAG5 I2C access, resume after | | EEPROM write endurance (~100K cycles) | Low | Use SRAM for transient communication, EEPROM only for pattern persistence | | Boot time too slow for user experience | Low | Estimated ~15-20ms total — fast enough to appear instant | | NFC coupling distance too short | Medium | Optimize antenna, use low field strength EH mode, accept 1-3cm range | | Firmware bug post-implant with no OTA | High | Hall sensor recovery, thorough pre-implant testing, pattern-only updates cover most needs |