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}