The ESP32 can be set to wake up from sleep due to one of several pins going high. Which pin caused it to wake up? This has been causing me a few headaches with my combined mailbox and driveway monitor because sometimes multiple pins can be high at once. While researching this I also discovered that there is a far easier way to setup the pins than calculating a bitmask value.
The project I am working on has three sensors; one for detecting a mail flap being lifted, another for when the mailbox door is opened and finally a PIR movement sensor being triggered by movement. All of these will wake the ESP32. It then checks what woke it up, sends some data to another ESP32 in the house using ESPNOW and then goes back to sleep.
There are a couple of problems. Firstly, sometimes multiple sensor pins can be high at once. This usually occurs because the PIR driveway sensor stays high for a while after being triggered. It is often while it is high that mail is delivered. So, it’s important not just to know what pin caused it to wake up, but also what combination of pins were high. I wasn’t able to find easy to understand info about this. I found some things that may have described it, but not in simple enough language for me to understand. It’s now starting to make sense.
Which pins woke it up
To learn how to use deep sleep and determine what woke it up I started with this useful article by Random Nerd Tutorials ESP32 Deep Sleep with Arduino IDE and Wake Up Sources. I find Random Nerd Tutorials to be a great resource. I’m using an external wake up using ext1 set to trigger if any of the pins (27, 32 and 33 in my case) are high. In the sketch this is done using these lines:
define BUTTON_PIN_BITMASK 13019119616
esp_sleep_enable_ext1_wakeup(bitmask, ESP_EXT1_WAKEUP_ANY_HIGH)
This got me started, and it includes a method of determining which pin triggered the wakeup using this:
esp_sleep_get_ext1_wakeup_status()
And this function uses it to return a number of base 2 that should be the GPIO pin:
void print_GPIO_wake_up(){
uint64_t GPIO_reason = esp_sleep_get_ext1_wakeup_status();
Serial.print("GPIO that triggered the wake up: GPIO ");
Serial.println((log(GPIO_reason))/log(2), 0);
}
However, this is not reliable with my circuit as this is intended only for a single pin to trigger at once. In my project multiple pins could be high at once. I added in a print statement to see what values for GPIO_reason was being returned and discovered they were bitmask values for the pins that were high. For example, 4429185024 is the bitmask value for pins 27 and 32, that is 2^27 + 2^32. The print_GPIO_wake_up function prints 32 (log(4429185024))/log(2) = 32.0443941194) for this. In my tests it usually returns one of the high pins, but I think it is just due to rounding the returned value.
I found in the ESPRESSIF ESP-IDE programming guide: Checking Sleep Wakeup Cause esp_sleep_get_ext1_wakeup_status() returns a bit mask. It looks like I’m on the right path.
Get the bit mask of GPIOs which caused wakeup (ext1) If wakeup was caused by another source, this function will return 0. Returns bit mask, if GPIOn caused wakeup, BIT(n) will be set.
Now that I believed that GPIO_reason was returning a bitmask value, it allowed me to use it get more detailed information about the status of the pins at wakeup, for example, this prints out the pin or combination of pins:
switch (GPIO_reason)
{
case 134217728 : Serial.println("Only GPIO 27"); break; // GPIO 27
case 4294967296 : Serial.println("Only GPIO 32"); break; // GPIO 32
case 8589934592 : Serial.println("Only GPIO 33"); break; // GPIO 33
case 4429185024 : Serial.println("Both GPIO 27 + 32"); break; // GPIO 27 + 32
case 8724152320 : Serial.println("Both GPIO 27 + 33"); break; // GPIO 27 + 33
case 12884901888 : Serial.println("Both GPIO 32 + 33"); break; // GPIO 32 + 33
case 13019119616 : Serial.println("All GPIO 27 + 32 + 33"); break; // GPIO 27 + 32 + 33
default : Serial.println("Unknown pin"); break;
}
Predefined GPIO bitmask values
I found an interesting comment on Reddit, Deep sleep ext1 arduino by focojs that asked about creating bitmask values and later answered their own question.
Nevermind, it turns out you don't have to use a bitmask at all. I kept digging and found an example where someone used this:
esp_sleep_enable_ext1_wakeup(GPIO_SEL_26 | GPIO_SEL_27, ESP_EXT1_WAKEUP_ANY_HIGH);
I changed it to 26 and 27 and it works great. With that ability I guess I don't understand why anyone would mess with the bitmask. I would still be interested in learning how the bitmask works though.
Why indeed. I couldn’t find an example that they may be referring to, but I decided to explore this. Eventually I found that there are definitions built into the ESP32 library. If you include a definition in a sketch, for example GPIO_SEL_27, right click on it in the sketch and select ‘Go to definition’ from the menu.

