diff --git a/Readme.md b/Readme.md
index 2be427e..97d547d 100644
--- a/Readme.md
+++ b/Readme.md
@@ -132,7 +132,6 @@ 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)
@@ -146,10 +145,7 @@ Images are loaded as TGA assets, but shall not have an alpha channel.
You can convert them using [ImageMagick](https://imagemagick.org) like this:
```shell
# resize to 25px height, adding a black background to the white symbol and vice versa
-magick Sinnbild_Radfahrer_StVO_1992-200px-white.png -resize x25 -background black -alpha background -flatten -alpha off -colorspace Gray -verbose Sinnbild_Radfahrer_StVO_1992-25px-white.tga
-magick Sinnbild_Radfahrer_StVO_1992-200px-black.png -resize x25 -background white -alpha background -flatten -alpha off -colorspace Gray -verbose Sinnbild_Radfahrer_StVO_1992-25px-black.tga
-
-magick Stationary_Emergency_Vehicle-140x105.svg -resize 70 -background black -alpha background -flatten -alpha off -colorspace Gray -verbose Stationary_Emergency_Vehicle-70px.tga
-magick Stationary_Vehicle-140x70.svg -resize 80 -background black -alpha background -flatten -alpha off -colorspace Gray -verbose Stationary_Vehicle-80px.tga
-magick Work_Zone-140x95.svg -resize 80 -background black -alpha background -flatten -alpha off -colorspace Gray -verbose Work_Zone-80px.tga
+magick Sinnbild_Radfahrer_StVO_1992-200px-white.png -resize x25 -background black -alpha background -flatten -alpha off -verbose Sinnbild_Radfahrer_StVO_1992-25px-white.tga
+magick Sinnbild_Radfahrer_StVO_1992-200px-black.png -resize x25 -background white -alpha background -flatten -alpha off -verbose Sinnbild_Radfahrer_StVO_1992-25px-black.tga
```
+
diff --git a/graphics/Stationary_Emergency_Vehicle-140x105.svg b/graphics/Stationary_Emergency_Vehicle-140x105.svg
deleted file mode 100644
index f5b5aa0..0000000
--- a/graphics/Stationary_Emergency_Vehicle-140x105.svg
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-
diff --git a/graphics/Stationary_Emergency_Vehicle-70px.tga b/graphics/Stationary_Emergency_Vehicle-70px.tga
deleted file mode 100644
index 3076adb..0000000
Binary files a/graphics/Stationary_Emergency_Vehicle-70px.tga and /dev/null differ
diff --git a/graphics/Stationary_Vehicle-140x70.svg b/graphics/Stationary_Vehicle-140x70.svg
deleted file mode 100644
index 3ff1cd7..0000000
--- a/graphics/Stationary_Vehicle-140x70.svg
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
diff --git a/graphics/Stationary_Vehicle-80px.tga b/graphics/Stationary_Vehicle-80px.tga
deleted file mode 100644
index 475ef75..0000000
Binary files a/graphics/Stationary_Vehicle-80px.tga and /dev/null differ
diff --git a/graphics/Work_Zone-140x95.svg b/graphics/Work_Zone-140x95.svg
deleted file mode 100644
index bbbe6fb..0000000
--- a/graphics/Work_Zone-140x95.svg
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
diff --git a/graphics/Work_Zone-80px.tga b/graphics/Work_Zone-80px.tga
deleted file mode 100644
index a397e21..0000000
Binary files a/graphics/Work_Zone-80px.tga and /dev/null differ
diff --git a/hardware/housing/c_its-companion-housing-bottom-2mm.3mf b/hardware/housing/c_its-companion-housing-bottom-2mm.3mf
deleted file mode 100644
index 2244f69..0000000
Binary files a/hardware/housing/c_its-companion-housing-bottom-2mm.3mf and /dev/null differ
diff --git a/hardware/housing/c_its-companion-housing-bottom-7mm.3mf b/hardware/housing/c_its-companion-housing-bottom-7mm.3mf
deleted file mode 100644
index aa4c7f9..0000000
Binary files a/hardware/housing/c_its-companion-housing-bottom-7mm.3mf and /dev/null differ
diff --git a/hardware/housing/c_its-companion-housing-bottom-9mm.3mf b/hardware/housing/c_its-companion-housing-bottom-9mm.3mf
deleted file mode 100644
index 2ba2eee..0000000
Binary files a/hardware/housing/c_its-companion-housing-bottom-9mm.3mf and /dev/null 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
new file mode 100644
index 0000000..53686ab
Binary files /dev/null and b/hardware/housing/c_its-companion-housing-bottom-v1.1-7mm.3mf 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
new file mode 100644
index 0000000..cc7504d
Binary files /dev/null and b/hardware/housing/c_its-companion-housing-bottom-v1.1-9mm.3mf 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
new file mode 100644
index 0000000..25b8bc6
Binary files /dev/null and b/hardware/housing/c_its-companion-housing-lid-v1.1.3mf differ
diff --git a/hardware/housing/c_its-companion-housing-lid.3mf b/hardware/housing/c_its-companion-housing-lid.3mf
deleted file mode 100644
index 2ec16e3..0000000
Binary files a/hardware/housing/c_its-companion-housing-lid.3mf and /dev/null differ
diff --git a/hardware/housing/c_its-companion-housing.FCStd b/hardware/housing/c_its-companion-housing.FCStd
index 64edf0c..9ea4657 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-denm.sh b/run-standalone-denm.sh
deleted file mode 100755
index 70a0905..0000000
--- a/run-standalone-denm.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/sh
-
-## Builds common config for standalone device
-## -> enable DENM with GNSS and a screen (no GLOSA)
-
-cargo run --release --no-default-features -F esp,screen,gnss_ubx,denm,ble
diff --git a/run-standalone-glosa+denm.sh b/run-standalone-glosa+denm.sh
deleted file mode 100755
index fa0f975..0000000
--- a/run-standalone-glosa+denm.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/sh
-
-## Builds common config for standalone device
-## -> enable SPAT/ GLOSA and DENM icons with GNSS and a screen and BLE
-
-cargo run --release -F denm,ble
diff --git a/src/applogic/denm.rs b/src/applogic/denm.rs
index d4a5516..9d46dbb 100644
--- a/src/applogic/denm.rs
+++ b/src/applogic/denm.rs
@@ -1,14 +1,13 @@
//! C-ITS DENM Evaluation
-use c_its_parser::standards::cdd_2_2_1::etsi_its_cdd;
use c_its_parser::standards::denm_2_2_1::denm_pdu_description;
use log::info;
-pub fn handle_denm(etsi: &denm_pdu_description::DENM) -> Option {
+pub fn handle_denm(etsi: &denm_pdu_description::DENM) {
let mgmt = &etsi.denm.management;
if mgmt.termination.is_some() {
- return None;
+ return;
}
if let (Some(sit), Some(loc)) = (&etsi.denm.situation, &etsi.denm.location) {
@@ -39,11 +38,7 @@ pub fn handle_denm(etsi: &denm_pdu_description::DENM) -> Option {
+ // run advertisement and set data until a client connects
+ let a = advertise(&mut peripheral, &server, &short_name);
+ let b = data_update_task(&server);
+ match embassy_futures::select::select(a, b).await {
+ embassy_futures::select::Either::First(Ok(conn)) => {
info!("BLE: New client connected");
// set up tasks when the connection is established to a central, so they don't run when no one is connected.
@@ -162,15 +165,17 @@ pub async fn run(
let _ = value_ref.replace(false);
});
}
- Err(e) => {
+ embassy_futures::select::Either::First(Err(e)) => {
panic!("BLE ADV: fatal error: {e:?}");
}
+ _ => {}
}
}
})
.await;
}
+/// Queues data for BLE notification (only when client is connected)
pub fn update(payload: BleSendable) {
critical_section::with(|cs| {
// only add to queue when someone is connected
@@ -186,14 +191,32 @@ pub fn update(payload: BleSendable) {
});
}
+/// Queues data for characteristic update (may notify a connected client)
+///
+/// This can be used instead of `update()`, but will always write the data to the characteristic even when no client is connected.
+/// So a newly connected client will be able to read an up to date value at connection and not just when new data was received.
+///
+/// This is useful for (device) information which is not time-critical (like received V2X data).
+pub fn set(payload: BleSendable) {
+ critical_section::with(|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 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>(
peripheral: &mut Peripheral<'values, C, DefaultPacketPool>,
server: &'server Server<'values>,
- // name in advertisement, max. 12 characters
+ // name in advertisement, max. 8 characters
short_name: &'values str,
) -> Result, BleHostError> {
- let mut advertiser_data = [0; 35];
+ let mut advertiser_data = [0; 31]; // 37 bytes payload - 6 byte address
let service_uuid = Uuid::from(0xc0b7_0000_d4f4_4000_ada8_f99a_02ee_315c_u128)
.as_raw()
.try_into()
@@ -202,7 +225,7 @@ async fn advertise<'values, 'server, C: Controller>(
&[
AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED), // 2+ 1 byte
AdStructure::ServiceUuids128(&[service_uuid]), // 2+16 byte = 21
- AdStructure::CompleteLocalName(short_name.as_bytes()), // 2+ 12 byte = 35
+ AdStructure::ShortenedLocalName(short_name.as_bytes()), // 2+ 8 byte = 31
],
&mut advertiser_data[..],
)?;
@@ -210,7 +233,11 @@ async fn advertise<'values, 'server, C: Controller>(
let advertiser = peripheral
.advertise(
&trouble_host::advertise::AdvertisementParameters {
- timeout: Some(embassy_time::Duration::from_secs(2)),
+ 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),
..Default::default()
},
Advertisement::ConnectableScannableUndirected {
@@ -410,6 +437,33 @@ async fn publishing_task(server: &Server<'_>, conn: &GattConnecti
}
}
+async fn data_update_task(server: &Server<'_>) {
+ loop {
+ embassy_time::Timer::after(embassy_time::Duration::from_millis(10)).await;
+
+ let mut new_data = None;
+ critical_section::with(|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(data) = queue.pop() {
+ new_data = Some(data);
+ }
+ });
+
+ #[allow(clippy::single_match)]
+ match new_data {
+ Some(BleSendable::BatteryLevel(value)) => {
+ if let Err(err) = server.battery_service.level.set(server, &value) {
+ warn!("BLE: failed setting new battery level: {err:?}");
+ }
+ }
+ _ => {}
+ }
+ }
+}
+
async fn send_raw_notify(
server: &Server<'_>,
conn: &GattConnection<'_, '_, P>,
diff --git a/src/main.rs b/src/main.rs
index e99012e..26782d5 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -228,12 +228,12 @@ async fn main(spawner: Spawner) -> ! {
let controller = ble::make_controller(peripherals.BT);
let serial = &mac.as_bytes()[3..6];
let name = alloc::format!(
- "ESP32 C-ITS {:02x}{:02x}{:02x}",
+ "C-ITS ESP {:02x}:{:02x}:{:02x}",
serial[0],
serial[1],
serial[2]
);
- let short_name = alloc::format!("V2X {:02x}{:02x}{:02x}", serial[0], serial[1], serial[2]); // max 8 chars! -> 10 chars
+ let short_name = alloc::format!("V2X {:02x}{:02x}", serial[1], serial[2]); // max 8 chars!
spawner.spawn(ble::run(controller, name, short_name).expect("Failed to spawn BLE task"));
}
@@ -280,7 +280,10 @@ async fn main(spawner: Spawner) -> ! {
info!("GNSS task started, waiting for GNSS fix...");
// Setup main loop
- info!("Battery: {} %", battery.read_percent());
+ let (bat_percent, bat_millivolt) = battery.read();
+ log::info!("Battery: {bat_millivolt} mV, {bat_percent} %");
+ #[cfg(feature = "ble")]
+ ble::set(ble::BleSendable::BatteryLevel(bat_percent));
let mut last_bat_update = esp_hal::time::Instant::now();
#[cfg(any(feature = "cam_tx", feature = "uart"))]
@@ -400,16 +403,7 @@ async fn main(spawner: Spawner) -> ! {
}
// update GNSS HB on screen
- #[cfg(feature = "screen")]
- {
- let _ = screen.update_gnss(pvt);
-
- // revert to waiting for DENMs, if SPAT is not enabled
- #[cfg(all(not(feature = "spat"), feature = "denm"))]
- let _ = screen
- .update(screen::ScreenState::DenmWaiting())
- .inspect_err(log_screen_error);
- }
+ let _ = screen.update_gnss(pvt);
}
ublox::proto27::PacketRef::AckNak(nak) => {
error!("UBX: {nak:?}");
@@ -599,15 +593,7 @@ async fn main(spawner: Spawner) -> ! {
transport: _,
ref etsi,
} => {
- match applogic::denm::handle_denm(etsi) {
- #[cfg(feature = "screen")]
- Some(cc) => {
- let _ = screen
- .update(screen::ScreenState::DenmWarning(cc))
- .inspect_err(log_screen_error);
- }
- _ => {}
- }
+ applogic::denm::handle_denm(etsi);
// Send data to BLE thread
#[cfg(feature = "ble")]
@@ -645,11 +631,11 @@ async fn main(spawner: Spawner) -> ! {
let cpu_now = esp_hal::time::Instant::now();
if last_bat_update + BAT_UPDATE_INTERVAL < cpu_now {
- let bat_percent = battery.read_percent();
- info!("Battery: {bat_percent} %");
+ let (bat_percent, bat_millivolt) = battery.read();
+ log::info!("Battery: {bat_millivolt} mV, {bat_percent} %");
#[cfg(feature = "ble")]
- ble::update(ble::BleSendable::BatteryLevel(bat_percent));
+ ble::set(ble::BleSendable::BatteryLevel(bat_percent));
last_bat_update = cpu_now;
}
diff --git a/src/screen.rs b/src/screen.rs
index 4f9d01d..32bcfc7 100644
--- a/src/screen.rs
+++ b/src/screen.rs
@@ -1,16 +1,11 @@
//! Screen Output
-#[cfg(feature = "denm")]
-use c_its_parser::standards::cdd_2_2_1;
use embedded_graphics::prelude::*;
use embedded_graphics::{geometry, mono_font, pixelcolor, primitives, text};
#[cfg(feature = "spat")]
use crate::applogic;
-#[cfg(feature = "denm")]
-const DENM_MIN_DISPLAY_TIME: esp_hal::time::Duration = esp_hal::time::Duration::from_secs(3);
-
pub struct DisplaySize {
width: u16,
height: u16,
@@ -29,20 +24,10 @@ where
prev_state: ScreenState,
hb_state: bool,
gnss_hb_state: bool,
- #[cfg(feature = "denm")]
- denm_display_start_time: esp_hal::time::Instant,
// assets
#[cfg(feature = "spat")]
- bike_image: tinytga::Tga<'static, pixelcolor::Rgb565>, // 45x25px
- // #[cfg(feature = "denm")]
- // warn_icon: tinytga::Tga<'static, pixelcolor::Rgb565>, // 200x153 px
- #[cfg(feature = "denm")]
- sv_insert: tinytga::Tga<'static, pixelcolor::Rgb565>, // 80px wide (original 140x70)
- #[cfg(feature = "denm")]
- ev_insert: tinytga::Tga<'static, pixelcolor::Rgb565>, // 60px wide (original 140x105)
- #[cfg(feature = "denm")]
- rww_insert: tinytga::Tga<'static, pixelcolor::Rgb565>, // 80px wide (original 140x95)
+ bike_image: tinytga::Tga<'static, pixelcolor::Rgb565>,
}
#[derive(Debug, Default, PartialEq, Eq)]
@@ -58,10 +43,6 @@ pub enum ScreenState {
GlosaFound(applogic::GlosaFound),
#[cfg(feature = "spat")]
GlosaLocked(applogic::GlosaSignalInfo),
- #[cfg(all(not(feature = "spat"), feature = "denm"))]
- DenmWaiting(),
- #[cfg(feature = "denm")]
- DenmWarning(cdd_2_2_1::etsi_its_cdd::CauseCodeChoice),
}
#[cfg(feature = "spat")]
@@ -120,23 +101,9 @@ where
pub fn new(target: D, display_size: DisplaySize) -> Self {
#[cfg(feature = "spat")]
- let bike_image = tinytga::Tga::from_slice(include_bytes!(
- "../graphics/Sinnbild_Radfahrer_StVO_1992-25px-white.tga"
- ))
- .expect("Failed to parse TGA image");
-
- #[cfg(feature = "denm")]
- let sv_insert =
- tinytga::Tga::from_slice(include_bytes!("../graphics/Stationary_Vehicle-80px.tga"))
- .expect("Failed to parse TGA image");
- #[cfg(feature = "denm")]
- let ev_insert = tinytga::Tga::from_slice(include_bytes!(
- "../graphics/Stationary_Emergency_Vehicle-70px.tga"
- ))
- .expect("Failed to parse TGA image");
- #[cfg(feature = "denm")]
- let rww_insert = tinytga::Tga::from_slice(include_bytes!("../graphics/Work_Zone-80px.tga"))
- .expect("Failed to parse TGA image");
+ let data = include_bytes!("../graphics/Sinnbild_Radfahrer_StVO_1992-25px-white.tga");
+ #[cfg(feature = "spat")]
+ let bike_image = tinytga::Tga::from_slice(data).expect("Failed to parse TGA image");
Self {
target,
@@ -144,53 +111,15 @@ where
prev_state: ScreenState::Uninitialized,
hb_state: false,
gnss_hb_state: false,
- #[cfg(feature = "denm")]
- denm_display_start_time: esp_hal::time::Instant::EPOCH,
#[cfg(feature = "spat")]
bike_image,
- // #[cfg(feature = "denm")]
- // warn_icon,
- #[cfg(feature = "denm")]
- sv_insert,
- #[cfg(feature = "denm")]
- ev_insert,
- #[cfg(feature = "denm")]
- rww_insert,
}
}
pub fn update(&mut self, state: ScreenState) -> Result<(), D::Error> {
- #[cfg(feature = "denm")]
- let now = esp_hal::time::Instant::now();
-
- #[cfg(feature = "denm")]
- // when a DENM was shown before, make sure it's shown for a minimum time
- if let ScreenState::DenmWarning(_) = self.prev_state {
- use core::ops::Sub;
-
- // update DENM display, if there are updates
- if let ScreenState::DenmWarning(cause_code_choice) = &state {
- // TODO: find a way to immediately turn off the DENM display when the DENM is over, but keep it for a certain time initially
- self.denm_display_start_time = now;
-
- if self.prev_state != state {
- self.draw_denm_warning(cause_code_choice)?;
- self.prev_state = state;
-
- return Ok(());
- }
- }
-
- // otherwise early return when timer isn't expired yet
- if now.sub(self.denm_display_start_time) < DENM_MIN_DISPLAY_TIME {
- return Ok(());
- }
- }
-
if self.prev_state != state {
match &state {
ScreenState::Uninitialized | ScreenState::Initial => self.draw_slash_screen()?,
-
#[cfg(feature = "spat")]
ScreenState::GlosaSearching(_glosa_searching) => self.draw_glosa_searching()?,
#[cfg(feature = "spat")]
@@ -207,18 +136,10 @@ where
self.draw_glosa(glosa_data, prev_glosa_data)?;
}
ScreenState::Error(msg) => self.draw_message(msg)?,
- #[cfg(all(not(feature = "spat"), feature = "denm"))]
- ScreenState::DenmWaiting() => self.draw_denm_waiting()?,
- #[cfg(feature = "denm")]
- ScreenState::DenmWarning(cause_code_choice) => {
- self.denm_display_start_time = now;
- self.draw_denm_warning(cause_code_choice)?;
- }
}
}
// Update indicator/ heartbeat
- #[cfg(feature = "spat")]
{
let update_ind_dia = 10;
let circle_x_pos =
@@ -556,100 +477,6 @@ where
Ok(())
}
- #[cfg(all(not(feature = "spat"), feature = "denm"))]
- fn draw_denm_waiting(&mut self) -> Result<(), D::Error> {
- // clear whole display and draw text
- self.clear()?;
-
- text::Text::with_alignment(
- "consider\ninnovation",
- geometry::Point::new(i32::from(self.size.width) / 2, 40 + self.size.offset_top),
- Self::MID_TEXT_STYLE,
- text::Alignment::Center,
- )
- .draw(&mut self.target)?;
-
- text::Text::with_alignment(
- "Waiting\nfor DENM...",
- geometry::Point::new(i32::from(self.size.width) / 2, 130 + self.size.offset_top),
- Self::DEFAULT_TEXT_STYLE,
- text::Alignment::Center,
- )
- .draw(&mut self.target)?;
-
- Ok(())
- }
-
- #[cfg(feature = "denm")]
- pub fn draw_denm_warning(
- &mut self,
- cc: &cdd_2_2_1::etsi_its_cdd::CauseCodeChoice,
- ) -> Result<(), D::Error> {
- // clear whole display and draw image
- self.clear()?;
-
- let centerline = i32::from(self.size.width) / 2;
- let sign_top = self.size.offset_top + 40;
-
- // build triangle shape
- {
- let point1 = geometry::Point::new(centerline, sign_top);
- let point2 = geometry::Point::new(centerline - 100, sign_top + 150);
- let point3 = geometry::Point::new(centerline + 100, sign_top + 150);
- let mut line_style =
- primitives::PrimitiveStyle::with_stroke(pixelcolor::Rgb565::CSS_RED, 10);
- line_style.fill_color = Some(pixelcolor::Rgb565::WHITE);
-
- primitives::Triangle::new(point1, point2, point3)
- .into_styled(line_style)
- .draw(&mut self.target)?;
- }
-
- // add icon inside the triangle
- match cc {
- cdd_2_2_1::etsi_its_cdd::CauseCodeChoice::slowVehicle26(_)
- | cdd_2_2_1::etsi_its_cdd::CauseCodeChoice::stationaryVehicle94(_) => {
- embedded_graphics::image::Image::new(
- &self.sv_insert,
- geometry::Point::new(centerline - (80 / 2), sign_top + 90),
- )
- .draw(&mut self.target)?;
- }
-
- cdd_2_2_1::etsi_its_cdd::CauseCodeChoice::rescueAndRecoveryWorkInProgress15(_)
- | cdd_2_2_1::etsi_its_cdd::CauseCodeChoice::emergencyVehicleApproaching95(_) => {
- embedded_graphics::image::Image::new(
- &self.ev_insert,
- geometry::Point::new(centerline - (70 / 2), sign_top + 85),
- )
- .draw(&mut self.target)?;
- }
-
- cdd_2_2_1::etsi_its_cdd::CauseCodeChoice::roadworks3(_) => {
- embedded_graphics::image::Image::new(
- &self.rww_insert,
- geometry::Point::new(centerline - (80 / 2), sign_top + 85),
- )
- .draw(&mut self.target)?;
- }
- _ => {}
- }
-
- let (cc, scc) = cc.to_u8_tuple();
- text::Text::with_alignment(
- &alloc::format!("DENM CC {cc}/{scc}"),
- geometry::Point::new(
- i32::from(self.size.width) / 2,
- sign_top + 150 + (10 / 2) + 30,
- ),
- Self::MID_TEXT_STYLE,
- text::Alignment::Center,
- )
- .draw(&mut self.target)?;
-
- Ok(())
- }
-
#[allow(unused)]
fn draw_slash_screen(&mut self) -> Result<(), D::Error> {
self.clear_all()?;
@@ -692,13 +519,6 @@ where
)
.draw(&mut self.target)?;
} else {
- text::Text::with_alignment(
- "consider\ninnovation",
- geometry::Point::new(centerline, 40 + self.size.offset_top),
- Self::MID_TEXT_STYLE,
- text::Alignment::Center,
- )
- .draw(&mut self.target)?;
text::Text::with_alignment(
"C-ITS Companion",
geometry::Point::new(centerline, 130 + self.size.offset_top),
diff --git a/src/xiao.rs b/src/xiao.rs
index 6cbff88..8e6d8be 100644
--- a/src/xiao.rs
+++ b/src/xiao.rs
@@ -44,7 +44,7 @@ where
match self.adc.read_oneshot(&mut self.adc_pin) {
Ok(value) => break value,
Err(err) => {
- debug!("Failed to read from ADC: {err:?}")
+ debug!("Failed to read from ADC: {err:?}");
}
}
};
@@ -58,10 +58,19 @@ where
}
/// Returns estimated battery charge state in percent
+ #[allow(unused)]
pub fn read_percent(&mut self) -> u8 {
Self::millivolts_to_percent(self.read_millivolts())
}
+ /// Returns estimated battery charge state in percent and millivolts
+ pub fn read(&mut self) -> (u8, u16) {
+ let millivolt = self.read_millivolts();
+ let percent = Self::millivolts_to_percent(millivolt);
+
+ (percent, millivolt)
+ }
+
/// Converts single cell LiPo battery voltage to charge state in percent
///
/// Note: The implementation is a drastically simplified linear estimation.