secure-serial 0.1.0

Reliable CRC-framed serial protocol with chunked packets and ACKs for no_std + Embassy
Documentation
use crc::{CRC_32_ISO_HDLC, Crc};
use embassy_sync::{
    blocking_mutex::raw::{RawMutex, ThreadModeRawMutex},
    pipe::{Pipe, Reader, Writer},
};
use embassy_time::{Duration, Timer};
use secure_serial::{
    CrcDevice, SecureSerialResources, SecureSerialResourcesDefault, SecureSerialSender,
    TransportRead, TransportWrite, run_read, run_write,
};
use static_cell::ConstStaticCell;

static PIPE_A_TO_B: ConstStaticCell<Pipe<ThreadModeRawMutex, 200>> =
    ConstStaticCell::new(Pipe::new());
static PIPE_B_TO_A: ConstStaticCell<Pipe<ThreadModeRawMutex, 200>> =
    ConstStaticCell::new(Pipe::new());

#[tokio::main(flavor = "current_thread")]
async fn main() {
    let local = tokio::task::LocalSet::new();
    local
        .run_until(async {
            let (a_to_b_rx, a_to_b_tx) = PIPE_A_TO_B.take().split();
            let (b_to_a_rx, b_to_a_tx) = PIPE_B_TO_A.take().split();

            let a_read = PipeTransportRead { inner: b_to_a_rx };
            let a_write = PipeTransportWrite { inner: a_to_b_tx };
            let b_read = PipeTransportRead { inner: a_to_b_rx };
            let b_write = PipeTransportWrite { inner: b_to_a_tx };

            tokio::task::spawn_local(run_one_side(a_read, a_write, true));
            tokio::task::spawn_local(run_one_side(b_read, b_write, false));

            loop {
                Timer::after_secs(1).await;
            }
        })
        .await;
}

async fn run_one_side<R: TransportRead + Send + 'static, W: TransportWrite + Send + 'static>(
    read: R,
    write: W,
    is_a: bool,
) where
    R::Error: Send,
    W::Error: Send,
{
    let res: &'static SecureSerialResources<ThreadModeRawMutex, 8, 4, 4096> = &*Box::leak(
        Box::new(SecureSerialResourcesDefault::<ThreadModeRawMutex>::new()),
    );

    tokio::task::spawn_local(run_write(
        Box::leak(Box::new(write)),
        Box::leak(Box::new(res.tx_chunks_receiver())),
        Box::leak(Box::new(res.acks_to_wire_receiver())),
        Box::leak(Box::new(SoftwareCrc)),
    ));

    tokio::task::spawn_local(run_read(
        Box::leak(Box::new(read)),
        Box::leak(Box::new(SoftwareCrc)),
        res.rx_pool(),
        res.rx_complete_sender(),
        res.acks_to_wire_sender(),
        res.acks_from_peer_sender(),
    ));

    tokio::task::spawn_local(async move {
        let rx = res.rx_complete_receiver();
        loop {
            let buf = rx.receive().await;
            println!("received buffer {:?}", &*buf);
        }
    });

    let mut sender = SecureSerialSender::new(
        res.tx_pool(),
        res.tx_chunks_sender(),
        res.acks_from_peer_receiver(),
        Duration::from_millis(1000),
        3,
    );

    loop {
        Timer::after_secs(1).await;

        if is_a {
            sender.write_packet(b"test hello").await.unwrap();
        }
        Timer::after_secs(4).await;
    }
}

// ------------------------------------------------------------------------------------
pub struct SoftwareCrc;

impl CrcDevice for SoftwareCrc {
    async fn crc(&mut self, data: &[u8]) -> u32 {
        const SW_CRC: Crc<u32> = Crc::<u32>::new(&CRC_32_ISO_HDLC);
        SW_CRC.checksum(data)
    }
}

pub struct PipeTransportRead<'p, M: RawMutex, const N: usize> {
    inner: Reader<'p, M, N>,
}

impl<'p, M: RawMutex, const N: usize> TransportRead for PipeTransportRead<'p, M, N> {
    type Error = ();

    async fn read(&mut self, data: &mut [u8]) -> Result<usize, Self::Error> {
        let res = self.inner.read(data).await;
        Ok(res)
    }
}

pub struct PipeTransportWrite<'p, M: RawMutex, const N: usize> {
    inner: Writer<'p, M, N>,
}

impl<'p, M: RawMutex, const N: usize> TransportWrite for PipeTransportWrite<'p, M, N> {
    type Error = ();

    async fn write(&mut self, data: &[u8]) -> Result<(), Self::Error> {
        let mut remaining = data;
        println!("write {} bytes: {:02x?}", data.len(), data);
        while !remaining.is_empty() {
            let n = self.inner.write(remaining).await;
            remaining = &remaining[n..];
        }
        Ok(())
    }
}