ESP32 Deep Sleep Mode and Wake Up Sources using Arduino IDE

In this tutorial, we will learn to use ESP32 deep sleep modes and wake up sources using Arduino IDE. In this comprehensive guide, we will look at how to put the ESP32 development board into deep sleep mode. Furthermore, we will also discuss different wake up sources through which ESP32 can go from sleep to normal execution mode such as timer, external events, touch pins, etc. For the demo, we will explain it through different examples.

ESP32 deep sleep wake up sources arduino ide

Prerequisites

Before proceeding further, make sure you have the latest version of Arduino IDE installed on your system. Moreover, you should also install an ESP32 add-on in Arduino IDE. You can check this tutorial:

This ESP32 deep sleep article consists of 4 parts:

ESP32 Operating Modes

ESP32 core provides five different operating modes as follows:

  1. Active mode
  2. Modem Sleep mode
  3. Light Sleep mode
  4. Deep Sleep mode
  5. Hibernation mode

The following table lists which peripherals and units are either active or inactive during different modes. Maximum units remain in an inactive state in deep sleep and hibernation mode to save power consumption.

ESP32 operating modes

To see how these modes are useful in different applications, you can compare the power consumption of different operating modes listed in the table below:

ESP32 power consumtpion for different operating modes

What is Deep Sleep in ESP32?

To reduce power consumption and save battery life, deep sleep mode is implemented in ESP32. Generally, the module is very power-hungry and may pull up 75mA in normal consumption or inactive mode.

Esp32 Deep sleep micropython

But if WiFi is being used as well then the current consumption can go up to 240mA. Thus, if you are powering your ESP boards using batteries, it is not very convenient to use ESP32 in active mode when we want to perform some events after some specific duration only.

We can put the module into a deep sleep mode to reduce power consumption. When we have successfully activated deep sleep in ESP32, the current consumption would be in the range of micro Amperes (μA) and batteries will also last longer.

The table below shows the individual active units in ESP32 when it is set in deep sleep.

Peripherals and UnitsActive Units
ULP Co-processorON/OFF
RTC & RTC PeripheralsON
ESP32 Core & MemoryOFF
Wi-FiOFF
BluetoothOFF
PeripheralsOFF
RadioOFF

As shown in the above table, during the deep sleep mode only the RTC (real-time clock) controller, RTC peripherals, and RTC memories are in active mode. All the digital peripherals, most of the RAM, and the CPU is powered off.

Because only the RTC module is active. Therefore, the data will be lost which was not initially present in the RTC recovery memory. The current consumption goes down to 0.15mA – 10 µA if the ULP co-processor is turned on.

ESP32 Wake-Up Sources

After activating the deep sleep mode of ESP32, there are several ways in which the ESP board can be woken up such as:

  1. Timer wake up
  2. Touch pin wake up
  3. External wake up

We’ll be focusing on all three methods in this tutorial comprehensively. We can also combine these sources together as well. Hence, whenever any one of them is triggered the ESP32 development board will wake up from deep sleep mode.

How to Write Deep Sleep Code for ESP32?

To program ESP32 deep sleep mode and set up a wake up resource, follow these steps:

  • Start by configuring the wake up resource. In other words, define what source will wake up ESP32 from a deep sleep. As you may know that ESP32 supports multiple sources such as timer, external interrupt, and touch pins. We can use any one of them or a combination of these.
  • Next, we need to decide which peripheral’s power should be cut off. By default, ESP32 shut down power for all peripherals that are not required by a selected wake up source.
  • In the end, use esp_deep_sleep_start() function to put ESP32 into deep sleep mode.

ESP32 Timer Wake Up from Deep Sleep

In this section, we will learn to use ESP32 Timer as a wake-up resource from a deep sleep. First, we will put ESP32 into deep sleep mode and then see how to wake it up using a predefined time using a timer.

ESESP32 Timer Wake Up from Deep Sleep

How to Set Timer Wake Up on ESP32

The real-time clock controller (RTC) of ESP32 has a built-in timer which we can use to wake up ESP32 from a deep sleep after a predefine time is expired. We have to set a predefined amount of time, after it is over, the built-in timer wakes up the chip from deep sleep mode. This is achieved through a function. We will put the board in deep sleep mode for a specified amount of milliseconds.

We can use esp_sleep_enable_timer_wakeup(sleep_time_in_us) function which is available in the Arduino IDE. This function takes a single argument that denotes the sleeping time in microseconds. For example:

esp_sleep_enable_timer_wakeup(sleep_time_in_us)

ESP32 Timer Wake Up from Deep Sleep Example

Let’s have a look at a simple example demonstrating a timer wake up using the example from the library. We are going to show you a simple demonstration of how to use ESP32 deep sleep timer to wake up. The ESP32 will remain in active mode for 5 seconds and after that time is over, the ESP32 will be back in deep sleep mode. It will remain in deep sleep for 5 seconds. After that, it will again wake up and execute the same code.

Open your Arduino IDE and go to File > Examples > ESP32 > Deep Sleep > TimerWakeUp. The following sketch will open up.

