1#![cfg_attr(
2 feature = "doc-images",
3 doc = ::embed_doc_image::embed_image!(
4 "led_strip_simple",
5 "docs/assets/led_strip_simple.png"
6 ),
7 doc = ::embed_doc_image::embed_image!(
8 "led_strip_animated",
9 "docs/assets/led_strip_animated.png"
10 )
11)]
12pub use device_envoy_core::led_strip::*;
125pub mod led_strip_generated;
126
127#[doc(hidden)]
131pub struct LedStripEsp<const N: usize, const MAX_FRAMES: usize> {
132 command_signal: &'static LedStripCommandSignal<N, MAX_FRAMES>,
133}
134
135impl<const N: usize, const MAX_FRAMES: usize> LedStripEsp<N, MAX_FRAMES> {
136 #[doc(hidden)]
137 pub const fn new_static() -> LedStripStatic<N, MAX_FRAMES> {
138 LedStripStatic::new_static()
139 }
140
141 #[doc(hidden)]
142 pub fn new(led_strip_static: &'static LedStripStatic<N, MAX_FRAMES>) -> Self {
143 Self {
144 command_signal: led_strip_static.command_signal(),
145 }
146 }
147
148 #[doc(hidden)]
150 pub fn __command_signal(&self) -> &'static LedStripCommandSignal<N, MAX_FRAMES> {
151 self.command_signal
152 }
153}
154
155#[derive(Clone, Copy, Debug, Eq, PartialEq)]
158pub enum Engine {
159 Rmt,
161 Spi,
163}
164
165impl Default for Engine {
166 fn default() -> Self {
167 Self::Rmt
168 }
169}
170
171#[doc(hidden)]
174pub const CURRENT_DEFAULT: Current = Current::Milliamps(250);
177
178#[cfg(target_os = "none")]
183use embassy_futures::select::{select, Either};
184#[cfg(target_os = "none")]
185use embassy_time::Timer;
186
187#[cfg(target_os = "none")]
188use esp_hal::gpio::Level;
189#[cfg(target_os = "none")]
190use esp_hal::rmt::{Channel, PulseCode, Tx};
191
192#[cfg(target_os = "none")]
196const BIT0: PulseCode = PulseCode::new(Level::High, 8, Level::Low, 17);
197#[cfg(target_os = "none")]
198const BIT1: PulseCode = PulseCode::new(Level::High, 16, Level::Low, 9);
199
200#[cfg(target_os = "none")]
209#[doc(hidden)]
212pub struct RmtWs2812<'d, const LEDS: usize, const PULSES: usize> {
213 channel: Option<Channel<'d, esp_hal::Blocking, Tx>>,
214 pulse_buf: [PulseCode; PULSES],
215}
216
217#[cfg(target_os = "none")]
218impl<'d, const LEDS: usize, const PULSES: usize> RmtWs2812<'d, LEDS, PULSES> {
219 #[must_use]
224 pub fn new(channel: Channel<'d, esp_hal::Blocking, Tx>) -> Self {
225 assert_eq!(
226 PULSES,
227 LEDS * 24 + 1,
228 "PULSES must equal LEDS * 24 + 1; this is enforced by led_strip!"
229 );
230 Self {
231 channel: Some(channel),
232 pulse_buf: [PulseCode::end_marker(); PULSES],
233 }
234 }
235
236 pub fn write(&mut self, frame: &Frame1d<LEDS>) -> Result<(), WritingError> {
241 for (led_index, pixel) in frame.iter().enumerate() {
243 let grb: u32 = ((pixel.g as u32) << 16) | ((pixel.r as u32) << 8) | (pixel.b as u32);
244 for bit_index in 0..24 {
245 let bit = (grb >> (23 - bit_index)) & 1;
246 self.pulse_buf[led_index * 24 + bit_index] = if bit == 1 { BIT1 } else { BIT0 };
247 }
248 }
249 self.pulse_buf[LEDS * 24] = PulseCode::end_marker();
252
253 let channel = self.channel.take().ok_or(WritingError::ChannelMissing)?;
254 let transfer = channel
255 .transmit(&self.pulse_buf)
256 .map_err(|_| WritingError::TransmitStart)?;
257 match transfer.wait() {
258 Ok(channel) => {
259 self.channel = Some(channel);
260 Ok(())
261 }
262 Err((err, channel)) => {
263 self.channel = Some(channel);
264 Err(WritingError::Transmit(err))
265 }
266 }
267 }
268}
269
270#[cfg(target_os = "none")]
271#[doc(hidden)]
272#[derive(Debug)]
274pub enum WritingError {
275 ChannelMissing,
277 TransmitStart,
279 Transmit(esp_hal::rmt::Error),
281}
282
283#[doc(hidden)]
295#[cfg(target_os = "none")]
296pub async fn led_strip_device_loop<
297 'd,
298 const LEDS: usize,
299 const PULSES: usize,
300 const MAX_FRAMES: usize,
301>(
302 mut driver: RmtWs2812<'d, LEDS, PULSES>,
303 command_signal: &'static LedStripCommandSignal<LEDS, MAX_FRAMES>,
304 combo_table: &'static [u8; 256],
305) -> ! {
306 let _ = driver.write(&Frame1d::new());
308
309 let mut pending: Option<Command<LEDS, MAX_FRAMES>> = None;
312
313 loop {
314 let command = match pending.take() {
315 Some(cmd) => cmd,
316 None => command_signal.wait().await,
317 };
318
319 match command {
320 Command::DisplayStatic(mut frame) => {
321 apply_correction(&mut frame, combo_table);
322 let _ = driver.write(&frame);
323 }
325 Command::Animate(sequence) => {
326 'animate: loop {
328 for (mut frame, duration) in sequence.iter().cloned() {
329 apply_correction(&mut frame, combo_table);
330 let _ = driver.write(&frame);
331 match select(Timer::after(duration), command_signal.wait()).await {
332 Either::First(_) => {
333 }
335 Either::Second(new_command) => {
336 pending = Some(new_command);
339 break 'animate;
340 }
341 }
342 }
343 if let Some(new_command) = command_signal.try_take() {
346 pending = Some(new_command);
347 break 'animate;
348 }
349 }
350 }
351 }
352 }
353}
354
355#[doc = include_str!("docs/current_limiting_and_gamma.md")]
395#[doc(hidden)]
400#[macro_export]
401macro_rules! led_strip {
402 ($($tt:tt)*) => { $crate::__led_strip_entry! { $($tt)* } };
403}
404
405#[doc(hidden)]
407#[macro_export]
408macro_rules! __led_strip_entry {
409 (
410 $name:ident {
411 $($before:tt)*
412 led2d: { $($led2d_fields:tt)* }
413 $($after:tt)*
414 }
415 ) => {
416 compile_error!("led_strip! is 1D-only. Use led2d! for panel generation.");
417 };
418 (
419 $name:ident {
420 $($fields:tt)*
421 }
422 ) => {
423 $crate::__led_strip_collect_fields!{
424 name = $name,
425 pin = [],
426 len = [],
427 max_current = [],
428 engine = [],
429 gamma = [],
430 max_frames = [],
431 reset_us = [],
432 fields = [$($fields)*],
433 }
434 };
435}
436
437#[cfg(target_os = "none")]
438#[doc(inline)]
439pub use led_strip;
440
441#[doc(hidden)]
442#[macro_export]
443macro_rules! __led_strip_collect_fields {
444 (
445 name = $name:ident,
446 pin = [$pin:ident],
447 len = [$len:expr],
448 max_current = [$($max_current:expr)?],
449 engine = [$($engine:tt)?],
450 gamma = [$($gamma:expr)?],
451 max_frames = [$($max_frames:expr)?],
452 reset_us = [$($reset_us:expr)?],
453 fields = [],
454 ) => {
455 $crate::__led_strip_dispatch_engine!(
456 $name,
457 $pin,
458 $len,
459 $crate::__led_strip_max_current_or_default!([$($max_current)?]),
460 [$($engine)?],
461 [$($gamma)?],
462 [$($max_frames)?],
463 [$($reset_us)?],
464 );
465 };
466 (
467 name = $name:ident,
468 pin = [],
469 len = [$($len:expr)?],
470 max_current = [$($max_current:expr)?],
471 engine = [$($engine:tt)?],
472 gamma = [$($gamma:expr)?],
473 max_frames = [$($max_frames:expr)?],
474 reset_us = [$($reset_us:expr)?],
475 fields = [],
476 ) => {
477 compile_error!("led_strip! missing required `pin` field");
478 };
479 (
480 name = $name:ident,
481 pin = [$pin:ident],
482 len = [],
483 max_current = [$($max_current:expr)?],
484 engine = [$($engine:tt)?],
485 gamma = [$($gamma:expr)?],
486 max_frames = [$($max_frames:expr)?],
487 reset_us = [$($reset_us:expr)?],
488 fields = [],
489 ) => {
490 compile_error!("led_strip! missing required `len` field");
491 };
492 (
493 name = $name:ident,
494 pin = [],
495 len = [$($len:expr)?],
496 max_current = [$($max_current:expr)?],
497 engine = [$($engine:tt)?],
498 gamma = [$($gamma:expr)?],
499 max_frames = [$($max_frames:expr)?],
500 reset_us = [$($reset_us:expr)?],
501 fields = [pin: $pin:ident $(, $($rest:tt)*)?],
502 ) => {
503 $crate::__led_strip_collect_fields!{
504 name = $name,
505 pin = [$pin],
506 len = [$($len)?],
507 max_current = [$($max_current)?],
508 engine = [$($engine)?],
509 gamma = [$($gamma)?],
510 max_frames = [$($max_frames)?],
511 reset_us = [$($reset_us)?],
512 fields = [$($($rest)*)?],
513 }
514 };
515 (
516 name = $name:ident,
517 pin = [$already_pin:ident],
518 len = [$($len:expr)?],
519 max_current = [$($max_current:expr)?],
520 engine = [$($engine:tt)?],
521 gamma = [$($gamma:expr)?],
522 max_frames = [$($max_frames:expr)?],
523 reset_us = [$($reset_us:expr)?],
524 fields = [pin: $pin:ident $(, $($rest:tt)*)?],
525 ) => {
526 compile_error!("led_strip! duplicate `pin` field");
527 };
528 (
529 name = $name:ident,
530 pin = [$($pin:ident)?],
531 len = [],
532 max_current = [$($max_current:expr)?],
533 engine = [$($engine:tt)?],
534 gamma = [$($gamma:expr)?],
535 max_frames = [$($max_frames:expr)?],
536 reset_us = [$($reset_us:expr)?],
537 fields = [len: $len:expr $(, $($rest:tt)*)?],
538 ) => {
539 $crate::__led_strip_collect_fields!{
540 name = $name,
541 pin = [$($pin)?],
542 len = [$len],
543 max_current = [$($max_current)?],
544 engine = [$($engine)?],
545 gamma = [$($gamma)?],
546 max_frames = [$($max_frames)?],
547 reset_us = [$($reset_us)?],
548 fields = [$($($rest)*)?],
549 }
550 };
551 (
552 name = $name:ident,
553 pin = [$($pin:ident)?],
554 len = [$already_len:expr],
555 max_current = [$($max_current:expr)?],
556 engine = [$($engine:tt)?],
557 gamma = [$($gamma:expr)?],
558 max_frames = [$($max_frames:expr)?],
559 reset_us = [$($reset_us:expr)?],
560 fields = [len: $len:expr $(, $($rest:tt)*)?],
561 ) => {
562 compile_error!("led_strip! duplicate `len` field");
563 };
564 (
565 name = $name:ident,
566 pin = [$($pin:ident)?],
567 len = [$($len:expr)?],
568 max_current = [],
569 engine = [$($engine:tt)?],
570 gamma = [$($gamma:expr)?],
571 max_frames = [$($max_frames:expr)?],
572 reset_us = [$($reset_us:expr)?],
573 fields = [max_current: $max_current:expr $(, $($rest:tt)*)?],
574 ) => {
575 $crate::__led_strip_collect_fields!{
576 name = $name,
577 pin = [$($pin)?],
578 len = [$($len)?],
579 max_current = [$max_current],
580 engine = [$($engine)?],
581 gamma = [$($gamma)?],
582 max_frames = [$($max_frames)?],
583 reset_us = [$($reset_us)?],
584 fields = [$($($rest)*)?],
585 }
586 };
587 (
588 name = $name:ident,
589 pin = [$($pin:ident)?],
590 len = [$($len:expr)?],
591 max_current = [$already_max_current:expr],
592 engine = [$($engine:tt)?],
593 gamma = [$($gamma:expr)?],
594 max_frames = [$($max_frames:expr)?],
595 reset_us = [$($reset_us:expr)?],
596 fields = [max_current: $max_current:expr $(, $($rest:tt)*)?],
597 ) => {
598 compile_error!("led_strip! duplicate `max_current` field");
599 };
600 (
601 name = $name:ident,
602 pin = [$($pin:ident)?],
603 len = [$($len:expr)?],
604 max_current = [$($max_current:expr)?],
605 engine = [],
606 gamma = [$($gamma:expr)?],
607 max_frames = [$($max_frames:expr)?],
608 reset_us = [$($reset_us:expr)?],
609 fields = [engine: Engine::Spi $(, $($rest:tt)*)?],
610 ) => {
611 $crate::__led_strip_collect_fields!{
612 name = $name,
613 pin = [$($pin)?],
614 len = [$($len)?],
615 max_current = [$($max_current)?],
616 engine = [Spi],
617 gamma = [$($gamma)?],
618 max_frames = [$($max_frames)?],
619 reset_us = [$($reset_us)?],
620 fields = [$($($rest)*)?],
621 }
622 };
623 (
624 name = $name:ident,
625 pin = [$($pin:ident)?],
626 len = [$($len:expr)?],
627 max_current = [$($max_current:expr)?],
628 engine = [],
629 gamma = [$($gamma:expr)?],
630 max_frames = [$($max_frames:expr)?],
631 reset_us = [$($reset_us:expr)?],
632 fields = [engine: $crate::led_strip::Engine::Spi $(, $($rest:tt)*)?],
633 ) => {
634 $crate::__led_strip_collect_fields!{
635 name = $name,
636 pin = [$($pin)?],
637 len = [$($len)?],
638 max_current = [$($max_current)?],
639 engine = [Spi],
640 gamma = [$($gamma)?],
641 max_frames = [$($max_frames)?],
642 reset_us = [$($reset_us)?],
643 fields = [$($($rest)*)?],
644 }
645 };
646 (
647 name = $name:ident,
648 pin = [$($pin:ident)?],
649 len = [$($len:expr)?],
650 max_current = [$($max_current:expr)?],
651 engine = [],
652 gamma = [$($gamma:expr)?],
653 max_frames = [$($max_frames:expr)?],
654 reset_us = [$($reset_us:expr)?],
655 fields = [engine: device_envoy_esp::led_strip::Engine::Spi $(, $($rest:tt)*)?],
656 ) => {
657 $crate::__led_strip_collect_fields!{
658 name = $name,
659 pin = [$($pin)?],
660 len = [$($len)?],
661 max_current = [$($max_current)?],
662 engine = [Spi],
663 gamma = [$($gamma)?],
664 max_frames = [$($max_frames)?],
665 reset_us = [$($reset_us)?],
666 fields = [$($($rest)*)?],
667 }
668 };
669 (
670 name = $name:ident,
671 pin = [$($pin:ident)?],
672 len = [$($len:expr)?],
673 max_current = [$($max_current:expr)?],
674 engine = [],
675 gamma = [$($gamma:expr)?],
676 max_frames = [$($max_frames:expr)?],
677 reset_us = [$($reset_us:expr)?],
678 fields = [engine: Engine::Rmt $(, $($rest:tt)*)?],
679 ) => {
680 $crate::__led_strip_collect_fields!{
681 name = $name,
682 pin = [$($pin)?],
683 len = [$($len)?],
684 max_current = [$($max_current)?],
685 engine = [Rmt],
686 gamma = [$($gamma)?],
687 max_frames = [$($max_frames)?],
688 reset_us = [$($reset_us)?],
689 fields = [$($($rest)*)?],
690 }
691 };
692 (
693 name = $name:ident,
694 pin = [$($pin:ident)?],
695 len = [$($len:expr)?],
696 max_current = [$($max_current:expr)?],
697 engine = [],
698 gamma = [$($gamma:expr)?],
699 max_frames = [$($max_frames:expr)?],
700 reset_us = [$($reset_us:expr)?],
701 fields = [engine: $crate::led_strip::Engine::Rmt $(, $($rest:tt)*)?],
702 ) => {
703 $crate::__led_strip_collect_fields!{
704 name = $name,
705 pin = [$($pin)?],
706 len = [$($len)?],
707 max_current = [$($max_current)?],
708 engine = [Rmt],
709 gamma = [$($gamma)?],
710 max_frames = [$($max_frames)?],
711 reset_us = [$($reset_us)?],
712 fields = [$($($rest)*)?],
713 }
714 };
715 (
716 name = $name:ident,
717 pin = [$($pin:ident)?],
718 len = [$($len:expr)?],
719 max_current = [$($max_current:expr)?],
720 engine = [],
721 gamma = [$($gamma:expr)?],
722 max_frames = [$($max_frames:expr)?],
723 reset_us = [$($reset_us:expr)?],
724 fields = [engine: device_envoy_esp::led_strip::Engine::Rmt $(, $($rest:tt)*)?],
725 ) => {
726 $crate::__led_strip_collect_fields!{
727 name = $name,
728 pin = [$($pin)?],
729 len = [$($len)?],
730 max_current = [$($max_current)?],
731 engine = [Rmt],
732 gamma = [$($gamma)?],
733 max_frames = [$($max_frames)?],
734 reset_us = [$($reset_us)?],
735 fields = [$($($rest)*)?],
736 }
737 };
738 (
739 name = $name:ident,
740 pin = [$($pin:ident)?],
741 len = [$($len:expr)?],
742 max_current = [$($max_current:expr)?],
743 engine = [$already_engine:tt],
744 gamma = [$($gamma:expr)?],
745 max_frames = [$($max_frames:expr)?],
746 reset_us = [$($reset_us:expr)?],
747 fields = [engine: $ignored:path $(, $($rest:tt)*)?],
748 ) => {
749 compile_error!("led_strip! duplicate `engine` field");
750 };
751 (
752 name = $name:ident,
753 pin = [$($pin:ident)?],
754 len = [$($len:expr)?],
755 max_current = [$($max_current:expr)?],
756 engine = [],
757 gamma = [$($gamma:expr)?],
758 max_frames = [$($max_frames:expr)?],
759 reset_us = [$($reset_us:expr)?],
760 fields = [engine: $ignored:path $(, $($rest:tt)*)?],
761 ) => {
762 compile_error!("led_strip! engine must be Engine::Rmt or Engine::Spi");
763 };
764 (
765 name = $name:ident,
766 pin = [$($pin:ident)?],
767 len = [$($len:expr)?],
768 max_current = [$($max_current:expr)?],
769 engine = [$($engine:tt)?],
770 gamma = [],
771 max_frames = [$($max_frames:expr)?],
772 reset_us = [$($reset_us:expr)?],
773 fields = [gamma: $gamma:expr $(, $($rest:tt)*)?],
774 ) => {
775 $crate::__led_strip_collect_fields!{
776 name = $name,
777 pin = [$($pin)?],
778 len = [$($len)?],
779 max_current = [$($max_current)?],
780 engine = [$($engine)?],
781 gamma = [$gamma],
782 max_frames = [$($max_frames)?],
783 reset_us = [$($reset_us)?],
784 fields = [$($($rest)*)?],
785 }
786 };
787 (
788 name = $name:ident,
789 pin = [$($pin:ident)?],
790 len = [$($len:expr)?],
791 max_current = [$($max_current:expr)?],
792 engine = [$($engine:tt)?],
793 gamma = [$already_gamma:expr],
794 max_frames = [$($max_frames:expr)?],
795 reset_us = [$($reset_us:expr)?],
796 fields = [gamma: $gamma:expr $(, $($rest:tt)*)?],
797 ) => {
798 compile_error!("led_strip! duplicate `gamma` field");
799 };
800 (
801 name = $name:ident,
802 pin = [$($pin:ident)?],
803 len = [$($len:expr)?],
804 max_current = [$($max_current:expr)?],
805 engine = [$($engine:tt)?],
806 gamma = [$($gamma:expr)?],
807 max_frames = [],
808 reset_us = [$($reset_us:expr)?],
809 fields = [max_frames: $max_frames:expr $(, $($rest:tt)*)?],
810 ) => {
811 $crate::__led_strip_collect_fields!{
812 name = $name,
813 pin = [$($pin)?],
814 len = [$($len)?],
815 max_current = [$($max_current)?],
816 engine = [$($engine)?],
817 gamma = [$($gamma)?],
818 max_frames = [$max_frames],
819 reset_us = [$($reset_us)?],
820 fields = [$($($rest)*)?],
821 }
822 };
823 (
824 name = $name:ident,
825 pin = [$($pin:ident)?],
826 len = [$($len:expr)?],
827 max_current = [$($max_current:expr)?],
828 engine = [$($engine:tt)?],
829 gamma = [$($gamma:expr)?],
830 max_frames = [$already_max_frames:expr],
831 reset_us = [$($reset_us:expr)?],
832 fields = [max_frames: $max_frames:expr $(, $($rest:tt)*)?],
833 ) => {
834 compile_error!("led_strip! duplicate `max_frames` field");
835 };
836 (
837 name = $name:ident,
838 pin = [$($pin:ident)?],
839 len = [$($len:expr)?],
840 max_current = [$($max_current:expr)?],
841 engine = [$($engine:tt)?],
842 gamma = [$($gamma:expr)?],
843 max_frames = [$($max_frames:expr)?],
844 reset_us = [],
845 fields = [reset_us: $reset_us:expr $(, $($rest:tt)*)?],
846 ) => {
847 $crate::__led_strip_collect_fields!{
848 name = $name,
849 pin = [$($pin)?],
850 len = [$($len)?],
851 max_current = [$($max_current)?],
852 engine = [$($engine)?],
853 gamma = [$($gamma)?],
854 max_frames = [$($max_frames)?],
855 reset_us = [$reset_us],
856 fields = [$($($rest)*)?],
857 }
858 };
859 (
860 name = $name:ident,
861 pin = [$($pin:ident)?],
862 len = [$($len:expr)?],
863 max_current = [$($max_current:expr)?],
864 engine = [$($engine:tt)?],
865 gamma = [$($gamma:expr)?],
866 max_frames = [$($max_frames:expr)?],
867 reset_us = [$already_reset_us:expr],
868 fields = [reset_us: $reset_us:expr $(, $($rest:tt)*)?],
869 ) => {
870 compile_error!("led_strip! duplicate `reset_us` field");
871 };
872 (
873 name = $name:ident,
874 pin = [$($pin:ident)?],
875 len = [$($len:expr)?],
876 max_current = [$($max_current:expr)?],
877 engine = [$($engine:tt)?],
878 gamma = [$($gamma:expr)?],
879 max_frames = [$($max_frames:expr)?],
880 reset_us = [$($reset_us:expr)?],
881 fields = [$field:ident : $value:expr $(, $($rest:tt)*)?],
882 ) => {
883 compile_error!(
884 "led_strip! unknown field; expected `pin`, `len`, `max_current`, `engine`, `gamma`, `max_frames`, or `reset_us`"
885 );
886 };
887}
888
889#[doc(hidden)]
890#[macro_export]
891macro_rules! __led_strip_max_current_or_default {
892 ([$max_current:expr]) => {
893 $max_current
894 };
895 ([]) => {
896 $crate::led_strip::CURRENT_DEFAULT
897 };
898}
899
900#[doc(hidden)]
907#[macro_export]
908macro_rules! __led_strip_inner {
909 (
910 $name:ident,
911 $pin:ident,
912 $len:expr,
913 $max_current:expr,
914 [$($gamma:expr)?],
915 [$($max_frames:expr)?],
916 [$($led2d_layout:expr)?],
917 [$($led2d_font:expr)?],
918 ) => {
919 $crate::__led_strip_impl!{
920 name = $name,
921 pin = $pin,
922 len = $len,
923 max_current = $max_current,
924 gamma = $crate::__led_strip_first_or_default!(
925 [$($gamma)?],
926 $crate::led_strip::GAMMA_DEFAULT
927 ),
928 max_frames = $crate::__led_strip_first_or_default!(
929 [$($max_frames)?],
930 $crate::led_strip::MAX_FRAMES_DEFAULT
931 ),
932 led2d_layout = [$($led2d_layout)?],
933 led2d_font = [$($led2d_font)?],
934 }
935 };
936}
937
938#[doc(hidden)]
942#[macro_export]
943macro_rules! __led_strip_parse_options {
944 (
945 name = $name:ident,
946 pin = $pin:ident,
947 len = $len:expr,
948 max_current = $max_current:expr,
949 engine = [$($engine:tt)*],
950 gamma = [$($gamma:expr)?],
951 max_frames = [$($max_frames:expr)?],
952 reset_us = [$($reset_us:expr)?],
953 ) => {
954 $crate::__led_strip_dispatch_engine! {
955 $name,
956 $pin,
957 $len,
958 $max_current,
959 [$($engine)*],
960 [$($gamma)?],
961 [$($max_frames)?],
962 [$($reset_us)?],
963 }
964 };
965 (
966 name = $name:ident,
967 pin = $pin:ident,
968 len = $len:expr,
969 max_current = $max_current:expr,
970 engine = [],
971 gamma = [$($gamma:expr)?],
972 max_frames = [$($max_frames:expr)?],
973 reset_us = [$($reset_us:expr)?],
974 engine: Engine::Spi
975 $(, $($tail:tt)*)?
976 ) => {
977 $crate::__led_strip_parse_options! {
978 name = $name,
979 pin = $pin,
980 len = $len,
981 max_current = $max_current,
982 engine = [Spi],
983 gamma = [$($gamma)?],
984 max_frames = [$($max_frames)?],
985 reset_us = [$($reset_us)?],
986 $($($tail)*)?
987 }
988 };
989 (
990 name = $name:ident,
991 pin = $pin:ident,
992 len = $len:expr,
993 max_current = $max_current:expr,
994 engine = [],
995 gamma = [$($gamma:expr)?],
996 max_frames = [$($max_frames:expr)?],
997 reset_us = [$($reset_us:expr)?],
998 engine: $crate::led_strip::Engine::Spi
999 $(, $($tail:tt)*)?
1000 ) => {
1001 $crate::__led_strip_parse_options! {
1002 name = $name,
1003 pin = $pin,
1004 len = $len,
1005 max_current = $max_current,
1006 engine = [Spi],
1007 gamma = [$($gamma)?],
1008 max_frames = [$($max_frames)?],
1009 reset_us = [$($reset_us)?],
1010 $($($tail)*)?
1011 }
1012 };
1013 (
1014 name = $name:ident,
1015 pin = $pin:ident,
1016 len = $len:expr,
1017 max_current = $max_current:expr,
1018 engine = [],
1019 gamma = [$($gamma:expr)?],
1020 max_frames = [$($max_frames:expr)?],
1021 reset_us = [$($reset_us:expr)?],
1022 engine: device_envoy_esp::led_strip::Engine::Spi
1023 $(, $($tail:tt)*)?
1024 ) => {
1025 $crate::__led_strip_parse_options! {
1026 name = $name,
1027 pin = $pin,
1028 len = $len,
1029 max_current = $max_current,
1030 engine = [Spi],
1031 gamma = [$($gamma)?],
1032 max_frames = [$($max_frames)?],
1033 reset_us = [$($reset_us)?],
1034 $($($tail)*)?
1035 }
1036 };
1037 (
1038 name = $name:ident,
1039 pin = $pin:ident,
1040 len = $len:expr,
1041 max_current = $max_current:expr,
1042 engine = [],
1043 gamma = [$($gamma:expr)?],
1044 max_frames = [$($max_frames:expr)?],
1045 reset_us = [$($reset_us:expr)?],
1046 engine: Engine::Rmt
1047 $(, $($tail:tt)*)?
1048 ) => {
1049 $crate::__led_strip_parse_options! {
1050 name = $name,
1051 pin = $pin,
1052 len = $len,
1053 max_current = $max_current,
1054 engine = [Rmt],
1055 gamma = [$($gamma)?],
1056 max_frames = [$($max_frames)?],
1057 reset_us = [$($reset_us)?],
1058 $($($tail)*)?
1059 }
1060 };
1061 (
1062 name = $name:ident,
1063 pin = $pin:ident,
1064 len = $len:expr,
1065 max_current = $max_current:expr,
1066 engine = [],
1067 gamma = [$($gamma:expr)?],
1068 max_frames = [$($max_frames:expr)?],
1069 reset_us = [$($reset_us:expr)?],
1070 engine: $crate::led_strip::Engine::Rmt
1071 $(, $($tail:tt)*)?
1072 ) => {
1073 $crate::__led_strip_parse_options! {
1074 name = $name,
1075 pin = $pin,
1076 len = $len,
1077 max_current = $max_current,
1078 engine = [Rmt],
1079 gamma = [$($gamma)?],
1080 max_frames = [$($max_frames)?],
1081 reset_us = [$($reset_us)?],
1082 $($($tail)*)?
1083 }
1084 };
1085 (
1086 name = $name:ident,
1087 pin = $pin:ident,
1088 len = $len:expr,
1089 max_current = $max_current:expr,
1090 engine = [],
1091 gamma = [$($gamma:expr)?],
1092 max_frames = [$($max_frames:expr)?],
1093 reset_us = [$($reset_us:expr)?],
1094 engine: device_envoy_esp::led_strip::Engine::Rmt
1095 $(, $($tail:tt)*)?
1096 ) => {
1097 $crate::__led_strip_parse_options! {
1098 name = $name,
1099 pin = $pin,
1100 len = $len,
1101 max_current = $max_current,
1102 engine = [Rmt],
1103 gamma = [$($gamma)?],
1104 max_frames = [$($max_frames)?],
1105 reset_us = [$($reset_us)?],
1106 $($($tail)*)?
1107 }
1108 };
1109 (
1110 name = $name:ident,
1111 pin = $pin:ident,
1112 len = $len:expr,
1113 max_current = $max_current:expr,
1114 engine = [$($engine:tt)+],
1115 gamma = [$($gamma:expr)?],
1116 max_frames = [$($max_frames:expr)?],
1117 reset_us = [$($reset_us:expr)?],
1118 engine: $ignored:path
1119 $(, $($tail:tt)*)?
1120 ) => {
1121 compile_error!("led_strip! duplicate `engine` field");
1122 };
1123 (
1124 name = $name:ident,
1125 pin = $pin:ident,
1126 len = $len:expr,
1127 max_current = $max_current:expr,
1128 engine = [],
1129 gamma = [$($gamma:expr)?],
1130 max_frames = [$($max_frames:expr)?],
1131 reset_us = [$($reset_us:expr)?],
1132 engine: $ignored:path
1133 $(, $($tail:tt)*)?
1134 ) => {
1135 compile_error!("led_strip! engine must be Engine::Rmt or Engine::Spi");
1136 };
1137 (
1138 name = $name:ident,
1139 pin = $pin:ident,
1140 len = $len:expr,
1141 max_current = $max_current:expr,
1142 engine = [$($engine:tt)*],
1143 gamma = [],
1144 max_frames = [$($max_frames:expr)?],
1145 reset_us = [$($reset_us:expr)?],
1146 gamma: $gamma:expr
1147 $(, $($tail:tt)*)?
1148 ) => {
1149 $crate::__led_strip_parse_options! {
1150 name = $name,
1151 pin = $pin,
1152 len = $len,
1153 max_current = $max_current,
1154 engine = [$($engine)*],
1155 gamma = [$gamma],
1156 max_frames = [$($max_frames)?],
1157 reset_us = [$($reset_us)?],
1158 $($($tail)*)?
1159 }
1160 };
1161 (
1162 name = $name:ident,
1163 pin = $pin:ident,
1164 len = $len:expr,
1165 max_current = $max_current:expr,
1166 engine = [$($engine:tt)*],
1167 gamma = [$already_gamma:expr],
1168 max_frames = [$($max_frames:expr)?],
1169 reset_us = [$($reset_us:expr)?],
1170 gamma: $gamma:expr
1171 $(, $($tail:tt)*)?
1172 ) => {
1173 compile_error!("led_strip! duplicate `gamma` field");
1174 };
1175 (
1176 name = $name:ident,
1177 pin = $pin:ident,
1178 len = $len:expr,
1179 max_current = $max_current:expr,
1180 engine = [$($engine:tt)*],
1181 gamma = [$($gamma:expr)?],
1182 max_frames = [],
1183 reset_us = [$($reset_us:expr)?],
1184 max_frames: $max_frames:expr
1185 $(, $($tail:tt)*)?
1186 ) => {
1187 $crate::__led_strip_parse_options! {
1188 name = $name,
1189 pin = $pin,
1190 len = $len,
1191 max_current = $max_current,
1192 engine = [$($engine)*],
1193 gamma = [$($gamma)?],
1194 max_frames = [$max_frames],
1195 reset_us = [$($reset_us)?],
1196 $($($tail)*)?
1197 }
1198 };
1199 (
1200 name = $name:ident,
1201 pin = $pin:ident,
1202 len = $len:expr,
1203 max_current = $max_current:expr,
1204 engine = [$($engine:tt)*],
1205 gamma = [$($gamma:expr)?],
1206 max_frames = [$already_max_frames:expr],
1207 reset_us = [$($reset_us:expr)?],
1208 max_frames: $max_frames:expr
1209 $(, $($tail:tt)*)?
1210 ) => {
1211 compile_error!("led_strip! duplicate `max_frames` field");
1212 };
1213 (
1214 name = $name:ident,
1215 pin = $pin:ident,
1216 len = $len:expr,
1217 max_current = $max_current:expr,
1218 engine = [$($engine:tt)*],
1219 gamma = [$($gamma:expr)?],
1220 max_frames = [$($max_frames:expr)?],
1221 reset_us = [],
1222 reset_us: $reset_us:expr
1223 $(, $($tail:tt)*)?
1224 ) => {
1225 $crate::__led_strip_parse_options! {
1226 name = $name,
1227 pin = $pin,
1228 len = $len,
1229 max_current = $max_current,
1230 engine = [$($engine)*],
1231 gamma = [$($gamma)?],
1232 max_frames = [$($max_frames)?],
1233 reset_us = [$reset_us],
1234 $($($tail)*)?
1235 }
1236 };
1237 (
1238 name = $name:ident,
1239 pin = $pin:ident,
1240 len = $len:expr,
1241 max_current = $max_current:expr,
1242 engine = [$($engine:tt)*],
1243 gamma = [$($gamma:expr)?],
1244 max_frames = [$($max_frames:expr)?],
1245 reset_us = [$already_reset_us:expr],
1246 reset_us: $reset_us:expr
1247 $(, $($tail:tt)*)?
1248 ) => {
1249 compile_error!("led_strip! duplicate `reset_us` field");
1250 };
1251 (
1252 name = $name:ident,
1253 pin = $pin:ident,
1254 len = $len:expr,
1255 max_current = $max_current:expr,
1256 engine = [$($engine:tt)*],
1257 gamma = [$($gamma:expr)?],
1258 max_frames = [$($max_frames:expr)?],
1259 reset_us = [$($reset_us:expr)?],
1260 $field:ident : $value:expr
1261 $(, $($tail:tt)*)?
1262 ) => {
1263 compile_error!("led_strip! unknown field; expected `engine`, `gamma`, `max_frames`, or `reset_us`");
1264 };
1265}
1266
1267#[doc(hidden)]
1271#[macro_export]
1272macro_rules! __led_strip_dispatch_engine {
1273 (
1274 $name:ident,
1275 $pin:ident,
1276 $len:expr,
1277 $max_current:expr,
1278 [Spi],
1279 [$($gamma:expr)?],
1280 [$($max_frames:expr)?],
1281 [$($reset_us:expr)?],
1282 ) => {
1283 $crate::led_strip::spi::__led_strip_spi_inner!{
1284 $name,
1285 $pin,
1286 $len,
1287 $max_current,
1288 [$($gamma)?],
1289 [$($max_frames)?],
1290 [$($reset_us)?],
1291 [],
1292 [],
1293 }
1294 };
1295 (
1296 $name:ident,
1297 $pin:ident,
1298 $len:expr,
1299 $max_current:expr,
1300 [Rmt],
1301 [$($gamma:expr)?],
1302 [$($max_frames:expr)?],
1303 [$reset_us:expr],
1304 ) => {
1305 compile_error!("led_strip! `reset_us` is only supported with `engine: Engine::Spi`");
1306 };
1307 (
1308 $name:ident,
1309 $pin:ident,
1310 $len:expr,
1311 $max_current:expr,
1312 [Rmt],
1313 [$($gamma:expr)?],
1314 [$($max_frames:expr)?],
1315 [],
1316 ) => {
1317 $crate::led_strip::__led_strip_inner!{
1318 $name,
1319 $pin,
1320 $len,
1321 $max_current,
1322 [$($gamma)?],
1323 [$($max_frames)?],
1324 [],
1325 [],
1326 }
1327 };
1328 (
1329 $name:ident,
1330 $pin:ident,
1331 $len:expr,
1332 $max_current:expr,
1333 [],
1334 [$($gamma:expr)?],
1335 [$($max_frames:expr)?],
1336 [$reset_us:expr],
1337 ) => {
1338 compile_error!("led_strip! `reset_us` is only supported with `engine: Engine::Spi`");
1339 };
1340 (
1341 $name:ident,
1342 $pin:ident,
1343 $len:expr,
1344 $max_current:expr,
1345 [],
1346 [$($gamma:expr)?],
1347 [$($max_frames:expr)?],
1348 [],
1349 ) => {
1350 $crate::led_strip::__led_strip_inner!{
1351 $name,
1352 $pin,
1353 $len,
1354 $max_current,
1355 [$($gamma)?],
1356 [$($max_frames)?],
1357 [],
1358 [],
1359 }
1360 };
1361}
1362
1363#[doc(hidden)]
1367#[macro_export]
1368macro_rules! __led_strip_first_or_default {
1369 ([$value:expr], $_default:expr) => {
1370 $value
1371 };
1372 ([], $default:expr) => {
1373 $default
1374 };
1375}
1376
1377#[doc(hidden)]
1379#[macro_export]
1380macro_rules! __led2d_strip_methods {
1381 ($_leds:expr, $max_frames:expr, [$led_layout:expr], [$font:expr]) => {
1382 pub const FONT: $crate::led2d::Led2dFont = $font;
1384 pub const WIDTH: usize = $led_layout.width();
1386 pub const HEIGHT: usize = $led_layout.height();
1388 pub const SIZE: $crate::led2d::Size =
1390 $crate::led2d::Frame2d::<{ $led_layout.width() }, { $led_layout.height() }>::SIZE;
1391 pub const TOP_LEFT: $crate::led2d::Point =
1393 $crate::led2d::Frame2d::<{ $led_layout.width() }, { $led_layout.height() }>::TOP_LEFT;
1394 pub const TOP_RIGHT: $crate::led2d::Point =
1396 $crate::led2d::Frame2d::<{ $led_layout.width() }, { $led_layout.height() }>::TOP_RIGHT;
1397 pub const BOTTOM_LEFT: $crate::led2d::Point = $crate::led2d::Frame2d::<
1399 { $led_layout.width() },
1400 { $led_layout.height() },
1401 >::BOTTOM_LEFT;
1402 pub const BOTTOM_RIGHT: $crate::led2d::Point = $crate::led2d::Frame2d::<
1404 { $led_layout.width() },
1405 { $led_layout.height() },
1406 >::BOTTOM_RIGHT;
1407 };
1408 ($_leds:expr, $_max_frames:expr, [], []) => {};
1409}
1410
1411#[doc(hidden)]
1413#[macro_export]
1414macro_rules! __led2d_strip_trait_impl {
1415 ($name:ident, [$led_layout:expr], [$font:expr], $max_frames:expr) => {
1416 impl $crate::led2d::Led2d<{ $led_layout.width() }, { $led_layout.height() }>
1417 for &'static $name
1418 {
1419 const WIDTH: usize = $name::WIDTH;
1420 const HEIGHT: usize = $name::HEIGHT;
1421 const LEN: usize = $name::LEN;
1422 const SIZE: $crate::led2d::Size = $name::SIZE;
1423 const TOP_LEFT: $crate::led2d::Point = $name::TOP_LEFT;
1424 const TOP_RIGHT: $crate::led2d::Point = $name::TOP_RIGHT;
1425 const BOTTOM_LEFT: $crate::led2d::Point = $name::BOTTOM_LEFT;
1426 const BOTTOM_RIGHT: $crate::led2d::Point = $name::BOTTOM_RIGHT;
1427 const MAX_FRAMES: usize = $max_frames;
1428 const MAX_BRIGHTNESS: u8 = $name::MAX_BRIGHTNESS;
1429 const FONT: $crate::led2d::Led2dFont = $font;
1430
1431 fn write_frame(
1432 &self,
1433 frame2d: $crate::led2d::Frame2d<{ $led_layout.width() }, { $led_layout.height() }>,
1434 ) {
1435 let led2d = $crate::led2d::Led2dEsp::new(*self, &$led_layout);
1436 $crate::led2d::Led2dStripBacked::write_frame(&led2d, frame2d);
1437 }
1438
1439 fn animate<I>(&self, frames: I)
1440 where
1441 I: IntoIterator,
1442 I::Item: ::core::borrow::Borrow<(
1443 $crate::led2d::Frame2d<{ $led_layout.width() }, { $led_layout.height() }>,
1444 embassy_time::Duration,
1445 )>,
1446 {
1447 let led2d = $crate::led2d::Led2dEsp::new(*self, &$led_layout);
1448 $crate::led2d::Led2dStripBacked::animate(&led2d, frames);
1449 }
1450 }
1451 };
1452 ($_name:ident, [], [], $_max_frames:expr) => {};
1453}
1454
1455#[doc(hidden)]
1458#[macro_export]
1459macro_rules! __led_strip_impl {
1460 (
1461 name = $name:ident,
1462 pin = $pin:ident,
1463 len = $len:expr,
1464 max_current = $max_current:expr,
1465 gamma = $gamma:expr,
1466 max_frames = $max_frames:expr,
1467 led2d_layout = [$($led2d_layout:expr)?],
1468 led2d_font = [$($led2d_font:expr)?],
1469 ) => {
1470 ::paste::paste! {
1471 mod [<$name:snake _consts>] {
1476 pub const LEDS: usize = $len;
1478 pub const PULSES: usize = LEDS * 24 + 1;
1480 pub const WORST_CASE_MA: u32 = LEDS as u32 * 60;
1482 }
1483
1484 static [<$name:snake:upper _STATIC>]:
1488 $crate::led_strip::LedStripStatic<
1489 { [<$name:snake _consts>]::LEDS },
1490 { $max_frames },
1491 > = $crate::led_strip::LedStripEsp::new_static();
1492
1493 pub struct $name {
1497 inner: $crate::led_strip::LedStripEsp<
1498 { [<$name:snake _consts>]::LEDS },
1499 { $max_frames },
1500 >,
1501 }
1502
1503 impl $name {
1504 pub const LEN: usize = [<$name:snake _consts>]::LEDS;
1506
1507 pub const MAX_FRAMES: usize = $max_frames;
1509
1510 pub const MAX_BRIGHTNESS: u8 = <$crate::led_strip::Current>::max_brightness(
1513 $max_current,
1514 [<$name:snake _consts>]::WORST_CASE_MA,
1515 );
1516
1517 pub const COMBO_TABLE: [u8; 256] =
1519 $crate::led_strip::generate_combo_table($gamma, Self::MAX_BRIGHTNESS);
1520
1521 $crate::__led2d_strip_methods!(
1522 { [<$name:snake _consts>]::LEDS },
1523 { $max_frames },
1524 [$($led2d_layout)?],
1525 [$($led2d_font)?]
1526 );
1527
1528 pub fn new(
1533 pin: $crate::esp_hal::peripherals::$pin<'static>,
1534 channel_creator: impl ::esp_hal::rmt::TxChannelCreator<
1535 'static,
1536 ::esp_hal::Blocking,
1537 >,
1538 spawner: ::embassy_executor::Spawner,
1539 ) -> $crate::Result<&'static Self> {
1540 use ::static_cell::StaticCell;
1541
1542 static INSTANCE: StaticCell<$name> = StaticCell::new();
1543 static COMBO: StaticCell<[u8; 256]> = StaticCell::new();
1544
1545 let combo_ref: &'static [u8; 256] =
1546 COMBO.init(<$name>::COMBO_TABLE);
1547
1548 let channel = channel_creator
1549 .configure_tx(pin, $crate::init_and_start::rmt::ws2812_tx_config())
1550 .map_err($crate::Error::Rmt)?;
1551
1552 let driver =
1553 $crate::led_strip::RmtWs2812::<
1554 { [<$name:snake _consts>]::LEDS },
1555 { [<$name:snake _consts>]::PULSES },
1556 >::new(channel);
1557
1558 let strip_static: &'static _ = &[<$name:snake:upper _STATIC>];
1559
1560 spawner
1561 .spawn([<$name:snake _device_task>](driver, strip_static, combo_ref))
1562 .map_err($crate::Error::TaskSpawn)?;
1563
1564 let instance = INSTANCE.init($name {
1565 inner: $crate::led_strip::LedStripEsp::new(strip_static),
1566 });
1567 Ok(instance)
1568 }
1569 }
1570
1571 impl $crate::led_strip::LedStrip<{ [<$name:snake _consts>]::LEDS }> for $name {
1572 const MAX_FRAMES: usize = $max_frames;
1573 const MAX_BRIGHTNESS: u8 = Self::MAX_BRIGHTNESS;
1574
1575 fn write_frame(
1576 &self,
1577 frame: $crate::led_strip::Frame1d<{ [<$name:snake _consts>]::LEDS }>,
1578 ) {
1579 $crate::led_strip::__write_frame(self.inner.__command_signal(), frame);
1580 }
1581
1582 fn animate<I>(&self, frames: I)
1583 where
1584 I: IntoIterator,
1585 I::Item: ::core::borrow::Borrow<(
1586 $crate::led_strip::Frame1d<{ [<$name:snake _consts>]::LEDS }>,
1587 embassy_time::Duration,
1588 )>,
1589 {
1590 $crate::led_strip::__animate(self.inner.__command_signal(), frames);
1591 }
1592 }
1593
1594 $crate::__led2d_strip_trait_impl!(
1595 $name,
1596 [$($led2d_layout)?],
1597 [$($led2d_font)?],
1598 $max_frames
1599 );
1600
1601 #[::embassy_executor::task]
1605 async fn [<$name:snake _device_task>](
1606 driver: $crate::led_strip::RmtWs2812<
1607 'static,
1608 { [<$name:snake _consts>]::LEDS },
1609 { [<$name:snake _consts>]::PULSES },
1610 >,
1611 strip_static: &'static $crate::led_strip::LedStripStatic<
1612 { [<$name:snake _consts>]::LEDS },
1613 { $max_frames },
1614 >,
1615 combo_table: &'static [u8; 256],
1616 ) {
1617 $crate::led_strip::led_strip_device_loop(
1618 driver,
1619 strip_static.command_signal(),
1620 combo_table,
1621 )
1622 .await;
1623 }
1624 }
1625 };
1626}
1627
1628#[cfg(target_os = "none")]
1633#[doc(hidden)]
1634pub mod spi;
1635
1636pub use crate::{
1638 __led2d_strip_methods, __led2d_strip_trait_impl, __led_strip_dispatch_engine,
1639 __led_strip_first_or_default, __led_strip_impl, __led_strip_inner, __led_strip_parse_options,
1640};