Skip to main content

modulino/
pixels.rs

1//! Modulino Pixels driver.
2//!
3//! The Modulino Pixels module has 8 RGB LEDs (APA102-compatible).
4
5use crate::{addresses, Color, Error, I2cDevice, Result};
6use embedded_hal::i2c::I2c;
7
8/// Number of LEDs on the Modulino Pixels.
9pub const NUM_LEDS: usize = 8;
10
11/// Driver for the Modulino Pixels module.
12///
13/// # Example
14///
15/// ```rust,ignore
16/// use modulino::{Pixels, Color};
17///
18/// let mut pixels = Pixels::new(i2c)?;
19///
20/// // Set individual LED
21/// pixels.set_color(0, Color::RED, 50)?;
22///
23/// // Set all LEDs to the same color
24/// pixels.set_all_color(Color::BLUE, 25)?;
25///
26/// // Apply changes
27/// pixels.show()?;
28///
29/// // Clear all LEDs
30/// pixels.clear_all()?;
31/// pixels.show()?;
32/// ```
33pub struct Pixels<I2C> {
34    device: I2cDevice<I2C>,
35    data: [u8; NUM_LEDS * 4],
36}
37
38impl<I2C, E> Pixels<I2C>
39where
40    I2C: I2c<Error = E>,
41{
42    /// Create a new Pixels instance with the default address.
43    pub fn new(i2c: I2C) -> Result<Self, E> {
44        Self::new_with_address(i2c, addresses::PIXELS)
45    }
46
47    /// Create a new Pixels instance with a custom address.
48    pub fn new_with_address(i2c: I2C, address: u8) -> Result<Self, E> {
49        let mut pixels = Self {
50            device: I2cDevice::new(i2c, address),
51            data: [0xE0; NUM_LEDS * 4], // Initialize with brightness bits set, LEDs off
52        };
53
54        // Clear all LEDs on init
55        pixels.clear_all();
56
57        Ok(pixels)
58    }
59
60    /// Get the I2C address.
61    pub fn address(&self) -> u8 {
62        self.device.address
63    }
64
65    /// Map brightness from 0-100 to 0-31 (5-bit brightness for APA102).
66    fn map_brightness(brightness: u8) -> u8 {
67        let clamped = if brightness > 100 { 100 } else { brightness };
68        ((clamped as u16 * 31) / 100) as u8
69    }
70
71    /// Set the color of a specific LED.
72    ///
73    /// # Arguments
74    ///
75    /// * `index` - LED index (0-7)
76    /// * `color` - The color to set
77    /// * `brightness` - Brightness level (0-100)
78    pub fn set_color(
79        &mut self,
80        index: usize,
81        color: Color,
82        brightness: u8,
83    ) -> Result<&mut Self, E> {
84        if index >= NUM_LEDS {
85            return Err(Error::OutOfRange);
86        }
87
88        let byte_index = index * 4;
89        let mapped_brightness = Self::map_brightness(brightness);
90        let color_data = color.to_apa102_data() | (mapped_brightness as u32) | 0xE0;
91
92        let bytes = color_data.to_le_bytes();
93        self.data[byte_index..byte_index + 4].copy_from_slice(&bytes);
94
95        Ok(self)
96    }
97
98    /// Set the color of a specific LED using RGB values.
99    ///
100    /// # Arguments
101    ///
102    /// * `index` - LED index (0-7)
103    /// * `r` - Red component (0-255)
104    /// * `g` - Green component (0-255)
105    /// * `b` - Blue component (0-255)
106    /// * `brightness` - Brightness level (0-100)
107    pub fn set_rgb(
108        &mut self,
109        index: usize,
110        r: u8,
111        g: u8,
112        b: u8,
113        brightness: u8,
114    ) -> Result<&mut Self, E> {
115        self.set_color(index, Color::new(r, g, b), brightness)
116    }
117
118    /// Set the brightness of a specific LED without changing its color.
119    ///
120    /// # Arguments
121    ///
122    /// * `index` - LED index (0-7)
123    /// * `brightness` - Brightness level (0-100)
124    pub fn set_brightness(&mut self, index: usize, brightness: u8) -> Result<&mut Self, E> {
125        if index >= NUM_LEDS {
126            return Err(Error::OutOfRange);
127        }
128
129        let byte_index = index * 4;
130        let mapped_brightness = Self::map_brightness(brightness);
131        self.data[byte_index] = mapped_brightness | 0xE0;
132
133        Ok(self)
134    }
135
136    /// Set the color of all LEDs.
137    pub fn set_all_color(&mut self, color: Color, brightness: u8) -> &mut Self {
138        for i in 0..NUM_LEDS {
139            let _ = self.set_color(i, color, brightness);
140        }
141        self
142    }
143
144    /// Set all LEDs to the same RGB color.
145    pub fn set_all_rgb(&mut self, r: u8, g: u8, b: u8, brightness: u8) -> &mut Self {
146        self.set_all_color(Color::new(r, g, b), brightness)
147    }
148
149    /// Set the color of a range of LEDs.
150    ///
151    /// # Arguments
152    ///
153    /// * `from` - Starting index (inclusive)
154    /// * `to` - Ending index (inclusive)
155    /// * `color` - The color to set
156    /// * `brightness` - Brightness level (0-100)
157    pub fn set_range_color(
158        &mut self,
159        from: usize,
160        to: usize,
161        color: Color,
162        brightness: u8,
163    ) -> &mut Self {
164        let end = if to >= NUM_LEDS { NUM_LEDS - 1 } else { to };
165        for i in from..=end {
166            let _ = self.set_color(i, color, brightness);
167        }
168        self
169    }
170
171    /// Set the brightness of all LEDs without changing their colors.
172    pub fn set_all_brightness(&mut self, brightness: u8) -> &mut Self {
173        for i in 0..NUM_LEDS {
174            let _ = self.set_brightness(i, brightness);
175        }
176        self
177    }
178
179    /// Clear (turn off) a specific LED.
180    pub fn clear(&mut self, index: usize) -> Result<&mut Self, E> {
181        self.set_color(index, Color::BLACK, 0)
182    }
183
184    /// Clear a range of LEDs.
185    pub fn clear_range(&mut self, from: usize, to: usize) -> &mut Self {
186        let end = if to >= NUM_LEDS { NUM_LEDS - 1 } else { to };
187        for i in from..=end {
188            let _ = self.clear(i);
189        }
190        self
191    }
192
193    /// Clear all LEDs.
194    pub fn clear_all(&mut self) -> &mut Self {
195        for i in 0..NUM_LEDS {
196            // Brightness 0 maps to 0 | 0xE0 = 0xE0.
197            // Color Black is 0.
198            // We want [0xE0, 0, 0, 0] in memory (LE).
199            let _ = self.set_color(i, Color::BLACK, 0);
200        }
201        self
202    }
203
204    /// Apply the current LED states to the hardware.
205    ///
206    /// This must be called after setting colors for changes to take effect.
207    pub fn show(&mut self) -> Result<(), E> {
208        self.device.write(&self.data)?;
209        Ok(())
210    }
211
212    /// Set a color and immediately show it.
213    pub fn set_color_show(&mut self, index: usize, color: Color, brightness: u8) -> Result<(), E> {
214        self.set_color(index, color, brightness)?;
215        self.show()
216    }
217
218    /// Release the I2C bus.
219    pub fn release(self) -> I2C {
220        self.device.release()
221    }
222}