/*
Simple Deep Sleep with Timer Wake Up
=====================================
ESP32 offers a deep sleep mode for effective power
saving as power is an important factor for IoT
applications. In this mode CPUs, most of the RAM,
and all the digital peripherals which are clocked
from APB_CLK are powered off. The only parts of
the chip which can still be powered on are:
RTC controller, RTC peripherals ,and RTC memories

This code displays the most basic deep sleep with
a timer to wake it up and how to store data in
RTC memory to use it over reboots

This code is under Public Domain License.

Author:
Pranav Cherukupalli <cherukupallip@gmail.com>
*/

#define uS_TO_S_FACTOR 1000000ULL  /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP  5        /* Time ESP32 will go to sleep (in seconds) */

RTC_DATA_ATTR int bootCount = 0;

/*
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();

  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;
  }
}

void setup(){
  Serial.begin(115200);
  delay(1000); //Take some time to open up the Serial Monitor

  //Increment boot number and print it every reboot
  ++bootCount;
  Serial.println("Boot number: " + String(bootCount));

  //Print the wakeup reason for ESP32
  print_wakeup_reason();

  /*
  First we configure the wake up source
  We set our ESP32 to wake up every 5 seconds
  */
  esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
  Serial.println("Setup ESP32 to sleep for every " + String(TIME_TO_SLEEP) +
  " Seconds");

  /*
  Next we decide what all peripherals to shut down/keep on
  By default, ESP32 will automatically power down the peripherals
  not needed by the wakeup source, but if you want to be a poweruser
  this is for you. Read in detail at the API docs
  http://esp-idf.readthedocs.io/en/latest/api-reference/system/deep_sleep.html
  Left the line commented as an example of how to configure peripherals.
  The line below turns off all RTC peripherals in deep sleep.
  */
  //esp_deep_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF);
  //Serial.println("Configured all RTC Peripherals to be powered down in sleep");

  /*
  Now that we have setup a wake cause and if needed setup the
  peripherals state in deep sleep, we can now start going to
  deep sleep.
  In the case that no wake up sources were provided but deep
  sleep was started, it will sleep forever unless hardware
  reset occurs.
  */
  Serial.println("Going to sleep now");
  Serial.flush(); 
  esp_deep_sleep_start();
  Serial.println("This will never be printed");
}

void loop(){
  //This is not going to be called
}

How does the Code Work?

Defining Sleep Time

Firstly, the sleep time will be defined in the variable TIME_TO_SLEEP. It is set to 5 meaning the ESP32 board will remain in deep sleep for 5 seconds. Also, a microsecond to second conversion factor is also defined which will be used later on in the program code.

#define uS_TO_S_FACTOR 1000000ULL  /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP  5        /* Time ESP32 will go to sleep (in seconds) */

Saving Boot Count in RTC Memory

ESP32 has an 8kb SRAM for the RTC controller which is also known as RTC fast memory and this memory can be used to save data. The data stored on this memory will remain saved during deep sleep but it will be erased during boot-up. Hence, we can use this memory to save data before putting ESP32 into deep sleep mode.

Now we will save the number of times our ESP32 board woke up from deep sleep inside the variable ‘bootCount.’

This will be saved in the RTC Memory of the development board by using ‘RTC_DATA_ATTR’ before defining the bootCount variable.

RTC_DATA_ATTR int bootCount = 0;

ESP32 Wake Up Reason

The following section of code shows the function print_wakeup_reason(). Through this function, the reason of our ESP32 waking up from deep sleep mode will get displayed on the serial monitor. As you can see there are various options including external wakeup using RTC_IO/RTC_CNTL, timer, touchpad, ULP program. For whichever reason the wake up will occur, it will get displayed.

void print_wakeup_reason(){
  esp_sleep_wakeup_cause_t wakeup_reason;

  wakeup_reason = esp_sleep_get_wakeup_cause();

  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;
  }
}

setup()

Inside the setup() function, we will open a serial connection at a baud rate of 115200 and add a delay of 1 second to open the serial monitor.

Serial.begin(115200);
  delay(1000); //Take some time to open up the Serial Monitor

Then we will increment the bootCount variable to 1 every time the ESP32 board wakes up from deep sleep. On the serial monitor, the number of times the ESP32 module booted will be displayed automatically.

++bootCount;
  Serial.println("Boot number: " + String(bootCount));

Now, we will call the print_wakeup_reason() function which was defined earlier to display the reason of the wake up. The message will be displayed on the serial monitor.

//Print the wakeup reason for ESP32
  print_wakeup_reason();

Using the following function, the ESP32 board wakes up from deep sleep mode. This function takes in the time in microseconds as a parameter. Thus, we will pass TIME_TO_SLEEP (which is 5 seconds)* the conversion factor to insure that the argument inside the function is in micro seconds.

 esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);

Also, display the following message on the Serial Monitor.

Serial.println("Setup ESP32 to sleep for every " + String(TIME_TO_SLEEP) +
  " Seconds");

