diff --git a/src/applogic/mod.rs b/src/applogic/mod.rs index 55672a7..0ced524 100644 --- a/src/applogic/mod.rs +++ b/src/applogic/mod.rs @@ -118,6 +118,7 @@ pub struct TimingData { pub max_end_sec: Option, } +#[cfg(feature = "spat")] impl TimingData { pub fn new(value: &map::SignalGroup, now: chrono::DateTime) -> Option { let min = value diff --git a/src/ble.rs b/src/ble.rs index 8fc86e5..e58ffb0 100644 --- a/src/ble.rs +++ b/src/ble.rs @@ -6,6 +6,7 @@ use core::cell::RefCell; use critical_section::Mutex; +use crossbeam_queue::ArrayQueue; use esp_backtrace as _; use log::{info, warn}; use trouble_host::prelude::*; @@ -16,7 +17,9 @@ pub const CONTROLLER_SLOTS: usize = 20; const CONNECTIONS_MAX: usize = 1; const L2CAP_CHANNELS_MAX: usize = 1; -pub static TX_DATA: Mutex>> = Mutex::new(RefCell::new(None)); // TODO: this needs to be an array! +pub static CLIENT_CONNECTED: Mutex> = Mutex::new(RefCell::new(false)); +pub static TX_DATA: Mutex>>> = + Mutex::new(RefCell::new(None)); pub static RX_DATA: Mutex>> = Mutex::new(RefCell::new(None)); pub enum BleSendable { @@ -82,6 +85,10 @@ pub fn make_controller( esp_radio::ble::controller::BleConnector<'static>, CONTROLLER_SLOTS, > { + critical_section::with(|cs| { + TX_DATA.borrow(cs).replace(Some(ArrayQueue::new(2))); + }); + let transport = esp_radio::ble::controller::BleConnector::new( bt_peripheral, esp_radio::ble::Config::default(), @@ -120,10 +127,15 @@ pub async fn run( Ok(conn) => { // set up tasks when the connection is established to a central, so they don't run when no one is connected. let a = gatt_events_task(&server, &conn); - let b = custom_task(&server, &conn); - // run until any task ends (usually because the connection has been closed), - // then return to advertising state. + let b = publishing_task(&server, &conn); + + // run until any task ends (usually because the connection has been closed), then return to advertising state. embassy_futures::select::select(a, b).await; + + critical_section::with(|cs| { + let value_ref = CLIENT_CONNECTED.borrow(cs); + let _ = value_ref.replace(false); + }); } Err(e) => { panic!("BLE ADV: fatal error {e:?}"); @@ -134,6 +146,21 @@ pub async fn run( .await; } +pub fn update(payload: BleSendable) { + critical_section::with(|cs| { + // only add to queue when someone is connected + if *CLIENT_CONNECTED.borrow(cs).borrow() { + let queue_rc = TX_DATA.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).is_err() { + log::error!("BLE TX queue is full"); + } + } + }); +} + /// Create an advertiser to use to connect to a BLE Central, and wait for it to connect. async fn advertise<'values, 'server, C: Controller>( name: &'values str, @@ -223,20 +250,23 @@ async fn gatt_events_task( Ok(()) } -/// Example task to use the BLE notifier interface. -/// This task will notify the connected central of a counter value every 2 seconds. -/// It will also read the RSSI value every 2 seconds. -/// and will stop when the connection is closed by the central or an error occurs. -async fn custom_task(server: &Server<'_>, conn: &GattConnection<'_, '_, P>) { +async fn publishing_task(server: &Server<'_>, conn: &GattConnection<'_, '_, P>) { + critical_section::with(|cs| { + let value_ref = CLIENT_CONNECTED.borrow(cs); + let _ = value_ref.replace(true); + }); + loop { embassy_time::Timer::after(embassy_time::Duration::from_millis(10)).await; let mut new_data = None; critical_section::with(|cs| { - let tx_data_ref = TX_DATA.borrow(cs); + let queue_rc = TX_DATA.borrow(cs).borrow(); + // unwrap is fine b/c we stored something in it before + let queue = queue_rc.as_ref().unwrap(); - if let Some(tx_data) = tx_data_ref.replace(None) { - new_data = Some(tx_data); + if let Some(data) = queue.pop() { + new_data = Some(data); } }); diff --git a/src/io.rs b/src/io.rs index aeeab92..1241953 100644 --- a/src/io.rs +++ b/src/io.rs @@ -454,11 +454,11 @@ impl msg::RawMsgTx { } #[cfg(feature = "cam")] -impl From> +impl From<&alloc::boxed::Box> for msg::CamEvent { fn from( - value: alloc::boxed::Box, + value: &alloc::boxed::Box, ) -> Self { use c_its_parser::standards::cam_1_4_1::cam_pdu_descriptions; @@ -469,12 +469,14 @@ impl From None, }); - let vehicle_data = match value.cam.cam_parameters.high_frequency_container { + let vehicle_data = match &value.cam.cam_parameters.high_frequency_container { cam_pdu_descriptions::HighFrequencyContainer::basicVehicleContainerHighFrequency( basic_vehicle_container_high_frequency, ) => Some(basic_vehicle_container_high_frequency.into()), @@ -500,8 +502,8 @@ impl From for msg::VehicleContainer { - fn from(value: c_its_parser::standards::cam_1_4_1::cam_pdu_descriptions::BasicVehicleContainerHighFrequency) -> Self { +impl From<&c_its_parser::standards::cam_1_4_1::cam_pdu_descriptions::BasicVehicleContainerHighFrequency> for msg::VehicleContainer { + fn from(value: &c_its_parser::standards::cam_1_4_1::cam_pdu_descriptions::BasicVehicleContainerHighFrequency) -> Self { let heading_deg = if value.heading.heading_value.is_unavailable() { None } else { @@ -529,11 +531,11 @@ impl From> +impl From<&alloc::boxed::Box> for msg::DenmEvent { fn from( - value: alloc::boxed::Box, + value: &alloc::boxed::Box, ) -> Self { let denm_mgmt = &value.denm.management; @@ -552,7 +554,8 @@ impl From ! { transport: _, ref etsi, } => { - state.handle_mapem(&etsi); + state.handle_mapem(etsi); // Send data to BLE thread #[cfg(feature = "ble")] match io::msg::RawMsgRx::try_from(&msg) { Ok(data) => { - critical_section::with(|cs| { - let ble_update_ref = ble::TX_DATA.borrow(cs); - ble_update_ref - .replace(Some(ble::BleSendable::MapemRaw(data))); - }); + ble::update(ble::BleSendable::MapemRaw(data)); } Err(err) => warn!("Failed to create RawMsgRx: {err}"), } @@ -530,18 +526,14 @@ async fn main(spawner: Spawner) -> ! { ref etsi, } => { if state.initialized() { - state.handle_spatem(&etsi); + state.handle_spatem(etsi); } // Send data to BLE thread #[cfg(feature = "ble")] match io::msg::RawMsgRx::try_from(&msg) { Ok(data) => { - critical_section::with(|cs| { - let ble_update_ref = ble::TX_DATA.borrow(cs); - ble_update_ref - .replace(Some(ble::BleSendable::SpatemRaw(data))); - }); + ble::update(ble::BleSendable::SpatemRaw(data)); } Err(err) => warn!("Failed to create RawMsgRx: {err}"), } @@ -551,37 +543,48 @@ async fn main(spawner: Spawner) -> ! { ItsMessage::Cam { geonetworking: _, transport: _, - etsi, + ref etsi, } => { #[cfg(feature = "cam")] - applogic::cam::handle_cam(&etsi); + applogic::cam::handle_cam(etsi); // Send data to BLE thread #[cfg(all(feature = "ble", feature = "cam"))] - critical_section::with(|cs| { - let ble_update_ref = ble::TX_DATA.borrow(cs); - ble_update_ref.replace(Some(ble::BleSendable::Cam(etsi.into()))); - }); + { + ble::update(ble::BleSendable::Cam(etsi.into())); - // TODO: Send Raw message, but only when it has high-freq container + // raw data only when it's a full CAM + if etsi.cam.cam_parameters.low_frequency_container.is_some() { + match io::msg::RawMsgRx::try_from(&msg) { + Ok(data) => { + ble::update(ble::BleSendable::CamRaw(data)); + } + Err(err) => warn!("Failed to create RawMsgRx: {err}"), + } + } + } } #[cfg(feature = "denm")] ItsMessage::DenmV2 { geonetworking: _, transport: _, - etsi, + ref etsi, } => { - applogic::denm::handle_denm(&etsi); + applogic::denm::handle_denm(etsi); // Send data to BLE thread #[cfg(feature = "ble")] - critical_section::with(|cs| { - let ble_update_ref = ble::TX_DATA.borrow(cs); - ble_update_ref.replace(Some(ble::BleSendable::Denm(etsi.into()))); - }); + { + ble::update(ble::BleSendable::Denm(etsi.into())); - // TODO: Send Raw message + match io::msg::RawMsgRx::try_from(&msg) { + Ok(data) => { + ble::update(ble::BleSendable::DenmRaw(data)); + } + Err(err) => warn!("Failed to create RawMsgRx: {err}"), + } + } } #[cfg(feature = "denm")] ItsMessage::DenmV1 {