From 78765ce5951a083d90587470c5908bd4ae3bd207 Mon Sep 17 00:00:00 2001 From: lukas Date: Sun, 2 Aug 2026 16:26:13 +0200 Subject: [PATCH] Added NFC implementation into refactored structure --- Cargo.lock | 11 ++++ Cargo.toml | 2 + src/bin/main.rs | 14 +++++ src/drivers/nfc_pn532.rs | 119 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 145 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index cb1baf7..795d024 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -320,6 +320,7 @@ dependencies = [ "esp-rtos", "ili9341", "log", + "pn532", ] [[package]] @@ -2584,6 +2585,16 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" +[[package]] +name = "pn532" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce656b2f9335bedbd8806597f6f78e644a955366673cd9280a0fb67a0d7b35a1" +dependencies = [ + "embedded-hal 1.0.0", + "nb 1.1.0", +] + [[package]] name = "portable-atomic" version = "1.14.0" diff --git a/Cargo.toml b/Cargo.toml index 2ecb546..4133b04 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,8 @@ embedded-hal-bus = "0.3.0" embedded-graphics = "0.8.2" embedded-graphics-core = "0.4.1" base64 = { version = "0.22", default-features = false, features = ["alloc"] } +pn532 = "0.5.0" + # For fine tuning these settings, please refer to https://doc.rust-lang.org/cargo/reference/profiles.html diff --git a/src/bin/main.rs b/src/bin/main.rs index 4070797..e9861ae 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -10,6 +10,7 @@ use creaturedex::card::mock::{MOCK_PAYLOAD, MOCK_PAYLOAD2}; use creaturedex::display::primary_lcd::init_primary_lcd; use creaturedex::display::views::card_view::render_nfc_payload; +use creaturedex::drivers::nfc_pn532::NfcPn532Driver; use creaturedex::navigation::input::init_navigation_button; use creaturedex::navigation::state::NavigationState; use creaturedex::network::wifi::setup_wifi; @@ -18,6 +19,7 @@ use embassy_time::Timer; use embedded_graphics::draw_target::DrawTarget; use embedded_graphics::pixelcolor::Rgb565; use embedded_graphics::prelude::RgbColor; +use esp_hal::time::Duration; use esp_hal::clock::CpuClock; use esp_hal::delay::Delay; use esp_hal::timer::timg::TimerGroup; @@ -83,7 +85,19 @@ async fn main(spawner: Spawner) { let mut nav_state = NavigationState::new(); + let mut nfc_driver = NfcPn532Driver::new(peripherals.I2C0, peripherals.GPIO17, peripherals.GPIO18); + let timeout = Duration::from_millis(1000); + if nfc_driver.configure_sam(timeout).is_ok() { + log::info!("NFC SAM configuration successful"); + } else { + log::error!("NFC SAM configuration failed"); + } + loop { + if let Some(card_data) = nfc_driver.read_card(timeout) { + log::info!("NFC Card Read: {:?}", card_data); + } + if button1.is_low() { log::info!("Button pressed!"); display.clear(Rgb565::BLACK).unwrap(); diff --git a/src/drivers/nfc_pn532.rs b/src/drivers/nfc_pn532.rs index 36d4478..12cf8f6 100644 --- a/src/drivers/nfc_pn532.rs +++ b/src/drivers/nfc_pn532.rs @@ -1 +1,118 @@ -// Drivers nfc_pn532 module +use core::convert::Infallible; +use esp_hal::gpio::interconnect::{PeripheralInput, PeripheralOutput}; +use esp_hal::i2c::master::{Config as I2cConfig, I2c}; +use esp_hal::peripherals::I2C0; +use esp_hal::time::{Duration, Instant, Rate}; +use log::{error, info}; +use pn532::i2c::I2CInterface; +use pn532::requests::SAMMode; +use pn532::{nb, Pn532, Request}; + +/// Timer implementation required by `pn532::CountDown`. +pub struct TimerWrapper { + start: Instant, + duration: Duration, + counter: u32, +} + +impl TimerWrapper { + pub fn new() -> Self { + Self { + start: Instant::now(), + duration: Duration::ZERO, + counter: 0, + } + } +} + +impl Default for TimerWrapper { + fn default() -> Self { + Self::new() + } +} + +impl pn532::CountDown for TimerWrapper { + type Time = Duration; + + fn start(&mut self, timeout: T) + where + T: Into, + { + // without this debug log, it works worse: stuck in NFC card search without ever returning. + self.start = esp_println::dbg!(Instant::now()); + self.duration = timeout.into(); + self.counter = 0; + } + + fn wait(&mut self) -> nb::Result<(), Infallible> { + let elapsed = self.start.elapsed(); + self.counter += 1; + if self.counter.is_multiple_of(1) { + info!("Elapsed: {elapsed}"); + } + if elapsed >= self.duration { + Ok(()) + } else { + Err(nb::Error::WouldBlock) + } + } +} + +pub type Pn532Device<'a> = Pn532>, TimerWrapper, 32>; + +/// PN532 NFC reader driver. +pub struct NfcPn532Driver<'a> { + pn532: Pn532Device<'a>, +} + +impl<'a> NfcPn532Driver<'a> { + pub fn new(i2c0: I2C0<'static>, sda: SDA, scl: SCL) -> Self + where + SDA: PeripheralInput<'static> + PeripheralOutput<'static>, + SCL: PeripheralInput<'static> + PeripheralOutput<'static>, + { + let config = I2cConfig::default().with_frequency(Rate::from_khz(100)); + let i2c = I2c::new(i2c0, config) + .expect("Failed to init I2C") + .with_sda(sda) + .with_scl(scl); + + let pn532 = Pn532::new(I2CInterface { i2c }, TimerWrapper::new()); + Self { pn532 } + } + + pub fn configure_sam(&mut self, timeout: Duration) -> Result<(), ()> { + if let Err(e) = self.pn532.process( + &Request::sam_configuration(SAMMode::Normal, false), + 0, + timeout, + ) { + error!("Could not initialize PN532: {e:?}"); + Err(()) + } else { + info!("Successfully init'ed PN532"); + Ok(()) + } + } + + pub fn read_card(&mut self, timeout: Duration) -> Option<[u8; 4]> { + if let Ok(uid) = self.pn532.process(&Request::INLIST_ONE_ISO_A_TARGET, 7, timeout) { + info!("uid = {uid:?}"); + if let Ok(result) = self.pn532.process(&Request::ntag_read(10), 17, timeout) { + info!("page 10: {:?}", &result[1..5]); + let mut data = [0u8; 4]; + data.copy_from_slice(&result[1..5]); + Some(data) + } else { + None + } + } else { + info!("No NFC card found."); + None + } + } + + pub fn device_mut(&mut self) -> &mut Pn532Device<'a> { + &mut self.pn532 + } +}