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