Files
lora-card-reader/src/components/card_reader.rs
T
2026-09-13 20:51:26 +08:00

168 lines
5.3 KiB
Rust

use crate::types::{LoraPublisher, SharedI2c};
use defmt::Debug2Format;
use embassy_sync::once_lock::OnceLock;
use embassy_time::{Delay, Timer};
use esp_hal::gpio;
use esp_hal::rng::Trng;
use pn532::types::felica::felica_lite_s::auth::{CardKey, CardKeyVersion};
use pn532::types::felica::felica_lite_s::config::{MemoryConfig, RFParameter};
use pn532::types::felica::{CardSession, FelicaLiteSBlock};
use pn532::Pn532;
static CARD_KEY: OnceLock<CardKey> = OnceLock::new();
const CARD_KEY_STR: &str = env!("CARD_KEY");
fn get_card_key() -> &'static CardKey {
CARD_KEY.get_or_init(|| {
let mut buffer = [0u8; 16];
hex::decode_to_slice(CARD_KEY_STR, &mut buffer).unwrap();
CardKey::from(buffer)
})
}
pub struct CardReader<'a> {
driver: Pn532<
gpio::Output<'a>,
gpio::Input<'a>,
Delay,
pn532::I2cInterface<SharedI2c<'a>, Delay, gpio::Output<'a>, gpio::Input<'a>>,
>,
}
impl<'a> CardReader<'a> {
pub fn new(i2c: SharedI2c<'a>, irq: gpio::Input<'a>, reset: gpio::Output<'a>) -> Self {
Self {
driver: Pn532::new(pn532::I2cInterface::with_reset_irq(i2c, Delay, reset, irq)),
}
}
pub fn init(&mut self) -> Result<(), crate::error::Error> {
self.driver.begin()?;
self.driver.sam_config()?;
self.driver.set_passive_activation_retries(5)?;
Ok(())
}
}
#[embassy_executor::task]
pub async fn start_card_reader(
mut card_reader: CardReader<'static>,
_publisher: LoraPublisher<'static>,
mut trng: Trng,
) -> () {
loop {
match _start_card_reader(&mut card_reader, &_publisher, &mut trng).await {
Ok(_) => {}
Err(e) => {
defmt::error!("Error while reading card: {}", e);
}
}
}
}
async fn _start_card_reader(
card_reader: &mut CardReader<'static>,
_publisher: &LoraPublisher<'static>,
trng: &mut Trng,
) -> Result<(), crate::error::Error> {
let card_key = get_card_key();
defmt::trace!("card_key: {}", card_key);
let driver = &mut card_reader.driver;
loop {
Timer::after_millis(10).await;
let Some(response) = (match driver.felica_polling(Default::default()) {
Ok(v) => v,
Err(e) => {
defmt::error!("card_reader driver error: {}", e);
continue;
}
}) else {
defmt::trace!("No card detected");
continue;
};
defmt::info!("Polling card: {}", Debug2Format(&response));
defmt::debug!("IDm: {}", response.idm.into_bytes());
let card = CardSession::from(response);
// if let Err(e) = card_reader.driver.felica_write_without_encryption(
// &response.idm, &response.pmm,
// &[FelicaLiteSServiceCode::Write],
// &[FelicaLiteSBlock::CK],
// &[*card_key]
// ) {
// defmt::error!("card_reader write ck error: {}", e);
// continue;
// }
let id = driver.get_id(&card)?;
defmt::debug!("ID: {}", id);
if let Some(card_id) = option_env!("CARD_ID") {
let mut id = [0u8; 6];
hex::decode_to_slice(card_id,&mut id)?;
driver.set_id(&card, id, None)?;
}
let mut mc = driver.get_mc(&card)?;
defmt::info!("Config: {}", mc);
mc.access.set_system_block(0x00);
mc.ndef = 0x00;
mc.rf = RFParameter::from(0xFF);
mc.access.set_is_ck_ckv_rewritable(true);
let mut read_auth = mc.auth.scratch_pad_read();
read_auth.set_pad0(true);
mc.auth.set_scratch_pad_read(read_auth);
let mut write_auth = mc.auth.scratch_pad_write();
write_auth.set_pad0(true);
mc.auth.set_scratch_pad_write(write_auth);
let mut mac_auth = mc.auth.scratch_pad_mac_write();
mac_auth.set_pad0(true);
mc.auth.set_scratch_pad_mac_write(mac_auth);
let block: [u8; 16] = mc.into();
defmt::debug!("1st insurance Config: {}", block);
driver.set_mc(&card, mc)?;
let session = match driver.try_authenticate(trng, &card, card_key) {
Ok(Some(sk)) => sk,
Ok(None) => {
defmt::warn!("card key is invalid");
continue;
}
Err(e) => {
defmt::error!("card_reader auth error: {}", e);
continue;
}
};
driver.set_ck_with_mac(&card, &session, &card_key)?;
driver.set_ckv_with_mac(&card, &session, &CardKeyVersion::new(1))?;
driver.get_ckv(&card)?;
let _read_data = match driver.read_with_mac(&card, &session, FelicaLiteSBlock::PAD0) {
Ok(d) => d,
Err(e) => {
defmt::error!("card_reader read with mac error: {}", e);
continue;
}
};
match driver.release() {
Ok(_) => {}
Err(e) => {
defmt::error!("card_reader release error: {}", e);
continue;
}
};
// InRelease switches the RF field off when no target remains
// (UM0701-02 §7.3.11), so re-enable it before the next poll.
if let Err(e) = driver.set_rf_field(0x00, 0x01) {
defmt::error!("card_reader RF field error: {}", e);
}
Timer::after_millis(200).await;
}
}