tasks: motion: implement a sensor recording layer
This commit is contained in:
@@ -11,21 +11,21 @@ use core::{num::{self, Wrapping}, ptr::addr_of_mut};
|
|||||||
|
|
||||||
use alloc::{string::String, sync::Arc};
|
use alloc::{string::String, sync::Arc};
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_time::{Instant, Timer};
|
use embassy_time::{Duration, Instant, Timer, WithTimeout};
|
||||||
|
|
||||||
use esp_hal::{gpio::{Output, OutputConfig, Pin}, time::Rate, xtensa_lx::debug_break};
|
use enum_map::EnumMap;
|
||||||
|
use esp_hal::{gpio::{Output, OutputConfig, Pin}, time::Rate};
|
||||||
use esp_hal::{
|
use esp_hal::{
|
||||||
clock::CpuClock, system::{AppCoreGuard, CpuControl, Stack}, timer::{systimer::SystemTimer, timg::{TimerGroup, Wdt}}
|
clock::CpuClock, system::{AppCoreGuard, CpuControl, Stack}, timer::{systimer::SystemTimer, timg::{TimerGroup, Wdt}}
|
||||||
};
|
};
|
||||||
|
|
||||||
use embassy_sync::{
|
use embassy_sync::{
|
||||||
pubsub::PubSubChannel,
|
blocking_mutex::raw::NoopRawMutex, channel::DynamicReceiver, pubsub::PubSubChannel
|
||||||
blocking_mutex::raw::NoopRawMutex
|
|
||||||
};
|
};
|
||||||
use static_cell::ConstStaticCell;
|
use esp_storage::FlashStorage;
|
||||||
use log::*;
|
use log::*;
|
||||||
use renderbug_embassy::{events::Prediction, graphics::display::DisplayControls, logging::RenderbugLogger, tasks::{oled::{OledUI, OledUiSurfacePool, oled_ui}, safetyui::{SafetyUi, safety_ui_main}, ui::UiSurfacePool}};
|
use renderbug_bike::{events::{Prediction, SensorSource, SensorState}, graphics::display::DisplayControls, logging::RenderbugLogger, simdata::IMUReading, storage::{SharedFlash, SimDataRecorder}, tasks::{oled::{OledUI, OledUiSurfacePool, oled_ui}, safetyui::{SafetyUi, safety_ui_main}, ui::UiSurfacePool}, tracing::Tracer};
|
||||||
use renderbug_embassy::events::Measurement;
|
use renderbug_bike::events::Measurement;
|
||||||
use static_cell::StaticCell;
|
use static_cell::StaticCell;
|
||||||
use esp_backtrace as _;
|
use esp_backtrace as _;
|
||||||
use esp_rtos::embassy::{Executor, InterruptExecutor};
|
use esp_rtos::embassy::{Executor, InterruptExecutor};
|
||||||
@@ -63,6 +63,9 @@ async fn main(spawner: Spawner) {
|
|||||||
static MOTION_BUS: StaticCell<Channel<CriticalSectionRawMutex,Measurement,5> > = StaticCell::new();
|
static MOTION_BUS: StaticCell<Channel<CriticalSectionRawMutex,Measurement,5> > = StaticCell::new();
|
||||||
let motion_bus = MOTION_BUS.init_with(|| { Channel::new() });
|
let motion_bus = MOTION_BUS.init_with(|| { Channel::new() });
|
||||||
|
|
||||||
|
static RECORDING_BUS: StaticCell<PubSubChannel<CriticalSectionRawMutex,Measurement,1, 1, 1> > = StaticCell::new();
|
||||||
|
let recording_bus = RECORDING_BUS.init_with(|| { PubSubChannel::new() });
|
||||||
|
|
||||||
info!("Setting up rendering pipeline");
|
info!("Setting up rendering pipeline");
|
||||||
let mut surfaces = UiSurfacePool::default();
|
let mut surfaces = UiSurfacePool::default();
|
||||||
let ui = Ui::new(&mut surfaces);
|
let ui = Ui::new(&mut surfaces);
|
||||||
@@ -218,7 +221,7 @@ async fn main(spawner: Spawner) {
|
|||||||
spawner.must_spawn(wdt_task(ui_wdt));
|
spawner.must_spawn(wdt_task(ui_wdt));
|
||||||
}
|
}
|
||||||
|
|
||||||
spawner.must_spawn(print_telemetry(predictions.dyn_subscriber().unwrap()));
|
spawner.must_spawn(print_sensor_status(predictions.dyn_subscriber().unwrap()));
|
||||||
|
|
||||||
info!("System is ready in {}ms", Instant::now().as_millis());
|
info!("System is ready in {}ms", Instant::now().as_millis());
|
||||||
};
|
};
|
||||||
@@ -253,12 +256,77 @@ async fn wdt_task(mut wdt: Wdt<esp_hal::peripherals::TIMG1<'static>>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
async fn print_telemetry(mut events: DynSubscriber<'static, Prediction>) {
|
async fn record_telemetry(firehose: DynamicReceiver<'static, Measurement>, mut storage: SimDataRecorder<SharedFlash<FlashStorage>>) {
|
||||||
|
loop {
|
||||||
|
match firehose.receive().await {
|
||||||
|
Measurement::IMU { accel, gyro } => {
|
||||||
|
let reading = IMUReading {
|
||||||
|
accel_x: accel.x as f64,
|
||||||
|
accel_y: accel.y as f64,
|
||||||
|
accel_z: accel.z as f64,
|
||||||
|
gyro_x: gyro.x as f64,
|
||||||
|
gyro_y: gyro.y as f64,
|
||||||
|
gyro_z: gyro.z as f64
|
||||||
|
};
|
||||||
|
storage.write_next(reading).unwrap();
|
||||||
|
info!("Wrote IMU to flash");
|
||||||
|
},
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn print_sensor_readings(mut events: DynSubscriber<'static, Measurement>) {
|
||||||
|
loop {
|
||||||
|
match events.next_message_pure().await {
|
||||||
|
Measurement::IMU { accel, gyro } => {
|
||||||
|
esp_println::println!("accel=({},{},{}) gyro=({},{},{})", accel.x, accel.y, accel.z, gyro.x, gyro.y, gyro.z);
|
||||||
|
},
|
||||||
|
Measurement::GPS(gps) => {
|
||||||
|
esp_println::println!("gps={gps:?}");
|
||||||
|
},
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn print_sensor_status(mut events: DynSubscriber<'static, Prediction>) {
|
||||||
|
|
||||||
info!("telemetry ready");
|
info!("telemetry ready");
|
||||||
let mut num_events = Wrapping(0usize);
|
let mut num_events = Wrapping(0usize);
|
||||||
loop {
|
loop {
|
||||||
let next = events.next_message_pure().await;
|
let next = events.next_message_pure().with_timeout(Duration::from_secs(5)).await;
|
||||||
trace!("idx={} predict={next:?}", num_events.0);
|
match next {
|
||||||
num_events += 1;
|
Ok(Prediction::SensorStatus(sensor, status)) => {
|
||||||
|
sensor_states[sensor] = status;
|
||||||
|
let mut report_str = String::new();
|
||||||
|
for (sensor, state) in &sensor_states {
|
||||||
|
let state_icon = match state {
|
||||||
|
SensorState::AcquiringFix => "?",
|
||||||
|
SensorState::Degraded => "-",
|
||||||
|
SensorState::Offline => "X",
|
||||||
|
SensorState::Online => "O"
|
||||||
|
};
|
||||||
|
report_str += alloc::format!("{sensor:?}={state_icon} ").as_str();
|
||||||
|
}
|
||||||
|
info!("{report_str}");
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
let mut report_str = String::new();
|
||||||
|
for (sensor, state) in &sensor_states {
|
||||||
|
let state_icon = match state {
|
||||||
|
SensorState::AcquiringFix => "?",
|
||||||
|
SensorState::Degraded => "-",
|
||||||
|
SensorState::Offline => "X",
|
||||||
|
SensorState::Online => "O"
|
||||||
|
};
|
||||||
|
report_str += alloc::format!("{sensor:?}={state_icon} ").as_str();
|
||||||
|
}
|
||||||
|
info!("{report_str}");
|
||||||
|
},
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,7 @@ use crate::{ego::engine::BikeStates, events::{Measurement, Prediction}};
|
|||||||
const TIMEOUT: Duration = Duration::from_millis(3);
|
const TIMEOUT: Duration = Duration::from_millis(3);
|
||||||
|
|
||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
pub async fn motion_task(src: DynamicReceiver<'static, Measurement>, prediction_sink: DynPublisher<'static, Prediction>) {
|
pub async fn motion_task(src: DynamicReceiver<'static, Measurement>, prediction_sink: DynPublisher<'static, Prediction>, recording_sink: DynPublisher<'static, Measurement>) {
|
||||||
let mut states = BikeStates::default();
|
let mut states = BikeStates::default();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@@ -34,5 +34,6 @@ pub async fn motion_task(src: DynamicReceiver<'static, Measurement>, prediction_
|
|||||||
Measurement::SimulationProgress(source, duration, pct) => debug!("{source:?} simulation time: {} {} / 255", duration.as_secs(), pct),
|
Measurement::SimulationProgress(source, duration, pct) => debug!("{source:?} simulation time: {} {} / 255", duration.as_secs(), pct),
|
||||||
Measurement::Annotation => ()
|
Measurement::Annotation => ()
|
||||||
}
|
}
|
||||||
|
let _ = recording_sink.try_publish(next_measurement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user