Repository navigation
Fix wakeup from sleep: touch wakeup on CoreS3, and the wakeup pin being ignored on older ESP-IDF - #291
Merged
lovyan03 merged 2 commits intoJul 31, 2026
Conversation
deepSleep() selected its wakeup API with `#if SOC_PM_SUPPORT_EXT0_WAKEUP` / `#elif SOC_PM_SUPPORT_EXT1_WAKEUP`, but ESP-IDF v4 defines neither of them; it only has the generic SOC_PM_SUPPORT_EXT_WAKEUP. Both branches therefore disappeared and the wakeup pin was silently dropped on every board built with Arduino 2.x, leaving the timer as the only wakeup source. esp_sleep_enable_ext1_wakeup_io() has the same problem: it was added in ESP-IDF 5.3, so the EXT1 branch did not even compile on older versions. - add M5UNIFIED_PM_SUPPORT_EXT0 / EXT1, which fall back to SOC_PM_SUPPORT_EXT_WAKEUP when the split macros are missing ( EXT0 exists on ESP32 / S2 / S3 , EXT1 on all of them ) - call esp_sleep_enable_ext1_wakeup() when the _io variant is not available - lightSleep() decided between EXT0 and gpio wakeup by board name ( board_M5PaperS3 ), which is really a question of whether the pin is an RTC IO. Ask rtc_gpio_is_valid_gpio() instead and fall back to gpio wakeup, so any board with a non RTC IO wakeup pin, and any target without EXT0, behaves the same way. The pull settings of the EXT1 branch look inverted against the ANY_LOW wakeup condition, but they are left as they are with a TODO, because they were introduced for M5Tab5 and the wiring of its wakeup pin is unknown here.
CoreS3 had no wakeup pin, so lightSleep() and deepSleep() could only be left by the timer or by a power cycle. According to the schematic the touch interrupt reaches the ESP32 as TOUCH_INT -> AW9523 P1_2 -> AW9523 INTN -> I2C_INT -> GPIO21, which is an RTC IO and can therefore be used for both light sleep and deep sleep. The power key is wired to the AXP2101 PWRON only, and the AXP2101 IRQ pin is shared with the RTC INT without reaching the ESP32, so touch is the only usable wakeup source on this board. Simply setting the pin is not enough. The touch panel holds its INT asserted until the touch data is read, and the AW9523 latches its INTN until its input port is read; if either of them is left asserted, the sleep request is rejected or the wakeup source is dead while sleeping. Releasing them needs to know how the board is wired, which is knowledge that belongs to M5Unified rather than to Power_Class, so it is added as an internal M5Unified::_clearWakeupInterrupt() that Power_Class calls through a friend declaration. The release is repeated immediately before sleeping, because an interrupt that arrives after the wait loop would otherwise stay latched. Reading the touch data is not specific to CoreS3, so it is done for every board that has a touch panel. M5Paper ( GPIO36 ), M5PaperS3 ( GPIO48 ) and Core2 / Tough ( GPIO39 ) use the touch INT as their wakeup pin as well, and their lightSleep() had no touch read at all while waiting for the pin to be released. deepSleep() happened to avoid this only because its wait loop calls M5.update(). The extra read costs nothing on the other boards, because _clearWakeupInterrupt() is called only while the wakeup pin is asserted. Also warn when touch_wakeup is requested on a board that has no wakeup pin at all; this used to fail silently. Verified on CoreS3 with Arduino 2.0.17 ( ESP-IDF 4.4 ) and Arduino 3.3.4 ( ESP-IDF 5.5 ): touch wakes the device from both light sleep and deep sleep. The other boards are checked by build only.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #214
Two changes in the sleep wakeup pin handling. They are independent, so they are kept as separate commits.
1. The wakeup pin was ignored on older ESP-IDF
deepSleep()chose its wakeup API with#if SOC_PM_SUPPORT_EXT0_WAKEUP/#elif SOC_PM_SUPPORT_EXT1_WAKEUP, but ESP-IDF v4 defines neither of them; it only has the genericSOC_PM_SUPPORT_EXT_WAKEUP. Both branches therefore vanished, and the wakeup pin was silently dropped on every board built with Arduino 2.x, leaving the timer as the only way out of deep sleep. This has been the case since the EXT1 branch was added for M5Tab5.esp_sleep_enable_ext1_wakeup_io()has the same problem in the other direction: it was added in ESP-IDF 5.3, so the EXT1 branch did not compile on older versions at all.M5UNIFIED_PM_SUPPORT_EXT0/EXT1, falling back toSOC_PM_SUPPORT_EXT_WAKEUPwhen the split macros are missing (EXT0 exists on ESP32 / S2 / S3, EXT1 on all of them)esp_sleep_enable_ext1_wakeup()when the_iovariant is not availablelightSleep()decided between EXT0 and gpio wakeup by board name (board_M5PaperS3), which is really a question of whether the pin is an RTC IO. It now asksrtc_gpio_is_valid_gpio()and falls back to gpio wakeup, so any board with a non RTC IO wakeup pin — and any target without EXT0 — behaves the same way without being named.The pull settings in the EXT1 branch look inverted against the
ANY_LOWwakeup condition (pulling the pin down biases it toward the wakeup condition, so a pin without an external pull-up would wake immediately). They are left as they are with a TODO, because they were introduced for M5Tab5 and the wiring of its wakeup pin is unknown to me. Only M5Tab5 (P4) and C6 reach that branch now that S3 uses EXT0.2. Wakeup by touch on CoreS3 (#214)
CoreS3 had no wakeup pin at all, so
lightSleep()/deepSleep()could only be left by the timer or by a power cycle.From the schematic, the touch interrupt reaches the ESP32 as:
GPIO21 is an RTC IO on ESP32-S3, so it works for both light sleep and deep sleep.
Wakeup by the power key is not possible on this board. The power key is wired to the AXP2101
PWRONpin only, and the AXP2101IRQpin is shared with the RTC INT without reaching the ESP32. Touch is the only usable wakeup source here.Setting the pin alone is not enough:
INTNand reports changes only, so its input port has to be read as well — otherwise its input stays in the touched state, a later touch produces no change, and the wakeup source is deadIf either is left asserted,
esp_light_sleep_start()returnsESP_ERR_SLEEP_REJECT_WAKEUP, or the device sleeps with no way to be woken.Releasing them requires knowledge of how the board is wired, which belongs to
M5Unifiedrather than toPower_Class. It is added as an internalM5Unified::_clearWakeupInterrupt()thatPower_Classreaches through afrienddeclaration. The release is repeated immediately before sleeping, because an interrupt arriving after the wait loop would otherwise stay latched.Reading the touch data is not specific to CoreS3, so it is done for every board that has a touch panel. M5Paper (GPIO36), M5PaperS3 (GPIO48) and Core2 / Tough (GPIO39) use the touch INT as their wakeup pin too, and their
lightSleep()had no touch read at all while waiting for the pin to be released;deepSleep()happened to avoid the problem only because its wait loop callsM5.update(). The extra read costs nothing on other boards, because_clearWakeupInterrupt()is only called while the wakeup pin is asserted.Finally, requesting
touch_wakeupon a board that has no wakeup pin now logs a warning instead of failing silently.Verification
Verified on CoreS3, driven by touch so that nothing depends on timing:
Build checked for ESP32-S3 (ESP-IDF 4.4 / 5.5), ESP32 and the PC build.
Not verified on hardware:
lightSleep()improvement for M5Paper / M5PaperS3 / Core2 / Tough. Note that the current code loops forever in that situation, so there is little room for a regression.