Finally, the ESP32 board goes to deep sleep when esp_deep_sleep_start() function is called.

Serial.println("Going to sleep now");
Serial.flush(); 
esp_deep_sleep_start();

loop()

The loop() function is empty because it never reaches this part of the program code. The ESP32 goes into deep sleep.

Demonstration

Make sure you choose the correct board and COM port before uploading your code to the board. Therefore go to Tools > Board and select ESP32 Dev Module.

select esp32 board

Then, go to Tools > Port and select the appropriate port through which your board is connected.

Selecting COM PORT ESP32

Click on the upload button to upload the code to ESP32 development board.

After you have uploaded your code to the ESP32, press its ENABLE button.

ESP32 enable reset button

In your Arduino IDE, open up the serial monitor and you will see the messages showing the boot count and the board going to deep sleep and waking up after every 5 seconds.

ESP32 Deep sleep timer wake up serial monitor
Serial Monitor

Pressing the ENABLE button on the ESP32 board will erase the RTC fast memory and the boot count will go back to zero.

In summary:

In this section, we have learned how to put ESP32 into deep sleep mode and wake it up from deep sleep by using a timer as a wake up source. Furthermore, we have also learned how to save data to RTC memories before putting ESP32 into a deep sleep.

ESP32 Touch Wake Up from Deep Sleep

In this section, we will learn to use ESP32 touch pins as a wake-up source from a deep sleep. First, we will put ESP32 into deep sleep mode and then see how to wake it up using touch sensor pins. The touch wake up feature allows the ESP32 to wake up from deep sleep when a capacitive touch sensor is triggered, providing a low-power way to wake up the device.

ESP32 Touch Wake Up from Deep Sleep

To wake up the ESP32 from deep sleep using touch, we will use the esp_sleep_enable_touchpad_wakeup() function in the Arduino IDE.

esp_sleep_enable_touchpad_wakeup()

ESP32 Touch Wake Up

ESP32 provides 10 capacitive touch GPIO pins which can be used for touch sensing. These are capacitive touch pins that detect changes in capacitance when a conductive object, such as a finger, comes into contact with the sensor. The touch sensing mechanism is based on the capacitance variation principle, where the capacitance of a capacitive sensor is affected by changes in the dielectric constant of the surrounding environment.

The ESP32 uses a capacitive sensing module to measure the capacitance of the touch pins. When a touch is detected, the capacitance of the corresponding touch pin changes, and the capacitive sensing module generates an interrupt to wake up the ESP32 from a deep sleep. The touch pins can also be used as regular GPIO pins when touch sensing is not required.

Touch sensor pins of Devkit

The touch pins on the ESP32 are numbered T0 to T9 and their respective GPIO numbers are shown below:

  • TOUCH0 – GPIO4
  • TOUCH1 – GPIO0
  • TOUCH2 – GPIO2
  • TOUCH3 – GPIO15
  • TOUCH4 – GPIO13
  • TOUCH5 – GPIO12
  • TOUCH6 – GPIO14
  • TOUCH7 – GPIO27
  • TOUCH8 – GPIO33
  • TOUCH9 – GPIO32

Attach a connecting wire to your ESP32 module as shown below. In this case we are using GPIO15 (Touch 3) because that is the touch pin used in the example code. You can use any suitable touch pin.

ESP32 Deep sleep touch wake up schematic diagram

ESP32 Touch Pins Wake Up from Deep Sleep Example

Let’s have a look at a simple example demonstrating a touch wake up using the example from the library. We are going to show you a simple demonstration of how to use the touch pins to wake up. The ESP32 will remain in active mode whenever the user will touch the wire which is connected to the touch pin otherwise it will be in deep sleep mode.

Open your Arduino IDE and go to File > Examples > ESP32 > Deep Sleep > TouchWakeUp. The following sketch will open up.

/*
Deep Sleep with Touch Wake Up
=====================================
This code displays how to use deep sleep with
a touch as a wake up source and how to store data in
RTC memory to use it over reboots

This code is under Public Domain License.

Author:
Pranav Cherukupalli <cherukupallip@gmail.com>
*/

#define Threshold 40 /* Greater the value, more the sensitivity */

RTC_DATA_ATTR int bootCount = 0;
touch_pad_t touchPin;
/*
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();

  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;
  }
}

/*
Method to print the touchpad by which ESP32
has been awaken from sleep
*/
void print_wakeup_touchpad(){
  touchPin = esp_sleep_get_touchpad_wakeup_status();

  switch(touchPin)
  {
    case 0  : Serial.println("Touch detected on GPIO 4"); break;
    case 1  : Serial.println("Touch detected on GPIO 0"); break;
    case 2  : Serial.println("Touch detected on GPIO 2"); break;
    case 3  : Serial.println("Touch detected on GPIO 15"); break;
    case 4  : Serial.println("Touch detected on GPIO 13"); break;
    case 5  : Serial.println("Touch detected on GPIO 12"); break;
    case 6  : Serial.println("Touch detected on GPIO 14"); break;
    case 7  : Serial.println("Touch detected on GPIO 27"); break;
    case 8  : Serial.println("Touch detected on GPIO 33"); break;
    case 9  : Serial.println("Touch detected on GPIO 32"); break;
    default : Serial.println("Wakeup not by touchpad"); break;
  }
}

