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]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.9.0" version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
[[package]] [[package]]
name = "block-device-driver" name = "block-device-driver"
@ -245,7 +245,7 @@ dependencies = [
[[package]] [[package]]
name = "embassy-embedded-hal" name = "embassy-embedded-hal"
version = "0.3.0" version = "0.3.0"
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b" source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
dependencies = [ dependencies = [
"defmt 0.3.100", "defmt 0.3.100",
"embassy-futures", "embassy-futures",
@ -263,7 +263,7 @@ dependencies = [
[[package]] [[package]]
name = "embassy-executor" name = "embassy-executor"
version = "0.7.0" version = "0.7.0"
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b" source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
dependencies = [ dependencies = [
"cortex-m", "cortex-m",
"critical-section", "critical-section",
@ -275,7 +275,7 @@ dependencies = [
[[package]] [[package]]
name = "embassy-executor-macros" name = "embassy-executor-macros"
version = "0.6.2" version = "0.6.2"
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b" source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
dependencies = [ dependencies = [
"darling", "darling",
"proc-macro2", "proc-macro2",
@ -286,7 +286,7 @@ dependencies = [
[[package]] [[package]]
name = "embassy-futures" name = "embassy-futures"
version = "0.1.1" version = "0.1.1"
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b" source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
dependencies = [ dependencies = [
"defmt 0.3.100", "defmt 0.3.100",
] ]
@ -294,7 +294,7 @@ dependencies = [
[[package]] [[package]]
name = "embassy-hal-internal" name = "embassy-hal-internal"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b" source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
dependencies = [ dependencies = [
"cortex-m", "cortex-m",
"critical-section", "critical-section",
@ -305,7 +305,7 @@ dependencies = [
[[package]] [[package]]
name = "embassy-net-driver" name = "embassy-net-driver"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b" source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
dependencies = [ dependencies = [
"defmt 0.3.100", "defmt 0.3.100",
] ]
@ -313,11 +313,11 @@ dependencies = [
[[package]] [[package]]
name = "embassy-stm32" name = "embassy-stm32"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b" source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
dependencies = [ dependencies = [
"aligned", "aligned",
"bit_field", "bit_field",
"bitflags 2.9.0", "bitflags 2.9.1",
"block-device-driver", "block-device-driver",
"cfg-if", "cfg-if",
"cortex-m", "cortex-m",
@ -360,7 +360,7 @@ dependencies = [
[[package]] [[package]]
name = "embassy-sync" name = "embassy-sync"
version = "0.6.2" version = "0.6.2"
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b" source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"critical-section", "critical-section",
@ -374,7 +374,7 @@ dependencies = [
[[package]] [[package]]
name = "embassy-time" name = "embassy-time"
version = "0.4.0" version = "0.4.0"
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b" source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"critical-section", "critical-section",
@ -390,7 +390,7 @@ dependencies = [
[[package]] [[package]]
name = "embassy-time-driver" name = "embassy-time-driver"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b" source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
dependencies = [ dependencies = [
"document-features", "document-features",
] ]
@ -398,7 +398,7 @@ dependencies = [
[[package]] [[package]]
name = "embassy-time-queue-utils" name = "embassy-time-queue-utils"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b" source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
dependencies = [ dependencies = [
"embassy-executor", "embassy-executor",
"heapless", "heapless",
@ -407,7 +407,7 @@ dependencies = [
[[package]] [[package]]
name = "embassy-usb-driver" name = "embassy-usb-driver"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b" source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
dependencies = [ dependencies = [
"defmt 0.3.100", "defmt 0.3.100",
"embedded-io-async", "embedded-io-async",
@ -416,7 +416,7 @@ dependencies = [
[[package]] [[package]]
name = "embassy-usb-synopsys-otg" name = "embassy-usb-synopsys-otg"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/embassy-rs/embassy#eb68a81c2ab2d90abd036f3597a1a242c5e8702b" source = "git+https://github.com/embassy-rs/embassy#e8b1ea14c7fb151aa5e296ca8f9724f175bdeaef"
dependencies = [ dependencies = [
"critical-section", "critical-section",
"defmt 0.3.100", "defmt 0.3.100",
@ -516,9 +516,9 @@ dependencies = [
[[package]] [[package]]
name = "errno" name = "errno"
version = "0.3.11" version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys", "windows-sys",
@ -635,9 +635,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]] [[package]]
name = "micropb" name = "micropb"
version = "0.1.1" version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fba9d45f70365e5ecfe23bc1dbe523ce5c3f2e81aa762880961b61ab25c99a7" checksum = "bff8c16c9fb4b03e9b23e0bc17ce3b67465c3f0f75a004a65d604d382c13b7bd"
dependencies = [ dependencies = [
"heapless", "heapless",
"never", "never",
@ -646,9 +646,9 @@ dependencies = [
[[package]] [[package]]
name = "micropb-gen" name = "micropb-gen"
version = "0.1.0" version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21d0a9d827869dd3d834e6992c6a8fdb4229a82b44afe0679e82efe14c50643e" checksum = "dd8b5b73c6511641cc191a1c7ba95cbe86abb9801dcb6f2a6d8d23b79836ee31"
dependencies = [ dependencies = [
"convert_case", "convert_case",
"micropb", "micropb",
@ -862,7 +862,7 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
dependencies = [ dependencies = [
"bitflags 2.9.0", "bitflags 2.9.1",
"errno", "errno",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys",
@ -1100,5 +1100,5 @@ version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [ 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"] } portable-atomic = { version = "1.5", features = ["unsafe-assume-single-core"] }
static_cell = "2.1.0" 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" ] } micropb = { version = "0.1.1", default-features = false, features = [ "encode", "decode", "container-heapless" ] }
defmt = { version = "1.0.0", optional = true } defmt = { version = "1.0.0", optional = true }
defmt-rtt = { version = "1.0.0", optional = true } defmt-rtt = { version = "1.0.0", optional = true }
panic-probe = { version = "1.0.0", optional = true } panic-probe = { version = "1.0.0", optional = true }
panic-halt = "1.0.0" panic-halt = "1.0.0"
thiserror = { version = "2.0.12", default-features = false }
[build-dependencies] [build-dependencies]
micropb-gen = "0.1.0" micropb-gen = "0.1.0"
@ -35,7 +35,7 @@ micropb-gen = "0.1.0"
default = ["board-nucleo64", "status-led", "trace"] default = ["board-nucleo64", "status-led", "trace"]
board-nucleo64 = [] board-nucleo64 = []
status-led = [] status-led = []
use-copy-within = [] copy-within = []
trace = [ trace = [
"dep:defmt", "dep:defmt",
"dep:defmt-rtt", "dep:defmt-rtt",

View file

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

View file

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

View file

@ -1,5 +1,5 @@
pub mod echo_ { pub mod echo_ {
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, Clone)]
pub struct EchoRequest { pub struct EchoRequest {
pub r#data: u32, 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 { impl ::micropb::MessageDecode for EchoRequest {
fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>( fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(
&mut self, &mut self,
@ -68,7 +98,7 @@ pub mod echo_ {
size size
} }
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, Clone)]
pub struct EchoResponse { pub struct EchoResponse {
pub r#data: u32, 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 { impl ::micropb::MessageDecode for EchoResponse {
fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>( fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(
&mut self, &mut self,
@ -139,7 +199,7 @@ pub mod echo_ {
} }
} }
pub mod test_ { pub mod test_ {
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, Clone)]
pub struct TestResponse { pub struct TestResponse {
pub r#f1: u32, pub r#f1: u32,
pub r#f2: ::micropb::heapless::String<16>, 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 { impl ::micropb::MessageDecode for TestResponse {
fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>( fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(
&mut self, &mut self,
@ -286,7 +445,7 @@ pub mod api_ {
Resp(super::Response), Resp(super::Response),
} }
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, Clone)]
pub struct Message { pub struct Message {
pub r#inner: ::core::option::Option<Message_::Inner>, 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 Message {}
impl ::micropb::MessageDecode for Message { impl ::micropb::MessageDecode for Message {
fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>( fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(
@ -402,7 +568,7 @@ pub mod api_ {
Echo(super::super::echo_::EchoRequest), Echo(super::super::echo_::EchoRequest),
} }
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, Clone)]
pub struct Request { pub struct Request {
pub r#msg: ::core::option::Option<Request_::Msg>, 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 Request {}
impl ::micropb::MessageDecode for Request { impl ::micropb::MessageDecode for Request {
fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>( fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>(
@ -491,7 +664,7 @@ pub mod api_ {
Test(super::super::test_::TestResponse), Test(super::super::test_::TestResponse),
} }
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, Clone)]
pub struct Response { pub struct Response {
pub r#msg: ::core::option::Option<Response_::Msg>, 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 Response {}
impl ::micropb::MessageDecode for Response { impl ::micropb::MessageDecode for Response {
fn decode<IMPL_MICROPB_READ: ::micropb::PbRead>( 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 embassy_time::{Duration, WithTimeout};
use embedded_io::ReadReady; use embedded_io::ReadReady;
use embedded_io_async::Read; use embedded_io_async::Read;
use micropb::heapless::Vec;
use thiserror::Error; use thiserror::Error;
pub mod framed_rx;
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum UartReadError { pub enum ReadError {
#[error("timeout expired waiting for data")] #[error("timeout expired waiting for data")]
Timeout, Timeout,
#[error("ready check on uart failed")] #[error("ready check on uart failed")]
Ready, ReadyCheck,
#[error("buffer is too small for message fragment")] #[error("buffer is too small for message fragment")]
BufferCapacity, BufferCapacity,
} }
@ -23,10 +24,10 @@ pub enum UartReadError {
/// `between_bytes`: if data is available immediately, or after the /// `between_bytes`: if data is available immediately, or after the
/// first byte has been read, will wait this long between bytes /// 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 /// 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, /// Returns `Ok()` with the number of bytes read in all other cases,
/// including a between-bytes timeout; in that case the data read /// including a between-bytes timeout; in that case the data read
@ -36,7 +37,7 @@ async fn read_with_timeouts(
buf: &mut [u8], buf: &mut [u8],
first_byte: Duration, first_byte: Duration,
between_bytes: Duration, between_bytes: Duration,
) -> Result<usize, UartReadError> { ) -> Result<usize, ReadError> {
// if the requested read size is zero bytes, return immediately // if the requested read size is zero bytes, return immediately
if buf.is_empty() { if buf.is_empty() {
return Ok(0); return Ok(0);
@ -47,7 +48,7 @@ async fn read_with_timeouts(
if let Ok(ready) = rx.read_ready() { if let Ok(ready) = rx.read_ready() {
timeout = if ready { between_bytes } else { first_byte }; timeout = if ready { between_bytes } else { first_byte };
} else { } else {
return Err(UartReadError::Ready); return Err(ReadError::ReadyCheck);
} }
while bytes_read < buf.len() { while bytes_read < buf.len() {
@ -58,54 +59,9 @@ async fn read_with_timeouts(
if bytes_read > 0 { if bytes_read > 0 {
return Ok(bytes_read); return Ok(bytes_read);
} }
return Err(UartReadError::Timeout); return Err(ReadError::Timeout);
} }
} }
Ok(bytes_read) 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(())
}