WMT-GmbH / pn532

no_std implementation of the Pn532 protocol using embedded_hal traits
Apache License 2.0
20 stars 19 forks source link

Higher-level API #10

Open averyanalex opened 1 year ago

averyanalex commented 1 year ago

At this moment API is really low-lewel: i.e. when you send INLIST_ONE_ISO_A_TARGET cmd you get byte slice in response, not parsed struct. And I maybe can help with it, if you want.

dimpolo commented 1 year ago

I'd appreciate your help very much! How do you think a higher level API could look like?

averyanalex commented 1 year ago

I'd appreciate your help very much! How do you think a higher level API could look like?

I think something like this: https://github.com/averyanalex/pn532/commit/d43ce6a64175a65906f12dfc04c6cf05477d2fd5. But I am not good in Rust, so IDK how to implement it for blocking and async version without just copy-pasting same code and adding await to it.

P.S. I am limited on time too the next 1-2 months.

SirVer commented 5 months ago

Here is one potential proposal: We introduce a new trait Cmd. It knows how to construct a Request for itself and how to parse a response for itself. We add a new function Pn532::call to use these commands. Here is a full implementation of GetFirmwareVersion.

Usage:

let firmware = pn532.call(GetFirmwareVersion::new(), millis(100)).unwrap();
println!("Found PN532 with firmware version: {}", firmware.version_string());

Implementation is done through a trait (here called Cmd), one example is just the FirmwareVersion call:

pub trait Cmd: Sized {
   const NUM_BYTES: usize;
   type Response;

   fn request(&self) -> crate::requests::Request<0>;
   fn parse_result(val: &[u8]) -> Self::Response;
}

pub struct GetFirmwareVersion;
impl GetFirmwareVersion {
    pub fn new() -> Self {
        Self
    }
}

pub struct FirmwareVersion {
    pub ic: u8,
    pub ver: u8,
    pub rev: u8,
    pub support: u8,
}

impl FirmwareVersion {
    pub fn version_string(&self) -> String {
        format!("{}.{}", self.ver, self.rev)
    }
}

impl Cmd for GetFirmwareVersion {
    const NUM_BYTES: usize = 4;
    type Response = FirmwareVersion;

    fn request(&self) -> crate::requests::Request<0> {
        crate::requests::Request::GET_FIRMWARE_VERSION
    }

    fn parse_result(val: &[u8]) -> FirmwareVersion {
        FirmwareVersion {
            ic: val[0],
            ver: val[1],
            rev: val[2],
            support: val[3],
        }
    }
}

and here is the Pn532::call function that integrates with these Command structs:

pub fn call<C: Cmd>(&mut self, cmd: C, timeout: T::Time) -> Result<C::Response, Error<I::Error>> {
    let request = cmd.request();
    let res = self._process((&request).into(), C::NUM_BYTES, timeout)?;
    Ok(C::parse_result(&res))
}