ws2812_esp32_rmt_driver/
lib_smart_leds.rs

1//! smart-leds driver wrapper API.
2
3use crate::driver::color::{LedPixelColor, LedPixelColorGrb24, LedPixelColorImpl};
4use crate::driver::{Ws2812Esp32RmtDriver, Ws2812Esp32RmtDriverError};
5#[cfg(all(not(feature = "std"), feature = "alloc"))]
6use alloc::vec::Vec;
7use core::marker::PhantomData;
8use esp_idf_hal::rmt::TxRmtDriver;
9#[cfg(feature = "alloc")]
10use smart_leds_trait::SmartLedsWrite;
11use smart_leds_trait::{RGB8, RGBW};
12
13#[cfg(not(target_vendor = "espressif"))]
14use crate::mock::esp_idf_hal;
15use esp_idf_hal::{gpio::OutputPin, peripheral::Peripheral, rmt::RmtChannel};
16
17/// 8-bit RGBW (RGB + white)
18pub type RGBW8 = RGBW<u8, u8>;
19
20impl<
21        const N: usize,
22        const R_ORDER: usize,
23        const G_ORDER: usize,
24        const B_ORDER: usize,
25        const W_ORDER: usize,
26    > From<RGB8> for LedPixelColorImpl<N, R_ORDER, G_ORDER, B_ORDER, W_ORDER>
27{
28    fn from(x: RGB8) -> Self {
29        Self::new_with_rgb(x.r, x.g, x.b)
30    }
31}
32
33impl<
34        const N: usize,
35        const R_ORDER: usize,
36        const G_ORDER: usize,
37        const B_ORDER: usize,
38        const W_ORDER: usize,
39    > From<RGBW8> for LedPixelColorImpl<N, R_ORDER, G_ORDER, B_ORDER, W_ORDER>
40{
41    fn from(x: RGBW8) -> Self {
42        Self::new_with_rgbw(x.r, x.g, x.b, x.a.0)
43    }
44}
45
46/// ws2812-like smart led driver wrapper providing smart-leds API
47///
48/// This is a generalization to handle variants such as SK6812-RGBW 4-color LED.
49/// Use [`Ws2812Esp32Rmt`] for typical RGB LED (WS2812B/SK6812) consisting of 8-bit GRB (total 24-bit pixel).
50///
51/// # Examples
52///
53/// ```
54/// # #[cfg(not(target_vendor = "espressif"))]
55/// # use ws2812_esp32_rmt_driver::mock::esp_idf_hal;
56/// #
57/// use esp_idf_hal::peripherals::Peripherals;
58/// use smart_leds::{SmartLedsWrite, White};
59/// use ws2812_esp32_rmt_driver::{LedPixelEsp32Rmt, RGBW8};
60/// use ws2812_esp32_rmt_driver::driver::color::LedPixelColorGrbw32;
61///
62/// let peripherals = Peripherals::take().unwrap();
63/// let led_pin = peripherals.pins.gpio26;
64/// let channel = peripherals.rmt.channel0;
65/// let mut ws2812 = LedPixelEsp32Rmt::<RGBW8, LedPixelColorGrbw32>::new(channel, led_pin).unwrap();
66///
67/// let pixels = std::iter::repeat(RGBW8 {r: 0, g: 0, b: 0, a: White(30)}).take(25);
68/// ws2812.write(pixels).unwrap();
69/// ```
70pub struct LedPixelEsp32Rmt<'d, CSmart, CDev>
71where
72    CDev: LedPixelColor + From<CSmart>,
73{
74    driver: Ws2812Esp32RmtDriver<'d>,
75    phantom: PhantomData<(CSmart, CDev)>,
76}
77
78impl<'d, CSmart, CDev> LedPixelEsp32Rmt<'d, CSmart, CDev>
79where
80    CDev: LedPixelColor + From<CSmart>,
81{
82    /// Create a new driver wrapper.
83    ///
84    /// `channel` shall be different between different `pin`.
85    pub fn new<C: RmtChannel>(
86        channel: impl Peripheral<P = C> + 'd,
87        pin: impl Peripheral<P = impl OutputPin> + 'd,
88    ) -> Result<Self, Ws2812Esp32RmtDriverError> {
89        Self::new_with_ws2812_driver(Ws2812Esp32RmtDriver::<'d>::new(channel, pin)?)
90    }
91
92    /// Create a new driver wrapper with `TxRmtDriver`.
93    ///
94    /// The clock divider must be set to 1 for the `driver` configuration.
95    ///
96    /// ```
97    /// # #[cfg(not(target_vendor = "espressif"))]
98    /// # use ws2812_esp32_rmt_driver::mock::esp_idf_hal;
99    /// #
100    /// # use esp_idf_hal::peripherals::Peripherals;
101    /// # use esp_idf_hal::rmt::config::TransmitConfig;
102    /// # use esp_idf_hal::rmt::TxRmtDriver;
103    /// #
104    /// # let peripherals = Peripherals::take().unwrap();
105    /// # let led_pin = peripherals.pins.gpio27;
106    /// # let channel = peripherals.rmt.channel0;
107    /// #
108    /// let driver_config = TransmitConfig::new()
109    ///     .clock_divider(1); // Required parameter.
110    /// let driver = TxRmtDriver::new(channel, led_pin, &driver_config).unwrap();
111    /// ```
112    pub fn new_with_rmt_driver(tx: TxRmtDriver<'d>) -> Result<Self, Ws2812Esp32RmtDriverError> {
113        Self::new_with_ws2812_driver(Ws2812Esp32RmtDriver::<'d>::new_with_rmt_driver(tx)?)
114    }
115
116    /// Create a new driver wrapper with `Ws2812Esp32RmtDriver`.
117    pub fn new_with_ws2812_driver(
118        driver: Ws2812Esp32RmtDriver<'d>,
119    ) -> Result<Self, Ws2812Esp32RmtDriverError> {
120        Ok(Self {
121            driver,
122            phantom: Default::default(),
123        })
124    }
125}
126
127impl<
128        'd,
129        CSmart,
130        const N: usize,
131        const R_ORDER: usize,
132        const G_ORDER: usize,
133        const B_ORDER: usize,
134        const W_ORDER: usize,
135    > LedPixelEsp32Rmt<'d, CSmart, LedPixelColorImpl<N, R_ORDER, G_ORDER, B_ORDER, W_ORDER>>
136where
137    LedPixelColorImpl<N, R_ORDER, G_ORDER, B_ORDER, W_ORDER>: From<CSmart>,
138{
139    /// Writes pixel data from a color sequence to the driver without data copy
140    ///
141    /// # Errors
142    ///
143    /// Returns an error if an RMT driver error occurred.
144    pub fn write_nocopy<T, I>(&mut self, iterator: T) -> Result<(), Ws2812Esp32RmtDriverError>
145    where
146        T: IntoIterator<Item = I>,
147        I: Into<CSmart>,
148        <T as IntoIterator>::IntoIter: Send,
149    {
150        self.driver
151            .write_blocking(iterator.into_iter().flat_map(|color| {
152                let c =
153                    LedPixelColorImpl::<N, R_ORDER, G_ORDER, B_ORDER, W_ORDER>::from(color.into());
154                c.0
155            }))?;
156        Ok(())
157    }
158}
159
160#[cfg(feature = "alloc")]
161impl<'d, CSmart, CDev> SmartLedsWrite for LedPixelEsp32Rmt<'d, CSmart, CDev>
162where
163    CDev: LedPixelColor + From<CSmart>,
164{
165    type Error = Ws2812Esp32RmtDriverError;
166    type Color = CSmart;
167
168    /// Writes pixel data from a color sequence to the driver
169    ///
170    /// # Errors
171    ///
172    /// Returns an error if an RMT driver error occurred.
173    fn write<T, I>(&mut self, iterator: T) -> Result<(), Self::Error>
174    where
175        T: IntoIterator<Item = I>,
176        I: Into<Self::Color>,
177    {
178        let pixel_data = iterator.into_iter().fold(Vec::new(), |mut vec, color| {
179            vec.extend_from_slice(CDev::from(color.into()).as_ref());
180            vec
181        });
182        self.driver.write_blocking(pixel_data.into_iter())?;
183        Ok(())
184    }
185}
186
187/// 8-bit GRB (total 24-bit pixel) LED driver wrapper providing smart-leds API,
188/// Typical RGB LED (WS2812B/SK6812) driver wrapper providing smart-leds API
189///
190/// # Examples
191///
192/// ```
193/// # #[cfg(not(target_vendor = "espressif"))]
194/// # use ws2812_esp32_rmt_driver::mock::esp_idf_hal;
195/// #
196/// use esp_idf_hal::peripherals::Peripherals;
197/// use smart_leds::{RGB8, SmartLedsWrite};
198/// use ws2812_esp32_rmt_driver::Ws2812Esp32Rmt;
199///
200/// let peripherals = Peripherals::take().unwrap();
201/// let led_pin = peripherals.pins.gpio27;
202/// let channel = peripherals.rmt.channel0;
203/// let mut ws2812 = Ws2812Esp32Rmt::new(channel, led_pin).unwrap();
204///
205/// let pixels = std::iter::repeat(RGB8::new(30, 0, 0)).take(25);
206/// ws2812.write(pixels).unwrap();
207/// ```
208///
209/// The LED colors may flicker randomly when using Wi-Fi or Bluetooth with Ws2812 LEDs.
210/// This issue can be resolved by:
211///
212/// - Separating Wi-Fi/Bluetooth processing from LED control onto different cores.
213/// - Use multiple memory blocks (memory symbols) in the RMT driver.
214///
215/// To do the second option, prepare `TxRmtDriver` yourself and
216/// initialize with [`Self::new_with_rmt_driver`] as shown below.
217///
218/// ```
219/// # #[cfg(not(target_vendor = "espressif"))]
220/// # use ws2812_esp32_rmt_driver::mock::esp_idf_hal;
221/// #
222/// # use esp_idf_hal::peripherals::Peripherals;
223/// # use esp_idf_hal::rmt::config::TransmitConfig;
224/// # use esp_idf_hal::rmt::TxRmtDriver;
225/// # use smart_leds::{RGB8, SmartLedsWrite};
226/// # use ws2812_esp32_rmt_driver::Ws2812Esp32Rmt;
227/// #
228/// # let peripherals = Peripherals::take().unwrap();
229/// # let led_pin = peripherals.pins.gpio27;
230/// # let channel = peripherals.rmt.channel0;
231/// #
232/// let driver_config = TransmitConfig::new()
233///     .clock_divider(1)  // Required parameter.
234///     .mem_block_num(2); // Increase the number depending on your code.
235/// let driver = TxRmtDriver::new(channel, led_pin, &driver_config).unwrap();
236///
237/// let mut ws2812 = Ws2812Esp32Rmt::new_with_rmt_driver(driver).unwrap();
238/// #
239/// # let pixels = std::iter::repeat(RGB8::new(30, 0, 0)).take(25);
240/// # ws2812.write(pixels).unwrap();
241/// ```
242pub type Ws2812Esp32Rmt<'d> = LedPixelEsp32Rmt<'d, RGB8, LedPixelColorGrb24>;
243
244#[cfg(test)]
245mod test {
246    use super::*;
247    use crate::mock::esp_idf_hal::peripherals::Peripherals;
248
249    #[test]
250    fn test_ws2812_esp32_rmt_smart_leds() {
251        let sample_data = [RGB8::new(0x00, 0x01, 0x02), RGB8::new(0x03, 0x04, 0x05)];
252        let expected_values: [u8; 6] = [0x01, 0x00, 0x02, 0x04, 0x03, 0x05];
253
254        let peripherals = Peripherals::take().unwrap();
255        let led_pin = peripherals.pins.gpio0;
256        let channel = peripherals.rmt.channel0;
257
258        let mut ws2812 = Ws2812Esp32Rmt::new(channel, led_pin).unwrap();
259        ws2812.write(sample_data.iter().cloned()).unwrap();
260        assert_eq!(ws2812.driver.pixel_data.unwrap(), &expected_values);
261    }
262}