A new tab should open and in this case is called gpio_types.h. This lists lots of pin definitions including the GPIO_SEL_x definitions.
Using the predefined definitions
I found two ways to use them. Firstly, they can be used to set up the BUTTON_PIN_BITMASK definition. I was using this in the define:
define BUTTON_PIN_BITMASK 13019119616 // This defines wake up source pins. For pins 27, 32 and 33 = 2^27 + 2^32 + 2^33 = 134217728 + 4294967296 + 8589934592 = 13019119616
Along with this in the sleep function
esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ANY_HIGH);
Instead of those two lines I can just have this in the sleep function:
esp_sleep_enable_ext1_wakeup(GPIO_SEL_27 | GPIO_SEL_32 | GPIO_SEL_33, ESP_EXT1_WAKEUP_ANY_HIGH);
The vertical bar symbol | is the bitwise OR operator.
The other way I found to use them was to determine which pins were high during wakeup. Intead of using the bitmask value as I did above, I used the built in definitions.
switch (GPIO_reason)
{
case GPIO_SEL_27 : Serial.println("Only GPIO 27"); break; // GPIO 27
case GPIO_SEL_32 : Serial.println("Only GPIO 32"); break; // GPIO 32
case GPIO_SEL_33 : Serial.println("Only GPIO 33"); break; // GPIO 33
case GPIO_SEL_27 | GPIO_SEL_32 : Serial.println("Both GPIO 27 + 32"); break; // GPIO 27 + 32
case GPIO_SEL_27 | GPIO_SEL_33 : Serial.println("Both GPIO 27 + 33"); break; // GPIO 27 + 33
case GPIO_SEL_32 | GPIO_SEL_33 : Serial.println("Both GPIO 32 + 33"); break; // GPIO 32 + 33
case GPIO_SEL_27 | GPIO_SEL_32 | GPIO_SEL_33 : Serial.println("All GPIO 27 + 32 + 33"); break; // GPIO 27 + 32 + 33
default : Serial.println("Unknown pin"); break;
}
There is a bitwise AND operator ‘&’. I don’t understand bitwise operators and so don’t understand why the OR operator ‘|’ works here instead of the AND one. Post a comment if you know.
Example sketch
Here is a simple example sketch. It is written to use ESP32 pins 27, 32 and 33, but you could use others. It will sleep until any one of the pins goes high. It then prints what awoke it, and if it was a pin, which of these pins were high at wakeup. It prints the raw bitmask and the wake up pins using three different methods; using the Random Nerd Tutorials log method, using the bitmask value and finally using the built-in definitions.
The circuit diagram is simple. Just three push buttons or switches (normally open) with pull down resistors. For each, connect between a GPIO pin and ground, with an open button switch connected between GPIO and 3.3v.
This is my test setup. It was built to allow me to test the mailbox alert. It’s more permanent than I would normally make it, but this is due the ongoing issues I have with it.

