refactor: unified host/ communication module, cfg-free run()
- Created host/ module with internal cfg dispatch (USB vs UART) - Moved usb/task.rs → host/usb.rs, uart/task.rs → host/uart.rs - Moved protocol_io.rs → host/framing.rs - Renamed usb_task/uart_task to host_task (uniform name) - Removed all host-comm cfg from main.rs - The run() function now has ZERO board-specific conditional compilation - Deleted old usb/, uart/, protocol_io.rs modules
This commit is contained in:
7 files changed
+44
-79
No files matched your search
File renamed without changes.
@@ -0,0 +1,16 @@
|
|||||||
|
//! Host communication: unified USB/UART transport.
|
||||||
|
//!
|
||||||
|
//! Exports a single `host_task` regardless of transport.
|
||||||
|
//! The cfg dispatch is contained entirely within this module.
|
||||||
|
|
||||||
|
pub mod framing;
|
||||||
|
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(feature = "heltec_v3")] {
|
||||||
|
mod uart;
|
||||||
|
pub use uart::host_task;
|
||||||
|
} else {
|
||||||
|
mod usb;
|
||||||
|
pub use usb::host_task;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +1,16 @@
|
|||||||
//! UART task: COBS-framed fixed-size LE command/response protocol.
|
//! UART host task: COBS-framed fixed-size LE command/response protocol.
|
||||||
//!
|
//!
|
||||||
//! Reads COBS frames from UART, decodes them, parses commands, routes them
|
//! Used for boards with USB-UART bridge chips (e.g. CP2102 on Heltec V3).
|
||||||
//! to the radio or display task, and sends COBS-framed responses back.
|
|
||||||
//! Used for boards with USB-UART bridge chips (e.g. CP2102 on some Heltec V3).
|
|
||||||
|
|
||||||
use defmt::warn;
|
use defmt::warn;
|
||||||
use embassy_executor::task;
|
use embassy_executor::task;
|
||||||
use embedded_io_async::Write;
|
use embedded_io_async::Write;
|
||||||
|
|
||||||
use crate::channel::{CommandChannel, DisplayCommandChannel, ResponseChannel};
|
use crate::channel::{CommandChannel, DisplayCommandChannel, ResponseChannel};
|
||||||
use crate::protocol_io::{self, CobsDecoder, MAX_FRAME};
|
use super::framing::{self, CobsDecoder, MAX_FRAME};
|
||||||
|
|
||||||
#[task]
|
#[task]
|
||||||
pub async fn uart_task(
|
pub async fn host_task(
|
||||||
parts: crate::board::UartParts,
|
parts: crate::board::UartParts,
|
||||||
commands: &'static CommandChannel,
|
commands: &'static CommandChannel,
|
||||||
responses: &'static ResponseChannel,
|
responses: &'static ResponseChannel,
|
||||||
@@ -28,8 +26,7 @@ pub async fn uart_task(
|
|||||||
let mut decoder = CobsDecoder::new();
|
let mut decoder = CobsDecoder::new();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
use embassy_futures::select::select;
|
use embassy_futures::select::{select, Either};
|
||||||
use embassy_futures::select::Either;
|
|
||||||
|
|
||||||
match select(
|
match select(
|
||||||
embedded_io_async::Read::read(&mut rx, &mut read_buf),
|
embedded_io_async::Read::read(&mut rx, &mut read_buf),
|
||||||
@@ -53,7 +50,7 @@ pub async fn uart_task(
|
|||||||
let _ = cmds.push(cmd);
|
let _ = cmds.push(cmd);
|
||||||
});
|
});
|
||||||
for cmd in cmds {
|
for cmd in cmds {
|
||||||
protocol_io::route_command(
|
framing::route_command(
|
||||||
cmd, commands, responses, display_commands, has_display, mac,
|
cmd, commands, responses, display_commands, has_display, mac,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
@@ -61,7 +58,7 @@ pub async fn uart_task(
|
|||||||
}
|
}
|
||||||
Either::Second(response) => {
|
Either::Second(response) => {
|
||||||
if let Some(frame) =
|
if let Some(frame) =
|
||||||
protocol_io::cobs_encode_response(response, &mut write_buf, &mut cobs_encode_buf)
|
framing::cobs_encode_response(response, &mut write_buf, &mut cobs_encode_buf)
|
||||||
{
|
{
|
||||||
if tx.write_all(frame).await.is_err() {
|
if tx.write_all(frame).await.is_err() {
|
||||||
warn!("UART write failed, response dropped");
|
warn!("UART write failed, response dropped");
|
||||||
@@ -1,7 +1,4 @@
|
|||||||
//! USB CDC-ACM task: COBS-framed fixed-size LE command/response protocol.
|
//! USB CDC-ACM host task: COBS-framed fixed-size LE command/response protocol.
|
||||||
//!
|
|
||||||
//! Reads COBS frames from USB, decodes them, parses commands, routes them
|
|
||||||
//! to the radio or display task, and sends COBS-framed responses back.
|
|
||||||
|
|
||||||
use defmt::warn;
|
use defmt::warn;
|
||||||
use embassy_executor::task;
|
use embassy_executor::task;
|
||||||
@@ -11,12 +8,12 @@ use embassy_usb::Builder;
|
|||||||
use static_cell::StaticCell;
|
use static_cell::StaticCell;
|
||||||
|
|
||||||
use crate::channel::{CommandChannel, DisplayCommand, DisplayCommandChannel, ResponseChannel};
|
use crate::channel::{CommandChannel, DisplayCommand, DisplayCommandChannel, ResponseChannel};
|
||||||
use crate::protocol_io::{self, CobsDecoder, MAX_FRAME};
|
use super::framing::{self, CobsDecoder, MAX_FRAME};
|
||||||
|
|
||||||
const MAX_PACKET: usize = 64;
|
const MAX_PACKET: usize = 64;
|
||||||
|
|
||||||
#[task]
|
#[task]
|
||||||
pub async fn usb_task(
|
pub async fn host_task(
|
||||||
parts: crate::board::UsbParts,
|
parts: crate::board::UsbParts,
|
||||||
commands: &'static CommandChannel,
|
commands: &'static CommandChannel,
|
||||||
responses: &'static ResponseChannel,
|
responses: &'static ResponseChannel,
|
||||||
@@ -24,10 +21,6 @@ pub async fn usb_task(
|
|||||||
has_display: bool,
|
has_display: bool,
|
||||||
mac: [u8; 6],
|
mac: [u8; 6],
|
||||||
) {
|
) {
|
||||||
// ── USB device configuration ────────────────────────────────────
|
|
||||||
// VID 0x1209: pid.codes open-source USB vendor ID
|
|
||||||
// PID 0x5741: DongLoRa product ID
|
|
||||||
// Class 0xEF/0x02/0x01: Miscellaneous / Interface Association Descriptor
|
|
||||||
let mut config = embassy_usb::Config::new(0x1209, 0x5741);
|
let mut config = embassy_usb::Config::new(0x1209, 0x5741);
|
||||||
config.manufacturer = Some("DongLoRa");
|
config.manufacturer = Some("DongLoRa");
|
||||||
config.product = Some("DongLoRa LoRa Radio");
|
config.product = Some("DongLoRa LoRa Radio");
|
||||||
@@ -36,7 +29,6 @@ pub async fn usb_task(
|
|||||||
config.device_protocol = 0x01;
|
config.device_protocol = 0x01;
|
||||||
config.composite_with_iads = true;
|
config.composite_with_iads = true;
|
||||||
|
|
||||||
// Use MAC address as USB serial number for unique device identification.
|
|
||||||
static SERIAL_BUF: StaticCell<[u8; 12]> = StaticCell::new();
|
static SERIAL_BUF: StaticCell<[u8; 12]> = StaticCell::new();
|
||||||
let serial_buf = SERIAL_BUF.init([0u8; 12]);
|
let serial_buf = SERIAL_BUF.init([0u8; 12]);
|
||||||
const HEX: &[u8; 16] = b"0123456789ABCDEF";
|
const HEX: &[u8; 16] = b"0123456789ABCDEF";
|
||||||
@@ -64,18 +56,9 @@ pub async fn usb_task(
|
|||||||
let mut usb_dev = builder.build();
|
let mut usb_dev = builder.build();
|
||||||
let (sender, receiver) = class.split();
|
let (sender, receiver) = class.split();
|
||||||
|
|
||||||
// ── Run USB device + protocol loop concurrently ────────────────
|
|
||||||
join(
|
join(
|
||||||
usb_dev.run(),
|
usb_dev.run(),
|
||||||
protocol_loop(
|
protocol_loop(sender, receiver, commands, responses, display_commands, has_display, mac),
|
||||||
sender,
|
|
||||||
receiver,
|
|
||||||
commands,
|
|
||||||
responses,
|
|
||||||
display_commands,
|
|
||||||
has_display,
|
|
||||||
mac,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
@@ -89,8 +72,7 @@ async fn protocol_loop<'d, D: embassy_usb_driver::Driver<'d>>(
|
|||||||
has_display: bool,
|
has_display: bool,
|
||||||
mac: [u8; 6],
|
mac: [u8; 6],
|
||||||
) {
|
) {
|
||||||
use embassy_futures::select::select3;
|
use embassy_futures::select::{select3, Either3};
|
||||||
use embassy_futures::select::Either3;
|
|
||||||
|
|
||||||
let mut read_buf = [0u8; MAX_PACKET];
|
let mut read_buf = [0u8; MAX_PACKET];
|
||||||
let mut write_buf = [0u8; MAX_FRAME];
|
let mut write_buf = [0u8; MAX_FRAME];
|
||||||
@@ -102,7 +84,7 @@ async fn protocol_loop<'d, D: embassy_usb_driver::Driver<'d>>(
|
|||||||
match select3(
|
match select3(
|
||||||
receiver.read_packet(&mut read_buf),
|
receiver.read_packet(&mut read_buf),
|
||||||
responses.receive(),
|
responses.receive(),
|
||||||
embassy_time::Timer::after_millis(250), // DTR poll interval for disconnect detection
|
embassy_time::Timer::after_millis(250),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
@@ -122,7 +104,7 @@ async fn protocol_loop<'d, D: embassy_usb_driver::Driver<'d>>(
|
|||||||
let _ = cmds.push(cmd);
|
let _ = cmds.push(cmd);
|
||||||
});
|
});
|
||||||
for cmd in cmds {
|
for cmd in cmds {
|
||||||
protocol_io::route_command(
|
framing::route_command(
|
||||||
cmd, commands, responses, display_commands, has_display, mac,
|
cmd, commands, responses, display_commands, has_display, mac,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
@@ -130,7 +112,7 @@ async fn protocol_loop<'d, D: embassy_usb_driver::Driver<'d>>(
|
|||||||
}
|
}
|
||||||
Either3::Second(response) => {
|
Either3::Second(response) => {
|
||||||
if let Some(frame) =
|
if let Some(frame) =
|
||||||
protocol_io::cobs_encode_response(response, &mut write_buf, &mut cobs_encode_buf)
|
framing::cobs_encode_response(response, &mut write_buf, &mut cobs_encode_buf)
|
||||||
{
|
{
|
||||||
for chunk in frame.chunks(MAX_PACKET) {
|
for chunk in frame.chunks(MAX_PACKET) {
|
||||||
if sender.write_packet(chunk).await.is_err() {
|
if sender.write_packet(chunk).await.is_err() {
|
||||||
@@ -140,12 +122,9 @@ async fn protocol_loop<'d, D: embassy_usb_driver::Driver<'d>>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Either3::Third(()) => {
|
Either3::Third(()) => {}
|
||||||
// Poll DTR every 250ms to detect host disconnect
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check DTR after every select wake
|
|
||||||
let connected = receiver.dtr();
|
let connected = receiver.dtr();
|
||||||
if was_connected && !connected && has_display {
|
if was_connected && !connected && has_display {
|
||||||
display_commands.send(DisplayCommand::Reset).await;
|
display_commands.send(DisplayCommand::Reset).await;
|
||||||
+12
-31
@@ -25,15 +25,11 @@ mod display;
|
|||||||
mod driver;
|
mod driver;
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
mod hal;
|
mod hal;
|
||||||
|
#[cfg(not(test))]
|
||||||
|
mod host;
|
||||||
mod protocol;
|
mod protocol;
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
mod radio;
|
mod radio;
|
||||||
#[cfg(not(test))]
|
|
||||||
mod protocol_io;
|
|
||||||
#[cfg(all(not(test), feature = "heltec_v3"))]
|
|
||||||
mod uart;
|
|
||||||
#[cfg(all(not(test), not(feature = "heltec_v3")))]
|
|
||||||
mod usb;
|
|
||||||
|
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
@@ -90,31 +86,16 @@ async fn run(spawner: Spawner) {
|
|||||||
.spawn(radio::radio_task(radio, &COMMANDS, &RESPONSES, &STATUS))
|
.spawn(radio::radio_task(radio, &COMMANDS, &RESPONSES, &STATUS))
|
||||||
.expect("spawn radio_task");
|
.expect("spawn radio_task");
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
spawner
|
||||||
if #[cfg(feature = "heltec_v3")] {
|
.spawn(host::host_task(
|
||||||
spawner
|
comm,
|
||||||
.spawn(uart::uart_task(
|
&COMMANDS,
|
||||||
comm,
|
&RESPONSES,
|
||||||
&COMMANDS,
|
&DISPLAY_COMMANDS,
|
||||||
&RESPONSES,
|
has_display,
|
||||||
&DISPLAY_COMMANDS,
|
mac,
|
||||||
has_display,
|
))
|
||||||
mac,
|
.expect("spawn host_task");
|
||||||
))
|
|
||||||
.expect("spawn uart_task");
|
|
||||||
} else {
|
|
||||||
spawner
|
|
||||||
.spawn(usb::usb_task(
|
|
||||||
comm,
|
|
||||||
&COMMANDS,
|
|
||||||
&RESPONSES,
|
|
||||||
&DISPLAY_COMMANDS,
|
|
||||||
has_display,
|
|
||||||
mac,
|
|
||||||
))
|
|
||||||
.expect("spawn usb_task");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(dp) = display {
|
if let Some(dp) = display {
|
||||||
spawner
|
spawner
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
//! UART interface with COBS-framed command/response protocol.
|
|
||||||
|
|
||||||
mod task;
|
|
||||||
pub use task::uart_task;
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
//! USB CDC-ACM interface with COBS-framed command/response protocol.
|
|
||||||
|
|
||||||
mod task;
|
|
||||||
pub use task::usb_task;
|
|
||||||
Reference in new issue
Block a user