1#![doc = include_str!("../docs/how_servos_work.md")]
20
21use crate::servo::Servo;
141use core::borrow::Borrow;
142use embassy_futures::select::{Either, select};
143use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
144use embassy_sync::signal::Signal;
145use embassy_time::{Duration, Timer};
146use heapless::Vec;
147
148#[doc(inline)]
149pub use crate::combine;
150pub use crate::servo::servo;
155#[doc(hidden)]
156pub use paste;
157
158pub mod servo_player_generated;
163
164enum PlayerCommand<const MAX_STEPS: usize> {
166 Set {
167 degrees: u16,
168 },
169 Animate {
170 steps: Vec<(u16, Duration), MAX_STEPS>,
171 mode: AtEnd,
172 },
173 Hold,
174 Relax,
175}
176
177#[derive(Clone, Copy, Debug, defmt::Format)]
181pub enum AtEnd {
182 Loop,
184 Hold,
186 Relax,
188}
189
190#[must_use]
204pub const fn linear<const N: usize>(
205 start_degrees: u16,
206 end_degrees: u16,
207 total_duration: Duration,
208) -> [(u16, Duration); N] {
209 assert!(N > 0, "at least one step required");
210 let step_duration = Duration::from_micros(total_duration.as_micros() / (N as u64));
211 let delta = end_degrees as i32 - start_degrees as i32;
212 let denom = if N == 1 { 1 } else { (N - 1) as i32 };
213
214 let mut result = [(0u16, Duration::from_micros(0)); N];
215 let mut step_index = 0;
216 while step_index < N {
217 let degrees = if N == 1 {
218 start_degrees
219 } else {
220 let step_delta = delta * (step_index as i32) / denom;
221 (start_degrees as i32 + step_delta) as u16
222 };
223 result[step_index] = (degrees, step_duration);
224 step_index += 1;
225 }
226 result
227}
228
229#[must_use]
246#[doc(hidden)]
247pub const fn combine<const N1: usize, const N2: usize, const OUT_N: usize>(
248 first: [(u16, Duration); N1],
249 second: [(u16, Duration); N2],
250) -> [(u16, Duration); OUT_N] {
251 assert!(OUT_N == N1 + N2, "OUT_N must equal N1 + N2");
252
253 let mut result = [(0u16, Duration::from_micros(0)); OUT_N];
254 let mut i = 0;
255 while i < N1 {
256 result[i] = first[i];
257 i += 1;
258 }
259 let mut j = 0;
260 while j < N2 {
261 result[N1 + j] = second[j];
262 j += 1;
263 }
264 result
265}
266
267#[doc(hidden)]
281#[macro_export]
282macro_rules! combine {
283 () => {
284 []
285 };
286 ($single:expr) => {
287 $single
288 };
289 ($first:expr, $second:expr) => {{
290 const FIRST: &[(u16, ::embassy_time::Duration)] = &$first;
291 const SECOND: &[(u16, ::embassy_time::Duration)] = &$second;
292 $crate::servo_player::combine::<{FIRST.len()}, {SECOND.len()}, {FIRST.len() + SECOND.len()}>($first, $second)
293 }};
294 ($first:expr, $($rest:expr),+ $(,)?) => {{
295 const FIRST: &[(u16, ::embassy_time::Duration)] = &$first;
296 const REST: &[(u16, ::embassy_time::Duration)] = &$crate::combine!($($rest),+);
297 $crate::servo_player::combine::<{FIRST.len()}, {REST.len()}, {FIRST.len() + REST.len()}>($first, $crate::combine!($($rest),+))
298 }};
299}
300
301#[doc(hidden)]
303pub struct ServoPlayerStatic<const MAX_STEPS: usize> {
305 command: Signal<CriticalSectionRawMutex, PlayerCommand<MAX_STEPS>>,
306}
307
308impl<const MAX_STEPS: usize> ServoPlayerStatic<MAX_STEPS> {
309 #[must_use]
311 pub const fn new_static() -> Self {
312 Self {
313 command: Signal::new(),
314 }
315 }
316
317 fn signal(&self, command: PlayerCommand<MAX_STEPS>) {
318 self.command.signal(command);
319 }
320
321 async fn wait(&self) -> PlayerCommand<MAX_STEPS> {
322 self.command.wait().await
323 }
324}
325
326#[doc(hidden)]
328pub struct ServoPlayer<const MAX_STEPS: usize> {
333 servo_player_static: &'static ServoPlayerStatic<MAX_STEPS>,
334}
335
336impl<const MAX_STEPS: usize> ServoPlayer<MAX_STEPS> {
337 #[must_use]
339 pub const fn new_static() -> ServoPlayerStatic<MAX_STEPS> {
340 ServoPlayerStatic::new_static()
341 }
342
343 #[must_use]
347 pub const fn new(servo_player_static: &'static ServoPlayerStatic<MAX_STEPS>) -> Self {
348 Self {
349 servo_player_static,
350 }
351 }
352
353 pub fn set_degrees(&self, degrees: u16) {
358 self.servo_player_static
359 .signal(PlayerCommand::Set { degrees });
360 }
361
362 pub fn hold(&self) {
367 self.servo_player_static.signal(PlayerCommand::Hold);
368 }
369
370 pub fn relax(&self) {
375 self.servo_player_static.signal(PlayerCommand::Relax);
376 }
377
378 pub fn animate<I>(&self, steps: I, at_end: AtEnd)
386 where
387 I: IntoIterator,
388 I::Item: Borrow<(u16, Duration)>,
389 {
390 assert!(MAX_STEPS > 0, "animate disabled: max_steps is 0");
391 let mut sequence: Vec<(u16, Duration), MAX_STEPS> = Vec::new();
392 for step in steps {
393 let step = *step.borrow();
394 assert!(
395 step.1.as_micros() > 0,
396 "animation step duration must be positive"
397 );
398 sequence
399 .push(step)
400 .expect("animate sequence fits within max_steps");
401 }
402 assert!(!sequence.is_empty(), "animate requires at least one step");
403
404 self.servo_player_static.signal(PlayerCommand::Animate {
405 steps: sequence,
406 mode: at_end,
407 });
408 }
409}
410
411#[cfg(not(feature = "host"))]
462#[doc(hidden)]
463#[macro_export]
464macro_rules! servo_player {
465 ($($tt:tt)*) => { $crate::__servo_player_impl! { $($tt)* } };
466}
467#[doc(inline)]
468pub use servo_player;
469
470#[doc(hidden)]
472#[macro_export]
473macro_rules! __servo_player_impl {
474 (
476 $name:ident {
477 $($fields:tt)*
478 }
479 ) => {
480 $crate::__servo_player_impl! {
481 @__fill_defaults
482 vis: pub(self),
483 name: $name,
484 pin: _UNSET_,
485 slice: _UNSET_,
486 channel: _UNSET_,
487 min_us: $crate::servo::SERVO_MIN_US_DEFAULT,
488 max_us: $crate::servo::SERVO_MAX_US_DEFAULT,
489 max_degrees: $crate::servo::Servo::DEFAULT_MAX_DEGREES,
490 max_steps: 16,
491 fields: [ $($fields)* ]
492 }
493 };
494
495 (
497 $vis:vis $name:ident {
498 $($fields:tt)*
499 }
500 ) => {
501 $crate::__servo_player_impl! {
502 @__fill_defaults
503 vis: $vis,
504 name: $name,
505 pin: _UNSET_,
506 slice: _UNSET_,
507 channel: _UNSET_,
508 min_us: $crate::servo::SERVO_MIN_US_DEFAULT,
509 max_us: $crate::servo::SERVO_MAX_US_DEFAULT,
510 max_degrees: $crate::servo::Servo::DEFAULT_MAX_DEGREES,
511 max_steps: 16,
512 fields: [ $($fields)* ]
513 }
514 };
515
516 (@__fill_defaults
518 vis: $vis:vis,
519 name: $name:ident,
520 pin: $pin:tt,
521 slice: $slice:tt,
522 channel: $channel:tt,
523 min_us: $min_us:expr,
524 max_us: $max_us:expr,
525 max_degrees: $max_degrees:expr,
526 max_steps: $max_steps:expr,
527 fields: [ pin: $pin_value:ident $(, $($rest:tt)* )? ]
528 ) => {
529 $crate::__servo_player_impl! {
530 @__fill_defaults
531 vis: $vis,
532 name: $name,
533 pin: $pin_value,
534 slice: $slice,
535 channel: $channel,
536 min_us: $min_us,
537 max_us: $max_us,
538 max_degrees: $max_degrees,
539 max_steps: $max_steps,
540 fields: [ $($($rest)*)? ]
541 }
542 };
543
544 (@__fill_defaults
545 vis: $vis:vis,
546 name: $name:ident,
547 pin: $pin:tt,
548 slice: $slice:tt,
549 channel: $channel:tt,
550 min_us: $min_us:expr,
551 max_us: $max_us:expr,
552 max_degrees: $max_degrees:expr,
553 max_steps: $max_steps:expr,
554 fields: [ pin: $pin_value:ident ]
555 ) => {
556 $crate::__servo_player_impl! {
557 @__fill_defaults
558 vis: $vis,
559 name: $name,
560 pin: $pin_value,
561 slice: $slice,
562 channel: $channel,
563 min_us: $min_us,
564 max_us: $max_us,
565 max_degrees: $max_degrees,
566 max_steps: $max_steps,
567 fields: [ ]
568 }
569 };
570
571 (@__fill_defaults
573 vis: $vis:vis,
574 name: $name:ident,
575 pin: $pin:tt,
576 slice: $slice:tt,
577 channel: $channel:tt,
578 min_us: $min_us:expr,
579 max_us: $max_us:expr,
580 max_degrees: $max_degrees:expr,
581 max_steps: $max_steps:expr,
582 fields: [ slice: $slice_value:ident $(, $($rest:tt)* )? ]
583 ) => {
584 $crate::__servo_player_impl! {
585 @__fill_defaults
586 vis: $vis,
587 name: $name,
588 pin: $pin,
589 slice: $slice_value,
590 channel: $channel,
591 min_us: $min_us,
592 max_us: $max_us,
593 max_degrees: $max_degrees,
594 max_steps: $max_steps,
595 fields: [ $($($rest)*)? ]
596 }
597 };
598
599 (@__fill_defaults
600 vis: $vis:vis,
601 name: $name:ident,
602 pin: $pin:tt,
603 slice: $slice:tt,
604 channel: $channel:tt,
605 min_us: $min_us:expr,
606 max_us: $max_us:expr,
607 max_degrees: $max_degrees:expr,
608 max_steps: $max_steps:expr,
609 fields: [ slice: $slice_value:ident ]
610 ) => {
611 $crate::__servo_player_impl! {
612 @__fill_defaults
613 vis: $vis,
614 name: $name,
615 pin: $pin,
616 slice: $slice_value,
617 channel: $channel,
618 min_us: $min_us,
619 max_us: $max_us,
620 max_degrees: $max_degrees,
621 max_steps: $max_steps,
622 fields: [ ]
623 }
624 };
625
626 (@__fill_defaults
628 vis: $vis:vis,
629 name: $name:ident,
630 pin: $pin:tt,
631 slice: $slice:tt,
632 channel: $channel:tt,
633 min_us: $min_us:expr,
634 max_us: $max_us:expr,
635 max_degrees: $max_degrees:expr,
636 max_steps: $max_steps:expr,
637 fields: [ min_us: $min_us_value:expr $(, $($rest:tt)* )? ]
638 ) => {
639 $crate::__servo_player_impl! {
640 @__fill_defaults
641 vis: $vis,
642 name: $name,
643 pin: $pin,
644 slice: $slice,
645 channel: $channel,
646 min_us: $min_us_value,
647 max_us: $max_us,
648 max_degrees: $max_degrees,
649 max_steps: $max_steps,
650 fields: [ $($($rest)*)? ]
651 }
652 };
653
654 (@__fill_defaults
655 vis: $vis:vis,
656 name: $name:ident,
657 pin: $pin:tt,
658 slice: $slice:tt,
659 channel: $channel:tt,
660 min_us: $min_us:expr,
661 max_us: $max_us:expr,
662 max_degrees: $max_degrees:expr,
663 max_steps: $max_steps:expr,
664 fields: [ min_us: $min_us_value:expr ]
665 ) => {
666 $crate::__servo_player_impl! {
667 @__fill_defaults
668 vis: $vis,
669 name: $name,
670 pin: $pin,
671 slice: $slice,
672 channel: $channel,
673 min_us: $min_us_value,
674 max_us: $max_us,
675 max_degrees: $max_degrees,
676 max_steps: $max_steps,
677 fields: [ ]
678 }
679 };
680
681 (@__fill_defaults
683 vis: $vis:vis,
684 name: $name:ident,
685 pin: $pin:tt,
686 slice: $slice:tt,
687 channel: $channel:tt,
688 min_us: $min_us:expr,
689 max_us: $max_us:expr,
690 max_degrees: $max_degrees:expr,
691 max_steps: $max_steps:expr,
692 fields: [ max_us: $max_us_value:expr $(, $($rest:tt)* )? ]
693 ) => {
694 $crate::__servo_player_impl! {
695 @__fill_defaults
696 vis: $vis,
697 name: $name,
698 pin: $pin,
699 slice: $slice,
700 channel: $channel,
701 min_us: $min_us,
702 max_us: $max_us_value,
703 max_degrees: $max_degrees,
704 max_steps: $max_steps,
705 fields: [ $($($rest)*)? ]
706 }
707 };
708
709 (@__fill_defaults
710 vis: $vis:vis,
711 name: $name:ident,
712 pin: $pin:tt,
713 slice: $slice:tt,
714 channel: $channel:tt,
715 min_us: $min_us:expr,
716 max_us: $max_us:expr,
717 max_degrees: $max_degrees:expr,
718 max_steps: $max_steps:expr,
719 fields: [ max_us: $max_us_value:expr ]
720 ) => {
721 $crate::__servo_player_impl! {
722 @__fill_defaults
723 vis: $vis,
724 name: $name,
725 pin: $pin,
726 slice: $slice,
727 channel: $channel,
728 min_us: $min_us,
729 max_us: $max_us_value,
730 max_degrees: $max_degrees,
731 max_steps: $max_steps,
732 fields: [ ]
733 }
734 };
735
736 (@__fill_defaults
738 vis: $vis:vis,
739 name: $name:ident,
740 pin: $pin:tt,
741 slice: $slice:tt,
742 channel: $channel:tt,
743 min_us: $min_us:expr,
744 max_us: $max_us:expr,
745 max_degrees: $max_degrees:expr,
746 max_steps: $max_steps:expr,
747 fields: [ max_degrees: $max_degrees_value:expr $(, $($rest:tt)* )? ]
748 ) => {
749 $crate::__servo_player_impl! {
750 @__fill_defaults
751 vis: $vis,
752 name: $name,
753 pin: $pin,
754 slice: $slice,
755 channel: $channel,
756 min_us: $min_us,
757 max_us: $max_us,
758 max_degrees: $max_degrees_value,
759 max_steps: $max_steps,
760 fields: [ $($($rest)*)? ]
761 }
762 };
763
764 (@__fill_defaults
765 vis: $vis:vis,
766 name: $name:ident,
767 pin: $pin:tt,
768 slice: $slice:tt,
769 channel: $channel:tt,
770 min_us: $min_us:expr,
771 max_us: $max_us:expr,
772 max_degrees: $max_degrees:expr,
773 max_steps: $max_steps:expr,
774 fields: [ max_degrees: $max_degrees_value:expr ]
775 ) => {
776 $crate::__servo_player_impl! {
777 @__fill_defaults
778 vis: $vis,
779 name: $name,
780 pin: $pin,
781 slice: $slice,
782 channel: $channel,
783 min_us: $min_us,
784 max_us: $max_us,
785 max_degrees: $max_degrees_value,
786 max_steps: $max_steps,
787 fields: [ ]
788 }
789 };
790
791 (@__fill_defaults
793 vis: $vis:vis,
794 name: $name:ident,
795 pin: $pin:tt,
796 slice: $slice:tt,
797 channel: $channel:tt,
798 min_us: $min_us:expr,
799 max_us: $max_us:expr,
800 max_degrees: $max_degrees:expr,
801 max_steps: $max_steps:expr,
802 fields: [ max_steps: $max_steps_value:expr $(, $($rest:tt)* )? ]
803 ) => {
804 $crate::__servo_player_impl! {
805 @__fill_defaults
806 vis: $vis,
807 name: $name,
808 pin: $pin,
809 slice: $slice,
810 channel: $channel,
811 min_us: $min_us,
812 max_us: $max_us,
813 max_degrees: $max_degrees,
814 max_steps: $max_steps_value,
815 fields: [ $($($rest)*)? ]
816 }
817 };
818
819 (@__fill_defaults
820 vis: $vis:vis,
821 name: $name:ident,
822 pin: $pin:tt,
823 slice: $slice:tt,
824 channel: $channel:tt,
825 min_us: $min_us:expr,
826 max_us: $max_us:expr,
827 max_degrees: $max_degrees:expr,
828 max_steps: $max_steps:expr,
829 fields: [ max_steps: $max_steps_value:expr ]
830 ) => {
831 $crate::__servo_player_impl! {
832 @__fill_defaults
833 vis: $vis,
834 name: $name,
835 pin: $pin,
836 slice: $slice,
837 channel: $channel,
838 min_us: $min_us,
839 max_us: $max_us,
840 max_degrees: $max_degrees,
841 max_steps: $max_steps_value,
842 fields: [ ]
843 }
844 };
845
846 (@__fill_defaults
848 vis: $vis:vis,
849 name: $name:ident,
850 pin: $pin:tt,
851 slice: $slice:tt,
852 channel: $channel:tt,
853 min_us: $min_us:expr,
854 max_us: $max_us:expr,
855 max_degrees: $max_degrees:expr,
856 max_steps: $max_steps:expr,
857 fields: [ channel: A $(, $($rest:tt)* )? ]
858 ) => {
859 $crate::__servo_player_impl! {
860 @__fill_defaults
861 vis: $vis,
862 name: $name,
863 pin: $pin,
864 slice: $slice,
865 channel: A,
866 min_us: $min_us,
867 max_us: $max_us,
868 max_degrees: $max_degrees,
869 max_steps: $max_steps,
870 fields: [ $($($rest)*)? ]
871 }
872 };
873
874 (@__fill_defaults
875 vis: $vis:vis,
876 name: $name:ident,
877 pin: $pin:tt,
878 slice: $slice:tt,
879 channel: $channel:tt,
880 min_us: $min_us:expr,
881 max_us: $max_us:expr,
882 max_degrees: $max_degrees:expr,
883 max_steps: $max_steps:expr,
884 fields: [ channel: A ]
885 ) => {
886 $crate::__servo_player_impl! {
887 @__fill_defaults
888 vis: $vis,
889 name: $name,
890 pin: $pin,
891 slice: $slice,
892 channel: A,
893 min_us: $min_us,
894 max_us: $max_us,
895 max_degrees: $max_degrees,
896 max_steps: $max_steps,
897 fields: [ ]
898 }
899 };
900
901 (@__fill_defaults
902 vis: $vis:vis,
903 name: $name:ident,
904 pin: $pin:tt,
905 slice: $slice:tt,
906 channel: $channel:tt,
907 min_us: $min_us:expr,
908 max_us: $max_us:expr,
909 max_degrees: $max_degrees:expr,
910 max_steps: $max_steps:expr,
911 fields: [ channel: B $(, $($rest:tt)* )? ]
912 ) => {
913 $crate::__servo_player_impl! {
914 @__fill_defaults
915 vis: $vis,
916 name: $name,
917 pin: $pin,
918 slice: $slice,
919 channel: B,
920 min_us: $min_us,
921 max_us: $max_us,
922 max_degrees: $max_degrees,
923 max_steps: $max_steps,
924 fields: [ $($($rest)*)? ]
925 }
926 };
927
928 (@__fill_defaults
929 vis: $vis:vis,
930 name: $name:ident,
931 pin: $pin:tt,
932 slice: $slice:tt,
933 channel: $channel:tt,
934 min_us: $min_us:expr,
935 max_us: $max_us:expr,
936 max_degrees: $max_degrees:expr,
937 max_steps: $max_steps:expr,
938 fields: [ channel: B ]
939 ) => {
940 $crate::__servo_player_impl! {
941 @__fill_defaults
942 vis: $vis,
943 name: $name,
944 pin: $pin,
945 slice: $slice,
946 channel: B,
947 min_us: $min_us,
948 max_us: $max_us,
949 max_degrees: $max_degrees,
950 max_steps: $max_steps,
951 fields: [ ]
952 }
953 };
954
955 (@__fill_defaults
956 vis: $vis:vis,
957 name: $name:ident,
958 pin: $pin:tt,
959 slice: $slice:tt,
960 channel: $channel:tt,
961 min_us: $min_us:expr,
962 max_us: $max_us:expr,
963 max_degrees: $max_degrees:expr,
964 max_steps: $max_steps:expr,
965 fields: [ even $(, $($rest:tt)* )? ]
966 ) => {
967 $crate::__servo_player_impl! {
968 @__fill_defaults
969 vis: $vis,
970 name: $name,
971 pin: $pin,
972 slice: $slice,
973 channel: A,
974 min_us: $min_us,
975 max_us: $max_us,
976 max_degrees: $max_degrees,
977 max_steps: $max_steps,
978 fields: [ $($($rest)*)? ]
979 }
980 };
981
982 (@__fill_defaults
983 vis: $vis:vis,
984 name: $name:ident,
985 pin: $pin:tt,
986 slice: $slice:tt,
987 channel: $channel:tt,
988 min_us: $min_us:expr,
989 max_us: $max_us:expr,
990 max_degrees: $max_degrees:expr,
991 max_steps: $max_steps:expr,
992 fields: [ even ]
993 ) => {
994 $crate::__servo_player_impl! {
995 @__fill_defaults
996 vis: $vis,
997 name: $name,
998 pin: $pin,
999 slice: $slice,
1000 channel: A,
1001 min_us: $min_us,
1002 max_us: $max_us,
1003 max_degrees: $max_degrees,
1004 max_steps: $max_steps,
1005 fields: [ ]
1006 }
1007 };
1008
1009 (@__fill_defaults
1010 vis: $vis:vis,
1011 name: $name:ident,
1012 pin: $pin:tt,
1013 slice: $slice:tt,
1014 channel: $channel:tt,
1015 min_us: $min_us:expr,
1016 max_us: $max_us:expr,
1017 max_degrees: $max_degrees:expr,
1018 max_steps: $max_steps:expr,
1019 fields: [ odd $(, $($rest:tt)* )? ]
1020 ) => {
1021 $crate::__servo_player_impl! {
1022 @__fill_defaults
1023 vis: $vis,
1024 name: $name,
1025 pin: $pin,
1026 slice: $slice,
1027 channel: B,
1028 min_us: $min_us,
1029 max_us: $max_us,
1030 max_degrees: $max_degrees,
1031 max_steps: $max_steps,
1032 fields: [ $($($rest)*)? ]
1033 }
1034 };
1035
1036 (@__fill_defaults
1037 vis: $vis:vis,
1038 name: $name:ident,
1039 pin: $pin:tt,
1040 slice: $slice:tt,
1041 channel: $channel:tt,
1042 min_us: $min_us:expr,
1043 max_us: $max_us:expr,
1044 max_degrees: $max_degrees:expr,
1045 max_steps: $max_steps:expr,
1046 fields: [ odd ]
1047 ) => {
1048 $crate::__servo_player_impl! {
1049 @__fill_defaults
1050 vis: $vis,
1051 name: $name,
1052 pin: $pin,
1053 slice: $slice,
1054 channel: B,
1055 min_us: $min_us,
1056 max_us: $max_us,
1057 max_degrees: $max_degrees,
1058 max_steps: $max_steps,
1059 fields: [ ]
1060 }
1061 };
1062
1063 (@__fill_defaults
1065 vis: $vis:vis,
1066 name: $name:ident,
1067 pin: $pin:tt,
1068 slice: $slice:tt,
1069 channel: $channel:tt,
1070 min_us: $min_us:expr,
1071 max_us: $max_us:expr,
1072 max_degrees: $max_degrees:expr,
1073 max_steps: $max_steps:expr,
1074 fields: [ ]
1075 ) => {
1076 $crate::__servo_player_impl! {
1077 @__build
1078 vis: $vis,
1079 name: $name,
1080 pin: $pin,
1081 slice: $slice,
1082 channel: $channel,
1083 min_us: $min_us,
1084 max_us: $max_us,
1085 max_degrees: $max_degrees,
1086 max_steps: $max_steps
1087 }
1088 };
1089
1090 (@__build
1092 vis: $vis:vis,
1093 name: $name:ident,
1094 pin: _UNSET_,
1095 slice: $slice:tt,
1096 channel: $channel:tt,
1097 min_us: $min_us:expr,
1098 max_us: $max_us:expr,
1099 max_degrees: $max_degrees:expr,
1100 max_steps: $max_steps:expr
1101 ) => {
1102 compile_error!("servo_player! requires `pin: ...`");
1103 };
1104
1105 (@__build
1107 vis: $vis:vis,
1108 name: $name:ident,
1109 pin: $pin:ident,
1110 slice: _UNSET_,
1111 channel: $channel:tt,
1112 min_us: $min_us:expr,
1113 max_us: $max_us:expr,
1114 max_degrees: $max_degrees:expr,
1115 max_steps: $max_steps:expr
1116 ) => {
1117 $crate::servo_player::paste::paste! {
1118 static [<$name:upper _SERVO_PLAYER_STATIC>]: $crate::servo_player::ServoPlayerStatic<$max_steps> =
1119 $crate::servo_player::ServoPlayer::<$max_steps>::new_static();
1120 static [<$name:upper _SERVO_PLAYER_CELL>]: ::static_cell::StaticCell<$name> =
1121 ::static_cell::StaticCell::new();
1122
1123 #[allow(missing_docs)]
1124 $vis struct $name {
1125 player: $crate::servo_player::ServoPlayer<$max_steps>,
1126 }
1127
1128 #[allow(missing_docs)]
1129 impl $name {
1130 pub fn new<S: 'static>(
1149 pin: impl Into<::embassy_rp::Peri<'static, ::embassy_rp::peripherals::$pin>>,
1150 slice: impl Into<::embassy_rp::Peri<'static, S>>,
1151 spawner: ::embassy_executor::Spawner,
1152 ) -> $crate::Result<&'static Self>
1153 where
1154 ::embassy_rp::peripherals::$pin: $crate::servo::ServoPwmPin<S>,
1155 S: ::embassy_rp::PeripheralType,
1156 {
1157 let pin = pin.into();
1158 let slice = slice.into();
1159 let servo = $crate::servo::servo_from_pin_slice(
1160 pin,
1161 slice,
1162 $min_us,
1163 $max_us,
1164 $max_degrees
1165 );
1166 let token = [<$name:snake _servo_player_task>](&[<$name:upper _SERVO_PLAYER_STATIC>], servo);
1167 spawner.spawn(token)?;
1168 let player = $crate::servo_player::ServoPlayer::new(&[<$name:upper _SERVO_PLAYER_STATIC>]);
1169 Ok([<$name:upper _SERVO_PLAYER_CELL>].init(Self { player }))
1170 }
1171 }
1172
1173 impl ::core::ops::Deref for $name {
1174 type Target = $crate::servo_player::ServoPlayer<$max_steps>;
1175
1176 fn deref(&self) -> &Self::Target {
1177 &self.player
1178 }
1179 }
1180
1181 #[::embassy_executor::task]
1182 async fn [<$name:snake _servo_player_task>](
1183 servo_player_static: &'static $crate::servo_player::ServoPlayerStatic<$max_steps>,
1184 servo: $crate::servo::Servo<'static>,
1185 ) -> ! {
1186 $crate::servo_player::device_loop(servo_player_static, servo).await
1187 }
1188 }
1189 };
1190
1191 (@__build
1192 vis: $vis:vis,
1193 name: $name:ident,
1194 pin: $pin:ident,
1195 slice: $slice:ident,
1196 channel: $channel:tt,
1197 min_us: $min_us:expr,
1198 max_us: $max_us:expr,
1199 max_degrees: $max_degrees:expr,
1200 max_steps: $max_steps:expr
1201 ) => {
1202 $crate::servo_player::paste::paste! {
1203 static [<$name:upper _SERVO_PLAYER_STATIC>]: $crate::servo_player::ServoPlayerStatic<$max_steps> =
1204 $crate::servo_player::ServoPlayer::<$max_steps>::new_static();
1205 static [<$name:upper _SERVO_PLAYER_CELL>]: ::static_cell::StaticCell<$name> =
1206 ::static_cell::StaticCell::new();
1207
1208 #[allow(missing_docs)]
1209 $vis struct $name {
1210 player: $crate::servo_player::ServoPlayer<$max_steps>,
1211 }
1212
1213 #[allow(missing_docs)]
1214 impl $name {
1215 pub fn new(
1231 pin: impl Into<::embassy_rp::Peri<'static, ::embassy_rp::peripherals::$pin>>,
1232 slice: impl Into<::embassy_rp::Peri<'static, ::embassy_rp::peripherals::$slice>>,
1233 spawner: ::embassy_executor::Spawner,
1234 ) -> $crate::Result<&'static Self> {
1235 let pin = pin.into();
1236 let slice = slice.into();
1237 let servo = $crate::__servo_player_impl! {
1238 @__build_servo
1239 pin: pin,
1240 slice: slice,
1241 channel: $channel,
1242 min_us: $min_us,
1243 max_us: $max_us,
1244 max_degrees: $max_degrees
1245 };
1246 let token = [<$name:snake _servo_player_task>](&[<$name:upper _SERVO_PLAYER_STATIC>], servo);
1247 spawner.spawn(token)?;
1248 let player = $crate::servo_player::ServoPlayer::new(&[<$name:upper _SERVO_PLAYER_STATIC>]);
1249 Ok([<$name:upper _SERVO_PLAYER_CELL>].init(Self { player }))
1250 }
1251 }
1252
1253 impl ::core::ops::Deref for $name {
1254 type Target = $crate::servo_player::ServoPlayer<$max_steps>;
1255
1256 fn deref(&self) -> &Self::Target {
1257 &self.player
1258 }
1259 }
1260
1261 #[::embassy_executor::task]
1262 async fn [<$name:snake _servo_player_task>](
1263 servo_player_static: &'static $crate::servo_player::ServoPlayerStatic<$max_steps>,
1264 servo: $crate::servo::Servo<'static>,
1265 ) -> ! {
1266 $crate::servo_player::device_loop(servo_player_static, servo).await
1267 }
1268 }
1269 };
1270
1271 (@__build_servo
1272 pin: $pin:expr,
1273 slice: $slice:expr,
1274 channel: _UNSET_,
1275 min_us: $min_us:expr,
1276 max_us: $max_us:expr,
1277 max_degrees: $max_degrees:expr,
1278 max_steps: $max_steps:expr
1279 ) => {
1280 $crate::servo::servo_from_pin_slice($pin, $slice, $min_us, $max_us, $max_degrees)
1281 };
1282
1283 (@__build_servo
1284 pin: $pin:expr,
1285 slice: $slice:expr,
1286 channel: A,
1287 min_us: $min_us:expr,
1288 max_us: $max_us:expr,
1289 max_degrees: $max_degrees:expr,
1290 max_steps: $max_steps:expr
1291 ) => {
1292 $crate::servo::Servo::new_output_a(
1293 embassy_rp::pwm::Pwm::new_output_a(
1294 $slice,
1295 $pin,
1296 embassy_rp::pwm::Config::default(),
1297 ),
1298 $min_us,
1299 $max_us,
1300 $max_degrees,
1301 )
1302 };
1303
1304 (@__build_servo
1305 pin: $pin:expr,
1306 slice: $slice:expr,
1307 channel: B,
1308 min_us: $min_us:expr,
1309 max_us: $max_us:expr,
1310 max_degrees: $max_degrees:expr,
1311 max_steps: $max_steps:expr
1312 ) => {
1313 $crate::servo::Servo::new_output_b(
1314 embassy_rp::pwm::Pwm::new_output_b(
1315 $slice,
1316 $pin,
1317 embassy_rp::pwm::Config::default(),
1318 ),
1319 $min_us,
1320 $max_us,
1321 $max_degrees,
1322 )
1323 };
1324
1325 (
1326 $($fields:tt)*
1327 ) => {
1328 $crate::__servo_player_impl! {
1329 @__fill_defaults
1330 vis: pub(self),
1331 name: ServoPlayerGenerated,
1332 pin: _UNSET_,
1333 slice: _UNSET_,
1334 channel: _UNSET_,
1335 min_us: $crate::servo::SERVO_MIN_US_DEFAULT,
1336 max_us: $crate::servo::SERVO_MAX_US_DEFAULT,
1337 max_degrees: $crate::servo::Servo::DEFAULT_MAX_DEGREES,
1338 max_steps: 16,
1339 fields: [ $($fields)* ]
1340 }
1341 };
1342}
1343
1344#[doc(hidden)]
1346pub async fn device_loop<const MAX_STEPS: usize>(
1347 servo_player_static: &'static ServoPlayerStatic<MAX_STEPS>,
1348 mut servo: Servo<'static>,
1349) -> ! {
1350 let mut current_degrees: u16 = 0;
1351 servo.set_degrees(current_degrees);
1352
1353 let mut command = servo_player_static.wait().await;
1354 loop {
1355 match command {
1356 PlayerCommand::Set { degrees } => {
1357 current_degrees = degrees;
1358 servo.set_degrees(current_degrees);
1359 command = servo_player_static.wait().await;
1360 }
1361 PlayerCommand::Hold => {
1362 servo.hold();
1363 command = servo_player_static.wait().await;
1364 }
1365 PlayerCommand::Relax => {
1366 servo.relax();
1367 command = servo_player_static.wait().await;
1368 }
1369 PlayerCommand::Animate { steps, mode } => {
1370 command = run_animation(
1371 &steps,
1372 mode,
1373 &mut servo,
1374 servo_player_static,
1375 &mut current_degrees,
1376 )
1377 .await;
1378 }
1379 }
1380 }
1381}
1382
1383async fn run_animation<const MAX_STEPS: usize>(
1384 steps: &[(u16, Duration)],
1385 mode: AtEnd,
1386 servo: &mut Servo<'static>,
1387 servo_player_static: &'static ServoPlayerStatic<MAX_STEPS>,
1388 current_degrees: &mut u16,
1389) -> PlayerCommand<MAX_STEPS> {
1390 loop {
1391 for step in steps {
1392 if *current_degrees != step.0 {
1393 servo.set_degrees(step.0);
1394 *current_degrees = step.0;
1395 }
1396 match select(Timer::after(step.1), servo_player_static.wait()).await {
1397 Either::First(_) => {}
1398 Either::Second(command) => return command,
1399 }
1400 }
1401
1402 match mode {
1404 AtEnd::Loop => {
1405 }
1407 AtEnd::Hold => {
1408 return servo_player_static.wait().await;
1410 }
1411 AtEnd::Relax => {
1412 servo.relax();
1414 return servo_player_static.wait().await;
1415 }
1416 }
1417 }
1418}