Use postcard to serialize uart messages

This commit is contained in:
2023-05-21 11:11:28 +02:00
parent 481066a343
commit 5c68b483af
4 changed files with 93 additions and 29 deletions

2
Cargo.lock generated
View File

@ -1333,6 +1333,7 @@ dependencies = [
"atomic-polyfill 1.0.2", "atomic-polyfill 1.0.2",
"cortex-m", "cortex-m",
"cortex-m-rt", "cortex-m-rt",
"crc-any",
"critical-section", "critical-section",
"embassy-executor", "embassy-executor",
"embassy-futures", "embassy-futures",
@ -1355,6 +1356,7 @@ dependencies = [
"postcard", "postcard",
"ron", "ron",
"rtt-target", "rtt-target",
"serde",
"smart-leds", "smart-leds",
"static_cell", "static_cell",
"tgnt", "tgnt",

View File

@ -10,7 +10,6 @@ tgnt = { git = "https://git.nubo.sh/hulthe/tgnt.git", default-features = false }
cortex-m = "0.7.6" cortex-m = "0.7.6"
cortex-m-rt = "0.7" cortex-m-rt = "0.7"
embedded-hal = "0.2.5" embedded-hal = "0.2.5"
#panic-halt = "0.2.0"
usb-device = "0.2.9" usb-device = "0.2.9"
usbd-hid = "0.6.1" usbd-hid = "0.6.1"
static_cell = "1.0.0" static_cell = "1.0.0"
@ -36,6 +35,8 @@ heapless = "0.7.16"
once_cell = { version = "1.17.1", default-features = false } once_cell = { version = "1.17.1", default-features = false }
atomic-polyfill = "1.0.2" atomic-polyfill = "1.0.2"
critical-section = "1.1.1" critical-section = "1.1.1"
crc-any = "2.4.3"
serde = { version = "1.0.163", default-features = false, features = ["derive"] }
#[patch."https://git.nubo.sh/hulthe/tgnt.git"] #[patch."https://git.nubo.sh/hulthe/tgnt.git"]
#tgnt = { path = "../tgnt" } #tgnt = { path = "../tgnt" }

View File

@ -12,6 +12,7 @@ use embassy_sync::pubsub::{ImmediatePublisher, PubSubChannel, Subscriber};
use embassy_time::{Duration, Timer}; use embassy_time::{Duration, Timer};
use futures::{select_biased, FutureExt}; use futures::{select_biased, FutureExt};
use log::{debug, error, info, warn}; use log::{debug, error, info, warn};
use serde::{Deserialize, Serialize};
use static_cell::StaticCell; use static_cell::StaticCell;
use tgnt::{ use tgnt::{
button::{Button, Modifier}, button::{Button, Modifier},
@ -43,13 +44,13 @@ struct State {
} }
/// A keyboard half. /// A keyboard half.
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Half { pub enum Half {
Left, Left,
Right, Right,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Event { pub struct Event {
/// The keyboard half that triggered the event. /// The keyboard half that triggered the event.
pub source: Half, pub source: Half,
@ -60,7 +61,7 @@ pub struct Event {
pub kind: EventKind, pub kind: EventKind,
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub enum EventKind { pub enum EventKind {
PressKey(Key), PressKey(Key),
ReleaseKey(Key), ReleaseKey(Key),

View File

@ -1,25 +1,24 @@
use core::mem::{size_of, transmute}; use crc_any::CRCu16;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_rp::peripherals::{PIN_0, PIN_1, UART0}; use embassy_rp::peripherals::{PIN_0, PIN_1, UART0};
use embassy_rp::uart::{self, BufferedUart, DataBits, Parity, StopBits}; use embassy_rp::uart::{self, BufferedUart, DataBits, Parity, StopBits};
use embassy_time::{with_timeout, Duration, TimeoutError};
use embedded_io::asynch::{Read, Write}; use embedded_io::asynch::{Read, Write};
use futures::{select_biased, FutureExt}; use futures::{select_biased, FutureExt};
use log::{error, info}; use heapless::Vec;
use serde::{Deserialize, Serialize};
use static_cell::StaticCell; use static_cell::StaticCell;
use crate::keyboard::{self, Half, KbEvents}; use crate::keyboard::{self, Half, KbEvents};
use crate::Irqs; use crate::Irqs;
#[derive(Clone, Debug)] #[derive(Clone, Debug, Serialize, Deserialize)]
enum Message { enum Message {
KeyboardEvent(keyboard::Event), KeyboardEvent(keyboard::Event),
} }
pub async fn start(tx: PIN_0, rx: PIN_1, uart: UART0, board: Half, events: KbEvents) { pub async fn start(tx: PIN_0, rx: PIN_1, uart: UART0, board: Half, events: KbEvents) {
static TX_BUF: StaticCell<[u8; 1024]> = StaticCell::new(); static TX_BUF: StaticCell<[u8; 256]> = StaticCell::new();
static RX_BUF: StaticCell<[u8; 1024]> = StaticCell::new(); static RX_BUF: StaticCell<[u8; 256]> = StaticCell::new();
let mut config = uart::Config::default(); let mut config = uart::Config::default();
config.baudrate = 115200; config.baudrate = 115200;
@ -32,8 +31,8 @@ pub async fn start(tx: PIN_0, rx: PIN_1, uart: UART0, board: Half, events: KbEve
Irqs, Irqs,
tx, tx,
rx, rx,
TX_BUF.init_with(|| [0u8; 1024]), TX_BUF.init_with(|| [0u8; 256]),
RX_BUF.init_with(|| [0u8; 1024]), RX_BUF.init_with(|| [0u8; 256]),
config, config,
); );
@ -47,42 +46,103 @@ async fn uart_task(uart: BufferedUart<'static, UART0>, this_half: Half, mut even
let (mut rx, mut tx) = uart.split(); let (mut rx, mut tx) = uart.split();
let (mut events_rx, mut events_tx) = events.split(); let (mut events_rx, mut events_tx) = events.split();
const HEADER_LEN: usize = 4;
let rx_task = async { let rx_task = async {
let mut buf: heapless::Vec<u8, 1024> = Vec::new();
loop { loop {
let mut buf = [0u8; size_of::<Message>()]; if let &[len, random, crc1, crc2, ref rest @ ..] = buf.as_slice() {
// TODO: this timout thing seems sketchy let crc = u16::from_le_bytes([crc1, crc2]);
match with_timeout(Duration::from_millis(5), rx.read_exact(&mut buf)).await { let mut calculated_crc = CRCu16::crc16();
Ok(Ok(_)) => {} calculated_crc.digest(&[len, random]);
Ok(Err(e)) => { let calculated_crc = calculated_crc.get_crc();
error!("uart error: {:?}", e);
if calculated_crc != crc {
log::error!("invalid uart package crc: {:x?}", &buf[..HEADER_LEN]);
buf.remove(0); // pop the first byte and hope we find a good packet header
continue; continue;
} }
Err(TimeoutError) => continue,
};
let message: Message = unsafe { transmute(buf) }; // crimes :) log::debug!("got uart header {:x?}", &buf[..HEADER_LEN]);
match &message {
Message::KeyboardEvent(event) => events_tx.send(event.clone()), let len = usize::from(len);
if rest.len() >= len {
let r = postcard::from_bytes(&rest[..len]);
// drop packet from buffer
buf.rotate_left(len + HEADER_LEN);
buf.truncate(buf.len() - len - HEADER_LEN);
let message: Message = match r {
Ok(v) => v,
Err(e) => {
log::error!("failed to deserialize message: {e}");
continue;
}
};
match &message {
Message::KeyboardEvent(event) => events_tx.send(event.clone()),
}
log::info!("got msg: {:?}", message);
}
}
let mut chunk = [0u8; 128];
let n = match rx.read(&mut chunk).await {
Ok(n) => n,
Err(e) => {
log::error!("uart error: {:?}", e);
continue;
}
};
if buf.extend_from_slice(&chunk[..n]).is_err() {
log::error!("uart buffer full");
buf.clear();
} }
info!("got msg: {:?}", message);
} }
}; };
let tx_task = async { let tx_task = async {
let mut buf = [0u8; 256 + HEADER_LEN];
let mut counter = 1;
loop { loop {
// forward messages to the other keyboard half // forward messages to the other keyboard half
let event = events_rx.recv().await; let event = events_rx.recv().await;
if event.source != this_half { if event.source != this_half {
continue; // do not forward messages from the other half back to it continue; // do not forward messages from the other half back to it
} }
let message = Message::KeyboardEvent(event); let message = Message::KeyboardEvent(event);
let message: [u8; size_of::<Message>()] = unsafe { transmute(message) }; // crimes :) let (header, body) = buf.split_at_mut(HEADER_LEN);
tx.write_all(&message).await.ok(); let serialized = match postcard::to_slice(&message, body) {
Ok(s) => s,
Err(e) => {
log::error!("failed to serialize uart message: {e}");
continue;
}
};
// add a "random" value to feed the crc
let random = counter;
counter += 1;
let len = serialized.len() as u8;
let mut crc = CRCu16::crc16();
crc.digest(&[len, random]);
let [crc1, crc2] = crc.get_crc().to_le_bytes();
header.copy_from_slice(&[len, random, crc1, crc2]);
let package = &buf[..HEADER_LEN + usize::from(len)];
tx.write_all(package).await.ok();
} }
}; };
select_biased! { select_biased! {
_ = tx_task.fuse() => error!("uart tx_task exited"), _ = tx_task.fuse() => log::error!("uart tx_task exited"),
_ = rx_task.fuse() => error!("eart rx_task exited"), _ = rx_task.fuse() => log::error!("eart rx_task exited"),
} }
} }