Skip to main content

esp_idf_hal/
sleep.rs

1use core::time::Duration;
2use esp_idf_sys::*;
3
4#[cfg(not(any(esp32c2, esp32c3, esp32h2, esp32h4)))]
5pub use self::rtc::{ChainedRtcWakeupPins, RtcWakeLevel, RtcWakeupPins};
6
7pub mod timer {
8    use core::time::Duration;
9    use esp_idf_sys::*;
10
11    pub fn configure(duration: Duration) -> Result<(), EspError> {
12        esp!(unsafe { esp_sleep_enable_timer_wakeup(duration.as_micros() as u64) })
13    }
14}
15
16#[cfg(not(any(esp32c2, esp32c3, esp32h2, esp32h4)))]
17pub mod rtc {
18    use crate::gpio::{PinDriver, PinId, RTCMode};
19    use esp_idf_sys::*;
20
21    #[derive(Debug, Copy, Clone, PartialEq, Eq)]
22    pub enum RtcWakeLevel {
23        AllLow,
24        AnyHigh,
25    }
26
27    impl RtcWakeLevel {
28        pub fn mode(&self) -> u32 {
29            match self {
30                Self::AllLow => esp_sleep_ext1_wakeup_mode_t_ESP_EXT1_WAKEUP_ALL_LOW,
31                Self::AnyHigh => esp_sleep_ext1_wakeup_mode_t_ESP_EXT1_WAKEUP_ANY_HIGH,
32            }
33        }
34    }
35
36    pub trait RtcWakeupPins {
37        type Iterator<'a>: Iterator<Item = PinId>
38        where
39            Self: 'a;
40
41        fn iter(&self) -> Self::Iterator<'_>;
42
43        fn chain<P: RtcWakeupPins>(self, other: P) -> ChainedRtcWakeupPins<Self, P>
44        where
45            Self: Sized,
46        {
47            ChainedRtcWakeupPins {
48                first: self,
49                second: other,
50            }
51        }
52    }
53
54    impl<P> RtcWakeupPins for &PinDriver<'_, P>
55    where
56        P: RTCMode,
57    {
58        type Iterator<'a>
59            = core::iter::Once<PinId>
60        where
61            Self: 'a;
62
63        fn iter(&self) -> Self::Iterator<'_> {
64            core::iter::once(self.pin())
65        }
66    }
67
68    pub struct ChainedRtcWakeupPins<F, S> {
69        first: F,
70        second: S,
71    }
72
73    impl<F, S> RtcWakeupPins for ChainedRtcWakeupPins<F, S>
74    where
75        F: RtcWakeupPins,
76        S: RtcWakeupPins,
77    {
78        type Iterator<'a>
79            = core::iter::Chain<F::Iterator<'a>, S::Iterator<'a>>
80        where
81            Self: 'a;
82
83        fn iter(&self) -> Self::Iterator<'_> {
84            self.first.iter().chain(self.second.iter())
85        }
86    }
87
88    impl<F, S> RtcWakeupPins for &ChainedRtcWakeupPins<F, S>
89    where
90        F: RtcWakeupPins,
91        S: RtcWakeupPins,
92    {
93        type Iterator<'a>
94            = core::iter::Chain<F::Iterator<'a>, S::Iterator<'a>>
95        where
96            Self: 'a;
97
98        fn iter(&self) -> Self::Iterator<'_> {
99            self.first.iter().chain(self.second.iter())
100        }
101    }
102
103    pub fn configure<P: RtcWakeupPins>(pins: P, level: RtcWakeLevel) -> Result<(), EspError> {
104        let mut mask: u64 = 0;
105        for pin_id in pins.iter() {
106            mask |= 1 << pin_id;
107        }
108
109        #[cfg(any(esp32, esp32s3))]
110        esp!(unsafe {
111            esp_sleep_pd_config(
112                esp_sleep_pd_domain_t_ESP_PD_DOMAIN_RTC_PERIPH,
113                esp_sleep_pd_option_t_ESP_PD_OPTION_ON,
114            )
115        })?;
116
117        esp!(unsafe { esp_sleep_enable_ext1_wakeup(mask, level.mode()) })
118    }
119}
120
121pub mod gpio {
122    use crate::gpio::{Level, PinId};
123    use esp_idf_sys::*;
124
125    pub fn configure_light(pin: PinId, level: Level) -> Result<(), EspError> {
126        let intr = match level {
127            Level::Low => gpio_int_type_t_GPIO_INTR_LOW_LEVEL,
128            Level::High => gpio_int_type_t_GPIO_INTR_HIGH_LEVEL,
129        };
130        esp!(unsafe { gpio_wakeup_enable(pin as _, intr) })?;
131        esp!(unsafe { esp_sleep_enable_gpio_wakeup() })
132    }
133
134    #[cfg(any(esp32c2, esp32c3))]
135    pub fn configure_deep(pin: PinId, level: Level) -> Result<(), EspError> {
136        let mask = 1 << pin;
137        let mode = match level {
138            #[cfg(not(esp_idf_version_at_least_6_0_0))]
139            Level::Low => esp_deepsleep_gpio_wake_up_mode_t_ESP_GPIO_WAKEUP_GPIO_LOW,
140            #[cfg(not(esp_idf_version_at_least_6_0_0))]
141            Level::High => esp_deepsleep_gpio_wake_up_mode_t_ESP_GPIO_WAKEUP_GPIO_HIGH,
142            #[cfg(esp_idf_version_at_least_6_0_0)]
143            Level::Low => esp_sleep_gpio_wake_up_mode_t_ESP_GPIO_WAKEUP_GPIO_LOW,
144            #[cfg(esp_idf_version_at_least_6_0_0)]
145            Level::High => esp_sleep_gpio_wake_up_mode_t_ESP_GPIO_WAKEUP_GPIO_HIGH,
146        };
147        #[cfg(not(esp_idf_version_at_least_6_0_0))]
148        return esp!(unsafe { esp_deep_sleep_enable_gpio_wakeup(mask, mode) });
149        #[cfg(esp_idf_version_at_least_6_0_0)]
150        return esp!(unsafe { esp_sleep_enable_gpio_wakeup_on_hp_periph_powerdown(mask, mode) });
151    }
152}
153
154pub mod uart {
155    use crate::uart::UartDriver;
156    use esp_idf_sys::*;
157
158    pub fn configure(uart: &UartDriver, threshold: i32) -> Result<(), EspError> {
159        esp!(unsafe { uart_set_wakeup_threshold(uart.port(), threshold) })?;
160        esp!(unsafe { esp_sleep_enable_uart_wakeup(uart.port() as _) })
161    }
162}
163
164#[cfg(any(esp32, esp32s2, esp32s3, esp32p4))]
165pub mod touch {
166    use esp_idf_sys::*;
167    pub fn configure() -> Result<(), EspError> {
168        esp!(unsafe { esp_sleep_enable_touchpad_wakeup() })
169    }
170}
171
172#[cfg(any(esp32, esp32s2, esp32s3, esp32c5, esp32c6, esp32p4))]
173pub mod ulp {
174    use esp_idf_sys::*;
175    pub fn configure() -> Result<(), EspError> {
176        esp!(unsafe { esp_sleep_enable_ulp_wakeup() })
177    }
178}
179
180fn reset_wakeup_sources() -> Result<(), EspError> {
181    esp!(unsafe { esp_sleep_disable_wakeup_source(esp_sleep_source_t_ESP_SLEEP_WAKEUP_ALL) })
182}
183
184pub struct LightSleep(());
185
186impl LightSleep {
187    pub fn new() -> Result<Self, EspError> {
188        reset_wakeup_sources()?;
189        Ok(Self(()))
190    }
191
192    pub fn wakeup_on_timer(self, duration: Duration) -> Result<Self, EspError> {
193        timer::configure(duration)?;
194        Ok(self)
195    }
196
197    pub fn wakeup_on_uart(
198        self,
199        uart: &crate::uart::UartDriver,
200        threshold: i32,
201    ) -> Result<Self, EspError> {
202        uart::configure(uart, threshold)?;
203        Ok(self)
204    }
205
206    pub fn wakeup_on_gpio<M: crate::gpio::GPIOMode>(
207        self,
208        pin: &crate::gpio::PinDriver<M>,
209        level: crate::gpio::Level,
210    ) -> Result<Self, EspError> {
211        gpio::configure_light(pin.pin(), level)?;
212        Ok(self)
213    }
214
215    #[cfg(not(any(esp32c2, esp32c3, esp32h2, esp32h4)))]
216    pub fn wakeup_on_rtc<P>(self, pins: P, level: rtc::RtcWakeLevel) -> Result<Self, EspError>
217    where
218        P: rtc::RtcWakeupPins,
219    {
220        rtc::configure(pins, level)?;
221        Ok(self)
222    }
223
224    #[cfg(any(esp32, esp32s2, esp32s3, esp32p4))]
225    pub fn wakeup_on_touch(self) -> Result<Self, EspError> {
226        touch::configure()?;
227        Ok(self)
228    }
229
230    #[cfg(any(esp32, esp32s2, esp32s3, esp32c5, esp32c6, esp32p4))]
231    pub fn wakeup_on_ulp(self) -> Result<Self, EspError> {
232        ulp::configure()?;
233        Ok(self)
234    }
235
236    pub fn enter(&mut self) -> Result<(), EspError> {
237        esp!(unsafe { esp_light_sleep_start() })
238    }
239}
240
241pub struct DeepSleep(());
242
243impl DeepSleep {
244    pub fn new() -> Result<Self, EspError> {
245        reset_wakeup_sources()?;
246        Ok(Self(()))
247    }
248
249    pub fn wakeup_on_timer(self, duration: Duration) -> Result<Self, EspError> {
250        timer::configure(duration)?;
251        Ok(self)
252    }
253
254    #[cfg(not(any(esp32c2, esp32c3, esp32h2, esp32h4)))]
255    pub fn wakeup_on_rtc<P>(self, pins: P, level: rtc::RtcWakeLevel) -> Result<Self, EspError>
256    where
257        P: rtc::RtcWakeupPins,
258    {
259        rtc::configure(pins, level)?;
260        Ok(self)
261    }
262
263    #[cfg(any(esp32c2, esp32c3))]
264    pub fn wakeup_on_gpio<M: crate::gpio::GPIOMode>(
265        self,
266        pin: &crate::gpio::PinDriver<M>,
267        level: crate::gpio::Level,
268    ) -> Result<Self, EspError> {
269        gpio::configure_deep(pin.pin(), level)?;
270        Ok(self)
271    }
272
273    #[cfg(any(esp32, esp32s2, esp32s3, esp32p4))]
274    pub fn wakeup_on_touch(self) -> Result<Self, EspError> {
275        touch::configure()?;
276        Ok(self)
277    }
278
279    #[cfg(any(esp32, esp32s2, esp32s3, esp32c5, esp32c6, esp32p4))]
280    pub fn wakeup_on_ulp(self) -> Result<Self, EspError> {
281        ulp::configure()?;
282        Ok(self)
283    }
284
285    pub fn enter(self) -> ! {
286        unsafe { esp_deep_sleep_start() }
287        #[allow(unreachable_code)]
288        {
289            panic!("Deep sleep failed to start");
290        }
291    }
292}