Compare commits

...

2 Commits

4 changed files with 110 additions and 50 deletions

View File

@ -33,6 +33,31 @@ const LIGHT_SCALE: I16F16 = I16F16::lit("256.0");
const DASHBOARD_BRIGHTESS: u8 = 1; const DASHBOARD_BRIGHTESS: u8 = 1;
const BODY_BRIGHTNESS: u8 = 8; const BODY_BRIGHTNESS: u8 = 8;
struct DebouncedButton<P: PinId> {
debounce: Instant,
pin: Pin<P, FunctionSio<SioInput>, PullUp>,
}
impl<P: PinId> DebouncedButton<P> {
fn new(pin: Pin<P, FunctionSio<SioInput>, PullUp>) -> Self {
Self {
debounce: Instant((0 as u32).into()),
pin,
}
}
fn is_low(&self, time: Instant) -> bool {
if time <= self.debounce {
return false;
}
self.pin.is_low().unwrap_or(false)
}
fn set_debounce(&mut self, time: Instant) {
self.debounce = time + Instant((250 as u32).into());
}
}
struct BikeUI< struct BikeUI<
D: SpiDevice, D: SpiDevice,
P: ValidSpiPinout<D>, P: ValidSpiPinout<D>,
@ -42,10 +67,10 @@ struct BikeUI<
NextId: PinId, NextId: PinId,
> { > {
spi: RefCell<Spi<Enabled, D, P, 8>>, spi: RefCell<Spi<Enabled, D, P, 8>>,
left_blinker_button: Pin<LeftId, FunctionSio<SioInput>, PullUp>, left_blinker_button: DebouncedButton<LeftId>,
right_blinker_button: Pin<RightId, FunctionSio<SioInput>, PullUp>, right_blinker_button: DebouncedButton<RightId>,
previous_animation_button: Pin<PreviousId, FunctionSio<SioInput>, PullUp>, previous_animation_button: DebouncedButton<PreviousId>,
next_animation_button: Pin<NextId, FunctionSio<SioInput>, PullUp>, next_animation_button: DebouncedButton<NextId>,
} }
impl< impl<
@ -66,10 +91,10 @@ impl<
) -> Self { ) -> Self {
Self { Self {
spi: RefCell::new(spi), spi: RefCell::new(spi),
left_blinker_button, left_blinker_button: DebouncedButton::new(left_blinker_button),
right_blinker_button, right_blinker_button: DebouncedButton::new(right_blinker_button),
previous_animation_button, previous_animation_button: DebouncedButton::new(previous_animation_button),
next_animation_button, next_animation_button: DebouncedButton::new(next_animation_button),
} }
} }
} }
@ -83,14 +108,18 @@ impl<
NextId: PinId, NextId: PinId,
> UI for BikeUI<D, P, LeftId, RightId, PreviousId, NextId> > UI for BikeUI<D, P, LeftId, RightId, PreviousId, NextId>
{ {
fn check_event(&self) -> Option<Event> { fn check_event(&mut self, current_time: Instant) -> Option<Event> {
if self.left_blinker_button.is_low().unwrap_or(false) { if self.left_blinker_button.is_low(current_time) {
self.left_blinker_button.set_debounce(current_time);
Some(Event::LeftBlinker) Some(Event::LeftBlinker)
} else if self.right_blinker_button.is_low().unwrap_or(false) { } else if self.right_blinker_button.is_low(current_time) {
self.right_blinker_button.set_debounce(current_time);
Some(Event::RightBlinker) Some(Event::RightBlinker)
} else if self.previous_animation_button.is_low().unwrap_or(false) { } else if self.previous_animation_button.is_low(current_time) {
self.previous_animation_button.set_debounce(current_time);
Some(Event::PreviousPattern) Some(Event::PreviousPattern)
} else if self.next_animation_button.is_low().unwrap_or(false) { } else if self.next_animation_button.is_low(current_time) {
self.next_animation_button.set_debounce(current_time);
Some(Event::NextPattern) Some(Event::NextPattern)
} else { } else {
None None
@ -111,9 +140,9 @@ impl<
} }
for (idx, rgb) in body_lights.iter().enumerate() { for (idx, rgb) in body_lights.iter().enumerate() {
lights[(idx + 4) * 4 + 0] = 0xe0 + BODY_BRIGHTNESS; lights[(idx + 4) * 4 + 0] = 0xe0 + BODY_BRIGHTNESS;
lights[(idx + 4) * 4 + 1] = (I16F16::from(rgb.r) * LIGHT_SCALE).saturating_as(); lights[(idx + 4) * 4 + 1] = (I16F16::from(rgb.b) * LIGHT_SCALE).saturating_as();
lights[(idx + 4) * 4 + 2] = (I16F16::from(rgb.b) * LIGHT_SCALE).saturating_as(); lights[(idx + 4) * 4 + 2] = (I16F16::from(rgb.g) * LIGHT_SCALE).saturating_as();
lights[(idx + 4) * 4 + 3] = (I16F16::from(rgb.g) * LIGHT_SCALE).saturating_as(); lights[(idx + 4) * 4 + 3] = (I16F16::from(rgb.r) * LIGHT_SCALE).saturating_as();
} }
let mut spi = self.spi.borrow_mut(); let mut spi = self.spi.borrow_mut();
spi.write(lights.as_slice()); spi.write(lights.as_slice());

View File

@ -33,7 +33,7 @@ fn linear_ease(value: I8F8, frames: U16F0, slope: I8F8) -> I8F8 {
value_i16f16.saturating_as() value_i16f16.saturating_as()
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq)]
pub struct Instant(pub U128F0); pub struct Instant(pub U128F0);
impl Default for Instant { impl Default for Instant {
@ -61,7 +61,7 @@ impl Sub for Instant {
pub const FPS: u8 = 30; pub const FPS: u8 = 30;
pub trait UI { pub trait UI {
fn check_event(&self) -> Option<Event>; fn check_event(&mut self, current_time: Instant) -> Option<Event>;
fn update_lights(&self, dashboard_lights: DashboardPattern, body_lights: BodyPattern); fn update_lights(&self, dashboard_lights: DashboardPattern, body_lights: BodyPattern);
} }
@ -69,13 +69,15 @@ pub trait Animation {
fn tick(&mut self, time: Instant) -> (DashboardPattern, BodyPattern); fn tick(&mut self, time: Instant) -> (DashboardPattern, BodyPattern);
} }
/*
pub struct DefaultAnimation {} pub struct DefaultAnimation {}
impl Animation for DefaultAnimation { impl Animation for DefaultAnimation {
fn tick(&mut self, _: Instant) -> (DashboardPattern, BodyPattern) { fn tick(&mut self, _: Instant) -> (DashboardPattern, BodyPattern) {
(PRIDE_DASHBOARD, PRIDE_BODY) (WATER_DASHBOARD, WATER_BODY)
} }
} }
*/
pub struct Fade { pub struct Fade {
starting_dashboard: DashboardPattern, starting_dashboard: DashboardPattern,
@ -193,12 +195,10 @@ impl Blinker {
fn new( fn new(
starting_dashboard: DashboardPattern, starting_dashboard: DashboardPattern,
starting_body: BodyPattern, starting_body: BodyPattern,
home_dashboard: DashboardPattern,
home_body: BodyPattern,
direction: BlinkerDirection, direction: BlinkerDirection,
time: Instant, time: Instant,
) -> Self { ) -> Self {
let mut ending_dashboard = home_dashboard.clone(); let mut ending_dashboard = OFF_DASHBOARD.clone();
match direction { match direction {
BlinkerDirection::Left => { BlinkerDirection::Left => {
@ -213,7 +213,7 @@ impl Blinker {
} }
} }
let mut ending_body = home_body.clone(); let mut ending_body = OFF_BODY.clone();
match direction { match direction {
BlinkerDirection::Left => { BlinkerDirection::Left => {
for i in 0..30 { for i in 0..30 {
@ -241,8 +241,8 @@ impl Blinker {
time, time,
), ),
fade_in: Fade::new( fade_in: Fade::new(
home_dashboard.clone(), OFF_DASHBOARD.clone(),
home_body.clone(), OFF_BODY.clone(),
ending_dashboard.clone(), ending_dashboard.clone(),
ending_body.clone(), ending_body.clone(),
BLINKER_FRAMES, BLINKER_FRAMES,
@ -251,8 +251,8 @@ impl Blinker {
fade_out: Fade::new( fade_out: Fade::new(
ending_dashboard.clone(), ending_dashboard.clone(),
ending_body.clone(), ending_body.clone(),
home_dashboard.clone(), OFF_DASHBOARD.clone(),
home_body.clone(), OFF_BODY.clone(),
BLINKER_FRAMES, BLINKER_FRAMES,
time, time,
), ),
@ -302,8 +302,9 @@ pub enum Event {
RightBlinker, RightBlinker,
} }
#[derive(Clone, Copy, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub enum Pattern { pub enum Pattern {
Water,
GayPride, GayPride,
TransPride, TransPride,
} }
@ -311,20 +312,23 @@ pub enum Pattern {
impl Pattern { impl Pattern {
fn previous(&self) -> Pattern { fn previous(&self) -> Pattern {
match self { match self {
Pattern::GayPride => Pattern::TransPride, Pattern::Water => Pattern::TransPride,
Pattern::GayPride => Pattern::Water,
Pattern::TransPride => Pattern::GayPride, Pattern::TransPride => Pattern::GayPride,
} }
} }
fn next(&self) -> Pattern { fn next(&self) -> Pattern {
match self { match self {
Pattern::Water => Pattern::GayPride,
Pattern::GayPride => Pattern::TransPride, Pattern::GayPride => Pattern::TransPride,
Pattern::TransPride => Pattern::GayPride, Pattern::TransPride => Pattern::Water,
} }
} }
fn dashboard(&self) -> DashboardPattern { fn dashboard(&self) -> DashboardPattern {
match self { match self {
Pattern::Water => WATER_DASHBOARD,
Pattern::GayPride => PRIDE_DASHBOARD, Pattern::GayPride => PRIDE_DASHBOARD,
Pattern::TransPride => TRANS_PRIDE_DASHBOARD, Pattern::TransPride => TRANS_PRIDE_DASHBOARD,
} }
@ -332,13 +336,14 @@ impl Pattern {
fn body(&self) -> BodyPattern { fn body(&self) -> BodyPattern {
match self { match self {
Pattern::Water => OFF_BODY,
Pattern::GayPride => PRIDE_BODY, Pattern::GayPride => PRIDE_BODY,
Pattern::TransPride => TRANS_PRIDE_BODY, Pattern::TransPride => TRANS_PRIDE_BODY,
} }
} }
} }
#[derive(Clone, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum State { pub enum State {
Pattern(Pattern), Pattern(Pattern),
Brake, Brake,
@ -359,11 +364,19 @@ pub struct App {
impl App { impl App {
pub fn new(ui: Box<dyn UI>) -> Self { pub fn new(ui: Box<dyn UI>) -> Self {
let pattern = Pattern::Water;
Self { Self {
ui, ui,
state: State::Pattern(Pattern::GayPride), state: State::Pattern(pattern),
home_pattern: Pattern::GayPride, home_pattern: pattern,
current_animation: Box::new(DefaultAnimation {}), current_animation: Box::new(Fade::new(
OFF_DASHBOARD,
OFF_BODY,
pattern.dashboard(),
pattern.body(),
DEFAULT_FRAMES,
Instant((0 as u32).into()),
)),
dashboard_lights: OFF_DASHBOARD, dashboard_lights: OFF_DASHBOARD,
lights: OFF_BODY, lights: OFF_BODY,
} }
@ -395,8 +408,6 @@ impl App {
self.current_animation = Box::new(Blinker::new( self.current_animation = Box::new(Blinker::new(
self.dashboard_lights.clone(), self.dashboard_lights.clone(),
self.lights.clone(), self.lights.clone(),
self.home_pattern.dashboard(),
self.home_pattern.body(),
BlinkerDirection::Left, BlinkerDirection::Left,
time, time,
)); ));
@ -405,8 +416,6 @@ impl App {
self.current_animation = Box::new(Blinker::new( self.current_animation = Box::new(Blinker::new(
self.dashboard_lights.clone(), self.dashboard_lights.clone(),
self.lights.clone(), self.lights.clone(),
self.home_pattern.dashboard(),
self.home_pattern.body(),
BlinkerDirection::Right, BlinkerDirection::Right,
time, time,
)); ));
@ -456,7 +465,7 @@ impl App {
} }
pub fn tick(&mut self, time: Instant) { pub fn tick(&mut self, time: Instant) {
match self.ui.check_event() { match self.ui.check_event(time) {
Some(event) => { Some(event) => {
self.update_state(event); self.update_state(event);
self.update_animation(time); self.update_animation(time);

View File

@ -26,26 +26,26 @@ pub const BLINKER_AMBER: RGB<I8F8> = RGB {
}; };
pub const PRIDE_RED: RGB<I8F8> = RGB { pub const PRIDE_RED: RGB<I8F8> = RGB {
r: I8F8::lit("0.89"), r: I8F8::lit("0.95"),
g: I8F8::lit("0.01"), g: I8F8::lit("0.00"),
b: I8F8::lit("0.01"), b: I8F8::lit("0.00"),
}; };
pub const PRIDE_ORANGE: RGB<I8F8> = RGB { pub const PRIDE_ORANGE: RGB<I8F8> = RGB {
r: I8F8::lit("1.0"), r: I8F8::lit("1.0"),
g: I8F8::lit("0.54"), g: I8F8::lit("0.25"),
b: I8F8::lit("0"), b: I8F8::lit("0"),
}; };
pub const PRIDE_YELLOW: RGB<I8F8> = RGB { pub const PRIDE_YELLOW: RGB<I8F8> = RGB {
r: I8F8::lit("1.0"), r: I8F8::lit("1.0"),
g: I8F8::lit("0.92"), g: I8F8::lit("0.85"),
b: I8F8::lit("0"), b: I8F8::lit("0"),
}; };
pub const PRIDE_GREEN: RGB<I8F8> = RGB { pub const PRIDE_GREEN: RGB<I8F8> = RGB {
r: I8F8::lit("0"), r: I8F8::lit("0"),
g: I8F8::lit("0.5"), g: I8F8::lit("0.95"),
b: I8F8::lit("0.05"), b: I8F8::lit("0.05"),
}; };
@ -56,9 +56,9 @@ pub const PRIDE_INDIGO: RGB<I8F8> = RGB {
}; };
pub const PRIDE_VIOLET: RGB<I8F8> = RGB { pub const PRIDE_VIOLET: RGB<I8F8> = RGB {
r: I8F8::lit("0.45"), r: I8F8::lit("0.75"),
g: I8F8::lit("0.16"), g: I8F8::lit("0.0"),
b: I8F8::lit("0.50"), b: I8F8::lit("0.80"),
}; };
pub const TRANS_BLUE: RGB<I8F8> = RGB { pub const TRANS_BLUE: RGB<I8F8> = RGB {
@ -73,11 +73,33 @@ pub const TRANS_PINK: RGB<I8F8> = RGB {
b: I8F8::lit("0.32"), b: I8F8::lit("0.32"),
}; };
pub const WATER_1: RGB<I8F8> = RGB {
r: I8F8::lit("0.0"),
g: I8F8::lit("0.0"),
b: I8F8::lit("0.75"),
};
pub const WATER_2: RGB<I8F8> = RGB {
r: I8F8::lit("0.8"),
g: I8F8::lit("0.8"),
b: I8F8::lit("0.8"),
};
pub const WATER_3: RGB<I8F8> = RGB {
r: I8F8::lit("0.00"),
g: I8F8::lit("0.75"),
b: I8F8::lit("0.75"),
};
pub const OFF_DASHBOARD: DashboardPattern = [RGB_OFF; 3]; pub const OFF_DASHBOARD: DashboardPattern = [RGB_OFF; 3];
pub const OFF_BODY: BodyPattern = [RGB_OFF; 60]; pub const OFF_BODY: BodyPattern = [RGB_OFF; 60];
pub const DEFAULT_FRAMES: U16F0 = U16F0::lit("30"); pub const DEFAULT_FRAMES: U16F0 = U16F0::lit("30");
pub const WATER_DASHBOARD: DashboardPattern = [WATER_1, WATER_2, WATER_3];
pub const WATER_BODY: BodyPattern = [RGB_OFF; 60];
pub const PRIDE_DASHBOARD: DashboardPattern = [PRIDE_RED, PRIDE_GREEN, PRIDE_INDIGO]; pub const PRIDE_DASHBOARD: DashboardPattern = [PRIDE_RED, PRIDE_GREEN, PRIDE_INDIGO];
pub const PRIDE_BODY: BodyPattern = [ pub const PRIDE_BODY: BodyPattern = [
@ -178,7 +200,7 @@ pub const BRAKES_DASHBOARD: DashboardPattern = [BRAKES_RED; 3];
pub const BRAKES_BODY: BodyPattern = [BRAKES_RED; 60]; pub const BRAKES_BODY: BodyPattern = [BRAKES_RED; 60];
pub const BLINKER_FRAMES: U16F0 = U16F0::lit("15"); pub const BLINKER_FRAMES: U16F0 = U16F0::lit("10");
pub const LEFT_BLINKER_DASHBOARD: DashboardPattern = [BLINKER_AMBER, RGB_OFF, RGB_OFF]; pub const LEFT_BLINKER_DASHBOARD: DashboardPattern = [BLINKER_AMBER, RGB_OFF, RGB_OFF];

View File

@ -152,7 +152,7 @@ struct GTKUI {
} }
impl UI for GTKUI { impl UI for GTKUI {
fn check_event(&self) -> Option<Event> { fn check_event(&mut self, _: Instant) -> Option<Event> {
match self.rx.try_recv() { match self.rx.try_recv() {
Ok(event) => Some(event), Ok(event) => Some(event),
Err(TryRecvError::Empty) => None, Err(TryRecvError::Empty) => None,