Watch
0
0
Fork
You've already forked esp32-c_its-companion
0

Implement UART I/O protocol

This commit is contained in:
Jannik Beyerstedt 2026-07-20 11:58:44 +02:00
commit 4d7bef58a1
11 changed files with 610 additions and 36 deletions

View file

@ -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: <https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/app_image_format.html#application-description>
esp_bootloader_esp_idf::esp_app_desc!();
#[cfg(feature = "gnss")]
#[cfg(feature = "_gnss")]
static GNSS_UPDATE: Mutex<RefCell<Option<applogic::GnssFix>>> = Mutex::new(RefCell::new(None));
static WIFI_RX_QUEUE: Mutex<RefCell<Option<ArrayQueue<Vec<u8>>>>> = 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);
}
}
}
}