use esp_hal::{
Async,
gpio::{AnyPin, Level},
peripherals::RMT,
rmt::{Channel, PulseCode, Rmt, Tx, TxChannelConfig, TxChannelCreator},
time::Rate,
};
use thiserror_no_std::Error;
pub const MAX_LEDS: usize = 16;
const MAX_CODES: usize = MAX_LEDS * 24 + 2;
const T0H: u16 = 24; const T0L: u16 = 72; const T1H: u16 = 48; const T1L: u16 = 48; const RESET_HALF: u16 = 4000;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Rgb {
pub r: u8,
pub g: u8,
pub b: u8,
}
impl Rgb {
pub const OFF: Rgb = Rgb { r: 0, g: 0, b: 0 };
pub const fn new(r: u8, g: u8, b: u8) -> Self {
Self { r, g, b }
}
}
#[derive(Debug, Error)]
pub enum Sk6812Error {
#[error("Failed to init RMT")]
Rmt(#[from] esp_hal::rmt::Error),
#[error("RMT config error")]
Config,
#[error("Too many LEDs (max {MAX_LEDS})")]
TooManyLeds,
}
pub struct Sk6812Driver {
channel: Channel<'static, Async, Tx>,
}
impl Sk6812Driver {
pub fn new(rmt: RMT<'static>, pin: AnyPin<'static>) -> Result<Self, Sk6812Error> {
let rmt = Rmt::new(rmt, Rate::from_mhz(80))
.map_err(|_| Sk6812Error::Config)?
.into_async();
let channel = rmt
.channel0
.configure_tx(
&TxChannelConfig::default()
.with_clk_divider(1)
.with_idle_output(true)
.with_idle_output_level(Level::Low),
)
.map_err(|_| Sk6812Error::Config)?
.with_pin(pin);
Ok(Self { channel })
}
pub async fn write(&mut self, colors: &[Rgb]) -> Result<(), Sk6812Error> {
if colors.len() > MAX_LEDS {
return Err(Sk6812Error::TooManyLeds);
}
let one = PulseCode::new(Level::High, T1H, Level::Low, T1L);
let zero = PulseCode::new(Level::High, T0H, Level::Low, T0L);
let mut buf: heapless::Vec<PulseCode, MAX_CODES> = heapless::Vec::new();
for c in colors {
for byte in [c.g, c.r, c.b] {
for bit in 0..8 {
let code = if byte & (0x80 >> bit) != 0 { one } else { zero };
let _ = buf.push(code);
}
}
}
let _ = buf.push(PulseCode::new(
Level::Low,
RESET_HALF,
Level::Low,
RESET_HALF,
));
let _ = buf.push(PulseCode::end_marker());
self.channel.transmit(&buf).await?;
Ok(())
}
pub async fn fill(&mut self, color: Rgb, len: usize) -> Result<(), Sk6812Error> {
let len = len.min(MAX_LEDS);
let mut frame: heapless::Vec<Rgb, MAX_LEDS> = heapless::Vec::new();
for _ in 0..len {
let _ = frame.push(color);
}
self.write(&frame).await
}
}