WMT-GmbH / pn532

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

pn532 crate is not truly no_std #20

Open quan-thecreator opened 4 months ago

quan-thecreator commented 4 months ago

The countdown trait required by the timer arg for initializing an object of the Pn532 struct has a member function like so: docs.rs

fn wait(&mut self) -> nb::Result<(), Void>;

which references the Void empty enum provided by the void crate. This crate does not supply a no_std feature/distribution, and therefore should not be used in an embedded crate like this one. However, this can be trivially solved by replacing Void with (), which has the same effect. I've done this in my fork: https://github.com/quan-thecreator/pn532/tree/master

Lastly, I believe an overhaul to embedded-hal v1.0.0 is required to solve inconveniences like the following:

pub struct MyDelay {
    inner: Delay,
}

impl MyDelay {
    pub fn new(delay: Delay) -> Self {
        MyDelay { inner: delay }
    }
}

impl CountDown for MyDelay {
    type Time = u32;

    fn start<T>(&mut self, count: T)
    where
        T: Into<Self::Time>,
    {   
        let delay_ms = count.into();
        info!("Start function called time; {} Delay count down timer", &delay_ms );
        self.inner.delay_millis(delay_ms);
        info!("Start function exited - Delay count down");
    }

    fn wait(&mut self) -> nb::Result<(), ()> {
        // In this context, the wait is Blocking
        info!("wait function called");
        Ok(())

    }
}

// Define a custom error type if needed
#[derive(Debug)]
pub enum MyI2CError {
    I2CError,
    // You can add other specific errors here
}

impl From<esp_hal::i2c::Error> for MyI2CError {
    fn from(_: esp_hal::i2c::Error) -> Self {
        MyI2CError::I2CError
    }
}

pub struct MyI2C<'a> {
    inner: I2C<'a, esp_hal::peripherals::I2C0, esp_hal::Blocking>,
}

impl<'a> MyI2C<'a> {
    pub fn new(inner: I2C<'a, esp_hal::peripherals::I2C0, esp_hal::Blocking>) -> Self {
        MyI2C { inner }
    }
}

impl<'a> Read for MyI2C<'a> {
    type Error = MyI2CError;

    fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
        self.inner
            .read(addr, buffer)
            .map_err(|_| MyI2CError::I2CError)
    }
}

impl<'a> Write for MyI2C<'a> {
    type Error = MyI2CError;

    fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> {
        self.inner
            .write(addr, bytes)
            .map_err(|_| MyI2CError::I2CError)
    }
}

impl<'a> WriteRead for MyI2C<'a> {
    type Error = MyI2CError;

    fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> {
        self.inner
            .write_read(addr, bytes, buffer)
            .map_err(|_| MyI2CError::I2CError)
    }
}

impl<'a> Transactional for MyI2C<'a> {
    type Error = MyI2CError;

    fn exec(&mut self, addr: u8, operations: &mut [Operation]) -> Result<(), Self::Error> {
        for op in operations {
            match op {
                Operation::Write(bytes) => self
                    .inner
                    .write(addr, bytes)
                    .map_err(|_| MyI2CError::I2CError)?,
                Operation::Read(buffer) => self
                    .inner
                    .read(addr, buffer)
                    .map_err(|_| MyI2CError::I2CError)?,
            }
        }
        Ok(())
    }
}

Should I create a PR for solving the Void type problem?

dimpolo commented 4 months ago

Thanks for writing. I'm a little confused. This crate is definitely being used in no_std contexts, so something must have gone wrong here. Maybe you or one of your dependencies forgot default_features = false for the void crate?

I definitely want to update the crate to the new embedded hal version, but currently I'm not sure when I'll have time to do so.