From 6604670490cbb36874fd7e36d718b445da88a5bc Mon Sep 17 00:00:00 2001 From: Jannik Beyerstedt Date: Sun, 26 Jul 2026 21:40:55 +0200 Subject: [PATCH] main: Add UART I/O helpers --- Cargo.toml | 2 + src/main.rs | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index fb0d588..bb70ea2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,8 @@ spat_debug = [] # enable ST7789 based display screen = ["dep:mipidsi", "dep:embedded-graphics", "dep:embedded-hal-bus", "dep:profont"] +# internal feature, that any UART I/O is enabled +_uart = [] [dependencies] esp-hal = { version = "~1.1.0", optional = true, features = ["esp32c5", "log-04", "unstable"] } diff --git a/src/main.rs b/src/main.rs index daac5b8..03ef304 100644 --- a/src/main.rs +++ b/src/main.rs @@ -53,6 +53,8 @@ const OWN_STATION_TYPE: ItsStationType = ItsStationType::Cyclist; const OWN_VEHICLE_WIDTH: u8 = 5; // in 10cm steps! #[cfg(feature = "cam_tx")] const OWN_VEHICLE_LENGTH: u16 = 20; // in 10cm steps! +#[cfg(feature = "_uart")] +const SERIAL_FIFO_SIZE_THLD: u8 = 100; // hardware has max. 128 byte // This creates a default app-descriptor required by the esp-idf bootloader. // For more information see: @@ -61,6 +63,11 @@ esp_bootloader_esp_idf::esp_app_desc!(); #[cfg(feature = "gnss")] static GNSS_UPDATE: Mutex>> = Mutex::new(RefCell::new(None)); static WIFI_RX_QUEUE: Mutex>>>> = Mutex::new(RefCell::new(None)); +#[cfg(feature = "_uart")] +static UART_RX_BUF: Mutex>>> = Mutex::new(RefCell::new(None)); +#[cfg(feature = "_uart")] +static SERIAL: Mutex>>> = + Mutex::new(RefCell::new(None)); #[allow( clippy::large_stack_frames, @@ -192,6 +199,25 @@ async fn main(spawner: Spawner) -> ! { loop { embassy_time::Timer::after(embassy_time::Duration::from_millis(1)).await; + #[cfg(feature = "_uart")] + { + let mut new_data = None; + critical_section::with(|cs| { + let mut buf_rc = UART_RX_BUF.borrow(cs).borrow_mut(); + // unwrap is fine b/c we stored something in it before + let buf = buf_rc.as_mut().unwrap(); + + // we move the buffer to new_data to do the processing outside of the critical section lock + if !buf.is_empty() { + new_data = Some(core::mem::take(buf)); + } + }); + + if let Some(data) = new_data { + warn!("TODO: New UART data: {data:x?}"); + } + } + #[cfg(feature = "gnss")] { let mut new_position = false; @@ -573,3 +599,95 @@ where { error!("graphics failed: {error:?}"); } + +#[cfg(feature = "_uart")] +/// Configures UART0 with an interrupt handler and saves it to `SERIAL` +fn setup_uart0( + baud: u32, + cmd_char: Option, + gpio12: esp_hal::peripherals::GPIO12<'static>, + gpio11: esp_hal::peripherals::GPIO11<'static>, + uart0: esp_hal::peripherals::UART0<'static>, + handler: esp_hal::interrupt::InterruptHandler, +) -> Result<(), alloc::string::String> { + critical_section::with(|cs| { + UART_RX_BUF + .borrow(cs) + .replace(Some(alloc::vec::Vec::with_capacity(255))); + }); + + let mut uart = esp_hal::uart::Uart::new( + uart0, + esp_hal::uart::Config::default() + .with_baudrate(baud) + .with_rx( + esp_hal::uart::RxConfig::default() + .with_fifo_full_threshold(u16::from(SERIAL_FIFO_SIZE_THLD)) + .with_timeout(50), + ), + ) + .map_err(|err| alloc::format!("UART0 config error: {err}"))? + .with_rx(gpio12) + .with_tx(gpio11); + + if let Some(char) = cmd_char { + uart.set_at_cmd( + esp_hal::uart::AtCmdConfig::default() + .with_cmd_char(char) + .with_char_num(1) + .with_pre_idle_count(0) // command start can be right after other data + .with_post_idle_count(0) // we don't expect a gap between the command start and subsequent text + .with_gap_timeout(5), // needs to be >0 to work + ); + } + uart.set_interrupt_handler(handler); + uart.listen( + esp_hal::uart::UartInterrupt::AtCmd + | esp_hal::uart::UartInterrupt::RxTimeout + | esp_hal::uart::UartInterrupt::RxFifoFull, // just to be save + ); + + critical_section::with(|cs| SERIAL.borrow_ref_mut(cs).replace(uart)); + + Ok(()) +} + +#[cfg(feature = "_uart")] +#[esp_hal::handler] +fn serial_handler() { + critical_section::with(|cs| { + let mut serial = SERIAL.borrow_ref_mut(cs); + let mut uart_buf = UART_RX_BUF.borrow_ref_mut(cs); + + if let (Some(serial), Some(uart_buf)) = (serial.as_mut(), uart_buf.as_mut()) { + let mut buf = [0u8; SERIAL_FIFO_SIZE_THLD as usize]; + + match serial.read_buffered(&mut buf) { + Ok(0) => {} + Ok(len) => { + uart_buf.extend_from_slice(&buf[..len]); + } + Err(err) => error!("Failed to read from UART: {err}"), + } + + serial.clear_interrupts( + esp_hal::uart::UartInterrupt::RxFifoFull + | esp_hal::uart::UartInterrupt::RxTimeout + | esp_hal::uart::UartInterrupt::AtCmd, + ); + } + }); +} + +#[cfg(feature = "_uart")] +fn send_uart0(data: &[u8]) { + critical_section::with(|cs| { + let mut serial = SERIAL.borrow_ref_mut(cs); + + if let Some(serial) = serial.as_mut() + && let Err(err) = serial.write(data) + { + error!("Failed to write to UART: {err:?}"); + } + }); +}