void callback(){
  //placeholder callback function
}

void setup(){
  Serial.begin(115200);
  delay(1000); //Take some time to open up the Serial Monitor

  //Increment boot number and print it every reboot
  ++bootCount;
  Serial.println("Boot number: " + String(bootCount));

  //Print the wakeup reason for ESP32 and touchpad too
  print_wakeup_reason();
  print_wakeup_touchpad();

  //Setup interrupt on Touch Pad 3 (GPIO15)
  touchAttachInterrupt(T3, callback, Threshold);

  //Configure Touchpad as wakeup source
  esp_sleep_enable_touchpad_wakeup();

  //Go to sleep now
  Serial.println("Going to sleep now");
  esp_deep_sleep_start();
  Serial.println("This will never be printed");
}

void loop(){
  //This will never be reached
}

How does the Code Work?

In this section, we will explain different parts of code to give you a better understanding.

Define Touch Pins Threshold

As we have to use the touch pins to awaken the ESP32 board from deep sleep thus, we will have to set a threshold value. This threshold value will be compared with the value read by the sensor when the user will touch the touch pin. If the read value is lower than the threshold which is 40 in this case then the ESP32 board will wake up. One thing to mention here is that, whenever the user touches the wire connected with the touch pin, the value read by the sensor will decrease.

#define Threshold 40

Saving Boot Count in RTC Memory

ESP32 has an 8kb SRAM for the RTC controller which is also known as RTC fast memory and this memory can be used to save data. The data stored on this memory will remain saved during deep sleep but it will be erased during boot-up. Hence, we can use this memory to save data before putting ESP32 into deep sleep mode.

Now we will save the number of times our ESP32 board woke up from deep sleep inside the variable ‘bootCount.’

This will be saved in the RTC Memory of the development board by using ‘RTC_DATA_ATTR’ before defining the bootCount variable.

RTC_DATA_ATTR int bootCount = 0;

ESP32 Wake Up Reason

This print_wakeup_reason() function prints the reason for the ESP32 waking up from deep sleep. The method gets the wakeup reason using the function esp_sleep_get_wakeup_cause() and prints the corresponding message using a switch-case statement. The possible wakeup reasons include external signals using RTC_IO or RTC_CNTL pins, a timer, touchpad, or ULP program. If the wakeup reason is not caused by deep sleep, a message with the wakeup reason code is printed.

/*
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();

  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;
  }
}

This print_wakeup_touchpad function defines print the touchpad by which the ESP32 was awakened from a deep sleep. The esp_sleep_get_touchpad_wakeup_status() function is called to retrieve the touchpad number from the RTC memory, which is stored during the sleep cycle. A switch-case statement is used to print the corresponding GPIO pin number of the touchpad. If the wake up was not caused by a touchpad, the function prints “Wakeup not by touchpad”.

/*
Method to print the touchpad by which ESP32
has been awaken from sleep
*/
void print_wakeup_touchpad(){
  touchPin = esp_sleep_get_touchpad_wakeup_status();

  switch(touchPin)
  {
    case 0  : Serial.println("Touch detected on GPIO 4"); break;
    case 1  : Serial.println("Touch detected on GPIO 0"); break;
    case 2  : Serial.println("Touch detected on GPIO 2"); break;
    case 3  : Serial.println("Touch detected on GPIO 15"); break;
    case 4  : Serial.println("Touch detected on GPIO 13"); break;
    case 5  : Serial.println("Touch detected on GPIO 12"); break;
    case 6  : Serial.println("Touch detected on GPIO 14"); break;
    case 7  : Serial.println("Touch detected on GPIO 27"); break;
    case 8  : Serial.println("Touch detected on GPIO 33"); break;
    case 9  : Serial.println("Touch detected on GPIO 32"); break;
    default : Serial.println("Wakeup not by touchpad"); break;
  }
}

Interrupt

Another important part in the program code for the touch wake up is including an interrupt for the respective touch pin. We will define the interrupt in the following line:

//Setup interrupt on Touch Pad 3 (GPIO15)
touchAttachInterrupt(T3, callback, Threshold);

The touchAttachInterrupt() function takes in three parameters. the first is the touch pin, the second is the callback function and the third is the threshold variable which we previously defined. Notice, that we are using GPIO15 (Touch3) as the touch pin thus we have used T3 as a parameter inside the function. For your particular touch pin specify the parameter accordingly e.g. T2 if using GPIO2. When the user will touch the touch pin (T3), the value on that particular pin will be lower than the threshold value, the ESP32 board wakes up. As a result, the callback function will be called then.

The callback function will only be called when the ESP32 module has woken up from deep sleep. This function is empty.

void callback(){
   //placeholder callback function
}

