From 622eeeae82c200e6bb47b809b80717c072a0af50 Mon Sep 17 00:00:00 2001 From: Jannik Beyerstedt Date: Sat, 20 Jun 2026 20:07:48 +0200 Subject: [PATCH 01/26] WIP v2x: drop hopped messages and unsupported message types TODO: Wait for c-its-parser release with updated GN (for Packet.btp_payload, etc.) TODO: Remove explicit GN dependency when feat/gn-pub-use is merged in c-its-parser --- Cargo.lock | 29 ++++++++++++----- Cargo.toml | 4 ++- src/main.rs | 93 +++++++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 103 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0b7e161..2a1246b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -131,9 +131,9 @@ checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" [[package]] name = "bitvec" -version = "1.0.1" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +checksum = "ddcec3d12c579d40898fe0a9a358a803c23e9c52ca3c425707f81c9436211837" dependencies = [ "funty", "radium", @@ -186,14 +186,13 @@ checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "c-its-parser" -version = "2.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "993244dbda1234597ca13fe2e2565fc6f2ece585040404faa011482b55ed544e" +version = "2.2.4" +source = "git+https://github.com/consider-it/C-ITS-Parser.git?branch=feat%2Fgn-update#481dc7a92ee43a0aee5389762e15a17168019b3a" dependencies = [ "chrono", "etherparse", "geo-types", - "geonetworking", + "geonetworking 0.3.0-rc.6 (git+https://github.com/consider-it/geonetworking.git?branch=main)", "hex", "nom", "num-traits", @@ -1156,6 +1155,7 @@ dependencies = [ "esp-radio", "esp-rtos", "geo-types", + "geonetworking 0.3.0-rc.6 (registry+https://github.com/rust-lang/crates.io-index)", "log", "mipidsi", "num-traits", @@ -1347,9 +1347,22 @@ dependencies = [ [[package]] name = "geonetworking" -version = "0.2.1" +version = "0.3.0-rc.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea4f48e7b1cdf91ad77ec1e4c5617aee3666a929288f4a3f11e473b67d3a6314" +checksum = "c60eaa1367ffa087fa9a5c2c9f32578290a808232189e811746d84fcde01e479" +dependencies = [ + "bitvec", + "bitvec-nom2", + "bytes", + "nom", + "num", + "num-traits", +] + +[[package]] +name = "geonetworking" +version = "0.3.0-rc.6" +source = "git+https://github.com/consider-it/geonetworking.git?branch=main#fd97d28f547355e008caced31271361ed2893405" dependencies = [ "bitvec", "bitvec-nom2", diff --git a/Cargo.toml b/Cargo.toml index 3eddcc6..9976afe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,7 +85,8 @@ esp-radio = { version = "0.18.0", optional = true, features = [ embassy_gps = { version = "0.1.0", optional = true, default-features = false, features = ["esp", "log-04"], git = "https://github.com/jbeyerstedt/embassy_gps.git", branch = "feat/rework-gpsfix" } embedded-hal-bus = { version = "0.3.0", optional = true } embedded-graphics = { version = "0.8.2", optional = true } -c-its-parser = { version = "2.2.4", default-features = false, features = [ +# needs newer GN version +c-its-parser = { version = "2.2.4", default-features = false, branch = "feat/gn-update", git = "https://github.com/consider-it/C-ITS-Parser.git", features = [ "time", "geo", "libm", @@ -94,6 +95,7 @@ c-its-parser = { version = "2.2.4", default-features = false, features = [ chrono = { version = "0.4.44", default-features = false, features = ["alloc"] } crossbeam-queue = { version = "0.3.12", default-features = false, features = ["alloc"] } geo-types = { version = "0.7.19", default-features = false } +geonetworking = { version = "0.3.0-rc.5", default-features = false } mipidsi = { version = "0.10.0", optional = true } num-traits = { version = "0.2.19", default-features = false, features = ["libm"] } profont = { version = "0.7.0", optional = true } diff --git a/src/main.rs b/src/main.rs index 1a5d2ef..91b7fc3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ )] // #![deny(clippy::large_stack_frames)] +use alloc::string::ToString as _; use alloc::vec::Vec; use core::cell::RefCell; @@ -23,6 +24,8 @@ use esp_hal::{delay, gpio, spi, time}; #[cfg(all(target_arch = "riscv32", feature = "spat"))] use esp_println::println; use esp_radio::wifi; +// use c_its_parser::gn as geonetworking; +use geonetworking::Decode as _; use log::{debug, error, info, warn}; extern crate alloc; @@ -227,12 +230,12 @@ async fn main(spawner: Spawner) -> ! { } }); if let Some(data) = new_data { - match c_its_parser::de::decode(&data, c_its_parser::Headers::IEEE802LlcGnBtp) { + match c_its_parser::de::decode(&data, c_its_parser::Headers::None) { Ok(msg) => { use c_its_parser::ItsMessage; - use c_its_parser::standards::extensions::ItsMessageId; + // use c_its_parser::standards::extensions::ItsMessageId; - debug!("Got new {:?} message", ItsMessageId::from(&msg)); + // debug!("Got new {:?} message", ItsMessageId::from(&msg)); match msg { #[cfg(feature = "spat")] @@ -276,8 +279,9 @@ async fn main(spawner: Spawner) -> ! { } } Err(err) => { - // will give false-positives when a message is received which wasn't enabled - debug!("Failed to parse V2X message: {err}"); + // we already filtered out all unsupported message types in the wifi rx callback + // so these are actual errors + warn!("Failed to parse V2X message: {err}"); } } } @@ -319,16 +323,77 @@ fn handle_frame(frame: wifi::sniffer::PromiscuousPkt<'_>) { debug!("Received frame with {} bytes", frame.len); - // Send data to main thread - critical_section::with(|cs| { - let queue_rc = WIFI_RX_QUEUE.borrow(cs).borrow(); - // unwrap is fine b/c we stored something in it before - let queue = queue_rc.as_ref().unwrap(); - - if queue.push(frame.data.to_vec()).is_err() { - error!("V2X RX queue is full"); + // parse GN headers + match c_its_parser::pcap::remove_wlan_headers(frame.data) + .map_err(|err| alloc::format!("Failed to parse WLAN headers: {err}")) + .and_then(|data| { + geonetworking::Packet::decode(data) + .map_err(|err| alloc::format!("Failed to parse GN headers: {err:?}")) + }) { + Err(err) => { + warn!("{err}"); } - }); + Ok(packet) => { + // drop hopped (for now) + if packet.decoded.is_hopped() { + return; + } + + // "parse" ITS PDU Header + if let Ok((payload, message_id, _)) = get_its_header(&packet) { + // drop all unsupported message IDs + if match message_id { + #[cfg(feature = "denm")] + c_its_parser::standards::extensions::ItsMessageId::Denm => false, + #[cfg(feature = "cam")] + c_its_parser::standards::extensions::ItsMessageId::Cam => false, + #[cfg(feature = "spat")] + c_its_parser::standards::extensions::ItsMessageId::Spatem + | c_its_parser::standards::extensions::ItsMessageId::Mapem => false, + _ => true, + } { + return; + } + + // Send data to main thread + critical_section::with(|cs| { + let queue_rc = WIFI_RX_QUEUE.borrow(cs).borrow(); + // unwrap is fine b/c we stored something in it before + let queue = queue_rc.as_ref().unwrap(); + + if queue.push(payload.to_vec()).is_err() { + error!("V2X RX queue is full"); + } + }); + } + } + } +} + +fn get_its_header<'p>( + packet: &geonetworking::Decoded>, +) -> Result< + ( + &'p [u8], + c_its_parser::standards::extensions::ItsMessageId, + u32, + ), + alloc::string::String, +> { + let payload = packet.decoded.btp_payload()?; + + if payload.len() < 6 { + return Err("ITS payload too small for ItsPduHeader".to_string()); + } + + if let Ok(message_id) = payload[1].try_into() { + let station_id_buf: [u8; 4] = payload[2..6].try_into().unwrap(); + let station_id = u32::from_be_bytes(station_id_buf); + + Ok((payload, message_id, station_id)) + } else { + Err("Unknown message ID in ItsPduHeader".to_string()) + } } #[cfg(feature = "gnss")] From e7493efcc182e196dfcb380e2485cdbbbbadd169 Mon Sep 17 00:00:00 2001 From: Jannik Beyerstedt Date: Sat, 20 Jun 2026 20:15:11 +0200 Subject: [PATCH 02/26] v2x: Reduce SPAT rate --- src/main.rs | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 84 insertions(+), 5 deletions(-) diff --git a/src/main.rs b/src/main.rs index 91b7fc3..23ed774 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,8 @@ use alloc::string::ToString as _; use alloc::vec::Vec; use core::cell::RefCell; +#[cfg(feature = "spat")] +use core::cell::UnsafeCell; use critical_section::Mutex; use crossbeam_queue::ArrayQueue; @@ -43,6 +45,8 @@ const WIFI_CHANNEL: radio::Channel = 180; pub const DISPLAY_WIDTH: u16 = 320; #[cfg(feature = "screen")] pub const DISPLAY_HEIGHT: u16 = 172; +#[cfg(feature = "spat")] +const SPAT_RATE_LIMIT: u8 = 5; // keep every n-th message // This creates a default app-descriptor required by the esp-idf bootloader. // For more information see: @@ -235,7 +239,7 @@ async fn main(spawner: Spawner) -> ! { use c_its_parser::ItsMessage; // use c_its_parser::standards::extensions::ItsMessageId; - // debug!("Got new {:?} message", ItsMessageId::from(&msg)); + // info!("Got new {:?} message", ItsMessageId::from(&msg)); match msg { #[cfg(feature = "spat")] @@ -295,7 +299,28 @@ async fn main(spawner: Spawner) -> ! { clippy::needless_pass_by_value, reason = "adhering to callback interface" )] +#[allow(clippy::too_many_lines)] fn handle_frame(frame: wifi::sniffer::PromiscuousPkt<'_>) { + #[cfg(feature = "spat")] + const PRUNE_INTERVAL: chrono::Duration = chrono::Duration::seconds(60); + #[cfg(feature = "spat")] + static mut RATELIMIT_CACHE_LAST_PRUNE: UnsafeCell = + UnsafeCell::new(chrono::NaiveDateTime::MIN); + + #[cfg(feature = "spat")] + static mut SPAT_RATELIMIT_CACHE: UnsafeCell> = UnsafeCell::new( + cache::Cache::::new(chrono::Duration::seconds(20)), + ); + + #[cfg(feature = "spat")] + let now = { + let time = esp_hal::time::Instant::now().duration_since_epoch(); + // unwrap is fine since value range is u64 microseconds + chrono::DateTime::from_timestamp(time.as_secs().cast_signed(), 0) + .unwrap() + .naive_utc() + }; + // Ignore frames with errors and non-data frames if frame.rx_cntl.rx_state != 0 { warn!("Received frame has RX error: {}", frame.rx_cntl.rx_state); @@ -340,16 +365,37 @@ fn handle_frame(frame: wifi::sniffer::PromiscuousPkt<'_>) { } // "parse" ITS PDU Header - if let Ok((payload, message_id, _)) = get_its_header(&packet) { - // drop all unsupported message IDs + if let Ok((payload, message_id, station_id)) = get_its_header(&packet) { + // drop all unsupported message IDs and rate-limit SPATEMs if match message_id { #[cfg(feature = "denm")] c_its_parser::standards::extensions::ItsMessageId::Denm => false, #[cfg(feature = "cam")] c_its_parser::standards::extensions::ItsMessageId::Cam => false, #[cfg(feature = "spat")] - c_its_parser::standards::extensions::ItsMessageId::Spatem - | c_its_parser::standards::extensions::ItsMessageId::Mapem => false, + c_its_parser::standards::extensions::ItsMessageId::Spatem => { + // reduce SPAT rate per station ID + let mut drop_msg = false; + unsafe { + #[allow( + static_mut_refs, + reason = "this function will only run consecutive" + )] + let spat_cache = SPAT_RATELIMIT_CACHE.get(); + + (*spat_cache).update_or_init(now, station_id, |i| { + i.msg_count = i.msg_count.wrapping_add(1); + + if (i.msg_count % SPAT_RATE_LIMIT) > 0 { + drop_msg = true; + } + }); + } + + drop_msg + } + #[cfg(feature = "spat")] + c_its_parser::standards::extensions::ItsMessageId::Mapem => false, _ => true, } { return; @@ -368,6 +414,39 @@ fn handle_frame(frame: wifi::sniffer::PromiscuousPkt<'_>) { } } } + + #[cfg(feature = "spat")] + unsafe { + #[allow(static_mut_refs, reason = "this function will only run consecutive")] + let last_prune_time = RATELIMIT_CACHE_LAST_PRUNE.get(); + if *last_prune_time + PRUNE_INTERVAL < now { + #[allow(static_mut_refs, reason = "this function will only run consecutive")] + let spat_cache = SPAT_RATELIMIT_CACHE.get(); + (*spat_cache).prune(now); + + *last_prune_time = now; + } + } +} + +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +struct RatelimitData { + station_id: u32, + msg_count: u8, +} + +impl cache::Cachable for RatelimitData { + fn key(&self) -> u32 { + self.station_id + } +} +impl cache::Initable for RatelimitData { + fn init(key: u32) -> Self { + Self { + station_id: key, + ..Default::default() + } + } } fn get_its_header<'p>( From 8097937cdb9a5b1b6457241c2ede3d3c87f16f48 Mon Sep 17 00:00:00 2001 From: Jannik Beyerstedt Date: Mon, 22 Jun 2026 22:57:48 +0200 Subject: [PATCH 03/26] WIP: try to debug V2X rx dropouts --- src/main.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main.rs b/src/main.rs index 23ed774..d1603f3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -321,6 +321,8 @@ fn handle_frame(frame: wifi::sniffer::PromiscuousPkt<'_>) { .naive_utc() }; + esp_println::print!("."); // TODO: debug only! + // Ignore frames with errors and non-data frames if frame.rx_cntl.rx_state != 0 { warn!("Received frame has RX error: {}", frame.rx_cntl.rx_state); From d7eeb67538b9a307cc7a9a4b50bab6c98e651c9d Mon Sep 17 00:00:00 2001 From: Jannik Beyerstedt Date: Mon, 20 Jul 2026 11:36:05 +0200 Subject: [PATCH 04/26] screen: Move setup to module --- src/main.rs | 79 ++++++--------------------------- src/screen.rs | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+), 65 deletions(-) diff --git a/src/main.rs b/src/main.rs index 1a5d2ef..76557b1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,8 +18,6 @@ use embassy_gps::gps::l76k; use esp_backtrace as _; use esp_hal::clock::CpuClock; use esp_hal::timer::timg::TimerGroup; -#[cfg(feature = "screen")] -use esp_hal::{delay, gpio, spi, time}; #[cfg(all(target_arch = "riscv32", feature = "spat"))] use esp_println::println; use esp_radio::wifi; @@ -36,10 +34,6 @@ mod radio; mod screen; const WIFI_CHANNEL: radio::Channel = 180; -#[cfg(feature = "screen")] -pub const DISPLAY_WIDTH: u16 = 320; -#[cfg(feature = "screen")] -pub const DISPLAY_HEIGHT: u16 = 172; // This creates a default app-descriptor required by the esp-idf bootloader. // For more information see: @@ -84,7 +78,7 @@ async fn main(spawner: Spawner) -> ! { info!("Embassy initialized!"); - // ST7789 172*320px screen + // Setup ST7789 screen // SCL: D8/ GPIO8 (SPI_SCK) // SDA: D10/ GPIO10 (SPI_MOSI) // RES: D3/ GPIO7 @@ -95,43 +89,28 @@ async fn main(spawner: Spawner) -> ! { let mut spi_buffer = [0_u8; 512]; #[cfg(feature = "screen")] let mut screen = { - let (spi, cs) = setup_st7789_spi( + // 172*320px in landscape orientation + let (display, width, height) = screen::make_small_display( peripherals.GPIO8, peripherals.GPIO10, + peripherals.GPIO7, + peripherals.GPIO23, peripherals.GPIO24, - peripherals.GPIO9, + peripherals.GPIO0, peripherals.SPI2, + &mut spi_buffer, ) - .expect("Failed to build SPI config"); - let output_config = gpio::OutputConfig::default(); - let dc_output = gpio::Output::new(peripherals.GPIO23, gpio::Level::Low, output_config); - let rst_output = gpio::Output::new(peripherals.GPIO7, gpio::Level::High, output_config); - let _ = gpio::Output::new(peripherals.GPIO0, gpio::Level::High, output_config); // backlight + .expect("Fatal error building screen"); - let mut display_delay = delay::Delay::new(); + let mut screen = screen::Handler::new(display, width, height); - let spi_device = embedded_hal_bus::spi::ExclusiveDevice::new_no_delay(spi, cs) - .expect("Failed to initialize SPI device"); + let _ = screen + .update(screen::ScreenState::Initial) + .inspect_err(log_screen_error); - let display = mipidsi::Builder::new( - mipidsi::models::ST7789, - mipidsi::interface::SpiInterface::new(spi_device, dc_output, &mut spi_buffer), - ) - .display_size(DISPLAY_HEIGHT + 34, DISPLAY_WIDTH) // somehow this display has 34 px of invisible space on the left (in native orientation) - .reset_pin(rst_output) - .invert_colors(mipidsi::options::ColorInversion::Inverted) - .orientation(mipidsi::options::Orientation::new().rotate(mipidsi::options::Rotation::Deg90)) - .init(&mut display_delay) - .expect("Failed to initialize display"); - - screen::Handler::new(display, DISPLAY_WIDTH, DISPLAY_HEIGHT) + screen }; - #[cfg(feature = "screen")] - let _ = screen - .update(screen::ScreenState::Initial) - .inspect_err(log_screen_error); - // Setup WiFi let (mut wifi_controller, interfaces) = esp_radio::wifi::new( peripherals.WIFI, @@ -155,7 +134,7 @@ async fn main(spawner: Spawner) -> ! { let mac = esp_hal::efuse::base_mac_address(); info!("WiFi MAC: {mac}"); - // Seeed XIAO L67K: + // Setup GNSS: Seeed XIAO L67K: // RX: D7/ GPIO12 // TX: D6/ GPIO11 // WAKEUP: D0/ GPIO1 -> HIGH for active, LOW for Sleep @@ -376,36 +355,6 @@ async fn gnss_task( } } -/// Builds the SPI interface for the ST7789 display -/// -/// Returns (spi master, chip-select) tuple or SPI config error -#[cfg(feature = "screen")] -fn setup_st7789_spi( - scl: esp_hal::peripherals::GPIO8<'static>, - sda: esp_hal::peripherals::GPIO10<'static>, - cs: esp_hal::peripherals::GPIO24<'static>, - miso: esp_hal::peripherals::GPIO9<'static>, - spi: esp_hal::peripherals::SPI2<'static>, -) -> Result< - ( - spi::master::Spi<'static, esp_hal::Blocking>, - gpio::Output<'static>, - ), - spi::master::ConfigError, -> { - let spi_config = spi::master::Config::default() - .with_frequency(time::Rate::from_mhz(40)) - .with_mode(spi::Mode::_0); - let spi: spi::master::Spi<'_, esp_hal::Blocking> = spi::master::Spi::new(spi, spi_config)? - .with_sck(scl) - .with_mosi(sda) - .with_miso(miso); - - let cs_output = gpio::Output::new(cs, gpio::Level::High, gpio::OutputConfig::default()); - - Ok((spi, cs_output)) -} - #[cfg(feature = "screen")] fn log_screen_error(error: &E) where diff --git a/src/screen.rs b/src/screen.rs index d72d0f3..b2ddf90 100644 --- a/src/screen.rs +++ b/src/screen.rs @@ -348,3 +348,123 @@ fn maneuver_to_str(value: applogic::v2x::map::Maneuvers) -> alloc::string::Strin if value.right_allowed { ">" } else { " " }, ) } + +/// Creates an [`mipidsi::Display`], width, height tuple for an ST7789 172*320px screen in landscape orientation +/// +/// # Errors +/// Human-readable fatal errors +#[allow(clippy::too_many_arguments, clippy::type_complexity)] +pub fn make_small_display( + scl: IoScl, + sda: IoSda, + res: IoRes, + dc: IoDc, + cs: IoCs, + bl: IoBl, + spi: Spi, + spi_buffer: &mut [u8], +) -> Result< + ( + mipidsi::Display< + mipidsi::interface::SpiInterface< + '_, + embedded_hal_bus::spi::ExclusiveDevice< + esp_hal::spi::master::Spi<'_, esp_hal::Blocking>, + esp_hal::gpio::Output<'_>, + embedded_hal_bus::spi::NoDelay, + >, + esp_hal::gpio::Output<'_>, + >, + mipidsi::models::ST7789, + esp_hal::gpio::Output<'_>, + >, + u16, + u16, + ), + alloc::string::String, +> +where + IoScl: esp_hal::gpio::interconnect::PeripheralOutput<'static>, + IoSda: esp_hal::gpio::interconnect::PeripheralOutput<'static>, + IoRes: + esp_hal::gpio::interconnect::PeripheralOutput<'static> + esp_hal::gpio::OutputPin + 'static, + IoDc: + esp_hal::gpio::interconnect::PeripheralOutput<'static> + esp_hal::gpio::OutputPin + 'static, + IoCs: + esp_hal::gpio::interconnect::PeripheralOutput<'static> + esp_hal::gpio::OutputPin + 'static, + IoBl: + esp_hal::gpio::interconnect::PeripheralOutput<'static> + esp_hal::gpio::OutputPin + 'static, + Spi: esp_hal::spi::master::Instance + 'static, +{ + const DISPLAY_WIDTH: u16 = 320; + const DISPLAY_HEIGHT: u16 = 172; + const DISPLAY_HIDDEN_X: u16 = 34; // somehow this display has 34 px of invisible space on the left (in native orientation) + + let (spi, cs) = setup_st7789_spi(scl, sda, cs, spi) + .map_err(|err| alloc::format!("Failed to build SPI config: {err}"))?; + + let output_config = esp_hal::gpio::OutputConfig::default(); + let dc_output = esp_hal::gpio::Output::new(dc, esp_hal::gpio::Level::Low, output_config); + let rst_output = esp_hal::gpio::Output::new(res, esp_hal::gpio::Level::High, output_config); + let _ = esp_hal::gpio::Output::new(bl, esp_hal::gpio::Level::High, output_config); // enable backlight + + let mut display_delay = esp_hal::delay::Delay::new(); + + let spi_device = embedded_hal_bus::spi::ExclusiveDevice::new_no_delay(spi, cs) + .map_err(|err| alloc::format!("Failed to initialize SPI device: {err}"))?; + + let display = mipidsi::Builder::new( + mipidsi::models::ST7789, + mipidsi::interface::SpiInterface::new(spi_device, dc_output, spi_buffer), + ) + .display_size(DISPLAY_HEIGHT + DISPLAY_HIDDEN_X, DISPLAY_WIDTH) + .reset_pin(rst_output) + .invert_colors(mipidsi::options::ColorInversion::Inverted) + .orientation(mipidsi::options::Orientation::new().rotate(mipidsi::options::Rotation::Deg90)) + .init(&mut display_delay) + .map_err(|err| alloc::format!("Failed to initialize display: {err:?}"))?; + + Ok((display, DISPLAY_WIDTH, DISPLAY_HEIGHT)) +} + +/// Builds the SPI interface for the ST7789 display +/// +/// Returns (spi master, chip-select) tuple +/// +/// # Errors +/// Returns SPI config error when SPI setup failed +fn setup_st7789_spi( + scl: IoScl, + sda: IoSda, + cs: IoCs, + spi: Spi, +) -> Result< + ( + esp_hal::spi::master::Spi<'static, esp_hal::Blocking>, + esp_hal::gpio::Output<'static>, + ), + esp_hal::spi::master::ConfigError, +> +where + IoScl: esp_hal::gpio::interconnect::PeripheralOutput<'static>, + IoSda: esp_hal::gpio::interconnect::PeripheralOutput<'static>, + IoCs: + esp_hal::gpio::interconnect::PeripheralOutput<'static> + esp_hal::gpio::OutputPin + 'static, + Spi: esp_hal::spi::master::Instance + 'static, +{ + let spi_config = esp_hal::spi::master::Config::default() + .with_frequency(esp_hal::time::Rate::from_mhz(40)) + .with_mode(esp_hal::spi::Mode::_0); + let spi: esp_hal::spi::master::Spi<'_, esp_hal::Blocking> = + esp_hal::spi::master::Spi::new(spi, spi_config)? + .with_sck(scl) + .with_mosi(sda); + + let cs_output = esp_hal::gpio::Output::new( + cs, + esp_hal::gpio::Level::High, + esp_hal::gpio::OutputConfig::default(), + ); + + Ok((spi, cs_output)) +} From 055b03850b1006c96b2910e0f9e0ca403de68647 Mon Sep 17 00:00:00 2001 From: Jannik Beyerstedt Date: Mon, 20 Jul 2026 11:48:15 +0200 Subject: [PATCH 05/26] wifi: Move setup to radio module --- src/main.rs | 11 ++++------- src/radio.rs | 9 +++------ 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/main.rs b/src/main.rs index 76557b1..77dfeaf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -112,7 +112,8 @@ async fn main(spawner: Spawner) -> ! { }; // Setup WiFi - let (mut wifi_controller, interfaces) = esp_radio::wifi::new( + // sadly, it doesn't work any more when we move `esp_radio::wifi::new` to `setup_wifi_sniffer` + let (mut wifi_controller, mut interfaces) = esp_radio::wifi::new( peripherals.WIFI, esp_radio::wifi::ControllerConfig::default(), ) @@ -124,9 +125,7 @@ async fn main(spawner: Spawner) -> ! { critical_section::with(|cs| { WIFI_RX_QUEUE.borrow(cs).replace(Some(ArrayQueue::new(2))); }); - - let mut sniffer = interfaces.sniffer; - radio::setup_wifi_sniffer(WIFI_CHANNEL, &mut sniffer, handle_frame) + radio::setup_wifi_sniffer(WIFI_CHANNEL, &mut interfaces.sniffer, handle_frame) .expect("Fatal error initializing 802.11p sniffer"); info!("WiFi promiscuous mode on channel {WIFI_CHANNEL} running"); @@ -276,9 +275,7 @@ fn handle_frame(frame: wifi::sniffer::PromiscuousPkt<'_>) { warn!("Received frame has RX error: {}", frame.rx_cntl.rx_state); return; } - if !radio::PromiscuousPktType::Data.matches(frame.frame_type) { - debug!("Received frame is not a DATA frame: {}", frame.frame_type); return; } @@ -296,7 +293,7 @@ fn handle_frame(frame: wifi::sniffer::PromiscuousPkt<'_>) { return; } - debug!("Received frame with {} bytes", frame.len); + // info!("Received frame with {} bytes", frame.len); // Send data to main thread critical_section::with(|cs| { diff --git a/src/radio.rs b/src/radio.rs index 325cbf0..46cd72a 100644 --- a/src/radio.rs +++ b/src/radio.rs @@ -1,9 +1,6 @@ //! IEEE 802.11p C-ITS Capture with ESP32-C5 #![allow(unused)] -#[cfg(feature = "esp")] -use esp_radio::wifi::sniffer; - // ----------------------------- // missing interface definitions // ----------------------------- @@ -77,8 +74,8 @@ impl PromiscuousPktType { /// Configure WiFi Sniffer to a certain 802.11p channel pub fn setup_wifi_sniffer( channel: Channel, - sniffer: &mut sniffer::Sniffer, - callback: fn(sniffer::PromiscuousPkt<'_>), + sniffer: &mut esp_radio::wifi::sniffer::Sniffer, + callback: fn(esp_radio::wifi::sniffer::PromiscuousPkt<'_>), ) -> Result<(), alloc::string::String> { sniffer .set_promiscuous_mode(true) @@ -106,7 +103,7 @@ pub fn setup_wifi_sniffer( /// # Errors /// Fails when MAC is not the right size or returns frame send error pub fn send_80211_bcast_frame( - sniffer: &mut sniffer::Sniffer, + sniffer: &mut esp_radio::wifi::sniffer::Sniffer, mac_addr: &[u8], data: &[u8], ) -> Result<(), alloc::string::String> { From f32887df619fb311b546192b3e876c23867fd35c Mon Sep 17 00:00:00 2001 From: Jannik Beyerstedt Date: Mon, 20 Jul 2026 22:05:01 +0200 Subject: [PATCH 06/26] applogic: Move to generic position state interface --- src/applogic/mod.rs | 84 +++++++++++++++++++++++++++------------------ src/main.rs | 13 +++++-- 2 files changed, 61 insertions(+), 36 deletions(-) diff --git a/src/applogic/mod.rs b/src/applogic/mod.rs index ed8b546..bf3252a 100644 --- a/src/applogic/mod.rs +++ b/src/applogic/mod.rs @@ -47,11 +47,7 @@ pub struct State { time: chrono::NaiveDateTime, #[allow(unused)] - position: geo_types::Point, - #[allow(unused)] - heading_deg: Option, - #[allow(unused)] - speed_mps: Option, + pos: PositionState, #[cfg(feature = "spat")] last_prune: chrono::NaiveDateTime, @@ -122,6 +118,41 @@ pub enum SignalPhase { Yellow, } +#[allow(unused)] +#[derive(Debug, Default, Clone, PartialEq)] +pub struct GnssFix { + pub time: Option, + pub latitude_deg: f64, + pub longitude_deg: f64, + pub heading_deg: Option, + pub speed_mps: Option, +} + +#[allow(unused)] +#[derive(Debug, Default, Clone, PartialEq)] +pub struct PositionState { + pub position: geo_types::Point, + pub heading_deg: Option, + pub speed_mps: Option, +} + +impl From<&GnssFix> for PositionState { + fn from(value: &GnssFix) -> Self { + let position = geo_types::Point::new(value.longitude_deg, value.latitude_deg); + + Self { + position, + heading_deg: value.heading_deg, + speed_mps: value.speed_mps, + } + } +} +impl From for PositionState { + fn from(value: GnssFix) -> Self { + (&value).into() + } +} + #[cfg(feature = "spat")] impl core::fmt::Display for SignalPhase { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { @@ -179,9 +210,7 @@ impl State { Self { initialized: false, time: chrono::NaiveDateTime::default(), - position: geo_types::Point::default(), - heading_deg: None, - speed_mps: None, + pos: PositionState::default(), #[cfg(feature = "spat")] last_prune: chrono::NaiveDateTime::default(), #[cfg(feature = "spat")] @@ -191,23 +220,11 @@ impl State { } } - #[cfg(all(feature = "esp", feature = "gnss"))] - pub fn update_with_gpsfix(&mut self, fix: &embassy_gps::types::GpsFix) { - self.position = geo_types::Point::new(fix.longitude_deg, fix.latitude_deg); - - if let Some(heading) = fix.true_course_deg { - self.heading_deg = Some(heading); - } else { - warn!("State: Course missing from GpsFix"); - } - - if let Some(speed) = fix.speed_over_ground { - self.speed_mps = Some(speed); - } else { - warn!("State: Speed missing from GpsFix"); - } - - if let Some(time) = fix.get_timestamp() { + #[allow(unused)] + #[cfg(feature = "esp")] + pub fn update_with_gpsfix(&mut self, fix: &GnssFix) { + self.pos = fix.into(); + if let Some(time) = fix.time { self.time = time; if !self.initialized { @@ -217,18 +234,19 @@ impl State { } } - #[cfg(feature = "gnss")] + #[allow(unused)] pub fn print_fix(&self) -> alloc::string::String { alloc::format!( "{:?}, {:.4} N, {:.4} E, {:.0}°, {:.2} m/s", self.time, - self.position.y(), - self.position.x(), - self.heading_deg.unwrap_or_default(), - self.speed_mps.unwrap_or_default(), + self.pos.position.y(), + self.pos.position.x(), + self.pos.heading_deg.unwrap_or_default(), + self.pos.speed_mps.unwrap_or_default(), ) } + #[allow(clippy::unused_self)] pub fn prune(&mut self) { #[cfg(feature = "spat")] if self.last_prune + PRUNE_INTERVAL < self.time { @@ -252,7 +270,7 @@ impl State { #[cfg(feature = "spat")] pub fn run_glosa(&mut self) -> GlosaOutput { - let Some(own_heading) = self.heading_deg else { + let Some(own_heading) = self.pos.heading_deg else { use alloc::string::ToString; return GlosaOutput::Error("Heading unknown".to_string()); @@ -268,7 +286,7 @@ impl State { ); if let Some(search_result) = - Self::find_intersection(&self.position, own_heading, &self.tlc_cache) + Self::find_intersection(&self.pos.position, own_heading, &self.tlc_cache) { info!( "GLOSA: Approach has signal groups: {:?}", @@ -315,7 +333,7 @@ impl State { &intersection, info.lane_id, info.approach_id, - &self.position, + &self.pos.position, ) { info!("GLOSA: Intersection was left -> back to idle"); ( diff --git a/src/main.rs b/src/main.rs index 77dfeaf..35f6602 100644 --- a/src/main.rs +++ b/src/main.rs @@ -40,8 +40,7 @@ const WIFI_CHANNEL: radio::Channel = 180; esp_bootloader_esp_idf::esp_app_desc!(); #[cfg(feature = "gnss")] -static GNSS_UPDATE: Mutex>> = - Mutex::new(RefCell::new(None)); +static GNSS_UPDATE: Mutex>> = Mutex::new(RefCell::new(None)); static WIFI_RX_QUEUE: Mutex>>>> = Mutex::new(RefCell::new(None)); #[allow( @@ -341,9 +340,17 @@ async fn gnss_task( fix_drop_message = !fix_drop_message; if fix_drop_message { // send to main thread + let pos_state = applogic::GnssFix { + time: fix.get_timestamp(), + latitude_deg: fix.latitude_deg, + longitude_deg: fix.longitude_deg, + heading_deg: fix.true_course_deg, + speed_mps: fix.speed_over_ground, + }; + critical_section::with(|cs| { let gnss_update_ref = GNSS_UPDATE.borrow(cs); - gnss_update_ref.replace(Some(fix)); + gnss_update_ref.replace(Some(pos_state)); }); } } else { From e178c740dbc0e4829b06077767e67a9919437bb9 Mon Sep 17 00:00:00 2001 From: Jannik Beyerstedt Date: Sat, 20 Jun 2026 20:07:48 +0200 Subject: [PATCH 07/26] v2x: drop hopped messages and unsupported message types --- Cargo.lock | 12 +++---- Cargo.toml | 2 +- src/main.rs | 93 +++++++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 86 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0b7e161..ab58dc3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -131,9 +131,9 @@ checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" [[package]] name = "bitvec" -version = "1.0.1" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +checksum = "ddcec3d12c579d40898fe0a9a358a803c23e9c52ca3c425707f81c9436211837" dependencies = [ "funty", "radium", @@ -186,9 +186,9 @@ checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "c-its-parser" -version = "2.2.5" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "993244dbda1234597ca13fe2e2565fc6f2ece585040404faa011482b55ed544e" +checksum = "f9bd739e1f76c017fc9fc6c21b9ff2aee4536bef357075d454e387e94a4d6287" dependencies = [ "chrono", "etherparse", @@ -1347,9 +1347,9 @@ dependencies = [ [[package]] name = "geonetworking" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea4f48e7b1cdf91ad77ec1e4c5617aee3666a929288f4a3f11e473b67d3a6314" +checksum = "6f6d36111f5057d3be7eb4e312a07edac4737fc791e4b0a73d812c6f710161e7" dependencies = [ "bitvec", "bitvec-nom2", diff --git a/Cargo.toml b/Cargo.toml index 3eddcc6..546cb89 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,7 +85,7 @@ esp-radio = { version = "0.18.0", optional = true, features = [ embassy_gps = { version = "0.1.0", optional = true, default-features = false, features = ["esp", "log-04"], git = "https://github.com/jbeyerstedt/embassy_gps.git", branch = "feat/rework-gpsfix" } embedded-hal-bus = { version = "0.3.0", optional = true } embedded-graphics = { version = "0.8.2", optional = true } -c-its-parser = { version = "2.2.4", default-features = false, features = [ +c-its-parser = { version = "2.3.0", default-features = false, features = [ "time", "geo", "libm", diff --git a/src/main.rs b/src/main.rs index 35f6602..7f0fb48 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,9 +7,11 @@ )] // #![deny(clippy::large_stack_frames)] +use alloc::string::ToString as _; use alloc::vec::Vec; use core::cell::RefCell; +use c_its_parser::gn as geonetworking; use critical_section::Mutex; use crossbeam_queue::ArrayQueue; use embassy_executor::Spawner; @@ -21,6 +23,7 @@ use esp_hal::timer::timg::TimerGroup; #[cfg(all(target_arch = "riscv32", feature = "spat"))] use esp_println::println; use esp_radio::wifi; +use geonetworking::Decode as _; use log::{debug, error, info, warn}; extern crate alloc; @@ -204,12 +207,12 @@ async fn main(spawner: Spawner) -> ! { } }); if let Some(data) = new_data { - match c_its_parser::de::decode(&data, c_its_parser::Headers::IEEE802LlcGnBtp) { + match c_its_parser::de::decode(&data, c_its_parser::Headers::None) { Ok(msg) => { use c_its_parser::ItsMessage; - use c_its_parser::standards::extensions::ItsMessageId; + // use c_its_parser::standards::extensions::ItsMessageId; - debug!("Got new {:?} message", ItsMessageId::from(&msg)); + // debug!("Got new {:?} message", ItsMessageId::from(&msg)); match msg { #[cfg(feature = "spat")] @@ -253,8 +256,9 @@ async fn main(spawner: Spawner) -> ! { } } Err(err) => { - // will give false-positives when a message is received which wasn't enabled - debug!("Failed to parse V2X message: {err}"); + // we already filtered out all unsupported message types in the wifi rx callback + // so these are actual errors + warn!("Failed to parse V2X message: {err}"); } } } @@ -294,16 +298,77 @@ fn handle_frame(frame: wifi::sniffer::PromiscuousPkt<'_>) { // info!("Received frame with {} bytes", frame.len); - // Send data to main thread - critical_section::with(|cs| { - let queue_rc = WIFI_RX_QUEUE.borrow(cs).borrow(); - // unwrap is fine b/c we stored something in it before - let queue = queue_rc.as_ref().unwrap(); - - if queue.push(frame.data.to_vec()).is_err() { - error!("V2X RX queue is full"); + // parse GN headers + match c_its_parser::pcap::remove_wlan_headers(frame.data) + .map_err(|err| alloc::format!("Failed to parse WLAN headers: {err}")) + .and_then(|data| { + geonetworking::Packet::decode(data) + .map_err(|err| alloc::format!("Failed to parse GN headers: {err:?}")) + }) { + Err(err) => { + warn!("{err}"); } - }); + Ok(packet) => { + // drop hopped (for now) + if packet.decoded.is_hopped() { + return; + } + + // "parse" ITS PDU Header + if let Ok((payload, message_id, _)) = get_its_header(&packet) { + // drop all unsupported message IDs + if match message_id { + #[cfg(feature = "denm")] + c_its_parser::standards::extensions::ItsMessageId::Denm => false, + #[cfg(feature = "cam")] + c_its_parser::standards::extensions::ItsMessageId::Cam => false, + #[cfg(feature = "spat")] + c_its_parser::standards::extensions::ItsMessageId::Spatem + | c_its_parser::standards::extensions::ItsMessageId::Mapem => false, + _ => true, + } { + return; + } + + // Send data to main thread + critical_section::with(|cs| { + let queue_rc = WIFI_RX_QUEUE.borrow(cs).borrow(); + // unwrap is fine b/c we stored something in it before + let queue = queue_rc.as_ref().unwrap(); + + if queue.push(payload.to_vec()).is_err() { + error!("V2X RX queue is full"); + } + }); + } + } + } +} + +fn get_its_header<'p>( + packet: &geonetworking::Decoded>, +) -> Result< + ( + &'p [u8], + c_its_parser::standards::extensions::ItsMessageId, + u32, + ), + alloc::string::String, +> { + let payload = packet.decoded.btp_payload()?; + + if payload.len() < 6 { + return Err("ITS payload too small for ItsPduHeader".to_string()); + } + + if let Ok(message_id) = payload[1].try_into() { + let station_id_buf: [u8; 4] = payload[2..6].try_into().unwrap(); + let station_id = u32::from_be_bytes(station_id_buf); + + Ok((payload, message_id, station_id)) + } else { + Err("Unknown message ID in ItsPduHeader".to_string()) + } } #[cfg(feature = "gnss")] From 58e5d5ec751564d1514c0c1baaa7b33e616dc876 Mon Sep 17 00:00:00 2001 From: Jannik Beyerstedt Date: Sat, 20 Jun 2026 20:15:11 +0200 Subject: [PATCH 08/26] v2x: Reduce SPAT rate --- src/main.rs | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 84 insertions(+), 5 deletions(-) diff --git a/src/main.rs b/src/main.rs index 7f0fb48..79cf7d9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,8 @@ use alloc::string::ToString as _; use alloc::vec::Vec; use core::cell::RefCell; +#[cfg(feature = "spat")] +use core::cell::UnsafeCell; use c_its_parser::gn as geonetworking; use critical_section::Mutex; @@ -37,6 +39,8 @@ mod radio; mod screen; const WIFI_CHANNEL: radio::Channel = 180; +#[cfg(feature = "spat")] +const SPAT_RATE_LIMIT: u8 = 5; // keep every n-th message // This creates a default app-descriptor required by the esp-idf bootloader. // For more information see: @@ -212,7 +216,7 @@ async fn main(spawner: Spawner) -> ! { use c_its_parser::ItsMessage; // use c_its_parser::standards::extensions::ItsMessageId; - // debug!("Got new {:?} message", ItsMessageId::from(&msg)); + // info!("Got new {:?} message", ItsMessageId::from(&msg)); match msg { #[cfg(feature = "spat")] @@ -272,7 +276,28 @@ async fn main(spawner: Spawner) -> ! { clippy::needless_pass_by_value, reason = "adhering to callback interface" )] +#[allow(clippy::too_many_lines)] fn handle_frame(frame: wifi::sniffer::PromiscuousPkt<'_>) { + #[cfg(feature = "spat")] + const PRUNE_INTERVAL: chrono::Duration = chrono::Duration::seconds(60); + #[cfg(feature = "spat")] + static mut RATELIMIT_CACHE_LAST_PRUNE: UnsafeCell = + UnsafeCell::new(chrono::NaiveDateTime::MIN); + + #[cfg(feature = "spat")] + static mut SPAT_RATELIMIT_CACHE: UnsafeCell> = UnsafeCell::new( + cache::Cache::::new(chrono::Duration::seconds(20)), + ); + + #[cfg(feature = "spat")] + let now = { + let time = esp_hal::time::Instant::now().duration_since_epoch(); + // unwrap is fine since value range is u64 microseconds + chrono::DateTime::from_timestamp(time.as_secs().cast_signed(), 0) + .unwrap() + .naive_utc() + }; + // Ignore frames with errors and non-data frames if frame.rx_cntl.rx_state != 0 { warn!("Received frame has RX error: {}", frame.rx_cntl.rx_state); @@ -315,16 +340,37 @@ fn handle_frame(frame: wifi::sniffer::PromiscuousPkt<'_>) { } // "parse" ITS PDU Header - if let Ok((payload, message_id, _)) = get_its_header(&packet) { - // drop all unsupported message IDs + if let Ok((payload, message_id, station_id)) = get_its_header(&packet) { + // drop all unsupported message IDs and rate-limit SPATEMs if match message_id { #[cfg(feature = "denm")] c_its_parser::standards::extensions::ItsMessageId::Denm => false, #[cfg(feature = "cam")] c_its_parser::standards::extensions::ItsMessageId::Cam => false, #[cfg(feature = "spat")] - c_its_parser::standards::extensions::ItsMessageId::Spatem - | c_its_parser::standards::extensions::ItsMessageId::Mapem => false, + c_its_parser::standards::extensions::ItsMessageId::Spatem => { + // reduce SPAT rate per station ID + let mut drop_msg = false; + unsafe { + #[allow( + static_mut_refs, + reason = "this function will only run consecutive" + )] + let spat_cache = SPAT_RATELIMIT_CACHE.get(); + + (*spat_cache).update_or_init(now, station_id, |i| { + i.msg_count = i.msg_count.wrapping_add(1); + + if (i.msg_count % SPAT_RATE_LIMIT) > 0 { + drop_msg = true; + } + }); + } + + drop_msg + } + #[cfg(feature = "spat")] + c_its_parser::standards::extensions::ItsMessageId::Mapem => false, _ => true, } { return; @@ -343,6 +389,39 @@ fn handle_frame(frame: wifi::sniffer::PromiscuousPkt<'_>) { } } } + + #[cfg(feature = "spat")] + unsafe { + #[allow(static_mut_refs, reason = "this function will only run consecutive")] + let last_prune_time = RATELIMIT_CACHE_LAST_PRUNE.get(); + if *last_prune_time + PRUNE_INTERVAL < now { + #[allow(static_mut_refs, reason = "this function will only run consecutive")] + let spat_cache = SPAT_RATELIMIT_CACHE.get(); + (*spat_cache).prune(now); + + *last_prune_time = now; + } + } +} + +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +struct RatelimitData { + station_id: u32, + msg_count: u8, +} + +impl cache::Cachable for RatelimitData { + fn key(&self) -> u32 { + self.station_id + } +} +impl cache::Initable for RatelimitData { + fn init(key: u32) -> Self { + Self { + station_id: key, + ..Default::default() + } + } } fn get_its_header<'p>( From 29a5d86e438a9afbcbe76759b7bba8ebec883309 Mon Sep 17 00:00:00 2001 From: Jannik Beyerstedt Date: Fri, 15 May 2026 15:17:04 +0200 Subject: [PATCH 09/26] docs: Add BLE and UART protocol specification --- Readme.md | 2 +- docs/ble-protocol.md | 53 +++++++++++++++++ docs/c_its-messages.proto | 122 ++++++++++++++++++++++++++++++++++++++ docs/uart-protocol.md | 20 +++++++ 4 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 docs/ble-protocol.md create mode 100644 docs/c_its-messages.proto create mode 100644 docs/uart-protocol.md diff --git a/Readme.md b/Readme.md index f80ac99..9cbf097 100644 --- a/Readme.md +++ b/Readme.md @@ -1,4 +1,4 @@ -# ESP32-C5 C-ITS Visualizer +# ESP32-C5 C-ITS Companion/ Visualizer Displays different C-ITS receive-only use cases for cheap. diff --git a/docs/ble-protocol.md b/docs/ble-protocol.md new file mode 100644 index 0000000..65f68ff --- /dev/null +++ b/docs/ble-protocol.md @@ -0,0 +1,53 @@ +# Bluetooth LE Protocol + +Some data is made available via BLE so that a smartphone app can be used to display it in other use cases. + +## General + +The BLE protocol contains some standardized services, but can also transport custom services. +Each service is compromised of a set of characteristics which can be read or clients can even subscribe for changes. + +Sadly we can't use any of the standardized service/ profiles to transmit V2X data. +Even notifications about DENMs can't be transmitted since the Alert Notification Profile is only meant for the companion device to query notifications from the phone, but not the other way around. + +So we will only use the Device Information Service to announce ourselves and use a custom service for everything else. + +## V2X BLE Service + +V2X data is exposed via the service UUID `c0b70000-d4f4-4000-ada8-f99a02ee315c` (randomly generated UUID, with byte 3 and 4 set to zero) using the following characteristics: + +UUID | Type | Description +------------------------------------- | --------------- | ----------- +c0b70001-d4f4-4001-ada8-f99a02ee315c | `PositionState` | Set position from external source +c0b70001-d4f4-4001-ada8-f99a02ee315c | `RawMsgRx` | Raw CAM message (de-duplicated/ rate-limited) +c0b70001-d4f4-4002-ada8-f99a02ee315c | `CamEvent` | Latest received CAM +c0b70001-d4f4-4003-ada8-f99a02ee315c | `RawMsgRx` | Raw DENM message (de-duplicated/ rate-limited) +c0b70001-d4f4-4004-ada8-f99a02ee315c | `DenmEvent` | Latest received DENM +c0b70001-d4f4-4005-ada8-f99a02ee315c | `RawMsgRx` | Raw MAPEM message (de-duplicated/ rate-limited) +c0b70001-d4f4-4006-ada8-f99a02ee315c | `RawMsgRx` | Raw SPATEM message (de-duplicated/ rate-limited) + +## Message Format + +Messages are encoded according to the [Protocol Buffers](https://protobuf.dev/) specification since it provides decent binary size and has parser libraries for many programming languages. +All data types are defined in one `.proto` file: [c_its-messages.proto](./c_its-messages.proto). + +### Position State Input + +When there's no GNSS receiver connected to the ESP, position, heading and speed information can be set using a `PositionState` message (see [c_its-messages.proto](./c_its-messages.proto)). + +### DENM Event + +Each received DENM will be "published" as a `DenmEvent` message (see [c_its-messages.proto](./c_its-messages.proto)). +Repetitions of the same message (e.g. no changes to the validity timeframe and location) may be dropped by the BLE server. + +### CAM Event + +Each received (low-frequency) CAM will be "published" as a `CamEvent` message (see [c_its-messages.proto](./c_its-messages.proto)). + +### Raw Received Message + +Received V2X messages will be "published" as a `RawMsgRx` message (see [c_its-messages.proto](./c_its-messages.proto)). +Repetitions of the same message should be dropped by the BLE server. +Consecutive messages with no relevant changes may be dropped by the BLE server while keeping some minimal publishing rate, e.g.: +- only publish MAPEMs every 10 seconds (since content doesn't change, but client may be restarted after first MAPEM from an intersection was received) +- drop high-frequency CAMs (only publish "full" CAMs which include the low-frequency container) diff --git a/docs/c_its-messages.proto b/docs/c_its-messages.proto new file mode 100644 index 0000000..a45083c --- /dev/null +++ b/docs/c_its-messages.proto @@ -0,0 +1,122 @@ +syntax = "proto2"; + +package c_its_io; + +// ------------------------------ +// Data Types +// ------------------------------ + +message Position { + required float latitude_deg = 1; + required float longitude_deg = 2; +} + +enum TrafficDir { + // for all directions of traffic + ALL_DIRECTIONS = 0; + // for the direction of traffic according to the reference direction, and the portion of traffic upstream of the reference position + UPSTREAM = 1; + // for the direction of traffic according to the reference direction, and the portion of traffic downstream of the reference position + DOWNSTREAM = 2; + // for the direction of traffic opposite to the reference direction + OPPOSITE = 3; +} + +message VehicleContainer { + optional uint32 heading_deg = 1; // vehicle heading in degrees, if not "unavailable" + optional uint32 speed = 2; // vehicle speed in 1/100 m/s, if not "unavailable" + optional uint32 vehicle_length_dm = 3; // vehicle length in 1/10 m, if not "unavailable" + optional uint32 vehicle_width_dm = 4; // vehicle width in 1/10 m, if not "unavailable" +} + + +// GeoNetworking Destination Area Shape +enum GNAreaShape { + ETSI_AREASHAPE_CIRCLE = 0; + ETSI_AREASHAPE_RECTANGLE = 1; + ETSI_AREASHAPE_ELLIPSIS = 2; + ETSI_AREASHAPE_UNKNOWN = 15; +} + +// GeoNetworking Destination Area/ Source Position +message ITSPosition { + optional int32 latitude = 1; // Latitude in 1/10th of micro degrees + optional int32 longitude = 2; // Longitude in 1/10th of micro degrees +} + +// GeoNetworking Destination Area +message GNArea { + optional GNAreaShape area = 1 [default = ETSI_AREASHAPE_UNKNOWN]; + optional ITSPosition position = 2; + optional int32 distA = 3; // Distance A of the geometric shape in meters + optional int32 distB = 4; // Distance B of the geometric shape in meters, mandatory for rectangle and ellipsis + optional int32 angle = 5; // Angle of the geometric shape in degrees, mandatory for rectangle and ellipsis +} + +// GeoNetworking Transport +enum GNTransport { + GN_TRANSPORT_DEFAULT = 0; + GN_TRANSPORT_GUC = 2; // GeoUnicast + GN_TRANSPORT_GAC = 3; // Geographically-Scoped Anycast + GN_TRANSPORT_GBC = 4; // Geographically-Scoped broadcast + GN_TRANSPORT_TSB = 5; // Topologically-scoped broadcast + GN_TRANSPORT_SHB = 7; // Single hop broadcast +} + +message RawMsgTx { + required bytes payload = 1; // as UPER + required uint32 message_type = 2; // ITS `MessageId` integer value + + required GNTransport gnTransport = 3; + required GNArea gnArea = 4; + required uint32 hopLimit = 5; +} + +// ------------------------------ +// Messages +// ------------------------------ + +message PositionState { + required Position position = 1; + optional uint32 heading_deg = 2; + optional uint32 speed = 3; // speed in 1/100 m/s +} + +message DenmEvent { + required fixed64 timestamp = 1; // DENM reference time as UNIX time + required fixed64 validity_end_ts = 2; // UNIX time when event validity ends (from denm.management.validityDuration) + required fixed32 station_id = 3; // unique ID of the event when combined with `seq_num` + required uint32 seq_num = 4; // unique ID of the event when combined with `station_id` + optional Position event_position = 5; // event location (from denm.management.eventPosition) + optional uint32 event_heading_deg = 6; // event heading (from denm.location.eventPositionHeading) + optional TrafficDir direction = 7; // relevance traffic direction (from denm.management.trafficDirection) + optional uint32 cause_code = 8; + optional uint32 sub_cause_code = 9; +} + +message CamEvent { + required fixed32 station_id = 1; // Station ID of the vehicle + required uint32 station_type = 2; // ITS `StationType` + required Position position = 3; // vehicle position + optional uint32 vehicle_role = 4; // ITS `VehicleRole` + optional VehicleContainer vehicle_data = 5; // speed, etc unless CAM contains an RSU container +} + +message RawMsgRx { + required uint32 msg_type = 1; // ITS message type (`MessageId` from PDU header) + required bytes payload = 2; // UPER message with Geonetworking and BTP headers +} + +message SerialOutputMsg { + oneof payload { + DenmEvent denm = 1; + RawMsgRx rx_msg = 2; + } +} + +message SerialInputMsg { + oneof payload { + PositionState position = 1; + RawMsgTx tx_msg = 2; + } +} diff --git a/docs/uart-protocol.md b/docs/uart-protocol.md new file mode 100644 index 0000000..6a765e3 --- /dev/null +++ b/docs/uart-protocol.md @@ -0,0 +1,20 @@ +# UART Protocol + +If enabled using the `TODO` feature flag, all received messages are transmitted on UART using the protocol described in this document. +The serial communication can also be used to send V2X data. + +## Message Format + +The UART communication is using a custom framing protocol to encapsulate binary messages. +The payload is: + +- a `SerialOutputMsg` for transmissions from the ESP to the host or +- a `SerialInputMsg` for transmissions from the host to the ESP + +in binary [Proto](https://protobuf.dev/) serialization according to the [c_its-messages.proto](./c_its-messages.proto) definition. + +Each transmission: +- starts with a Start of Frame (SOF) sequence: `0x56, 0x32, 0x58, 0x2B` ("V2X+") +- followed by a 16-bit big-endian length field +- followed by the payload +- followed by an End of Frame (EOF) sequence `0x0d, 0x0a` From 21db09ad7f3ad140755eb3ac1548bc35989692e3 Mon Sep 17 00:00:00 2001 From: Jannik Beyerstedt Date: Wed, 22 Jul 2026 15:30:47 +0200 Subject: [PATCH 10/26] build: Add "io" module for BLE/ UART messages --- Cargo.lock | 123 ++++++++++++++++++++++++++++++++++++++ Cargo.toml | 4 ++ build.rs | 12 +++- docs/c_its-messages.proto | 4 +- src/io.rs | 51 ++++++++++++++++ src/main.rs | 3 +- 6 files changed, 193 insertions(+), 4 deletions(-) create mode 100644 src/io.rs diff --git a/Cargo.lock b/Cargo.lock index ab58dc3..359840b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -763,6 +763,16 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "esp-alloc" version = "0.10.0" @@ -1160,6 +1170,8 @@ dependencies = [ "mipidsi", "num-traits", "profont", + "prost", + "prost-build", ] [[package]] @@ -1234,12 +1246,24 @@ dependencies = [ "arrayvec", ] +[[package]] +name = "fastrand" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" + [[package]] name = "find-msvc-tools" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + [[package]] name = "float-cmp" version = "0.9.0" @@ -1549,6 +1573,12 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b23ac50abb8261cb38c6e2a7192d3302e0836dac1628f6a93b82b4fad185897" +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + [[package]] name = "litrs" version = "1.0.0" @@ -1612,6 +1642,12 @@ dependencies = [ "heapless 0.8.0", ] +[[package]] +name = "multimap" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" + [[package]] name = "nb" version = "0.1.3" @@ -1770,6 +1806,17 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "petgraph" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" +dependencies = [ + "fixedbitset", + "hashbrown 0.15.5", + "indexmap", +] + [[package]] name = "pin-project-lite" version = "0.2.17" @@ -1849,6 +1896,57 @@ dependencies = [ "embedded-graphics", ] +[[package]] +name = "prost" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528ac67416ff8646872a3c02cad9cc4ee5dc9f9540c9b10771855c95cb2e5ae1" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03da047801ff44bb6a4d407d4860c05fd70bb81714e6b2f3812603d5b145b042" +dependencies = [ + "heck", + "itertools", + "log", + "multimap", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn 2.0.117", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "prost-types" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f94967dc7688f3054c7fac87473ffae4cc4c3904800e2d9f5b857246d8963b0a" +dependencies = [ + "prost", +] + [[package]] name = "quote" version = "1.0.45" @@ -2037,6 +2135,19 @@ dependencies = [ "svgbobdoc", ] +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -2248,6 +2359,18 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "once_cell", + "rustix", + "windows-sys", +] + [[package]] name = "termcolor" version = "1.4.1" diff --git a/Cargo.toml b/Cargo.toml index 546cb89..ac69615 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -97,11 +97,15 @@ geo-types = { version = "0.7.19", default-features = false } mipidsi = { version = "0.10.0", optional = true } num-traits = { version = "0.2.19", default-features = false, features = ["libm"] } profont = { version = "0.7.0", optional = true } +prost = { version = "0.14", default-features = false, features = ["derive"] } [dev-dependencies] assert_float_eq = "1.2.0" env_logger = "0.11.10" +[build-dependencies] +prost-build = "0.14" + # For fine tuning these settings, please refer to https://doc.rust-lang.org/cargo/reference/profiles.html [profile.dev] diff --git a/build.rs b/build.rs index 7ad15d3..f376e0e 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,6 @@ use std::env; -fn main() { +fn main() -> std::io::Result<()> { let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap(); if target_arch == "riscv32" { @@ -8,6 +8,16 @@ fn main() { // make sure linkall.x is the last linker script (otherwise might cause problems with flip-link) println!("cargo:rustc-link-arg=-Tlinkall.x"); } + + let descriptor_path = + std::path::PathBuf::from(env::var("OUT_DIR").unwrap()).join("proto_descriptor.bin"); + + prost_build::Config::new() + .file_descriptor_set_path(&descriptor_path) + .compile_well_known_types() + .compile_protos(&["docs/c_its-messages.proto"], &["docs/"])?; + + Ok(()) } fn linker_be_nice() { diff --git a/docs/c_its-messages.proto b/docs/c_its-messages.proto index a45083c..c2ca54e 100644 --- a/docs/c_its-messages.proto +++ b/docs/c_its-messages.proto @@ -78,8 +78,8 @@ message RawMsgTx { message PositionState { required Position position = 1; - optional uint32 heading_deg = 2; - optional uint32 speed = 3; // speed in 1/100 m/s + optional uint32 heading = 3; // heading in 1/10 deg + optional uint32 speed = 4; // speed in 1/100 m/s } message DenmEvent { diff --git a/src/io.rs b/src/io.rs new file mode 100644 index 0000000..cb23055 --- /dev/null +++ b/src/io.rs @@ -0,0 +1,51 @@ +//! UART/ BLE I/O Protocol + +pub mod msg { + #![allow(clippy::all, clippy::pedantic, clippy::nursery, dead_code)] + include!(concat!(env!("OUT_DIR"), "/c_its_io.rs")); +} + +impl From for msg::SerialOutputMsg { + fn from(value: msg::RawMsgRx) -> Self { + use msg::serial_output_msg::Payload; + + Self { + payload: Some(Payload::RxMsg(value)), + } + } +} + +impl From for msg::SerialOutputMsg { + fn from(value: msg::DenmEvent) -> Self { + use msg::serial_output_msg::Payload; + + Self { + payload: Some(Payload::Denm(value)), + } + } +} + +impl From for crate::applogic::PositionState { + fn from(value: msg::PositionState) -> Self { + let position = geo_types::Point::new( + value.position.longitude_deg.into(), + value.position.latitude_deg.into(), + ); + let heading_deg = value.heading.map(|v| { + #[allow(clippy::cast_precision_loss)] + let val = (v as f32) / 10.; + val + }); + let speed_mps = value.speed.map(|v| { + #[allow(clippy::cast_precision_loss)] + let val = (v as f32) / 100.; + val + }); + + Self { + position, + heading_deg, + speed_mps, + } + } +} diff --git a/src/main.rs b/src/main.rs index 79cf7d9..c0d9e3a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,7 +26,7 @@ use esp_hal::timer::timg::TimerGroup; use esp_println::println; use esp_radio::wifi; use geonetworking::Decode as _; -use log::{debug, error, info, warn}; +use log::{error, info, warn}; extern crate alloc; @@ -34,6 +34,7 @@ mod applogic; #[cfg(feature = "spat")] mod cache; mod geo_alg; +mod io; mod radio; #[cfg(feature = "screen")] mod screen; From fc3581d0d1d8595457c956b37f9634a6435634d6 Mon Sep 17 00:00:00 2001 From: Jannik Beyerstedt Date: Wed, 22 Jul 2026 16:15:58 +0200 Subject: [PATCH 11/26] main: Fix feature flags --- src/main.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index c0d9e3a..bddb837 100644 --- a/src/main.rs +++ b/src/main.rs @@ -31,7 +31,6 @@ use log::{error, info, warn}; extern crate alloc; mod applogic; -#[cfg(feature = "spat")] mod cache; mod geo_alg; mod io; @@ -405,17 +404,20 @@ fn handle_frame(frame: wifi::sniffer::PromiscuousPkt<'_>) { } } +#[cfg(feature = "spat")] #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] struct RatelimitData { station_id: u32, msg_count: u8, } +#[cfg(feature = "spat")] impl cache::Cachable for RatelimitData { fn key(&self) -> u32 { self.station_id } } +#[cfg(feature = "spat")] impl cache::Initable for RatelimitData { fn init(key: u32) -> Self { Self { From baaee5f6ffd1d51cb80b06eca6acbc830dabebec Mon Sep 17 00:00:00 2001 From: Jannik Beyerstedt Date: Wed, 22 Jul 2026 21:20:48 +0200 Subject: [PATCH 12/26] logging: Only print to jtag-serial Otherwise esp-println will use UART0 when no USB-JTAG/Serial is connected. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index ac69615..f322c5b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,7 +72,7 @@ esp-backtrace = { version = "0.19.0", optional = true, features = [ "panic-handler", "println", ] } -esp-println = { version = "0.17.0", optional = true, features = ["esp32c5", "log-04"] } +esp-println = { version = "0.17.0", optional = true, default-features = false, features = ["colors", "critical-section", "jtag-serial", "esp32c5", "log-04"] } esp-radio = { version = "0.18.0", optional = true, features = [ "esp-alloc", "esp32c5", From 7668c9bb7bd5151879f5e3ff181eebd076f076c3 Mon Sep 17 00:00:00 2001 From: Jannik Beyerstedt Date: Fri, 24 Jul 2026 11:51:00 +0200 Subject: [PATCH 13/26] proto/RawMsgTx: Add station type for source position vector --- docs/c_its-messages.proto | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/c_its-messages.proto b/docs/c_its-messages.proto index c2ca54e..9848910 100644 --- a/docs/c_its-messages.proto +++ b/docs/c_its-messages.proto @@ -70,6 +70,8 @@ message RawMsgTx { required GNTransport gnTransport = 3; required GNArea gnArea = 4; required uint32 hopLimit = 5; + + optional uint32 stationType = 6; // for source position vector } // ------------------------------ From 1d4e82335d02e6b513933c6defd783a76898314a Mon Sep 17 00:00:00 2001 From: Jannik Beyerstedt Date: Fri, 24 Jul 2026 11:39:35 +0200 Subject: [PATCH 14/26] feat(docs): Explain supported feature combinations, add test script --- Readme.md | 9 ++++-- feat-permut.sh | 69 +++++++++++++++++++++++++++++++++++++++++++++ src/applogic/mod.rs | 12 ++++---- 3 files changed, 82 insertions(+), 8 deletions(-) create mode 100755 feat-permut.sh diff --git a/Readme.md b/Readme.md index 9cbf097..8659877 100644 --- a/Readme.md +++ b/Readme.md @@ -52,14 +52,19 @@ rustup component add --toolchain nightly rustfmt ### Build and Flash In default configuration only signal phases (SPAT) data is displayed for the upcoming intersection. -Additional features are available though feature flags: +Additional features are available through feature flags: - `esp`: Run on ESP (required as feature flag to enable unit tests on host) - `std`: Run on system with `std` library -- `spat`: Enable signal phase (and intersection map) data +- `spat`: Enable signal phase (and intersection map) data +- `spat_debug`: Enable additional debug information for the SPAT/ GLOSA use case - `denm`: Enable DENMs - `cam`: Enable CAMs +Officially supported feature combinations are: +- `esp,spat,gnss,screen` (default features) +- To be implemented: No GNSS and screen when BLE/ UART interface is implemented + Connect your ESP32-C5 and run: ``` diff --git a/feat-permut.sh b/feat-permut.sh new file mode 100755 index 0000000..cd20431 --- /dev/null +++ b/feat-permut.sh @@ -0,0 +1,69 @@ +#!/bin/bash +## Build/ Test/ Clippy all supported feature permutations +set -eu + +mode=${1:-help} +wasm_target="--chrome --headless" +extended=${2:-false} + + +features_base=( + #cam,denm,uart + spat,gnss,screen +) +features_more=( + spat,gnss,screen,cam,denm +) +features_test=( + spat + spat,cam,denm +) + + +runBuild () { + for feat in "${features[@]}"; do + echo "cargo build for $feat ..." + cargo build --release --no-default-features -F esp,$feat + done +} + +runTest () { + for feat in "${features_test[@]}"; do + echo "cargo test for $feat ..." + cargo test --target host-tuple --bin test --no-default-features -F std,$feat + done +} + +runClippy () { + for feat in "${features[@]}"; do + echo "clippy for $feat ..." + cargo clippy --no-default-features -F esp,$feat -- -Wclippy::pedantic + done +} + +if [ $extended == "--more" ]; then + echo "running with extended feature list" + features=( "${features_base[@]}" "${features_more[@]}" ) +else + echo "running standard feature list" + features=( "${features_base[@]}" ) +fi + +case $mode in +build) + runBuild + ;; +clippy) + runClippy + ;; +test) + runTest + ;; +all) + runBuild + runClippy + runTest + ;; +*) + echo "Usage: $0 [build | clippy | test | all] [--more]" +esac diff --git a/src/applogic/mod.rs b/src/applogic/mod.rs index bf3252a..98d100f 100644 --- a/src/applogic/mod.rs +++ b/src/applogic/mod.rs @@ -6,7 +6,7 @@ use alloc::rc::Rc; use alloc::vec::Vec; use log::info; -#[cfg(any(feature = "gnss", feature = "spat"))] +#[cfg(feature = "spat")] use log::warn; #[cfg(feature = "spat")] use log::{debug, error}; @@ -688,13 +688,13 @@ mod tests { let own_pos_passed = geo_types::Point::new(9.977217, 53.5560602); let own_heading = 90.; - state.heading_deg = Some(own_heading); + state.pos.heading_deg = Some(own_heading); - state.position = own_pos_infront; + state.pos.position = own_pos_infront; state.run_glosa(); assert!(matches!(state.glosa_state, GlosaState::Idle)); - state.position = own_pos_inside; + state.pos.position = own_pos_inside; state.run_glosa(); assert!(matches!(state.glosa_state, GlosaState::Locked(_))); if let GlosaState::Locked(info) = &state.glosa_state { @@ -702,7 +702,7 @@ mod tests { assert!(info.lane_id == 1 || info.lane_id == 2); } - state.position = own_pos_inside; + state.pos.position = own_pos_inside; state.run_glosa(); assert!(matches!(state.glosa_state, GlosaState::Locked(_))); if let GlosaState::Locked(info) = &state.glosa_state { @@ -710,7 +710,7 @@ mod tests { assert!(info.lane_id == 1 || info.lane_id == 2); } - state.position = own_pos_passed; + state.pos.position = own_pos_passed; state.run_glosa(); assert!(matches!(state.glosa_state, GlosaState::Idle)); } From 2379a7eefc69d2342b5041b7d35ee371864d4a71 Mon Sep 17 00:00:00 2001 From: Jannik Beyerstedt Date: Fri, 3 Jul 2026 13:38:54 +0200 Subject: [PATCH 15/26] Enable V2X transmission, add optional stand-alone CAM transmission --- Cargo.lock | 22 +++-- Cargo.toml | 5 +- Readme.md | 7 ++ feat-permut.sh | 2 + src/applogic/cam_tx.rs | 197 +++++++++++++++++++++++++++++++++++++++++ src/applogic/mod.rs | 2 + src/main.rs | 68 +++++++++++++- src/v2x_tx/gn.rs | 94 ++++++++++++++++++++ src/v2x_tx/mod.rs | 160 +++++++++++++++++++++++++++++++++ 9 files changed, 543 insertions(+), 14 deletions(-) create mode 100644 src/applogic/cam_tx.rs create mode 100644 src/v2x_tx/gn.rs create mode 100644 src/v2x_tx/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 359840b..3fba778 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,6 +73,12 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +[[package]] +name = "arbitrary-int" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993a810118f8f37e9c4411c86f1c4c940a09a7ab34b7bf2d88d06f50c553fab7" + [[package]] name = "arrayvec" version = "0.7.6" @@ -186,9 +192,9 @@ checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "c-its-parser" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9bd739e1f76c017fc9fc6c21b9ff2aee4536bef357075d454e387e94a4d6287" +checksum = "cd4db66f495280ec88eef8712dd9ab7d1aacc590f62485e3e03a89b3ce4f8b14" dependencies = [ "chrono", "etherparse", @@ -1239,9 +1245,9 @@ dependencies = [ [[package]] name = "etherparse" -version = "0.20.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ac016aaf11dfe643edcd088a166234bfcb72e7f06691abfcf21e9af524037f4" +checksum = "17304d06addb3283cdc4bd528e42dd95e73c8ee2d6492ffce415e93660885449" dependencies = [ "arrayvec", ] @@ -1371,13 +1377,11 @@ dependencies = [ [[package]] name = "geonetworking" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f6d36111f5057d3be7eb4e312a07edac4737fc791e4b0a73d812c6f710161e7" +checksum = "6da6fde770112587d5f5f7af439546e35bd35aa77a9e3e47e9968056783fe50d" dependencies = [ - "bitvec", - "bitvec-nom2", - "bytes", + "arbitrary-int", "nom", "num", "num-traits", diff --git a/Cargo.toml b/Cargo.toml index f322c5b..834f164 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,8 @@ std = ["c-its-parser/std"] gnss = ["dep:embassy_gps"] # echo CAM data on serial cam = ["c-its-parser/cam"] +# send CAM autonomously +cam_tx = ["c-its-parser/cam", "gnss"] # echo DENM events on serial and BLE denm = ["c-its-parser/denm"] # evaluate SPAT information @@ -85,7 +87,8 @@ esp-radio = { version = "0.18.0", optional = true, features = [ embassy_gps = { version = "0.1.0", optional = true, default-features = false, features = ["esp", "log-04"], git = "https://github.com/jbeyerstedt/embassy_gps.git", branch = "feat/rework-gpsfix" } embedded-hal-bus = { version = "0.3.0", optional = true } embedded-graphics = { version = "0.8.2", optional = true } -c-its-parser = { version = "2.3.0", default-features = false, features = [ +# needs newer GN with removed bitvec dependency +c-its-parser = { version = "2.4.0", default-features = false, features = [ "time", "geo", "libm", diff --git a/Readme.md b/Readme.md index 8659877..281f995 100644 --- a/Readme.md +++ b/Readme.md @@ -28,6 +28,10 @@ Shall "publish" the most relevant information of a DENMs via BLE ### CAM Just prints a line for each received CAM. +### CAM-TX +Generates and transmits a CAM when a GNSS update was received. +Depends on the `gnss` feature. + ## Usage @@ -60,9 +64,12 @@ Additional features are available through feature flags: - `spat_debug`: Enable additional debug information for the SPAT/ GLOSA use case - `denm`: Enable DENMs - `cam`: Enable CAMs +- `cam_tx`: Enable CAM generation and transmission Officially supported feature combinations are: - `esp,spat,gnss,screen` (default features) +- `esp,spat,gnss,screen,cam_tx` +- `esp,spat,gnss,screen,cam_tx,cam,denm` - To be implemented: No GNSS and screen when BLE/ UART interface is implemented Connect your ESP32-C5 and run: diff --git a/feat-permut.sh b/feat-permut.sh index cd20431..a40a8ca 100755 --- a/feat-permut.sh +++ b/feat-permut.sh @@ -10,9 +10,11 @@ extended=${2:-false} features_base=( #cam,denm,uart spat,gnss,screen + spat,gnss,screen,cam_tx ) features_more=( spat,gnss,screen,cam,denm + spat,gnss,screen,cam_tx,cam,denm ) features_test=( spat diff --git a/src/applogic/cam_tx.rs b/src/applogic/cam_tx.rs new file mode 100644 index 0000000..7de554e --- /dev/null +++ b/src/applogic/cam_tx.rs @@ -0,0 +1,197 @@ +//! C-ITS CAM Transmission + +use alloc::vec::Vec; + +use c_its_parser::gn as geonetworking; +use c_its_parser::standards::cam_1_4_1::cam_pdu_descriptions; +use c_its_parser::standards::cdd_1_3_1_1::its_container; +use c_its_parser::standards::extensions; + +#[derive(Debug)] +pub struct CamState { + station_id: u32, + station_type: its_container::StationType, + + vehicle_length_dm: u16, // vehicle length is in 10cm steps + vehicle_width_dm: u8, // vehicle width is in 10cm steps + + time: chrono::DateTime, + pos_state: super::PositionState, +} + +impl CamState { + pub fn new( + station_id: u32, + station_type: c_its_parser::standards::extensions::ItsStationType, + vehicle_length_dm: u16, + vehicle_width_dm: u8, + ) -> Self { + Self { + station_id, + station_type: station_type.into(), + vehicle_length_dm, + vehicle_width_dm, + time: chrono::DateTime::::default(), + pos_state: super::PositionState::default(), + } + } + + pub fn update(&mut self, time: chrono::DateTime, pos: super::PositionState) { + self.time = time; + self.pos_state = pos; + + // TODO: build CAM trace + } + + #[allow(clippy::too_many_lines)] + fn make_cam(&self) -> Result { + let header = its_container::ItsPduHeader::new( + 2, + extensions::ItsMessageId::Cam.as_u8(), + its_container::StationID(self.station_id), + ); + + let generation_delta_time = { + let its_timestamp = c_its_parser::time_utils::TimestampIts::from(self.time); + + #[allow(clippy::cast_possible_truncation)] + let ts_mod = (its_timestamp.0 % 65_536) as u16; + + cam_pdu_descriptions::GenerationDeltaTime(ts_mod) + }; + + let heading = match self.pos_state.heading_deg { + Some(value) => its_container::Heading { + heading_value: its_container::HeadingValue::from_deg(value) + .map_err(|err| alloc::format!("Failed to make HeadingValue: {err}"))?, + heading_confidence: its_container::HeadingConfidence(127), // unavailable for now + }, + None => its_container::Heading { + heading_value: its_container::HeadingValue(0), + heading_confidence: its_container::HeadingConfidence(127), // unavailable for now + }, + }; + let speed = match self.pos_state.speed_mps { + Some(value) => its_container::Speed { + speed_value: its_container::SpeedValue::from_mps(value) + .map_err(|err| alloc::format!("Failed to make SpeedValue: {err}"))?, + speed_confidence: its_container::SpeedConfidence(127), // unavailable for now + }, + None => its_container::Speed { + speed_value: its_container::SpeedValue(0), + speed_confidence: its_container::SpeedConfidence(127), // unavailable for now + }, + }; + let reference_position = its_container::ReferencePosition { + latitude: its_container::Latitude::from_deg(self.pos_state.position.y()), + longitude: its_container::Longitude::from_deg(self.pos_state.position.x()), + position_confidence_ellipse: its_container::PosConfidenceEllipse { + semi_major_confidence: its_container::SemiAxisLength(4095), // unavailable for now + semi_minor_confidence: its_container::SemiAxisLength(4095), // unavailable for now + semi_major_orientation: its_container::HeadingValue::unavailable(), + }, + altitude: its_container::Altitude { + altitude_value: its_container::AltitudeValue(800_001), // unavailable for now + altitude_confidence: its_container::AltitudeConfidence::unavailable, + }, + }; + + let basic_container = cam_pdu_descriptions::BasicContainer::new( + self.station_type.clone(), + reference_position, + ); + let high_frequency_container = + cam_pdu_descriptions::HighFrequencyContainer::basicVehicleContainerHighFrequency( + cam_pdu_descriptions::BasicVehicleContainerHighFrequency { + heading, + speed, + drive_direction: its_container::DriveDirection::unavailable, + vehicle_length: its_container::VehicleLength { + vehicle_length_value: its_container::VehicleLengthValue( + self.vehicle_length_dm, + ), + vehicle_length_confidence_indication: + its_container::VehicleLengthConfidenceIndication::unavailable, + }, + vehicle_width: its_container::VehicleWidth(self.vehicle_width_dm), + longitudinal_acceleration: its_container::LongitudinalAcceleration { + longitudinal_acceleration_value: + its_container::LongitudinalAccelerationValue::unavailable(), + longitudinal_acceleration_confidence: its_container::AccelerationConfidence( + 102, // unavailable + ), + }, + curvature: its_container::Curvature { + curvature_value: its_container::CurvatureValue::unavailable(), + curvature_confidence: its_container::CurvatureConfidence::unavailable, + }, + curvature_calculation_mode: + its_container::CurvatureCalculationMode::unavailable, + yaw_rate: its_container::YawRate { + yaw_rate_value: its_container::YawRateValue::unavailable(), + yaw_rate_confidence: its_container::YawRateConfidence::unavailable, + }, + acceleration_control: None, + lane_position: None, + steering_wheel_angle: None, + lateral_acceleration: None, + vertical_acceleration: None, + performance_class: None, + cen_dsrc_tolling_zone: None, + }, + ); + let low_frequency_container = + cam_pdu_descriptions::LowFrequencyContainer::basicVehicleContainerLowFrequency( + cam_pdu_descriptions::BasicVehicleContainerLowFrequency { + vehicle_role: its_container::VehicleRole::default, + exterior_lights: its_container::ExteriorLights::default(), + path_history: its_container::PathHistory(alloc::vec![]), // TODO: fill trace + }, + ); + + let cam_parameters = cam_pdu_descriptions::CamParameters::new( + basic_container, + high_frequency_container, + Some(low_frequency_container), + None, + ); + + Ok(cam_pdu_descriptions::CAM::new( + header, + cam_pdu_descriptions::CoopAwareness::new(generation_delta_time, cam_parameters), + )) + } +} + +impl crate::v2x_tx::GnItsPayload for CamState { + fn make_eh( + &self, + address: [u8; 6], + own_position: &crate::applogic::PositionState, + time: chrono::DateTime, + _seq_no: &mut u16, + ) -> ( + geonetworking::en302636_4_1::ExtendedHeader, + Option, + u8, + ) { + let station_type = geonetworking::en302636_4_1::StationType::try_from(self.station_type.0) + .unwrap_or_default(); + + // CAM always uses SHB, hop-limit 1 + ( + crate::v2x_tx::gn::make_shb_eh(address, station_type, own_position, time), + None, + 1, + ) + } + + fn make_payload( + &self, + ) -> Result<(Vec, c_its_parser::standards::extensions::ItsMessageId), alloc::string::String> + { + let msg = self.make_cam()?; + let uper = msg.encode_to_uper()?; + Ok((uper, c_its_parser::standards::extensions::ItsMessageId::Cam)) + } +} diff --git a/src/applogic/mod.rs b/src/applogic/mod.rs index 98d100f..d1d082e 100644 --- a/src/applogic/mod.rs +++ b/src/applogic/mod.rs @@ -18,6 +18,8 @@ pub mod v2x; #[cfg(feature = "cam")] pub mod cam; +#[cfg(feature = "cam_tx")] +pub mod cam_tx; #[cfg(feature = "denm")] pub mod denm; #[cfg(feature = "spat")] diff --git a/src/main.rs b/src/main.rs index bddb837..daac5b8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,6 +14,8 @@ use core::cell::RefCell; use core::cell::UnsafeCell; use c_its_parser::gn as geonetworking; +#[cfg(feature = "cam_tx")] +use c_its_parser::standards::extensions::ItsStationType; use critical_section::Mutex; use crossbeam_queue::ArrayQueue; use embassy_executor::Spawner; @@ -37,10 +39,20 @@ mod io; mod radio; #[cfg(feature = "screen")] mod screen; +#[cfg(feature = "cam_tx")] +mod v2x_tx; const WIFI_CHANNEL: radio::Channel = 180; #[cfg(feature = "spat")] const SPAT_RATE_LIMIT: u8 = 5; // keep every n-th message +#[cfg(feature = "cam_tx")] +const GN_IS_MOBILE: bool = true; +#[cfg(feature = "cam_tx")] +const OWN_STATION_TYPE: ItsStationType = ItsStationType::Cyclist; +#[cfg(feature = "cam_tx")] +const OWN_VEHICLE_WIDTH: u8 = 5; // in 10cm steps! +#[cfg(feature = "cam_tx")] +const OWN_VEHICLE_LENGTH: u16 = 20; // in 10cm steps! // This creates a default app-descriptor required by the esp-idf bootloader. // For more information see: @@ -119,7 +131,7 @@ async fn main(spawner: Spawner) -> ! { // Setup WiFi // sadly, it doesn't work any more when we move `esp_radio::wifi::new` to `setup_wifi_sniffer` - let (mut wifi_controller, mut interfaces) = esp_radio::wifi::new( + let (mut wifi_controller, interfaces) = esp_radio::wifi::new( peripherals.WIFI, esp_radio::wifi::ControllerConfig::default(), ) @@ -131,7 +143,8 @@ async fn main(spawner: Spawner) -> ! { critical_section::with(|cs| { WIFI_RX_QUEUE.borrow(cs).replace(Some(ArrayQueue::new(2))); }); - radio::setup_wifi_sniffer(WIFI_CHANNEL, &mut interfaces.sniffer, handle_frame) + let mut wlan_iface = interfaces.sniffer; + radio::setup_wifi_sniffer(WIFI_CHANNEL, &mut wlan_iface, handle_frame) .expect("Fatal error initializing 802.11p sniffer"); info!("WiFi promiscuous mode on channel {WIFI_CHANNEL} running"); @@ -159,6 +172,23 @@ async fn main(spawner: Spawner) -> ! { info!("GNSS task started, waiting for GNSS fix..."); let mut state = applogic::State::new(); + #[cfg(feature = "cam_tx")] + let (mut seq_no, mut cam, mac_bytes) = { + let station_id = v2x_tx::make_station_id(mac); + info!("V2X Station ID: {station_id}"); + + // unwrap is fine since [`esp_hal::efuse::MacAddress`] is just an `[u8; 6]` + let mac_bytes = mac.as_bytes().try_into().unwrap(); + + let cam = applogic::cam_tx::CamState::new( + station_id, + OWN_STATION_TYPE, + OWN_VEHICLE_LENGTH, + OWN_VEHICLE_WIDTH, + ); + + (0, cam, mac_bytes) + }; loop { embassy_time::Timer::after(embassy_time::Duration::from_millis(1)).await; @@ -171,6 +201,33 @@ async fn main(spawner: Spawner) -> ! { if let Some(fix) = gnss_update_ref.replace(None) { state.update_with_gpsfix(&fix); new_position = true; + + #[cfg(feature = "cam_tx")] + { + use v2x_tx::GnItsPayload; + let time = state.time().and_utc(); + + cam.update(time, fix.clone().into()); + + match cam.encode_packet( + mac_bytes, + &fix.into(), + time, + GN_IS_MOBILE, + &mut seq_no, + ) { + Ok(data) => { + if let Err(err) = radio::send_80211_bcast_frame( + &mut wlan_iface, + mac.as_bytes(), + &data, + ) { + warn!("Failed to send 802.11 frame: {err}"); + } + } + Err(err) => warn!("Failed to create CAM: {err}"), + } + } } }); @@ -236,12 +293,15 @@ async fn main(spawner: Spawner) -> ! { } } - #[cfg(feature = "cam")] + #[cfg(any(feature = "cam", feature = "cam_tx"))] ItsMessage::Cam { geonetworking: _, transport: _, etsi, - } => applogic::cam::handle_cam(&etsi), + } => { + #[cfg(feature = "cam")] + applogic::cam::handle_cam(&etsi); + } #[cfg(feature = "denm")] ItsMessage::DenmV2 { diff --git a/src/v2x_tx/gn.rs b/src/v2x_tx/gn.rs new file mode 100644 index 0000000..828c74d --- /dev/null +++ b/src/v2x_tx/gn.rs @@ -0,0 +1,94 @@ +//! Geonetworking utilities + +use c_its_parser::gn as geonetworking; + +#[allow(unused)] +pub fn make_shb_eh( + address: [u8; 6], + station_type: geonetworking::en302636_4_1::StationType, + own_position: &crate::applogic::PositionState, + time: chrono::DateTime, +) -> geonetworking::en302636_4_1::ExtendedHeader { + let source_position_vector = make_lpv(address, station_type, own_position, time); + + geonetworking::en302636_4_1::ExtendedHeader::SHB( + geonetworking::en302636_4_1::SingleHopBroadcast { + source_position_vector, + media_dependent_data: [0; 4], + }, + ) +} + +#[allow(unused)] +pub fn make_tsb_eh( + address: [u8; 6], + station_type: geonetworking::en302636_4_1::StationType, + own_position: &crate::applogic::PositionState, + time: chrono::DateTime, + sequence_number: u16, +) -> geonetworking::en302636_4_1::ExtendedHeader { + let source_position_vector = make_lpv(address, station_type, own_position, time); + + geonetworking::en302636_4_1::ExtendedHeader::TSB( + geonetworking::en302636_4_1::TopologicallyScopedBroadcast::new( + sequence_number, + source_position_vector, + ), + ) +} + +#[allow(unused)] +pub fn make_gbc_eh( + address: [u8; 6], + station_type: geonetworking::en302636_4_1::StationType, + own_position: &crate::applogic::PositionState, + time: chrono::DateTime, + target_pos: geo_types::Point, + radius_m: u16, + sequence_number: u16, +) -> geonetworking::en302636_4_1::ExtendedHeader { + let source_position_vector = make_lpv(address, station_type, own_position, time); + #[allow(clippy::cast_possible_truncation)] + let latitude_deg = target_pos.x() as f32; + #[allow(clippy::cast_possible_truncation)] + let longitude_deg = target_pos.y() as f32; + + geonetworking::en302636_4_1::ExtendedHeader::GBC( + geonetworking::en302636_4_1::GeoBroadcast::try_from_values( + sequence_number, + source_position_vector, + latitude_deg, + longitude_deg, + radius_m, + 0, + 0, + ) + .expect("Failed to create circular GBC"), + ) +} + +fn make_lpv( + address: [u8; 6], + station_type: geonetworking::en302636_4_1::StationType, + own_position: &crate::applogic::PositionState, + time: chrono::DateTime, +) -> geonetworking::en302636_4_1::LongPositionVector { + let timestamp_its = c_its_parser::time_utils::TimestampIts::from(time); + #[allow(clippy::cast_possible_truncation)] + let latitude_deg = own_position.position.y() as f32; + #[allow(clippy::cast_possible_truncation)] + let longitude_deg = own_position.position.x() as f32; + let speed_mps = own_position.speed_mps.unwrap_or_default(); + let heading_deg = own_position.heading_deg.unwrap_or_default(); + + geonetworking::en302636_4_1::LongPositionVector::try_from_values( + geonetworking::en302636_4_1::Address::new(false, station_type, address), + timestamp_its.0, + latitude_deg, + longitude_deg, + true, + speed_mps, + heading_deg, + ) + .expect("Failed to create LongPositionVector") +} diff --git a/src/v2x_tx/mod.rs b/src/v2x_tx/mod.rs new file mode 100644 index 0000000..e9b1a28 --- /dev/null +++ b/src/v2x_tx/mod.rs @@ -0,0 +1,160 @@ +//! V2X Transmission + +use alloc::vec::Vec; + +use c_its_parser::gn::{self as geonetworking, Encode}; + +pub mod gn; + +#[cfg(feature = "esp")] +/// Creates a station ID from the WLAN MAC address +/// +/// Will use `0xC173` as static prefix and uses last 16 bit from MAC address for the remainder. +pub fn make_station_id(mac: esp_hal::efuse::MacAddress) -> u32 { + // unwrap is fine here since we can be sure that the MacAddress is 6 byte + // and a slice of 2 bytes can be converted to [u8; 2] + let mac_last_16bit = u16::from_be_bytes(mac.as_bytes().get(4..6).unwrap().try_into().unwrap()); + + 0xC173_0000 | u32::from(mac_last_16bit) +} + +pub trait GnItsPayload { + // common GN parameters + const LIFETIME_MILLIS: u32 = 60_000; // itsGnDefaultPacketLifetime is 60 seconds according to ETSI EN 302 636-4-1 V1.4.1 + + // --------------------------------------------- + // methods to build a GN packet with ITS payload + // --------------------------------------------- + + /// Builds the [`geonetworking::en302636_4_1::ExtendedHeader`] and additional information for the ITS message + /// + /// Output: + /// - Extended header + /// - Area type (when using GAC or GBC) + /// - hop limit + /// + /// Note: Make sure to increase the sequence number on multi-hop packets. + fn make_eh( + &self, + address: [u8; 6], + own_position: &crate::applogic::PositionState, + time: chrono::DateTime, + seq_no: &mut u16, + ) -> ( + geonetworking::en302636_4_1::ExtendedHeader, + Option, + u8, + ); + + /// Creates the ITS payload (without BTP header) + fn make_payload( + &self, + ) -> Result<(Vec, c_its_parser::standards::extensions::ItsMessageId), alloc::string::String>; + + // --------------------------------------------- + // common methods (with implementation) + // --------------------------------------------- + + /// Encodes the GN packet + fn encode_packet( + &self, + address: [u8; 6], + own_position: &crate::applogic::PositionState, + time: chrono::DateTime, + is_mobile: bool, + seq_no: &mut u16, + ) -> Result, alloc::string::String> { + // Build payload (including BTP header) + let (mut its_payload, msg_type) = self.make_payload()?; + let mut payload = make_btp_b(msg_type).encode()?; + + payload.append(&mut its_payload); + + #[allow(clippy::cast_possible_truncation)] + let payload_length = (payload.len()) as u16; + + // get extended header (depending on message type and sometimes content as well) + let (eh, area_type, maximum_hop_limit) = self.make_eh(address, own_position, time, seq_no); + + // Build GN headers + let traffic_class = geonetworking::en302636_4_1::TrafficClass::try_new( + false, + false, + make_traffic_class(msg_type), + ) + .map_err(|err| alloc::format!("Failed to create TrafficClass: {err:?}"))?; + let common = geonetworking::en302636_4_1::CommonHeader::from_values( + geonetworking::en302636_4_1::NextAfterCommon::BTPB, + geonetworking::en302636_4_1::HeaderType::new_from(&eh, area_type)?, + traffic_class, + is_mobile, + payload_length, + maximum_hop_limit, + ); + + let remaining_hop_limit = maximum_hop_limit; + let basic = geonetworking::en302636_4_1::BasicHeader::try_new( + 1, + geonetworking::en302636_4_1::NextAfterBasic::CommonHeader, + geonetworking::en302636_4_1::Lifetime::from_milliseconds(Self::LIFETIME_MILLIS), + remaining_hop_limit, + ) + .map_err(|err| alloc::format!("Failed to create BasicHeader: {err}"))?; + + // Assemble and encode packet + let packet = geonetworking::Packet::Unsecured { + basic, + common, + extended: Some(eh), + payload: &payload, + }; + + packet + .encode_to_vec() + .map_err(|err| alloc::format!("Failed to encode GN packet: {err:?}")) + } +} + +/// Creates a BTP-B header based on the ITS message ID (message type) +/// +/// Port numbers and destination port info according to ETSI TS 103 248 +fn make_btp_b( + msg_type: c_its_parser::standards::extensions::ItsMessageId, +) -> c_its_parser::transport::TransportHeader { + let (destination_port, destination_port_info) = match msg_type { + c_its_parser::standards::extensions::ItsMessageId::Denm => (2002, 0), + c_its_parser::standards::extensions::ItsMessageId::Cam => (2001, 0), + c_its_parser::standards::extensions::ItsMessageId::Mapem => (2003, 0), + c_its_parser::standards::extensions::ItsMessageId::Spatem => (2004, 0), + c_its_parser::standards::extensions::ItsMessageId::Saem => (2005, 0), + c_its_parser::standards::extensions::ItsMessageId::Ivim => (2006, 0), + c_its_parser::standards::extensions::ItsMessageId::Srem => (2007, 0), + c_its_parser::standards::extensions::ItsMessageId::Ssem => (2008, 0), + c_its_parser::standards::extensions::ItsMessageId::Cpm => (2009, 0), + c_its_parser::standards::extensions::ItsMessageId::Poi => (2010, 0), + c_its_parser::standards::extensions::ItsMessageId::EvRsr => (2012, 0), + c_its_parser::standards::extensions::ItsMessageId::Rtcmem => (2013, 0), + _ => (0, 0), // no information found for the subsequent types + }; + + c_its_parser::transport::TransportHeader::BtpB(c_its_parser::transport::BasicTransportBHeader { + destination_port, + destination_port_info, + }) +} + +/// Creates a traffic class ID based on the ITS message ID (message type) +fn make_traffic_class(msg_type: c_its_parser::standards::extensions::ItsMessageId) -> u8 { + #[allow(clippy::match_same_arms)] + match msg_type { + c_its_parser::standards::extensions::ItsMessageId::Denm => 1, + c_its_parser::standards::extensions::ItsMessageId::Cam => 2, + c_its_parser::standards::extensions::ItsMessageId::Mapem => 3, // from C-Roads RS_RSP_063(1) + c_its_parser::standards::extensions::ItsMessageId::Spatem => 3, // from C-Roads RS_RSP_063(1) + c_its_parser::standards::extensions::ItsMessageId::Ivim => 3, // from C-Roads RS_RSP_063(1) + c_its_parser::standards::extensions::ItsMessageId::Srem => 2, // from C-Roads RS_RSP_063(1) + c_its_parser::standards::extensions::ItsMessageId::Ssem => 2, // from C-Roads RS_RSP_063(1) + c_its_parser::standards::extensions::ItsMessageId::Rtcmem => 3, // CSP_MaxPrio=252 -> 3 + _ => 3, // fall-back to lowest priority + } +} From 7bbb47a844d96195abf2075eb388bc2faa9ef088 Mon Sep 17 00:00:00 2001 From: Jannik Beyerstedt Date: Fri, 24 Jul 2026 17:25:52 +0200 Subject: [PATCH 16/26] cam_tx: Fill path history --- Cargo.toml | 2 +- feat-permut.sh | 1 - src/applogic/cam_tx.rs | 230 ++++++++++++++++++++++++++++++++++++++++- src/applogic/mod.rs | 2 +- src/test.rs | 1 + 5 files changed, 231 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 834f164..fb0d588 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ path = "./src/main.rs" [[bin]] name = "test" path = "./src/test.rs" -required-features = ["std"] +required-features = ["std", "cam"] [features] default = ["esp", "spat", "gnss", "screen"] diff --git a/feat-permut.sh b/feat-permut.sh index a40a8ca..6ddd438 100755 --- a/feat-permut.sh +++ b/feat-permut.sh @@ -17,7 +17,6 @@ features_more=( spat,gnss,screen,cam_tx,cam,denm ) features_test=( - spat spat,cam,denm ) diff --git a/src/applogic/cam_tx.rs b/src/applogic/cam_tx.rs index 7de554e..0cd0a16 100644 --- a/src/applogic/cam_tx.rs +++ b/src/applogic/cam_tx.rs @@ -7,6 +7,10 @@ use c_its_parser::standards::cam_1_4_1::cam_pdu_descriptions; use c_its_parser::standards::cdd_1_3_1_1::its_container; use c_its_parser::standards::extensions; +const PH_MAX_LENGHT_M: f32 = 500.; +const PH_MAX_ITEMS: usize = 23; +const PH_MIN_DIST_M: f32 = 20.; + #[derive(Debug)] pub struct CamState { station_id: u32, @@ -17,6 +21,39 @@ pub struct CamState { time: chrono::DateTime, pos_state: super::PositionState, + + /// database for path history, oldest item at the back of the vector + path_history: Vec, +} + +#[derive(Debug, Clone, Copy, PartialEq)] +struct PathPoint { + pos: geo_types::Point, + time: chrono::DateTime, + /// distance to previous point. Ignore on last point! + dist_m: f32, +} + +impl PathPoint { + fn new_from_pos(pos: &super::PositionState, time: chrono::DateTime) -> Self { + Self { + pos: pos.position, + time, + dist_m: 0., + } + } + + fn new_from_pos_with_dist( + pos: &super::PositionState, + time: chrono::DateTime, + dist_m: f32, + ) -> Self { + Self { + pos: pos.position, + time, + dist_m, + } + } } impl CamState { @@ -33,6 +70,7 @@ impl CamState { vehicle_width_dm, time: chrono::DateTime::::default(), pos_state: super::PositionState::default(), + path_history: Vec::with_capacity(PH_MAX_ITEMS + 1), } } @@ -40,7 +78,99 @@ impl CamState { self.time = time; self.pos_state = pos; - // TODO: build CAM trace + // Update path history + Self::update_ph(&mut self.path_history, time, &self.pos_state); + } + + // this is a function, not a method for easier testing + fn update_ph( + path_history: &mut Vec, + time: chrono::DateTime, + pos: &super::PositionState, + ) { + // Update path history + if path_history.is_empty() { + // prime with current position if empty + path_history.push(PathPoint::new_from_pos(pos, time)); + } else { + // unwrap is fine since we checked for non-empty before + let prev = path_history.first().unwrap(); + + // only add a point, if we moved more than PH_MIN_DIST_M + #[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)] + let dist = crate::geo_alg::haversine_dist(&prev.pos, &pos.position) as f32; + if dist > PH_MIN_DIST_M { + // new items shall be at the front of the vector + path_history.insert(0, PathPoint::new_from_pos_with_dist(pos, time, dist)); + } + } + + // check length (PH_MAX_LENGHT_M and PH_MAX_ITEMS points max) + if path_history.len() > PH_MAX_ITEMS { + let _ = path_history.pop(); + } + + let (_, cutoff_idx) = + path_history + .iter() + .enumerate() + .fold((0., None), |(len, cutoff_idx), (idx, item)| { + if cutoff_idx.is_none() { + let new_len = len + item.dist_m; + let new_cutoff_idx = if len > PH_MAX_LENGHT_M { + Some(idx) + } else { + None + }; + + (new_len, new_cutoff_idx) + } else { + (len, cutoff_idx) + } + }); + if let Some(idx) = cutoff_idx { + path_history.drain(idx..); + } + } + + fn make_ph(&self) -> Vec { + let mut ph = alloc::vec![]; + + self.path_history.iter().fold( + PathPoint::new_from_pos(&self.pos_state, self.time), + |prev_point, pt| { + // calculate delta position and append to `ph` + + let delta_time = pt.time - prev_point.time; + let delta_lat = pt.pos.y() - prev_point.pos.y(); + let delta_lon = pt.pos.x() - prev_point.pos.x(); + + // delta time is in 1/100 second / 10 ms steps + let path_delta_time = its_container::PathDeltaTime( + (delta_time.num_milliseconds().abs() / 10) + .min(65_535) // clamp to max value + .into(), + ); + let delta_latitude = its_container::DeltaLatitude::from_deg(delta_lat); + let delta_longitude = its_container::DeltaLongitude::from_deg(delta_lon); + let delta_altitude = its_container::DeltaAltitude::unavailable(); + + let ppoint = its_container::PathPoint::new( + its_container::DeltaReferencePosition::new( + delta_latitude, + delta_longitude, + delta_altitude, + ), + Some(path_delta_time), + ); + + ph.push(ppoint); + + *pt + }, + ); + + ph } #[allow(clippy::too_many_lines)] @@ -145,7 +275,7 @@ impl CamState { cam_pdu_descriptions::BasicVehicleContainerLowFrequency { vehicle_role: its_container::VehicleRole::default, exterior_lights: its_container::ExteriorLights::default(), - path_history: its_container::PathHistory(alloc::vec![]), // TODO: fill trace + path_history: its_container::PathHistory(self.make_ph()), }, ); @@ -195,3 +325,99 @@ impl crate::v2x_tx::GnItsPayload for CamState { Ok((uper, c_its_parser::standards::extensions::ItsMessageId::Cam)) } } + +#[cfg(test)] +mod tests { + use alloc::vec; + + use super::*; + use crate::init_test_env_logger; + + fn posstate_from_pos(position: geo_types::Point) -> crate::applogic::PositionState { + crate::applogic::PositionState { + position, + heading_deg: None, + speed_mps: None, + } + } + + #[test] + fn cam_path_history_update() { + init_test_env_logger(); + + let time = chrono::DateTime::from_timestamp(0, 0).unwrap(); + let p1 = geo_types::Point::new(10., 53.5); + let p2 = geo_types::Point::new(10., 53.5 + 0.000045); // ~5m further (< PH_MIN_DIST_M) + let p3 = geo_types::Point::new(10., 53.5 + 0.00027); // ~30m further (> PH_MIN_DIST_M) + + let ps1 = posstate_from_pos(p1); + let pp1 = PathPoint::new_from_pos(&ps1, time); + let ps2 = posstate_from_pos(p2); + let pp2 = PathPoint::new_from_pos(&ps2, time); + let ps3 = posstate_from_pos(p3); + let pp3 = PathPoint::new_from_pos_with_dist(&ps3, time, 30.02267); + + let mut path_history: Vec = vec![]; + + // add first point, expect one point in PH + CamState::update_ph(&mut path_history, time, &posstate_from_pos(p1)); + assert_eq!(1, path_history.len()); + assert_eq!(&pp1, path_history.first().unwrap()); + + // add insignificant point, expect still one point in PH + CamState::update_ph(&mut path_history, time, &posstate_from_pos(p2)); + assert_eq!(1, path_history.len()); + assert_eq!(&pp1, path_history.first().unwrap()); + + // add significantly moved point, expect [pp3, pp1] vector + CamState::update_ph(&mut path_history, time, &posstate_from_pos(p3)); + assert_eq!(2, path_history.len()); + assert_eq!(&pp3, path_history.first().unwrap()); + assert_eq!(&pp1, path_history.last().unwrap()); + } + + #[test] + fn cam_path_history_max_len() { + init_test_env_logger(); + + let time = chrono::DateTime::from_timestamp(0, 0).unwrap(); + let p1 = geo_types::Point::new(10., 53.5); + let p2 = geo_types::Point::new(10., 53.5 + 0.00023); // at ~25m from p1 (> PH_MIN_DIST_M) + let p3 = geo_types::Point::new(10., 53.5 + 0.00445); // at ~495m from p1 (slightly less than PH_MAX_LENGHT_M) + let p4 = geo_types::Point::new(10., 53.5 + 0.00477); // at ~530m from p1 (slightly more than PH_MAX_LENGHT_M) + + let ps1 = posstate_from_pos(p1); + let pp1 = PathPoint::new_from_pos(&ps1, time); + let ps2 = posstate_from_pos(p2); + let pp2 = PathPoint::new_from_pos(&ps2, time); + let ps3 = posstate_from_pos(p3); + let pp3 = PathPoint::new_from_pos(&ps3, time); + let ps4 = posstate_from_pos(p4); + let pp4 = PathPoint::new_from_pos(&ps4, time); + + let mut path_history: Vec = vec![]; + + // prime with p3, p4 (should give a length of ~470m) + CamState::update_ph(&mut path_history, time, &posstate_from_pos(p4)); + CamState::update_ph(&mut path_history, time, &posstate_from_pos(p3)); + assert_eq!(2, path_history.len()); + + // Add p2, should drop p4 from vector because of > PH_MAX_LENGHT_M + CamState::update_ph(&mut path_history, time, &posstate_from_pos(p2)); + assert_eq!(2, path_history.len()); + assert_eq!(p2, path_history.get(0).unwrap().pos); + assert_eq!(469.24323, path_history.get(0).unwrap().dist_m); + assert_eq!(p3, path_history.get(1).unwrap().pos); + assert_eq!(35.582424, path_history.get(1).unwrap().dist_m); + + // Add p1, should not drop anything from vector because still < PH_MAX_LENGHT_M + CamState::update_ph(&mut path_history, time, &posstate_from_pos(p1)); + assert_eq!(3, path_history.len()); + assert_eq!(p1, path_history.get(0).unwrap().pos); + assert_eq!(25.57487, path_history.get(0).unwrap().dist_m); + assert_eq!(p2, path_history.get(1).unwrap().pos); + assert_eq!(469.24323, path_history.get(1).unwrap().dist_m); + assert_eq!(p3, path_history.get(2).unwrap().pos); + assert_eq!(35.582424, path_history.get(2).unwrap().dist_m); + } +} diff --git a/src/applogic/mod.rs b/src/applogic/mod.rs index d1d082e..32ee4f6 100644 --- a/src/applogic/mod.rs +++ b/src/applogic/mod.rs @@ -18,7 +18,7 @@ pub mod v2x; #[cfg(feature = "cam")] pub mod cam; -#[cfg(feature = "cam_tx")] +#[cfg(any(feature = "cam_tx", test))] pub mod cam_tx; #[cfg(feature = "denm")] pub mod denm; diff --git a/src/test.rs b/src/test.rs index 5a04150..7de1bf8 100644 --- a/src/test.rs +++ b/src/test.rs @@ -8,6 +8,7 @@ mod cache; mod geo_alg; mod radio; mod testdata; +mod v2x_tx; fn main() { // env_logger::init(); From 6604670490cbb36874fd7e36d718b445da88a5bc Mon Sep 17 00:00:00 2001 From: Jannik Beyerstedt Date: Sun, 26 Jul 2026 21:40:55 +0200 Subject: [PATCH 17/26] 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:?}"); + } + }); +} From ba71e718dfcd99e96ad441bf2347b8344f8f8432 Mon Sep 17 00:00:00 2001 From: Jannik Beyerstedt Date: Mon, 27 Jul 2026 11:14:14 +0200 Subject: [PATCH 18/26] proto/PositionState: Add time for geonetworking --- docs/c_its-messages.proto | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/docs/c_its-messages.proto b/docs/c_its-messages.proto index 9848910..af8f802 100644 --- a/docs/c_its-messages.proto +++ b/docs/c_its-messages.proto @@ -35,18 +35,17 @@ enum GNAreaShape { ETSI_AREASHAPE_CIRCLE = 0; ETSI_AREASHAPE_RECTANGLE = 1; ETSI_AREASHAPE_ELLIPSIS = 2; - ETSI_AREASHAPE_UNKNOWN = 15; } // GeoNetworking Destination Area/ Source Position message ITSPosition { - optional int32 latitude = 1; // Latitude in 1/10th of micro degrees - optional int32 longitude = 2; // Longitude in 1/10th of micro degrees + required int32 latitude = 1; // Latitude in 1/10th of micro degrees + required int32 longitude = 2; // Longitude in 1/10th of micro degrees } // GeoNetworking Destination Area message GNArea { - optional GNAreaShape area = 1 [default = ETSI_AREASHAPE_UNKNOWN]; + optional GNAreaShape area = 1; optional ITSPosition position = 2; optional int32 distA = 3; // Distance A of the geometric shape in meters optional int32 distB = 4; // Distance B of the geometric shape in meters, mandatory for rectangle and ellipsis @@ -55,8 +54,6 @@ message GNArea { // GeoNetworking Transport enum GNTransport { - GN_TRANSPORT_DEFAULT = 0; - GN_TRANSPORT_GUC = 2; // GeoUnicast GN_TRANSPORT_GAC = 3; // Geographically-Scoped Anycast GN_TRANSPORT_GBC = 4; // Geographically-Scoped broadcast GN_TRANSPORT_TSB = 5; // Topologically-scoped broadcast @@ -79,7 +76,8 @@ message RawMsgTx { // ------------------------------ message PositionState { - required Position position = 1; + required sfixed64 timestamp_ms = 1; // current UNIX time in milliseconds + required Position position = 2; optional uint32 heading = 3; // heading in 1/10 deg optional uint32 speed = 4; // speed in 1/100 m/s } From 4d7bef58a165eb2f3a80a4d702034279ff3d50a4 Mon Sep 17 00:00:00 2001 From: Jannik Beyerstedt Date: Mon, 20 Jul 2026 11:58:44 +0200 Subject: [PATCH 19/26] Implement UART I/O protocol --- Cargo.toml | 10 +- Readme.md | 7 +- feat-permut.sh | 4 +- src/applogic/cam_tx.rs | 17 +- src/applogic/mod.rs | 5 + src/io.rs | 464 +++++++++++++++++++++++++++++++++++++++++ src/main.rs | 114 ++++++++-- src/test.rs | 1 + src/testdata.rs | 8 + src/v2x_tx/gn.rs | 2 +- src/v2x_tx/mod.rs | 18 +- 11 files changed, 612 insertions(+), 38 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bb70ea2..eef57d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,11 +34,11 @@ esp = [ std = ["c-its-parser/std"] # enable L67K GNSS module -gnss = ["dep:embassy_gps"] +gnss = ["_gnss", "dep:embassy_gps"] # echo CAM data on serial cam = ["c-its-parser/cam"] # send CAM autonomously -cam_tx = ["c-its-parser/cam", "gnss"] +cam_tx = ["c-its-parser/cam"] # echo DENM events on serial and BLE denm = ["c-its-parser/denm"] # evaluate SPAT information @@ -47,9 +47,13 @@ spat = ["c-its-parser/spatem", "c-its-parser/mapem"] spat_debug = [] # enable ST7789 based display screen = ["dep:mipidsi", "dep:embedded-graphics", "dep:embedded-hal-bus", "dep:profont"] +# enable I/O via UART +uart = ["_uart", "_gnss", "dep:prost"] # internal feature, that any UART I/O is enabled _uart = [] +# internal feature, that any GNSS source is enabled +_gnss = [] [dependencies] esp-hal = { version = "~1.1.0", optional = true, features = ["esp32c5", "log-04", "unstable"] } @@ -102,7 +106,7 @@ geo-types = { version = "0.7.19", default-features = false } mipidsi = { version = "0.10.0", optional = true } num-traits = { version = "0.2.19", default-features = false, features = ["libm"] } profont = { version = "0.7.0", optional = true } -prost = { version = "0.14", default-features = false, features = ["derive"] } +prost = { version = "0.14", optional = true, default-features = false, features = ["derive"] } [dev-dependencies] assert_float_eq = "1.2.0" diff --git a/Readme.md b/Readme.md index 281f995..c8dcf5c 100644 --- a/Readme.md +++ b/Readme.md @@ -32,6 +32,9 @@ Just prints a line for each received CAM. Generates and transmits a CAM when a GNSS update was received. Depends on the `gnss` feature. +### UART +Enable UART I/O protocol according to [docs/uart-protocol.md](./docs/uart-protocol.md). + ## Usage @@ -64,13 +67,13 @@ Additional features are available through feature flags: - `spat_debug`: Enable additional debug information for the SPAT/ GLOSA use case - `denm`: Enable DENMs - `cam`: Enable CAMs -- `cam_tx`: Enable CAM generation and transmission +- `cam_tx`: Enable CAM generation and transmission (needs time and position source) Officially supported feature combinations are: - `esp,spat,gnss,screen` (default features) - `esp,spat,gnss,screen,cam_tx` - `esp,spat,gnss,screen,cam_tx,cam,denm` -- To be implemented: No GNSS and screen when BLE/ UART interface is implemented +- `esp,uart,cam,denm` Connect your ESP32-C5 and run: diff --git a/feat-permut.sh b/feat-permut.sh index 6ddd438..2019bef 100755 --- a/feat-permut.sh +++ b/feat-permut.sh @@ -8,7 +8,7 @@ extended=${2:-false} features_base=( - #cam,denm,uart + uart,cam,denm spat,gnss,screen spat,gnss,screen,cam_tx ) @@ -17,7 +17,7 @@ features_more=( spat,gnss,screen,cam_tx,cam,denm ) features_test=( - spat,cam,denm + spat,cam,denm,uart ) diff --git a/src/applogic/cam_tx.rs b/src/applogic/cam_tx.rs index 0cd0a16..e15de27 100644 --- a/src/applogic/cam_tx.rs +++ b/src/applogic/cam_tx.rs @@ -300,20 +300,23 @@ impl crate::v2x_tx::GnItsPayload for CamState { own_position: &crate::applogic::PositionState, time: chrono::DateTime, _seq_no: &mut u16, - ) -> ( - geonetworking::en302636_4_1::ExtendedHeader, - Option, - u8, - ) { + ) -> Result< + ( + geonetworking::en302636_4_1::ExtendedHeader, + Option, + u8, + ), + alloc::string::String, + > { let station_type = geonetworking::en302636_4_1::StationType::try_from(self.station_type.0) .unwrap_or_default(); // CAM always uses SHB, hop-limit 1 - ( + Ok(( crate::v2x_tx::gn::make_shb_eh(address, station_type, own_position, time), None, 1, - ) + )) } fn make_payload( diff --git a/src/applogic/mod.rs b/src/applogic/mod.rs index 32ee4f6..7847010 100644 --- a/src/applogic/mod.rs +++ b/src/applogic/mod.rs @@ -639,6 +639,11 @@ impl State { pub fn time(&self) -> chrono::prelude::NaiveDateTime { self.time } + + #[allow(unused)] + pub fn pos(&self) -> &PositionState { + &self.pos + } } #[cfg(test)] diff --git a/src/io.rs b/src/io.rs index cb23055..4642253 100644 --- a/src/io.rs +++ b/src/io.rs @@ -5,6 +5,12 @@ pub mod msg { include!(concat!(env!("OUT_DIR"), "/c_its_io.rs")); } +use alloc::string::ToString; +use alloc::vec::Vec; + +use c_its_parser::gn as geonetworking; +use log::error; + impl From for msg::SerialOutputMsg { fn from(value: msg::RawMsgRx) -> Self { use msg::serial_output_msg::Payload; @@ -25,6 +31,144 @@ impl From for msg::SerialOutputMsg { } } +impl msg::RawMsgRx { + pub fn serialize_uart_proto(&self) -> Vec { + serialize_serial_msg(&Into::::into(self.clone())) + } +} +impl msg::DenmEvent { + #[allow(unused)] + pub fn serialize_uart_proto(&self) -> Vec { + serialize_serial_msg(&Into::::into(*self)) + } +} + +#[derive(Debug, Default)] +pub struct Parser { + buffer: alloc::vec::Vec, +} + +impl Parser { + const SOF_LEN: u16 = 4; + const SOF1: u8 = 0x56; + const SOF2: u8 = 0x32; + const SOF3: u8 = 0x58; + const SOF4: u8 = 0x2B; + const EOF_LEN: u16 = 2; + #[allow(unused)] + const EOF1: u8 = 0x0d; + #[allow(unused)] + const EOF2: u8 = 0x0a; + const LENGTH_LEN: u16 = 2; + + /// Parses incoming data and return list of messages + pub fn parse(&mut self, input: &[u8]) -> alloc::vec::Vec { + use prost::Message as _; + + // combine with existing data + self.buffer.append(&mut input.to_vec()); + + // find start of frame and parse + let mut output = alloc::vec![]; + while let Some(pos) = Self::find_sof(&self.buffer) { + // drop everything before the SOF + self.buffer.drain(..pos); + + // get length field and check buffer size + if let Some(len_bytes) = self + .buffer + .get(usize::from(Self::SOF_LEN)..usize::from(Self::SOF_LEN + Self::LENGTH_LEN)) + { + // unwrap is fine since we queried 2 bytes before, so we can also convert to 2-byte slice + let msg_size = u16::from_be_bytes(len_bytes.try_into().unwrap()); + + let required_buf_size = Self::SOF_LEN + Self::LENGTH_LEN + msg_size + Self::EOF_LEN; + if let Some(payload) = self.buffer.get( + usize::from(Self::SOF_LEN + Self::LENGTH_LEN) + ..usize::from(required_buf_size - 2), + ) { + match msg::SerialInputMsg::decode(payload) { + Ok(data) => { + output.push(data); + } + Err(err) => error!("Failed to parse input: {err:?}"), + } + + // consume buffer + self.buffer.drain(..usize::from(required_buf_size)); + } else { + break; + } + } + } + + output + } + + /// Returns the start index of the SOF sequence, if present + fn find_sof(input: &[u8]) -> Option { + // search for SOF + if let Some(sof1_pos) = input.iter().position(|i| *i == Self::SOF1) { + // drop everything before sof1_pos and check if remainder continues with SOF sequence + let (_, msg) = input.split_at(sof1_pos); + if msg.starts_with(&[Self::SOF1, Self::SOF2, Self::SOF3, Self::SOF4]) { + // found the SOF sequence + return Some(sof1_pos); + } + } + + // if we reach this position, SOF sequence wasn't found + None + } +} + +fn serialize_serial_msg(msg: &T) -> Vec +where + T: prost::Message, +{ + let mut msg_buf = msg.encode_to_vec(); + + let sof = [0x56u8, 0x32, 0x58, 0x2B]; + let eof = [0x0du8, 0x0a]; + #[allow(clippy::cast_possible_truncation)] + let len: [u8; 2] = (msg_buf.len() as u16).to_be_bytes(); + + let mut out = sof.to_vec(); + out.extend_from_slice(&len); + out.append(&mut msg_buf); + out.extend_from_slice(&eof); + + out +} + +impl From for crate::applogic::GnssFix { + fn from(value: msg::PositionState) -> Self { + let time = + chrono::DateTime::from_timestamp_millis(value.timestamp_ms).map(|v| v.naive_utc()); + + let latitude_deg = value.position.latitude_deg.into(); + let longitude_deg = value.position.longitude_deg.into(); + let heading_deg = value.heading.map(|v| { + #[allow(clippy::cast_precision_loss)] + let val = (v as f32) / 10.; + val + }); + let speed_mps = value.speed.map(|v| { + #[allow(clippy::cast_precision_loss)] + let val = (v as f32) / 100.; + val + }); + + Self { + time, + latitude_deg, + longitude_deg, + heading_deg, + speed_mps, + } + } +} + impl From for crate::applogic::PositionState { fn from(value: msg::PositionState) -> Self { let position = geo_types::Point::new( @@ -49,3 +193,323 @@ impl From for crate::applogic::PositionState { } } } + +impl From for crate::applogic::PositionState { + fn from(value: msg::ItsPosition) -> Self { + let latitude_deg = f64::from(value.latitude) / 10_000_000.; + let longitude_deg = f64::from(value.longitude) / 10_000_000.; + let position = geo_types::Point::new(longitude_deg, latitude_deg); + + Self { + position, + heading_deg: None, + speed_mps: None, + } + } +} + +impl crate::v2x_tx::GnItsPayload for msg::RawMsgTx { + fn make_eh( + &self, + address: [u8; 6], + own_position: &crate::applogic::PositionState, + time: chrono::DateTime, + seq_no: &mut u16, + ) -> Result< + ( + c_its_parser::gn::en302636_4_1::ExtendedHeader, + Option, + u8, + ), + alloc::string::String, + > { + let station_type = self + .station_type + .and_then(|v| { + #[allow(clippy::cast_possible_truncation)] + let raw_val = v as u8; + geonetworking::en302636_4_1::StationType::try_from(raw_val).ok() + }) + .unwrap_or_default(); + #[allow(clippy::cast_possible_truncation)] + let hop_limit = self.hop_limit as u8; + + Ok(match self.gn_transport() { + msg::GnTransport::Gac => { + if self.gn_area.position.is_none() { + return Err("Failed to create GAC: gnArea.position missing".to_string()); + } + + *seq_no += 1; + + let (atype, geoanycast) = Self::make_geobcast( + address, + station_type, + own_position, + time, + self.gn_area, + *seq_no, + )?; + let eh = geonetworking::en302636_4_1::ExtendedHeader::GAC(geoanycast); + (eh, Some(atype), hop_limit) + } + msg::GnTransport::Gbc => { + if self.gn_area.position.is_none() { + return Err("Failed to create GAC: gnArea.position missing".to_string()); + } + + *seq_no += 1; + + let (atype, geoanycast) = Self::make_geobcast( + address, + station_type, + own_position, + time, + self.gn_area, + *seq_no, + )?; + let eh = geonetworking::en302636_4_1::ExtendedHeader::GBC(geoanycast); + (eh, Some(atype), hop_limit) + } + msg::GnTransport::Tsb => { + *seq_no += 1; + + ( + crate::v2x_tx::gn::make_tsb_eh( + address, + station_type, + own_position, + time, + *seq_no, + ), + None, + hop_limit, + ) + } + msg::GnTransport::Shb => ( + crate::v2x_tx::gn::make_shb_eh(address, station_type, own_position, time), + None, + 1, + ), + }) + } + + fn make_payload( + &self, + ) -> Result<(Vec, c_its_parser::standards::extensions::ItsMessageId), alloc::string::String> + { + #[allow(clippy::cast_possible_truncation)] + let message_id = (self.message_type as u8).try_into()?; + Ok((self.payload.clone(), message_id)) + } +} + +impl msg::RawMsgTx { + fn make_geobcast( + address: [u8; 6], + station_type: geonetworking::en302636_4_1::StationType, + own_position: &crate::applogic::PositionState, + time: chrono::DateTime, + gn_area: msg::GnArea, + sequence_number: u16, + ) -> Result< + ( + c_its_parser::gn::en302636_4_1::AreaType, + geonetworking::en302636_4_1::GeoBroadcast, + ), + alloc::string::String, + > { + let source_position_vector = + crate::v2x_tx::gn::make_lpv(address, station_type, own_position, time); + + let (atype, dist_a, dist_b, angle) = match gn_area.area() { + msg::GnAreaShape::EtsiAreashapeCircle => ( + c_its_parser::gn::en302636_4_1::AreaType::Circular, + gn_area.dist_a, + None, + None, + ), + msg::GnAreaShape::EtsiAreashapeRectangle => ( + c_its_parser::gn::en302636_4_1::AreaType::Rectangular, + gn_area.dist_a, + gn_area.dist_b, + gn_area.angle, + ), + msg::GnAreaShape::EtsiAreashapeEllipsis => ( + c_its_parser::gn::en302636_4_1::AreaType::Ellipsoidal, + gn_area.dist_a, + gn_area.dist_b, + gn_area.angle, + ), + }; + + #[allow(clippy::cast_possible_truncation)] + let latitude_deg = own_position.position.y() as f32; + #[allow(clippy::cast_possible_truncation)] + let longitude_deg = own_position.position.x() as f32; + let distance_a = dist_a + .map(|v| { + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] + let val = v as u16; + val + }) + .unwrap_or_default(); + let distance_b = dist_b + .map(|v| { + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] + let val = v as u16; + val + }) + .unwrap_or_default(); + let angle = angle + .map(|v| { + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] + let val = v as u16; + val + }) + .unwrap_or_default(); + + let gac = geonetworking::en302636_4_1::GeoAnycast::try_from_values( + sequence_number, + source_position_vector, + latitude_deg, + longitude_deg, + distance_a, + distance_b, + angle, + ) + .map_err(|err| alloc::format!("{err}"))?; + + Ok((atype, gac)) + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::init_test_env_logger; + + impl From for msg::SerialInputMsg { + fn from(value: msg::RawMsgTx) -> Self { + use msg::serial_input_msg::Payload; + + Self { + payload: Some(Payload::TxMsg(value)), + } + } + } + + impl From for msg::SerialInputMsg { + fn from(value: msg::PositionState) -> Self { + use msg::serial_input_msg::Payload; + + Self { + payload: Some(Payload::Position(value)), + } + } + } + + // manual example data: + // SerialInputMsg(RawMsgTx): 5632582b0040123e0a340202c173256817284029d5b3400f1feaf01ffffffc23b7743e00000fc0007e8138250737feebfff600000dffff7ffff1ce40400010011807220028020d0a + // SerialInputMsg(PositionState): 5632582b00170a1509a0bbe2a29f010000120a0d00002842150000b8410d0a + + #[test] + fn uart_parser() { + // generate some test UART input messages + let raw_tx = msg::RawMsgTx { + payload: crate::testdata::CAM_SMALL.to_vec(), + message_type: 1, + gn_transport: msg::GnTransport::Shb.into(), + gn_area: msg::GnArea { + area: None, + position: None, + dist_a: None, + dist_b: None, + angle: None, + }, + hop_limit: 2, + station_type: None, + }; + let raw_tx_buf = serialize_serial_msg::(&raw_tx.clone().into()); + + let pos_state = msg::PositionState { + timestamp_ms: 1785144196000, + position: msg::Position { + latitude_deg: 42., + longitude_deg: 23., + }, + heading: None, + speed: None, + }; + let pos_state_buf = serialize_serial_msg::(&pos_state.clone().into()); + + let mut parser = Parser::default(); + + // feed individual complete messages to parser + let output = parser.parse(&raw_tx_buf); + assert_eq!(1, output.len()); + assert_eq!( + &msg::SerialInputMsg::from(raw_tx.clone()), + output.get(0).unwrap() + ); + + let output = parser.parse(&pos_state_buf); + assert_eq!(1, output.len()); + assert_eq!( + &msg::SerialInputMsg::from(pos_state.clone()), + output.get(0).unwrap() + ); + + // feed multiple complete messages to parser + let mut combined_buf = pos_state_buf.clone(); + combined_buf.extend_from_slice(&raw_tx_buf); + + let output = parser.parse(&combined_buf); + assert_eq!(2, output.len()); + assert_eq!( + &msg::SerialInputMsg::from(pos_state.clone()), + output.get(0).unwrap() + ); + assert_eq!( + &msg::SerialInputMsg::from(raw_tx.clone()), + output.get(1).unwrap() + ); + + // feed partial messages to parser + let buf1 = &raw_tx_buf[0..3]; + let buf2 = &raw_tx_buf[3..]; + + let output = parser.parse(buf1); + assert_eq!(0, output.len()); + + let output = parser.parse(buf2); + assert_eq!(1, output.len()); + assert_eq!( + &msg::SerialInputMsg::from(raw_tx.clone()), + output.get(0).unwrap() + ); + + // feed messages with garbage in between + let mut buf1 = vec![0xabu8, 0xcd]; // prepend some garbage + buf1.extend_from_slice(&raw_tx_buf[0..3]); + let mut buf2 = (&raw_tx_buf[3..]).to_vec(); + buf2.extend_from_slice(&[0xfeu8, 0x42]); // append some garbage + buf2.extend_from_slice(&pos_state_buf); // append second message + buf2.extend_from_slice(&[0x11u8, 0x22]); // append some more garbage + + let output = parser.parse(&buf1); + assert_eq!(0, output.len()); + + let output = parser.parse(&buf2); + assert_eq!(2, output.len()); + assert_eq!( + &msg::SerialInputMsg::from(raw_tx.clone()), + output.get(0).unwrap() + ); + assert_eq!( + &msg::SerialInputMsg::from(pos_state.clone()), + output.get(1).unwrap() + ); + } +} diff --git a/src/main.rs b/src/main.rs index 03ef304..772adb6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,13 @@ )] // #![deny(clippy::large_stack_frames)] +// we can't use UART I/O and GNSS at the same time +#[cfg(all(feature = "uart", feature = "gnss"))] +compile_error!("feature \"uart\" and feature \"gnss\" cannot be enabled at the same time"); +// autonomous CAM generation need a time and position source (aka _gnss) +#[cfg(all(feature = "cam_tx", not(feature = "_gnss")))] +compile_error!("feature \"cam_tx\" needs a time and position source (either GNSS or UART)"); + use alloc::string::ToString as _; use alloc::vec::Vec; use core::cell::RefCell; @@ -35,17 +42,18 @@ extern crate alloc; mod applogic; mod cache; mod geo_alg; +#[cfg(feature = "uart")] mod io; mod radio; #[cfg(feature = "screen")] mod screen; -#[cfg(feature = "cam_tx")] +#[cfg(any(feature = "cam_tx", feature = "uart"))] mod v2x_tx; const WIFI_CHANNEL: radio::Channel = 180; #[cfg(feature = "spat")] const SPAT_RATE_LIMIT: u8 = 5; // keep every n-th message -#[cfg(feature = "cam_tx")] +#[cfg(any(feature = "cam_tx", feature = "uart"))] const GN_IS_MOBILE: bool = true; #[cfg(feature = "cam_tx")] const OWN_STATION_TYPE: ItsStationType = ItsStationType::Cyclist; @@ -60,7 +68,7 @@ const SERIAL_FIFO_SIZE_THLD: u8 = 100; // hardware has max. 128 byte // For more information see: esp_bootloader_esp_idf::esp_app_desc!(); -#[cfg(feature = "gnss")] +#[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")] @@ -136,6 +144,18 @@ async fn main(spawner: Spawner) -> ! { screen }; + // Setup UART I/O channel + #[cfg(feature = "uart")] + setup_uart0( + 115_200, + Some(b'\n'), + peripherals.GPIO12, + peripherals.GPIO11, + peripherals.UART0, + serial_handler, + ) + .expect("Failed to initialize UART"); + // Setup WiFi // sadly, it doesn't work any more when we move `esp_radio::wifi::new` to `setup_wifi_sniffer` let (mut wifi_controller, interfaces) = esp_radio::wifi::new( @@ -178,24 +198,29 @@ async fn main(spawner: Spawner) -> ! { #[cfg(feature = "gnss")] info!("GNSS task started, waiting for GNSS fix..."); - let mut state = applogic::State::new(); - #[cfg(feature = "cam_tx")] - let (mut seq_no, mut cam, mac_bytes) = { - let station_id = v2x_tx::make_station_id(mac); - info!("V2X Station ID: {station_id}"); - + #[cfg(any(feature = "cam_tx", feature = "uart"))] + let (mut seq_no, mac_bytes) = { // unwrap is fine since [`esp_hal::efuse::MacAddress`] is just an `[u8; 6]` let mac_bytes = mac.as_bytes().try_into().unwrap(); - let cam = applogic::cam_tx::CamState::new( + (0, mac_bytes) + }; + + let mut state = applogic::State::new(); + #[cfg(feature = "cam_tx")] + let mut cam = { + let station_id = v2x_tx::make_station_id(mac); + info!("V2X Station ID: {station_id}"); + + applogic::cam_tx::CamState::new( station_id, OWN_STATION_TYPE, OWN_VEHICLE_LENGTH, OWN_VEHICLE_WIDTH, - ); - - (0, cam, mac_bytes) + ) }; + #[cfg(feature = "uart")] + let mut uart_input = io::Parser::default(); loop { embassy_time::Timer::after(embassy_time::Duration::from_millis(1)).await; @@ -214,11 +239,56 @@ async fn main(spawner: Spawner) -> ! { }); if let Some(data) = new_data { - warn!("TODO: New UART data: {data:x?}"); + #[cfg(feature = "uart")] + { + for msg in uart_input.parse(&data) { + match msg.payload { + Some(io::msg::serial_input_msg::Payload::Position(fix)) => { + let pos_state = fix.into(); + + critical_section::with(|cs| { + let gnss_update_ref = GNSS_UPDATE.borrow(cs); + gnss_update_ref.replace(Some(pos_state)); + }); + } + Some(io::msg::serial_input_msg::Payload::TxMsg(tx_request)) => { + use v2x_tx::GnItsPayload as _; + + info!( + "UART: Got new TX message: Type {} with {} bytes", + tx_request.message_type, + tx_request.payload.len() + ); + let own_position = state.pos(); + let time = state.time().and_utc(); + + match tx_request.encode_packet( + mac_bytes, + own_position, + time, + GN_IS_MOBILE, + &mut seq_no, + ) { + Ok(data) => { + if let Err(err) = radio::send_80211_bcast_frame( + &mut wlan_iface, + &mac_bytes.clone(), + &data, + ) { + warn!("Failed to send 802.11 frame: {err}"); + } + } + Err(err) => warn!("Failed to create GN message: {err}"), + } + } + None => {} + } + } + } } } - #[cfg(feature = "gnss")] + #[cfg(feature = "_gnss")] { let mut new_position = false; critical_section::with(|cs| { @@ -245,7 +315,7 @@ async fn main(spawner: Spawner) -> ! { Ok(data) => { if let Err(err) = radio::send_80211_bcast_frame( &mut wlan_iface, - mac.as_bytes(), + &mac_bytes.clone(), &data, ) { warn!("Failed to send 802.11 frame: {err}"); @@ -472,6 +542,18 @@ fn handle_frame(frame: wifi::sniffer::PromiscuousPkt<'_>) { error!("V2X RX queue is full"); } }); + + // Send data to UART + #[cfg(feature = "uart")] + { + let message = io::msg::RawMsgRx { + msg_type: message_id.as_u8().into(), + payload: frame.data.to_vec(), + } + .serialize_uart_proto(); + + send_uart0(&message); + } } } } diff --git a/src/test.rs b/src/test.rs index 7de1bf8..6bb20d2 100644 --- a/src/test.rs +++ b/src/test.rs @@ -6,6 +6,7 @@ extern crate alloc; mod applogic; mod cache; mod geo_alg; +mod io; mod radio; mod testdata; mod v2x_tx; diff --git a/src/testdata.rs b/src/testdata.rs index 567410d..fa884d6 100644 --- a/src/testdata.rs +++ b/src/testdata.rs @@ -455,3 +455,11 @@ pub const CAM: &[u8] = &[ 0x00, 0x06, 0xc6, 0x70, 0x00, 0xdf, 0x80, 0x8d, 0x5f, 0xde, 0x66, 0x27, 0x00, 0x07, 0x3c, 0x04, 0x76, 0xfd, 0x67, 0x31, 0x9c, 0x00, 0x58, 0x60, 0x41, 0x37, 0xd3, 0x15, 0x89, 0xc0, 0x06, 0xdc, ]; + +#[cfg(any(feature = "cam_tx", feature = "uart"))] +pub const CAM_SMALL: &[u8] = &[ + 0x2, 0x2, 0xc1, 0x73, 0x25, 0x68, 0x17, 0x28, 0x40, 0x29, 0xd5, 0xb3, 0x40, 0xf, 0x1f, 0xea, + 0xf0, 0x1f, 0xff, 0xff, 0xfc, 0x23, 0xb7, 0x74, 0x3e, 0x0, 0x0, 0xf, 0xc0, 0x0, 0x7e, 0x81, + 0x38, 0x25, 0x7, 0x37, 0xfe, 0xeb, 0xff, 0xf6, 0x0, 0x0, 0xd, 0xff, 0xff, 0x7f, 0xff, 0xf1, + 0xce, 0x40, 0x40, 0x0, +]; diff --git a/src/v2x_tx/gn.rs b/src/v2x_tx/gn.rs index 828c74d..4b2b0b9 100644 --- a/src/v2x_tx/gn.rs +++ b/src/v2x_tx/gn.rs @@ -67,7 +67,7 @@ pub fn make_gbc_eh( ) } -fn make_lpv( +pub fn make_lpv( address: [u8; 6], station_type: geonetworking::en302636_4_1::StationType, own_position: &crate::applogic::PositionState, diff --git a/src/v2x_tx/mod.rs b/src/v2x_tx/mod.rs index e9b1a28..ee48578 100644 --- a/src/v2x_tx/mod.rs +++ b/src/v2x_tx/mod.rs @@ -6,7 +6,7 @@ use c_its_parser::gn::{self as geonetworking, Encode}; pub mod gn; -#[cfg(feature = "esp")] +#[cfg(all(feature = "esp", feature = "cam_tx"))] /// Creates a station ID from the WLAN MAC address /// /// Will use `0xC173` as static prefix and uses last 16 bit from MAC address for the remainder. @@ -40,11 +40,14 @@ pub trait GnItsPayload { own_position: &crate::applogic::PositionState, time: chrono::DateTime, seq_no: &mut u16, - ) -> ( - geonetworking::en302636_4_1::ExtendedHeader, - Option, - u8, - ); + ) -> Result< + ( + geonetworking::en302636_4_1::ExtendedHeader, + Option, + u8, + ), + alloc::string::String, + >; /// Creates the ITS payload (without BTP header) fn make_payload( @@ -74,7 +77,8 @@ pub trait GnItsPayload { let payload_length = (payload.len()) as u16; // get extended header (depending on message type and sometimes content as well) - let (eh, area_type, maximum_hop_limit) = self.make_eh(address, own_position, time, seq_no); + let (eh, area_type, maximum_hop_limit) = + self.make_eh(address, own_position, time, seq_no)?; // Build GN headers let traffic_class = geonetworking::en302636_4_1::TrafficClass::try_new( From 2c50668e7e74ed67d182440022407466963b7d25 Mon Sep 17 00:00:00 2001 From: Jannik Beyerstedt Date: Fri, 31 Jul 2026 12:04:46 +0200 Subject: [PATCH 20/26] 802.11-TX: Reduce transmit power, add notes about legal requirements --- Readme.md | 19 +++++++++++++++++++ src/main.rs | 5 +++++ 2 files changed, 24 insertions(+) diff --git a/Readme.md b/Readme.md index c8dcf5c..1dda26f 100644 --- a/Readme.md +++ b/Readme.md @@ -32,6 +32,25 @@ Just prints a line for each received CAM. Generates and transmits a CAM when a GNSS update was received. Depends on the `gnss` feature. +Note: In germany, using the 5875 to 5915 MHz frequency band is only permitted for C-ITS use cases which implement transmit power reduction measures with at least 30 dB reduction range (or equivalent measures) and have a maximum EIRP of 33 dBm. +This is based on EU regulation 2014/53/EU, so it applies to other EU countries in similar ways. +The relevant harmonized standard is ETSI EN 302 571 which mandates (excerpt of most relevant points): + +- a maximum power spectral density of 23 dBm/Mhz EIRP +- transmit power control (TCP) in a range from 3 dBm up to the maximum output power of the equipment + * used for CEN DSRC and HDR DSRC interference mitigation by implementing measures from ETSI TS 102 792 + + Mode A or B: Output power less than 10 dBm EIRP and less than -65 or -45 dBm/MHz EIRP in CEN DSRC band -> **needs to be measured** + + Mode C: Maximum transmission time of less than 1 ms and at least a few 100 ms between transmission (realistically) -> **can't be ensured with big messages** + + Mode D: "any" realistically possible transmission time, but at least 500 ms between transmissions for ~5 ITS stations/ 1000ms for 10 ITS stations -> **safe to assume when we only send CAMs at 1 Hz** + + But modes C and D also need to: + - use protected zone information from CAM messages + - use a protected zone database, e.g. from www.asecap-pzdb.com + * used for dynamic congestion control (DCC) + + mandatory mechanism described in ETSI EN 302 571, chapter 4.2.10 + +This is **not** implemented in this application! +**So legally, you're not allowed to send with this.** + ### UART Enable UART I/O protocol according to [docs/uart-protocol.md](./docs/uart-protocol.md). diff --git a/src/main.rs b/src/main.rs index 772adb6..64ed528 100644 --- a/src/main.rs +++ b/src/main.rs @@ -51,6 +51,7 @@ mod screen; mod v2x_tx; const WIFI_CHANNEL: radio::Channel = 180; +const WIFI_TXPOWER: i8 = 10; // dBm, 2..20 #[cfg(feature = "spat")] const SPAT_RATE_LIMIT: u8 = 5; // keep every n-th message #[cfg(any(feature = "cam_tx", feature = "uart"))] @@ -163,9 +164,13 @@ async fn main(spawner: Spawner) -> ! { esp_radio::wifi::ControllerConfig::default(), ) .expect("Failed to initialize Wi-Fi controller"); + if let Err(err) = wifi_controller.set_band_mode(esp_radio::wifi::BandMode::_5G) { error!("Failed to set WiFi to 5GHz only: {err}"); } + if let Err(err) = wifi_controller.set_max_tx_power(WIFI_TXPOWER * 4) { + error!("Failed to set WiFi TX power: {err}"); + } critical_section::with(|cs| { WIFI_RX_QUEUE.borrow(cs).replace(Some(ArrayQueue::new(2))); From 71161d1d991a49c81ecff9b0d0cfeb5034aaac0d Mon Sep 17 00:00:00 2001 From: Jannik Beyerstedt Date: Fri, 31 Jul 2026 12:13:54 +0200 Subject: [PATCH 21/26] Add scripts to run/ flash the supported feature-sets --- Readme.md | 6 +++--- run-smartphone.sh | 6 ++++++ run-standalone-cam.sh | 6 ++++++ run-standalone-no_cam.sh | 6 ++++++ 4 files changed, 21 insertions(+), 3 deletions(-) create mode 100755 run-smartphone.sh create mode 100755 run-standalone-cam.sh create mode 100755 run-standalone-no_cam.sh diff --git a/Readme.md b/Readme.md index 1dda26f..ace338e 100644 --- a/Readme.md +++ b/Readme.md @@ -89,10 +89,10 @@ Additional features are available through feature flags: - `cam_tx`: Enable CAM generation and transmission (needs time and position source) Officially supported feature combinations are: -- `esp,spat,gnss,screen` (default features) -- `esp,spat,gnss,screen,cam_tx` +- `esp,spat,gnss,screen` (default features, "standalone") +- `esp,spat,gnss,screen,cam_tx` ("standalone with CAM") - `esp,spat,gnss,screen,cam_tx,cam,denm` -- `esp,uart,cam,denm` +- `esp,uart,cam,denm` ("smartphone companion") Connect your ESP32-C5 and run: diff --git a/run-smartphone.sh b/run-smartphone.sh new file mode 100755 index 0000000..ee5170d --- /dev/null +++ b/run-smartphone.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +## Builds common config for smartphone connected device +## -> only receive CAM and DENMs via UART (TODO: and BLE) + +cargo run --release --no-default-features -F esp,cam,denm,uart diff --git a/run-standalone-cam.sh b/run-standalone-cam.sh new file mode 100755 index 0000000..9bfafbe --- /dev/null +++ b/run-standalone-cam.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +## Builds common config for standalone device with automatic CAM transmission +## -> enable SPAT/ GLOSA with GNSS and a screen + CAM transmission + +cargo run --release -F cam_tx diff --git a/run-standalone-no_cam.sh b/run-standalone-no_cam.sh new file mode 100755 index 0000000..e61d25c --- /dev/null +++ b/run-standalone-no_cam.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +## Builds common config for standalone device +## -> enable SPAT/ GLOSA with GNSS and a screen + +cargo run --release From ac6a3e398ecc091e603324887cdc89d4264113ff Mon Sep 17 00:00:00 2001 From: Jannik Beyerstedt Date: Sun, 12 Jul 2026 00:14:29 +0200 Subject: [PATCH 22/26] Hardware: Add PCB layout --- .gitignore | 4 + hardware/c_its-companion-v0.1-back.jpg | Bin 0 -> 148719 bytes hardware/c_its-companion-v0.1-front.jpg | Bin 0 -> 145854 bytes .../c_its-companion/c_its-companion.kicad_pcb | 8034 +++++++++++ .../c_its-companion/c_its-companion.kicad_prl | 138 + .../c_its-companion/c_its-companion.kicad_pro | 744 + .../c_its-companion/c_its-companion.kicad_sch | 4985 +++++++ .../fabrication-toolkit-options.json | 1 + hardware/c_its-companion/fp-lib-table | 4 + .../Seeed_Studio_XIAO_Series.kicad_sym | 11661 ++++++++++++++++ .../library/XIAO-ESP32-C5-DIP.kicad_mod | 542 + .../library/XIAO-ESP32-C5-SMD-DIP.kicad_mod | 558 + .../library/XIAO-ESP32-C5-SMD.kicad_mod | 445 + .../production/C-ITS_Companion_v0.1.zip | Bin 0 -> 100901 bytes hardware/c_its-companion/sym-lib-table | 4 + 15 files changed, 27120 insertions(+) create mode 100644 hardware/c_its-companion-v0.1-back.jpg create mode 100644 hardware/c_its-companion-v0.1-front.jpg create mode 100644 hardware/c_its-companion/c_its-companion.kicad_pcb create mode 100644 hardware/c_its-companion/c_its-companion.kicad_prl create mode 100644 hardware/c_its-companion/c_its-companion.kicad_pro create mode 100644 hardware/c_its-companion/c_its-companion.kicad_sch create mode 100644 hardware/c_its-companion/fabrication-toolkit-options.json create mode 100644 hardware/c_its-companion/fp-lib-table create mode 100644 hardware/c_its-companion/library/Seeed_Studio_XIAO_Series.kicad_sym create mode 100644 hardware/c_its-companion/library/XIAO-ESP32-C5-DIP.kicad_mod create mode 100644 hardware/c_its-companion/library/XIAO-ESP32-C5-SMD-DIP.kicad_mod create mode 100644 hardware/c_its-companion/library/XIAO-ESP32-C5-SMD.kicad_mod create mode 100644 hardware/c_its-companion/production/C-ITS_Companion_v0.1.zip create mode 100644 hardware/c_its-companion/sym-lib-table diff --git a/.gitignore b/.gitignore index deb0257..1acf1bd 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,7 @@ target/ # Ignore .DS_Store file in mac **/.DS_Store + +# Ignore KiCad backups and lock files +hardware/c_its-companion/c_its-companion-backups/ +*.kicad_pro.lck diff --git a/hardware/c_its-companion-v0.1-back.jpg b/hardware/c_its-companion-v0.1-back.jpg new file mode 100644 index 0000000000000000000000000000000000000000..969862400169b2debe51bf728ca9ccf0a0f6abcc GIT binary patch literal 148719 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≦W^vFvsT%isj-`H-(H*|n$gt5H{)l;pPzqT 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 literal 0 HcmV?d00001 diff --git a/hardware/c_its-companion-v0.1-front.jpg b/hardware/c_its-companion-v0.1-front.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2386379ce7aa76af99918f3f004c5e94111d7310 GIT binary patch literal 145854 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≷oBCMy-*bi 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 z%0D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 Date: Sun, 26 Jul 2026 21:34:18 +0200 Subject: [PATCH 23/26] gnss: Add support for ublox GNSS --- Cargo.lock | 24 ++++++++++ Cargo.toml | 7 ++- Readme.md | 13 ++--- feat-permut.sh | 8 ++-- src/main.rs | 125 +++++++++++++++++++++++++++++++++++++++++++++---- src/ubx.rs | 106 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 262 insertions(+), 21 deletions(-) create mode 100644 src/ubx.rs diff --git a/Cargo.lock b/Cargo.lock index 3fba778..b3d5c4c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1178,6 +1178,7 @@ dependencies = [ "profont", "prost", "prost-build", + "ublox", ] [[package]] @@ -2490,6 +2491,29 @@ version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" +[[package]] +name = "ublox" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bcc96bd937066775fb87034e1dbf6d7165a8975c74ddec1105e31b4a050538b" +dependencies = [ + "bitflags", + "chrono", + "num-traits", + "ublox_derive", +] + +[[package]] +name = "ublox_derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5eda7d1c1151a40ae85c7c065a19959909573535034e18e1015b738993cd2c0" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "ufmt-write" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index eef57d9..55f42c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ path = "./src/test.rs" required-features = ["std", "cam"] [features] -default = ["esp", "spat", "gnss", "screen"] +default = ["esp", "spat", "gnss_ubx", "screen"] # build for ESP (needed to build test for `host-tuple`) esp = [ @@ -33,8 +33,10 @@ esp = [ ] std = ["c-its-parser/std"] +# enable u-blox GNSS module +gnss_ubx = ["_gnss", "_uart", "dep:ublox"] # enable L67K GNSS module -gnss = ["_gnss", "dep:embassy_gps"] +gnss_l67k = ["_gnss", "dep:embassy_gps"] # echo CAM data on serial cam = ["c-its-parser/cam"] # send CAM autonomously @@ -107,6 +109,7 @@ mipidsi = { version = "0.10.0", optional = true } num-traits = { version = "0.2.19", default-features = false, features = ["libm"] } profont = { version = "0.7.0", optional = true } prost = { version = "0.14", optional = true, default-features = false, features = ["derive"] } +ublox = { version = "0.10", optional = true, default-features = false, features = ["alloc", "ubx_proto27"] } [dev-dependencies] assert_float_eq = "1.2.0" diff --git a/Readme.md b/Readme.md index ace338e..2ae3aab 100644 --- a/Readme.md +++ b/Readme.md @@ -5,15 +5,16 @@ Displays different C-ITS receive-only use cases for cheap. ## Hardware Requirements The code is currently tailored to: - Seeed XIAO ESP32-C5 (8MB PSRAM) -- Seeed XIAO L67K (connected on GPIO12 (RX), GPIO11 (TX), GPIO1 (wakeup) and GPIO25 (reset)) -only available with `gnss` feature (which is included in `spat` feature) +- U-Blox M10Q GNSS (connected on GPIO12 (RX), GPIO11 (TX), and GPIO25 (reset)) +or Seeed XIAO L67K (connected on GPIO12 (RX), GPIO11 (TX), GPIO1 (wakeup) and GPIO25 (reset)) +only available with `gnss_ubx` or `gnss_l67k` feature (which is included in `spat` feature) - Some ST7789 172*320px screen (connected on GPIO8 (SCL/ SCK), GPIO10 (SDA/ MOSI), GPIO7 (reset), GPIO23 (DC), GPIO24 (CS), TODO GPIO0 (BL)) only available with `screen` feature (which is included in `spat` feature) ## Features ### GNSS -Enable L67K GNSS module and in main application logic/ state. +Enable u-blox or L67K GNSS module and in main application logic/ state. ### SPAT Takes the current position and heading from the GNSS receiver and displays the signal phases of the upcoming intersection. @@ -89,9 +90,9 @@ Additional features are available through feature flags: - `cam_tx`: Enable CAM generation and transmission (needs time and position source) Officially supported feature combinations are: -- `esp,spat,gnss,screen` (default features, "standalone") -- `esp,spat,gnss,screen,cam_tx` ("standalone with CAM") -- `esp,spat,gnss,screen,cam_tx,cam,denm` +- `esp,spat,gnss_ubx,screen` (default features, "standalone") +- `esp,spat,gnss_ubx,screen,cam_tx` ("standalone with CAM") +- `esp,spat,gnss_ubx,screen,cam_tx,cam,denm` - `esp,uart,cam,denm` ("smartphone companion") Connect your ESP32-C5 and run: diff --git a/feat-permut.sh b/feat-permut.sh index 2019bef..9c08d65 100755 --- a/feat-permut.sh +++ b/feat-permut.sh @@ -9,12 +9,12 @@ extended=${2:-false} features_base=( uart,cam,denm - spat,gnss,screen - spat,gnss,screen,cam_tx + spat,gnss_ubx,screen + spat,gnss_ubx,screen,cam_tx ) features_more=( - spat,gnss,screen,cam,denm - spat,gnss,screen,cam_tx,cam,denm + spat,gnss_ubx,screen,cam,denm + spat,gnss_ubx,screen,cam_tx,cam,denm ) features_test=( spat,cam,denm,uart diff --git a/src/main.rs b/src/main.rs index 64ed528..5320951 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,11 +8,16 @@ // #![deny(clippy::large_stack_frames)] // we can't use UART I/O and GNSS at the same time -#[cfg(all(feature = "uart", feature = "gnss"))] -compile_error!("feature \"uart\" and feature \"gnss\" cannot be enabled at the same time"); +#[cfg(all(feature = "uart", feature = "gnss_ubx", feature = "gnss_l67k"))] +compile_error!( + "feature \"uart\" and features \"gnss_ubx\" or \"gnss_l67k\" cannot be enabled at the same time" +); // autonomous CAM generation need a time and position source (aka _gnss) #[cfg(all(feature = "cam_tx", not(feature = "_gnss")))] compile_error!("feature \"cam_tx\" needs a time and position source (either GNSS or UART)"); +// we can't have both GNSS drivers enabled at the same time +#[cfg(all(feature = "gnss_ubx", feature = "gnss_l67k"))] +compile_error!("feature \"gnss_ubx\" and feature \"gnss_l67k\" cannot be enabled at the same time"); use alloc::string::ToString as _; use alloc::vec::Vec; @@ -26,7 +31,7 @@ use c_its_parser::standards::extensions::ItsStationType; use critical_section::Mutex; use crossbeam_queue::ArrayQueue; use embassy_executor::Spawner; -#[cfg(feature = "gnss")] +#[cfg(feature = "gnss_l67k")] use embassy_gps::gps::l76k; use esp_backtrace as _; use esp_hal::clock::CpuClock; @@ -47,6 +52,8 @@ mod io; mod radio; #[cfg(feature = "screen")] mod screen; +#[cfg(feature = "gnss_ubx")] +mod ubx; #[cfg(any(feature = "cam_tx", feature = "uart"))] mod v2x_tx; @@ -184,14 +191,15 @@ async fn main(spawner: Spawner) -> ! { let mac = esp_hal::efuse::base_mac_address(); info!("WiFi MAC: {mac}"); - // Setup GNSS: Seeed XIAO L67K: + // Setup GNSS: + // Seeed XIAO L67K: // RX: D7/ GPIO12 // TX: D6/ GPIO11 // WAKEUP: D0/ GPIO1 -> HIGH for active, LOW for Sleep // RESET: D2/ GPIO25 -> HIGH for normal, LOW for Reset - #[cfg(feature = "gnss")] + #[cfg(feature = "gnss_l67k")] spawner.spawn( - gnss_task( + gnss_l67k_task( peripherals.GPIO1, peripherals.GPIO25, peripherals.UART0, @@ -200,7 +208,29 @@ async fn main(spawner: Spawner) -> ! { ) .expect("Failed to spawn GNSS task"), ); - #[cfg(feature = "gnss")] + + // U-Blox module: + // RX: D7/ GPIO12 + // TX: D6/ GPIO11 + // RESET: D2/ GPIO25 -> HIGH for normal, LOW for Reset + #[cfg(feature = "gnss_ubx")] + { + setup_uart0( + 9600, + Some(b'\n'), + peripherals.GPIO12, + peripherals.GPIO11, + peripherals.UART0, + serial_handler, + ) + .expect("Failed to initialize UART for GNSS"); + + // do some settings (UART receive and parsing runs in main loop) + spawner + .spawn(gnss_ubx_config(peripherals.GPIO25).expect("Failed to spawn GNSS config task")); + } + + #[cfg(feature = "_gnss")] info!("GNSS task started, waiting for GNSS fix..."); #[cfg(any(feature = "cam_tx", feature = "uart"))] @@ -226,6 +256,8 @@ async fn main(spawner: Spawner) -> ! { }; #[cfg(feature = "uart")] let mut uart_input = io::Parser::default(); + #[cfg(feature = "gnss_ubx")] + let mut ubx_parser = ublox::Parser::default(); loop { embassy_time::Timer::after(embassy_time::Duration::from_millis(1)).await; @@ -290,6 +322,49 @@ async fn main(spawner: Spawner) -> ! { } } } + #[cfg(feature = "gnss_ubx")] + { + let mut it = ubx_parser.consume_ubx(&data); + loop { + match it.next() { + Some(Ok(ublox::UbxPacket::Proto27(packet_ref))) => match &packet_ref { + ublox::proto27::PacketRef::MonVer(packet) => { + println!("{}", ubx::pretty_print_mon_ver(packet)); + } + ublox::proto27::PacketRef::MonComms(packet) => { + println!("{}", ubx::pretty_print_mon_comms(packet)); + } + ublox::proto27::PacketRef::MonRf(packet) => { + println!("{}", ubx::pretty_print_mon_rf(packet)); + } + ublox::proto27::PacketRef::NavSat(packet) => { + println!("{}", ubx::pretty_print_nav_sat(packet)); + } + ublox::proto27::PacketRef::NavPvt(pvt) => { + match applogic::GnssFix::try_from(pvt) { + Ok(pos_state) => { + critical_section::with(|cs| { + let gnss_update_ref = GNSS_UPDATE.borrow(cs); + gnss_update_ref.replace(Some(pos_state)); + }); + } + Err(err) => warn!("{err}"), + } + } + _ => { + println!("UBX: {packet_ref:?}"); + } + }, + Some(Err(_)) => { + // Received a malformed packet + } + None => { + // The internal buffer is now empty + break; + } + } + } + } } } @@ -626,10 +701,10 @@ fn get_its_header<'p>( } } -#[cfg(feature = "gnss")] +#[cfg(feature = "gnss_l67k")] #[allow(clippy::large_stack_frames, reason = "GNSS driver needs some space")] #[embassy_executor::task] -async fn gnss_task( +async fn gnss_l67k_task( gps_wakeup: esp_hal::peripherals::GPIO1<'static>, gps_reset: esp_hal::peripherals::GPIO25<'static>, uart: esp_hal::peripherals::UART0<'static>, @@ -679,6 +754,38 @@ async fn gnss_task( } } +#[cfg(feature = "gnss_ubx")] +#[embassy_executor::task] +async fn gnss_ubx_config(reset_pin: esp_hal::peripherals::GPIO25<'static>) { + // reset the module + let mut gnss_reset = esp_hal::gpio::Output::new( + reset_pin, + esp_hal::gpio::Level::Low, + esp_hal::gpio::OutputConfig::default(), + ); + embassy_time::Timer::after(embassy_time::Duration::from_millis(2)).await; + gnss_reset.set_high(); + + embassy_time::Timer::after(embassy_time::Duration::from_secs(1)).await; + + info!("UBX: Enabling ubx protocol with NAV-PVT and NAV-SAT"); + let buffer = ublox::packets::cfg_val::CfgValSetBuilder { + version: 1, + layers: ublox::packets::cfg_val::CfgLayerSet::RAM, + reserved1: 0, + cfg_data: &[ + ublox::cfg_val::CfgVal::Uart1OutProtNmea(false), + ublox::cfg_val::CfgVal::Uart1OutProtUbx(true), + // CFG-MSGOUT-UBX_NAV_PVT_UART1 + ublox::cfg_val::CfgVal::MsgOutUbxNavPvtUart1(1), + // CFG-MSGOUT-UBX_NAV_SAT_UART1 + // ublox::cfg_val::CfgVal::MsgOutUbxNavSatUart1(10), + ], + } + .into_packet_vec(); + send_uart0(&buffer); +} + #[cfg(feature = "screen")] fn log_screen_error(error: &E) where diff --git a/src/ubx.rs b/src/ubx.rs new file mode 100644 index 0000000..934af7b --- /dev/null +++ b/src/ubx.rs @@ -0,0 +1,106 @@ +//! u-blox helpers +#![allow(unused)] + +use alloc::vec::Vec; + +pub fn pretty_print_mon_ver(packet: &ublox::mon_ver::MonVerRef) -> alloc::string::String { + alloc::format!( + "UBX-MON-VER: SW version: {} HW version: {}; Extensions: {:?}", + packet.software_version(), + packet.hardware_version(), + packet.extension().collect::>() + ) +} + +pub fn pretty_print_mon_comms(packet: &ublox::mon_comms::MonCommsRef) -> alloc::string::String { + alloc::format!( + "UBX-MON-COMMS: {} ports ({:?}), {} tx err, info {:?}", + packet.n_ports(), + packet.prot_ids(), + packet.tx_errors(), + packet + .ports() + .map(|v| alloc::format!("{v:?}")) + .collect::>() + ) +} + +pub fn pretty_print_mon_rf(packet: &ublox::mon_rf::MonRfRef) -> alloc::string::String { + alloc::format!( + "UBX-MON-RF: {:?}", + packet + .blocks() + .map(|v| alloc::format!("{v:?}")) + .collect::>() + ) +} + +pub fn pretty_print_nav_sat(packet: &ublox::nav_sat::NavSatRef) -> alloc::string::String { + // sort by quality flags + let mut sat_infos = packet.svs().collect::>(); + sat_infos.sort_unstable_by_key(|v| v.flags_raw() & 0x7); + sat_infos.reverse(); + + let sat_info_strings = sat_infos + .iter() + .map(|v| { + alloc::format!( + "- {}/{:3}: E {:3.0}, A {:3.0}, CNO {:2.1} dBHz, {:?}, {:?}", + v.gnss_id(), + v.sv_id(), + v.elev(), + v.azim(), + v.cno(), + v.flags().quality_ind(), + v.flags().health(), + ) + }) + .collect::>(); + + alloc::format!( + "UBX-NAV-SAT: num {:?}\n{}", + packet.num_svs(), + sat_info_strings.join("\n") + ) +} + +impl<'a> TryFrom<&ublox::nav_pvt::proto27::NavPvtRef<'a>> for crate::applogic::GnssFix { + type Error = alloc::string::String; + + fn try_from(value: &ublox::nav_pvt::proto27::NavPvtRef<'a>) -> Result { + let has_time = value.fix_type() == ublox::GnssFixType::Fix3D + || value.fix_type() == ublox::GnssFixType::GPSPlusDeadReckoning + || value.fix_type() == ublox::GnssFixType::TimeOnlyFix; + let has_posvel = value.fix_type() == ublox::GnssFixType::Fix3D + || value.fix_type() == ublox::GnssFixType::GPSPlusDeadReckoning; + + if !has_posvel || !has_time { + return Err(alloc::format!( + "NAV-PVT without valid fix: {:?}", + value.fix_type() + )); + } + + let pos: ublox::PositionLLA = value.into(); + let vel: ublox::Velocity = value.into(); + + let time: chrono::DateTime = value + .try_into() + .map_err(|err| alloc::format!("Failed to convert time: {err}"))?; + + let latitude_deg = pos.lat; + let longitude_deg = pos.lon; + #[allow(clippy::cast_precision_loss)] + let heading_deg = vel.heading as f32; + #[allow(clippy::cast_precision_loss)] + let speed_mps = vel.speed as f32; + + Ok(Self { + time: Some(time.naive_utc()), + latitude_deg, + longitude_deg, + heading_deg: Some(heading_deg), + speed_mps: Some(speed_mps), + }) + } +} From 666499c240bb22f118c6cf6d2ad92b1c9e254b0b Mon Sep 17 00:00:00 2001 From: Jannik Beyerstedt Date: Fri, 31 Jul 2026 16:01:43 +0200 Subject: [PATCH 24/26] Move core ESP32 V2X to own repo --- Cargo.lock | 17 ++- Cargo.toml | 5 +- src/applogic/cam_tx.rs | 61 +++++++-- src/applogic/mod.rs | 18 +-- src/io.rs | 18 +-- src/main.rs | 62 ++++----- src/radio.rs | 288 ----------------------------------------- src/test.rs | 2 - src/ubx.rs | 4 +- src/v2x_tx/gn.rs | 94 -------------- src/v2x_tx/mod.rs | 164 ----------------------- 11 files changed, 118 insertions(+), 615 deletions(-) delete mode 100644 src/radio.rs delete mode 100644 src/v2x_tx/gn.rs delete mode 100644 src/v2x_tx/mod.rs diff --git a/Cargo.lock b/Cargo.lock index b3d5c4c..98a4c6a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -192,9 +192,9 @@ checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "c-its-parser" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd4db66f495280ec88eef8712dd9ab7d1aacc590f62485e3e03a89b3ce4f8b14" +checksum = "7e94d9990b20498f621f164f23ae198c4365115eb4191ae106352ec74d4a4110" dependencies = [ "chrono", "etherparse", @@ -1171,6 +1171,7 @@ dependencies = [ "esp-println", "esp-radio", "esp-rtos", + "esp32-cits-core", "geo-types", "log", "mipidsi", @@ -1181,6 +1182,18 @@ dependencies = [ "ublox", ] +[[package]] +name = "esp32-cits-core" +version = "0.1.0" +source = "git+https://git.hamburg.ccc.de/jtbx/esp32-cits-core?branch=main#a6d0ea6ca2da130fde2b84060a34ef45e772a459" +dependencies = [ + "c-its-parser", + "chrono", + "esp-hal", + "esp-radio", + "geo-types", +] + [[package]] name = "esp32c2" version = "0.29.2" diff --git a/Cargo.toml b/Cargo.toml index 55f42c5..9b3cbd9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,8 +30,10 @@ esp = [ "dep:esp-backtrace", "dep:esp-println", "dep:esp-radio", + "esp32-cits-core/esp32c5", + "esp32-cits-core/log-04", ] -std = ["c-its-parser/std"] +std = ["c-its-parser/std", "esp32-cits-core/std"] # enable u-blox GNSS module gnss_ubx = ["_gnss", "_uart", "dep:ublox"] @@ -93,6 +95,7 @@ esp-radio = { version = "0.18.0", optional = true, features = [ ] } embassy_gps = { version = "0.1.0", optional = true, default-features = false, features = ["esp", "log-04"], git = "https://github.com/jbeyerstedt/embassy_gps.git", branch = "feat/rework-gpsfix" } +esp32-cits-core = { version = "0.1.0", default-features = false, git = "https://git.hamburg.ccc.de/jtbx/esp32-cits-core", branch = "main" } embedded-hal-bus = { version = "0.3.0", optional = true } embedded-graphics = { version = "0.8.2", optional = true } # needs newer GN with removed bitvec dependency diff --git a/src/applogic/cam_tx.rs b/src/applogic/cam_tx.rs index e15de27..850ae40 100644 --- a/src/applogic/cam_tx.rs +++ b/src/applogic/cam_tx.rs @@ -11,6 +11,45 @@ const PH_MAX_LENGHT_M: f32 = 500.; const PH_MAX_ITEMS: usize = 23; const PH_MIN_DIST_M: f32 = 20.; +/// Stand-alone CAM generator +/// +/// Generates CAM messages from time and [`PosVel`] updates +/// +/// # Send a message +/// ``` +/// use esp32_cits_core::tx::GnItsPayload as _; +/// +/// let (mut seq_no, mac_bytes) = { +/// // unwrap is fine since [`esp_hal::efuse::MacAddress`] is just an `[u8; 6]` +/// let mac_bytes = mac.as_bytes().try_into().unwrap(); +/// +/// (0, mac_bytes) +/// }; +/// +/// let mut cam = { +/// let station_id = make_station_id(mac); +/// info!("V2X Station ID: {station_id}"); +/// +/// applogic::cam_tx::CamState::new( +/// station_id, +/// OWN_STATION_TYPE, +/// OWN_VEHICLE_LENGTH, +/// OWN_VEHICLE_WIDTH, +/// ) +/// }; +/// +/// cam.update(time, fix.clone().into()); +/// match cam.encode_packet(mac_bytes, pos_vel, time, GN_IS_MOBILE, &mut seq_no) { +/// Ok(data) => { +/// if let Err(err) = +/// radio::send_80211_bcast_frame(&mut wlan_iface, &mac_bytes.clone(), &data) +/// { +/// warn!("Failed to send 802.11 frame: {err}"); +/// } +/// } +/// Err(err) => warn!("Failed to create CAM: {err}"), +/// } +/// ``` #[derive(Debug)] pub struct CamState { station_id: u32, @@ -20,7 +59,7 @@ pub struct CamState { vehicle_width_dm: u8, // vehicle width is in 10cm steps time: chrono::DateTime, - pos_state: super::PositionState, + pos_state: esp32_cits_core::PosVel, /// database for path history, oldest item at the back of the vector path_history: Vec, @@ -35,7 +74,7 @@ struct PathPoint { } impl PathPoint { - fn new_from_pos(pos: &super::PositionState, time: chrono::DateTime) -> Self { + fn new_from_pos(pos: &esp32_cits_core::PosVel, time: chrono::DateTime) -> Self { Self { pos: pos.position, time, @@ -44,7 +83,7 @@ impl PathPoint { } fn new_from_pos_with_dist( - pos: &super::PositionState, + pos: &esp32_cits_core::PosVel, time: chrono::DateTime, dist_m: f32, ) -> Self { @@ -69,12 +108,12 @@ impl CamState { vehicle_length_dm, vehicle_width_dm, time: chrono::DateTime::::default(), - pos_state: super::PositionState::default(), + pos_state: esp32_cits_core::PosVel::default(), path_history: Vec::with_capacity(PH_MAX_ITEMS + 1), } } - pub fn update(&mut self, time: chrono::DateTime, pos: super::PositionState) { + pub fn update(&mut self, time: chrono::DateTime, pos: esp32_cits_core::PosVel) { self.time = time; self.pos_state = pos; @@ -86,7 +125,7 @@ impl CamState { fn update_ph( path_history: &mut Vec, time: chrono::DateTime, - pos: &super::PositionState, + pos: &esp32_cits_core::PosVel, ) { // Update path history if path_history.is_empty() { @@ -293,11 +332,11 @@ impl CamState { } } -impl crate::v2x_tx::GnItsPayload for CamState { +impl esp32_cits_core::tx::GnItsPayload for CamState { fn make_eh( &self, address: [u8; 6], - own_position: &crate::applogic::PositionState, + own_position: &esp32_cits_core::PosVel, time: chrono::DateTime, _seq_no: &mut u16, ) -> Result< @@ -313,7 +352,7 @@ impl crate::v2x_tx::GnItsPayload for CamState { // CAM always uses SHB, hop-limit 1 Ok(( - crate::v2x_tx::gn::make_shb_eh(address, station_type, own_position, time), + esp32_cits_core::tx::gn::make_shb_eh(address, station_type, own_position, time)?, None, 1, )) @@ -336,8 +375,8 @@ mod tests { use super::*; use crate::init_test_env_logger; - fn posstate_from_pos(position: geo_types::Point) -> crate::applogic::PositionState { - crate::applogic::PositionState { + fn posstate_from_pos(position: geo_types::Point) -> esp32_cits_core::PosVel { + esp32_cits_core::PosVel { position, heading_deg: None, speed_mps: None, diff --git a/src/applogic/mod.rs b/src/applogic/mod.rs index 7847010..70cc086 100644 --- a/src/applogic/mod.rs +++ b/src/applogic/mod.rs @@ -49,7 +49,7 @@ pub struct State { time: chrono::NaiveDateTime, #[allow(unused)] - pos: PositionState, + pos: esp32_cits_core::PosVel, #[cfg(feature = "spat")] last_prune: chrono::NaiveDateTime, @@ -130,15 +130,7 @@ pub struct GnssFix { pub speed_mps: Option, } -#[allow(unused)] -#[derive(Debug, Default, Clone, PartialEq)] -pub struct PositionState { - pub position: geo_types::Point, - pub heading_deg: Option, - pub speed_mps: Option, -} - -impl From<&GnssFix> for PositionState { +impl From<&GnssFix> for esp32_cits_core::PosVel { fn from(value: &GnssFix) -> Self { let position = geo_types::Point::new(value.longitude_deg, value.latitude_deg); @@ -149,7 +141,7 @@ impl From<&GnssFix> for PositionState { } } } -impl From for PositionState { +impl From for esp32_cits_core::PosVel { fn from(value: GnssFix) -> Self { (&value).into() } @@ -212,7 +204,7 @@ impl State { Self { initialized: false, time: chrono::NaiveDateTime::default(), - pos: PositionState::default(), + pos: esp32_cits_core::PosVel::default(), #[cfg(feature = "spat")] last_prune: chrono::NaiveDateTime::default(), #[cfg(feature = "spat")] @@ -641,7 +633,7 @@ impl State { } #[allow(unused)] - pub fn pos(&self) -> &PositionState { + pub fn pos(&self) -> &esp32_cits_core::PosVel { &self.pos } } diff --git a/src/io.rs b/src/io.rs index 4642253..44997ee 100644 --- a/src/io.rs +++ b/src/io.rs @@ -169,7 +169,7 @@ impl From for crate::applogic::GnssFix { } } -impl From for crate::applogic::PositionState { +impl From for esp32_cits_core::PosVel { fn from(value: msg::PositionState) -> Self { let position = geo_types::Point::new( value.position.longitude_deg.into(), @@ -194,7 +194,7 @@ impl From for crate::applogic::PositionState { } } -impl From for crate::applogic::PositionState { +impl From for esp32_cits_core::PosVel { fn from(value: msg::ItsPosition) -> Self { let latitude_deg = f64::from(value.latitude) / 10_000_000.; let longitude_deg = f64::from(value.longitude) / 10_000_000.; @@ -208,11 +208,11 @@ impl From for crate::applogic::PositionState { } } -impl crate::v2x_tx::GnItsPayload for msg::RawMsgTx { +impl esp32_cits_core::tx::GnItsPayload for msg::RawMsgTx { fn make_eh( &self, address: [u8; 6], - own_position: &crate::applogic::PositionState, + own_position: &esp32_cits_core::PosVel, time: chrono::DateTime, seq_no: &mut u16, ) -> Result< @@ -275,19 +275,19 @@ impl crate::v2x_tx::GnItsPayload for msg::RawMsgTx { *seq_no += 1; ( - crate::v2x_tx::gn::make_tsb_eh( + esp32_cits_core::tx::gn::make_tsb_eh( address, station_type, own_position, time, *seq_no, - ), + )?, None, hop_limit, ) } msg::GnTransport::Shb => ( - crate::v2x_tx::gn::make_shb_eh(address, station_type, own_position, time), + esp32_cits_core::tx::gn::make_shb_eh(address, station_type, own_position, time)?, None, 1, ), @@ -308,7 +308,7 @@ impl msg::RawMsgTx { fn make_geobcast( address: [u8; 6], station_type: geonetworking::en302636_4_1::StationType, - own_position: &crate::applogic::PositionState, + own_position: &esp32_cits_core::PosVel, time: chrono::DateTime, gn_area: msg::GnArea, sequence_number: u16, @@ -320,7 +320,7 @@ impl msg::RawMsgTx { alloc::string::String, > { let source_position_vector = - crate::v2x_tx::gn::make_lpv(address, station_type, own_position, time); + esp32_cits_core::tx::gn::make_lpv(address, station_type, own_position, time)?; let (atype, dist_a, dist_b, angle) = match gn_area.area() { msg::GnAreaShape::EtsiAreashapeCircle => ( diff --git a/src/main.rs b/src/main.rs index 5320951..67e181c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -38,7 +38,7 @@ use esp_hal::clock::CpuClock; use esp_hal::timer::timg::TimerGroup; #[cfg(all(target_arch = "riscv32", feature = "spat"))] use esp_println::println; -use esp_radio::wifi; +use esp32_cits_core::radio; use geonetworking::Decode as _; use log::{error, info, warn}; @@ -49,13 +49,10 @@ mod cache; mod geo_alg; #[cfg(feature = "uart")] mod io; -mod radio; #[cfg(feature = "screen")] mod screen; #[cfg(feature = "gnss_ubx")] mod ubx; -#[cfg(any(feature = "cam_tx", feature = "uart"))] -mod v2x_tx; const WIFI_CHANNEL: radio::Channel = 180; const WIFI_TXPOWER: i8 = 10; // dBm, 2..20 @@ -244,7 +241,7 @@ async fn main(spawner: Spawner) -> ! { let mut state = applogic::State::new(); #[cfg(feature = "cam_tx")] let mut cam = { - let station_id = v2x_tx::make_station_id(mac); + let station_id = make_station_id(mac); info!("V2X Station ID: {station_id}"); applogic::cam_tx::CamState::new( @@ -289,7 +286,7 @@ async fn main(spawner: Spawner) -> ! { }); } Some(io::msg::serial_input_msg::Payload::TxMsg(tx_request)) => { - use v2x_tx::GnItsPayload as _; + use esp32_cits_core::tx::GnItsPayload as _; info!( "UART: Got new TX message: Type {} with {} bytes", @@ -380,7 +377,7 @@ async fn main(spawner: Spawner) -> ! { #[cfg(feature = "cam_tx")] { - use v2x_tx::GnItsPayload; + use esp32_cits_core::tx::GnItsPayload as _; let time = state.time().and_utc(); cam.update(time, fix.clone().into()); @@ -513,7 +510,7 @@ async fn main(spawner: Spawner) -> ! { reason = "adhering to callback interface" )] #[allow(clippy::too_many_lines)] -fn handle_frame(frame: wifi::sniffer::PromiscuousPkt<'_>) { +fn handle_frame(frame: esp_radio::wifi::sniffer::PromiscuousPkt<'_>) { #[cfg(feature = "spat")] const PRUNE_INTERVAL: chrono::Duration = chrono::Duration::seconds(60); #[cfg(feature = "spat")] @@ -534,27 +531,18 @@ fn handle_frame(frame: wifi::sniffer::PromiscuousPkt<'_>) { .naive_utc() }; - // Ignore frames with errors and non-data frames - if frame.rx_cntl.rx_state != 0 { - warn!("Received frame has RX error: {}", frame.rx_cntl.rx_state); - return; - } - if !radio::PromiscuousPktType::Data.matches(frame.frame_type) { - return; - } - - // Ignore non-broadcast frames - if frame.len < (4 + 6) { - warn!( - "Received frame too small to be a valid broadcast frame: {} bytes", - frame.len - ); - return; - } - let dest_mac = &frame.data[4..10]; - if dest_mac != [0xff; 6] { - warn!("Received frame is not broadcast frame: Dest MAC {dest_mac:02x?}"); - return; + match esp32_cits_core::rx::is_broadcast_frame(&frame) { + Err(err) => { + warn!("{err}"); + return; + } + Ok(false) => { + // drop frame + return; + } + Ok(true) => { + // continue + } } // info!("Received frame with {} bytes", frame.len); @@ -885,3 +873,19 @@ fn send_uart0(data: &[u8]) { } }); } + +#[cfg(all(feature = "esp", feature = "cam_tx"))] +/// Creates a station ID from the WLAN MAC address +/// +/// Will use `0xC173` as static prefix and uses last 16 bit from MAC address for the remainder. +/// +/// # Panics +/// Won't panic in practive b/c of pre-conditions +#[must_use] +pub fn make_station_id(mac: esp_hal::efuse::MacAddress) -> u32 { + // unwrap is fine here since we can be sure that the MacAddress is 6 byte + // and a slice of 2 bytes can be converted to [u8; 2] + let mac_last_16bit = u16::from_be_bytes(mac.as_bytes().get(4..6).unwrap().try_into().unwrap()); + + 0xC173_0000 | u32::from(mac_last_16bit) +} diff --git a/src/radio.rs b/src/radio.rs deleted file mode 100644 index 46cd72a..0000000 --- a/src/radio.rs +++ /dev/null @@ -1,288 +0,0 @@ -//! IEEE 802.11p C-ITS Capture with ESP32-C5 -#![allow(unused)] - -// ----------------------------- -// missing interface definitions -// ----------------------------- - -pub type Channel = core::ffi::c_int; - -unsafe extern "C" { - /// Changes the 802.11 PHY channel - /// - /// C function signature: `void phy_change_channel(int,int,int,int)` - /// - /// Options: - /// - `primary`: Channel frequency in MHz - /// - `ignored1`: Ignored (set to 1) - /// - `ignored2`: Ignored (set to 0) - /// - `ht_mode`: Probably HT mode - unsafe fn phy_change_channel( - primary: Channel, - ignored1: core::ffi::c_int, - ignored2: core::ffi::c_int, - ht_mode: core::ffi::c_int, - ) -> core::ffi::c_int; - - /// Enables 802.11p mode on the ESP32-C5 - /// - /// C function signature: `void phy_11p_set(int,int)` - /// - /// Options: - /// - `enable`: Boolean option to enable 802.11p mode - /// - `half_rate`: 5 MHz mode - unsafe fn phy_11p_set(enable: u8, half_rate: u8); - -} - -/// Enum values for `wifi_promiscuous_pkt_type_t` used in `esp_radio::wifi::sniffer::PromiscuousPkt::frame_type` -/// -/// Original value is actually `core::ffi::c_uint`, but that can't be used in `#[repr()]` attributes, -/// so we're using `u8` as the smallest suitable type here that can be casted to a bigger type. -#[repr(u8)] -#[derive(Debug, Clone, Copy)] -#[allow(dead_code)] -pub(crate) enum PromiscuousPktType { - /// Management frame, indicates 'buf' argument is `wifi_promiscuous_pkt_t` - Mgmt = 0, - /// Control frame, indicates 'buf' argument is `wifi_promiscuous_pkt_t` - Ctrl = 1, - /// Data frame, indicates 'buf' argument is `wifi_promiscuous_pkt_t` - Data = 2, - /// Other type, such as MIMO etc. 'buf' argument is `wifi_promiscuous_pkt_t` but the payload is zero length. - Misc = 3, -} - -impl PromiscuousPktType { - #[allow(dead_code)] - pub fn as_repr(self) -> u8 { - self as u8 - } - - /// Converts to underlying type of `wifi_promiscuous_pkt_type_t` (used by `esp_radio::wifi::sniffer::PromiscuousPkt::frame_type`) - pub fn as_frame_type(self) -> core::ffi::c_uint { - core::ffi::c_uint::from(self.as_repr()) - } - - /// Determines if we are the same frame type as `esp_radio::wifi::sniffer::PromiscuousPkt::frame_type` - pub fn matches(self, frame_type: core::ffi::c_uint) -> bool { - self.as_frame_type() == frame_type - } -} - -#[cfg(feature = "esp")] -/// Configure WiFi Sniffer to a certain 802.11p channel -pub fn setup_wifi_sniffer( - channel: Channel, - sniffer: &mut esp_radio::wifi::sniffer::Sniffer, - callback: fn(esp_radio::wifi::sniffer::PromiscuousPkt<'_>), -) -> Result<(), alloc::string::String> { - sniffer - .set_promiscuous_mode(true) - .map_err(|err| alloc::format!("Failed to enable promiscuous mode: {err:?}"))?; - - sniffer.set_receive_cb(callback); - - // convert channel number to frequency in MHz - let primary = 5000 + (channel * 5); - - unsafe { - phy_11p_set(1, 0); - match phy_change_channel(primary, 1, 0, 0) { - 0 => Ok(()), - ret => Err(alloc::format!("Failed to change channel: {ret}")), - } - } -} - -#[cfg(feature = "esp")] -/// Sends 802.11p broadcast frame -/// -/// Expects `data` to contain GN header (BTP header and ITS payload) to be a valid C-ITS/ ITS-G5 packet/ frame. -/// -/// # Errors -/// Fails when MAC is not the right size or returns frame send error -pub fn send_80211_bcast_frame( - sniffer: &mut esp_radio::wifi::sniffer::Sniffer, - mac_addr: &[u8], - data: &[u8], -) -> Result<(), alloc::string::String> { - const HEADER_SIZE: usize = 24 + 8; - - let mac = mac_addr - .try_into() - .map_err(|err| alloc::format!("Unexpected MAC address size: {err}"))?; - - // assemble frame - let mut frame_buf = alloc::vec::Vec::with_capacity(HEADER_SIZE + data.len()); - frame_buf.extend_from_slice(&DataFrameHeader::new_80211p(mac).to_binary()); - frame_buf.extend_from_slice(&LlcHeader::new_80211p().to_binary()); - frame_buf.extend_from_slice(data); - - // send - sniffer - .send_raw_frame(true, &frame_buf, true) - .map_err(|err| alloc::format!("Failed to send 802.11p frame: {err:?}")) -} - -/// IEEE 802.11p frame header -#[derive(Debug, Default)] -struct DataFrameHeader { - /// byte 0: subtype in upper nibble, type in lower nibble - /// "normal" data frame: 0x08, QoS data frame: 0x88 - pub type_and_subtype: u8, - /// byte 1: frame contol field, 0x00 for 802.11p - pub fcf_flags: u8, - /// byte 2..3: duration in micros, usually 0x0000 for 802.11p (toDS = 0, fromDS = 0) - pub duration: u16, - /// byte 4..9: receiver/ destination for 802.11p - pub address_1: [u8; 6], - /// byte 10..15: transmitter/ source for 802.11p - pub address_2: [u8; 6], - /// byte 16..21: BSSID for 802.11p - pub address_3: [u8; 6], - /// byte 22..24: upper 12 bits: seq. no, lower 4 bits: fragment no - pub sequence_control: u16, - // Note: address 4 is not used in 802.11p - // pub qos: Option<[u8; 2]>, // QoS frames are supported by radio library - // Note: 802.11p does not use HT mode(s) -} - -impl DataFrameHeader { - const BCAST_MAC: [u8; 6] = [0xFF; 6]; - - /// Create an 802.11p (non-QoS) data frame header with a source MAC address - pub(crate) fn new_80211p(source_mac: &[u8; 6]) -> Self { - Self { - type_and_subtype: 0x08, - address_1: Self::BCAST_MAC, - address_2: *source_mac, - address_3: Self::BCAST_MAC, - ..Default::default() - } - } - - pub(crate) fn to_binary(&self) -> [u8; 24] { - let mut buf = [0u8; 24]; - - buf[0] = self.type_and_subtype; - - buf[1] = self.fcf_flags; - - Self::write_u16(&mut buf, 2, self.duration); - Self::write_mac_addr(&mut buf, 4, &self.address_1); - Self::write_mac_addr(&mut buf, 10, &self.address_2); - Self::write_mac_addr(&mut buf, 16, &self.address_3); - Self::write_u16(&mut buf, 22, self.sequence_control); - - buf - } - - fn write_u16(buf: &mut [u8; 24], offset: usize, data: u16) { - for (idx, byte) in data.to_be_bytes().iter().enumerate() { - buf[idx + offset] = *byte; - } - } - - fn write_mac_addr(buf: &mut [u8; 24], offset: usize, data: &[u8; 6]) { - for (idx, byte) in data.iter().enumerate() { - buf[idx + offset] = *byte; - } - } -} - -/// IEEE 802.11p LLC header -#[derive(Debug, Default)] -struct LlcHeader { - /// byte 0: SNAP (0xaa) in 802.11p - pub dsap: u8, // SNAP in - /// byte 1: SNAP (0xaa) in 802.11p - pub lsap: u8, // SNAP in - /// byte 2: SNAP (0xaa) in 802.11p - pub control_1: u8, // 0x03 - // Note: optional second control byte not used in 802.11p - - // SNAP fields - /// set to 0 since ethertype is used for protocol ID in 802.11p - pub oui: [u8; 3], - /// ethertype GeoNetworking (0x8947) in 802.11p - pub protocol_id: u16, -} - -impl LlcHeader { - pub(crate) fn new_80211p() -> Self { - Self { - dsap: 0xaa, - lsap: 0xaa, - control_1: 0x03, - oui: [0; 3], - protocol_id: 0x8947, - } - } - - pub(crate) fn to_binary(&self) -> [u8; 8] { - let mut buf: [u8; 8] = [ - self.dsap, - self.lsap, - self.control_1, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - ]; - - buf[3] = self.oui[0]; - buf[4] = self.oui[1]; - buf[5] = self.oui[2]; - - buf[6] = self.protocol_id.to_be_bytes()[0]; - buf[7] = self.protocol_id.to_be_bytes()[1]; - - buf - } -} - -#[cfg(test)] -mod tests { - - use super::*; - - #[test] - fn data_frame_test() { - // build a typical IEEE 802.11p frame - const OWN_MAC: [u8; 6] = [0x12, 0x34, 0x56, 0x78, 0xab, 0xcd]; - const REF_HEADER: [u8; 24] = [ - 0x08, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x12, 0x34, 0x56, 0x78, - 0xab, 0xcd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, - ]; - - let frame = DataFrameHeader::new_80211p(&OWN_MAC); - - assert_eq!(0x08, frame.type_and_subtype); // data frame, subtype 0 - assert_eq!(0x0000, frame.fcf_flags); - assert_eq!(0x0000, frame.duration); - assert_eq!([0xFF; 6], frame.address_1); - assert_eq!(OWN_MAC, frame.address_2); - assert_eq!([0xFF; 6], frame.address_3); - assert_eq!(0x0000, frame.sequence_control); - - assert_eq!(REF_HEADER, frame.to_binary()); - } - - #[test] - fn llc_test() { - // build a typical IEEE 802.11p LLC header - const REF_HEADER: [u8; 8] = [0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00, 0x89, 0x47]; - - let llc = LlcHeader::new_80211p(); - - assert_eq!(0xaa, llc.dsap); - assert_eq!(0xaa, llc.lsap); - assert_eq!(0x03, llc.control_1); - assert_eq!([0x00; 3], llc.oui); - assert_eq!(0x8947, llc.protocol_id); - - assert_eq!(REF_HEADER, llc.to_binary()); - } -} diff --git a/src/test.rs b/src/test.rs index 6bb20d2..a7a5bb5 100644 --- a/src/test.rs +++ b/src/test.rs @@ -7,9 +7,7 @@ mod applogic; mod cache; mod geo_alg; mod io; -mod radio; mod testdata; -mod v2x_tx; fn main() { // env_logger::init(); diff --git a/src/ubx.rs b/src/ubx.rs index 934af7b..8e1f32e 100644 --- a/src/ubx.rs +++ b/src/ubx.rs @@ -90,9 +90,9 @@ impl<'a> TryFrom<&ublox::nav_pvt::proto27::NavPvtRef<'a>> for crate::applogic::G let latitude_deg = pos.lat; let longitude_deg = pos.lon; - #[allow(clippy::cast_precision_loss)] + #[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)] let heading_deg = vel.heading as f32; - #[allow(clippy::cast_precision_loss)] + #[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)] let speed_mps = vel.speed as f32; Ok(Self { diff --git a/src/v2x_tx/gn.rs b/src/v2x_tx/gn.rs deleted file mode 100644 index 4b2b0b9..0000000 --- a/src/v2x_tx/gn.rs +++ /dev/null @@ -1,94 +0,0 @@ -//! Geonetworking utilities - -use c_its_parser::gn as geonetworking; - -#[allow(unused)] -pub fn make_shb_eh( - address: [u8; 6], - station_type: geonetworking::en302636_4_1::StationType, - own_position: &crate::applogic::PositionState, - time: chrono::DateTime, -) -> geonetworking::en302636_4_1::ExtendedHeader { - let source_position_vector = make_lpv(address, station_type, own_position, time); - - geonetworking::en302636_4_1::ExtendedHeader::SHB( - geonetworking::en302636_4_1::SingleHopBroadcast { - source_position_vector, - media_dependent_data: [0; 4], - }, - ) -} - -#[allow(unused)] -pub fn make_tsb_eh( - address: [u8; 6], - station_type: geonetworking::en302636_4_1::StationType, - own_position: &crate::applogic::PositionState, - time: chrono::DateTime, - sequence_number: u16, -) -> geonetworking::en302636_4_1::ExtendedHeader { - let source_position_vector = make_lpv(address, station_type, own_position, time); - - geonetworking::en302636_4_1::ExtendedHeader::TSB( - geonetworking::en302636_4_1::TopologicallyScopedBroadcast::new( - sequence_number, - source_position_vector, - ), - ) -} - -#[allow(unused)] -pub fn make_gbc_eh( - address: [u8; 6], - station_type: geonetworking::en302636_4_1::StationType, - own_position: &crate::applogic::PositionState, - time: chrono::DateTime, - target_pos: geo_types::Point, - radius_m: u16, - sequence_number: u16, -) -> geonetworking::en302636_4_1::ExtendedHeader { - let source_position_vector = make_lpv(address, station_type, own_position, time); - #[allow(clippy::cast_possible_truncation)] - let latitude_deg = target_pos.x() as f32; - #[allow(clippy::cast_possible_truncation)] - let longitude_deg = target_pos.y() as f32; - - geonetworking::en302636_4_1::ExtendedHeader::GBC( - geonetworking::en302636_4_1::GeoBroadcast::try_from_values( - sequence_number, - source_position_vector, - latitude_deg, - longitude_deg, - radius_m, - 0, - 0, - ) - .expect("Failed to create circular GBC"), - ) -} - -pub fn make_lpv( - address: [u8; 6], - station_type: geonetworking::en302636_4_1::StationType, - own_position: &crate::applogic::PositionState, - time: chrono::DateTime, -) -> geonetworking::en302636_4_1::LongPositionVector { - let timestamp_its = c_its_parser::time_utils::TimestampIts::from(time); - #[allow(clippy::cast_possible_truncation)] - let latitude_deg = own_position.position.y() as f32; - #[allow(clippy::cast_possible_truncation)] - let longitude_deg = own_position.position.x() as f32; - let speed_mps = own_position.speed_mps.unwrap_or_default(); - let heading_deg = own_position.heading_deg.unwrap_or_default(); - - geonetworking::en302636_4_1::LongPositionVector::try_from_values( - geonetworking::en302636_4_1::Address::new(false, station_type, address), - timestamp_its.0, - latitude_deg, - longitude_deg, - true, - speed_mps, - heading_deg, - ) - .expect("Failed to create LongPositionVector") -} diff --git a/src/v2x_tx/mod.rs b/src/v2x_tx/mod.rs deleted file mode 100644 index ee48578..0000000 --- a/src/v2x_tx/mod.rs +++ /dev/null @@ -1,164 +0,0 @@ -//! V2X Transmission - -use alloc::vec::Vec; - -use c_its_parser::gn::{self as geonetworking, Encode}; - -pub mod gn; - -#[cfg(all(feature = "esp", feature = "cam_tx"))] -/// Creates a station ID from the WLAN MAC address -/// -/// Will use `0xC173` as static prefix and uses last 16 bit from MAC address for the remainder. -pub fn make_station_id(mac: esp_hal::efuse::MacAddress) -> u32 { - // unwrap is fine here since we can be sure that the MacAddress is 6 byte - // and a slice of 2 bytes can be converted to [u8; 2] - let mac_last_16bit = u16::from_be_bytes(mac.as_bytes().get(4..6).unwrap().try_into().unwrap()); - - 0xC173_0000 | u32::from(mac_last_16bit) -} - -pub trait GnItsPayload { - // common GN parameters - const LIFETIME_MILLIS: u32 = 60_000; // itsGnDefaultPacketLifetime is 60 seconds according to ETSI EN 302 636-4-1 V1.4.1 - - // --------------------------------------------- - // methods to build a GN packet with ITS payload - // --------------------------------------------- - - /// Builds the [`geonetworking::en302636_4_1::ExtendedHeader`] and additional information for the ITS message - /// - /// Output: - /// - Extended header - /// - Area type (when using GAC or GBC) - /// - hop limit - /// - /// Note: Make sure to increase the sequence number on multi-hop packets. - fn make_eh( - &self, - address: [u8; 6], - own_position: &crate::applogic::PositionState, - time: chrono::DateTime, - seq_no: &mut u16, - ) -> Result< - ( - geonetworking::en302636_4_1::ExtendedHeader, - Option, - u8, - ), - alloc::string::String, - >; - - /// Creates the ITS payload (without BTP header) - fn make_payload( - &self, - ) -> Result<(Vec, c_its_parser::standards::extensions::ItsMessageId), alloc::string::String>; - - // --------------------------------------------- - // common methods (with implementation) - // --------------------------------------------- - - /// Encodes the GN packet - fn encode_packet( - &self, - address: [u8; 6], - own_position: &crate::applogic::PositionState, - time: chrono::DateTime, - is_mobile: bool, - seq_no: &mut u16, - ) -> Result, alloc::string::String> { - // Build payload (including BTP header) - let (mut its_payload, msg_type) = self.make_payload()?; - let mut payload = make_btp_b(msg_type).encode()?; - - payload.append(&mut its_payload); - - #[allow(clippy::cast_possible_truncation)] - let payload_length = (payload.len()) as u16; - - // get extended header (depending on message type and sometimes content as well) - let (eh, area_type, maximum_hop_limit) = - self.make_eh(address, own_position, time, seq_no)?; - - // Build GN headers - let traffic_class = geonetworking::en302636_4_1::TrafficClass::try_new( - false, - false, - make_traffic_class(msg_type), - ) - .map_err(|err| alloc::format!("Failed to create TrafficClass: {err:?}"))?; - let common = geonetworking::en302636_4_1::CommonHeader::from_values( - geonetworking::en302636_4_1::NextAfterCommon::BTPB, - geonetworking::en302636_4_1::HeaderType::new_from(&eh, area_type)?, - traffic_class, - is_mobile, - payload_length, - maximum_hop_limit, - ); - - let remaining_hop_limit = maximum_hop_limit; - let basic = geonetworking::en302636_4_1::BasicHeader::try_new( - 1, - geonetworking::en302636_4_1::NextAfterBasic::CommonHeader, - geonetworking::en302636_4_1::Lifetime::from_milliseconds(Self::LIFETIME_MILLIS), - remaining_hop_limit, - ) - .map_err(|err| alloc::format!("Failed to create BasicHeader: {err}"))?; - - // Assemble and encode packet - let packet = geonetworking::Packet::Unsecured { - basic, - common, - extended: Some(eh), - payload: &payload, - }; - - packet - .encode_to_vec() - .map_err(|err| alloc::format!("Failed to encode GN packet: {err:?}")) - } -} - -/// Creates a BTP-B header based on the ITS message ID (message type) -/// -/// Port numbers and destination port info according to ETSI TS 103 248 -fn make_btp_b( - msg_type: c_its_parser::standards::extensions::ItsMessageId, -) -> c_its_parser::transport::TransportHeader { - let (destination_port, destination_port_info) = match msg_type { - c_its_parser::standards::extensions::ItsMessageId::Denm => (2002, 0), - c_its_parser::standards::extensions::ItsMessageId::Cam => (2001, 0), - c_its_parser::standards::extensions::ItsMessageId::Mapem => (2003, 0), - c_its_parser::standards::extensions::ItsMessageId::Spatem => (2004, 0), - c_its_parser::standards::extensions::ItsMessageId::Saem => (2005, 0), - c_its_parser::standards::extensions::ItsMessageId::Ivim => (2006, 0), - c_its_parser::standards::extensions::ItsMessageId::Srem => (2007, 0), - c_its_parser::standards::extensions::ItsMessageId::Ssem => (2008, 0), - c_its_parser::standards::extensions::ItsMessageId::Cpm => (2009, 0), - c_its_parser::standards::extensions::ItsMessageId::Poi => (2010, 0), - c_its_parser::standards::extensions::ItsMessageId::EvRsr => (2012, 0), - c_its_parser::standards::extensions::ItsMessageId::Rtcmem => (2013, 0), - _ => (0, 0), // no information found for the subsequent types - }; - - c_its_parser::transport::TransportHeader::BtpB(c_its_parser::transport::BasicTransportBHeader { - destination_port, - destination_port_info, - }) -} - -/// Creates a traffic class ID based on the ITS message ID (message type) -fn make_traffic_class(msg_type: c_its_parser::standards::extensions::ItsMessageId) -> u8 { - #[allow(clippy::match_same_arms)] - match msg_type { - c_its_parser::standards::extensions::ItsMessageId::Denm => 1, - c_its_parser::standards::extensions::ItsMessageId::Cam => 2, - c_its_parser::standards::extensions::ItsMessageId::Mapem => 3, // from C-Roads RS_RSP_063(1) - c_its_parser::standards::extensions::ItsMessageId::Spatem => 3, // from C-Roads RS_RSP_063(1) - c_its_parser::standards::extensions::ItsMessageId::Ivim => 3, // from C-Roads RS_RSP_063(1) - c_its_parser::standards::extensions::ItsMessageId::Srem => 2, // from C-Roads RS_RSP_063(1) - c_its_parser::standards::extensions::ItsMessageId::Ssem => 2, // from C-Roads RS_RSP_063(1) - c_its_parser::standards::extensions::ItsMessageId::Rtcmem => 3, // CSP_MaxPrio=252 -> 3 - _ => 3, // fall-back to lowest priority - } -} From 759e6e0d044bea280c420ac056816f4297d7e3a8 Mon Sep 17 00:00:00 2001 From: Jannik Beyerstedt Date: Mon, 22 Jun 2026 22:57:48 +0200 Subject: [PATCH 25/26] WIP: try to debug V2X rx dropouts --- src/main.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main.rs b/src/main.rs index 67e181c..d9d77f8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -531,6 +531,8 @@ fn handle_frame(frame: esp_radio::wifi::sniffer::PromiscuousPkt<'_>) { .naive_utc() }; + esp_println::print!("."); // TODO: debug only! + match esp32_cits_core::rx::is_broadcast_frame(&frame) { Err(err) => { warn!("{err}"); From 105d3123f4b63a61609a3bf42acf0f9d53bffd0f Mon Sep 17 00:00:00 2001 From: Jannik Beyerstedt Date: Mon, 3 Aug 2026 11:45:28 +0200 Subject: [PATCH 26/26] FIXUP: use user LED --- src/main.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index d9d77f8..154082a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -76,6 +76,7 @@ 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)); +static mut USER_LED: Option> = None; #[cfg(feature = "_uart")] static UART_RX_BUF: Mutex>>> = Mutex::new(RefCell::new(None)); #[cfg(feature = "_uart")] @@ -176,6 +177,13 @@ async fn main(spawner: Spawner) -> ! { error!("Failed to set WiFi TX power: {err}"); } + unsafe { + USER_LED = Some(esp_hal::gpio::Output::new( + peripherals.GPIO27, + esp_hal::gpio::Level::Low, + esp_hal::gpio::OutputConfig::default(), + )); + } critical_section::with(|cs| { WIFI_RX_QUEUE.borrow(cs).replace(Some(ArrayQueue::new(2))); }); @@ -531,8 +539,6 @@ fn handle_frame(frame: esp_radio::wifi::sniffer::PromiscuousPkt<'_>) { .naive_utc() }; - esp_println::print!("."); // TODO: debug only! - match esp32_cits_core::rx::is_broadcast_frame(&frame) { Err(err) => { warn!("{err}"); @@ -548,6 +554,13 @@ fn handle_frame(frame: esp_radio::wifi::sniffer::PromiscuousPkt<'_>) { } // info!("Received frame with {} bytes", frame.len); + esp_println::print!("."); // TODO: debug only! + unsafe { + #[allow(static_mut_refs)] + if let Some(ref mut led) = USER_LED.as_mut() { + led.toggle(); + } + } // parse GN headers match c_its_parser::pcap::remove_wlan_headers(frame.data)