//! Minimal status LED driver for the CMDR keyboard. use rp2040_hal::{ gpio::AnyPin, pio::{PIO, PIOExt, StateMachineIndex, UninitStateMachine}, }; use smart_leds::{RGB8, SmartLedsWrite}; use ws2812_pio::Ws2812Direct; const COLOR_OFF: RGB8 = RGB8 { r: 0, g: 0, b: 0 }; const COLOR_GREEN: RGB8 = RGB8 { r: 10, g: 7, b: 0 }; const COLOR_BLUE: RGB8 = RGB8 { r: 10, g: 4, b: 10 }; const COLOR_ORANGE: RGB8 = RGB8 { r: 5, g: 10, b: 0 }; const COLOR_RED: RGB8 = RGB8 { r: 20, g: 0, b: 0 }; const COLOR_PURPLE: RGB8 = RGB8 { r: 0, g: 10, b: 10 }; const BREATH_PERIOD_MS: u32 = 3200; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum StatusMode { Off, Active, Idle, Suspended, Error, Bootloader, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct StatusSummary { pub caps_lock_active: bool, pub sticky_armed: bool, pub sticky_latched: bool, pub usb_initialized: bool, pub usb_active: bool, pub usb_suspended: bool, pub idle_mode: bool, } impl StatusSummary { pub const fn new( caps_lock_active: bool, sticky_armed: bool, sticky_latched: bool, usb_initialized: bool, usb_active: bool, usb_suspended: bool, idle_mode: bool, ) -> Self { Self { caps_lock_active, sticky_armed, sticky_latched, usb_initialized, usb_active, usb_suspended, idle_mode, } } } pub struct StatusLed
where
I: AnyPin ,
current_mode: StatusMode,
mode_started_at: Option StatusLed
where
I: AnyPin ,
sm: UninitStateMachine<(P, SM)>,
clock_freq: fugit::HertzU32,
) -> Self {
let mut led = Self {
ws2812_direct: Ws2812Direct::new(pin, pio, sm, clock_freq),
current_mode: StatusMode::Off,
mode_started_at: None,
};
led.write_color(COLOR_OFF);
led
}
pub fn update(&mut self, mode: StatusMode) {
self.current_mode = mode;
self.mode_started_at = None;
let color = mode_color(mode, 0);
self.write_color(color);
}
pub fn apply_summary(&mut self, summary: StatusSummary, current_time_ms: u32) {
let mode = summary_to_mode(summary);
let elapsed = if self.current_mode != mode {
self.current_mode = mode;
self.mode_started_at = Some(current_time_ms);
0
} else {
let start = self.mode_started_at.unwrap_or(current_time_ms);
current_time_ms.saturating_sub(start)
};
let base_color = mode_color(mode, elapsed);
let color = highlight_color(summary).unwrap_or(base_color);
self.write_color(color);
}
fn write_color(&mut self, color: RGB8) {
let _ = self.ws2812_direct.write([color].iter().copied());
}
}
fn summary_to_mode(summary: StatusSummary) -> StatusMode {
if summary.usb_suspended {
StatusMode::Suspended
} else if !summary.usb_initialized {
StatusMode::Off
} else if summary.idle_mode {
StatusMode::Idle
} else if summary.usb_active {
StatusMode::Active
} else {
StatusMode::Off
}
}
fn highlight_color(summary: StatusSummary) -> Option