use crate::{addresses, Color, Error, I2cDevice, Result};
use embedded_hal::i2c::I2c;
pub const NUM_LEDS: usize = 8;
pub struct Pixels<I2C> {
device: I2cDevice<I2C>,
data: [u8; NUM_LEDS * 4],
}
impl<I2C, E> Pixels<I2C>
where
I2C: I2c<Error = E>,
{
pub fn new(i2c: I2C) -> Result<Self, E> {
Self::new_with_address(i2c, addresses::PIXELS)
}
pub fn discover(i2c: &mut I2C) -> Result<u8, E> {
let addresses = [addresses::PIXELS];
for &addr in &addresses {
if i2c.write(addr, &[]).is_ok() {
return Ok(addr);
}
}
i2c.write(addresses[0], &[])
.map(|_| addresses[0])
.map_err(Error::I2c)
}
pub fn new_with_address(i2c: I2C, address: u8) -> Result<Self, E> {
let mut pixels = Self {
device: I2cDevice::new(i2c, address),
data: [0xE0; NUM_LEDS * 4], };
pixels.clear_all();
Ok(pixels)
}
pub fn address(&self) -> u8 {
self.device.address
}
fn map_brightness(brightness: u8) -> u8 {
let clamped = if brightness > 100 { 100 } else { brightness };
((clamped as u16 * 31) / 100) as u8
}
pub fn set_color(
&mut self,
index: usize,
color: Color,
brightness: u8,
) -> Result<&mut Self, E> {
if index >= NUM_LEDS {
return Err(Error::OutOfRange);
}
let byte_index = index * 4;
let mapped_brightness = Self::map_brightness(brightness);
let color_data = color.to_apa102_data() | (mapped_brightness as u32) | 0xE0;
let bytes = color_data.to_le_bytes();
self.data[byte_index..byte_index + 4].copy_from_slice(&bytes);
Ok(self)
}
pub fn set_rgb(
&mut self,
index: usize,
r: u8,
g: u8,
b: u8,
brightness: u8,
) -> Result<&mut Self, E> {
self.set_color(index, Color::new(r, g, b), brightness)
}
pub fn set_brightness(&mut self, index: usize, brightness: u8) -> Result<&mut Self, E> {
if index >= NUM_LEDS {
return Err(Error::OutOfRange);
}
let byte_index = index * 4;
let mapped_brightness = Self::map_brightness(brightness);
self.data[byte_index] = mapped_brightness | 0xE0;
Ok(self)
}
pub fn set_all_color(&mut self, color: Color, brightness: u8) -> &mut Self {
for i in 0..NUM_LEDS {
let _ = self.set_color(i, color, brightness);
}
self
}
pub fn set_all_rgb(&mut self, r: u8, g: u8, b: u8, brightness: u8) -> &mut Self {
self.set_all_color(Color::new(r, g, b), brightness)
}
pub fn set_range_color(
&mut self,
from: usize,
to: usize,
color: Color,
brightness: u8,
) -> &mut Self {
let end = if to >= NUM_LEDS { NUM_LEDS - 1 } else { to };
for i in from..=end {
let _ = self.set_color(i, color, brightness);
}
self
}
pub fn set_all_brightness(&mut self, brightness: u8) -> &mut Self {
for i in 0..NUM_LEDS {
let _ = self.set_brightness(i, brightness);
}
self
}
pub fn clear(&mut self, index: usize) -> Result<&mut Self, E> {
self.set_color(index, Color::BLACK, 0)
}
pub fn clear_range(&mut self, from: usize, to: usize) -> &mut Self {
let end = if to >= NUM_LEDS { NUM_LEDS - 1 } else { to };
for i in from..=end {
let _ = self.clear(i);
}
self
}
pub fn clear_all(&mut self) -> &mut Self {
for i in 0..NUM_LEDS {
let _ = self.set_color(i, Color::BLACK, 0);
}
self
}
pub fn show(&mut self) -> Result<(), E> {
self.device.write(&self.data)?;
Ok(())
}
pub fn set_color_show(&mut self, index: usize, color: Color, brightness: u8) -> Result<(), E> {
self.set_color(index, color, brightness)?;
self.show()
}
pub fn release(self) -> I2C {
self.device.release()
}
}