feat: implement USB serial with COBS-framed postcard protocol

Wire up UsbSerialJtag on ESP32-S3 boards for host communication:
- Read COBS-framed postcard commands from host via serial
- Route DisplayOn/Off to display task, everything else to radio
- Serialize responses back to host as COBS frames
- Use postcard::CobsAccumulator for chunked deserialization
- Concurrent read/write via embassy select

RAK nRF52840 USB (embassy-usb CDC-ACM) is still a stub — different
init path, will be implemented separately.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
swaitsandClaude Opus 4.6 committed 2026-03-29 20:42:55 -06:00
1 parent f1df675363
commit b6fd85495a
5 files changed
+113 -39

No files matched your search

+6 -4
View File
@@ -21,8 +21,8 @@ type Iv = GenericSx126xInterfaceVariant<Output<'static>, Input<'static>>;
type RadioSpiDevice = SpiDevice<'static, NoopRawMutex, SpiBus, Nss>;
pub type RadioDriver = Sx126x<RadioSpiDevice, Iv, Sx1262>;
// TODO: V3 has CP2102 on UART0 — USB OTG on GPIO19/20 goes nowhere
pub type UsbDriver = ();
// V3 has CP2102 on UART0 but also has USB-Serial-JTAG on the ESP32-S3
pub type UsbDriver = esp_hal::usb_serial_jtag::UsbSerialJtag<'static, esp_hal::Async>;
pub type DisplayI2c = I2c<'static, esp_hal::Async>;
@@ -111,8 +111,10 @@ impl Board {
delay: Delay,
};
// ── USB (placeholder — V3 uses CP2102 on UART0) ───────────
let usb = UsbParts { driver: () };
// ── USB Serial (via USB-Serial-JTAG) ───────────────────────
let usb_serial = esp_hal::usb_serial_jtag::UsbSerialJtag::new(p.USB_DEVICE)
.into_async();
let usb = UsbParts { driver: usb_serial };
// ── Display (SSD1306 OLED on I2C, 0x3C) ───────────────────
let i2c = I2c::new(p.I2C0, I2cConfig::default())
+5 -4
View File
@@ -21,8 +21,7 @@ type Iv = GenericSx126xInterfaceVariant<Output<'static>, Input<'static>>;
type RadioSpiDevice = SpiDevice<'static, NoopRawMutex, SpiBus, Nss>;
pub type RadioDriver = Sx126x<RadioSpiDevice, Iv, Sx1262>;
// TODO: USB CDC-ACM (UsbSerialJtag for dev, or full OTG stack for production)
pub type UsbDriver = ();
pub type UsbDriver = esp_hal::usb_serial_jtag::UsbSerialJtag<'static, esp_hal::Async>;
pub type DisplayI2c = I2c<'static, esp_hal::Async>;
@@ -112,8 +111,10 @@ impl Board {
delay: Delay,
};
// ── USB (placeholder) ──────────────────────────────────────
let usb = UsbParts { driver: () };
// ── USB Serial (via USB-Serial-JTAG, shares PHY safely) ───
let usb_serial = esp_hal::usb_serial_jtag::UsbSerialJtag::new(p.USB_DEVICE)
.into_async();
let usb = UsbParts { driver: usb_serial };
// ── Display (SSD1315 OLED on I2C, 0x3C) ───────────────────
// Reset display controller