From 54c4b99ab632efa0c276d5af4d811f1946e4006c Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Thu, 14 Dec 2023 22:03:51 -0500 Subject: [PATCH] Improve the blinker animations and state transitions when switching blinkers --- bike-lights/bike/src/main.rs | 43 ++++++----- bike-lights/core/src/lib.rs | 119 +++++++++++++++++++++---------- bike-lights/core/src/patterns.rs | 2 +- 3 files changed, 102 insertions(+), 62 deletions(-) diff --git a/bike-lights/bike/src/main.rs b/bike-lights/bike/src/main.rs index b309376..48b809a 100644 --- a/bike-lights/bike/src/main.rs +++ b/bike-lights/bike/src/main.rs @@ -11,13 +11,13 @@ use embedded_alloc::Heap; use embedded_hal::{blocking::spi::Write, digital::v2::InputPin}; use fixed::types::I16F16; use fugit::RateExtU32; -use lights_core::{App, BodyPattern, DashboardPattern, Event, Instant, State, FPS, UI}; +use lights_core::{App, BodyPattern, DashboardPattern, Event, Instant, FPS, UI}; use panic_halt as _; use rp_pico::{ entry, hal::{ clocks::init_clocks_and_plls, - gpio::{FunctionSio, Pin, PinId, PullDown, SioInput}, + gpio::{FunctionSio, Pin, PinId, PullUp, SioInput}, pac::{CorePeripherals, Peripherals}, spi::{Enabled, Spi, SpiDevice, ValidSpiPinout}, watchdog::Watchdog, @@ -30,7 +30,8 @@ use rp_pico::{ static HEAP: Heap = Heap::empty(); const LIGHT_SCALE: I16F16 = I16F16::lit("256.0"); -const GLOBAL_BRIGHTESS: u8 = 1; +const DASHBOARD_BRIGHTESS: u8 = 1; +const BODY_BRIGHTNESS: u8 = 8; struct BikeUI< D: SpiDevice, @@ -41,10 +42,10 @@ struct BikeUI< NextId: PinId, > { spi: RefCell>, - left_blinker_button: Pin, PullDown>, - right_blinker_button: Pin, PullDown>, - previous_animation_button: Pin, PullDown>, - next_animation_button: Pin, PullDown>, + left_blinker_button: Pin, PullUp>, + right_blinker_button: Pin, PullUp>, + previous_animation_button: Pin, PullUp>, + next_animation_button: Pin, PullUp>, } impl< @@ -58,10 +59,10 @@ impl< { fn new( spi: Spi, - left_blinker_button: Pin, PullDown>, - right_blinker_button: Pin, PullDown>, - previous_animation_button: Pin, PullDown>, - next_animation_button: Pin, PullDown>, + left_blinker_button: Pin, PullUp>, + right_blinker_button: Pin, PullUp>, + previous_animation_button: Pin, PullUp>, + next_animation_button: Pin, PullUp>, ) -> Self { Self { spi: RefCell::new(spi), @@ -83,13 +84,13 @@ impl< > UI for BikeUI { fn check_event(&self) -> Option { - if self.left_blinker_button.is_high().unwrap_or(false) { + if self.left_blinker_button.is_low().unwrap_or(false) { Some(Event::LeftBlinker) - } else if self.right_blinker_button.is_high().unwrap_or(false) { + } else if self.right_blinker_button.is_low().unwrap_or(false) { Some(Event::RightBlinker) - } else if self.previous_animation_button.is_high().unwrap_or(false) { + } else if self.previous_animation_button.is_low().unwrap_or(false) { Some(Event::PreviousPattern) - } else if self.next_animation_button.is_high().unwrap_or(false) { + } else if self.next_animation_button.is_low().unwrap_or(false) { Some(Event::NextPattern) } else { None @@ -98,14 +99,12 @@ impl< fn update_lights(&self, dashboard_lights: DashboardPattern, body_lights: BodyPattern) { let mut lights: [u8; 20] = [0; 20]; - // Check https://www.pololu.com/product/3089 for the end frame calculations. It is not what - // I thought. lights[16] = 0xff; lights[17] = 0xff; lights[18] = 0xff; lights[19] = 0xff; for (idx, rgb) in dashboard_lights.iter().enumerate() { - lights[(idx + 1) * 4 + 0] = 0xe0 + GLOBAL_BRIGHTESS; + lights[(idx + 1) * 4 + 0] = 0xe0 + DASHBOARD_BRIGHTESS; lights[(idx + 1) * 4 + 1] = (I16F16::from(rgb.r) * LIGHT_SCALE).saturating_as(); lights[(idx + 1) * 4 + 2] = (I16F16::from(rgb.b) * LIGHT_SCALE).saturating_as(); lights[(idx + 1) * 4 + 3] = (I16F16::from(rgb.g) * LIGHT_SCALE).saturating_as(); @@ -159,10 +158,10 @@ fn main() -> ! { embedded_hal::spi::MODE_1, ); - let left_blinker_button = pins.gpio18.into_pull_down_input(); - let right_blinker_button = pins.gpio19.into_function(); - let previous_animation_button = pins.gpio20.into_function(); - let next_animation_button = pins.gpio21.into_pull_down_input(); + let left_blinker_button = pins.gpio18.into_pull_up_input(); + let right_blinker_button = pins.gpio19.into_pull_up_input(); + let previous_animation_button = pins.gpio20.into_pull_up_input(); + let next_animation_button = pins.gpio21.into_pull_up_input(); let ui = BikeUI::new( spi, diff --git a/bike-lights/core/src/lib.rs b/bike-lights/core/src/lib.rs index 28e803c..6bdf9b1 100644 --- a/bike-lights/core/src/lib.rs +++ b/bike-lights/core/src/lib.rs @@ -169,6 +169,7 @@ impl Animation for Fade { #[derive(Debug)] pub enum FadeDirection { + Transition, FadeIn, FadeOut, } @@ -179,6 +180,7 @@ pub enum BlinkerDirection { } pub struct Blinker { + transition: Fade, fade_in: Fade, fade_out: Fade, direction: FadeDirection, @@ -191,10 +193,12 @@ impl Blinker { fn new( starting_dashboard: DashboardPattern, starting_body: BodyPattern, + home_dashboard: DashboardPattern, + home_body: BodyPattern, direction: BlinkerDirection, time: Instant, ) -> Self { - let mut ending_dashboard = starting_dashboard.clone(); + let mut ending_dashboard = home_dashboard.clone(); match direction { BlinkerDirection::Left => { @@ -209,7 +213,7 @@ impl Blinker { } } - let mut ending_body = starting_body.clone(); + let mut ending_body = home_body.clone(); match direction { BlinkerDirection::Left => { for i in 0..30 { @@ -228,7 +232,7 @@ impl Blinker { } Blinker { - fade_in: Fade::new( + transition: Fade::new( starting_dashboard.clone(), starting_body.clone(), ending_dashboard.clone(), @@ -236,15 +240,23 @@ impl Blinker { BLINKER_FRAMES, time, ), + fade_in: Fade::new( + home_dashboard.clone(), + home_body.clone(), + ending_dashboard.clone(), + ending_body.clone(), + BLINKER_FRAMES, + time, + ), fade_out: Fade::new( ending_dashboard.clone(), ending_body.clone(), - starting_dashboard.clone(), - starting_body.clone(), + home_dashboard.clone(), + home_body.clone(), BLINKER_FRAMES, time, ), - direction: FadeDirection::FadeIn, + direction: FadeDirection::Transition, start_time: time, frames: BLINKER_FRAMES, } @@ -256,6 +268,10 @@ impl Animation for Blinker { let frames = calculate_frames(self.start_time.0, time.0); if frames > self.frames { match self.direction { + FadeDirection::Transition => { + self.direction = FadeDirection::FadeOut; + self.fade_out.start_time = time; + } FadeDirection::FadeIn => { self.direction = FadeDirection::FadeOut; self.fade_out.start_time = time; @@ -269,6 +285,7 @@ impl Animation for Blinker { } match self.direction { + FadeDirection::Transition => self.transition.tick(time), FadeDirection::FadeIn => self.fade_in.tick(time), FadeDirection::FadeOut => self.fade_out.tick(time), } @@ -285,9 +302,45 @@ pub enum Event { RightBlinker, } +#[derive(Clone, Copy, PartialEq)] +pub enum Pattern { + GayPride, + TransPride, +} + +impl Pattern { + fn previous(&self) -> Pattern { + match self { + Pattern::GayPride => Pattern::TransPride, + Pattern::TransPride => Pattern::GayPride, + } + } + + fn next(&self) -> Pattern { + match self { + Pattern::GayPride => Pattern::TransPride, + Pattern::TransPride => Pattern::GayPride, + } + } + + fn dashboard(&self) -> DashboardPattern { + match self { + Pattern::GayPride => PRIDE_DASHBOARD, + Pattern::TransPride => TRANS_PRIDE_DASHBOARD, + } + } + + fn body(&self) -> BodyPattern { + match self { + Pattern::GayPride => PRIDE_BODY, + Pattern::TransPride => TRANS_PRIDE_BODY, + } + } +} + #[derive(Clone, PartialEq)] pub enum State { - Pattern(u8), + Pattern(Pattern), Brake, LeftBlinker, RightBlinker, @@ -298,7 +351,7 @@ pub enum State { pub struct App { ui: Box, state: State, - home_state: State, + home_pattern: Pattern, current_animation: Box, dashboard_lights: DashboardPattern, lights: BodyPattern, @@ -308,8 +361,8 @@ impl App { pub fn new(ui: Box) -> Self { Self { ui, - state: State::Pattern(0), - home_state: State::Pattern(0), + state: State::Pattern(Pattern::GayPride), + home_pattern: Pattern::GayPride, current_animation: Box::new(DefaultAnimation {}), dashboard_lights: OFF_DASHBOARD, lights: OFF_BODY, @@ -318,27 +371,16 @@ impl App { fn update_animation(&mut self, time: Instant) { match self.state { - State::Pattern(0) => { + State::Pattern(ref pattern) => { self.current_animation = Box::new(Fade::new( self.dashboard_lights.clone(), self.lights.clone(), - PRIDE_DASHBOARD, - PRIDE_BODY, + pattern.dashboard(), + pattern.body(), DEFAULT_FRAMES, time, )) } - State::Pattern(1) => { - self.current_animation = Box::new(Fade::new( - self.dashboard_lights.clone(), - self.lights.clone(), - TRANS_PRIDE_DASHBOARD, - TRANS_PRIDE_BODY, - DEFAULT_FRAMES, - time, - )) - } - State::Pattern(_) => {} State::Brake => { self.current_animation = Box::new(Fade::new( self.dashboard_lights.clone(), @@ -353,6 +395,8 @@ impl App { self.current_animation = Box::new(Blinker::new( self.dashboard_lights.clone(), self.lights.clone(), + self.home_pattern.dashboard(), + self.home_pattern.body(), BlinkerDirection::Left, time, )); @@ -361,6 +405,8 @@ impl App { self.current_animation = Box::new(Blinker::new( self.dashboard_lights.clone(), self.lights.clone(), + self.home_pattern.dashboard(), + self.home_pattern.body(), BlinkerDirection::Right, time, )); @@ -374,41 +420,36 @@ impl App { match event { Event::Brake => { if self.state == State::Brake { - self.state = self.home_state.clone(); + self.state = State::Pattern(self.home_pattern); } else { self.state = State::Brake; } } - Event::BrakeRelease => self.state = self.home_state.clone(), + Event::BrakeRelease => self.state = State::Pattern(self.home_pattern), Event::LeftBlinker => match self.state { State::Brake => self.state = State::BrakeLeftBlinker, State::BrakeLeftBlinker => self.state = State::Brake, - State::LeftBlinker => self.state = self.home_state.clone(), + State::LeftBlinker => self.state = State::Pattern(self.home_pattern), _ => self.state = State::LeftBlinker, }, Event::NextPattern => match self.state { - State::Pattern(i) => { - let next = i + 1; - self.state = State::Pattern(if next > 1 { 0 } else { next }); - self.home_state = self.state.clone(); + State::Pattern(ref pattern) => { + self.home_pattern = pattern.next(); + self.state = State::Pattern(self.home_pattern); } _ => (), }, Event::PreviousPattern => match self.state { - State::Pattern(i) => { - if i == 0 { - self.state = State::Pattern(1); - } else { - self.state = State::Pattern(i - 1); - } - self.home_state = self.state.clone(); + State::Pattern(ref pattern) => { + self.home_pattern = pattern.previous(); + self.state = State::Pattern(self.home_pattern); } _ => (), }, Event::RightBlinker => match self.state { State::Brake => self.state = State::BrakeRightBlinker, State::BrakeRightBlinker => self.state = State::Brake, - State::RightBlinker => self.state = self.home_state.clone(), + State::RightBlinker => self.state = State::Pattern(self.home_pattern), _ => self.state = State::RightBlinker, }, } diff --git a/bike-lights/core/src/patterns.rs b/bike-lights/core/src/patterns.rs index 0c8f609..fbaab4e 100644 --- a/bike-lights/core/src/patterns.rs +++ b/bike-lights/core/src/patterns.rs @@ -21,7 +21,7 @@ pub const BRAKES_RED: RGB = RGB { pub const BLINKER_AMBER: RGB = RGB { r: I8F8::lit("1"), - g: I8F8::lit("0.74"), + g: I8F8::lit("0.15"), b: I8F8::lit("0"), };