esp_hal_smartled/lib.rs
1//! Allows for the use of an RMT output channel on the ESP32 family to easily drive smart RGB LEDs. This is a driver for the [smart-leds](https://crates.io/crates/smart-leds) framework and allows using the utility functions from this crate as well as higher-level libraries based on smart-leds.
2//!
3//! Different from [ws2812-esp32-rmt-driver](https://crates.io/crates/ws2812-esp32-rmt-driver), which is based on the unofficial `esp-idf` SDK, this crate is based on the official no-std [esp-hal](https://github.com/esp-rs/esp-hal).
4//!
5//! This driver uses either the blocking RMT API, or the async one, depending on the given RMT channel.
6//! The [`SmartLedsWrite`] trait (or [`SmartLedsWriteAsync`]) is implemented for [`RmtSmartLeds`] with the corresponding channel mode.
7//!
8//! ## Example
9//!
10//! ```rust,ignore
11//! let rmt = Rmt::new(peripherals.RMT, Rate::from_mhz(80)).unwrap();
12//!
13//! let mut led = RmtSmartLeds::<{ buffer_size::<RGB8>(1) }, _, RGB8, color_order::Rgb, Ws2812Timing>::new(
14//! rmt.channel0, peripherals.GPIO2
15//! );
16//!
17//! led.write(brightness([RED], 10)).unwrap();
18//! ```
19//!
20//! ## Usage overview
21//!
22//! The [`RmtSmartLeds`] struct implements [`SmartLedsWrite`] or [`SmartLedsWriteAsync`]
23//! and can be used to send color data to connected LEDs.
24//! To initialize a [`RmtSmartLeds`], use [`RmtSmartLeds::new`],
25//! which takes an RMT channel and a [`PeripheralOutput`].
26//! If you want to reuse the channel afterwards, you can use [`esp_hal::rmt::ChannelCreator::reborrow`] to create a shorter-lived derived channel.
27//! [`RmtSmartLeds`] is configured at compile-time to support a variety of LED configurations. See the documentation for [`RmtSmartLeds`] for more info.
28//!
29//! ## Feature Flags
30#![doc = document_features::document_features!()]
31#![doc(html_logo_url = "https://avatars.githubusercontent.com/u/46717278")]
32#![deny(missing_docs)]
33#![no_std]
34
35use core::{fmt::Debug, marker::PhantomData};
36
37pub use color_order::ColorOrder;
38use esp_hal::{
39 Async, Blocking, DriverMode,
40 gpio::{Level, interconnect::PeripheralOutput},
41 rmt::{
42 Channel, ConfigError as RmtConfigError, Error as RmtError, PulseCode, Tx, TxChannelConfig,
43 TxChannelCreator,
44 },
45 time::Rate,
46};
47use num_traits::Unsigned;
48use smart_leds_trait::{CctWhite, RGB, RGBCCT, RGBW, SmartLedsWrite, SmartLedsWriteAsync, White};
49
50/// Defines the timing for a certain smart LED type.
51///
52/// All common smart LEDs are controlled by sending PWM-like pulses, in two different configurations for high and low.
53/// The required timings (and tolerances) can be found in the relevant datasheets.
54///
55/// Provided timings: [`SK68XX_TIMING`], [`WS2812B_TIMING`], [`WS2811_TIMING`], [`WS2812_TIMING`].
56#[derive(Clone, Copy)]
57pub struct Timing {
58 /// Low time for zero pulse, in nanoseconds.
59 pub time_0_low: u16,
60 /// High time for zero pulse, in nanoseconds.
61 pub time_0_high: u16,
62 /// Low time for one pulse, in nanoseconds.
63 pub time_1_low: u16,
64 /// High time for one pulse, in nanoseconds.
65 pub time_1_high: u16,
66 /// Time for the reset that is required in between transmissions, in microseconds.
67 /// Depending on the rmt's frequency, it can have a maximum of ~800us at 80mhz,
68 /// ~2000us at 32mhz, etc.
69 ///
70 /// The calculation is: max_reset_pulse_us = 0xfffe / rmt_freq_mhz.
71 /// 0xfffe(= 0x7fff * 2) is the max amount of ticks in a single [`PulseCode`].
72 pub reset_us: u16,
73}
74
75impl Timing {
76 /// Returns this timing configuration with the provided reset time.
77 /// Different revisions of the same led might have different reset times,
78 /// this is the reason behind this function.
79 #[must_use]
80 pub const fn with_reset_us(mut self, reset_us: u16) -> Self {
81 self.reset_us = reset_us;
82 self
83 }
84}
85
86const SK68XX_CODE_PERIOD: u16 = 1200;
87const SK68XX_TIME_0_HIGH: u16 = 320;
88const SK68XX_TIME_1_HIGH: u16 = 640;
89/// Timing for the SK68 collection of LEDs.
90pub const SK68XX_TIMING: Timing = Timing {
91 time_0_high: SK68XX_TIME_0_HIGH,
92 time_0_low: SK68XX_CODE_PERIOD - SK68XX_TIME_0_HIGH,
93 time_1_high: SK68XX_TIME_1_HIGH,
94 time_1_low: SK68XX_CODE_PERIOD - SK68XX_TIME_1_HIGH,
95 reset_us: 300,
96};
97
98/// Timing for the WS2812B LEDs.
99pub const WS2812B_TIMING: Timing = Timing {
100 time_0_high: 400,
101 time_0_low: 800,
102 time_1_high: 850,
103 time_1_low: 450,
104 reset_us: 300,
105};
106
107/// Timing for the WS2812 LEDs.
108pub const WS2812_TIMING: Timing = Timing {
109 time_0_high: 350,
110 time_0_low: 700,
111 time_1_high: 800,
112 time_1_low: 600,
113 reset_us: 80,
114};
115
116/// Timing for the WS2811 driver ICs, low-speed mode.
117pub const WS2811_LOW_SPEED_TIMING: Timing = Timing {
118 time_0_high: 500,
119 time_0_low: 2000,
120 time_1_high: 1200,
121 time_1_low: 1300,
122 reset_us: 300,
123};
124
125/// Timing for the WS2811 driver ICs, high-speed mode.
126pub const WS2811_TIMING: Timing = Timing {
127 time_0_high: WS2811_LOW_SPEED_TIMING.time_0_high / 2,
128 time_0_low: WS2811_LOW_SPEED_TIMING.time_0_low / 2,
129 time_1_high: WS2811_LOW_SPEED_TIMING.time_1_high / 2,
130 time_1_low: WS2811_LOW_SPEED_TIMING.time_1_low / 2,
131 reset_us: 300,
132};
133
134/// All types of errors that can happen during the conversion and transmission
135/// of LED commands.
136#[derive(Debug, Clone, Copy)]
137#[cfg_attr(feature = "defmt", derive(defmt::Format))]
138#[non_exhaustive]
139pub enum AdapterError {
140 /// Raised in the event that the RMT buffer is not large enough.
141 ///
142 /// This almost always points to an issue with the `BUFFER_SIZE` parameter of [`RmtSmartLeds`].
143 /// You should create this parameter using [`buffer_size`], passing in the desired number of LEDs that will be controlled.
144 BufferSizeExceeded,
145 /// Raised if something goes wrong in the transmission. This contains the inner HAL error ([`RmtError`]).
146 TransmissionError(RmtError),
147 /// Can be returned by flush after a failed write for example
148 BufferNotReady,
149}
150
151impl From<RmtError> for AdapterError {
152 fn from(value: RmtError) -> Self {
153 Self::TransmissionError(value)
154 }
155}
156
157/// Utility trait that retrieves metadata about all [`smart_leds_trait`] color types.
158pub trait Color {
159 /// The maximum channel number this color supports.
160 ///
161 /// - For RGB (or any permutation thereof), this is 3.
162 /// - For RGBW, this is 4.
163 /// - For RGBCCT, this is 5.
164 /// - For CCT, this is 2.
165 ///
166 /// Note that this channel count is used by users of [`ColorOrder`] to limit the channel number that’s passed into [`ColorOrder::get_channel_data`].
167 const CHANNELS: u8;
168
169 /// Type of a single channel of this color. Usually [`u8`], but [`u16`] is also used for some LEDs.
170 type ChannelType: Unsigned + Into<usize>;
171}
172
173impl<T> Color for RGB<T>
174where
175 T: Unsigned + Into<usize>,
176{
177 const CHANNELS: u8 = 3;
178 type ChannelType = T;
179}
180
181impl<T> Color for RGBW<T>
182where
183 T: Unsigned + Into<usize>,
184{
185 const CHANNELS: u8 = 4;
186 type ChannelType = T;
187}
188
189impl<T> Color for RGBCCT<T>
190where
191 T: Unsigned + Into<usize>,
192{
193 const CHANNELS: u8 = 5;
194 type ChannelType = T;
195}
196
197impl<T> Color for White<T>
198where
199 T: Unsigned + Into<usize>,
200{
201 const CHANNELS: u8 = 1;
202 type ChannelType = T;
203}
204
205impl<T> Color for CctWhite<T>
206where
207 T: Unsigned + Into<usize>,
208{
209 const CHANNELS: u8 = 2;
210 type ChannelType = T;
211}
212
213/// Calculate the required buffer size for a certain number of LEDs.
214/// This should be used to create the `BUFFER_SIZE` parameter of [`RmtSmartLeds`].
215///
216/// Attempting to use more LEDs that the buffer is configured for will result in
217/// an [`AdapterError::BufferSizeExceeded`] error.
218///
219/// You need to specify the correct color and channel type
220// TODO: As soon as generic expressions are more stabilized, we should be able to do this calculation entirely internally in `RmtSmartLeds`. For now, users have to be careful.
221pub const fn buffer_size<C: Color>(led_count: usize) -> usize {
222 // The size we're assigning here is calculated as following
223 // (
224 // Nr. of LEDs
225 // * channels
226 // * pulses per channel (=bitcount)
227 // ) + 1 additional pulse for the end delimiter + 1 reset
228 led_count * (size_of::<C::ChannelType>() * 8) * C::CHANNELS as usize + 2
229}
230
231/// Common [`ColorOrder`] implementations.
232pub mod color_order {
233 use num_traits::Unsigned;
234 use smart_leds_trait::{RGB, RGBW, White};
235
236 use crate::Color;
237
238 /// Order of colors in the physical LEDs.
239 /// The most common color orders for RGB LEDs are [`Rgb`] (most integrated controllers like WS2812) and [`Grb`].
240 /// Note that discrete ICs have generic channels and are often wired up arbitrarily, so you will have to check which order is correct for your hardware.
241 // Implementations of this should be vacant enums so they can’t be constructed.
242 // This should also be a constant trait once that becomes a stable Rust feature.
243 pub trait ColorOrder<C: Color> {
244 /// Retrieve the output value for the provided channel.
245 /// For instance, if color order is RGB, then the red value will be returned for channel 0,
246 /// the green value for channel 1 and the blue value for channel 2.
247 ///
248 /// The maximum channel number users are allowed to pass in is [`Color::CHANNELS`] minus one.
249 /// If this restriction is not upheld, the implementation may panic.
250 fn get_channel_data(color: &C, channel: u8) -> C::ChannelType;
251 }
252
253 macro_rules! color_order_rgb {
254 ($name:ident => $first:ident, $second:ident, $third:ident) => {
255 #[doc = concat!("[`ColorOrder`] ", stringify!($name), ".")]
256 pub enum $name {}
257 impl<T> ColorOrder<RGB<T>> for $name
258 where
259 T: Copy + Unsigned + Into<usize>,
260 {
261 fn get_channel_data(color: &RGB<T>, channel: u8) -> T {
262 match channel {
263 0 => color.$first,
264 1 => color.$second,
265 2 => color.$third,
266 _ => unreachable!(),
267 }
268 }
269 }
270 };
271 }
272
273 color_order_rgb!(Rgb => r, g, b);
274 color_order_rgb!(Rbg => r, b, g);
275 color_order_rgb!(Grb => g, r, b);
276 color_order_rgb!(Gbr => g, b, r);
277 color_order_rgb!(Brg => b, r, g);
278 color_order_rgb!(Bgr => b, g, r);
279
280 /// [`ColorOrder`] RGBW.
281 pub enum Rgbw {}
282 impl<T> ColorOrder<RGBW<T>> for Rgbw
283 where
284 T: Copy + Unsigned + Into<usize>,
285 {
286 fn get_channel_data(color: &RGBW<T>, channel: u8) -> T {
287 match channel {
288 0 => color.r,
289 1 => color.g,
290 2 => color.b,
291 3 => color.a.0,
292 _ => unreachable!(),
293 }
294 }
295 }
296
297 /// [`ColorOrder`] GRBW.
298 pub enum Grbw {}
299 impl<T> ColorOrder<RGBW<T>> for Grbw
300 where
301 T: Copy + num_traits::sign::Unsigned + Into<usize>,
302 {
303 fn get_channel_data(color: &RGBW<T>, channel: u8) -> T {
304 match channel {
305 0 => color.g,
306 1 => color.r,
307 2 => color.b,
308 3 => color.a.0,
309 _ => unreachable!(),
310 }
311 }
312 }
313
314 /// [`ColorOrder`] for single-channel smart LEDs, where the order is trivial.
315 pub enum SingleChannel {}
316 impl<T> ColorOrder<White<T>> for SingleChannel
317 where
318 T: Copy + Unsigned + Into<usize>,
319 {
320 fn get_channel_data(color: &White<T>, _channel: u8) -> T {
321 color.0
322 }
323 }
324}
325
326/// [`SmartLedsWrite`] driver implementation using the ESP32’s “remote control” (RMT) peripheral for hardware-offloaded, fast control of smart LEDs.
327///
328/// For usage examples and a general overview see [the crate documentation](`crate`).
329///
330/// This type supports many configurations of color order, LED timings, and LED count. For this reason, there are three main type parameters you have to choose:
331/// - The buffer size. This determines how many RMT pulses can be sent by this driver, and allows it to function entirely without heap allocation. It is strongly recommended to use the [`buffer_size`] function with the desired number of LEDs to choose a correct buffer size, otherwise [`SmartLedsWrite::write`] will return [`AdapterError::BufferSizeExceeded`].
332/// - The `Color`.
333/// This determines the color model and number of channels to be sent.
334/// - The [`ColorOrder`].
335/// This determines what order the LED expects the color values in.
336/// - The [`Timing`].
337/// This determines the smart LED type in use; what kind of signal it expects.
338/// Several implementations for common LED types like WS2812 are provided.
339/// Note that many WS2812-like LEDs are at least almost compatible in their timing, even though the datasheets specify different amounts, the other LEDs’ values are within the tolerance range, and even exceeding these, many LEDs continue to work beyond their specified timing range.
340/// It is however recommended to use the corresponding LED type, or implement your own when needed.
341///
342/// When the driver mode is [`Blocking`], this type implements the blocking [`SmartLedsWrite`] interface.
343/// When the driver mode is [`Async`], this type implements the [`SmartLedsWriteAsync`] interface instead.
344/// (You usually don’t need to choose this manually, Rust can deduce it from the passed-in RMT channel.)
345pub struct RmtSmartLeds<'d, const BUFFER_SIZE: usize, Mode, C, Order>
346where
347 Mode: DriverMode,
348 C: Color,
349 Order: ColorOrder<C>,
350{
351 channel: Option<Channel<'d, Mode, Tx>>,
352 rmt_buffer: [PulseCode; BUFFER_SIZE],
353 buffer_valid: bool,
354 zero_pulse: PulseCode,
355 one_pulse: PulseCode,
356 reset_pulse: PulseCode,
357 rmt_freq: Rate,
358 _order: PhantomData<Order>,
359 _color: PhantomData<C>,
360}
361
362/// Returns the pulse code for a zero bit, given the RMT source clock’s speed in MHz.
363fn zero_pulse(t: &Timing, src_clock_mhz: u32) -> Option<PulseCode> {
364 PulseCode::try_new(
365 Level::High,
366 (t.time_0_high as u32 * src_clock_mhz) / 1000,
367 Level::Low,
368 (t.time_0_low as u32 * src_clock_mhz) / 1000,
369 )
370}
371/// Returns the pulse code for a one bit, given the RMT source clock’s speed in MHz.
372fn one_pulse(t: &Timing, src_clock_mhz: u32) -> Option<PulseCode> {
373 PulseCode::try_new(
374 Level::High,
375 (t.time_1_high as u32 * src_clock_mhz) / 1000,
376 Level::Low,
377 (t.time_1_low as u32 * src_clock_mhz) / 1000,
378 )
379}
380
381/// Returns the reset pulse code, given the RMT source clock’s speed in MHz.
382fn reset_pulse(t: &Timing, src_clock_mhz: u32) -> Option<PulseCode> {
383 let reset_half = (t.reset_us / 2) as u32;
384 PulseCode::try_new(
385 Level::Low,
386 reset_half * src_clock_mhz,
387 Level::Low,
388 reset_half * src_clock_mhz,
389 )
390}
391
392/// Error returned when creating the driver
393#[derive(Debug, thiserror::Error)]
394pub enum Error {
395 /// Failed to satisfy the requested timing
396 #[error("could not calculate valid pulses for the provided timing")]
397 Timing,
398 /// RMT configuration error
399 #[error("{_0:?}")]
400 RmtConfig(#[from] RmtConfigError),
401}
402
403impl<'d, const BUFFER_SIZE: usize, Mode, C, Order> RmtSmartLeds<'d, BUFFER_SIZE, Mode, C, Order>
404where
405 Mode: DriverMode,
406 C: Color,
407 Order: ColorOrder<C>,
408{
409 /// Creates a new [`RmtSmartLeds`] that drives the provided output using the given RMT channel.
410 ///
411 /// Note that calling this function usually requires you to specify the desired buffer size, [`ColorOrder`] and [`Timing`].
412 /// See the struct documentation for details.
413 ///
414 /// If you want to reuse the channel afterwards, you can use [`esp_hal::rmt::ChannelCreator::reborrow`] to create a shorter-lived derived channel.
415 ///
416 /// # Errors
417 ///
418 /// If any configuration issue with the RMT [`Channel`] occurs, the error will be returned.
419 pub fn new<Ch, P>(timing: Timing, channel: Ch, pin: P, rmt_freq: Rate) -> Result<Self, Error>
420 where
421 Ch: TxChannelCreator<'d, Mode>,
422 P: PeripheralOutput<'d>,
423 {
424 Self::new_with_memsize(timing, channel, pin, 1, rmt_freq)
425 }
426 /// Creates a new [`RmtSmartLeds`] that drives the provided output using the given RMT channel.
427 ///
428 /// Note that calling this function usually requires you to specify the desired buffer size and [`ColorOrder`].
429 /// See the struct documentation for details.
430 ///
431 /// If you want to reuse the channel afterwards, you can use [`esp_hal::rmt::ChannelCreator::reborrow`] to create a shorter-lived derived channel.
432 ///
433 /// The `memsize` parameter determines how many RMT blocks this adapter will use.
434 /// If you use any value other than 1, other RMT channels will not be available, as their memory blocks will be used up by this driver.
435 /// However, this can allow you to control many more LEDs without issues.
436 ///
437 /// # Errors
438 ///
439 /// If any configuration issue with the RMT [`Channel`] occurs, the error will be returned.
440 pub fn new_with_memsize<Ch, P>(
441 timing: Timing,
442 channel: Ch,
443 pin: P,
444 memsize: u8,
445 rmt_freq: Rate,
446 ) -> Result<Self, Error>
447 where
448 Ch: TxChannelCreator<'d, Mode>,
449 P: PeripheralOutput<'d>,
450 {
451 let config = TxChannelConfig::default()
452 .with_clk_divider(1)
453 .with_idle_output_level(Level::Low)
454 .with_memsize(memsize)
455 .with_carrier_modulation(false)
456 .with_idle_output(true);
457
458 let channel = channel.configure_tx(&config)?.with_pin(pin);
459
460 let (zero_pulse, one_pulse, reset_pulse) =
461 Self::get_timings_for(&timing, rmt_freq).ok_or(Error::Timing)?;
462
463 Ok(Self {
464 channel: Some(channel),
465 rmt_buffer: [PulseCode::end_marker(); BUFFER_SIZE],
466 buffer_valid: false,
467 zero_pulse,
468 one_pulse,
469 reset_pulse,
470 rmt_freq,
471 _order: PhantomData,
472 _color: PhantomData,
473 })
474 }
475
476 /// Returns (zero_pulse, one_pulse, reset_pulse)
477 pub fn get_timings_for(
478 t: &Timing,
479 rmt_freq: Rate,
480 ) -> Option<(PulseCode, PulseCode, PulseCode)> {
481 // convert to the MHz value to simplify nanosecond calculations
482 let src_clock = rmt_freq.as_mhz();
483
484 Some((
485 zero_pulse(t, src_clock)?,
486 one_pulse(t, src_clock)?,
487 reset_pulse(t, src_clock)?,
488 ))
489 }
490
491 /// Modifies the timing for the LED driver.
492 pub fn set_timing(&mut self, t: Timing) -> Result<(), Error> {
493 let (zero_pulse, one_pulse, reset_pulse) =
494 Self::get_timings_for(&t, self.rmt_freq).ok_or(Error::Timing)?;
495 self.zero_pulse = zero_pulse;
496 self.one_pulse = one_pulse;
497 self.reset_pulse = reset_pulse;
498 self.buffer_valid = false;
499
500 Ok(())
501 }
502
503 /// Create and store RMT data from the color information provided.
504 fn create_rmt_data(
505 &mut self,
506 iterator: impl IntoIterator<Item = impl Into<C>>,
507 ) -> Result<(), AdapterError> {
508 self.buffer_valid = false;
509 // We always start from the beginning of the buffer
510 let mut seq_iter = self.rmt_buffer.iter_mut();
511
512 // Add all converted iterator items to the buffer.
513 // This will result in an `BufferSizeExceeded` error in case
514 // the iterator provides more elements than the buffer can take.
515 for item in iterator {
516 convert_colors_to_pulse::<_, Order>(
517 &item.into(),
518 &mut seq_iter,
519 self.zero_pulse,
520 self.one_pulse,
521 )?;
522 }
523
524 // add a reset
525 *seq_iter.next().ok_or(AdapterError::BufferSizeExceeded)? = self.reset_pulse;
526 // Finally, add an end element.
527 *seq_iter.next().ok_or(AdapterError::BufferSizeExceeded)? = PulseCode::end_marker();
528
529 self.buffer_valid = true;
530
531 Ok(())
532 }
533
534 /// Write pixel buffer data at certain LED index.
535 /// Does not actually write data to the RMT peripheral.
536 #[allow(unused)]
537 pub(crate) fn write_pixel_data(
538 &mut self,
539 index: usize,
540 color: impl Into<C>,
541 ) -> Result<(), AdapterError> {
542 let buffer_start_index = index * C::CHANNELS as usize * (size_of::<C::ChannelType>() * 8);
543 let mut buffer_iter = self
544 .rmt_buffer
545 .get_mut(buffer_start_index..)
546 .ok_or(AdapterError::BufferSizeExceeded)?
547 .iter_mut();
548 convert_colors_to_pulse::<_, Order>(
549 &color.into(),
550 &mut buffer_iter,
551 self.zero_pulse,
552 self.one_pulse,
553 )
554 }
555}
556
557impl<'d, const BUFFER_SIZE: usize, C, Order> RmtSmartLeds<'d, BUFFER_SIZE, Blocking, C, Order>
558where
559 C: Color,
560 Order: ColorOrder<C>,
561{
562 /// Transmit existing LED data via the RMT peripheral.
563 pub fn flush(&mut self) -> Result<(), AdapterError> {
564 if !self.buffer_valid {
565 return Err(AdapterError::BufferNotReady);
566 }
567 // Perform the actual RMT operation. We use the u32 values here right away.
568 let channel = self.channel.take().unwrap();
569 // TODO: If the transmit fails, we’re in an unsafe state and future calls to write() will panic.
570 // This is currently unavoidable since transmit consumes the channel on error.
571 // This is a known design flaw in the current RMT API and will be fixed soon.
572 // We should adjust our usage accordingly as soon as possible.
573 match channel
574 .transmit(&self.rmt_buffer)
575 .map_err(|(e, _)| e)?
576 .wait()
577 {
578 Ok(chan) => {
579 self.channel = Some(chan);
580 Ok(())
581 }
582 Err((e, chan)) => {
583 self.channel = Some(chan);
584 Err(AdapterError::TransmissionError(e))
585 }
586 }
587 }
588}
589
590impl<'d, const BUFFER_SIZE: usize, C, Order> SmartLedsWrite
591 for RmtSmartLeds<'d, BUFFER_SIZE, Blocking, C, Order>
592where
593 C: Color,
594 Order: ColorOrder<C>,
595{
596 type Error = AdapterError;
597 type Color = C;
598
599 /// Convert all Color items of the iterator to the RMT format and
600 /// add them to internal buffer, then start a singular RMT operation
601 /// based on that buffer.
602 fn write<T, I>(&mut self, iterator: T) -> Result<(), Self::Error>
603 where
604 T: IntoIterator<Item = I>,
605 I: Into<Self::Color>,
606 {
607 self.create_rmt_data(iterator)?;
608 self.flush()
609 }
610}
611
612impl<'d, const BUFFER_SIZE: usize, C, Order> SmartLedsWriteAsync
613 for RmtSmartLeds<'d, BUFFER_SIZE, Async, C, Order>
614where
615 C: Color,
616 Order: ColorOrder<C>,
617{
618 type Error = AdapterError;
619 type Color = C;
620
621 /// Convert all Color items of the iterator to the RMT format and
622 /// add them to internal buffer, then start a singular RMT operation
623 /// based on that buffer.
624 fn write<T, I>(&mut self, iterator: T) -> impl Future<Output = Result<(), Self::Error>>
625 where
626 T: IntoIterator<Item = I>,
627 I: Into<Self::Color>,
628 {
629 // we split the future into a creation part and a sending part
630 // so we can prepare multiple futures and send/await then all at the same time
631 let res = self.create_rmt_data(iterator);
632
633 async move {
634 res?;
635 // Perform the actual RMT operation. We use the u32 values here right away.
636 self.channel
637 .as_mut()
638 .unwrap()
639 .transmit(&self.rmt_buffer)
640 .await?;
641 Ok(())
642 }
643 }
644}
645
646fn convert_colors_to_pulse<'a, C, Order>(
647 value: &C,
648 mut_iter: &mut impl Iterator<Item = &'a mut PulseCode>,
649 zero_pulse: PulseCode,
650 one_pulse: PulseCode,
651) -> Result<(), AdapterError>
652where
653 C: Color,
654 Order: ColorOrder<C>,
655{
656 for channel in 0..C::CHANNELS {
657 convert_channel_to_pulses(
658 Order::get_channel_data(value, channel),
659 mut_iter,
660 zero_pulse,
661 one_pulse,
662 )?;
663 }
664
665 Ok(())
666}
667
668fn convert_channel_to_pulses<'a, N>(
669 channel_value: N,
670 mut_iter: &mut impl Iterator<Item = &'a mut PulseCode>,
671 zero_pulse: PulseCode,
672 one_pulse: PulseCode,
673) -> Result<(), AdapterError>
674where
675 N: Unsigned + Into<usize>,
676{
677 let channel_value: usize = channel_value.into();
678 for index in (0..size_of::<N>() * 8).rev() {
679 let position = 1 << index;
680 *mut_iter.next().ok_or(AdapterError::BufferSizeExceeded)? = match channel_value & position {
681 0 => zero_pulse,
682 _ => one_pulse,
683 }
684 }
685
686 Ok(())
687}