Skip to content
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ smart-leds = "0.4"
embedded-graphics = "0.8"

[build-dependencies]
embuild = "0.32"
embuild = "0.33"

[profile.release]
strip = true
Expand Down
60 changes: 60 additions & 0 deletions examples/smart_leds_pl9823_rgb.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#![cfg(feature = "smart-leds-trait")]
use esp_idf_hal::peripherals::Peripherals;
use esp_idf_hal::rmt::config::TransmitConfig;
use esp_idf_hal::rmt::TxRmtDriver;
use esp_idf_hal::sys::esp_random;
use smart_leds::hsv::{hsv2rgb, Hsv};
use smart_leds_trait::{SmartLedsWrite, RGB8};
use std::thread::sleep;
use std::time::Duration;
use ws2812_esp32_rmt_driver::driver::color::LedPixelColorRgb24;
use ws2812_esp32_rmt_driver::driver::Ws2812Esp32RmtDriverBuilder;
use ws2812_esp32_rmt_driver::LedPixelEsp32Rmt;

fn main() -> ! {
// Temporary. Will disappear once ESP-IDF 4.4 is released, but for now it is necessary to call this function once,
// or else some patches to the runtime implemented by esp-idf-sys might not link properly.
esp_idf_sys::link_patches();

// Shenzhen Rita Lighting PL2823
const PL9823_T0H_NS: Duration = Duration::from_nanos(350);
const PL9823_T0L_NS: Duration = Duration::from_nanos(1360);
const PL9823_T1H_NS: Duration = Duration::from_nanos(1360);
const PL9823_T1L_NS: Duration = Duration::from_nanos(350);

let peripherals = Peripherals::take().unwrap();
let led_pin = peripherals.pins.gpio25;
let channel = peripherals.rmt.channel0;

let driver_config = TransmitConfig::new().clock_divider(1); // Required parameter.
let tx_driver = TxRmtDriver::new(channel, led_pin, &driver_config).unwrap();
let ws2812_driver = Ws2812Esp32RmtDriverBuilder::new_with_rmt_driver(tx_driver)
.unwrap()
.encoder_duration(
&PL9823_T0H_NS,
&PL9823_T0L_NS,
&PL9823_T1H_NS,
&PL9823_T1L_NS,
)
.unwrap()
.build()
.unwrap();
let mut ws2812 =
LedPixelEsp32Rmt::<RGB8, LedPixelColorRgb24>::new_with_ws2812_driver(ws2812_driver)
.unwrap();

let mut hue = unsafe { esp_random() } as u8;
loop {
let pixels = std::iter::repeat(hsv2rgb(Hsv {
hue,
sat: 255,
val: 8,
}))
.take(25);
ws2812.write(pixels).unwrap();

sleep(Duration::from_millis(100));

hue = hue.wrapping_add(10);
}
}
12 changes: 12 additions & 0 deletions src/driver/color.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,18 @@ fn test_led_pixel_color_brightness() {
/// ```
pub type LedPixelColorGrb24 = LedPixelColorImpl<3, 1, 0, 2, 255>;

/// 8-bit RGB LED pixel color (total 32-bit pixel)
///
/// # Examples
///
/// ```
/// use ws2812_esp32_rmt_driver::driver::color::{LedPixelColorRgb24, LedPixelColor};
///
/// let color = LedPixelColorRgb24::new_with_rgb(1, 2, 3);
/// assert_eq!(color.as_ref(), [1, 2, 3]);
/// ```
pub type LedPixelColorRgb24 = LedPixelColorImpl<3, 0, 1, 2, 255>;

/// 8-bit RGBW LED pixel color (total 32-bit pixel)
///
/// # Examples
Expand Down
205 changes: 143 additions & 62 deletions src/driver/esp32_rmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,12 @@ use core::marker::PhantomData;

#[cfg(not(target_vendor = "espressif"))]
use crate::mock::esp_idf_hal;
#[cfg(target_vendor = "espressif")]
use esp_idf_hal::rmt::{PinState, Pulse, Symbol};
use esp_idf_hal::{
gpio::OutputPin,
peripheral::Peripheral,
rmt::{config::TransmitConfig, RmtChannel, TxRmtDriver},
};
#[cfg(target_vendor = "espressif")]
use esp_idf_hal::{
rmt::{PinState, Pulse, Symbol},
units::Hertz,
};

Expand All @@ -36,40 +34,44 @@ const WS2812_T1L_NS: Duration = Duration::from_nanos(450);

/// Converter to a sequence of RMT items.
#[repr(C)]
#[cfg(target_vendor = "espressif")]
struct Ws2812Esp32RmtItemEncoder {
/// The RMT item that represents a 0 code.
#[cfg(target_vendor = "espressif")]
bit0: Symbol,
/// The RMT item that represents a 1 code.
#[cfg(target_vendor = "espressif")]
bit1: Symbol,
}

#[cfg(target_vendor = "espressif")]
impl Ws2812Esp32RmtItemEncoder {
/// Creates a new encoder with the given clock frequency.
///
/// # Arguments
///
/// * `clock_hz` - The clock frequency.
///
/// # Errors
///
/// Returns an error if the clock frequency is invalid or if the RMT item encoder cannot be created.
fn new(clock_hz: Hertz) -> Result<Self, EspError> {
let (bit0, bit1) = (
Symbol::new(
Pulse::new_with_duration(clock_hz, PinState::High, &WS2812_T0H_NS)?,
Pulse::new_with_duration(clock_hz, PinState::Low, &WS2812_T0L_NS)?,
),
Symbol::new(
Pulse::new_with_duration(clock_hz, PinState::High, &WS2812_T1H_NS)?,
Pulse::new_with_duration(clock_hz, PinState::Low, &WS2812_T1L_NS)?,
),
);

Ok(Self { bit0, bit1 })
/// Creates a new `Ws2812Esp32RmtItemEncoder`.
fn new(
clock_hz: Hertz,
t0h: &Duration,
t0l: &Duration,
t1h: &Duration,
t1l: &Duration,
) -> Result<Self, Ws2812Esp32RmtDriverError> {
#[cfg(target_vendor = "espressif")]
{
let (bit0, bit1) = (
Symbol::new(
Pulse::new_with_duration(clock_hz, PinState::High, &t0h)?,
Pulse::new_with_duration(clock_hz, PinState::Low, &t0l)?,
),
Symbol::new(
Pulse::new_with_duration(clock_hz, PinState::High, &t1h)?,
Pulse::new_with_duration(clock_hz, PinState::Low, &t1l)?,
),
);
Ok(Self { bit0, bit1 })
}
#[cfg(not(target_vendor = "espressif"))]
{
let _ = (clock_hz, t0h, t0l, t1h, t1l);
Ok(Self {})
}
}

/// Encodes a block of data as a sequence of RMT items.
///
/// # Arguments
Expand All @@ -80,6 +82,7 @@ impl Ws2812Esp32RmtItemEncoder {
///
/// An iterator over the RMT items that represent the encoded data.
#[inline]
#[cfg(target_vendor = "espressif")]
fn encode_iter<'a, 'b, T>(&'a self, src: T) -> impl Iterator<Item = Symbol> + Send + 'a
where
'b: 'a,
Expand Down Expand Up @@ -139,6 +142,115 @@ impl From<EspError> for Ws2812Esp32RmtDriverError {
}
}

/// Builder for `Ws2812Esp32RmtDriver`.
///
/// # Examples
///
///
/// ```
/// # #[cfg(not(target_vendor = "espressif"))]
/// # use ws2812_esp32_rmt_driver::mock::esp_idf_hal;
/// #
/// # use core::time::Duration;
/// # use esp_idf_hal::peripherals::Peripherals;
/// # use esp_idf_hal::rmt::config::TransmitConfig;
/// # use esp_idf_hal::rmt::TxRmtDriver;
/// # use ws2812_esp32_rmt_driver::driver::Ws2812Esp32RmtDriverBuilder;
/// #
/// # let peripherals = Peripherals::take().unwrap();
/// # let led_pin = peripherals.pins.gpio27;
/// # let channel = peripherals.rmt.channel0;
///
/// // WS2812B timing parameters.
/// const WS2812_T0H_NS: Duration = Duration::from_nanos(400);
/// const WS2812_T0L_NS: Duration = Duration::from_nanos(850);
/// const WS2812_T1H_NS: Duration = Duration::from_nanos(800);
/// const WS2812_T1L_NS: Duration = Duration::from_nanos(450);
///
/// let driver_config = TransmitConfig::new()
/// .clock_divider(1); // Required parameter.
/// let tx_driver = TxRmtDriver::new(channel, led_pin, &driver_config).unwrap();
/// let driver = Ws2812Esp32RmtDriverBuilder::new_with_rmt_driver(tx_driver).unwrap()
/// .encoder_duration(&WS2812_T0H_NS, &WS2812_T0L_NS, &WS2812_T1H_NS, &WS2812_T1L_NS).unwrap()
/// .build().unwrap();
/// ```
pub struct Ws2812Esp32RmtDriverBuilder<'d> {
/// TxRMT driver.
tx: TxRmtDriver<'d>,
/// `u8`-to-`rmt_item32_t` Encoder
encoder: Option<Ws2812Esp32RmtItemEncoder>,
}

impl<'d> Ws2812Esp32RmtDriverBuilder<'d> {
/// Creates a new `Ws2812Esp32RmtDriverBuilder`.
pub fn new<C: RmtChannel>(
channel: impl Peripheral<P = C> + 'd,
pin: impl Peripheral<P = impl OutputPin> + 'd,
) -> Result<Self, Ws2812Esp32RmtDriverError> {
let config = TransmitConfig::new().clock_divider(1);
let tx = TxRmtDriver::new(channel, pin, &config)?;

Self::new_with_rmt_driver(tx)
}

/// Creates a new `Ws2812Esp32RmtDriverBuilder` with `TxRmtDriver`.
pub fn new_with_rmt_driver(tx: TxRmtDriver<'d>) -> Result<Self, Ws2812Esp32RmtDriverError> {
Ok(Self { tx, encoder: None })
}

/// Sets the encoder duration times.
///
/// # Arguments
///
/// * `clock_hz` - The clock frequency.
/// * `t0h` - T0H duration time (0 code, high voltage time)
/// * `t0l` - T0L duration time (0 code, low voltage time)
/// * `t1h` - T1H duration time (1 code, high voltage time)
/// * `t1l` - T1L duration time (1 code, low voltage time)
///
/// # Errors
///
/// Returns an error if the encoder initialization failed.
pub fn encoder_duration(
mut self,
t0h: &Duration,
t0l: &Duration,
t1h: &Duration,
t1l: &Duration,
) -> Result<Self, Ws2812Esp32RmtDriverError> {
let clock_hz = self.tx.counter_clock()?;
self.encoder = Some(Ws2812Esp32RmtItemEncoder::new(
clock_hz, t0h, t0l, t1h, t1l,
)?);
Ok(self)
}

/// Builds the `Ws2812Esp32RmtDriver`.
pub fn build(self) -> Result<Ws2812Esp32RmtDriver<'d>, Ws2812Esp32RmtDriverError> {
let encoder = if let Some(encoder) = self.encoder {
encoder
} else {
let clock_hz = self.tx.counter_clock()?;
Ws2812Esp32RmtItemEncoder::new(
clock_hz,
&WS2812_T0H_NS,
&WS2812_T0L_NS,
&WS2812_T1H_NS,
&WS2812_T1L_NS,
)?
};

Ok(Ws2812Esp32RmtDriver {
tx: self.tx,
encoder,
#[cfg(not(target_vendor = "espressif"))]
pixel_data: None,
#[cfg(not(target_vendor = "espressif"))]
phantom: Default::default(),
})
}
}

/// WS2812 ESP32 RMT driver wrapper.
///
/// # Examples
Expand Down Expand Up @@ -167,7 +279,6 @@ pub struct Ws2812Esp32RmtDriver<'d> {
/// TxRMT driver.
tx: TxRmtDriver<'d>,
/// `u8`-to-`rmt_item32_t` Encoder
#[cfg(target_vendor = "espressif")]
encoder: Ws2812Esp32RmtItemEncoder,

/// Pixel binary array to be written
Expand All @@ -194,23 +305,7 @@ impl<'d> Ws2812Esp32RmtDriver<'d> {
channel: impl Peripheral<P = C> + 'd,
pin: impl Peripheral<P = impl OutputPin> + 'd,
) -> Result<Self, Ws2812Esp32RmtDriverError> {
#[cfg(target_vendor = "espressif")]
{
let config = TransmitConfig::new().clock_divider(1);
let tx = TxRmtDriver::new(channel, pin, &config)?;

Self::new_with_rmt_driver(tx)
}
#[cfg(not(target_vendor = "espressif"))] // Mock implement
{
let config = TransmitConfig::new();
let tx = TxRmtDriver::new(channel, pin, &config)?;
Ok(Self {
tx,
pixel_data: None,
phantom: Default::default(),
})
}
Ws2812Esp32RmtDriverBuilder::new(channel, pin)?.build()
}

/// Creates a WS2812 ESP32 RMT driver wrapper with `TxRmtDriver`.
Expand Down Expand Up @@ -238,21 +333,7 @@ impl<'d> Ws2812Esp32RmtDriver<'d> {
///
/// Returns an error if the RMT driver initialization failed.
pub fn new_with_rmt_driver(tx: TxRmtDriver<'d>) -> Result<Self, Ws2812Esp32RmtDriverError> {
#[cfg(target_vendor = "espressif")]
{
let clock_hz = tx.counter_clock()?;
let encoder = Ws2812Esp32RmtItemEncoder::new(clock_hz)?;

Ok(Self { tx, encoder })
}
#[cfg(not(target_vendor = "espressif"))] // Mock implement
{
Ok(Self {
tx,
pixel_data: None,
phantom: Default::default(),
})
}
Ws2812Esp32RmtDriverBuilder::new_with_rmt_driver(tx)?.build()
}

/// Writes pixel data from a pixel-byte sequence to the IO pin.
Expand Down
1 change: 1 addition & 0 deletions src/driver/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ pub mod color;
mod esp32_rmt;

pub use esp32_rmt::Ws2812Esp32RmtDriver;
pub use esp32_rmt::Ws2812Esp32RmtDriverBuilder;
pub use esp32_rmt::Ws2812Esp32RmtDriverError;
17 changes: 7 additions & 10 deletions src/lib_embedded_graphics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,16 +115,7 @@ where
pin: impl Peripheral<P = impl OutputPin> + 'd,
) -> Result<Self, Ws2812Esp32RmtDriverError> {
let driver = Ws2812Esp32RmtDriver::<'d>::new(channel, pin)?;
let data = core::iter::repeat(0)
.take(S::pixel_len() * CDev::BPP)
.collect::<Data>();
Ok(Self {
driver,
data,
brightness: u8::MAX,
changed: true,
_phantom: Default::default(),
})
Self::new_with_ws2812_driver(driver)
}

/// Create a new draw target with `TxRmtDriver`.
Expand All @@ -149,6 +140,12 @@ where
/// ```
pub fn new_with_rmt_driver(tx: TxRmtDriver<'d>) -> Result<Self, Ws2812Esp32RmtDriverError> {
let driver = Ws2812Esp32RmtDriver::<'d>::new_with_rmt_driver(tx)?;
Self::new_with_ws2812_driver(driver)
}

pub fn new_with_ws2812_driver(
driver: Ws2812Esp32RmtDriver<'d>,
) -> Result<Self, Ws2812Esp32RmtDriverError> {
let data = core::iter::repeat(0)
.take(S::pixel_len() * CDev::BPP)
.collect::<Data>();
Expand Down
Loading