events: rewrite how sensor statuses are reported, and implement some oled UI icons for it

This commit is contained in:
2025-11-08 12:04:22 +01:00
parent a36fe3d1ac
commit 092885f163
24 changed files with 845 additions and 111 deletions

View File

@@ -1,7 +1,7 @@
use embassy_sync::pubsub::DynPublisher;
use embassy_time::Timer;
use crate::events::{Notification, Scene};
use crate::events::{Notification, Scene, SensorSource, SensorState};
#[embassy_executor::task]
@@ -11,10 +11,16 @@ pub async fn demo_task(ui: DynPublisher<'static, Notification>) {
ui.publish(Notification::SetBrakelight(true)).await;
ui.publish(Notification::SetHeadlight(true)).await;
Timer::after_secs(10).await;
ui.publish(Notification::SensorStatus(SensorSource::Demo, SensorState::AcquiringFix)).await;
loop {
for scene in [Scene::Accelerating, Scene::Ready, Scene::Decelerating, Scene::Ready] {
Timer::after_secs(8).await;
ui.publish(Notification::SceneChange(scene)).await
};
ui.publish(Notification::SceneChange(scene)).await;
for state in [SensorState::Offline, SensorState::AcquiringFix, SensorState::Degraded, SensorState::Offline] {
for sensor in [SensorSource::ForwardsReference, SensorSource::GPS, SensorSource::GravityReference, SensorSource::IMU, SensorSource::Location] {
ui.publish(Notification::SensorStatus(sensor, state)).await;
}
Timer::after_secs(1).await;
}
}
}
}

View File

@@ -1,7 +1,7 @@
use embassy_sync::{channel::{DynamicReceiver, DynamicSender}, pubsub::DynPublisher};
use log::*;
use crate::{ego::engine::BikeStates, events::{Measurement, Notification, Prediction}};
use crate::{ego::engine::BikeStates, events::{Measurement, Notification, Prediction, SensorSource, SensorState}};
#[embassy_executor::task]
pub async fn motion_task(src: DynamicReceiver<'static, Measurement>, ui_sink: DynPublisher<'static, Notification>, prediction_sink: DynamicSender<'static, Prediction>) {
@@ -24,8 +24,10 @@ pub async fn motion_task(src: DynamicReceiver<'static, Measurement>, ui_sink: Dy
states.has_gps_fix.set(false);
},
// FIXME: This needs harmonized with the automatic data timeout from above, somehow?
Measurement::SensorOnline(source) => warn!("Sensor {source:?} reports online!"),
Measurement::SensorOffline(source) => warn!("Sensor {source:?} reports offline!"),
Measurement::SensorHardwareStatus(source, state) => {
warn!("Sensor {source:?} reports {state:?}!");
ui_sink.publish(Notification::SensorStatus(source, state)).await;
},
Measurement::SimulationProgress(source, duration, _pct) => debug!("{source:?} simulation time: {}", duration.as_secs()),
}
}

View File

@@ -31,7 +31,6 @@ fn gyro_raw_to_rads(raw: i16) -> f32 {
#[esp_hal::ram(rtc_fast, persistent)]
static mut MPU_WAS_CALIBRATED: u8 = 0;
//static mut MPU_CALIBRATION: Option<(Accel, Gyro)> = None;
#[embassy_executor::task]
pub async fn mpu_task(events: DynamicSender<'static, Measurement>, bus: I2cDevice<'static, CriticalSectionRawMutex, I2c<'static, Async>>) {
@@ -39,6 +38,7 @@ pub async fn mpu_task(events: DynamicSender<'static, Measurement>, bus: I2cDevic
let busref = RefCell::new(Some(bus));
backoff.forever().attempt::<_, (), ()>(async || {
events.send(Measurement::SensorHardwareStatus(SensorSource::IMU, crate::events::SensorState::Offline)).await;
let mut sensor = backoff.forever().attempt(async || {
warn!("Initializing connection to MPU");
match Mpu6050::new(busref.replace(None).unwrap(), Address::default()).await.map_err(|e| { e.i2c }) {
@@ -47,6 +47,7 @@ pub async fn mpu_task(events: DynamicSender<'static, Measurement>, bus: I2cDevic
Err(())
},
Ok(mut sensor) => {
events.send(Measurement::SensorHardwareStatus(SensorSource::IMU, crate::events::SensorState::AcquiringFix)).await;
match backoff.attempt(async || { mpu_init(&mut sensor).await }).await {
Err(_) => {
busref.replace(Some(sensor.release()));
@@ -60,7 +61,7 @@ pub async fn mpu_task(events: DynamicSender<'static, Measurement>, bus: I2cDevic
let sensor_ref = &mut sensor;
events.send(Measurement::SensorOnline(SensorSource::IMU)).await;
events.send(Measurement::SensorHardwareStatus(SensorSource::IMU, crate::events::SensorState::Online)).await;
//TODO: Need to read in a constant buffer of accelerometer measurements, which we can then use to determine where "forward" points in the body frame when converting from the sensor frame.
// From there, we can rotate the body frame into the world frame using gps headings to generate a compass north
fn lowpass(prev: f32, current: f32, alpha: f32) -> f32 {

View File

@@ -7,7 +7,7 @@ use figments::{mappings::embedded_graphics::Matrix2DSpace, prelude::{Coordinates
use figments_render::output::{Brightness, OutputAsync};
use log::*;
use crate::{animation::Animation, backoff::Backoff, events::{Notification, Prediction, SensorSource, Telemetry}, graphics::{display::DisplayControls, oled_ui::{OledUniforms, Screen}}};
use crate::{animation::Animation, backoff::Backoff, events::{Notification, Prediction, SensorSource, SensorState, Telemetry}, graphics::{display::DisplayControls, oled_ui::{OledUniforms, Screen}}};
#[cfg(feature="oled")]
pub type OledUiSurfacePool = BufferedSurfacePool<OledUniforms, Matrix2DSpace, BinaryColor>;
@@ -84,10 +84,7 @@ impl<S: core::fmt::Debug + Surface<CoordinateSpace = Matrix2DSpace, Pixel = Bina
Telemetry::Notification(Notification::SceneChange(scene)) => self.with_uniforms(|state| {state.ui.scene = scene}).await,
Telemetry::Notification(Notification::SetBrakelight(b)) => self.with_uniforms(|state| {state.ui.brakelight = b}).await,
Telemetry::Notification(Notification::SetHeadlight(b)) => self.with_uniforms(|state| {state.ui.headlight = b}).await,
Telemetry::Notification(Notification::SensorOffline(SensorSource::IMU)) => self.with_uniforms(|state| {state.ui.imu_online = false}).await,
Telemetry::Notification(Notification::SensorOnline(SensorSource::IMU)) => self.with_uniforms(|state| {state.ui.imu_online = true}).await,
Telemetry::Notification(Notification::SensorOffline(SensorSource::GPS)) => self.with_uniforms(|state| {state.ui.gps_online = false}).await,
Telemetry::Notification(Notification::SensorOnline(SensorSource::GPS)) => self.with_uniforms(|state| {state.ui.gps_online = true}).await,
Telemetry::Notification(Notification::SensorStatus(src, sensor_state)) => self.with_uniforms(|state| {state.ui.sensor_states[src] = sensor_state}).await,
_ => ()
}
}

View File

@@ -6,7 +6,7 @@ use core::fmt::Debug;
use futures::join;
use log::*;
use crate::{animation::{AnimatedSurface, Animation}, graphics::display::{SegmentSpace, Uniforms}, events::{Notification, Scene, SensorSource, Telemetry}, graphics::shaders::*};
use crate::{animation::{AnimatedSurface, Animation}, events::{Notification, Scene, SensorSource, SensorState, Telemetry}, graphics::{display::{SegmentSpace, Uniforms}, shaders::*}};
pub struct Ui<S: Surface> {
// Background layer provides an always-running background for everything to draw on
@@ -139,19 +139,13 @@ impl<S: Debug + Surface<Uniforms = Uniforms, CoordinateSpace = SegmentSpace, Pix
}
match event {
// TODO: We probably also want some events to indicate when the ESP has no calibration data or otherwise needs re-calibrated and is waiting for the bike to stand still
Notification::SensorOnline(SensorSource::IMU) => self.flash_notification_color(Rgb::new(0, 255, 0)).await,
Notification::SensorOffline(SensorSource::GPS) => self.flash_notification_color(Rgb::new(255, 0, 0)).await,
Notification::SensorOnline(SensorSource::GPS) => self.flash_notification_color(Rgb::new(0, 255, 255)).await,
Notification::SensorStatus(SensorSource::IMU, SensorState::Online) => self.flash_notification_color(Rgb::new(0, 255, 0)).await,
Notification::SensorStatus(SensorSource::Location, SensorState::Degraded) => self.flash_notification_color(Rgb::new(255, 0, 0)).await,
Notification::SensorStatus(SensorSource::GPS, SensorState::Online) => self.flash_notification_color(Rgb::new(0, 255, 255)).await,
// Scene change
Notification::SceneChange(scene) => self.apply_scene(scene).await,
Notification::SensorsOffline => {
self.flash_notification_color(Rgb::new(255, 0, 0)).await;
self.flash_notification_color(Rgb::new(0, 255, 0)).await;
self.flash_notification_color(Rgb::new(0, 0, 255)).await;
}
Notification::WakeUp => self.show().await,
_ => ()