//! UART host task: COBS-framed fixed-size LE command/response protocol. //! //! Used for boards with USB-UART bridge chips (e.g. CP2102 on Heltec V3). use defmt::warn; use embassy_executor::task; use embedded_io_async::Write; use super::framing::{self, CobsDecoder, MAX_FRAME}; use crate::channel::{CommandChannel, DisplayCommand, DisplayCommandChannel, ResponseChannel}; #[task] pub async fn host_task( parts: crate::board::UartParts, commands: &'static CommandChannel, responses: &'static ResponseChannel, display_commands: &'static DisplayCommandChannel, has_display: bool, mac: [u8; 6], ) { let (mut rx, mut tx) = parts.driver.split(); // UART has no DTR — assume a host is always present. if has_display { display_commands.send(DisplayCommand::On).await; } let mut read_buf = [0u8; 64]; let mut write_buf = [0u8; MAX_FRAME]; let mut cobs_encode_buf = [0u8; MAX_FRAME]; let mut decoder = CobsDecoder::new(); loop { use embassy_futures::select::{select, Either}; match select( embedded_io_async::Read::read(&mut rx, &mut read_buf), responses.receive(), ) .await { Either::First(result) => { let n = match result { Ok(0) => continue, Ok(n) => n, Err(_) => { decoder.reset(); embassy_time::Timer::after_millis(100).await; continue; } }; let mut cmds = heapless::Vec::<_, 4>::new(); decoder.feed(&read_buf[..n], |cmd| { let _ = cmds.push(cmd); }); for cmd in cmds { framing::route_command( cmd, commands, responses, display_commands, has_display, mac, ) .await; } } Either::Second(response) => { #[cfg(feature = "debug-checkpoint")] crate::debug_blink::set(13); if let Some(frame) = framing::cobs_encode_response(response, &mut write_buf, &mut cobs_encode_buf) { if tx.write_all(frame).await.is_err() { warn!("UART write failed, response dropped"); } else { #[cfg(feature = "debug-checkpoint")] crate::debug_blink::set(14); } } } } } }