1#![no_std]
200
201#[cfg(feature = "alloc")]
202extern crate alloc;
203#[cfg(feature = "alloc")]
204use alloc::collections::BTreeMap;
205
206#[cfg(feature = "defmt")]
207#[allow(unused_imports)]
208use defmt::{debug, error, info, trace, warn};
209
210#[cfg(not(feature = "defmt"))]
212mod log_impl {
213 #![allow(unused_macros)]
214 #![allow(unused_imports)]
215 macro_rules! _trace {
218 ($($arg:tt)*) => {};
219 }
220 macro_rules! _debug {
221 ($($arg:tt)*) => {};
222 }
223 macro_rules! _info {
224 ($($arg:tt)*) => {};
225 }
226 macro_rules! _warn {
227 ($($arg:tt)*) => {};
228 }
229 macro_rules! _error {
230 ($($arg:tt)*) => {};
231 }
232 pub(crate) use _debug as debug;
233 pub(crate) use _error as error;
234 pub(crate) use _info as info;
235 pub(crate) use _trace as trace;
236 pub(crate) use _warn as warn;
237}
238#[cfg(not(feature = "defmt"))]
239use log_impl::*;
240
241pub trait HardwareWatchdog<C: Clock> {
243 fn start(&mut self, timeout: C::Duration);
245
246 fn feed(&mut self);
248
249 fn trigger_reset(&mut self) -> !;
251
252 fn reset_reason(&self) -> Option<ResetReason>;
254}
255
256#[derive(Debug, Clone, Copy, PartialEq, Eq)]
258#[cfg_attr(feature = "defmt", derive(defmt::Format))]
259pub enum ResetReason {
260 Forced,
262
263 TimedOut,
265}
266
267#[derive(Debug, Clone, Copy)]
269pub struct WatchdogConfig<C: Clock> {
270 pub hardware_timeout: C::Duration,
272
273 pub check_interval: C::Duration,
278}
279
280impl<C: Clock> WatchdogConfig<C> {
281 pub fn new(hardware_timeout_ms: u64, check_interval_ms: u64, clock: &C) -> Self {
283 Self {
284 hardware_timeout: clock.duration_from_millis(hardware_timeout_ms),
285 check_interval: clock.duration_from_millis(check_interval_ms),
286 }
287 }
288
289 pub fn default(clock: &C) -> Self {
293 Self::new(5000, 1000, clock)
294 }
295}
296
297#[cfg(all(feature = "alloc", feature = "defmt"))]
298pub trait Id: PartialEq + Eq + Ord + defmt::Format + Clone + Copy {}
300#[cfg(all(feature = "alloc", not(feature = "defmt")))]
301pub trait Id: PartialEq + Eq + Ord + core::fmt::Debug + Clone + Copy {}
303#[cfg(all(not(feature = "alloc"), feature = "defmt"))]
304pub trait Id: PartialEq + Eq + defmt::Format + Clone + Copy {}
311#[cfg(all(not(feature = "alloc"), not(feature = "defmt")))]
312pub trait Id: PartialEq + Eq + core::fmt::Debug + Clone + Copy {}
314
315#[derive(Debug, Clone)]
317pub struct Task<I: Id, C: Clock> {
318 #[allow(dead_code)]
320 id: I,
321
322 last_feed: C::Instant,
324
325 max_duration: C::Duration,
327}
328
329impl<I: Id, C: Clock> Task<I, C> {
330 pub fn new(id: I, max_duration: C::Duration, clock: &C) -> Self {
332 Self {
333 id,
334 last_feed: clock.now(),
335 max_duration,
336 }
337 }
338
339 fn feed(&mut self, clock: &C) {
341 self.last_feed = clock.now();
342 }
343
344 fn is_starved(&self, clock: &C) -> bool {
346 clock.has_elapsed(self.last_feed, &self.max_duration)
347 }
348}
349
350pub trait Clock {
352 type Instant: Copy;
354
355 type Duration: Copy;
357
358 fn now(&self) -> Self::Instant;
360
361 fn elapsed_since(&self, instant: Self::Instant) -> Self::Duration;
363
364 fn has_elapsed(&self, instant: Self::Instant, duration: &Self::Duration) -> bool;
366
367 fn duration_from_millis(&self, millis: u64) -> Self::Duration;
369}
370
371#[cfg(feature = "alloc")]
373pub struct Watchdog<I: Id, W: HardwareWatchdog<C>, C: Clock> {
374 hw_watchdog: W,
376
377 tasks: BTreeMap<I, Task<I, C>>,
379
380 config: WatchdogConfig<C>,
382
383 clock: C,
385}
386
387#[cfg(feature = "alloc")]
388impl<I: Id, W: HardwareWatchdog<C>, C: Clock> Watchdog<I, W, C> {
389 pub fn new(hw_watchdog: W, config: WatchdogConfig<C>, clock: C) -> Self {
391 Self {
392 hw_watchdog,
393 tasks: BTreeMap::new(),
394 config,
395 clock,
396 }
397 }
398
399 pub fn register_task(&mut self, id: &I, max_duration: C::Duration) {
401 let task = Task::new(*id, max_duration, &self.clock);
402 self.tasks.insert(*id, task);
403 debug!("Registered task: {:?}", id);
404 }
405
406 pub fn deregister_task(&mut self, id: &I) {
408 #[allow(clippy::if_same_then_else)]
409 if self.tasks.remove(id).is_some() {
410 debug!("Deregistered task: {:?}", id);
411 } else {
412 debug!("Attempted to deregister unknown task: {:?}", id);
413 }
414 }
415
416 pub fn feed(&mut self, id: &I) {
418 if let Some(task) = self.tasks.get_mut(id) {
419 task.feed(&self.clock);
420 } else {
421 warn!("Attempt to feed unknown task: {:?}", id);
422 }
423 }
424
425 pub fn start(&mut self) {
427 for task in self.tasks.values_mut() {
429 task.feed(&self.clock);
430 }
431
432 self.hw_watchdog.start(self.config.hardware_timeout);
434
435 info!("Watchdog started");
436 }
437
438 pub fn check(&mut self) -> bool {
440 let mut starved = false;
442 for task in self.tasks.values() {
443 if task.is_starved(&self.clock) {
444 error!("Task {:?} has starved the watchdog", task.id);
445 starved = true;
446 }
447 }
448
449 if !starved {
451 self.hw_watchdog.feed();
452 }
453
454 starved
455 }
456
457 pub fn trigger_reset(&mut self) -> ! {
459 warn!("Triggering watchdog reset");
460 self.hw_watchdog.trigger_reset()
461 }
462
463 pub fn reset_reason(&self) -> Option<ResetReason> {
465 self.hw_watchdog.reset_reason()
466 }
467}
468
469#[cfg(not(feature = "alloc"))]
472pub struct Watchdog<I, const N: usize, W, C>
473where
474 I: Id,
475 W: HardwareWatchdog<C>,
476 C: Clock,
477{
478 hw_watchdog: W,
480
481 tasks: [Option<Task<I, C>>; N],
483
484 config: WatchdogConfig<C>,
486
487 clock: C,
489}
490
491pub enum Error {
493 NoSlotsAvailable,
495}
496
497#[cfg(not(feature = "alloc"))]
498impl<I: Id, W: HardwareWatchdog<C>, C: Clock, const N: usize> Watchdog<I, N, W, C> {
499 pub fn new(hw_watchdog: W, config: WatchdogConfig<C>, clock: C) -> Self {
506 Self {
507 hw_watchdog,
508 tasks: [const { None }; N],
509 config,
510 clock,
511 }
512 }
513
514 pub fn register_task(&mut self, id: &I, max_duration: C::Duration) -> Result<(), Error> {
528 for slot in &mut self.tasks {
530 if slot.is_none() {
531 *slot = Some(Task::new(*id, max_duration, &self.clock));
532 debug!("Registered task: {:?}", id);
533 return Ok(());
534 }
535 }
536
537 error!("Failed to register task: {:?} - no slots available", id);
539 Err(Error::NoSlotsAvailable)
540 }
541
542 pub fn deregister_task(&mut self, id: &I) {
549 for slot in &mut self.tasks {
550 if let Some(task) = slot {
551 if core::mem::discriminant(&task.id) == core::mem::discriminant(id) {
552 *slot = None;
553 debug!("Deregistered task: {:?}", id);
554 return;
555 }
556 }
557 }
558
559 info!("Attempted to deregister unknown task: {:?}", id);
560 }
561
562 pub fn feed(&mut self, id: &I) {
564 let fed = self.tasks.iter_mut().flatten().any(|task| {
565 if core::mem::discriminant(&task.id) == core::mem::discriminant(id) {
566 task.feed(&self.clock);
567 true
568 } else {
569 false
570 }
571 });
572
573 if !fed {
574 warn!("Attempt to feed unknown task: {:?}", id);
575 }
576 }
577
578 pub fn start(&mut self) {
583 self.tasks.iter_mut().flatten().for_each(|task| {
585 task.feed(&self.clock);
586 });
587
588 self.hw_watchdog.start(self.config.hardware_timeout);
590
591 info!("Watchdog started");
592 }
593
594 pub fn check(&mut self) -> bool {
596 let mut starved = false;
598 self.tasks.iter_mut().flatten().for_each(|task| {
599 if task.is_starved(&self.clock) {
600 error!("Task {:?} has starved the watchdog", task.id);
601 starved = true;
602 }
603 });
604
605 if !starved {
608 self.hw_watchdog.feed();
609 }
610
611 starved
612 }
613
614 pub fn trigger_reset(&mut self) -> ! {
616 warn!("Triggering watchdog reset");
617 self.hw_watchdog.trigger_reset()
618 }
619
620 pub fn reset_reason(&self) -> Option<ResetReason> {
622 self.hw_watchdog.reset_reason()
623 }
624}
625
626pub struct CoreClock;
629
630impl Clock for CoreClock {
631 type Instant = u64; type Duration = core::time::Duration;
633
634 fn now(&self) -> Self::Instant {
635 static mut MILLIS: u64 = 0;
638 unsafe {
639 MILLIS += 1;
640 MILLIS
641 }
642 }
643
644 fn elapsed_since(&self, instant: Self::Instant) -> Self::Duration {
645 let now = self.now();
646 let elapsed_ms = now.saturating_sub(instant);
647 core::time::Duration::from_millis(elapsed_ms)
648 }
649
650 fn has_elapsed(&self, instant: Self::Instant, duration: &Self::Duration) -> bool {
651 self.elapsed_since(instant) >= *duration
652 }
653
654 fn duration_from_millis(&self, millis: u64) -> Self::Duration {
655 core::time::Duration::from_millis(millis)
656 }
657}
658
659#[cfg(feature = "embassy")]
661pub struct EmbassyClock;
662
663#[cfg(feature = "embassy")]
664impl Clock for EmbassyClock {
665 type Instant = embassy_time::Instant;
666 type Duration = embassy_time::Duration;
667
668 fn now(&self) -> Self::Instant {
669 embassy_time::Instant::now()
670 }
671
672 fn elapsed_since(&self, instant: Self::Instant) -> Self::Duration {
673 embassy_time::Instant::now() - instant
674 }
675
676 fn has_elapsed(&self, instant: Self::Instant, duration: &Self::Duration) -> bool {
677 (embassy_time::Instant::now() - instant) >= *duration
678 }
679
680 fn duration_from_millis(&self, millis: u64) -> Self::Duration {
681 embassy_time::Duration::from_millis(millis)
682 }
683}
684
685#[cfg(any(feature = "rp2040-hal", feature = "rp2350-hal"))]
693pub mod rp_hal {
694 use super::{Clock, HardwareWatchdog, ResetReason};
695 use hal::fugit::{Duration as RpHalDuration, MicrosDurationU32};
696 #[cfg(feature = "rp2350-hal")]
697 use hal::timer::CopyableTimer0;
698 use hal::timer::{Instant as RpHalInstant, Timer as RpHalTimer};
699 use hal::watchdog::Watchdog as RpHalWatchdog;
700 #[cfg(feature = "rp2040-hal")]
701 use rp2040_hal as hal;
702 #[cfg(feature = "rp2350-hal")]
703 use rp235x_hal as hal;
704
705 #[cfg(feature = "rp2040-hal")]
707 pub struct RpHalClock {
708 inner: RpHalTimer,
709 }
710 #[cfg(feature = "rp2040-hal")]
711 impl RpHalClock {
712 pub fn new(timer: RpHalTimer) -> Self {
713 Self { inner: timer }
714 }
715 }
716 #[cfg(feature = "rp2350-hal")]
717 pub struct RpHalClock {
718 inner: RpHalTimer<CopyableTimer0>,
719 }
720 #[cfg(feature = "rp2350-hal")]
721 impl RpHalClock {
722 pub fn new(timer: RpHalTimer<CopyableTimer0>) -> Self {
723 Self { inner: timer }
724 }
725 }
726
727 impl Clock for RpHalClock {
729 type Instant = RpHalInstant;
730 type Duration = RpHalDuration<u64, 1, 1_000_000>;
731
732 fn now(&self) -> Self::Instant {
733 self.inner.get_counter()
734 }
735
736 fn elapsed_since(&self, instant: Self::Instant) -> Self::Duration {
737 self.now().checked_duration_since(instant).unwrap()
738 }
739
740 fn has_elapsed(&self, instant: Self::Instant, duration: &Self::Duration) -> bool {
741 (self.now() - instant) >= *duration
742 }
743
744 fn duration_from_millis(&self, millis: u64) -> Self::Duration {
745 RpHalDuration::<u64, 1, 1_000_000>::millis(millis as u64)
746 }
747 }
748
749 pub struct RpHalTaskWatchdog {
751 inner: RpHalWatchdog,
752 }
753 impl RpHalTaskWatchdog {
754 pub fn new(watchdog: RpHalWatchdog) -> Self {
755 Self { inner: watchdog }
756 }
757 }
758
759 impl HardwareWatchdog<RpHalClock> for RpHalTaskWatchdog {
761 fn start(&mut self, timeout: <RpHalClock as Clock>::Duration) {
762 let timeout_micros = timeout.to_micros();
763 assert!(timeout_micros <= u32::MAX as u64);
764 let micros_dur_u32: MicrosDurationU32 =
765 MicrosDurationU32::micros(timeout_micros as u32);
766 self.inner.start(micros_dur_u32);
767 }
768
769 fn feed(&mut self) {
770 self.inner.feed();
771 }
772
773 fn trigger_reset(&mut self) -> ! {
774 hal::reset()
777 }
778
779 fn reset_reason(&self) -> Option<ResetReason> {
780 None
783 }
784 }
785}
786
787#[cfg(any(feature = "rp2040-embassy", feature = "rp2350-embassy"))]
801pub mod embassy_rp {
802 use super::{
803 info, Clock, EmbassyClock, HardwareWatchdog, Id, ResetReason, Watchdog, WatchdogConfig,
804 };
805 use embassy_rp::peripherals::WATCHDOG as P_RpWatchdog;
806 use embassy_rp::watchdog as rp_watchdog;
807 use embassy_time::{Instant, Timer};
808
809 pub struct RpWatchdog {
811 inner: rp_watchdog::Watchdog,
812 }
813
814 impl RpWatchdog {
815 #[must_use]
817 pub fn new(peripheral: P_RpWatchdog) -> Self {
818 Self {
819 inner: rp_watchdog::Watchdog::new(peripheral),
820 }
821 }
822 }
823
824 impl HardwareWatchdog<EmbassyClock> for RpWatchdog {
826 fn start(&mut self, timeout: <EmbassyClock as Clock>::Duration) {
827 self.inner.start(timeout);
828 }
829
830 fn feed(&mut self) {
831 self.inner.feed();
832 }
833
834 fn trigger_reset(&mut self) -> ! {
835 self.inner.trigger_reset();
836 panic!("Triggering reset via watchdog failed");
837 }
838
839 fn reset_reason(&self) -> Option<ResetReason> {
840 self.inner.reset_reason().map(|reason| match reason {
841 embassy_rp::watchdog::ResetReason::Forced => ResetReason::Forced,
842 embassy_rp::watchdog::ResetReason::TimedOut => ResetReason::TimedOut,
843 })
844 }
845 }
846
847 #[cfg(feature = "alloc")]
849 pub struct WatchdogRunner<I>
850 where
851 I: Id,
852 {
853 watchdog: embassy_sync::mutex::Mutex<
854 embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex,
855 core::cell::RefCell<Watchdog<I, RpWatchdog, EmbassyClock>>,
856 >,
857 }
858
859 #[cfg(not(feature = "alloc"))]
872 pub struct WatchdogRunner<I, const N: usize>
873 where
874 I: Id,
875 {
876 watchdog: embassy_sync::mutex::Mutex<
877 embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex,
878 core::cell::RefCell<Watchdog<I, N, RpWatchdog, EmbassyClock>>,
879 >,
880 }
881
882 #[cfg(feature = "alloc")]
883 impl<I> WatchdogRunner<I>
884 where
885 I: Id + 'static,
886 {
887 pub fn new(hw_watchdog: P_RpWatchdog, config: WatchdogConfig<EmbassyClock>) -> Self {
889 let hw_watchdog = RpWatchdog::new(hw_watchdog);
890 let watchdog = Watchdog::new(hw_watchdog, config, EmbassyClock);
891 Self {
892 watchdog: embassy_sync::mutex::Mutex::new(core::cell::RefCell::new(watchdog)),
893 }
894 }
895
896 pub async fn register_task(&self, id: &I, max_duration: <EmbassyClock as Clock>::Duration) {
898 self.watchdog
899 .lock()
900 .await
901 .borrow_mut()
902 .register_task(id, max_duration);
903 }
904
905 pub async fn deregister_task(&self, id: &I) {
907 self.watchdog.lock().await.borrow_mut().deregister_task(id);
908 }
909
910 pub async fn feed(&self, id: &I) {
912 self.watchdog.lock().await.borrow_mut().feed(id);
913 }
914
915 pub async fn start(&self) {
917 self.watchdog.lock().await.borrow_mut().start();
918 }
919
920 pub async fn trigger_reset(&self) -> ! {
922 self.watchdog.lock().await.borrow_mut().trigger_reset()
923 }
924
925 pub async fn reset_reason(&self) -> Option<ResetReason> {
927 self.watchdog.lock().await.borrow().reset_reason()
928 }
929
930 pub async fn get_check_interval(&self) -> <EmbassyClock as Clock>::Duration {
932 self.watchdog.lock().await.borrow().config.check_interval
933 }
934
935 pub async fn check_tasks(&self) -> bool {
937 self.watchdog.lock().await.borrow_mut().check()
938 }
939 }
940
941 #[cfg(not(feature = "alloc"))]
942 impl<I, const N: usize> WatchdogRunner<I, N>
943 where
944 I: Id,
945 {
946 pub fn new(hw_watchdog: P_RpWatchdog, config: WatchdogConfig<EmbassyClock>) -> Self {
948 let hw_watchdog = RpWatchdog::new(hw_watchdog);
949 let watchdog = Watchdog::new(hw_watchdog, config, EmbassyClock);
950 Self {
951 watchdog: embassy_sync::mutex::Mutex::new(core::cell::RefCell::new(watchdog)),
952 }
953 }
954
955 pub async fn register_task(&self, id: &I, max_duration: <EmbassyClock as Clock>::Duration) {
957 self.watchdog
958 .lock()
959 .await
960 .borrow_mut()
961 .register_task(id, max_duration)
962 .ok();
963 }
964
965 pub async fn deregister_task(&self, id: &I) {
967 self.watchdog.lock().await.borrow_mut().deregister_task(id);
968 }
969
970 pub async fn feed(&self, id: &I) {
972 self.watchdog.lock().await.borrow_mut().feed(id);
973 }
974
975 pub async fn start(&self) {
977 self.watchdog.lock().await.borrow_mut().start();
978 }
979
980 pub async fn trigger_reset(&self) -> ! {
982 self.watchdog.lock().await.borrow_mut().trigger_reset()
983 }
984
985 pub async fn reset_reason(&self) -> Option<ResetReason> {
987 self.watchdog.lock().await.borrow().reset_reason()
988 }
989
990 pub async fn get_check_interval(&self) -> <EmbassyClock as Clock>::Duration {
992 self.watchdog.lock().await.borrow().config.check_interval
993 }
994
995 pub async fn check_tasks(&self) -> bool {
997 self.watchdog.lock().await.borrow_mut().check()
998 }
999 }
1000
1001 #[cfg(feature = "alloc")]
1003 pub struct WatchdogTask<I>
1004 where
1005 I: 'static + Id,
1006 {
1007 runner: &'static WatchdogRunner<I>,
1008 }
1009
1010 #[cfg(feature = "alloc")]
1011 impl<I> WatchdogRunner<I>
1012 where
1013 I: 'static + Id,
1014 {
1015 pub fn create_task(&'static self) -> WatchdogTask<I> {
1016 WatchdogTask { runner: self }
1017 }
1018 }
1019
1020 #[cfg(feature = "alloc")]
1021 pub async fn watchdog_run<I>(task: WatchdogTask<I>) -> !
1022 where
1023 I: 'static + Id,
1024 {
1025 info!("Watchdog runner started");
1026
1027 task.runner.start().await;
1029
1030 let interval = task.runner.get_check_interval().await;
1032 let mut check_time = Instant::now() + interval;
1033
1034 loop {
1035 let _ = task.runner.check_tasks().await;
1037
1038 Timer::at(check_time).await;
1040 check_time += interval;
1041 }
1042 }
1043
1044 #[cfg(not(feature = "alloc"))]
1049 pub struct NoAllocWatchdogTask<I, const N: usize>
1050 where
1051 I: 'static + Id,
1052 {
1053 runner: &'static WatchdogRunner<I, N>,
1054 }
1055
1056 #[cfg(not(feature = "alloc"))]
1057 impl<I, const N: usize> WatchdogRunner<I, N>
1058 where
1059 I: 'static + Id,
1060 {
1061 pub fn create_task(&'static self) -> NoAllocWatchdogTask<I, N> {
1066 NoAllocWatchdogTask { runner: self }
1067 }
1068 }
1069
1070 #[cfg(not(feature = "alloc"))]
1077 pub async fn watchdog_run<I, const N: usize>(task: NoAllocWatchdogTask<I, N>) -> !
1078 where
1079 I: 'static + Id,
1080 {
1081 info!("Watchdog runner started");
1082
1083 task.runner.start().await;
1085
1086 let interval = task.runner.get_check_interval().await;
1088 let mut check_time = Instant::now() + interval;
1089
1090 loop {
1091 let _ = task.runner.check_tasks().await;
1095
1096 Timer::at(check_time).await;
1098 check_time += interval;
1099 }
1100 }
1101}
1102
1103#[cfg(feature = "stm32-embassy")]
1108pub mod embassy_stm32 {
1109 use super::{
1110 info, Clock, EmbassyClock, HardwareWatchdog, Id, ResetReason, Watchdog, WatchdogConfig,
1111 };
1112 use embassy_stm32::peripherals::IWDG;
1113 use embassy_stm32::wdg::IndependentWatchdog;
1114 use embassy_time::{Instant, Timer};
1115
1116 pub struct Stm32Watchdog {
1118 peripheral: Option<IWDG>,
1119 inner: Option<IndependentWatchdog<'static, IWDG>>,
1120 }
1121
1122 impl Stm32Watchdog {
1123 #[must_use]
1125 pub fn new(peripheral: IWDG) -> Self {
1126 Self {
1127 peripheral: Some(peripheral),
1128 inner: None,
1129 }
1130 }
1131 }
1132
1133 impl HardwareWatchdog<EmbassyClock> for Stm32Watchdog {
1134 fn start(&mut self, timeout: embassy_time::Duration) {
1135 let timeout = timeout.as_micros();
1136 if timeout > u32::MAX as u64 {
1137 panic!("Watchdog timeout too large for STM32");
1138 }
1139 let peripheral = self
1140 .peripheral
1141 .take()
1142 .expect("STM32 Watchdog not properly initialized");
1143
1144 let mut wdg = IndependentWatchdog::new(peripheral, timeout as u32);
1146
1147 wdg.unleash();
1149
1150 self.inner = Some(wdg);
1152 }
1153
1154 fn feed(&mut self) {
1155 self.inner.as_mut().expect("Watchdog not started").pet();
1156 }
1157
1158 fn trigger_reset(&mut self) -> ! {
1159 cortex_m::peripheral::SCB::sys_reset();
1160 }
1161
1162 fn reset_reason(&self) -> Option<ResetReason> {
1163 None
1164 }
1165 }
1166
1167 #[cfg(feature = "alloc")]
1169 pub struct WatchdogRunner<I>
1170 where
1171 I: Id,
1172 {
1173 watchdog: embassy_sync::mutex::Mutex<
1174 embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex,
1175 core::cell::RefCell<Watchdog<I, Stm32Watchdog, EmbassyClock>>,
1176 >,
1177 }
1178
1179 #[cfg(not(feature = "alloc"))]
1180 pub struct WatchdogRunner<I, const N: usize>
1181 where
1182 I: Id,
1183 {
1184 watchdog: embassy_sync::mutex::Mutex<
1185 embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex,
1186 core::cell::RefCell<Watchdog<I, N, Stm32Watchdog, EmbassyClock>>,
1187 >,
1188 }
1189
1190 #[cfg(feature = "alloc")]
1191 impl<I> WatchdogRunner<I>
1192 where
1193 I: Id + 'static,
1194 {
1195 pub fn new(hw_watchdog: IWDG, config: WatchdogConfig<EmbassyClock>) -> Self {
1197 let hw_watchdog = Stm32Watchdog::new(hw_watchdog);
1198 let watchdog = Watchdog::new(hw_watchdog, config, EmbassyClock);
1199 Self {
1200 watchdog: embassy_sync::mutex::Mutex::new(core::cell::RefCell::new(watchdog)),
1201 }
1202 }
1203
1204 pub async fn register_task(&self, id: &I, max_duration: <EmbassyClock as Clock>::Duration) {
1206 self.watchdog
1207 .lock()
1208 .await
1209 .borrow_mut()
1210 .register_task(id, max_duration);
1211 }
1212
1213 pub async fn deregister_task(&self, id: &I) {
1215 self.watchdog.lock().await.borrow_mut().deregister_task(id);
1216 }
1217
1218 pub async fn feed(&self, id: &I) {
1220 self.watchdog.lock().await.borrow_mut().feed(id);
1221 }
1222
1223 pub async fn start(&self) {
1225 self.watchdog.lock().await.borrow_mut().start();
1226 }
1227
1228 pub async fn trigger_reset(&self) -> ! {
1230 self.watchdog.lock().await.borrow_mut().trigger_reset()
1231 }
1232
1233 pub async fn reset_reason(&self) -> Option<ResetReason> {
1235 self.watchdog.lock().await.borrow().reset_reason()
1236 }
1237
1238 pub async fn get_check_interval(&self) -> <EmbassyClock as Clock>::Duration {
1240 self.watchdog.lock().await.borrow().config.check_interval
1241 }
1242
1243 pub async fn check_tasks(&self) -> bool {
1245 self.watchdog.lock().await.borrow_mut().check()
1246 }
1247 }
1248
1249 #[cfg(not(feature = "alloc"))]
1250 impl<I, const N: usize> WatchdogRunner<I, N>
1251 where
1252 I: Id,
1253 {
1254 pub fn new(hw_watchdog: IWDG, config: WatchdogConfig<EmbassyClock>) -> Self {
1256 let hw_watchdog = Stm32Watchdog::new(hw_watchdog);
1257 let watchdog = Watchdog::new(hw_watchdog, config, EmbassyClock);
1258 Self {
1259 watchdog: embassy_sync::mutex::Mutex::new(core::cell::RefCell::new(watchdog)),
1260 }
1261 }
1262
1263 pub async fn register_task(&self, id: &I, max_duration: <EmbassyClock as Clock>::Duration) {
1265 self.watchdog
1266 .lock()
1267 .await
1268 .borrow_mut()
1269 .register_task(id, max_duration)
1270 .ok();
1271 }
1272
1273 pub async fn deregister_task(&self, id: &I) {
1275 self.watchdog.lock().await.borrow_mut().deregister_task(id);
1276 }
1277
1278 pub async fn feed(&self, id: &I) {
1280 self.watchdog.lock().await.borrow_mut().feed(id);
1281 }
1282
1283 pub async fn start(&self) {
1285 self.watchdog.lock().await.borrow_mut().start();
1286 }
1287
1288 pub async fn trigger_reset(&self) -> ! {
1290 self.watchdog.lock().await.borrow_mut().trigger_reset()
1291 }
1292
1293 pub async fn reset_reason(&self) -> Option<ResetReason> {
1295 self.watchdog.lock().await.borrow().reset_reason()
1296 }
1297
1298 pub async fn get_check_interval(&self) -> <EmbassyClock as Clock>::Duration {
1300 self.watchdog.lock().await.borrow().config.check_interval
1301 }
1302
1303 pub async fn check_tasks(&self) -> bool {
1305 self.watchdog.lock().await.borrow_mut().check()
1306 }
1307 }
1308
1309 #[cfg(feature = "alloc")]
1311 pub struct WatchdogTask<I>
1312 where
1313 I: 'static + Id,
1314 {
1315 runner: &'static WatchdogRunner<I>,
1316 }
1317
1318 #[cfg(feature = "alloc")]
1319 impl<I> WatchdogRunner<I>
1320 where
1321 I: 'static + Id,
1322 {
1323 pub fn create_task(&'static self) -> WatchdogTask<I> {
1324 WatchdogTask { runner: self }
1325 }
1326 }
1327
1328 #[cfg(feature = "alloc")]
1332 pub async fn watchdog_run<I>(task: WatchdogTask<I>) -> !
1333 where
1334 I: 'static + Id,
1335 {
1336 info!("Watchdog runner started");
1337
1338 task.runner.start().await;
1340
1341 let interval = task.runner.get_check_interval().await;
1343 let mut check_time = Instant::now() + interval;
1344
1345 loop {
1346 let _ = task.runner.check_tasks().await;
1350
1351 Timer::at(check_time).await;
1353 check_time += interval;
1354 }
1355 }
1356
1357 #[cfg(not(feature = "alloc"))]
1359 pub struct NoAllocWatchdogTask<I, const N: usize>
1360 where
1361 I: 'static + Id,
1362 {
1363 runner: &'static WatchdogRunner<I, N>,
1364 }
1365
1366 #[cfg(not(feature = "alloc"))]
1367 impl<I, const N: usize> WatchdogRunner<I, N>
1368 where
1369 I: 'static + Id,
1370 {
1371 pub fn create_task(&'static self) -> NoAllocWatchdogTask<I, N> {
1372 NoAllocWatchdogTask { runner: self }
1373 }
1374 }
1375
1376 #[cfg(not(feature = "alloc"))]
1379 pub async fn watchdog_run<I, const N: usize>(task: NoAllocWatchdogTask<I, N>) -> !
1380 where
1381 I: 'static + Id,
1382 {
1383 info!("Watchdog runner started");
1384
1385 task.runner.start().await;
1387
1388 let interval = task.runner.get_check_interval().await;
1390 let mut check_time = Instant::now() + interval;
1391
1392 loop {
1393 let _ = task.runner.check_tasks().await;
1397
1398 Timer::at(check_time).await;
1400 check_time += interval;
1401 }
1402 }
1403}
1404
1405#[cfg(feature = "nrf-embassy")]
1410pub mod embassy_nrf {
1411 use super::{
1412 info, warn, Clock, EmbassyClock, HardwareWatchdog, Id, ResetReason, Watchdog, WatchdogConfig,
1413 };
1414 use embassy_nrf::peripherals::WDT;
1415 use embassy_nrf::wdt::{Watchdog as NrfWatchdogStruct, WatchdogHandle, Config};
1416 use embassy_time::{Instant, Timer};
1417
1418 pub struct NrfWatchdog {
1420 peripheral: Option<WDT>,
1421 inner: Option<WatchdogHandle>,
1422 }
1423
1424 impl NrfWatchdog {
1425 #[must_use]
1427 pub fn new(peripheral: WDT) -> Self {
1428 Self {
1429 peripheral: Some(peripheral),
1430 inner: None,
1431 }
1432 }
1433 }
1434
1435 impl HardwareWatchdog<EmbassyClock> for NrfWatchdog {
1436 fn start(&mut self, timeout: embassy_time::Duration) {
1437 let ticks = timeout.as_micros() * 1_000_000 / 32_768_000;
1439 if ticks < 15 {
1440 warn!("Watchdog timeout {} ticks too small for nRF - will be set to 15 ticks", ticks);
1441 panic!("Watchdog timeout too large for nRF");
1442 }
1443 let mut config = Config::default();
1444 if ticks > u32::MAX as u64 {
1445 panic!("Watchdog timeout {} ticks too large for nRF", ticks);
1446 }
1447 config.timeout_ticks = ticks as u32;
1448 let peripheral = self
1449 .peripheral
1450 .take()
1451 .expect("nRF Watchdog not properly initialized");
1452
1453 let (_wdt, [handle]) = NrfWatchdogStruct::try_new(peripheral, config).unwrap_or_else(|_| panic!("Failed to create nRF watchdog"));
1455 self.inner = Some(handle);
1456 }
1457
1458 fn feed(&mut self) {
1459 self.inner.as_mut().expect("Watchdog not started").pet();
1460 }
1461
1462 fn trigger_reset(&mut self) -> ! {
1463 cortex_m::peripheral::SCB::sys_reset();
1464 }
1465
1466 fn reset_reason(&self) -> Option<ResetReason> {
1467 None
1468 }
1469 }
1470
1471 #[cfg(feature = "alloc")]
1473 pub struct WatchdogRunner<I>
1474 where
1475 I: Id,
1476 {
1477 watchdog: embassy_sync::mutex::Mutex<
1478 embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex,
1479 core::cell::RefCell<Watchdog<I, NrfWatchdog, EmbassyClock>>,
1480 >,
1481 }
1482
1483 #[cfg(not(feature = "alloc"))]
1484 pub struct WatchdogRunner<I, const N: usize>
1485 where
1486 I: Id,
1487 {
1488 watchdog: embassy_sync::mutex::Mutex<
1489 embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex,
1490 core::cell::RefCell<Watchdog<I, N, NrfWatchdog, EmbassyClock>>,
1491 >,
1492 }
1493
1494 #[cfg(feature = "alloc")]
1495 impl<I> WatchdogRunner<I>
1496 where
1497 I: Id + 'static,
1498 {
1499 pub fn new(hw_watchdog: WDT, config: WatchdogConfig<EmbassyClock>) -> Self {
1501 let hw_watchdog = NrfWatchdog::new(hw_watchdog);
1502 let watchdog = Watchdog::new(hw_watchdog, config, EmbassyClock);
1503 Self {
1504 watchdog: embassy_sync::mutex::Mutex::new(core::cell::RefCell::new(watchdog)),
1505 }
1506 }
1507
1508 pub async fn register_task(&self, id: &I, max_duration: <EmbassyClock as Clock>::Duration) {
1510 self.watchdog
1511 .lock()
1512 .await
1513 .borrow_mut()
1514 .register_task(id, max_duration);
1515 }
1516
1517 pub async fn deregister_task(&self, id: &I) {
1519 self.watchdog.lock().await.borrow_mut().deregister_task(id);
1520 }
1521
1522 pub async fn feed(&self, id: &I) {
1524 self.watchdog.lock().await.borrow_mut().feed(id);
1525 }
1526
1527 pub async fn start(&self) {
1529 self.watchdog.lock().await.borrow_mut().start();
1530 }
1531
1532 pub async fn trigger_reset(&self) -> ! {
1534 self.watchdog.lock().await.borrow_mut().trigger_reset()
1535 }
1536
1537 pub async fn reset_reason(&self) -> Option<ResetReason> {
1539 self.watchdog.lock().await.borrow().reset_reason()
1540 }
1541
1542 pub async fn get_check_interval(&self) -> <EmbassyClock as Clock>::Duration {
1544 self.watchdog.lock().await.borrow().config.check_interval
1545 }
1546
1547 pub async fn check_tasks(&self) -> bool {
1549 self.watchdog.lock().await.borrow_mut().check()
1550 }
1551 }
1552
1553 #[cfg(not(feature = "alloc"))]
1554 impl<I, const N: usize> WatchdogRunner<I, N>
1555 where
1556 I: Id,
1557 {
1558 pub fn new(hw_watchdog: WDT, config: WatchdogConfig<EmbassyClock>) -> Self {
1560 let hw_watchdog = NrfWatchdog::new(hw_watchdog);
1561 let watchdog = Watchdog::new(hw_watchdog, config, EmbassyClock);
1562 Self {
1563 watchdog: embassy_sync::mutex::Mutex::new(core::cell::RefCell::new(watchdog)),
1564 }
1565 }
1566
1567 pub async fn register_task(&self, id: &I, max_duration: <EmbassyClock as Clock>::Duration) {
1569 self.watchdog
1570 .lock()
1571 .await
1572 .borrow_mut()
1573 .register_task(id, max_duration)
1574 .ok();
1575 }
1576
1577 pub async fn deregister_task(&self, id: &I) {
1579 self.watchdog.lock().await.borrow_mut().deregister_task(id);
1580 }
1581
1582 pub async fn feed(&self, id: &I) {
1584 self.watchdog.lock().await.borrow_mut().feed(id);
1585 }
1586
1587 pub async fn start(&self) {
1589 self.watchdog.lock().await.borrow_mut().start();
1590 }
1591
1592 pub async fn trigger_reset(&self) -> ! {
1594 self.watchdog.lock().await.borrow_mut().trigger_reset()
1595 }
1596
1597 pub async fn reset_reason(&self) -> Option<ResetReason> {
1599 self.watchdog.lock().await.borrow().reset_reason()
1600 }
1601
1602 pub async fn get_check_interval(&self) -> <EmbassyClock as Clock>::Duration {
1604 self.watchdog.lock().await.borrow().config.check_interval
1605 }
1606
1607 pub async fn check_tasks(&self) -> bool {
1609 self.watchdog.lock().await.borrow_mut().check()
1610 }
1611 }
1612
1613 #[cfg(feature = "alloc")]
1615 pub struct WatchdogTask<I>
1616 where
1617 I: 'static + Id,
1618 {
1619 runner: &'static WatchdogRunner<I>,
1620 }
1621
1622 #[cfg(feature = "alloc")]
1623 impl<I> WatchdogRunner<I>
1624 where
1625 I: 'static + Id,
1626 {
1627 pub fn create_task(&'static self) -> WatchdogTask<I> {
1628 WatchdogTask { runner: self }
1629 }
1630 }
1631
1632 #[cfg(feature = "alloc")]
1636 pub async fn watchdog_run<I>(task: WatchdogTask<I>) -> !
1637 where
1638 I: 'static + Id,
1639 {
1640 info!("Watchdog runner started");
1641
1642 task.runner.start().await;
1644
1645 let interval = task.runner.get_check_interval().await;
1647 let mut check_time = Instant::now() + interval;
1648
1649 loop {
1650 let _ = task.runner.check_tasks().await;
1654
1655 Timer::at(check_time).await;
1657 check_time += interval;
1658 }
1659 }
1660
1661 #[cfg(not(feature = "alloc"))]
1663 pub struct NoAllocWatchdogTask<I, const N: usize>
1664 where
1665 I: 'static + Id,
1666 {
1667 runner: &'static WatchdogRunner<I, N>,
1668 }
1669
1670 #[cfg(not(feature = "alloc"))]
1671 impl<I, const N: usize> WatchdogRunner<I, N>
1672 where
1673 I: 'static + Id,
1674 {
1675 pub fn create_task(&'static self) -> NoAllocWatchdogTask<I, N> {
1676 NoAllocWatchdogTask { runner: self }
1677 }
1678 }
1679
1680 #[cfg(not(feature = "alloc"))]
1683 pub async fn watchdog_run<I, const N: usize>(task: NoAllocWatchdogTask<I, N>) -> !
1684 where
1685 I: 'static + Id,
1686 {
1687 info!("Watchdog runner started");
1688
1689 task.runner.start().await;
1691
1692 let interval = task.runner.get_check_interval().await;
1694 let mut check_time = Instant::now() + interval;
1695
1696 loop {
1697 let _ = task.runner.check_tasks().await;
1701
1702 Timer::at(check_time).await;
1704 check_time += interval;
1705 }
1706 }
1707}
1708
1709#[cfg(feature = "esp32-embassy")]
1714pub mod embassy_esp32 {
1715 use super::{
1716 info, Clock, EmbassyClock, HardwareWatchdog, Id, ResetReason, Watchdog, WatchdogConfig,
1717 };
1718 use embassy_time::{Instant, Timer};
1719 use esp_hal::peripherals::TIMG0;
1720 use esp_hal::timer::timg::MwdtStage;
1721 use esp_hal::timer::timg::TimerGroup;
1722 use esp_hal::timer::timg::Wdt;
1723
1724 pub struct Esp32Watchdog {
1726 inner: Wdt<TIMG0>,
1727 }
1728
1729 impl Esp32Watchdog {
1730 #[must_use]
1735 pub fn new(timg0: TimerGroup<TIMG0>) -> Self {
1736 let wdt = timg0.wdt;
1737 Self { inner: wdt }
1738 }
1739 }
1740
1741 impl HardwareWatchdog<EmbassyClock> for Esp32Watchdog {
1742 fn start(&mut self, timeout: embassy_time::Duration) {
1743 self.inner.set_timeout(
1744 MwdtStage::Stage0,
1745 esp_hal::time::Duration::from_millis(timeout.as_millis()),
1746 );
1747 self.inner.enable();
1748 }
1749
1750 fn feed(&mut self) {
1751 self.inner.feed();
1752 }
1753
1754 fn trigger_reset(&mut self) -> ! {
1755 esp_hal::system::software_reset();
1756 }
1757
1758 fn reset_reason(&self) -> Option<ResetReason> {
1759 None
1760 }
1761 }
1762
1763 #[cfg(feature = "alloc")]
1765 pub struct WatchdogRunner<I>
1766 where
1767 I: Id,
1768 {
1769 watchdog: embassy_sync::mutex::Mutex<
1770 embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex,
1771 core::cell::RefCell<Watchdog<I, Esp32Watchdog, EmbassyClock>>,
1772 >,
1773 }
1774
1775 #[cfg(not(feature = "alloc"))]
1776 pub struct WatchdogRunner<I, const N: usize>
1777 where
1778 I: Id,
1779 {
1780 watchdog: embassy_sync::mutex::Mutex<
1781 embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex,
1782 core::cell::RefCell<Watchdog<I, N, Esp32Watchdog, EmbassyClock>>,
1783 >,
1784 }
1785
1786 #[cfg(feature = "alloc")]
1787 impl<I> WatchdogRunner<I>
1788 where
1789 I: Id + 'static,
1790 {
1791 pub fn new(timg0: TimerGroup<TIMG0>, config: WatchdogConfig<EmbassyClock>) -> Self {
1793 let hw_watchdog = Esp32Watchdog::new(timg0);
1794 let watchdog = Watchdog::new(hw_watchdog, config, EmbassyClock);
1795 Self {
1796 watchdog: embassy_sync::mutex::Mutex::new(core::cell::RefCell::new(watchdog)),
1797 }
1798 }
1799
1800 pub async fn register_task(&self, id: &I, max_duration: <EmbassyClock as Clock>::Duration) {
1802 self.watchdog
1803 .lock()
1804 .await
1805 .borrow_mut()
1806 .register_task(id, max_duration);
1807 }
1808
1809 pub async fn deregister_task(&self, id: &I) {
1811 self.watchdog.lock().await.borrow_mut().deregister_task(id);
1812 }
1813
1814 pub async fn feed(&self, id: &I) {
1816 self.watchdog.lock().await.borrow_mut().feed(id);
1817 }
1818
1819 pub async fn start(&self) {
1821 self.watchdog.lock().await.borrow_mut().start();
1822 }
1823
1824 pub async fn trigger_reset(&self) -> ! {
1826 self.watchdog.lock().await.borrow_mut().trigger_reset()
1827 }
1828
1829 pub async fn reset_reason(&self) -> Option<ResetReason> {
1831 self.watchdog.lock().await.borrow().reset_reason()
1832 }
1833
1834 pub async fn get_check_interval(&self) -> <EmbassyClock as Clock>::Duration {
1836 self.watchdog.lock().await.borrow().config.check_interval
1837 }
1838
1839 pub async fn check_tasks(&self) -> bool {
1841 self.watchdog.lock().await.borrow_mut().check()
1842 }
1843 }
1844
1845 #[cfg(not(feature = "alloc"))]
1846 impl<I, const N: usize> WatchdogRunner<I, N>
1847 where
1848 I: Id,
1849 {
1850 pub fn new(timg0: TimerGroup<TIMG0>, config: WatchdogConfig<EmbassyClock>) -> Self {
1852 let hw_watchdog = Esp32Watchdog::new(timg0);
1853 let watchdog = Watchdog::new(hw_watchdog, config, EmbassyClock);
1854 Self {
1855 watchdog: embassy_sync::mutex::Mutex::new(core::cell::RefCell::new(watchdog)),
1856 }
1857 }
1858
1859 pub async fn register_task(&self, id: &I, max_duration: <EmbassyClock as Clock>::Duration) {
1861 self.watchdog
1862 .lock()
1863 .await
1864 .borrow_mut()
1865 .register_task(id, max_duration)
1866 .ok();
1867 }
1868
1869 pub async fn deregister_task(&self, id: &I) {
1871 self.watchdog.lock().await.borrow_mut().deregister_task(id);
1872 }
1873
1874 pub async fn feed(&self, id: &I) {
1876 self.watchdog.lock().await.borrow_mut().feed(id);
1877 }
1878
1879 pub async fn start(&self) {
1881 self.watchdog.lock().await.borrow_mut().start();
1882 }
1883
1884 pub async fn trigger_reset(&self) -> ! {
1886 self.watchdog.lock().await.borrow_mut().trigger_reset()
1887 }
1888
1889 pub async fn reset_reason(&self) -> Option<ResetReason> {
1891 self.watchdog.lock().await.borrow().reset_reason()
1892 }
1893
1894 pub async fn get_check_interval(&self) -> <EmbassyClock as Clock>::Duration {
1896 self.watchdog.lock().await.borrow().config.check_interval
1897 }
1898
1899 pub async fn check_tasks(&self) -> bool {
1901 self.watchdog.lock().await.borrow_mut().check()
1902 }
1903 }
1904
1905 #[cfg(feature = "alloc")]
1907 pub struct WatchdogTask<I>
1908 where
1909 I: 'static + Id,
1910 {
1911 runner: &'static WatchdogRunner<I>,
1912 }
1913
1914 #[cfg(feature = "alloc")]
1915 impl<I> WatchdogRunner<I>
1916 where
1917 I: 'static + Id,
1918 {
1919 pub fn create_task(&'static self) -> WatchdogTask<I> {
1920 WatchdogTask { runner: self }
1921 }
1922 }
1923
1924 #[cfg(feature = "alloc")]
1928 pub async fn watchdog_run<I>(task: WatchdogTask<I>) -> !
1929 where
1930 I: 'static + Id,
1931 {
1932 info!("Watchdog runner started");
1933
1934 task.runner.start().await;
1936
1937 let interval = task.runner.get_check_interval().await;
1939 let mut check_time = Instant::now() + interval;
1940
1941 loop {
1942 let _ = task.runner.check_tasks().await;
1946
1947 Timer::at(check_time).await;
1949 check_time += interval;
1950 }
1951 }
1952
1953 #[cfg(not(feature = "alloc"))]
1955 pub struct NoAllocWatchdogTask<I, const N: usize>
1956 where
1957 I: 'static + Id,
1958 {
1959 runner: &'static WatchdogRunner<I, N>,
1960 }
1961
1962 #[cfg(not(feature = "alloc"))]
1963 impl<I, const N: usize> WatchdogRunner<I, N>
1964 where
1965 I: 'static + Id,
1966 {
1967 pub fn create_task(&'static self) -> NoAllocWatchdogTask<I, N> {
1968 NoAllocWatchdogTask { runner: self }
1969 }
1970 }
1971
1972 #[cfg(not(feature = "alloc"))]
1975 pub async fn watchdog_run<I, const N: usize>(task: NoAllocWatchdogTask<I, N>) -> !
1976 where
1977 I: 'static + Id,
1978 {
1979 info!("Watchdog runner started");
1980
1981 task.runner.start().await;
1983
1984 let interval = task.runner.get_check_interval().await;
1986 let mut check_time = Instant::now() + interval;
1987
1988 loop {
1989 let _ = task.runner.check_tasks().await;
1993
1994 Timer::at(check_time).await;
1996 check_time += interval;
1997 }
1998 }
1999}