And this is the sketch
/*
Simple Deep Sleep Using EXT1 and Multple Pins
=============================================
This code shows deep sleep using ext1 with external wake up from multiple GPIOs
This code is under Public Domain License.
Expands on the sketch by Random Nerd Tutorials:
ESP32 Deep Sleep with Arduino IDE and Wake Up Sources
https://randomnerdtutorials.com/esp32-deep-sleep-arduino-ide-wake-up-sources/
Author: Pranav Cherukupalli <cherukupallip@gmail.com>
*/
#define BUTTON_PIN_BITMASK 13019119616 // This defines wake up source pins. For pins 27, 32 and 33 = 2^27 + 2^32 + 2^33 = 134217728 + 4294967296 + 8589934592 = 13019119616
//#define BUTTON_PIN_BITMASK GPIO_SEL_27 | GPIO_SEL_32 | GPIO_SEL_33
// Method to print the reason by which ESP32 has been awaken from sleep
void print_wakeup_reason(){
esp_sleep_wakeup_cause_t wakeup_reason;
wakeup_reason = esp_sleep_get_wakeup_cause();
// Print if if it was a GPIO or something else
switch(wakeup_reason)
{
case ESP_SLEEP_WAKEUP_EXT0 : Serial.println("Wakeup caused by external signal using RTC_IO"); break;
case ESP_SLEEP_WAKEUP_EXT1 : Serial.println("Wakeup caused by external signal using RTC_CNTL"); break;
case ESP_SLEEP_WAKEUP_TIMER : Serial.println("Wakeup caused by timer"); break;
case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial.println("Wakeup caused by touchpad"); break;
case ESP_SLEEP_WAKEUP_ULP : Serial.println("Wakeup caused by ULP program"); break;
default : Serial.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
}
// If it is due to the a GPIO pin find out which one/s
if (wakeup_reason == ESP_SLEEP_WAKEUP_EXT1) {
uint64_t GPIO_reason = esp_sleep_get_ext1_wakeup_status();
// Print the raw value returned by esp_sleep_get_ext1_wakeup_status. This is the bitmask of the pin/s that triggered wake up
Serial.print("Raw bitmask value returned: ");
Serial.println(GPIO_reason);
// Using log method to work out trigger pin. This is the method used by Random Nerd Tutorials
Serial.print("GPIO that triggered the wake up calculated using log method: ");
int wakeupPin = log(GPIO_reason)/log(2);
Serial.println(wakeupPin);
// Use bitmask to find which pin/s triggered wakeup
Serial.print("GPIO that triggered the wake up using raw bitmask value: ");
switch (GPIO_reason)
{
case 134217728 : Serial.println("Only GPIO 27"); break; // GPIO 27
case 4294967296 : Serial.println("Only GPIO 32"); break; // GPIO 32
case 8589934592 : Serial.println("Only GPIO 33"); break; // GPIO 33
case 4429185024 : Serial.println("Both GPIO 27 + 32"); break; // GPIO 27 + 32
case 8724152320 : Serial.println("Both GPIO 27 + 33"); break; // GPIO 27 + 33
case 12884901888 : Serial.println("Both GPIO 32 + 33"); break; // GPIO 32 + 33
case 13019119616 : Serial.println("All GPIO 27 + 32 + 33"); break; // GPIO 27 + 32 + 33
default : Serial.println("Unknown pin"); break;
}
// Use defined pins bitmask to find which pin/s triggered wakeup
Serial.print("GPIO that triggered the wake up using built in definitions: ");
switch (GPIO_reason)
{
case GPIO_SEL_27 : Serial.println("Only GPIO 27"); break; // GPIO 27
case GPIO_SEL_32 : Serial.println("Only GPIO 32"); break; // GPIO 32
case GPIO_SEL_33 : Serial.println("Only GPIO 33"); break; // GPIO 33
case GPIO_SEL_27 | GPIO_SEL_32 : Serial.println("Both GPIO 27 + 32"); break; // GPIO 27 + 32
case GPIO_SEL_27 | GPIO_SEL_33 : Serial.println("Both GPIO 27 + 33"); break; // GPIO 27 + 33
case GPIO_SEL_32 | GPIO_SEL_33 : Serial.println("Both GPIO 32 + 33"); break; // GPIO 32 + 33
case GPIO_SEL_27 | GPIO_SEL_32 | GPIO_SEL_33 : Serial.println("All GPIO 27 + 32 + 33"); break; // GPIO 27 + 32 + 33
default : Serial.println("Unknown pin"); break;
}
}
}
void setup(){
// Start serial monitor and give it time to start
Serial.begin(115200);
delay(250);
Serial.println("-------------------------------------------");
// Check the wakeup reason
print_wakeup_reason();
// Now go to sleep
goToSleep();
}
void goToSleep() {
// Prepare to sleep
// Set to wake up if any of the set pins go high
//esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ANY_HIGH);
esp_sleep_enable_ext1_wakeup(GPIO_SEL_27 | GPIO_SEL_32 | GPIO_SEL_33, ESP_EXT1_WAKEUP_ANY_HIGH);
Serial.println("===========================================");
Serial.flush();
esp_deep_sleep_start();
Serial.println("This will never be printed");
}
void loop(){
//This is not going to be called
}
A reminder that I am just a hobbyist. Let me know if you see any mistakes, have more info or have tried this and how it went.
Next, I intend to do a post looking at what I’m using to determine how long the ESP32 was asleep, sot that I can tell if it is a ‘bounce’ trigger.
Leave a comment