From 0e4faf647347a7f639a6445ef6d1d46dd621c47b Mon Sep 17 00:00:00 2001 From: Oyvind Netland Date: Sun, 13 Aug 2023 23:48:01 +0200 Subject: [PATCH] sleep: Adds module for light and deep sleep with examples --- examples/deep_sleep.rs | 36 +++++++++++++ examples/light_sleep.rs | 65 ++++++++++++++++++++++++ src/lib.rs | 2 + src/sleep.rs | 110 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 213 insertions(+) create mode 100644 examples/deep_sleep.rs create mode 100644 examples/light_sleep.rs create mode 100644 src/sleep.rs diff --git a/examples/deep_sleep.rs b/examples/deep_sleep.rs new file mode 100644 index 00000000000..b8f39c6cc4b --- /dev/null +++ b/examples/deep_sleep.rs @@ -0,0 +1,36 @@ +//! Tests deep sleep +//! +//! Prints reset and wakeup reason on start, before enabling mutiple +//! wakeup sources and start deep sleep. Deep sleep will reset the +//! device, so after sleep it will restart the application. + +use esp_idf_hal::reset::ResetReason; +use esp_idf_hal::reset::WakeupReason; +use esp_idf_hal::sleep::DeepSleep; +use esp_idf_hal::sleep::Sleep; +use esp_idf_hal::sleep::WakeupSource; +use std::thread; +use std::time::Duration; + +fn main() -> anyhow::Result<()> { + esp_idf_sys::link_patches(); + + let reset_reason = ResetReason::get(); + let wakeup_reason = WakeupReason::get(); + println!( + "start up after reset {:?} wake up reason {:?}", + reset_reason, wakeup_reason + ); + + thread::sleep(Duration::from_secs(2)); + + let mut dsleep = DeepSleep::default(); + dsleep.add_wakeup_source(WakeupSource::Timer { + dur: Duration::from_secs(5), + })?; + println!("{:?}", dsleep); + + dsleep.sleep()?; + + Ok(()) +} diff --git a/examples/light_sleep.rs b/examples/light_sleep.rs new file mode 100644 index 00000000000..2d976ce84ef --- /dev/null +++ b/examples/light_sleep.rs @@ -0,0 +1,65 @@ +//! Tests light sleep +//! +//! Enables multiple light sleep wakeup sources and do sleeps in a loop. +//! Prints wakeup reason and sleep time on wakeup. + +use esp_idf_hal::reset::WakeupReason; +use esp_idf_hal::sleep::LightSleep; +use esp_idf_hal::sleep::Sleep; +use esp_idf_hal::sleep::WakeupSource; +use std::thread; +use std::time::Duration; +use std::time::Instant; + +fn print_wakeup_result(time_before: Instant) { + let time_after = Instant::now(); + + let wakeup_reason = WakeupReason::get(); + println!( + "wake up from light sleep due to {:?} which lasted for {:?}", + wakeup_reason, + time_after - time_before + ); +} + +fn main() -> anyhow::Result<()> { + esp_idf_sys::link_patches(); + + println!("test timer light sleep oneliner first:"); + thread::sleep(Duration::from_millis(20)); + + let time_before = Instant::now(); + LightSleep::timer(Duration::from_secs(5))?.sleep()?; + print_wakeup_result(time_before); + + // run in a thread with increased stack size to prevent overflow + let builder = std::thread::Builder::new().stack_size(8 * 1024); + let th = builder.spawn(move || -> anyhow::Result<()> { + let mut lsleep = LightSleep::default(); + lsleep.add_wakeup_source(WakeupSource::Timer { + dur: Duration::from_secs(5), + })?; + lsleep.add_wakeup_source(WakeupSource::Uart { + uart_num: 0, + threshold: 3, + })?; + + loop { + println!("{:?}", lsleep); + // short sleep to flush stdout (stdout().flush() did not work) + thread::sleep(Duration::from_millis(20)); + + let time_before = Instant::now(); + lsleep.sleep()?; + print_wakeup_result(time_before); + + println!("---"); + } + })?; + + th.join(); + + loop { + thread::sleep(Duration::from_secs(1)); + } +} diff --git a/src/lib.rs b/src/lib.rs index e840509cfc8..d8f9d4e2c07 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -65,6 +65,8 @@ pub mod rmt; #[cfg(not(feature = "riscv-ulp-hal"))] pub mod rom; #[cfg(not(feature = "riscv-ulp-hal"))] +pub mod sleep; +#[cfg(not(feature = "riscv-ulp-hal"))] pub mod spi; #[cfg(not(feature = "riscv-ulp-hal"))] pub mod task; diff --git a/src/sleep.rs b/src/sleep.rs new file mode 100644 index 00000000000..01a4f0b9dca --- /dev/null +++ b/src/sleep.rs @@ -0,0 +1,110 @@ +//! Driver for light and deep sleep. +//! +//! Currently implemented Timer and UART wakeup sources. + +use esp_idf_sys::*; +use std::time::Duration; + +#[derive(Debug)] +pub enum WakeupSource { + Timer { dur: Duration }, + Uart { uart_num: i32, threshold: i32 }, + // TODO: add more sources +} + +impl WakeupSource { + pub fn apply(&self) -> Result<(), EspError> { + match *self { + WakeupSource::Timer { dur } => { + esp!(unsafe { esp_sleep_enable_timer_wakeup(dur.as_micros() as u64) })?; + } + WakeupSource::Uart { + uart_num, + threshold, + } => { + esp!(unsafe { uart_set_wakeup_threshold(uart_num, threshold) })?; + esp!(unsafe { esp_sleep_enable_uart_wakeup(uart_num) })?; + } + } + + Ok(()) + } +} + +pub trait Sleep { + fn add_wakeup_source(&mut self, wakeup: WakeupSource) -> Result<(), EspError>; + fn sleep(&self) -> Result<(), EspError>; + + fn apply_wakeup_sources(&self, wakeup_sources: &[WakeupSource]) -> Result<(), EspError> { + // reset wakeup sources + esp!(unsafe { esp_sleep_disable_wakeup_source(esp_sleep_source_t_ESP_SLEEP_WAKEUP_ALL) })?; + + for wakeup in wakeup_sources.iter() { + wakeup.apply()?; + } + Ok(()) + } +} + +#[derive(Default, Debug)] +pub struct LightSleep { + wakeup_sources: Vec, +} + +impl LightSleep { + pub fn timer(dur: Duration) -> Result { + // TODO: checks for duplicate wakeup sources + + let mut lsleep = Self::default(); + lsleep.add_wakeup_source(WakeupSource::Timer { dur })?; + Ok(lsleep) + } +} + +impl Sleep for LightSleep { + fn add_wakeup_source(&mut self, wakeup: WakeupSource) -> Result<(), EspError> { + self.wakeup_sources.push(wakeup); + Ok(()) + } + + fn sleep(&self) -> Result<(), EspError> { + self.apply_wakeup_sources(&self.wakeup_sources)?; + + esp!(unsafe { esp_light_sleep_start() })?; + Ok(()) + } +} + +#[derive(Default, Debug)] +pub struct DeepSleep { + wakeup_sources: Vec, +} + +impl DeepSleep { + pub fn timer(dur: Duration) -> Result { + let mut dsleep = Self::default(); + dsleep.add_wakeup_source(WakeupSource::Timer { dur })?; + Ok(dsleep) + } +} + +impl Sleep for DeepSleep { + fn add_wakeup_source(&mut self, wakeup: WakeupSource) -> Result<(), EspError> { + // TODO: checks for duplicate wakeup sources + + if matches!(wakeup, WakeupSource::Uart { .. }) { + return Err(EspError::from_infallible::()); + } + self.wakeup_sources.push(wakeup); + Ok(()) + } + + fn sleep(&self) -> Result<(), EspError> { + self.apply_wakeup_sources(&self.wakeup_sources)?; + + unsafe { esp_deep_sleep_start() }; + #[allow(unreachable_code)] + // if function returns, it means that deep sleep has been rejected + Err(EspError::from_infallible::()) + } +}