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}