pub use device_envoy_core::rfid::{Rfid, RfidEvent};
use embassy_executor::Spawner;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::channel::Channel as EmbassyChannel;
use embassy_time::{Instant, Timer};
use embedded_hal_bus::spi::{ExclusiveDevice, NoDelay};
use esp_hal::gpio::interconnect::{PeripheralInput, PeripheralOutput};
use esp_hal::gpio::{Level, Output, OutputConfig, OutputPin};
use esp_hal::spi::master::{Config as SpiConfig, Instance, Spi};
use esp_hal_mfrc522::MFRC522;
use esp_hal_mfrc522::consts::UidSize;
use esp_hal_mfrc522::drivers::SpiDriver;
use crate::{Error, Result};
type Mfrc522Device =
MFRC522<SpiDriver<ExclusiveDevice<Spi<'static, esp_hal::Async>, Output<'static>, NoDelay>>>;
pub struct RfidStatic(EmbassyChannel<CriticalSectionRawMutex, RfidEvent, 4>);
impl RfidStatic {
#[must_use]
pub const fn new() -> Self {
Self(EmbassyChannel::new())
}
async fn send(&self, event: RfidEvent) {
self.0.send(event).await;
}
async fn receive(&self) -> RfidEvent {
self.0.receive().await
}
}
pub struct RfidEsp<'a> {
rfid_static: &'a RfidStatic,
}
impl RfidEsp<'_> {
#[must_use]
pub const fn new_static() -> RfidStatic {
RfidStatic::new()
}
pub async fn new(
rfid_static: &'static RfidStatic,
spi: impl Instance + 'static,
sck: impl PeripheralOutput<'static>,
mosi: impl PeripheralOutput<'static>,
miso: impl PeripheralInput<'static>,
cs: impl OutputPin + 'static,
rst: impl OutputPin + 'static,
spawner: Spawner,
) -> Result<Self> {
let mfrc522 = init_mfrc522_hardware(spi, sck, mosi, miso, cs, rst).await?;
let token = rfid_polling_task(mfrc522, rfid_static);
spawner.spawn(token.map_err(Error::TaskSpawn)?);
Ok(Self { rfid_static })
}
}
impl Rfid for RfidEsp<'_> {
async fn wait_for_tap(&self) -> RfidEvent {
self.rfid_static.receive().await
}
}
fn uid_to_fixed_array(uid_bytes: &[u8]) -> [u8; 10] {
let mut uid_key = [0u8; 10];
for (uid_index, &uid_byte) in uid_bytes.iter().enumerate() {
if uid_index < uid_key.len() {
uid_key[uid_index] = uid_byte;
}
}
uid_key
}
#[embassy_executor::task(pool_size = 2)]
async fn rfid_polling_task(mut mfrc522: Mfrc522Device, rfid_static: &'static RfidStatic) -> ! {
loop {
let Ok(()) = mfrc522.picc_is_new_card_present().await else {
Timer::after_millis(500).await;
continue;
};
let Ok(uid) = mfrc522.get_card(UidSize::Four).await else {
Timer::after_millis(500).await;
continue;
};
let uid_key = uid_to_fixed_array(&uid.uid_bytes);
rfid_static
.send(RfidEvent::CardDetected { uid: uid_key })
.await;
Timer::after_millis(50).await;
}
}
async fn init_mfrc522_hardware(
spi: impl Instance + 'static,
sck: impl PeripheralOutput<'static>,
mosi: impl PeripheralOutput<'static>,
miso: impl PeripheralInput<'static>,
cs: impl OutputPin + 'static,
rst: impl OutputPin + 'static,
) -> Result<Mfrc522Device> {
let spi_config = SpiConfig::default()
.with_frequency(esp_hal::time::Rate::from_hz(1_000_000))
.with_mode(esp_hal::spi::Mode::_0);
let spi = Spi::new(spi, spi_config)
.map_err(Error::SpiConfig)?
.with_sck(sck)
.with_mosi(mosi)
.with_miso(miso)
.into_async();
let cs = Output::new(cs, Level::High, OutputConfig::default());
let mut rst = Output::new(rst, Level::High, OutputConfig::default());
rst.set_low();
Timer::after_millis(10).await;
rst.set_high();
Timer::after_millis(50).await;
let spi_device = ExclusiveDevice::new_no_delay(spi, cs).expect("CS pin is infallible");
let spi_driver = SpiDriver::new(spi_device);
let mut mfrc522 = MFRC522::new(spi_driver, || Instant::now().as_millis());
mfrc522.pcd_init().await.map_err(Error::Mfrc522Init)?;
let _version = mfrc522
.pcd_get_version()
.await
.map_err(Error::Mfrc522Version)?;
Ok(mfrc522)
}