Skip to content

Instantly share code, notes, and snippets.

@JasonKleban
Created February 28, 2026 17:01
Show Gist options
  • Select an option

  • Save JasonKleban/9e073490182848ea6927d07e463590ac to your computer and use it in GitHub Desktop.

Select an option

Save JasonKleban/9e073490182848ea6927d07e463590ac to your computer and use it in GitHub Desktop.
Bare-metal, no_std no-alloc esp32 ws2812 driver for driving NeoPixel strips
//! Usage:
//! let peripherals = esp_hal::init(esp_hal::Config::default().with_cpu_clock(CpuClock::max()));
//! let neopixels = ws2812_rmt::Ws2812::<'d>::new(peripherals.RMT, peripherals.GPIO3);
use esp_hal::{
Blocking, gpio::{Level, OutputPin}, peripherals::RMT, rmt::{self, Channel, PulseCode, Tx, TxChannelConfig, TxChannelCreator}, time::Rate
};
pub struct Ws2812<'d> {
channel: Option<Channel<'d, Blocking, Tx>>,
}
impl<'d> Ws2812<'d> {
pub fn new(
rmt_peripheral: RMT<'d>,
pin: impl OutputPin + 'd,
) -> Self {
let rmt = rmt::Rmt::new(rmt_peripheral, Rate::from_mhz(80)).unwrap();
// Configure channel 0 for TX
let tx_config = TxChannelConfig::default().with_clk_divider(1);
let channel = Some(rmt.channel0.configure_tx(pin, tx_config).unwrap());
Self { channel }
}
pub fn write(&mut self, pixels: &[[u8; 3]]) {
let channel = self.channel.take().unwrap();
// Worst case: 64 LEDs → 64*24 = 1536 symbols
// That fits in C3 RMT RAM.
let mut symbols: [PulseCode; 1536 + 2] =
[PulseCode::default(); 1538];
let mut idx = 0;
for pixel in pixels {
let bytes = [pixel[1], pixel[0], pixel[2]];
for byte in bytes {
for bit in (0..8).rev() {
let is_one = (byte >> bit) & 1 != 0;
symbols[idx] = if is_one {
ws2812_one()
} else {
ws2812_zero()
};
idx += 1;
}
}
}
// Reset pulse (>50µs low)
symbols[idx] = PulseCode::new(Level::Low, 4000, Level::Low, 0);
idx += 1;
symbols[idx] = PulseCode::end_marker();
let tx = channel.transmit(&symbols[..=idx]).unwrap();
self.channel = Some(tx.wait().unwrap());
}
}
fn ws2812_zero() -> PulseCode {
PulseCode::new(Level::High, 32, Level::Low, 68)
}
fn ws2812_one() -> PulseCode {
PulseCode::new(Level::High, 64, Level::Low, 36)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment