Skip to main content

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}