m5stack_core/io/serial_cmd.rs
1// SPDX-License-Identifier: MIT OR Apache-2.0
2//! HIL serial-cmd transport — exposes a `Read` endpoint for the
3//! `alternator-regulator::serial_cmd` parser.
4//!
5//! - fire27 (ESP32): UART0 RX (GPIO3), already routed through the on-board
6//! USB-serial bridge used by `espflash`. TX side is left to `esp-println`.
7//! - cores3 (ESP32-S3): native USB-Serial-JTAG CDC. Independent USB
8//! endpoint from probe-rs JTAG/RTT.
9
10#[cfg(feature = "fire27")]
11mod imp {
12 use esp_hal::{
13 Blocking,
14 gpio::AnyPin,
15 peripherals::UART0,
16 uart::{Config, UartRx},
17 };
18
19 pub struct SerialCmdResources<'d> {
20 pub uart: UART0<'d>,
21 pub rx_pin: AnyPin<'d>,
22 }
23
24 /// Blocking UartRx — caller drives reads via polling + Timer.
25 ///
26 /// `into_async()` is *not* called here. Two reasons:
27 /// 1. The blocking variant works fine with the polled `run_polled` loop
28 /// and avoids registering a UART interrupt handler entirely.
29 /// 2. **Init ordering matters:** `UartRx::new(...)` (even without
30 /// `into_async`) must run *after* the BLE radio has finished initializing
31 /// on ESP32 — calling it earlier hangs BLE setup silently (LCD goes
32 /// white, no `Device Address` log). The caller is responsible for
33 /// deferring `take_rx` until radio is up.
34 pub type SerialRx<'d> = UartRx<'d, Blocking>;
35
36 pub fn take_rx(r: SerialCmdResources<'static>) -> SerialRx<'static> {
37 UartRx::new(r.uart, Config::default())
38 .expect("UART0 RX init")
39 .with_rx(r.rx_pin)
40 }
41}
42
43#[cfg(feature = "cores3")]
44mod imp {
45 use esp_hal::{
46 Async,
47 peripherals::USB_DEVICE,
48 usb_serial_jtag::UsbSerialJtag,
49 };
50
51 pub struct SerialCmdResources<'d> {
52 pub usb: USB_DEVICE<'d>,
53 }
54
55 pub type SerialRx<'d> = UsbSerialJtag<'d, Async>;
56
57 pub fn take_rx(r: SerialCmdResources<'static>) -> SerialRx<'static> {
58 UsbSerialJtag::new(r.usb).into_async()
59 }
60}
61
62pub use imp::{SerialRx, SerialCmdResources, take_rx};