diff --git a/src/main.rs b/src/main.rs index 04242e3..f3334d9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,6 +19,7 @@ mod ntag5; mod pattern; use led::lp5562::{ClockSource, Lp5562, DEFAULT_ADDRESS}; use ntag5::Ntag5Link; +use ntag5::sram::MailboxState; use pattern::LedMode; /// Blink the on-board LED n times (active-low: low=on, high=off) @@ -59,6 +60,9 @@ fn main() -> ! { lp_en.set_high().unwrap(); delay.delay_ms(10u32); // Let LP5562 power stabilize + // NTAG5 FD pin on A1 (PA04) — input with pull-up (FD is open-drain) + let fd_pin = pins.a1.into_pull_up_input(); + // I2C on A4 (SDA) / A5 (SCL) at 400 kHz let i2c = bsp::i2c_master( &mut clocks, @@ -113,6 +117,9 @@ fn main() -> ! { } delay.delay_ms(500u32); + // Configure FD pin for SRAM-write-by-RF indication + let _ = ntag.configure_fd_sram_write(); // Best-effort, non-fatal + // Try reading XBLK library header from upper 1K let mut header_buf = [0u8; pattern::HEADER_SIZE]; let eeprom_ok = ntag.read_memory(pattern::LIBRARY_BASE_BLOCK, &mut header_buf).ok() @@ -131,20 +138,56 @@ fn main() -> ! { match eeprom_pattern { Some(pat) => { - // Reclaim I2C for LP5562 - let i2c = ntag.release(); - let mut lp = Lp5562::new(i2c, DEFAULT_ADDRESS); - // EEPROM pattern loaded — 3 fast blinks blink(&mut led, &mut delay, 3, 80); - if pattern::load_pattern(&mut lp, &pat, led_current, &mut delay).is_err() { - blink(&mut led, &mut delay, 10, 50); + // Load pattern into LP5562 (swap I2C to LP5562 then back) + { + let i2c = ntag.release(); + let mut lp = Lp5562::new(i2c, DEFAULT_ADDRESS); + if pattern::load_pattern(&mut lp, &pat, led_current, &mut delay).is_err() { + blink(&mut led, &mut delay, 10, 50); + } + let i2c = lp.release(); + ntag = Ntag5Link::new(i2c, ntag5::DEFAULT_ADDRESS); } - // LP5562 runs autonomously — MCU idles (future: STANDBY) + // Mailbox polling loop — LP5562 runs autonomously, + // MCU polls FD pin for NFC commands + let mut mbox = MailboxState::new(); loop { - delay.delay_ms(60000u32); + delay.delay_ms(200u32); + if fd_pin.is_low().unwrap_or(false) { + let cmd_ok = ntag5::sram::process_command( + &mut ntag, &mut mbox, &mut delay, + ); + if matches!(cmd_ok, Ok(true)) { + // Reload LP5562 from current EEPROM state + let mut hdr_buf = [0u8; pattern::HEADER_SIZE]; + if ntag.read_memory(pattern::LIBRARY_BASE_BLOCK, &mut hdr_buf).is_ok() { + if let Some(hdr) = pattern::parse_header(&hdr_buf) { + led_current = hdr.current; + let pat_block = pattern::LIBRARY_BASE_BLOCK + + (pattern::HEADER_SIZE as u16 / 4) + + (hdr.active_index as u16 + * (pattern::PATTERN_ENTRY_SIZE as u16 / 4)); + let mut pat_buf = [0u8; pattern::PATTERN_ENTRY_SIZE]; + if ntag.read_memory(pat_block, &mut pat_buf).is_ok() { + if let Some(new_pat) = pattern::parse_pattern_entry(&pat_buf) { + // Swap I2C to LP5562 for reprogramming + let i2c = ntag.release(); + let mut lp = Lp5562::new(i2c, DEFAULT_ADDRESS); + let _ = pattern::load_pattern( + &mut lp, &new_pat, led_current, &mut delay, + ); + let i2c = lp.release(); + ntag = Ntag5Link::new(i2c, ntag5::DEFAULT_ADDRESS); + } + } + } + } + } + } } } None => { @@ -219,17 +262,52 @@ fn main() -> ! { let loaded = ntag.read_memory(pat_base_block, &mut pat_buf).ok() .and_then(|()| pattern::parse_pattern_entry(&pat_buf)); - let i2c = ntag.release(); - let mut lp = Lp5562::new(i2c, DEFAULT_ADDRESS); - - if let Some(pat) = loaded { - if pattern::load_pattern(&mut lp, &pat, led_current, &mut delay).is_err() { - blink(&mut led, &mut delay, 10, 50); + // Load into LP5562 (swap I2C), then swap back for mailbox + { + let i2c = ntag.release(); + let mut lp = Lp5562::new(i2c, DEFAULT_ADDRESS); + if let Some(pat) = loaded { + if pattern::load_pattern(&mut lp, &pat, led_current, &mut delay).is_err() { + blink(&mut led, &mut delay, 10, 50); + } } + let i2c = lp.release(); + ntag = Ntag5Link::new(i2c, ntag5::DEFAULT_ADDRESS); } + // Mailbox polling loop (same as EEPROM-loaded path) + let mut mbox = MailboxState::new(); loop { - delay.delay_ms(60000u32); + delay.delay_ms(200u32); + if fd_pin.is_low().unwrap_or(false) { + let cmd_ok = ntag5::sram::process_command( + &mut ntag, &mut mbox, &mut delay, + ); + if matches!(cmd_ok, Ok(true)) { + let mut hdr_buf = [0u8; pattern::HEADER_SIZE]; + if ntag.read_memory(pattern::LIBRARY_BASE_BLOCK, &mut hdr_buf).is_ok() { + if let Some(hdr) = pattern::parse_header(&hdr_buf) { + led_current = hdr.current; + let pat_block = pattern::LIBRARY_BASE_BLOCK + + (pattern::HEADER_SIZE as u16 / 4) + + (hdr.active_index as u16 + * (pattern::PATTERN_ENTRY_SIZE as u16 / 4)); + let mut pat_buf2 = [0u8; pattern::PATTERN_ENTRY_SIZE]; + if ntag.read_memory(pat_block, &mut pat_buf2).is_ok() { + if let Some(new_pat) = pattern::parse_pattern_entry(&pat_buf2) { + let i2c = ntag.release(); + let mut lp = Lp5562::new(i2c, DEFAULT_ADDRESS); + let _ = pattern::load_pattern( + &mut lp, &new_pat, led_current, &mut delay, + ); + let i2c = lp.release(); + ntag = Ntag5Link::new(i2c, ntag5::DEFAULT_ADDRESS); + } + } + } + } + } + } } } else { // Provisioning failed — fall back to hardcoded cycle