Compare commits

..

2 commits

7 changed files with 365 additions and 176 deletions

48
firmware/Cargo.lock generated
View file

@ -74,9 +74,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.9.0"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
[[package]]
name = "block-device-driver"
@ -245,7 +245,7 @@ dependencies = [
[[package]]
name = "embassy-embedded-hal"
version = "0.3.0"
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b"
source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
dependencies = [
"defmt 0.3.100",
"embassy-futures",
@ -263,7 +263,7 @@ dependencies = [
[[package]]
name = "embassy-executor"
version = "0.7.0"
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b"
source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
dependencies = [
"cortex-m",
"critical-section",
@ -275,7 +275,7 @@ dependencies = [
[[package]]
name = "embassy-executor-macros"
version = "0.6.2"
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b"
source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
dependencies = [
"darling",
"proc-macro2",
@ -286,7 +286,7 @@ dependencies = [
[[package]]
name = "embassy-futures"
version = "0.1.1"
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b"
source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
dependencies = [
"defmt 0.3.100",
]
@ -294,7 +294,7 @@ dependencies = [
[[package]]
name = "embassy-hal-internal"
version = "0.2.0"
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b"
source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
dependencies = [
"cortex-m",
"critical-section",
@ -305,7 +305,7 @@ dependencies = [
[[package]]
name = "embassy-net-driver"
version = "0.2.0"
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b"
source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
dependencies = [
"defmt 0.3.100",
]
@ -313,11 +313,11 @@ dependencies = [
[[package]]
name = "embassy-stm32"
version = "0.2.0"
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b"
source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
dependencies = [
"aligned",
"bit_field",
"bitflags 2.9.0",
"bitflags 2.9.1",
"block-device-driver",
"cfg-if",
"cortex-m",
@ -360,7 +360,7 @@ dependencies = [
[[package]]
name = "embassy-sync"
version = "0.6.2"
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b"
source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
dependencies = [
"cfg-if",
"critical-section",
@ -374,7 +374,7 @@ dependencies = [
[[package]]
name = "embassy-time"
version = "0.4.0"
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b"
source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
dependencies = [
"cfg-if",
"critical-section",
@ -390,7 +390,7 @@ dependencies = [
[[package]]
name = "embassy-time-driver"
version = "0.2.0"
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b"
source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
dependencies = [
"document-features",
]
@ -398,7 +398,7 @@ dependencies = [
[[package]]
name = "embassy-time-queue-utils"
version = "0.1.0"
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b"
source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
dependencies = [
"embassy-executor",
"heapless",
@ -407,7 +407,7 @@ dependencies = [
[[package]]
name = "embassy-usb-driver"
version = "0.1.0"
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b"
source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
dependencies = [
"defmt 0.3.100",
"embedded-io-async",
@ -416,7 +416,7 @@ dependencies = [
[[package]]
name = "embassy-usb-synopsys-otg"
version = "0.2.0"
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b"
source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
dependencies = [
"critical-section",
"defmt 0.3.100",
@ -516,9 +516,9 @@ dependencies = [
[[package]]
name = "errno"
version = "0.3.11"
version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e"
checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18"
dependencies = [
"libc",
"windows-sys",
@ -635,9 +635,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "micropb"
version = "0.1.1"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fba9d45f70365e5ecfe23bc1dbe523ce5c3f2e81aa762880961b61ab25c99a7"
checksum = "bff8c16c9fb4b03e9b23e0bc17ce3b67465c3f0f75a004a65d604d382c13b7bd"
dependencies = [
"heapless",
"never",
@ -646,9 +646,9 @@ dependencies = [
[[package]]
name = "micropb-gen"
version = "0.1.0"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21d0a9d827869dd3d834e6992c6a8fdb4229a82b44afe0679e82efe14c50643e"
checksum = "dd8b5b73c6511641cc191a1c7ba95cbe86abb9801dcb6f2a6d8d23b79836ee31"
dependencies = [
"convert_case",
"micropb",
@ -862,7 +862,7 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
dependencies = [
"bitflags 2.9.0",
"bitflags 2.9.1",
"errno",
"libc",
"linux-raw-sys",
@ -1100,5 +1100,5 @@ version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [
"bitflags 2.9.0",
"bitflags 2.9.1",
]

View file

@ -20,13 +20,13 @@ embedded-resources = { version = "0.2.1", features = ["stm32"] }
portable-atomic = { version = "1.5", features = ["unsafe-assume-single-core"] }
static_cell = "2.1.0"
thiserror = { version = "2.0.12", default-features = false }
micropb = { version = "0.1.1", default-features = false, features = [ "encode", "decode", "container-heapless" ] }
defmt = { version = "1.0.0", optional = true }
defmt-rtt = { version = "1.0.0", optional = true }
panic-probe = { version = "1.0.0", optional = true }
panic-halt = "1.0.0"
thiserror = { version = "2.0.12", default-features = false }
[build-dependencies]
micropb-gen = "0.1.0"
@ -35,7 +35,7 @@ micropb-gen = "0.1.0"
default = ["board-nucleo64", "status-led", "trace"]
board-nucleo64 = []
status-led = []
use-copy-within = []
copy-within = []
trace = [
"dep:defmt",
"dep:defmt-rtt",

View file

@ -4,7 +4,7 @@ extern crate core;
mod crc_engine;
mod tasks;
mod uarts;
mod uart;
use core::mem;
use embassy_executor::Spawner;

View file

@ -1,11 +1,10 @@
use crate::uarts::{UartReadError, read_frame_fragment};
use crate::{crc_engine, println};
use crate::{crc_engine, println, uart};
use byteorder::{ByteOrder, LittleEndian};
use core::str::FromStr;
use embassy_executor::Spawner;
use embassy_stm32::{gpio, usart};
use embassy_time::{Duration, Timer};
use embedded_io_async::{Read, Write};
use embedded_io_async::Write;
use static_cell::StaticCell;
use micropb::{
@ -132,104 +131,67 @@ async fn tx_task(mut tx: Tx, crc: crc_engine::CrcHandle) {
#[embassy_executor::task]
#[allow(clippy::too_many_lines)]
async fn rx_task(mut rx: Rx, crc: crc_engine::CrcHandle) {
loop {
// look for a sync byte in the buffer; the buffer may have
// leftover bytes from a previous cycle through this loop
// (due to an incomplete message, or a message which failed
// validation)
if let Some(sync) = rx.message_buf.iter().position(|&x| x == SYNC_BYTE) {
// if a sync byte was found, reset the buffer to the data
// following it
//
let post_bytes = rx.message_buf.len() - sync - 1;
#[cfg(not(feature = "use-copy-within"))]
for i in 0..post_bytes {
rx.message_buf[i] = rx.message_buf[sync + 1 + i];
}
#[cfg(feature = "use-copy-within")]
rx.message_buf.copy_within(sync + 1.., 0);
rx.message_buf.truncate(post_bytes);
} else {
// wait for a sync byte to arrive
let mut buf = [0_u8; 1];
loop {
if (rx.uart.read_exact(&mut buf).await).is_ok() && buf[0] == SYNC_BYTE {
break;
}
}
}
async fn rx_task(rx: Rx, crc: crc_engine::CrcHandle) {
let mut frx = uart::framed_rx::FramedUartRx::new(
rx.message_buf,
rx.uart,
SYNC_BYTE,
Duration::from_millis(250),
Duration::from_millis(50),
);
loop {
frx.wait_for_frame_sync().await;
println!("received sync byte from host");
// sync byte was seen, read the length of the message; if
// there aren't enough bytes in the buffer, wait for them,
// with timeouts
match read_frame_fragment(
rx.message_buf,
0,
MESSAGE_LENGTH_SIZE,
&mut rx.uart,
Duration::from_millis(250),
Duration::from_millis(50),
)
.await
{
match frx.read_frame_fragment(0, MESSAGE_LENGTH_SIZE).await {
Ok(()) => {}
Err(UartReadError::Ready) => {
println!("uart ready error reading message length");
rx.message_buf.clear();
Err(uart::ReadError::ReadyCheck) => {
println!("uart ready-check error reading message length");
frx.buf.clear();
continue;
}
Err(UartReadError::BufferCapacity) => {
Err(uart::ReadError::BufferCapacity) => {
println!("insufficient space to read message length");
rx.message_buf.clear();
frx.buf.clear();
continue;
}
Err(UartReadError::Timeout) => {
Err(uart::ReadError::Timeout) => {
// restart loop, since the sync byte seen wasn't
// the beginning of a message, or the sender
// stopped sending
println!("timeout waiting for message message length");
println!("timeout waiting for message length");
continue;
}
}
// get the message length from the buffer
let message_len = LittleEndian::read_u16(&rx.message_buf[..MESSAGE_LENGTH_SIZE]) as usize;
let message_len = LittleEndian::read_u16(&frx.buf[..MESSAGE_LENGTH_SIZE]) as usize;
println!("receiving message of {=u16} bytes", message_len as u16);
// read the message; if there aren't enough bytes in the
// buffer for the message, wait for them to arrive, with
// timeouts
// buffer for the message, wait for them, with timeouts
let message_pos = MESSAGE_LENGTH_SIZE;
match read_frame_fragment(
rx.message_buf,
message_pos,
message_len,
&mut rx.uart,
Duration::from_millis(250),
Duration::from_millis(50),
)
.await
{
match frx.read_frame_fragment(message_pos, message_len).await {
Ok(()) => {}
Err(UartReadError::Ready) => {
println!("uart ready error reading message");
rx.message_buf.clear();
Err(uart::ReadError::ReadyCheck) => {
println!("uart ready-check error reading message body");
frx.buf.clear();
continue;
}
Err(UartReadError::BufferCapacity) => {
println!("insufficient space to read message");
rx.message_buf.clear();
Err(uart::ReadError::BufferCapacity) => {
println!("insufficient space to read message body");
frx.buf.clear();
continue;
}
Err(UartReadError::Timeout) => {
Err(uart::ReadError::Timeout) => {
// restart loop, since the sync byte seen wasn't
// the beginning of a message, or the sender
// stopped sending
println!("timeout waiting for message message");
println!("timeout waiting for message body");
continue;
}
}
@ -237,28 +199,19 @@ async fn rx_task(mut rx: Rx, crc: crc_engine::CrcHandle) {
// read the CRC-16 of the message; if there aren't enough
// bytes in the buffer, wait for them, with timeouts
let crc_pos = message_pos + message_len;
match read_frame_fragment(
rx.message_buf,
crc_pos,
MESSAGE_CRC_SIZE,
&mut rx.uart,
Duration::from_millis(250),
Duration::from_millis(50),
)
.await
{
match frx.read_frame_fragment(crc_pos, MESSAGE_CRC_SIZE).await {
Ok(()) => {}
Err(UartReadError::Ready) => {
println!("uart ready error reading CRC-16");
rx.message_buf.clear();
Err(uart::ReadError::ReadyCheck) => {
println!("uart ready-check error reading CRC-16");
frx.buf.clear();
continue;
}
Err(UartReadError::BufferCapacity) => {
Err(uart::ReadError::BufferCapacity) => {
println!("insufficient space to read CRC-16");
rx.message_buf.clear();
frx.buf.clear();
continue;
}
Err(UartReadError::Timeout) => {
Err(uart::ReadError::Timeout) => {
// restart loop, since the sync byte seen wasn't
// the beginning of a message, or the sender
// stopped sending
@ -268,9 +221,9 @@ async fn rx_task(mut rx: Rx, crc: crc_engine::CrcHandle) {
}
// get the expected CRC-16 from the buffer
let expected_crc = LittleEndian::read_u16(&rx.message_buf[crc_pos..MESSAGE_CRC_SIZE]);
let expected_crc = LittleEndian::read_u16(&frx.buf[crc_pos..MESSAGE_CRC_SIZE]);
// compute the actual CRC-16
let computed_crc = crc.compute(&rx.message_buf[message_pos..message_len]).await;
let computed_crc = crc.compute(&frx.buf[message_pos..message_len]).await;
if computed_crc != expected_crc {
println!(

View file

@ -1,5 +1,5 @@
pub mod echo_ {
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, Clone)]
pub struct EchoRequest {
pub r#data: u32,
}
@ -10,7 +10,37 @@ pub mod echo_ {
}
}
}
impl EchoRequest {}
impl ::core::cmp::PartialEq for EchoRequest {
fn eq(&self, other: &Self) -> bool {
let mut ret = true;
ret &= (self.r#data == other.r#data);
ret
}
}
impl EchoRequest {
///Return a reference to `data`
#[inline]
pub fn r#data(&self) -> &u32 {
&self.r#data
}
///Return a mutable reference to `data`
#[inline]
pub fn mut_data(&mut self) -> &mut u32 {
&mut self.r#data
}
///Set the value of `data`
#[inline]
pub fn set_data(&mut self, value: u32) -> &mut Self {
self.r#data = value.into();
self
}
///Builder method that sets the value of `data`. Useful for initializing the message.
#[inline]
pub fn init_data(mut self, value: u32) -> Self {
self.r#data = value.into();
self
}
}
impl ::micropb::MessageDecode for EchoRequest {
fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(
&mut self,
@ -68,7 +98,7 @@ pub mod echo_ {
size
}
}
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, Clone)]
pub struct EchoResponse {
pub r#data: u32,
}
@ -79,7 +109,37 @@ pub mod echo_ {
}
}
}
impl EchoResponse {}
impl ::core::cmp::PartialEq for EchoResponse {
fn eq(&self, other: &Self) -> bool {
let mut ret = true;
ret &= (self.r#data == other.r#data);
ret
}
}
impl EchoResponse {
///Return a reference to `data`
#[inline]
pub fn r#data(&self) -> &u32 {
&self.r#data
}
///Return a mutable reference to `data`
#[inline]
pub fn mut_data(&mut self) -> &mut u32 {
&mut self.r#data
}
///Set the value of `data`
#[inline]
pub fn set_data(&mut self, value: u32) -> &mut Self {
self.r#data = value.into();
self
}
///Builder method that sets the value of `data`. Useful for initializing the message.
#[inline]
pub fn init_data(mut self, value: u32) -> Self {
self.r#data = value.into();
self
}
}
impl ::micropb::MessageDecode for EchoResponse {
fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(
&mut self,
@ -139,7 +199,7 @@ pub mod echo_ {
}
}
pub mod test_ {
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, Clone)]
pub struct TestResponse {
pub r#f1: u32,
pub r#f2: ::micropb::heapless::String<16>,
@ -156,7 +216,106 @@ pub mod test_ {
}
}
}
impl TestResponse {}
impl ::core::cmp::PartialEq for TestResponse {
fn eq(&self, other: &Self) -> bool {
let mut ret = true;
ret &= (self.r#f1 == other.r#f1);
ret &= (self.r#f2 == other.r#f2);
ret &= (self.r#f3 == other.r#f3);
ret &= (self.r#f4 == other.r#f4);
ret
}
}
impl TestResponse {
///Return a reference to `f1`
#[inline]
pub fn r#f1(&self) -> &u32 {
&self.r#f1
}
///Return a mutable reference to `f1`
#[inline]
pub fn mut_f1(&mut self) -> &mut u32 {
&mut self.r#f1
}
///Set the value of `f1`
#[inline]
pub fn set_f1(&mut self, value: u32) -> &mut Self {
self.r#f1 = value.into();
self
}
///Builder method that sets the value of `f1`. Useful for initializing the message.
#[inline]
pub fn init_f1(mut self, value: u32) -> Self {
self.r#f1 = value.into();
self
}
///Return a reference to `f2`
#[inline]
pub fn r#f2(&self) -> &::micropb::heapless::String<16> {
&self.r#f2
}
///Return a mutable reference to `f2`
#[inline]
pub fn mut_f2(&mut self) -> &mut ::micropb::heapless::String<16> {
&mut self.r#f2
}
///Set the value of `f2`
#[inline]
pub fn set_f2(&mut self, value: ::micropb::heapless::String<16>) -> &mut Self {
self.r#f2 = value.into();
self
}
///Builder method that sets the value of `f2`. Useful for initializing the message.
#[inline]
pub fn init_f2(mut self, value: ::micropb::heapless::String<16>) -> Self {
self.r#f2 = value.into();
self
}
///Return a reference to `f3`
#[inline]
pub fn r#f3(&self) -> &bool {
&self.r#f3
}
///Return a mutable reference to `f3`
#[inline]
pub fn mut_f3(&mut self) -> &mut bool {
&mut self.r#f3
}
///Set the value of `f3`
#[inline]
pub fn set_f3(&mut self, value: bool) -> &mut Self {
self.r#f3 = value.into();
self
}
///Builder method that sets the value of `f3`. Useful for initializing the message.
#[inline]
pub fn init_f3(mut self, value: bool) -> Self {
self.r#f3 = value.into();
self
}
///Return a reference to `f4`
#[inline]
pub fn r#f4(&self) -> &::micropb::heapless::Vec<u8, 8> {
&self.r#f4
}
///Return a mutable reference to `f4`
#[inline]
pub fn mut_f4(&mut self) -> &mut ::micropb::heapless::Vec<u8, 8> {
&mut self.r#f4
}
///Set the value of `f4`
#[inline]
pub fn set_f4(&mut self, value: ::micropb::heapless::Vec<u8, 8>) -> &mut Self {
self.r#f4 = value.into();
self
}
///Builder method that sets the value of `f4`. Useful for initializing the message.
#[inline]
pub fn init_f4(mut self, value: ::micropb::heapless::Vec<u8, 8>) -> Self {
self.r#f4 = value.into();
self
}
}
impl ::micropb::MessageDecode for TestResponse {
fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(
&mut self,
@ -286,7 +445,7 @@ pub mod api_ {
Resp(super::Response),
}
}
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, Clone)]
pub struct Message {
pub r#inner: ::core::option::Option<Message_::Inner>,
}
@ -297,6 +456,13 @@ pub mod api_ {
}
}
}
impl ::core::cmp::PartialEq for Message {
fn eq(&self, other: &Self) -> bool {
let mut ret = true;
ret &= (self.r#inner == other.r#inner);
ret
}
}
impl Message {}
impl ::micropb::MessageDecode for Message {
fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(
@ -402,7 +568,7 @@ pub mod api_ {
Echo(super::super::echo_::EchoRequest),
}
}
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, Clone)]
pub struct Request {
pub r#msg: ::core::option::Option<Request_::Msg>,
}
@ -413,6 +579,13 @@ pub mod api_ {
}
}
}
impl ::core::cmp::PartialEq for Request {
fn eq(&self, other: &Self) -> bool {
let mut ret = true;
ret &= (self.r#msg == other.r#msg);
ret
}
}
impl Request {}
impl ::micropb::MessageDecode for Request {
fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(
@ -491,7 +664,7 @@ pub mod api_ {
Test(super::super::test_::TestResponse),
}
}
#[derive(Debug, PartialEq, Clone)]
#[derive(Debug, Clone)]
pub struct Response {
pub r#msg: ::core::option::Option<Response_::Msg>,
}
@ -502,6 +675,13 @@ pub mod api_ {
}
}
}
impl ::core::cmp::PartialEq for Response {
fn eq(&self, other: &Self) -> bool {
let mut ret = true;
ret &= (self.r#msg == other.r#msg);
ret
}
}
impl Response {}
impl ::micropb::MessageDecode for Response {
fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(

View file

@ -0,0 +1,100 @@
use crate::uart;
use embassy_stm32::usart;
use embassy_time::Duration;
use embedded_io_async::Read;
use micropb::heapless::Vec;
pub struct FramedUartRx<const N: usize> {
pub buf: &'static mut Vec<u8, N>,
uart: usart::BufferedUartRx<'static>,
sync: u8,
first_byte_timeout: Duration,
between_bytes_timeout: Duration,
}
impl<const N: usize> FramedUartRx<N> {
pub fn new(
buf: &'static mut Vec<u8, N>,
uart: usart::BufferedUartRx<'static>,
sync: u8,
first_byte_timeout: Duration,
between_bytes_timeout: Duration,
) -> Self {
Self {
buf,
uart,
sync,
first_byte_timeout,
between_bytes_timeout,
}
}
pub async fn wait_for_frame_sync(&mut self) {
// look for a sync byte in the buffer; the buffer may have
// leftover bytes from a previous cycle (due to an incomplete
// message, or a message which failed validation)
if let Some(sync_pos) = self.buf.iter().position(|&x| x == self.sync) {
// if a sync byte was found, reset the buffer to the data
// following it
//
let post_bytes = self.buf.len() - sync_pos - 1;
#[cfg(not(feature = "copy-within"))]
for i in 0..post_bytes {
self.buf[i] = self.buf[sync_pos + 1 + i];
}
#[cfg(feature = "copy-within")]
self.buf.copy_within(sync_pos + 1.., 0);
self.buf.truncate(post_bytes);
} else {
// wait for a sync byte to arrive
let mut buf = [0_u8; 1];
loop {
if (self.uart.read_exact(&mut buf).await).is_ok() && buf[0] == self.sync {
break;
}
}
}
}
pub async fn read_frame_fragment(
&mut self,
fragment_pos: usize,
fragment_len: usize,
) -> Result<(), uart::ReadError> {
// compute the number of bytes available in the buffer, and the
// number of bytes to read
let bytes_available = self.buf[fragment_pos..].len();
let bytes_to_read = fragment_len.saturating_sub(bytes_available);
if bytes_to_read == 0 {
return Ok(());
}
// if the fragment will not fit in the buffer's available space,
// return an error
if bytes_to_read > (self.buf.capacity() - self.buf.len()) {
return Err(uart::ReadError::BufferCapacity);
}
// if there aren't enough bytes in the buffer for the
// fragment, wait for them to arrive, with a timeout
if let Ok(r) = uart::read_with_timeouts(
&mut self.uart,
&mut self.buf[fragment_pos + bytes_available..bytes_to_read],
self.first_byte_timeout,
self.between_bytes_timeout,
)
.await
{
unsafe {
self.buf.set_len(fragment_pos + bytes_available + r);
}
if r < bytes_to_read {
return Err(uart::ReadError::Timeout);
}
} else {
return Err(uart::ReadError::Timeout);
}
Ok(())
}
}

View file

@ -2,15 +2,16 @@ use embassy_stm32::usart;
use embassy_time::{Duration, WithTimeout};
use embedded_io::ReadReady;
use embedded_io_async::Read;
use micropb::heapless::Vec;
use thiserror::Error;
pub mod framed_rx;
#[derive(Error, Debug)]
pub enum UartReadError {
pub enum ReadError {
#[error("timeout expired waiting for data")]
Timeout,
#[error("ready check on uart failed")]
Ready,
ReadyCheck,
#[error("buffer is too small for message fragment")]
BufferCapacity,
}
@ -23,10 +24,10 @@ pub enum UartReadError {
/// `between_bytes`: if data is available immediately, or after the
/// first byte has been read, will wait this long between bytes
///
/// Returns `UartReadError::Ready` if the initial check for data returns an
/// Returns `ReadError::ReadyCheck` if the initial check for data returns an
/// error
///
/// Returns `UartReadError::Timeout` if the first-byte timeout expires
/// Returns `ReadError::Timeout` if the first-byte timeout expires
///
/// Returns `Ok()` with the number of bytes read in all other cases,
/// including a between-bytes timeout; in that case the data read
@ -36,7 +37,7 @@ async fn read_with_timeouts(
buf: &mut [u8],
first_byte: Duration,
between_bytes: Duration,
) -> Result<usize, UartReadError> {
) -> Result<usize, ReadError> {
// if the requested read size is zero bytes, return immediately
if buf.is_empty() {
return Ok(0);
@ -47,7 +48,7 @@ async fn read_with_timeouts(
if let Ok(ready) = rx.read_ready() {
timeout = if ready { between_bytes } else { first_byte };
} else {
return Err(UartReadError::Ready);
return Err(ReadError::ReadyCheck);
}
while bytes_read < buf.len() {
@ -58,54 +59,9 @@ async fn read_with_timeouts(
if bytes_read > 0 {
return Ok(bytes_read);
}
return Err(UartReadError::Timeout);
return Err(ReadError::Timeout);
}
}
Ok(bytes_read)
}
pub async fn read_frame_fragment<const N: usize>(
buf: &mut Vec<u8, N>,
fragment_pos: usize,
fragment_len: usize,
uart: &mut usart::BufferedUartRx<'_>,
first_byte_timeout: Duration,
between_bytes_timeout: Duration,
) -> Result<(), UartReadError> {
// compute the number of bytes available in the buffer, and the
// number of bytes to read
let bytes_available = buf[fragment_pos..].len();
let bytes_to_read = fragment_len.saturating_sub(bytes_available);
if bytes_to_read == 0 {
return Ok(());
}
// if the fragment will not fit in the buffer's available space,
// return an error
if bytes_to_read > (buf.capacity() - buf.len()) {
return Err(UartReadError::BufferCapacity);
}
// if there aren't enough bytes in the buffer for the
// fragment, wait for them to arrive, with a timeout
if let Ok(r) = read_with_timeouts(
uart,
&mut buf[fragment_pos + bytes_available..bytes_to_read],
first_byte_timeout,
between_bytes_timeout,
)
.await
{
unsafe {
buf.set_len(fragment_pos + bytes_available + r);
}
if r < bytes_to_read {
return Err(UartReadError::Timeout);
}
} else {
return Err(UartReadError::Timeout);
}
Ok(())
}