The following line of code will enable the touch wake up as the source of wake up for the module.

//Configure Touchpad as wakeup source
esp_sleep_enable_touchpad_wakeup()

Demonstration

Attach a connecting wire to GPIO15 of your ESP32 module. After uploading the code to your module, press its ENABLE button. Now open the serial monitor in your Arduino IDE. Touch the connecting wire attached T3 and the module will wake up. In the serial monitor, you will be able to view the boot count, the reason for wakeup, the GPIO pin which was touched and a message displaying that the board is going back to sleep.

ESP32 Deep sleep touch wake up serial monitor
Serial Monitor

In summary:

In this section, we have learned how to use ESP32’s deep sleep mode with touch wake up, and how to store data in RTC memory. When the ESP32 wakes up from deep sleep, it prints the reason for wake-up and the touchpad that caused it to the serial monitor, attaches an interrupt to touchpad T3, configures the touch pin as a wake-up source, and puts the ESP32 into deep sleep mode.

ESP32 External Wake Up from Deep Sleep

In this section, we will learn how to use an external interrupt to wake up the ESP32 from a deep sleep. There are two external wake up resources available such as an ext0 and ext1. When the state of a GPIO pin is changed, the ESP32 will wake up from deep sleep. There are two types of external interrupt wake-ups in the ESP32. We will see examples to use both ext0 and ext1 as a wake up source.

ESP32 External interrupt Wake Up from Deep Sleep

Note: Only Real-time clock or RTC input-output pins can be used as a wake up source. Therefore, to make use of this external wake up we need to make sure that during deep sleep, the RTC peripherals are powered ON. Thus, we can only use the RTC GPIOs to induce the ext0, ext1 external wake-up.

The figure below shows the RTC GPIO pinout which we can use, highlighted in red:

ESP32 RTC GPIO pins as a wake up source from deep sleep mode

For ESP32 Devkit V1- DOIT we can use the following 14 GPIO pins:

  • RTC_GPIO0 : GPIO36
  • RTC_GPIO3: GPIO39
  • RTC_GPIO9: GPIO32
  • RTC_GPIO8: GPIO33
  • RTC_GPIO6: GPIO25
  • RTC_GPIO7: GPIO26
  • RTC_GPIO17: GPIO27
  • RTC_GPIO16: GPIO14
  • RTC_GPIO15: GPIO12
  • RTC_GPIO14: GPIO13
  • RTC_GPIO11: GPIO0
  • RTC_GPIO13: GPIO15
  • RTC_GPIO12: GPIO2
  • RTC_GPIO10: GPIO4

How to use External Interrupts as a wake up Source for ESP32

Ext0 and Ext1 are two external interrupts available with ESP32 microcontroller. These external interrupts can be used to wake up the ESP32 from deep sleep mode.

To use Ext0 or Ext1 as a wake-up source, you need to configure the corresponding GPIO pin as an input and enable the external interrupt using the ESP32 SDK’s GPIO API. Once the external interrupt is enabled, the ESP32 will be able to wake up from deep sleep mode when the corresponding pin changes state.

Ext0 and Ext1 are useful for waking up the ESP32 from deep sleep mode in response to external events, such as button presses or sensor readings.

External Wake Up – ext0

In this section, we will see how to configure and use external wake up ext0 as a source to exit deep sleep mode. Ext0 is associated with RTC GPIO pins, and it can be used to wake up the ESP32 from deep sleep using a rising or falling edge on the pin. The RTC module triggers a wake up interrupt when one of the RTC GPIO pins is set to a predefined logic level.

We will use the following function to wake our ESP32 board from deep sleep via ext0 wake up in Arduino IDE:

esp_sleep_enable_ext0_wakeup(GPIO_NUM_X, level)

The esp_sleep_enable_ext0_wakeup(GPIO_NUM_X, level) function defines how ext0 wakes up ESP32 from deep sleep. The first parameter is the RTC GPIO pin that will trigger the wakeup and the second parameter is the state (1 or 0) of that pin.

This function takes in the following parameters:

The first argument is the RTC GPIO pin. This is the object which denotes the GPIO which would be set up as the wake-up source. Additionally, the last argument is the trigger level. This trigger level defines the state of the GPIO pin as a source of wake up. It can be one of the following types:

  • 1: Wake up process is triggered when the GPIO is in a high state.
  • 0: Wake up process is triggered when the GPIO is in a low state.

External Wake Up – ext1

Ext1, on the other hand, can be used to wake up the ESP32 from deep sleep using multiple RTC GPIO pins. This is used when we want to have more than one RTC GPIO pin as a source to trigger the external wake-up. Only the RTC controller is powered on during deep sleep. Therefore, we can only use the RTC GPIOs which we previously stated. We will use the following function to wake our ESP32 board from deep sleep via ext1 wake up in Arduino IDE:

esp_sleep_enable_ext1_wakeup(bitmask, mode)

The esp_sleep_enable_ext1_wakeup(bitmask, mode) function defines how ext1 wakes up ESP32 from deep sleep.

