max485-async 0.3.0

Async driver for the MAX 485 RS-485 transceiver, using embedded-hal-async.
Documentation
#![no_std]

use core::fmt::{self, Debug, Display};
use embedded_hal::digital::OutputPin;
use embedded_io_async::{ErrorType, Read, ReadReady, Write, WriteReady};

/// Asynchronous driver for MAX485 RS-485 transceivers. Requires a serial port,
/// a RE/DE pin, and a delay provider.
pub struct Max485<RIDO, REDE>
where
    RIDO: Read + Write,
    REDE: OutputPin,
{
    serial: RIDO,
    pin: REDE,
    begun_transmission: bool,
}

impl<RIDO, REDE> Max485<RIDO, REDE>
where
    RIDO: Read + Write,
    REDE: OutputPin,
{
    pub fn new(serial: RIDO, pin: REDE) -> Self {
        Self {
            serial,
            pin,
            begun_transmission: false,
        }
    }

    pub fn into_parts(self) -> (RIDO, REDE) {
        (self.serial, self.pin)
    }

    pub fn inner_mut(&mut self) -> &mut RIDO {
        &mut self.serial
    }

    /// Begin the transmission by setting the RE/DE pin high. This is called
    /// automatically before any write operation.
    pub fn begin_transmission(&mut self) -> Result<(), <Self as ErrorType>::Error> {
        if !self.begun_transmission {
            self.pin.set_high().map_err(Error::Pin)?;
            self.begun_transmission = true;
        }
        Ok(())
    }

    fn end_transmission_inner(&mut self) -> Result<(), <Self as ErrorType>::Error> {
        if self.begun_transmission {
            self.pin.set_low().map_err(Error::Pin)?;
            self.begun_transmission = false;
        }
        Ok(())
    }

    /// End the transmission by flushing the serial port and setting the RE/DE
    /// pin low.
    pub async fn end_transmission(&mut self) -> Result<(), <Self as ErrorType>::Error> {
        self.serial.flush().await.map_err(Error::Serial)?;
        self.end_transmission_inner()
    }
}

impl<RIDO, REDE> ErrorType for Max485<RIDO, REDE>
where
    RIDO: Read + Write,
    REDE: OutputPin,
{
    type Error = crate::Error<RIDO::Error, REDE::Error>;
}

impl<RIDO, REDE> Write for Max485<RIDO, REDE>
where
    RIDO: Read + Write,
    REDE: OutputPin,
{
    async fn write(&mut self, bytes: &[u8]) -> Result<usize, Self::Error> {
        self.begin_transmission()?;
        self.serial.write(bytes).await.map_err(Error::Serial)
    }

    async fn flush(&mut self) -> Result<(), Self::Error> {
        self.begin_transmission()?;
        self.serial.flush().await.map_err(Error::Serial)?;
        self.end_transmission_inner()
    }
}

impl<RIDO, REDE> Read for Max485<RIDO, REDE>
where
    RIDO: Read + Write,
    REDE: OutputPin,
{
    async fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
        self.end_transmission_inner()?;
        self.serial.read(buf).await.map_err(Error::Serial)
    }
}

impl<RIDO, REDE> ReadReady for Max485<RIDO, REDE>
where
    RIDO: Read + Write + ReadReady,
    REDE: OutputPin,
{
    fn read_ready(&mut self) -> Result<bool, Self::Error> {
        Ok(self.serial.read_ready().map_err(Error::Serial)? && !self.begun_transmission)
    }
}

impl<RIDO, REDE> WriteReady for Max485<RIDO, REDE>
where
    RIDO: Read + Write + WriteReady,
    REDE: OutputPin,
{
    fn write_ready(&mut self) -> Result<bool, Self::Error> {
        self.serial.write_ready().map_err(Error::Serial)
    }
}

/// Custom Error type
#[derive(Debug)]
pub enum Error<S, P> {
    Serial(S),
    Pin(P),
}

impl<S, P> Display for Error<S, P>
where
    S: Display,
    P: Debug, // embedded_hal::digital::Error only implements Debug
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Error::Serial(s) => write!(f, "serial error: {s}"),
            Error::Pin(p) => write!(f, "pin error: {p:?}"),
        }
    }
}

impl<S, P> core::error::Error for Error<S, P>
where
    S: core::error::Error,
    P: Debug,
{
}

impl<S, P> embedded_io_async::Error for Error<S, P>
where
    S: embedded_io_async::Error,
    P: Debug,
{
    fn kind(&self) -> embedded_io_async::ErrorKind {
        match self {
            Error::Serial(s) => s.kind(),
            Error::Pin(_) => embedded_io_async::ErrorKind::Other,
        }
    }
}