use crate::time::{Milliseconds, Seconds};
use embedded_hal::watchdog::{Watchdog as WatchdogTrait, WatchdogDisable, WatchdogEnable};
use esp8266::WDT;
const WATCHDOG_RESET_MAGIC: u32 = 0x73;
pub trait WatchdogExt {
fn watchdog(self) -> Watchdog;
}
impl WatchdogExt for WDT {
fn watchdog(self) -> Watchdog {
Watchdog { watchdog: self }
}
}
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
#[repr(u8)]
pub enum StageTimeout {
StageDisabled = 0,
Stage084Sec = 10,
Stage168Sec = 11,
Stage336Sec = 12,
Stage671Sec = 13,
Stage1342Sec = 14,
Stage2684Sec = 15,
}
impl From<StageTimeout> for Milliseconds {
fn from(timeout: StageTimeout) -> Milliseconds {
match timeout {
StageTimeout::StageDisabled => Milliseconds(0),
StageTimeout::Stage084Sec => Milliseconds(840),
StageTimeout::Stage168Sec => Milliseconds(1_680),
StageTimeout::Stage336Sec => Milliseconds(3_360),
StageTimeout::Stage671Sec => Milliseconds(6_710),
StageTimeout::Stage1342Sec => Milliseconds(13_420),
StageTimeout::Stage2684Sec => Milliseconds(26_840),
}
}
}
impl From<Milliseconds> for StageTimeout {
fn from(ms: Milliseconds) -> StageTimeout {
StageTimeout::from_upper_bound(ms)
}
}
impl StageTimeout {
fn from_lower_bound(ms: Milliseconds) -> StageTimeout {
if ms == StageTimeout::StageDisabled.into() {
StageTimeout::StageDisabled
} else if ms <= StageTimeout::Stage168Sec.into() {
StageTimeout::Stage084Sec
} else if ms <= StageTimeout::Stage336Sec.into() {
StageTimeout::Stage168Sec
} else if ms <= StageTimeout::Stage671Sec.into() {
StageTimeout::Stage336Sec
} else if ms <= StageTimeout::Stage1342Sec.into() {
StageTimeout::Stage671Sec
} else if ms <= StageTimeout::Stage2684Sec.into() {
StageTimeout::Stage1342Sec
} else {
StageTimeout::Stage2684Sec
}
}
fn from_upper_bound(ms: Milliseconds) -> StageTimeout {
if ms == StageTimeout::StageDisabled.into() {
StageTimeout::StageDisabled
} else if ms <= StageTimeout::Stage084Sec.into() {
StageTimeout::Stage084Sec
} else if ms <= StageTimeout::Stage168Sec.into() {
StageTimeout::Stage168Sec
} else if ms <= StageTimeout::Stage336Sec.into() {
StageTimeout::Stage336Sec
} else if ms <= StageTimeout::Stage671Sec.into() {
StageTimeout::Stage671Sec
} else if ms <= StageTimeout::Stage1342Sec.into() {
StageTimeout::Stage1342Sec
} else {
StageTimeout::Stage2684Sec
}
}
}
impl From<StageTimeout> for (StageTimeout, StageTimeout) {
fn from(stage0: StageTimeout) -> (StageTimeout, StageTimeout) {
(stage0, StageTimeout::StageDisabled)
}
}
impl From<Milliseconds> for (StageTimeout, StageTimeout) {
fn from(ms: Milliseconds) -> (StageTimeout, StageTimeout) {
let stage0 = StageTimeout::from_lower_bound(ms);
let stage1_ms = Milliseconds(ms.0.saturating_sub(Milliseconds::from(stage0).0));
let stage1 = StageTimeout::from_upper_bound(stage1_ms);
(stage0, stage1)
}
}
impl From<Seconds> for (StageTimeout, StageTimeout) {
fn from(s: Seconds) -> (StageTimeout, StageTimeout) {
Self::from(Milliseconds::from(s))
}
}
#[test]
fn test_time_into_timeout() {
use crate::time::U32Ext;
assert_eq!(
(StageTimeout::StageDisabled, StageTimeout::StageDisabled),
0.ms().into()
);
assert_eq!(
(StageTimeout::Stage084Sec, StageTimeout::StageDisabled),
500.ms().into()
);
assert_eq!(
(StageTimeout::Stage084Sec, StageTimeout::Stage084Sec),
1500.ms().into()
);
assert_eq!(
(StageTimeout::Stage168Sec, StageTimeout::Stage084Sec),
2500.ms().into()
);
assert_eq!(
(StageTimeout::Stage2684Sec, StageTimeout::Stage084Sec),
27000.ms().into()
);
assert_eq!(
(StageTimeout::Stage2684Sec, StageTimeout::Stage2684Sec),
50000.ms().into()
);
assert_eq!(
(StageTimeout::Stage2684Sec, StageTimeout::Stage2684Sec),
100000.ms().into()
);
}
#[test]
fn test_time_into_timeout_is_higher() {
use crate::time::U32Ext;
for period in (1..53680).step_by(100) {
let (stage0, stage1) = period.ms().into();
let sum_ms = Milliseconds::from(stage0).0 + Milliseconds::from(stage1).0;
assert!(
sum_ms >= period,
"converted period {} is not more than input time {}. stages: {:?}, {:?}",
sum_ms,
period,
stage0,
stage1
);
}
}
pub struct Watchdog {
watchdog: WDT,
}
impl WatchdogTrait for Watchdog {
fn feed(&mut self) {
self.watchdog
.wdt_rst
.write(|w| unsafe { w.bits(WATCHDOG_RESET_MAGIC) })
}
}
impl WatchdogEnable for Watchdog {
type Time = (StageTimeout, StageTimeout);
fn start<T>(&mut self, period: T)
where
T: Into<Self::Time>,
{
let (period0, period1) = period.into();
if period0 == StageTimeout::StageDisabled {
return;
}
self.watchdog.wdt_ctl.write(|w| {
w.unknown_3()
.set_bit()
.unknown_4()
.set_bit()
.unknown_5()
.set_bit()
});
self.watchdog
.wdt_op
.write(|w| unsafe { w.bits(period0 as u8 as u32) });
if period1 == StageTimeout::StageDisabled {
self.watchdog
.wdt_ctl
.modify(|_, w| w.stage_1_disable().set_bit());
} else {
self.watchdog
.wdt_op_nd
.write(|w| unsafe { w.bits(period1 as u8 as u32) });
}
self.feed();
self.watchdog.wdt_ctl.modify(|_, w| w.enable().set_bit());
}
}
impl WatchdogDisable for Watchdog {
fn disable(&mut self) {
self.watchdog.wdt_ctl.modify(|_, w| w.enable().clear_bit());
}
}