1use defmt::info;
6use embassy_executor::Spawner;
7use embassy_rp::Peri;
8use embassy_rp::dma::Channel;
9use embassy_rp::gpio::{Level, Output, Pin};
10use embassy_rp::peripherals::SPI0;
11use embassy_rp::spi::{ClkPin, Config as SpiConfig, MisoPin, MosiPin, Phase, Polarity, Spi};
12use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
13use embassy_sync::channel::Channel as EmbassyChannel;
14use embassy_time::{Instant, Timer};
15use embedded_hal_bus::spi::{ExclusiveDevice, NoDelay};
16use esp_hal_mfrc522::MFRC522;
17use esp_hal_mfrc522::consts::UidSize;
18use esp_hal_mfrc522::drivers::SpiDriver;
19
20use crate::{Error, Result};
21
22#[derive(Debug, Clone, Copy)]
24pub enum RfidEvent {
25 CardDetected {
27 uid: [u8; 10],
29 },
30}
31
32pub type Mfrc522Device = MFRC522<
34 SpiDriver<
35 ExclusiveDevice<Spi<'static, SPI0, embassy_rp::spi::Async>, Output<'static>, NoDelay>,
36 >,
37>;
38
39pub type RfidStatic = EmbassyChannel<CriticalSectionRawMutex, RfidEvent, 4>;
41
42pub struct Rfid<'a> {
76 rfid_static: &'a RfidStatic,
77}
78
79impl Rfid<'_> {
80 #[must_use]
82 pub const fn new_static() -> RfidStatic {
83 EmbassyChannel::new()
84 }
85
86 pub async fn new<Sck, Mosi, Miso, Dma0, Dma1, Cs, Rst>(
90 rfid_static: &'static RfidStatic,
91 spi: Peri<'static, SPI0>,
92 sck: Peri<'static, Sck>,
93 mosi: Peri<'static, Mosi>,
94 miso: Peri<'static, Miso>,
95 dma_ch0: Peri<'static, Dma0>,
96 dma_ch1: Peri<'static, Dma1>,
97 cs: Peri<'static, Cs>,
98 rst: Peri<'static, Rst>,
99 spawner: Spawner,
100 ) -> Result<Self>
101 where
102 Sck: Pin + ClkPin<SPI0>,
103 Mosi: Pin + MosiPin<SPI0>,
104 Miso: Pin + MisoPin<SPI0>,
105 Dma0: Channel,
106 Dma1: Channel,
107 Cs: Pin,
108 Rst: Pin,
109 {
110 let mfrc522 =
112 init_mfrc522_hardware(spi, sck, mosi, miso, dma_ch0, dma_ch1, cs, rst).await?;
113
114 let token = rfid_polling_task(mfrc522, rfid_static);
116 spawner.spawn(token).map_err(Error::TaskSpawn)?;
117
118 Ok(Self { rfid_static })
119 }
120
121 pub async fn wait_for_tap(&self) -> RfidEvent {
123 self.rfid_static.receive().await
124 }
125}
126
127fn uid_to_fixed_array(uid_bytes: &[u8]) -> [u8; 10] {
129 let mut uid_key = [0u8; 10];
130 #[expect(clippy::indexing_slicing, reason = "Length checked")]
131 for (i, &byte) in uid_bytes.iter().enumerate() {
132 if i < 10 {
133 uid_key[i] = byte;
134 }
135 }
136 uid_key
137}
138
139#[embassy_executor::task]
141async fn rfid_polling_task(mut mfrc522: Mfrc522Device, rfid_static: &'static RfidStatic) -> ! {
142 info!("RFID polling task started");
143
144 loop {
145 let Ok(()) = mfrc522.picc_is_new_card_present().await else {
147 Timer::after_millis(500).await;
148 continue;
149 };
150
151 info!("Card detected!");
152
153 let Ok(uid) = mfrc522.get_card(UidSize::Four).await else {
155 info!("UID read error");
156 Timer::after_millis(500).await;
157 continue;
158 };
159
160 info!("UID read successfully ({} bytes)", uid.uid_bytes.len());
161
162 let uid_key = uid_to_fixed_array(&uid.uid_bytes);
164
165 rfid_static
167 .send(RfidEvent::CardDetected { uid: uid_key })
168 .await;
169
170 Timer::after_millis(50).await;
172 }
173}
174
175async fn init_mfrc522_hardware<Sck, Mosi, Miso, Dma0, Dma1, Cs, Rst>(
177 spi: Peri<'static, SPI0>,
178 sck: Peri<'static, Sck>,
179 mosi: Peri<'static, Mosi>,
180 miso: Peri<'static, Miso>,
181 dma_ch0: Peri<'static, Dma0>,
182 dma_ch1: Peri<'static, Dma1>,
183 cs: Peri<'static, Cs>,
184 rst: Peri<'static, Rst>,
185) -> Result<Mfrc522Device>
186where
187 Sck: Pin + ClkPin<SPI0>,
188 Mosi: Pin + MosiPin<SPI0>,
189 Miso: Pin + MisoPin<SPI0>,
190 Dma0: Channel,
191 Dma1: Channel,
192 Cs: Pin,
193 Rst: Pin,
194{
195 let spi = Spi::new(spi, sck, mosi, miso, dma_ch0, dma_ch1, {
197 let mut config = SpiConfig::default();
198 config.frequency = 1_000_000; config.polarity = Polarity::IdleLow;
200 config.phase = Phase::CaptureOnFirstTransition;
201 config
202 });
203
204 let cs = Output::new(cs, Level::High);
206
207 let mut rst = Output::new(rst, Level::High);
209 rst.set_low();
210 Timer::after_millis(10).await;
211 rst.set_high();
212 Timer::after_millis(50).await;
213
214 let spi_device = ExclusiveDevice::new_no_delay(spi, cs).expect("CS pin is infallible");
216 let spi_driver = SpiDriver::new(spi_device);
217 let mut mfrc522 = MFRC522::new(spi_driver, || Instant::now().as_millis());
218
219 mfrc522.pcd_init().await.map_err(Error::Mfrc522Init)?;
221 info!("MFRC522 initialized");
222
223 let _version = mfrc522
224 .pcd_get_version()
225 .await
226 .map_err(Error::Mfrc522Version)?;
227 info!("MFRC522 version read successfully");
228
229 Ok(mfrc522)
230}