diff --git a/Cargo.toml b/Cargo.toml index 7eb5d99..9e8d70a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ name = "renderbug-embassy" path = "./src/bin/main.rs" [features] -default = ["real-output"] +default = ["real-output", "oled"] real-output = [] simulation = ["dep:rmp"] radio = [ diff --git a/assets/boot-logo.pbm b/assets/boot-logo.pbm new file mode 100644 index 0000000..7a8f4d2 --- /dev/null +++ b/assets/boot-logo.pbm @@ -0,0 +1,121 @@ +P1 +# Created by GIMP version 3.0.4 PNM plug-in +128 64 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000001100000000000 +0000000001000000000000000000000000000000000000000000000000000000000000 +0001111111111000000000000000000000000000000011110000000000000000001110 +0000000000000000000000000000000000000000000000000000000000000111111111 +1110000000000000000000000000000011110000000000000000011110000000000000 +0000000000000000000000000000000000000000000000000001111001111000000000 +0000000000000000000001110000000000000000011110000000000000000000000000 +0000000000000000000000000000000000000001111000111100000000000000100000 +0000000101110000000000000000001110000000000000000000101110011000000000 +0000000000000000000000000001111000111000000011110001111111000001111111 +0000111100011011101111111100001100111100111111111100000000000000000000 +0000000000000001111001111000001111110011111111100011111111001111110011 +1111101111111110011111111100111111111100000000000000000000000000000000 +0001111111110000001110111011111111100011100111001110111011111110111100 +1110011110111001110011111000000000000000000000000000000000000111111110 +0000011111111001110011100111000111011111111011110110111000011100111001 +1001110011110000000000000000000000000000000000000111111110000001111111 +0001110011100111000011011111110001110000111000011100111001100111001111 +0000000000000000000000000000000000000111001111000001110000000111001110 +0111000011011100000001110000111000011100111001100011101110000000000000 +0000000000000000000000000111100111110001110000000111001110011100011101 +1100000001110000111000011100111001100001111110000000000000000000000000 +0000000000000111100011111001110001100111001111011110011101110001100111 +0000111100011100111011100011110000000000000000000000000000000000000000 +1111110001111000111111101111111111001111111100111111100111100011110011 +1000111111110011111110000000000000000000000000000000000001111111100111 +1100111111001111111111001111111110111111001111100001111111100011111111 +0011111111100000000000000000000000000000000000111111000011110000111000 +0111001111000111001100001110001111000001111111000001110110001111111110 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000011110001110000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000011100001110000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000011111111110000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000001111111100000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +1111110000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000001111101110000000110000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000001111001111000001110000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000011100 +1110000011110000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000011100110000001111 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000001111110000011111000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000001111100000111111000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000111100000110111000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000111000001100111000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000011100 +0011111111111100000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000011100001111111111 +1100000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000001000001111111111110000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000011100000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000011100000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000011100000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000011100000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000001110000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000000000000000000000000 +00 \ No newline at end of file diff --git a/src/bin/main.rs b/src/bin/main.rs index d95330c..942a39d 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -61,7 +61,7 @@ async fn main(spawner: Spawner) { let timer1 = TimerGroup::new(peripherals.TIMG1); let mut ui_wdt = timer1.wdt; ui_wdt.set_timeout(esp_hal::timer::timg::MwdtStage::Stage0, esp_hal::time::Duration::from_secs(10)); - ui_wdt.enable(); //FIXME: Re-enable UI watchdog once we have a brain task running + ui_wdt.enable(); let garage = BUS_GARAGE.init(Default::default()); @@ -104,19 +104,12 @@ async fn main(spawner: Spawner) { use esp_hal::i2c::master::{Config, I2c}; let mut rst = Output::new(peripherals.GPIO21, esp_hal::gpio::Level::Low, OutputConfig::default()); - Timer::after_millis(10).await; - rst.set_high(); - Timer::after_millis(10).await; - rst.set_low(); - Timer::after_millis(10).await; - rst.set_high(); - let i2c = I2c::new( peripherals.I2C0, Config::default().with_frequency(Rate::from_khz(400)) ).unwrap().with_scl(peripherals.GPIO18).with_sda(peripherals.GPIO17).into_async(); - spawner.must_spawn(renderbug_embassy::tasks::oled::oled_task(i2c, garage.telemetry.dyn_subscriber().unwrap())); + spawner.must_spawn(renderbug_embassy::tasks::oled::oled_task(i2c, rst, garage.telemetry.dyn_subscriber().unwrap())); } #[cfg(feature="simulation")] diff --git a/src/tasks/oled.rs b/src/tasks/oled.rs index 7dc57c6..f43fe9c 100644 --- a/src/tasks/oled.rs +++ b/src/tasks/oled.rs @@ -1,15 +1,19 @@ -use core::cell::RefCell; +use core::{cell::RefCell, f32::consts::PI}; use alloc::format; use embassy_sync::pubsub::DynSubscriber; use embassy_time::Timer; use embedded_graphics::{image::Image, mono_font::{ascii::{FONT_6X10, FONT_10X20, FONT_9X18_BOLD}, MonoTextStyleBuilder}, pixelcolor::BinaryColor, prelude::*, primitives::{Line, PrimitiveStyleBuilder, Rectangle, StyledDrawable}, text::{Alignment, Baseline, Text}}; -use esp_hal::{i2c::master::I2c, Async}; +use esp_hal::{i2c::master::I2c, Async, gpio::Output}; +use figments::{liber8tion::{interpolate::{ease_in_out_quad, scale8}, trig::{cos8, sin8}}, prelude::Fract8Ops}; use nalgebra::Vector2; -use ssd1306::{mode::DisplayConfigAsync, prelude::DisplayRotation, size::DisplaySize128x64, I2CDisplayInterface, Ssd1306Async}; +use ssd1306::{mode::{BufferedGraphicsModeAsync, DisplayConfigAsync}, prelude::{DisplayRotation, I2CInterface}, size::DisplaySize128x64, I2CDisplayInterface, Ssd1306Async}; use log::*; +use embedded_graphics::mono_font::MonoTextStyle; +use embedded_graphics::primitives::PrimitiveStyle; +use nalgebra::ComplexField; -use crate::{backoff::Backoff, ego::engine::MotionState, events::{Notification, Prediction, Scene, SensorSource, Telemetry}}; +use crate::{backoff::Backoff, ego::engine::MotionState, events::{Notification, Prediction, Scene, SensorSource, Telemetry}, tasks::ui}; mod images { use embedded_graphics::{ @@ -29,15 +33,194 @@ struct OledUI { imu_online: bool, velocity: f32, location: Vector2, - sleep: bool + sleep: bool, } +struct UiPainter<'a>(&'a mut Ssd1306Async>, DisplaySize128x64, BufferedGraphicsModeAsync>); + +#[derive(Debug, Default, Clone, Copy)] +enum Screen { + #[default] + Blank, + Bootsplash, + Home, + Sleeping, + Waking +} + +impl UiPainter<'_> { + pub async fn screen_transition(&mut self, from: Screen, to: Screen, state: &OledUI) { + warn!("Transitioning screens from {from:?} to {to:?}"); + for pos in 0..16 { + self.blank(); + // Draw the screen, then paint the swipe across it from left to right + self.draw_screen(from, 0, state); + Rectangle::new(Point::zero(), Size { width: figments::liber8tion::interpolate::ease_in_out_quad(pos*8) as u32, height: 64}).draw_styled(&DRAW_STYLE, self.0).unwrap(); + self.commit().await; + } + + for pos in 0..16 { + self.blank(); + // Paint the next screen, then cover the right half with the swipe + self.draw_screen(to, 0, state); + let pos = figments::liber8tion::interpolate::ease_in_out_quad(pos*8) as u32; + Rectangle::new(Point::new(pos as i32, 0), Size { width: 128 - pos, height: 64}).draw_styled(&DRAW_STYLE, self.0).unwrap(); + self.commit().await; + } + + // Finally draw the untouched screen + self.blank(); + self.draw_screen(to, 0, state); + self.commit().await; + } + + pub fn draw_screen(&mut self, screen: Screen, frame: usize, ui_state: &OledUI) { + match screen { + Screen::Blank => (), + Screen::Bootsplash => { + Image::new(&images::BOOT_LOGO, Point::zero()).draw(self.0).unwrap(); + const SPARKLE_COUNT: i32 = 8; + for n in 0..SPARKLE_COUNT { + let sparkle_center = Point::new(128u8.scale8(cos8(frame.wrapping_mul(n as usize))) as i32, 64u8.scale8(sin8(frame.wrapping_mul(n as usize) as u8)) as i32); + let offset = (frame / 2 % 32) as i32 - 16; + let rotation = PI * 2.0 * (sin8(frame) as f32 / 255.0); + let normal = Point::new((rotation.cos() * offset as f32) as i32, (rotation.sin() * offset as f32) as i32); + let cross_normal = Point::new((rotation.sin() * offset as f32) as i32, (rotation.cos() * offset as f32) as i32); + // Draw horizontal + Line::new(sparkle_center - normal, sparkle_center + normal) + .draw_styled(&SPARKLE_STYLE, self.0).unwrap(); + // Draw vertical + Line::new(sparkle_center - cross_normal, sparkle_center + cross_normal) + .draw_styled(&SPARKLE_STYLE, self.0).unwrap(); + } + }, + Screen::Home => { + // Status bar + // Sensor indicators + let gps_img = if ui_state.gps_online { + &images::GPS_ON + } else { + &images::GPS_OFF + }; + let imu_img = if ui_state.imu_online { + &images::IMU_ON + } else { + &images::IMU_OFF + }; + Image::new(gps_img, Point::zero()).draw(self.0).unwrap(); + Image::new(imu_img, Point::new((gps_img.size().width + 2) as i32, 0)).draw(self.0).unwrap(); + + #[cfg(feature="demo")] + Text::with_alignment("Demo", Point::new(128, 10), TEXT_STYLE, Alignment::Right) + .draw(self.0) + .unwrap(); + + #[cfg(feature="simulation")] + Text::with_alignment("Sim", Point::new(128, 10), TEXT_STYLE, Alignment::Right) + .draw(self.0) + .unwrap(); + + // Separates the status bar from the UI + Line::new(Point::new(0, 18), Point::new(128, 18)).draw_styled(&INACTIVE_STYLE, self.0).unwrap(); + + // Speed display at the top + Text::with_alignment(&format!("{}", ui_state.velocity), Point::new(128 / 2, 12), SPEED_STYLE, Alignment::Center) + .draw(self.0) + .unwrap(); + + // The main UI content + Image::new(&images::BIKE, Point::new((128 / 2 - images::BIKE.size().width / 2) as i32, 24)).draw(self.0).unwrap(); + + let headlight_img = if ui_state.headlight { + &images::HEADLIGHT_ON + } else { + &images::HEADLIGHT_OFF + }; + let brakelight_img = if ui_state.brakelight { + &images::BRAKELIGHT_ON + } else { + &images::BRAKELIGHT_OFF + }; + Image::new(headlight_img, Point::new(((128 / 2 - images::BIKE.size().width / 2) - 18) as i32, 28)).draw(self.0).unwrap(); + Image::new(brakelight_img, Point::new(((128 / 2 + images::BIKE.size().width / 2) + 2) as i32, 28)).draw(self.0).unwrap(); + + // TODO: Replace the state texts with cute animations or smth + // Current prediction from the motion engine + Text::with_alignment(&format!("{:?}", ui_state.motion), Point::new(128 / 2, 64 - 3), TEXT_STYLE, Alignment::Center) + .draw(self.0) + .unwrap(); + + // Current scene in the UI + Text::with_alignment(&format!("{:?}", ui_state.scene), Point::new(128 / 2, 64 - 13), TEXT_STYLE, Alignment::Center) + .draw(self.0) + .unwrap(); + }, + Screen::Sleeping => { + Text::with_alignment("so eepy Zzz...", Point::new(128 / 2, 64 / 2), LOGO_STYLE, Alignment::Center) + .draw(self.0) + .unwrap(); + }, + Screen::Waking => { + Text::with_alignment("OwO hewwo again :D", Point::new(128 / 2, 64 / 2), LOGO_STYLE, Alignment::Center) + .draw(self.0) + .unwrap(); + } + } + } + + pub fn blank(&mut self) { + self.0.clear_buffer(); + } + + pub async fn commit(&mut self) { + self.0.flush().await.expect("Could not commit frame"); + } +} + +const SPARKLE_STYLE: PrimitiveStyle = PrimitiveStyleBuilder::new() + .stroke_color(BinaryColor::On) + .stroke_width(2) + .build(); + +const TEXT_STYLE: MonoTextStyle = MonoTextStyleBuilder::new() + .font(&FONT_6X10) + .text_color(BinaryColor::On) + .build(); + +const LOGO_STYLE: MonoTextStyle = MonoTextStyleBuilder::new() + .font(&FONT_9X18_BOLD) + .text_color(BinaryColor::On) + .build(); + +const SPEED_STYLE: MonoTextStyle = MonoTextStyleBuilder::new() + .font(&FONT_10X20) + .text_color(BinaryColor::On) + .build(); + +const DRAW_STYLE: PrimitiveStyle = PrimitiveStyleBuilder::new() + .fill_color(BinaryColor::On) + .build(); + +const INACTIVE_STYLE: PrimitiveStyle = PrimitiveStyleBuilder::new() + .fill_color(BinaryColor::Off) + .stroke_color(BinaryColor::On) + .stroke_width(2) + .build(); + #[embassy_executor::task] -pub async fn oled_task(i2c: I2c<'static, Async>, mut events: DynSubscriber<'static, Telemetry>) { +pub async fn oled_task(i2c: I2c<'static, Async>, mut reset_pin: Output<'static>, mut events: DynSubscriber<'static, Telemetry>) { let interface = RefCell::new(Some(I2CDisplayInterface::new(i2c))); + let mut ui_state = OledUI::default(); + // initialize the display let mut display = Backoff::from_secs(3).forever().attempt(async || { info!("Setting up OLED display"); + Timer::after_millis(10).await; + reset_pin.set_high(); + Timer::after_millis(10).await; + reset_pin.set_low(); + Timer::after_millis(10).await; + reset_pin.set_high(); let mut display = Ssd1306Async::new(interface.replace(None).unwrap(), DisplaySize128x64, DisplayRotation::Rotate0) .into_buffered_graphics_mode(); if let Err(e) = display.init().await { @@ -52,119 +235,49 @@ pub async fn oled_task(i2c: I2c<'static, Async>, mut events: DynSubscriber<'sta display.clear(BinaryColor::Off).unwrap(); display.flush().await.unwrap(); - let text_style = MonoTextStyleBuilder::new() - .font(&FONT_6X10) - .text_color(BinaryColor::On) - .build(); - - let logo_style = MonoTextStyleBuilder::new() - .font(&FONT_9X18_BOLD) - .text_color(BinaryColor::On) - .build(); - - let speed_style = MonoTextStyleBuilder::new() - .font(&FONT_10X20) - .text_color(BinaryColor::On) - .build(); - - let draw_style = PrimitiveStyleBuilder::new() - .fill_color(BinaryColor::On) - .build(); - let erase_style = PrimitiveStyleBuilder::new() - .fill_color(BinaryColor::Off) - .build(); - - let inactive_style = PrimitiveStyleBuilder::new() - .fill_color(BinaryColor::Off) - .stroke_color(BinaryColor::On) - .stroke_width(2) - .build(); - - let mut ui_state = OledUI::default(); - info!("Running boot splash animation"); - for pos in 0..16 { - Rectangle::new(Point::zero(), Size { width: figments::liber8tion::interpolate::ease_in_out_quad(pos*8) as u32, height: 64}).draw_styled(&draw_style, &mut display).unwrap(); - display.flush().await.unwrap(); + let mut painter = UiPainter(&mut display); + painter.screen_transition(Screen::Blank, Screen::Bootsplash, &ui_state).await; + for i in 0..255 { + painter.blank(); + painter.draw_screen(Screen::Bootsplash, i, &ui_state); + painter.commit().await; } - - for pos in 0..16 { - Rectangle::new(Point::zero(), Size { width: figments::liber8tion::interpolate::ease_in_out_quad(pos*8) as u32, height: 64}).draw_styled(&erase_style, &mut display).unwrap(); - Text::with_baseline("Renderbug v4", Point::new(0, 16), logo_style, Baseline::Top) - .draw(&mut display) - .unwrap(); - display.flush().await.unwrap(); - } - - Timer::after_secs(2).await; + painter.screen_transition(Screen::Bootsplash, Screen::Home, &ui_state).await; info!("OLED display is ready!"); loop { - // FIXME: Need to implement sleep handling to electrically turn the display on/off and do a sleep animation - display.clear(BinaryColor::Off).unwrap(); + // FIXME: Need to implement sleep handling to electrically turn the display on/off if !ui_state.sleep { - - // Sensor indicators - let gps_img = if ui_state.gps_online { - &images::GPS_ON - } else { - &images::GPS_OFF - }; - let imu_img = if ui_state.imu_online { - &images::IMU_ON - } else { - &images::IMU_OFF - }; - Image::new(gps_img, Point::zero()).draw(&mut display).unwrap(); - Image::new(imu_img, Point::new((gps_img.size().width + 2) as i32, 0)).draw(&mut display).unwrap(); - - Image::new(&images::BIKE, Point::new((128 / 2 - images::BIKE.size().width / 2) as i32, 24)).draw(&mut display).unwrap(); - - let headlight_img = if ui_state.headlight { - &images::HEADLIGHT_ON - } else { - &images::HEADLIGHT_OFF - }; - let brakelight_img = if ui_state.brakelight { - &images::BRAKELIGHT_ON - } else { - &images::BRAKELIGHT_OFF - }; - Image::new(headlight_img, Point::new(((128 / 2 - images::BIKE.size().width / 2) - 18) as i32, 28)).draw(&mut display).unwrap(); - Image::new(brakelight_img, Point::new(((128 / 2 + images::BIKE.size().width / 2) + 2) as i32, 28)).draw(&mut display).unwrap(); - - Text::with_alignment(&format!("{}", ui_state.velocity), Point::new(128 / 2, 12), speed_style, Alignment::Center) - .draw(&mut display) - .unwrap(); - - // TODO: Replace the state texts with cute animations or smth - Text::with_alignment(&format!("{:?}", ui_state.motion), Point::new(128 / 2, 64 - 3), text_style, Alignment::Center) - .draw(&mut display) - .unwrap(); - - Text::with_alignment(&format!("{:?}", ui_state.scene), Point::new(128 / 2, 64 - 13), text_style, Alignment::Center) - .draw(&mut display) - .unwrap(); - - Line::new(Point::new(0, 18), Point::new(128, 18)).draw_styled(&inactive_style, &mut display).unwrap(); + painter.blank(); + painter.draw_screen(Screen::Home, 0, &ui_state); + painter.commit().await; } - display.flush().await.unwrap(); - let evt = events.next_message_pure().await; match evt { Telemetry::Prediction(Prediction::Velocity(v)) => ui_state.velocity = v, Telemetry::Prediction(Prediction::Location(loc)) => ui_state.location = loc, Telemetry::Prediction(Prediction::Motion(motion)) => ui_state.motion = motion, - Telemetry::Notification(crate::events::Notification::SceneChange(scene)) => ui_state.scene = scene, + Telemetry::Notification(Notification::SceneChange(scene)) => ui_state.scene = scene, Telemetry::Notification(Notification::SetBrakelight(b)) => ui_state.brakelight = b, Telemetry::Notification(Notification::SetHeadlight(b)) => ui_state.headlight = b, Telemetry::Notification(Notification::SensorOffline(SensorSource::IMU)) => ui_state.imu_online = false, Telemetry::Notification(Notification::SensorOnline(SensorSource::IMU)) => ui_state.imu_online = true, Telemetry::Notification(Notification::SensorOffline(SensorSource::GPS)) => ui_state.gps_online = false, Telemetry::Notification(Notification::SensorOnline(SensorSource::GPS)) => ui_state.gps_online = true, - Telemetry::Notification(Notification::Sleep) => ui_state.sleep = true, - Telemetry::Notification(Notification::WakeUp) => ui_state.sleep = false, + Telemetry::Notification(Notification::Sleep) => { + painter.screen_transition(Screen::Home, Screen::Sleeping, &ui_state).await; + Timer::after_secs(1).await; + painter.screen_transition(Screen::Sleeping, Screen::Blank, &ui_state).await; + ui_state.sleep = true + }, + Telemetry::Notification(Notification::WakeUp) => { + painter.screen_transition(Screen::Blank, Screen::Waking, &ui_state).await; + Timer::after_secs(1).await; + painter.screen_transition(Screen::Waking, Screen::Home, &ui_state).await; + ui_state.sleep = false + }, _ => () } } diff --git a/src/tasks/safetyui.rs b/src/tasks/safetyui.rs index a7996cb..6ed08c4 100644 --- a/src/tasks/safetyui.rs +++ b/src/tasks/safetyui.rs @@ -45,9 +45,8 @@ impl self.brakelight.set_on(is_on).await, - Notification::SetHeadlight(is_on) => self.headlight.set_on(is_on).await, + Notification::SetBrakelight(is_on) => { + if is_on { + TURN_ON.apply(&mut self.brakelight).await; + } else { + TURN_OFF.apply(&mut self.brakelight).await; + } + }, + Notification::SetHeadlight(is_on) => { + if is_on { + TURN_ON.apply(&mut self.headlight).await; + } else { + TURN_OFF.apply(&mut self.headlight).await; + } + }, Notification::Sleep => self.sleep().await, Notification::WakeUp => self.wake().await, @@ -119,6 +129,9 @@ impl, mut ui: SafetyUi<>::Surface>) { // Wait for the renderer to start running