feat: add COBS framing module
Imports CobsDecoder and cobs_encode_response from firmware's src/host/framing.rs, adjusted so the defmt::warn on encode overflow is gated on the crate's defmt feature rather than an unconditional import. The route_command helper stays in firmware — it depends on embassy channels, which have no business in a transport-agnostic crate.
This commit is contained in:
2 files changed
+95
No files matched your search
@@ -0,0 +1,93 @@
|
|||||||
|
//! COBS framing for the DongLoRa wire protocol.
|
||||||
|
//!
|
||||||
|
//! Every frame sent between host and firmware is a COBS-encoded
|
||||||
|
//! [`Command`] or [`Response`] payload followed by a single `0x00`
|
||||||
|
//! sentinel byte. [`CobsDecoder`] accumulates an inbound byte stream
|
||||||
|
//! and yields parsed [`Command`]s; [`cobs_encode_response`] produces a
|
||||||
|
//! wire-ready byte slice from a [`Response`].
|
||||||
|
//!
|
||||||
|
//! Kept tiny and alloc-free so both the firmware (no_std, bounded
|
||||||
|
//! buffers) and host crates can share it.
|
||||||
|
|
||||||
|
use crate::{Command, Response};
|
||||||
|
|
||||||
|
/// Maximum COBS frame size (bytes).
|
||||||
|
///
|
||||||
|
/// Worst case: tag(1) + rssi(2) + snr(2) + len(2) + payload(256) = 263
|
||||||
|
/// raw bytes. COBS adds `ceil(263/254)+1 = 3` bytes. 266 < 512.
|
||||||
|
pub const MAX_FRAME: usize = 512;
|
||||||
|
|
||||||
|
const _: () = assert!(
|
||||||
|
MAX_FRAME >= crate::MAX_PAYLOAD + 64,
|
||||||
|
"MAX_FRAME too small for max payload + COBS overhead"
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Decodes COBS-framed commands from a byte stream.
|
||||||
|
pub struct CobsDecoder {
|
||||||
|
buf: [u8; MAX_FRAME],
|
||||||
|
len: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for CobsDecoder {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CobsDecoder {
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
buf: [0u8; MAX_FRAME],
|
||||||
|
len: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reset the accumulator (discard partial frame).
|
||||||
|
pub fn reset(&mut self) {
|
||||||
|
self.len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Feed a chunk of bytes, calling `on_command` for each decoded command.
|
||||||
|
pub fn feed(&mut self, data: &[u8], mut on_command: impl FnMut(Command)) {
|
||||||
|
for &byte in data {
|
||||||
|
if byte == 0x00 {
|
||||||
|
// End of COBS frame — decode and dispatch
|
||||||
|
if self.len > 0 {
|
||||||
|
let mut decode_buf = [0u8; MAX_FRAME];
|
||||||
|
if let Some(decoded_len) = ucobs::decode(&self.buf[..self.len], &mut decode_buf)
|
||||||
|
&& let Some(cmd) = Command::from_bytes(&decode_buf[..decoded_len])
|
||||||
|
{
|
||||||
|
on_command(cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.len = 0;
|
||||||
|
} else if self.len < MAX_FRAME {
|
||||||
|
self.buf[self.len] = byte;
|
||||||
|
self.len += 1;
|
||||||
|
} else {
|
||||||
|
// Frame too large — discard
|
||||||
|
self.len = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// COBS-encode a response into `encode_buf` with trailing 0x00 sentinel.
|
||||||
|
///
|
||||||
|
/// Returns the slice to send (encoded frame + sentinel), or `None` on overflow.
|
||||||
|
pub fn cobs_encode_response<'a>(
|
||||||
|
response: Response,
|
||||||
|
write_buf: &mut [u8; MAX_FRAME],
|
||||||
|
encode_buf: &'a mut [u8; MAX_FRAME],
|
||||||
|
) -> Option<&'a [u8]> {
|
||||||
|
let raw_len = response.write_to(write_buf);
|
||||||
|
let encoded_len = ucobs::encode(&write_buf[..raw_len], encode_buf).unwrap_or(0);
|
||||||
|
if encoded_len < encode_buf.len() {
|
||||||
|
encode_buf[encoded_len] = 0x00;
|
||||||
|
Some(&encode_buf[..encoded_len + 1])
|
||||||
|
} else {
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
|
defmt::warn!("COBS encode buffer overflow");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
|
pub mod framing;
|
||||||
|
|
||||||
use heapless::Vec;
|
use heapless::Vec;
|
||||||
|
|
||||||
/// Maximum LoRa payload size in bytes.
|
/// Maximum LoRa payload size in bytes.
|
||||||
|
|||||||
Reference in new issue
Block a user