This function takes on the following parameters:

The first argument is the bitmask of the GPIO pin number which will trigger the wakeup. The second argument is the mode used to wake up the development board. This shows the state of the GPIO pin. It can be one of the following:

  • ESP_EXT1_WAKEUP_ANY_HIGH: Wake up process is triggered when ANY GPIO is in a high state.
  • ESP_EXT1_WAKEUP_ALL_LOW: Wake up process is triggered when ALL the GPIOs are in a low state.

ESP32 EXT0 External Wake Up from Deep Sleep Example

Let’s take a look at a simple example demonstrating the ext0 external wake-up feature of the ESP32. We will demonstrate how to use a push button as a wake-up source from deep sleep mode. The ESP32 will remain in deep sleep mode until the user presses the push button, which will trigger the ext0 external interrupt and wake up the ESP32 from deep sleep mode into active mode.

To use ext0 as a wake-up source from deep sleep mode, we need to connect the push button to the GPIO pin assigned as ext0 (by default, GPIO 26). We also need to configure the GPIO pin as an input, enable the ext0 external interrupt, and enter deep sleep mode. When the push button is pressed, it will cause a change in the state of the GPIO pin, triggering the ext0 external interrupt and waking up the ESP32 from deep sleep mode.

Once the ESP32 is woken up from deep sleep mode, it will resume executing the code from where it left off before entering deep sleep mode. The code can then handle the event that triggered the wake-up, such as reading sensor data or sending a message over a network.

ESP32 with push button ext0

We will need the following components.

Required Components

  • ESP 32 Devkit board
  • One Push button
  • One 10k ohm resistor
  • Breadboard
  • Connecting Wires

Schematic Diagram

As shown in the schematic diagram below, we will connect a push button to one of the RTC GPIO pins, specifically GPIO14 in this example. To ensure proper functioning of the push button, we also connected a 10k ohm pull-down resistor between GPIO14 and ground. One terminal of the push button is connected to the 3.3V power supply, and the other terminal is connected to GPIO14, which will sense a change in the logic level when the button is pressed.

By connecting the push button to GPIO14, we can use it as an external wake-up source for the ESP32 microcontroller when it is in deep sleep mode. When the push button is pressed, it will change the logic level on GPIO14, triggering the external interrupt and waking up the ESP32 from deep sleep mode.

ESP32 Deep sleep external wake up ext0 schematic diagram

ESP32 ext0 External Wake Up Code

Open your Arduino IDE and go to File > Examples > ESP32 > Deep Sleep > ExternalWakeUp. The following sketch will open up. This example sketch has set GPIO33 as the triggering pin although in our case the push button is connected with GPIO14. Thus, we will change the GPIO pin which will cause the trigger for the wake up inside the sketch. You can use any suitable input GPIO pin but remember to replace it appropriately in the program code.

/*
Deep Sleep with External Wake Up
=====================================
This code displays how to use deep sleep with
an external trigger as a wake up source and how
to store data in RTC memory to use it over reboots

This code is under Public Domain License.

Hardware Connections
======================
Push Button to GPIO 14 pulled down with a 10K Ohm
resistor

NOTE:
======
Only RTC IO can be used as a source for external wake
source. They are pins: 0,2,4,12-15,25-27,32-39.

Author:
Pranav Cherukupalli <cherukupallip@gmail.com>
*/

#define BUTTON_PIN_BITMASK 0x200000000 // 2^33 in hex

RTC_DATA_ATTR int bootCount = 0;

/*
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();

  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;
  }
}

void setup(){
  Serial.begin(115200);
  delay(1000); //Take some time to open up the Serial Monitor

  //Increment boot number and print it every reboot
  ++bootCount;
  Serial.println("Boot number: " + String(bootCount));

  //Print the wakeup reason for ESP32
  print_wakeup_reason();

  /*
  First we configure the wake up source
  We set our ESP32 to wake up for an external trigger.
  There are two types for ESP32, ext0 and ext1 .
  ext0 uses RTC_IO to wakeup thus requires RTC peripherals
  to be on while ext1 uses RTC Controller so doesnt need
  peripherals to be powered on.
  Note that using internal pullups/pulldowns also requires
  RTC peripherals to be turned on.
  */
  esp_sleep_enable_ext0_wakeup(GPIO_NUM_14,1); //1 = High, 0 = Low

  //If you were to use ext1, you would use it like
  //esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ANY_HIGH);

  //Go to sleep now
  Serial.println("Going to sleep now");
  esp_deep_sleep_start();
  Serial.println("This will never be printed");
}

void loop(){
  //This is not going to be called
}

How does the Code Work?

This example sketch shows both types of external wake up i.e. ext0 and ext1. In this section, we will explain the parts related to ext0. The parts for ext1 have been commented on inside the code.

Saving Boot Count in RTC Memory

ESP32 has an 8kb SRAM for the RTC controller which is also known as RTC fast memory and this memory can be used to save data. The data stored on this memory will remain saved during deep sleep but it will be erased during boot-up. Hence, we can use this memory to save data before putting ESP32 into deep sleep mode.

