ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
deep_sleep_esp32.cpp
Go to the documentation of this file.
1#ifdef USE_ESP32
2#include "soc/soc_caps.h"
3#include "driver/gpio.h"
5#include "esphome/core/log.h"
6#include <esp_idf_version.h>
7
8namespace esphome::deep_sleep {
9
10// Deep Sleep feature support matrix for ESP32 variants:
11//
12// | Variant | ext0 | ext1 | Touch | GPIO wakeup |
13// |-----------|------|------|-------|-------------|
14// | ESP32 | ✓ | ✓ | ✓ | |
15// | ESP32-S2 | ✓ | ✓ | ✓ | |
16// | ESP32-S3 | ✓ | ✓ | ✓ | |
17// | ESP32-C2 | | | | ✓ |
18// | ESP32-C3 | | | | ✓ |
19// | ESP32-C5 | | ✓ | | ✓ |
20// | ESP32-C6 | | ✓ | | ✓ |
21// | ESP32-C61 | | ✓ | | ✓ |
22// | ESP32-H2 | | ✓ | | |
23//
24// Notes:
25// - (✓) = Supported by hardware but not yet implemented in ESPHome
26// - ext0: Single pin wakeup using RTC GPIO (esp_sleep_enable_ext0_wakeup)
27// - ext1: Multiple pin wakeup (esp_sleep_enable_ext1_wakeup)
28// - Touch: Touch pad wakeup (esp_sleep_enable_touchpad_wakeup)
29// - GPIO wakeup: GPIO wakeup for RTC pins
30
31ESPHOME_LOG_TAG(TAG, "deep_sleep");
32
34 switch (esp_sleep_get_wakeup_cause()) {
35 case ESP_SLEEP_WAKEUP_EXT0:
36 case ESP_SLEEP_WAKEUP_EXT1:
37 case ESP_SLEEP_WAKEUP_GPIO:
38 return WAKEUP_CAUSE_GPIO;
39 case ESP_SLEEP_WAKEUP_TIMER:
40 return WAKEUP_CAUSE_TIMER;
41 case ESP_SLEEP_WAKEUP_TOUCHPAD:
42 return WAKEUP_CAUSE_TOUCH;
43 case ESP_SLEEP_WAKEUP_UNDEFINED:
44 return WAKEUP_CAUSE_NONE;
45 default:
47 }
48}
49
50optional<uint32_t> DeepSleepComponent::get_run_duration_() const {
51 if (this->wakeup_cause_to_run_duration_.has_value()) {
52 esp_sleep_wakeup_cause_t wakeup_cause = esp_sleep_get_wakeup_cause();
53 switch (wakeup_cause) {
54 case ESP_SLEEP_WAKEUP_EXT0:
55 case ESP_SLEEP_WAKEUP_EXT1:
56 case ESP_SLEEP_WAKEUP_GPIO:
57 return this->wakeup_cause_to_run_duration_->gpio_cause;
58 case ESP_SLEEP_WAKEUP_TOUCHPAD:
59 return this->wakeup_cause_to_run_duration_->touch_cause;
60 default:
61 return this->wakeup_cause_to_run_duration_->default_cause;
62 }
63 }
64 return this->run_duration_;
65}
66
68 this->wakeup_pin_mode_ = wakeup_pin_mode;
69}
70
71#if !defined(USE_ESP32_VARIANT_ESP32C2) && !defined(USE_ESP32_VARIANT_ESP32C3)
72void DeepSleepComponent::set_ext1_wakeup(Ext1Wakeup ext1_wakeup) { this->ext1_wakeup_ = ext1_wakeup; }
73#endif
74
76 wakeup_cause_to_run_duration_ = wakeup_cause_to_run_duration;
77}
78
80 if (wakeup_pin_ != nullptr) {
81 LOG_PIN(" Wakeup Pin: ", this->wakeup_pin_);
82 }
83 if (this->wakeup_cause_to_run_duration_.has_value()) {
84 ESP_LOGCONFIG(TAG,
85 " Default Wakeup Run Duration: %" PRIu32 " ms\n"
86 " Touch Wakeup Run Duration: %" PRIu32 " ms\n"
87 " GPIO Wakeup Run Duration: %" PRIu32 " ms",
88 this->wakeup_cause_to_run_duration_->default_cause, this->wakeup_cause_to_run_duration_->touch_cause,
89 this->wakeup_cause_to_run_duration_->gpio_cause);
90 }
91}
92
94 if (this->wakeup_pin_mode_ == WAKEUP_PIN_MODE_KEEP_AWAKE && this->wakeup_pin_ != nullptr &&
95 this->wakeup_pin_->digital_read()) {
96 // Defer deep sleep until inactive
97 if (!this->next_enter_deep_sleep_) {
98 this->status_set_warning();
99 ESP_LOGW(TAG, "Waiting for wakeup pin state change");
100 }
101 this->defer_sleep_();
102 return false;
103 }
104 return true;
105}
106
108 // Timer wakeup - all variants support this
109 if (this->sleep_duration_.has_value())
110 esp_sleep_enable_timer_wakeup(*this->sleep_duration_);
111
112#ifndef SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP
113#ifdef USE_GPIO_HOLD
114 // Variants without per-pin deep sleep hold need the global enable
115 gpio_deep_sleep_hold_en();
116#else
117 if (this->wakeup_pin_ != nullptr)
118 gpio_deep_sleep_hold_en();
119#endif
120#endif
121
122 // Single pin wakeup (ext0) - ESP32, S2, S3 only
123#if !defined(USE_ESP32_VARIANT_ESP32C2) && !defined(USE_ESP32_VARIANT_ESP32C3) && \
124 !defined(USE_ESP32_VARIANT_ESP32C5) && !defined(USE_ESP32_VARIANT_ESP32C6) && \
125 !defined(USE_ESP32_VARIANT_ESP32C61) && !defined(USE_ESP32_VARIANT_ESP32H2)
126 if (this->wakeup_pin_ != nullptr) {
127 const auto gpio_pin = gpio_num_t(this->wakeup_pin_->get_pin());
128 if (this->wakeup_pin_->get_flags() & gpio::FLAG_PULLUP) {
129 gpio_sleep_set_pull_mode(gpio_pin, GPIO_PULLUP_ONLY);
130 } else if (this->wakeup_pin_->get_flags() & gpio::FLAG_PULLDOWN) {
131 gpio_sleep_set_pull_mode(gpio_pin, GPIO_PULLDOWN_ONLY);
132 }
133 gpio_sleep_set_direction(gpio_pin, GPIO_MODE_INPUT);
134 gpio_hold_en(gpio_pin);
135 bool level = !this->wakeup_pin_->is_inverted();
137 level = !level;
138 }
139 esp_sleep_enable_ext0_wakeup(gpio_pin, level);
140 }
141#endif
142
143 // GPIO wakeup - C2, C3, C5, C6, C61 only
144#if defined(USE_ESP32_VARIANT_ESP32C2) || defined(USE_ESP32_VARIANT_ESP32C3) || defined(USE_ESP32_VARIANT_ESP32C5) || \
145 defined(USE_ESP32_VARIANT_ESP32C6) || defined(USE_ESP32_VARIANT_ESP32C61)
146 if (this->wakeup_pin_ != nullptr) {
147 const auto gpio_pin = gpio_num_t(this->wakeup_pin_->get_pin());
148 // Make sure GPIO is in input mode, not all RTC GPIO pins are input by default
149 gpio_set_direction(gpio_pin, GPIO_MODE_INPUT);
150 bool level = !this->wakeup_pin_->is_inverted();
152 level = !level;
153 }
154 // Internal pullup/pulldown resistors are enabled automatically, when
155 // ESP_SLEEP_GPIO_ENABLE_INTERNAL_RESISTORS is set (by default it is)
156#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0)
157 esp_sleep_enable_gpio_wakeup_on_hp_periph_powerdown(1ULL << this->wakeup_pin_->get_pin(),
158 static_cast<esp_sleep_gpio_wake_up_mode_t>(level));
159#else
160 esp_deep_sleep_enable_gpio_wakeup(1ULL << this->wakeup_pin_->get_pin(),
161 static_cast<esp_deepsleep_gpio_wake_up_mode_t>(level));
162#endif
163 }
164#endif
165
166 // Multiple pin wakeup (ext1) - All except C2, C3
167#if !defined(USE_ESP32_VARIANT_ESP32C2) && !defined(USE_ESP32_VARIANT_ESP32C3)
168 if (this->ext1_wakeup_.has_value()) {
169 esp_sleep_enable_ext1_wakeup(this->ext1_wakeup_->mask, this->ext1_wakeup_->wakeup_mode);
170 }
171#endif
172
173 // Touch wakeup - ESP32, S2, S3 only
174#if !defined(USE_ESP32_VARIANT_ESP32C2) && !defined(USE_ESP32_VARIANT_ESP32C3) && \
175 !defined(USE_ESP32_VARIANT_ESP32C5) && !defined(USE_ESP32_VARIANT_ESP32C6) && \
176 !defined(USE_ESP32_VARIANT_ESP32C61) && !defined(USE_ESP32_VARIANT_ESP32H2)
177 if (this->touch_wakeup_.has_value() && *(this->touch_wakeup_)) {
178 esp_sleep_enable_touchpad_wakeup();
179 esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
180 }
181#endif
182
183 esp_deep_sleep_start();
184}
185
186bool DeepSleepComponent::should_teardown_() { return true; }
187
188} // namespace esphome::deep_sleep
189
190#endif // USE_ESP32
void set_run_duration(WakeupCauseToRunDuration wakeup_cause_to_run_duration)
optional< uint32_t > get_run_duration_() const
void set_wakeup_pin_mode(WakeupPinMode wakeup_pin_mode)
void set_ext1_wakeup(Ext1Wakeup ext1_wakeup)
optional< WakeupCauseToRunDuration > wakeup_cause_to_run_duration_
gpio::Flags get_flags() const override
Definition gpio.h:34
WakeupPinMode
The values of this enum define what should be done if deep sleep is set up with a wakeup pin on the E...
@ WAKEUP_PIN_MODE_KEEP_AWAKE
As long as the wakeup pin is still in the wakeup state, keep awake.
@ WAKEUP_PIN_MODE_INVERT_WAKEUP
Automatically invert the wakeup level.
ESPHOME_LOG_TAG(TAG, "deep_sleep")
WakeupCause get_wakeup_cause()
Return why the device woke from deep sleep. Implemented per platform.
WakeupCause
Why the device woke from deep sleep. Passed to on_wake automations.
@ WAKEUP_CAUSE_TOUCH
The device was woken by a touch pad.
@ WAKEUP_CAUSE_GPIO
The device was woken by a GPIO pin (wakeup_pin or esp32_ext1_wakeup).
@ WAKEUP_CAUSE_TIMER
The device was woken by the sleep timer.
@ WAKEUP_CAUSE_UNKNOWN
The device woke from deep sleep, but the source could not be identified.
@ WAKEUP_CAUSE_NONE
The device did not wake from deep sleep (for example a cold boot, reset or OTA restart).