Add card detection
This commit is contained in:
@@ -0,0 +1,213 @@
|
||||
//! High-level PN532 command driver.
|
||||
|
||||
use crate::commands::*;
|
||||
use crate::error::{Error};
|
||||
use crate::interface::Interface;
|
||||
|
||||
pub(crate) const PACKET_BUFFER_SIZE: usize = 64;
|
||||
|
||||
/// A card UID read by [`Pn532::read_passive_target_id`].
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Uid {
|
||||
pub bytes: heapless::Vec<u8, 7>,
|
||||
}
|
||||
|
||||
impl core::fmt::Display for Uid {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
#[cfg(feature = "alloc")]
|
||||
return write!(f, "{}", hex::encode(&self.bytes));
|
||||
#[cfg(not(feature = "alloc"))]
|
||||
{
|
||||
let mut buf: heapless::Vec<u8, 14> = heapless::Vec::new();
|
||||
hex::encode_to_slice(&self.bytes, &mut buf)
|
||||
.map_err(|_| core::fmt::Error)?;
|
||||
let s = heapless::String::from_utf8(buf)
|
||||
.map_err(|_| core::fmt::Error)?;
|
||||
write!(f, "{}", s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
impl defmt::Format for Uid {
|
||||
fn format(&self, fmt: defmt::Formatter) {
|
||||
defmt::write!(fmt, "{}", defmt::Display2Format(self));
|
||||
}
|
||||
}
|
||||
|
||||
/// Outcome of [`Pn532::tg_init_as_target`] / [`Pn532::tg_init_as_target_default`].
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum TargetInitStatus {
|
||||
/// The PN532 entered target mode.
|
||||
Success,
|
||||
/// No initiator appeared within the timeout.
|
||||
Timeout,
|
||||
/// The operation failed.
|
||||
Failed,
|
||||
}
|
||||
|
||||
/// Result of a successful FeliCa polling request.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct FelicaPollingResponse {
|
||||
/// The card's IDm (NFCID2).
|
||||
pub idm: [u8; 8],
|
||||
/// The card's PMm (PAD).
|
||||
pub pmm: [u8; 8],
|
||||
/// The card's system code, when returned.
|
||||
pub system_code_response: Option<u16>,
|
||||
}
|
||||
|
||||
/// Driver for the NXP PN532 NFC controller.
|
||||
pub struct Pn532<B: Interface> {
|
||||
pub(crate) interface: B,
|
||||
pub(crate) in_listed_tag: u8,
|
||||
pub(crate) felica_idm: [u8; 8],
|
||||
pub(crate) felica_pmm: [u8; 8],
|
||||
pub(crate) buffer: [u8; PACKET_BUFFER_SIZE],
|
||||
}
|
||||
|
||||
impl<B: Interface> Pn532<B> {
|
||||
/// Create a driver over the given transport interface.
|
||||
pub fn new(interface: B) -> Self {
|
||||
Self {
|
||||
interface,
|
||||
in_listed_tag: 0,
|
||||
felica_idm: [0; 8],
|
||||
felica_pmm: [0; 8],
|
||||
buffer: [0; PACKET_BUFFER_SIZE],
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialise the PN532: pulse the reset pin (if provided) and wait for it
|
||||
/// to become ready.
|
||||
pub fn begin(&mut self) -> Result<(), Error<B::TransportError>> {
|
||||
self.interface.begin()
|
||||
}
|
||||
|
||||
// -- internal helpers ---------------------------------------------------
|
||||
|
||||
pub(crate) fn send(&mut self, header_len: usize) -> Result<(), Error<B::TransportError>> {
|
||||
self.interface.write_command(&self.buffer[..header_len], &[])
|
||||
}
|
||||
|
||||
pub(crate) fn send_with_body(
|
||||
&mut self,
|
||||
header_len: usize,
|
||||
body: &[u8],
|
||||
) -> Result<(), Error<B::TransportError>> {
|
||||
self.interface
|
||||
.write_command(&self.buffer[..header_len], body)
|
||||
}
|
||||
|
||||
pub(crate) fn read(&mut self) -> Result<usize, Error<B::TransportError>> {
|
||||
self.interface.read_response(&mut self.buffer, 1000)
|
||||
}
|
||||
|
||||
pub(crate) fn read_timeout(&mut self, timeout_ms: u16) -> Result<usize, Error<B::TransportError>> {
|
||||
self.interface.read_response(&mut self.buffer, timeout_ms)
|
||||
}
|
||||
|
||||
// -- generic PN532 functions -------------------------------------------
|
||||
|
||||
/// Read the PN532 firmware version and ID.
|
||||
pub fn get_firmware_version(&mut self) -> Result<u32, Error<B::TransportError>> {
|
||||
self.buffer[0] = PN532_COMMAND_GETFIRMWAREVERSION;
|
||||
self.send(1)?;
|
||||
let len = self.read()?;
|
||||
if len < 4 {
|
||||
return Err(Error::InvalidFrame);
|
||||
}
|
||||
Ok(u32::from_be_bytes([
|
||||
self.buffer[0],
|
||||
self.buffer[1],
|
||||
self.buffer[2],
|
||||
self.buffer[3],
|
||||
]))
|
||||
}
|
||||
|
||||
/// Read a 16-bit PN532 register.
|
||||
pub fn read_register(&mut self, reg: u16) -> Result<u8, Error<B::TransportError>> {
|
||||
self.buffer[0] = PN532_COMMAND_READREGISTER;
|
||||
self.buffer[1] = (reg >> 8) as u8;
|
||||
self.buffer[2] = reg as u8;
|
||||
self.send(3)?;
|
||||
let len = self.read()?;
|
||||
if len < 1 {
|
||||
return Err(Error::InvalidFrame);
|
||||
}
|
||||
Ok(self.buffer[0])
|
||||
}
|
||||
|
||||
/// Write to a 16-bit PN532 register.
|
||||
pub fn write_register(&mut self, reg: u16, val: u8) -> Result<(), Error<B::TransportError>> {
|
||||
self.buffer[0] = PN532_COMMAND_WRITEREGISTER;
|
||||
self.buffer[1] = (reg >> 8) as u8;
|
||||
self.buffer[2] = reg as u8;
|
||||
self.buffer[3] = val;
|
||||
self.send(4)?;
|
||||
self.read()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set the PN532's GPIO pins (see the PN532 user manual for valid pins).
|
||||
pub fn write_gpio(&mut self, pinstate: u8) -> Result<(), Error<B::TransportError>> {
|
||||
let pinstate = pinstate | (1 << PN532_GPIO_P32) | (1 << PN532_GPIO_P34);
|
||||
self.buffer[0] = PN532_COMMAND_WRITEGPIO;
|
||||
self.buffer[1] = PN532_GPIO_VALIDATIONBIT | pinstate;
|
||||
self.buffer[2] = 0x00;
|
||||
self.send(3)?;
|
||||
let len = self.read()?;
|
||||
if len == 0 {
|
||||
return Err(Error::InvalidFrame);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Read the state of the PN532's GPIO pins.
|
||||
pub fn read_gpio(&mut self) -> Result<u8, Error<B::TransportError>> {
|
||||
self.buffer[0] = PN532_COMMAND_READGPIO;
|
||||
self.send(1)?;
|
||||
self.read()?;
|
||||
Ok(self.buffer[0])
|
||||
}
|
||||
|
||||
/// Configure the SAM (Secure Access Module) in normal mode.
|
||||
pub fn sam_config(&mut self) -> Result<(), Error<B::TransportError>> {
|
||||
self.buffer[0] = PN532_COMMAND_SAMCONFIGURATION;
|
||||
self.buffer[1] = 0x01; // normal mode
|
||||
self.buffer[2] = 0x14; // timeout 50 ms * 20 = 1 second
|
||||
self.buffer[3] = 0x01; // use IRQ pin
|
||||
self.send(4)?;
|
||||
self.read()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set the `MxRtyPassiveActivation` RF configuration item.
|
||||
pub fn set_passive_activation_retries(
|
||||
&mut self,
|
||||
max_retries: u8,
|
||||
) -> Result<(), Error<B::TransportError>> {
|
||||
self.buffer[0] = PN532_COMMAND_RFCONFIGURATION;
|
||||
self.buffer[1] = 5;
|
||||
self.buffer[2] = 0xFF;
|
||||
self.buffer[3] = 0x01;
|
||||
self.buffer[4] = max_retries;
|
||||
self.send(5)?;
|
||||
self.read()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Switch the RF field on/off.
|
||||
pub fn set_rf_field(
|
||||
&mut self,
|
||||
auto_rfca: u8,
|
||||
rf_on_off: u8,
|
||||
) -> Result<(), Error<B::TransportError>> {
|
||||
self.buffer[0] = PN532_COMMAND_RFCONFIGURATION;
|
||||
self.buffer[1] = 1;
|
||||
self.buffer[2] = 0x00 | auto_rfca | rf_on_off;
|
||||
self.send(3)?;
|
||||
self.read()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user