Now we will save the number of times our ESP32 board woke up from deep sleep inside the variable ‘bootCount.’

This will be saved in the RTC Memory of the development board by using ‘RTC_DATA_ATTR’ before defining the bootCount variable.

RTC_DATA_ATTR int bootCount = 0;

ESP32 Wake Up Reason

This print_wakeup_reason() function prints the reason for the ESP32 waking up from deep sleep. The method gets the wakeup reason using the function esp_sleep_get_wakeup_cause() and prints the corresponding message using a switch-case statement. The possible wakeup reasons include external signals using RTC_IO or RTC_CNTL pins, a timer, touchpad, or ULP program. If the wakeup reason is not caused by deep sleep, a message with the wakeup reason code is printed.

/*
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();

  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;
  }
}

Inside the setup() function, we will open a serial connection at a baud rate of 115200 and add a delay of 1 second to open the serial monitor.

Serial.begin(115200);
delay(1000);

Then we will increment the bootCount variable to 1 every time the ESP32 board wakes up from deep sleep. On the serial monitor, the number of times the ESP32 module booted will be displayed automatically.

++bootCount;
Serial.println("Boot number: " + String(bootCount));

Now, we will call the print_wakeup_reason() function which was defined earlier to display the reason for the wake up. The message will be displayed on the serial monitor.

print_wakeup_reason();

To wake the ESP32 board from deep sleep via ext0, we will use the following function in our code:

The first argument specifies the GPIO pin number that is connected to the push button. In our case, we have connected the push button to GPIO14, but any appropriate RTC GPIO pin can be used for this purpose.

The second parameter, which is set to 1 in this example, specifies that the wake-up will be triggered when the push button is in a high state, or logic level 1.

esp_sleep_enable_ext0_wakeup(GPIO_NUM_14, 1);

By calling this function before putting the ESP32 into deep sleep mode, we are configuring it to wake up when the push button connected to the specified GPIO pin is pressed.

Finally, these lines put the ESP32 into deep sleep mode using esp_deep_sleep_start() after printing a message to the serial monitor. The ESP32 will consume minimal power until woken up by an external interrupt. Any code following the esp_deep_sleep_start() function call will not be executed until the ESP32 is woken up.

Serial.println("Going to sleep now");
esp_deep_sleep_start();
Serial.println("This will never be printed");

Demonstration

To see the demonstration of the above code, upload the code to Arduino. Before uploading the code, make sure to select the ESP32 board from Tools > Board and also select the correct COM port to which the ESP32 board is connected from Tools > Port.

Once the code is uploaded to ESP32, open the serial monitor of Arduino IDE and set the baud rate to 115200. Press the push button and notice the ESP32 board waking up from deep sleep. you can view the messages on the serial monitor showing the boot count, wake up reason, and going back to sleep message.

ESP32 Deep sleep ext0 wake up serial monitor
Serial Monitor

In summary, the ext0 external wake-up feature of the ESP32 allows the ESP32 to enter deep sleep mode and wake up in response to an external event, such as a push button press, conserving power and extending the battery life of battery-powered devices.

ESP32 ext1 External Wake Up from Deep Sleep Example

In the following example, we will demonstrate how to use the ext1 external wake-up feature of the ESP32 microcontroller with the help of two push buttons. We will use the same sketch that we used for ext0. The push buttons will act as wake-up sources for the ESP32 board.

ESP32 with two push buttons ext1

To build this circuit, we will need the following components.

  • ESP 32 Devkit board
  • Two Pushbuttons
  • Two 10k ohm resistors
  • Breadboard
  • Connecting Wires

Schematic Diagram

As shown in the schematic, both terminals of the two push buttons are connected to ground through two separate 10k ohm resistors, and the other terminals are connected to GPIO2 and GPIO15, respectively. Both buttons are also connected to the 3.3V power source pins.

By connecting the push buttons to GPIO2 and GPIO15, we can use them as external wake-up sources for the ESP32 microcontroller when it is in deep sleep mode. When either push button is pressed, it will change the logic level on the corresponding GPIO pin, triggering the external interrupt and waking up the ESP32 from deep sleep mode.

Hence, by connecting multiple push buttons to different GPIO pins, we can create multiple external wake-up sources for the ESP32, allowing for more versatile and complex wake-up scenarios. and demonstrating ext1 feature to use multiple RTC GPIOs as a wake up sources.

ESP32 Deep sleep external wake up ext1 schematic diagram

ESP32 ext1 External Wake Up Code

For this example, we will use the same sketch as we did for ext0. However, we will need to make some changes to the code to enable the use of ext1. Specifically, we will need to uncomment the lines of code for ext1 in the sketch and comment out the parts where ext0 is being used. Additionally, we will need to convert the GPIO15 and GPIO2 pin numbers into bit mask numbers. This has already been done in the program code provided below:

/*
Deep Sleep with External Wake Up
=====================================
This code displays how to use deep sleep with
an external trigger as a wake up source and how
to store data in RTC memory to use it over reboots

This code is under Public Domain License.

Hardware Connections
======================
Push Button to GPIO 33 pulled down with a 10K Ohm
resistor

NOTE:
======
Only RTC IO can be used as a source for external wake
source. They are pins: 0,2,4,12-15,25-27,32-39.

Author:
Pranav Cherukupalli <cherukupallip@gmail.com>
*/

