Waking on a pin or a touch
Three ways to wake a sleeping chip from the outside — one pin, several pins, or a touch pad — and the reason a board that never wakes is nearly always a pin that was never eligible.
Only some pins are eligible
While the chip is in deep sleep, most of it has no power — including most of the GPIO block. The pins that can still notice anything are the ones wired into the RTC power domain, and on the classic ESP32 that is sixteen of them.
Ask any other pin to wake the chip and the call returns an error code, the board goes to sleep, and it never comes back. From the outside that looks exactly like a dead board, which is why this is worth checking before anything else.
Which of the three
ext0 — one pin, one level, and it can ask the RTC to hold an internal pull-up while asleep. That last part is why it is the right choice for a single button: no external resistor.
ext1 — a bitmask of pins, waking on any-high or all-low. It works in hibernation, where ext0 does not, and it tells you which pin fired. It cannot hold internal pull-ups, so every pin in the mask needs a physical resistor.
touch — a pad, a threshold, no moving parts and nothing to wear out. Only on chips with touch hardware; the C3 and C6 have none and the call quietly does nothing there. It also keeps the RTC peripherals powered, so it rules out hibernation.
Hold the pin during sleep
If a pin was an output driving something before the sleep, its state is not
kept unless the RTC is asked to hold it. rtc_gpio_hold_en() freezes it at its
current level for the duration; gpio_hold_dis() on the next boot releases it.
Without that, a relay or a MOSFET gate driven high before sleeping goes floating the instant the chip powers down — which is a class of "it turns itself off overnight" bug that is very hard to see.
Say why it woke
esp_sleep_get_wakeup_cause() is one call and it changes what the sketch should
do. A timer wake is a scheduled reading; a button wake is a user standing there
waiting. Handling them identically is the commonest reason a battery remote
feels slow.
The code
ext0 for a single button, ext1 for several. Both need RTC-capable pins, and ext1 needs external resistors because it cannot hold a pull-up while asleep.
#include <esp_sleep.h>
#include <driver/rtc_io.h>
#define BTN GPIO_NUM_33
#define MASK ((1ULL << GPIO_NUM_32) | (1ULL << GPIO_NUM_33))
void setup() {
Serial.begin(115200);
delay(100);
switch (esp_sleep_get_wakeup_cause()) {
case ESP_SLEEP_WAKEUP_EXT0:
Serial.println("woken by the button on GPIO 33"); break;
case ESP_SLEEP_WAKEUP_EXT1:
Serial.printf("woken by mask %llx\n", esp_sleep_get_ext1_wakeup_status()); break;
case ESP_SLEEP_WAKEUP_TOUCHPAD:
Serial.printf("woken by pad %d\n", esp_sleep_get_touchpad_wakeup_status()); break;
default:
Serial.println("cold boot"); break;
}
// One pin, active low, with the RTC holding the pull-up while asleep.
rtc_gpio_pullup_en(BTN);
rtc_gpio_pulldown_dis(BTN);
esp_sleep_enable_ext0_wakeup(BTN, 0);
// Or several, waking when any of them goes high. No internal pull-ups here.
// esp_sleep_enable_ext1_wakeup(MASK, ESP_EXT1_WAKEUP_ANY_HIGH);
Serial.flush();
esp_deep_sleep_start();
}
void loop() {}esp_sleep_get_ext1_wakeup_status returns a mask of which pin fired. Without it you know the board woke and not what woke it, which for a three-button remote is the only interesting question.
esp32.wake_on_ext0 and wake_on_ext1 take Pin objects rather than numbers, and the pin has to be configured before it is handed over.
import machine, esp32
cause = machine.wake_reason()
if cause == machine.EXT0_WAKE:
print("woken by the button")
elif cause == machine.EXT1_WAKE:
print("woken by one of the group")
else:
print("cold boot")
btn = machine.Pin(33, machine.Pin.IN, machine.Pin.PULL_UP)
esp32.wake_on_ext0(pin=btn, level=esp32.WAKEUP_ALL_LOW)
# group = [machine.Pin(32, machine.Pin.IN), machine.Pin(33, machine.Pin.IN)]
# esp32.wake_on_ext1(pins=group, level=esp32.WAKEUP_ANY_HIGH)
machine.deepsleep() # no timeout: only the pin wakes itwake_on_touch exists too, and does nothing on a C3 or C6 — those chips have no touch hardware, and the call returns without complaint.
When it does not work
The pin is probably not in the RTC power domain. Only some GPIOs are, the rest are unpowered while the chip sleeps, and the enable call returns an error most sketches never check. Pick from the eligible list.
The pin is floating, or already at the trigger level when you sleep. ext1 has no internal pull-ups while asleep, so every pin in the mask needs a real resistor. Read the pin before sleeping and wait for it to settle.
Switch bounce, seen by hardware that reacts in microseconds. Wake, then wait a few tens of milliseconds and check the pin is still pressed before treating it as real.
Touch needs the RTC peripherals powered to notice a touch, so it rules out hibernation. Roughly 10 µA rather than 5 µA is the price, which is usually worth it and should be a decision rather than a surprise.
The board woke up. Three ways to have left yourself a note, and only one of them still says anything after a power cut.
What survives deep sleep →Edit this page — content/esp32/gpio-and-touch-wake-up.mdx
Discuss this article
Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.