diff --git a/Readme.md b/Readme.md index 968afd2..ccbedb0 100644 --- a/Readme.md +++ b/Readme.md @@ -98,8 +98,7 @@ Additional features are available through feature flags: Officially supported feature combinations are: - `esp,spat,gnss_ubx,screen` (default features, "standalone") -- `esp,spat,gnss_ubx,screen,ble` ("standalone with BLE") -- `esp,spat,gnss_ubx,screen,cam_tx,ble` ("standalone with CAM") +- `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 with UART") - `esp,ble,cam,denm,cam_tx` ("smartphone companion with BLE") @@ -133,6 +132,7 @@ Additional hardware: The housing is a FreeCAD model at [hardware/housing/c_its-companion-housing.FCStd](./hardware/housing/c_its-companion-housing.FCStd), but the same folder also contains finished 3mf models that can be loaded into a 3D printer slicer. Exports are available for: +- 2mm: no battery (just display standoffs) - 7mm: EREMIT 654060 (6.5x40x60 mm) LiPo battery - 9mm: Some 9x40x60 mm LiPo battery, e.g. LP803860 (2000 mAh) diff --git a/build.rs b/build.rs index 00deee1..f376e0e 100644 --- a/build.rs +++ b/build.rs @@ -9,7 +9,6 @@ fn main() -> std::io::Result<()> { println!("cargo:rustc-link-arg=-Tlinkall.x"); } - // compile protobuf definition to rust code let descriptor_path = std::path::PathBuf::from(env::var("OUT_DIR").unwrap()).join("proto_descriptor.bin"); @@ -18,27 +17,6 @@ fn main() -> std::io::Result<()> { .compile_well_known_types() .compile_protos(&["docs/c_its-messages.proto"], &["docs/"])?; - // get git revision - println!("cargo:rustc-rerun-if-changed=.git/HEAD"); - - // length: - // - tag name: 6-8 chars (v0.1.0 to v1.23.42) - // - offset+hash: 0-12 chars (up to 99 commits from tag + 8 char hash) - // - SUM: 6-20 - let git_output = std::process::Command::new("git") - .args(["describe", "--tags", "--match", "v*", "--always"]) - .output() - .unwrap(); - assert!( - git_output.stderr.is_empty(), - "{}", - String::from_utf8(git_output.stderr).unwrap() - ); - println!( - "cargo:rustc-env=GIT_REVISION={}", - String::from_utf8(git_output.stdout).unwrap() - ); - Ok(()) } diff --git a/feat-permut.sh b/feat-permut.sh index 9cebe55..c8d5646 100755 --- a/feat-permut.sh +++ b/feat-permut.sh @@ -7,16 +7,15 @@ extended=${2:-false} features_base=( - spat,gnss_ubx,screen # "standalone" simple - spat,gnss_ubx,screen,ble # "standalone" with BLE - spat,gnss_ubx,screen,ble,cam_tx # "standalone" with CAM - spat,gnss_ubx,screen,cam_tx,cam,denm - uart,cam,denm # "smartphone companion with UART" - ble,cam,denm,cam_tx # "smartphone companion with BLE" + uart,cam,denm + ble,cam,denm,cam_tx + uart,ble,cam,denm + spat,gnss_ubx,screen + spat,gnss_ubx,screen,cam_tx ) features_more=( - uart,ble,cam,denm - spat,gnss_ubx,screen,cam_tx + spat,gnss_ubx,screen,ble + spat,gnss_ubx,screen,ble,cam_tx spat,gnss_ubx,screen,cam,denm spat,gnss_ubx,screen,cam_tx,cam,denm gnss_ubx,screen,cam_tx,cam,denm diff --git a/hardware/housing/c_its-companion-housing-bottom-2mm.3mf b/hardware/housing/c_its-companion-housing-bottom-2mm.3mf new file mode 100644 index 0000000..2244f69 Binary files /dev/null and b/hardware/housing/c_its-companion-housing-bottom-2mm.3mf differ diff --git a/hardware/housing/c_its-companion-housing-bottom-7mm.3mf b/hardware/housing/c_its-companion-housing-bottom-7mm.3mf new file mode 100644 index 0000000..aa4c7f9 Binary files /dev/null and b/hardware/housing/c_its-companion-housing-bottom-7mm.3mf differ diff --git a/hardware/housing/c_its-companion-housing-bottom-9mm.3mf b/hardware/housing/c_its-companion-housing-bottom-9mm.3mf new file mode 100644 index 0000000..2ba2eee Binary files /dev/null and b/hardware/housing/c_its-companion-housing-bottom-9mm.3mf differ diff --git a/hardware/housing/c_its-companion-housing-bottom-v1.1-7mm.3mf b/hardware/housing/c_its-companion-housing-bottom-v1.1-7mm.3mf deleted file mode 100644 index 53686ab..0000000 Binary files a/hardware/housing/c_its-companion-housing-bottom-v1.1-7mm.3mf and /dev/null differ diff --git a/hardware/housing/c_its-companion-housing-bottom-v1.1-9mm.3mf b/hardware/housing/c_its-companion-housing-bottom-v1.1-9mm.3mf deleted file mode 100644 index cc7504d..0000000 Binary files a/hardware/housing/c_its-companion-housing-bottom-v1.1-9mm.3mf and /dev/null differ diff --git a/hardware/housing/c_its-companion-housing-lid-v1.1.3mf b/hardware/housing/c_its-companion-housing-lid-v1.1.3mf deleted file mode 100644 index 25b8bc6..0000000 Binary files a/hardware/housing/c_its-companion-housing-lid-v1.1.3mf and /dev/null differ diff --git a/hardware/housing/c_its-companion-housing-lid.3mf b/hardware/housing/c_its-companion-housing-lid.3mf new file mode 100644 index 0000000..2ec16e3 Binary files /dev/null and b/hardware/housing/c_its-companion-housing-lid.3mf differ diff --git a/hardware/housing/c_its-companion-housing.FCStd b/hardware/housing/c_its-companion-housing.FCStd index 9ea4657..64edf0c 100644 Binary files a/hardware/housing/c_its-companion-housing.FCStd and b/hardware/housing/c_its-companion-housing.FCStd differ diff --git a/run-standalone-ble.sh b/run-standalone-ble.sh deleted file mode 100755 index 6957199..0000000 --- a/run-standalone-ble.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh - -## Builds common config for standalone device -## -> enable SPAT/ GLOSA with GNSS and a screen + BLE - -cargo run --release -F ble diff --git a/run-standalone-cam.sh b/run-standalone-cam.sh index 0bc50c2..9bfafbe 100755 --- a/run-standalone-cam.sh +++ b/run-standalone-cam.sh @@ -3,4 +3,4 @@ ## 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,ble +cargo run --release -F cam_tx diff --git a/run-standalone-simple.sh b/run-standalone-no_cam.sh similarity index 100% rename from run-standalone-simple.sh rename to run-standalone-no_cam.sh diff --git a/src/ble.rs b/src/ble.rs index 8ff24c4..a460d4e 100644 --- a/src/ble.rs +++ b/src/ble.rs @@ -39,73 +39,9 @@ pub enum BleSendable { SpatemRaw(io::msg::RawMsgRx), } -pub const DIS_SERIAL_MAX_LEN: usize = 17; -pub const DIS_FW_REVISION_MAX_LEN: usize = 20; // git-describe -pub const DIS_SW_REVISION_MAX_LEN: usize = 66; // max length of get_feature_list, joined with comma - -pub struct DeviceInfo { - /// WiFi MAC - pub serial_number: [u8; DIS_SERIAL_MAX_LEN], - /// git tag/ revision - pub fw_revision: [u8; DIS_FW_REVISION_MAX_LEN], - /// used for feature flags - pub sw_revision: [u8; DIS_SW_REVISION_MAX_LEN], -} - -impl DeviceInfo { - pub fn new(serial_number: &str, fw_revision: &str, sw_revision: &str) -> Self { - Self { - serial_number: string_as_padded_buf(serial_number, DIS_SERIAL_MAX_LEN) - .try_into() - .unwrap(), - fw_revision: string_as_padded_buf(fw_revision, DIS_FW_REVISION_MAX_LEN) - .try_into() - .unwrap(), - sw_revision: string_as_padded_buf(sw_revision, DIS_SW_REVISION_MAX_LEN) - .try_into() - .unwrap(), - } - } -} - -pub fn string_as_padded_buf(value: &str, length: usize) -> alloc::vec::Vec { - let buf = value.as_bytes(); - let buf_len = buf.len(); - - // cut buffer at maximum length or pad with whitespace - if buf_len > length { - buf[..length].to_vec() - } else { - let padding_len = length - buf_len; - let padding = alloc::vec![b' '; padding_len]; - - [buf, &padding].concat() - } -} - #[gatt_server] struct Server { c_its_events: CITSEvents, - device_info_service: DeviceInfoService, -} - -#[gatt_service(uuid = service::DEVICE_INFORMATION)] -struct DeviceInfoService { - /// Manufacturer name - #[characteristic(uuid = characteristic::MANUFACTURER_NAME_STRING, read, value = "jtbx")] - mfg_name: &'static str, - /// Model number - #[characteristic(uuid = characteristic::MODEL_NUMBER_STRING, read, value = "ESP32-C5 C-ITS Companion")] - model_number: &'static str, - // Serial Number String - #[characteristic(uuid = characteristic::SERIAL_NUMBER_STRING, read, value = [0u8; DIS_SERIAL_MAX_LEN])] - serial_number: [u8; DIS_SERIAL_MAX_LEN], - // Firmware Revision String - #[characteristic(uuid = characteristic::FIRMWARE_REVISION_STRING, read, value = [0u8; DIS_FW_REVISION_MAX_LEN])] - fw_revision: [u8; DIS_FW_REVISION_MAX_LEN], - // Software Revision String - #[characteristic(uuid = characteristic::SOFTWARE_REVISION_STRING, read, value = [0u8; DIS_SW_REVISION_MAX_LEN])] - sw_revision: [u8; DIS_SW_REVISION_MAX_LEN], } #[gatt_service(uuid = "c0b70000-d4f4-4000-ada8-f99a02ee315c")] @@ -174,8 +110,6 @@ pub async fn run( CONTROLLER_SLOTS, >, name: alloc::string::String, - short_name: alloc::string::String, - device_info: DeviceInfo, ) { let mut resources: HostResources = HostResources::new(); @@ -186,7 +120,6 @@ pub async fn run( .. } = stack.build(); - info!("BLE name: {name} (short: {short_name})"); info!( "Starting advertising and GATT service for {}", crate::util::get_rx_feature_list().join(",") @@ -197,24 +130,9 @@ pub async fn run( })) .unwrap(); - // set "dynamic" device info data - let _ = server - .device_info_service - .serial_number - .set(&server, &device_info.serial_number); - let _ = server - .device_info_service - .fw_revision - .set(&server, &device_info.fw_revision); - let _ = server - .device_info_service - .sw_revision - .set(&server, &device_info.sw_revision); - - // advertise and run update logic let _ = embassy_futures::join::join(ble_task(runner), async { loop { - match advertise(&mut peripheral, &server, &short_name).await { + match advertise(&mut peripheral, &server).await { Ok(conn) => { info!("BLE: New client connected"); @@ -258,19 +176,16 @@ pub fn update(payload: BleSendable) { async fn advertise<'values, 'server, C: Controller>( peripheral: &mut Peripheral<'values, C, DefaultPacketPool>, server: &'server Server<'values>, - // name in advertisement, max. 8 characters - short_name: &'values str, ) -> Result, BleHostError> { - let mut advertiser_data = [0; 31]; // 37 bytes payload - 6 byte address + let mut advertiser_data = [0; 35]; let service_uuid = Uuid::from(0xc0b7_0000_d4f4_4000_ada8_f99a_02ee_315c_u128) .as_raw() .try_into() .unwrap(); let len = AdStructure::encode_slice( &[ - AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED), // 2+ 1 byte - AdStructure::ServiceUuids128(&[service_uuid]), // 2+16 byte = 21 - AdStructure::ShortenedLocalName(short_name.as_bytes()), // 2+ 8 byte = 31 + AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED), + AdStructure::ServiceUuids128(&[service_uuid]), ], &mut advertiser_data[..], )?; @@ -278,11 +193,7 @@ async fn advertise<'values, 'server, C: Controller>( let advertiser = peripheral .advertise( &trouble_host::advertise::AdvertisementParameters { - primary_phy: PhyKind::Le2M, - secondary_phy: PhyKind::Le2M, - timeout: Some(embassy_time::Duration::from_secs(3)), - interval_min: embassy_time::Duration::from_millis(150), - interval_max: embassy_time::Duration::from_secs(1), + timeout: Some(embassy_time::Duration::from_secs(2)), ..Default::default() }, Advertisement::ConnectableScannableUndirected { diff --git a/src/main.rs b/src/main.rs index d5956b7..a05b447 100644 --- a/src/main.rs +++ b/src/main.rs @@ -177,6 +177,116 @@ async fn main(spawner: Spawner) -> ! { screen }; + { + let _ = screen + .update(screen::ScreenState::Initial) + .inspect_err(log_screen_error); + embassy_time::Timer::after(embassy_time::Duration::from_secs(2)).await; + + #[cfg(feature = "spat")] + { + let _ = screen + .update(screen::ScreenState::GlosaSearching( + applogic::GlosaSearching { + known_intersection_ids: alloc::vec![123], + }, + )) + .inspect_err(log_screen_error); + embassy_time::Timer::after(embassy_time::Duration::from_secs(2)).await; + let _ = screen + .update(screen::ScreenState::GlosaFound(applogic::GlosaFound { + intersection_id: 1234, + approach_id: Some(42), + })) + .inspect_err(log_screen_error); + embassy_time::Timer::after(embassy_time::Duration::from_secs(2)).await; + let _ = screen + .update(screen::ScreenState::GlosaLocked( + applogic::GlosaSignalInfo { + intersection_id: 1234, + signal_groups: alloc::vec![ + applogic::SignalGroupData { + phase: applogic::SignalPhase::Red, + maneuver: applogic::v2x::map::Maneuvers { + left_allowed: true, + staight_allowed: false, + right_allowed: false + }, + timing: Some(applogic::TimingData { + min_end_sec: 23, + likely_end_sec: None, + max_end_sec: None + }), + usages: alloc::vec![applogic::v2x::map::UsageType::Vehicle] + }, + applogic::SignalGroupData { + phase: applogic::SignalPhase::Red, + maneuver: applogic::v2x::map::Maneuvers { + left_allowed: true, + staight_allowed: false, + right_allowed: false + }, + timing: Some(applogic::TimingData { + min_end_sec: 23, + likely_end_sec: None, + max_end_sec: None + }), + usages: alloc::vec![applogic::v2x::map::UsageType::Vehicle] + } + ], + }, + )) + .inspect_err(log_screen_error); + embassy_time::Timer::after(embassy_time::Duration::from_secs(2)).await; + + let _ = screen + .update(screen::ScreenState::GlosaLocked( + applogic::GlosaSignalInfo { + intersection_id: 1234, + signal_groups: alloc::vec![applogic::SignalGroupData { + phase: applogic::SignalPhase::Red, + maneuver: applogic::v2x::map::Maneuvers { + left_allowed: true, + staight_allowed: false, + right_allowed: false + }, + timing: Some(applogic::TimingData { + min_end_sec: 23, + likely_end_sec: Some(24), + max_end_sec: Some(42) + }), + usages: alloc::vec![ + applogic::v2x::map::UsageType::Vehicle, + applogic::v2x::map::UsageType::Bike, + ] + }], + }, + )) + .inspect_err(log_screen_error); + embassy_time::Timer::after(embassy_time::Duration::from_secs(2)).await; + } + + #[cfg(feature = "denm")] + { + use c_its_parser::standards::cdd_2_2_1::etsi_its_cdd::EmergencyVehicleApproachingSubCauseCode; + let _ = screen + .update(screen::ScreenState::DenmWarning( + // c_its_parser::standards::cdd_2_2_1::etsi_its_cdd::CauseCodeChoice::emergencyVehicleApproaching95(0.into()), + c_its_parser::standards::cdd_2_2_1::etsi_its_cdd::etsi_its_cdd::CauseCodeChoice::emergencyVehicleApproaching95(EmergencyVehicleApproachingSubCauseCode(0)), + )) + .inspect_err(log_screen_error); + embassy_time::Timer::after(embassy_time::Duration::from_secs(2)).await; + + // try to reset screen for 4 more seconds + for _ in 0..4 { + let _ = screen + .update(screen::ScreenState::Initial) + .inspect_err(log_screen_error); + embassy_time::Timer::after(embassy_time::Duration::from_secs(1)).await; + } + } + } + // Setup UART I/O channel #[cfg(feature = "uart")] setup_uart0( @@ -222,24 +332,12 @@ async fn main(spawner: Spawner) -> ! { let controller = ble::make_controller(peripherals.BT); let serial = &mac.as_bytes()[3..6]; let name = alloc::format!( - "C-ITS ESP {:02x}:{:02x}:{:02x}", + "ESP32 C-ITS {:02x}{:02x}{:02x}", serial[0], serial[1], serial[2] ); - let short_name = alloc::format!("V2X {:02x}{:02x}", serial[1], serial[2]); // max 8 chars! - - let fw_revision = core::env!("GIT_REVISION"); - let sw_revision = util::get_feature_list().join(","); - spawner.spawn( - ble::run( - controller, - name, - short_name, - ble::DeviceInfo::new(&mac.to_string(), fw_revision, &sw_revision), - ) - .expect("Failed to spawn BLE task"), - ); + spawner.spawn(ble::run(controller, name).expect("Failed to spawn BLE task")); } // Setup GNSS: diff --git a/src/util.rs b/src/util.rs index e92a760..4b0dd89 100644 --- a/src/util.rs +++ b/src/util.rs @@ -31,41 +31,3 @@ pub(crate) fn get_tx_feature_list() -> Vec<&'static str> { features } - -#[allow(unused)] -pub(crate) fn get_feature_list() -> Vec<&'static str> { - let mut features = alloc::vec![]; - - if cfg!(feature = "gnss_ubx") { - features.push("gnss_ubx"); - } - if cfg!(feature = "gnss_l67k") { - features.push("gnss_l67k"); - } - if cfg!(feature = "cam_tx") { - features.push("cam_tx"); - } - if cfg!(feature = "cam") { - features.push("cam"); - } - if cfg!(feature = "denm") { - features.push("denm"); - } - if cfg!(feature = "spat") { - features.push("spat"); - } - if cfg!(feature = "spat_debug") { - features.push("spat_debug"); - } - if cfg!(feature = "screen") { - features.push("screen"); - } - if cfg!(feature = "uart") { - features.push("uart"); - } - if cfg!(feature = "ble") { - features.push("ble"); - } - - features -}