#define BUTTON_PIN_BITMASK 0x8004 // GPIOs 2 and 15

RTC_DATA_ATTR int bootCount = 0;

/*
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();

  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;
  }
}

/*
Method to print the GPIO that triggered the wakeup
*/
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);
}
  
void setup(){
  Serial.begin(115200);
  delay(1000); //Take some time to open up the Serial Monitor

  //Increment boot number and print it every reboot
  ++bootCount;
  Serial.println("Boot number: " + String(bootCount));

  //Print the wakeup reason for ESP32
  print_wakeup_reason();

  //Print the GPIO used to wake up
  print_GPIO_wake_up();

  /*
  First we configure the wake up source
  We set our ESP32 to wake up for an external trigger.
  There are two types for ESP32, ext0 and ext1 .
  ext0 uses RTC_IO to wakeup thus requires RTC peripherals
  to be on while ext1 uses RTC Controller so doesnt need
  peripherals to be powered on.
  Note that using internal pullups/pulldowns also requires
  RTC peripherals to be turned on.
  */
  //esp_deep_sleep_enable_ext0_wakeup(GPIO_NUM_15,1); //1 = High, 0 = Low

  //If you were to use ext1, you would use it like
  esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ANY_HIGH);

  //Go to sleep now
  Serial.println("Going to sleep now");
  delay(1000);
  esp_deep_sleep_start();
  Serial.println("This will never be printed");
}

void loop(){
  //This is not going to be called
}

How does the Code Work?

We will explain the parts which are different in ext1 as compared to the previous sketches.

Defining BUTTON_PIN_BITMASK

Firstly, we will use bit mask values for the GPIO pins which we are using as wake up sources. As you already know, for ext1 we will use the esp_sleep_enable_ext1_wakeup() function. This takes in two parameters. For the first parameter we will have to specify the bit mask value for GPIO15 and GPIO2.

Bitmask for single GPIO

To find the GPIO bitmask we will follow the steps given below.

  • First, calculate 2^GPIO_Pin_Number. Save this number.
  • Now, go to an online decimal-to-hexadecimal number converter like this. Convert the number which you obtained above in hexadecimal.
  • If using a single GPIO pin then this is the value that you will use when defining the BUTTON_PIN_BITMASK variable. However, we are using two push buttons each attached to a different GPIO pin i.e. GPIO15 and GPIO2. We will have to perform additional steps for this purpose.

Bitmask for multiple GPIOs

  • For GPIO15 and GPIO2 follow the steps below:
    Calculate 2^15 + 2^2
  • Now convert the value obtained into hexadecimal. It will be 8004.
  • This is the value which we will define as ‘BUTTON_PIN_BITMASK.’
#define BUTTON_PIN_BITMASK 0x8004

If you increase the number of GPIOs just include it in the first step e.g. 2^GPIO_X + 2^GPIO_Y + 2^GPIO_Z + …

print_GPIO_wake_up()

We will use the print_GPIO_wake_up() function to determine which GPIO pin caused the ESP32 board to wake up from deep sleep. Inside this function we will call the esp_sleep_get_ext1_wakeup_status() function and save the value it returns in the variable ‘GPIO_reason.’ This value will be equal to 2^GPIO_PIN_NUMBER. To display the GPIO pin number in decimal we will use an appropriate conversion and print it in the serial monitor.

void print_GPIO_wake_up(){
  int 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);
}

Enabling ext1

We will use the following function to enable ext1 as the wake up source for our ESP32 module. This will cause GPIO15 and GPIO2 to act as triggering sources whenever their state is high.

esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ANY_HIGH);

Demonstration

Once the code is uploaded to ESP32, open the serial monitor of Arduino IDE and set the baud rate to 115200. Press the push buttons and notice the ESP32 board waking up from deep sleep. You can view the messages on the serial monitor showing the boot count, wake up reason, the GPIO which triggered the wake up, and then going back to sleep message.

ESP32 Deep sleep ext1 wake up serial monitor
Serial Monitor

You may also like to read:

Conclusion

In conclusion, we have learned comprehensively about the ESP32’s deep sleep mode and wake up sources such as timer, touch pins, and external wake up. We can use a single wake-up source or combine them together to wake the ESP32 module. These included timer wake up, touch pin wakes-up, and external wake up.

1 thought on “ESP32 Deep Sleep Mode and Wake Up Sources using Arduino IDE”

  1. I wonder if I can wake up esp32 caused of the internal clock reach a certain time as: if (Time_str == “10:52:00”)…wake_up… Or is the clock also sleeping during deep sleep?

    Reply

Leave a Comment