owb_core/utils/controllers/
leds.rs

1//! LED control module for the Omni-Wheel Bot.
2//!
3//! Manages an addressable LED strip via `SmartLedsWrite` and dispatches commands
4//! received over `LED_CHANNEL`.
5
6use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
7use serde::{Deserialize, Serialize};
8use smart_leds_trait::{SmartLedsWrite, RGB8};
9
10/// Channel used to receive LED commands (`LEDCommand` messages).
11pub static LED_CHANNEL: embassy_sync::channel::Channel<CriticalSectionRawMutex, LEDCommand, 16> =
12    embassy_sync::channel::Channel::new();
13
14/// Number of LEDs in the attached chain.
15const LED_COUNT: usize = 2;
16
17/// LED command variants for switching on/off or setting a color.
18///
19/// Serialized as JSON with tag `"lc"`.
20#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
21#[serde(tag = "lc", rename_all = "snake_case")]
22pub enum LEDCommand {
23    /// Turn the LEDs on (last color or white).
24    On,
25    /// Turn all LEDs off (set to black).
26    Off,
27    /// Set the LED strip to the given RGB color.
28    SC { r: u8, g: u8, b: u8 },
29}
30
31/// High-level LED controller that drives a strip of addressable LEDs.
32///
33/// Maintains the on/off state and last selected color.
34pub struct LedModule<Driver> {
35    driver: Driver,
36    is_on: bool,
37    last_color: Option<RGB8>,
38}
39
40impl<Driver, E> LedModule<Driver>
41where
42    Driver: SmartLedsWrite<Color = RGB8, Error = E>,
43{
44    /// Create a new `LedModule` over the given LED driver.
45    ///
46    /// The strip is initially off with no last color.
47    pub fn new(driver: Driver) -> Self {
48        Self {
49            driver,
50            is_on: false,
51            last_color: None,
52        }
53    }
54
55    /// Execute an incoming `LEDCommand`, updating internal state and LED strip.
56    ///
57    /// - `On`: enable LEDs with the last color or white.
58    /// - `Off`: disable LEDs (all black).
59    /// - `SC {r,g,b}`: set a new color, applied immediately if strip is on.
60    pub fn ex_command(
61        &mut self,
62        cmd: LEDCommand,
63    ) -> Result<(), E> {
64        match cmd {
65            LEDCommand::On => {
66                self.is_on = true;
67                let color = self.last_color.unwrap_or(RGB8 {
68                    r: 255,
69                    g: 255,
70                    b: 255,
71                });
72                self.set_all(color)?;
73            }
74            LEDCommand::Off => {
75                self.is_on = false;
76                self.set_all(RGB8 { r: 0, g: 0, b: 0 })?;
77            }
78            LEDCommand::SC { r, g, b } => {
79                let new_color = RGB8 { r, g, b };
80                self.last_color = Some(new_color);
81                if self.is_on {
82                    self.set_all(new_color)?;
83                }
84            }
85        }
86        Ok(())
87    }
88
89    /// Set all LEDs in the strip to the specified color.
90    fn set_all(
91        &mut self,
92        color: RGB8,
93    ) -> Result<(), E> {
94        let data = core::iter::repeat(color).take(LED_COUNT);
95        self.driver.write(data)
96    }
97}