ESP32 Light Sleep Mode and Wake-Up Sources (Arduino IDE)

Light Sleep is one of the power-saving modes supported by the ESP32. In this guide, we’ll look at how to implement the Light Sleep mode with the ESP32 and the different wake-up sources. We’ll cover timer wake-up, different methods of external wake-up, GPIO wake-up, touch wake-up, and wake-up via UART.

ESP32 Light Sleep Mode and Wake-Up Sources (Arduino IDE)

Table of Contents

In this tutorial, we’ll cover the following topics:

Prerequisites

This tutorial focuses on programming the ESP32 using the Arduino core. Before proceeding, you should have the ESP32 Arduino core installed in your Arduino IDE. Follow the next tutorial to install the ESP32 on the Arduino IDE, if you haven’t already.

Introducing Light Sleep

The ESP32 offers several power-saving modes: modem sleep, light sleep, and deep sleep. We covered deep sleep in great detail in a previous post.

You can compare the different modes in the following table from the ESP32 Espressif datasheet.

power consumption esp32

Unlike deep sleep, which fully powers down the CPU and most peripherals, light sleep retains the CPU’s state and keeps RAM data intact. This means program variables and states are preserved, allowing the ESP32 to resume tasks almost instantly upon waking.

This means that when the ESP32 wakes up, it will resume the code where it left off before going to sleep (contrary to deep sleep which runs the code from the start). Additionally, light sleep mode supports more wake-up sources like GPIO wake-up and UART wake-up.

The ESP32 Espressif datasheet also provides a table comparing the power consumption of the different power modes.

esp32 different sleep modes

In terms of power consumption, light sleep consumes less than active mode, but more than deep sleep.

Light sleep is ideal for applications that need moderate power savings but must still respond quickly to certain events or interrupts, such as temporarily inactive sensors that periodically check data.

Setting Up Light Sleep on the ESP32

Putting the ESP32 in light sleep mode is super simple. After setting up the wake-up source or sources, you just need to call the esp_light_sleep_start() function.

esp_light_sleep_start();

After that, the ESP32 will wake up when the required wake-up source(s) is triggered. When the ESP32 wakes up, it will return to where it was in the code.

Light Sleep Wake-Up Sources

After putting the ESP32 into light sleep mode, there are several ways to wake it up.

  • Timer Wake-Up: the RTC timer can be set to wake the ESP32 after a specified time interval. Use the esp_sleep_enable_timer_wakeup() function.
  • GPIO Wake-Up: GPIOs can be configured to trigger a wake-up on a high or low signal. This is useful to wake up the board with a pushbutton or a signal from sensors. You can use the esp_sleep_enable_ext0_wakeup(), the esp_sleep_enable_ext1_wakeup_io(), or the esp_sleep_enable_gpio_wakeup().
  • Touch Wake-Up: wake up from light sleep when a touch is detected on the ESP32 touch pins. Use the touchSleepWakeUpEnable() function.
  • UART Wake-Up: wake-up the ESP32 when data is received via UART on a specific port using the esp_sleep_enable_uart_wakeup() function.

Disable Sleep Wake-Up Sources

To disable previously enabled wake-up sources, use the esp_sleep_disable_wakeup_source() function and pass as argument the wake-up source you want to disable. If you want to disable all wake-up sources pass ESP_SLEEP_WAKEUP_ALL as argument.

A list of the wake-up sources can be found here: sleep wake-up source argument.


Light Sleep with Timer Wake-Up

In this section, we’ll show you how to put the ESP32 in light sleep mode and wake it up after a defined interval.

To wake up the ESP32 after a predetermined period, you need to set up a timer wake-up using the esp_sleep_enable_timer_wakeup() function. Pass as argument the time in microseconds.

esp_sleep_enable_timer_wakeup(uint64_t time_in_us)

ESP32 Light Sleep with Timer Wake-Up Example

The following example will put the ESP32 into Light Sleep mode. The ESP32 will wake up every 10 seconds.

/*********
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp32-light-sleep-arduino/
*********/

int counter = 0;
const int ledPin = 2;           // GPIO pin for onboard LED
uint64_t sleepTime = 10000000;  // Sleep duration in microseconds (10 seconds)

void setup() {
    Serial.begin(115200);
    pinMode(ledPin, OUTPUT);

    // Enable wake-up by timer
    esp_err_t result = esp_sleep_enable_timer_wakeup(sleepTime);

    if (result == ESP_OK) {
        Serial.println("Timer Wake-Up set successfully as wake-up source.");
    } else {
        Serial.println("Failed to set Timer Wake-Up as wake-up source.");
    }
}

void loop() {
    Serial.printf("Counter: %d\n", counter);
    counter++;

    digitalWrite(ledPin, HIGH);  // LED on to indicate wake-up
    delay(2000);
    digitalWrite(ledPin, LOW);   // Turn off LED before going to sleep

    Serial.println("Going into light sleep mode");
    delay(500);
    esp_light_sleep_start();     // Enter light sleep
    Serial.println("Returning from light sleep");
}

View raw code

This example turns an LED on when the ESP32 is woken up and increases a counter variable in each loop. This example demonstrates that the ESP32 RAM retains data, so variables and program states remain intact when the ESP32 wakes up.

When it wakes up, it returns to where it was in the code (contrary to deep sleep that starts running the code from the beginning).

How Does the Code Work?

Let’s take a quick look at the relevant parts for light sleep with timer wake-up.

In the setup(), we call the esp_sleep_enable_timer_wakeup() function to enable timer wake-up. We pass as an argument the sleepTime variable(10000000 microseconds = 10 seconds).

esp_err_t result = esp_sleep_enable_timer_wakeup(sleepTime);

The esp_sleep_enable_timer_wakeup() function returns ESP_OK if timer wake-up was successfully set up. We check that in the following lines.

if (result == ESP_OK) {
    Serial.println("Timer Wake-Up set successfully as wake-up source.");
} else {
    Serial.println("Failed to set Timer Wake-Up as wake-up source.");
}

In every loop(), we increase the counter variable.

counter++;

Light up the onboard LED for two seconds.

digitalWrite(ledPin, HIGH);    // LED on to indicate wake-up
delay(2000);
 digitalWrite(ledPin, LOW); // Turn off LED before going to sleep

And we put the ESP32 in light sleep mode by calling the esp_light_sleep_start() function.

esp_light_sleep_start();       // Enter light sleep

When the ESP32 wakes up from sleep, it will continue running the code. So, after waking up, it will print the following message.

Serial.println("Returning from light sleep");

In every loop() and wake-up cycle, the counter variable increases. This proves that the ESP32 can retain the values of variables during light sleep.

ESP32 Light Sleep Mode with Timer Wake-Up Testing Serial Monitor

Light Sleep with External Wake-Up

In this section, you’ll learn how to put the ESP32 into light sleep mode and wake it up with an external wake-up, in this case, a pushbutton. Any GPIO that is an RTC GPIO can be used to wake up the ESP32.

There are three different ways to use external wake-up with light sleep: the ext0 method, the ext1 method, and the GPIO wake-up. We’ll take a look at each of them.

ext0 External Wake-Up

The ext0 external wake-up allows you to wake-up the ESP32 when an RTC GPIO is set to a predefined state. RTC peripherals are kept on during sleep if this wake-up source is requested.

To use this wake-up source, use the esp_sleep_enable_ext0_wakeup() function and pass as argument the GPIO number in this format GPIO_NUM_X (in which X corresponds to the GPIO number), and the level (0=LOW, or 1=HIGH).

For example:

esp_sleep_enable_ext0_wakeup(GPIO_NUM_27, level);

Note: you can only use pins that are RTC GPIOs with this wake-up source. Here’s a list of the RTC GPIOs for different ESP32 chip models:

  • ESP32-S3: 0-21;
  • ESP32: 0, 2, 4, 12-15, 25-27, 32-39;
  • ESP32-S2: 0-21;

Recommended reading: ESP32 Pinout Reference: Which GPIO pins should you use?

Recommended reading: ESP32-S3 DevKitC Pinout Refence Guide: GPIOs Explained

Light Sleep with External Wake-Up Example (ext0)

Here’s a simple example using light sleep with external wake-up ext0. This example wakes up the ESP32 with the press of a pushbutton. Every time it wakes up, it turns on an LED for five seconds and increases the counter variable.

To test this example, wire a pushbutton to the ESP32 GPIO 27 as shown in the picture below (because we’re using an internal pull-down resistor, we don’t need to add to the the circuit).

ESP32 with pushbutton with internal pull down resistor

After wiring the circuit, you can upload the code to your board.

/*********
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp32-light-sleep-arduino/
*********/

int counter = 0;
const int ledPin = 2;            // GPIO pin for onboard LED
#define buttonPin  GPIO_NUM_27   // Connect a pushbutton to GPIO 27

void setup() {
    Serial.begin(115200);
    pinMode(ledPin, OUTPUT);
    pinMode(buttonPin, INPUT_PULLDOWN); // pull-down resistor

    // Enable wake-up by EXT0 using the button on GPIO 27
    esp_err_t result = esp_sleep_enable_ext0_wakeup(buttonPin, 1); // 1 = wake up on HIGH signal

    if (result == ESP_OK) {
        Serial.println("EXT0 Wake-Up set successfully as wake-up source.");
    } else {
        Serial.println("Failed to set EXT0 Wake-Up as wake-up source.");
    }
}

void loop() {
    Serial.printf("Counter: %d\n", counter);
    counter++;

    digitalWrite(ledPin, HIGH); // LED on to indicate wake-up
    delay(5000);
    digitalWrite(ledPin, LOW);  // Turn off LED before going to sleep

    Serial.println("Going into light sleep mode");
    delay(500);
    esp_light_sleep_start();    // Enter light sleep
    Serial.println("Returning from light sleep");
}

View raw code

How does the code work?

First, define the GPIO that will be used for wake-up. In this example, it’s GPIO 27.

#define buttonPin  GPIO_NUM_27     // GPIO pin for pushbutton

In the setup(), we define the pushbutton as an input with a pull-down resistor.

pinMode(buttonPin, INPUT_PULLDOWN); // pull-down resistor

Then, we enable the ext0 wake-up source on GPIO 27. We pass 1 for the level, which means it will wake-up the ESP32 on a HIGH signal—because we’re using a pull-down resistor, it will receive a HIGH signal when you press the pushbutton.

// Enable wake-up by EXT0 using the button on GPIO 27
esp_err_t result = esp_sleep_enable_ext0_wakeup(buttonPin, 1); // 1 = wake up on HIGH signal

In the loop(), we increase the counter variable, light up the onboard LED for five seconds and finally, call the esp_light_sleep_start() to put the board into light sleep mode.

esp_light_sleep_start();    // Enter light sleep

Testing the Code

Upload the code to your board. Open the Serial Monitor at a baud rate of 115200. Press the pushbutton to wake up the ESP32.

Every time you press the pushbutton, the ESP32 will wake up, print, and increase the counter variable, light up the onboard LED for five seconds and will go back to sleep.

ESP32 light sleep with external wake-up

ext1 External Wake-Up

You can wake-up the ESP32 using multiple GPIOs with this wake-up source. The ext1 wake-up source is implemented by the RTC controller. So, RTC peripherals and RTC memories can be powered off in this mode.

Use the esp_sleep_enable_ext1_wakeup_io() function. Pass as arguments the bitmask of the GPIOs used for wake-up, and the mode/logic for waking up.

You can use one of the following two logics:

  • ESP_EXT1_WAKEUP_ALL_LOW: wake up when all GPIOs go low;
  • ESP_EXT1_WAKEUP_ANY_HIGH: wake up if any of the GPIOs go high.

If you’re using an ESP32-S2, ESP32-S3, ESP32-C6 or ESP32-H2, these are the available modes:

  • ESP_EXT1_WAKEUP_ANY_LOW: wake up when any of the selected GPIOs is low
  • ESP_EXT1_WAKEUP_ANY_HIGH: wake up when any of the selected GPIOs is high

Note: you can only use pins that are RTC GPIOs.

Light Sleep with External Wake-Up () Example (ext1)

In the following example, we’ll use two pushbuttons to wake up the ESP32. The ESP32 will wake up when any of the pushbuttons are pressed.

To test this example, we’ll connect a pushbutton to GPIO27 and another to GPIO26. We’ll be using the ESP32 internal pull-down resistors. You can use the following diagram as a reference to wire your circuit.

ESP32 with two pushbuttons circuit

And this is the code you should upload to your board.

/*********
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp32-light-sleep-arduino/
*********/

#include "esp_sleep.h"
#include "driver/rtc_io.h"

int counter = 0;
const int ledPin = 2;                      // GPIO pin for onboard LED
const gpio_num_t buttonPin1 = GPIO_NUM_26; // RTC IO for pushbutton 1
const gpio_num_t buttonPin2 = GPIO_NUM_27; // RTC IO for pushbutton 2
#define BUTTON_PIN_BITMASK(GPIO) (1ULL << GPIO)  // Macro for individual GPIO bitmask

// Create a bitmask for GPIO 26 and GPIO 27
uint64_t bitmask = BUTTON_PIN_BITMASK(buttonPin1) | BUTTON_PIN_BITMASK(buttonPin2);

// Method to print the GPIO that triggered the wakeup
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);
}

void setup() {
    Serial.begin(115200);
    pinMode(ledPin, OUTPUT);
    pinMode(buttonPin1, INPUT_PULLDOWN); // pull-down resistor
    pinMode(buttonPin2, INPUT_PULLDOWN); // pull-down resistor

    // Configure GPIO 26 and GPIO 27 as RTC IOs for EXT1 wake-up
    rtc_gpio_deinit(buttonPin1);
    rtc_gpio_deinit(buttonPin2);

    // Enable EXT1 wake-up source
    esp_err_t result = esp_sleep_enable_ext1_wakeup(bitmask, ESP_EXT1_WAKEUP_ANY_HIGH);

    if (result == ESP_OK) {
        Serial.println("EXT1 Wake-Up set successfully.");
    } else {
        Serial.println("Failed to set EXT1 Wake-Up as wake-up source.");
    }
}

void loop() {
    Serial.printf("Counter: %d\n", counter);
    counter++;

    digitalWrite(ledPin, HIGH); // LED on to indicate wake-up
    delay(2000);
    digitalWrite(ledPin, LOW);  // Turn off LED before going to sleep

    Serial.println("Going into light sleep mode");
    delay(500);
    esp_light_sleep_start();    // Enter light sleep

    // After wake-up, disable the hold function on the RTC GPIOs
    rtc_gpio_hold_dis(buttonPin1);
    rtc_gpio_hold_dis(buttonPin2);

    Serial.println("----------------------");
    Serial.println("Returning from light sleep");
    // Print the GPIO (button) that caused the wake-up
    print_GPIO_wake_up();
}

View raw code

When you press any of the buttons, the ESP32 will wake-up and print which GPIO/button was pressed.

ESP32 Light Sleep External Wake-Up ext1 Demonstration

GPIO External Wake-Up

Besides the ext0 and ext1 methods, there is another method that allows you to configure individual GPIOs to wake up the ESP32 on either HIGH or LOW signals. With this method, you can use any GPIO, whether it’s an RTC GPIO or a “regular” digital input.

1) To use this method, first, you need to use the gpio_wakeup_enable() function to define the GPIOs you want to use as a wake-up source and the corresponding level. The GPIO number must be in this format GPIO_NUM_X (X is the number of the GPIO). The level can be one of the following options:

  • GPIO_INTR_LOW_LEVEL – wake up the ESP32 when the GPIO goes LOW
  • GPIO_INTR_HIGH_LEVEL – wake up the ESP32 when the GPIO goes HIGH

2) You can call the gpio_wakeup_enable() function several times to set up multiple GPIOs.

3) After setting the GPIOs that will wake-up the ESP32, you must call the esp_sleep_enable_gpio_wakeup() function to enable this wake-up source.

Then, you just need to call the esp_light_sleep_start() function to put the ESP32 in light sleep state.

Light Sleep with GPIO External Wake-Up Example

The following example works exactly like the previous one, but we use this new method. We use interrupts on the pushbuttons to determine which one was the cause of the wake-up.

/*********
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp32-light-sleep-arduino/
*********/

#include "esp_sleep.h"
#include "driver/gpio.h"

int counter = 0;
const int ledPin = 2;                      // GPIO pin for onboard LED
const gpio_num_t buttonPin1 = GPIO_NUM_26; // GPIO for pushbutton 1
const gpio_num_t buttonPin2 = GPIO_NUM_27; // GPIO for pushbutton 2

int wakeup_gpio; // Variable to store the GPIO that caused wake-up

// ISR for buttonPin1
void IRAM_ATTR handleInterrupt1() {
    wakeup_gpio = buttonPin1;
}

// ISR for buttonPin2
void IRAM_ATTR handleInterrupt2() {
    wakeup_gpio = buttonPin2;
}

void setup() {
    Serial.begin(115200);
    pinMode(ledPin, OUTPUT);
    pinMode(buttonPin1, INPUT_PULLDOWN); // pull-down resistor
    pinMode(buttonPin2, INPUT_PULLDOWN); // pull-down resistor

    // Configure GPIOs as wake-up source
    gpio_wakeup_enable(buttonPin1, GPIO_INTR_HIGH_LEVEL); // Trigger wake-up on high level
    gpio_wakeup_enable(buttonPin2, GPIO_INTR_HIGH_LEVEL); // Trigger wake-up on high level

    // Enable GPIO wake-up source
    esp_err_t result = esp_sleep_enable_gpio_wakeup();

    if (result == ESP_OK) {
        Serial.println("GPIO Wake-Up set successfully.");
    } else {
        Serial.println("Failed to set GPIO Wake-Up as wake-up source.");
    }

    // Attach interrupts to GPIO pins
    attachInterrupt(digitalPinToInterrupt(buttonPin1), handleInterrupt1, RISING);
    attachInterrupt(digitalPinToInterrupt(buttonPin2), handleInterrupt2, RISING);
}

void loop() {
    Serial.printf("Counter: %d\n", counter);
    counter++;

    digitalWrite(ledPin, HIGH); // LED on to indicate wake-up
    delay(5000);
    digitalWrite(ledPin, LOW);  // Turn off LED before going to sleep

    Serial.println("Going into light sleep mode");
    delay(500);
    esp_light_sleep_start();    // Enter light sleep

    Serial.println("----------------------");
    Serial.println("Returning from light sleep");

    // Print the GPIO that caused the wake-up
    Serial.printf("Wake-up caused by GPIO %d\n", wakeup_gpio);
}

View raw code

In this code, you start by defining the GPIOs connected to the pushbuttons in the following format:

const gpio_num_t buttonPin1 = GPIO_NUM_26; // GPIO for pushbutton 1
const gpio_num_t buttonPin2 = GPIO_NUM_27; // GPIO for pushbutton 2

We create a global variable to save the GPIO that caused the wake-up.

int wakeup_gpio; // Variable to store the GPIO that caused wake-up

In the setup(), use the gpio_wakeup_enable() function to set the GPIOs as wake-up sources and the level.

// Configure GPIOs as wake-up source
gpio_wakeup_enable(buttonPin1, GPIO_INTR_HIGH_LEVEL); // Trigger wake-up on high level
gpio_wakeup_enable(buttonPin2, GPIO_INTR_HIGH_LEVEL); // Trigger wake-up on high level

Then, to enable the GPIOs as a wake-up source, call the esp_sleep_enable_gpio_wakeup() function.

// Enable GPIO wake-up source
esp_err_t result = esp_sleep_enable_gpio_wakeup();

Still in the setup(), we set the pushbuttons as interrupts so that they trigger a function when they’re pushed. This allows us to check which GPIO caused the wake-up.

// Attach interrupts to GPIO pins
attachInterrupt(digitalPinToInterrupt(buttonPin1), handleInterrupt1, RISING);
attachInterrupt(digitalPinToInterrupt(buttonPin2), handleInterrupt2, RISING);

Recommended reading: Interrupts with the ESP32 using a PIR Motion Sensor.

The interrupt handler functions are defined before the setup(), and update the wakeup_gpio variable.

//ISR for buttonPin1
void IRAM_ATTR handleInterrupt1() {
    wakeup_gpio = buttonPin1;
}

//ISR for buttonPin2
void IRAM_ATTR handleInterrupt2() {
    wakeup_gpio = buttonPin2;
}

In the loop(), like in previous examples, we update the counter variable, light up the onboard LED, and put the board into light sleep mode.

Serial.printf("Counter: %d\n", counter);
counter++;

digitalWrite(ledPin, HIGH); // LED on to indicate wake-up
delay(5000);
digitalWrite(ledPin, LOW);  // Turn off LED before going to sleep

Serial.println("Going into light sleep mode");
delay(500);
esp_light_sleep_start();    // Enter light sleep

After waking up from sleep, we print the GPIO that caused the wake-up.

Serial.println("----------------------");
Serial.println("Returning from light sleep");

// Print the GPIO that caused the wake-up
Serial.printf("Wake-up caused by GPIO %d\n", wakeup_gpio);

Light Sleep with Touch Wake-Up

It’s possible to wake up the ESP32 from light sleep when touching its touch pins—basically when a touch sensor interrupt occurs. For that, use the touchSleepWakeUpEnable() function that accepts as argument the touch pin and the threshold to wake up. For example:

touchSleepWakeUpEnable(T3, THRESHOLD);

The values read by a touch pin decrease when you touch it. The threshold value means that the ESP32 will wake up when the value read on the touch pin is below 40. You can adjust that value depending on the desired sensitivity.

Recommended reading: ESP32 Touch Pins with Arduino IDE.

Important: however, if you’re using an ESP32-S2 or ESP32-S3 model, things work a little differently. In that case, the lower the value, the more the sensitivity.

Note: the touchpins are numbered as follows (in case of the ESP32):

  • T0 (GPIO 4)
  • T1 (GPIO 0)
  • T2 (GPIO 2)
  • T3 (GPIO 15)
  • T4 (GPIO 13)
  • T5 (GPIO 12)
  • T6 (GPIO 14)
  • T7 (GPIO 27)
  • T8 (GPIO 33)
  • T9 (GPIO 32)

If you’re using an ESP32-S2 or ESP32-S3, the numbering is different.

Light Sleep with Touch Wake-Up Example

The following code will wake-up the ESP32 when it senses touch on GPIO 15 (T3) or GPIO27 (T7).

Note: I tested this wake-up source with light sleep, and in my case, it was a bit unreliable. It worked well sometimes, and others, the ESP32 crashed after waking up. I’m not sure if it’s because of my hardware or the code. Try it yourself and check your results. (Let me know if you have any tips to make this method more reliable).

Note: If you’re using ESP32-S2 or ESP32-S3, it can only detect touch wake-up on a single GPIO.

/*********
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp32-light-sleep-arduino/
*********/

#if CONFIG_IDF_TARGET_ESP32
  #define THRESHOLD 40    // Greater the value, more the sensitivity 
#else                     // ESP32-S2 and ESP32-S3 + default for other chips (to be adjusted)
  #define THRESHOLD 5000  // Lower the value, more the sensitivity
#endif

int counter = 0;
const int ledPin = 2;  // GPIO pin for onboard LED
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();

#if CONFIG_IDF_TARGET_ESP32
  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;
  }
#else
  if (touchPin < TOUCH_PAD_MAX) {
    Serial.printf("Touch detected on GPIO %d\n", touchPin);
  } else {
    Serial.println("Wakeup not by touchpad");
  }
#endif
}

void setup() {
  Serial.begin(115200);
  pinMode(ledPin, OUTPUT);

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

#if CONFIG_IDF_TARGET_ESP32
  //Setup sleep wakeup on Touch Pad 3 + 7 (GPIO15 + GPIO 27)
  touchSleepWakeUpEnable(T3, THRESHOLD);
  touchSleepWakeUpEnable(T7, THRESHOLD);
#else  //ESP32-S2 + ESP32-S3
  //Setup sleep wakeup on Touch Pad 3 (GPIO3)
  touchSleepWakeUpEnable(T3, THRESHOLD);
#endif

}

void loop() {
  Serial.printf("Counter: %d\n", counter);
  counter++;

  digitalWrite(ledPin, HIGH); // LED on to indicate wake-up
  delay(5000);
  digitalWrite(ledPin, LOW);  // LED on to indicate wake-up

  Serial.println("Going into light sleep...");
  delay(500);
  esp_light_sleep_start(); 
  
  Serial.println("----------------------");
  Serial.println("Returning from light sleep");  
  delay(2000);
  
  print_wakeup_reason();
  print_wakeup_touchpad();
}

View raw code

How Does the Code Work?

Let’s take a quick look at the relevant parts of this code.

Setting the Threshold

The first thing you need to do is setting a threshold value for the touch pins.

#define THRESHOLD 40 // Greater the value, more the sensitivity

The values read by a touch pin decrease when you touch it. The threshold value means that the ESP32 will wake up when the value read on the touch pin is below 40. You can adjust that value depending on the desired sensitivity.

Important: however, if you’re using an ESP32-S2 or ESP32-S3 model, things work a little
differently. That’s why there’s a different section in the code defining a different threshold for those boards. In this case, the lower the value, the more the sensitivity.

#if CONFIG_IDF_TARGET_ESP32
#define THRESHOLD 40 // Greater the value, more the sensitivity
#else // ESP32-S2 and ESP32-S3 + default for other chips (to be adjusted)
#define THRESHOLD 5000 // Lower the value, more the sensitivity
#endif

Wake-Up Reason and Wake-Up Touch Pad

We have a function called print_wakeup_reason() to check and print what caused the wake-up.

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

And a function called print_wakeup_touchpad() to determine which touch pin caused the wake-up.

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

#if CONFIG_IDF_TARGET_ESP32
  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;
  }
#else
  if (touchPin < TOUCH_PAD_MAX) {
    Serial.printf("Touch detected on GPIO %d\n", touchPin);
  } else {
    Serial.println("Wakeup not by touchpad");
  }
#endif

Setting Touch Pins as a Wake-up Source

To set a touch pin as a wake-up source, you can use the touchSleepWakeUpEnable() function that accepts as arguments the touch pin and the threshold value that will wake up the board.

In this example, it sets GPIO 15 (T3) and GPIO 27 (T7) as wake-up sources with the same threshold value.

#if CONFIG_IDF_TARGET_ESP32
  // Setup sleep wakeup on Touch Pad 3 + 7 (GPIO15 + GPIO 27)
  touchSleepWakeUpEnable(T3, THRESHOLD);
  touchSleepWakeUpEnable(T7, THRESHOLD);

If you’re using an ESP32-S2 or S3 model, the example just defines wake-up on GPIO 3 (T3). Note that the touch pin numbering might be different depending on the board model you’re using.

#else // ESP32-S2 + ESP32-S3
  // Setup sleep wakeup on Touch Pad 3 (GPIO3)
  touchSleepWakeUpEnable(T3, THRESHOLD);

loop()

In each loop(), we increase the counter variable to keep track of the number of times the ESP32 has woken up. We also light up the onboard LED for a few seconds before putting the ESP32 into light sleep mode.

void loop() {
  Serial.printf("Counter: %d\n", counter);
  counter++;

  digitalWrite(ledPin, HIGH); // LED on to indicate wake-up
  delay(5000);
  digitalWrite(ledPin, LOW); // LED on to indicate wake-up

  Serial.println("Going into light sleep...");
  delay(500);
  esp_light_sleep_start(); 

After waking up from sleep, the ESP32 prints the wake-up reason and which touch pin caused the wake-up.

print_wakeup_reason();
print_wakeup_touchpad();

Wiring the Circuit

To test this example, wire a jumper wire to GPIO 15 and/or GPIO 27, as shown in the schematic below.

Touch wake up Wiring the Circuit

If you’re using an ESP32-S2 or ESP32-S3 model, please check the location of your touch pins.

Testing the Example

Upload the code to your ESP32, and open the Serial Monitor at a baud rate of 115200.

ESP32 Open Serial Monitor with Arduino IDE

The ESP32 goes into deep sleep mode.

You can wake it up by touching the wire connected to Touch Pin 3 or Touch Pin 7.

ESP32 Touch Sensor Pins Arduino IDE

When you touch the wire, the ESP32 displays on the Serial Monitor: the boot number, the wake-up cause, and which touch-sensitive GPIO caused the wake-up.

ESP32 light sleep with touch wake-up demonstration

Light Sleep with UART Wake-Up

The ESP32 can wake up from light sleep on UART input by enabling UART wake-up with esp_sleep_enable_uart_wakeup().

You should also call the uart_set_wakeup_threshold() function to set the number of positive edges on the RX pin that will cause the wake-up. Three positive edges is the number set for most cases.

The following example shows how to wake up the ESP32 using UART. This specific example will wake up the ESP32 on UART 0. We’ll send a message via the Serial Monitor to wake up the ESP32. But, for a practical application, you’ll want to connect a sensor or another board to the ESP32 RX pin and send bytes via serial to wake it up.

To learn more about UART with the ESP32, we recommend taking a look at our guide: ESP32 UART Communication (Serial): Set Pins, Interfaces, Send and Receive Data (Arduino IDE).

/*********
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp32-light-sleep-arduino/
*********/

#include <driver/uart.h>

int counter = 0;
const int ledPin = 2;         // GPIO pin for onboard LED
String receivedMessage = "";  // Variable to store the complete message

// 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;
    case ESP_SLEEP_WAKEUP_UART:     Serial.println("Wakeup caused by UART"); break;
    default:                        Serial.printf("Wakeup was not caused by light sleep: %d\n", wakeup_reason); break;
  }
}

void setup() {
  Serial.begin(115200);
  pinMode(ledPin, OUTPUT);

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

  // Enable UART wake-up from light sleep
  uart_set_wakeup_threshold(UART_NUM_0, 3);  // 3 edges on U0RXD to wakeup
  esp_sleep_enable_uart_wakeup(0);           // UART0 (default Serial (includes Serial Monitor))
}

void loop() {
  Serial.printf("Counter: %d\n", counter);
  counter++;

  digitalWrite(ledPin, HIGH); // LED on to indicate wake-up
  delay(5000);
  digitalWrite(ledPin, LOW); // LED on to indicate wake-up

  Serial.println("Going into light sleep...");
  delay(500);
  esp_light_sleep_start(); 
  
  Serial.println("----------------------");
  Serial.println("Returning from light sleep");  
  delay(2000);
  
  print_wakeup_reason();
   // Clear the internal wake-up indication by sending some extra data
  Serial.write(' ');   // Send a single space character

 while (Serial.available()) {
    char incomingChar = Serial.read();  // Read each character from the buffer
    
    if (incomingChar == '\n') {  // Check if the user pressed Enter (new line character)
      // Print the message
      Serial.print("You sent: ");
      Serial.println(receivedMessage);
      
      // Clear the message buffer for the next input
      receivedMessage = "";
    } else {
      // Append the character to the message string
      receivedMessage += incomingChar;
    }
  }  
}

View raw code

This code is similar to previous examples but uses UART wake-up. We call the following two functions in the setup() to set up UART wake-up on UART0 (which is also the UART used for serial communication with the Serial Monitor).

// Enable UART wake-up from light sleep
uart_set_wakeup_threshold(UART_NUM_0, 3);   // 3 edges on U0RXD to wakeup
esp_sleep_enable_uart_wakeup(0); // UART0 (default Serial (includes Serial Monitor))

In the loop(), we call the esp_light_sleep_start() function to put the ESP32 in deep sleep mode.

esp_light_sleep_start(); 

When it wakes up from sleep, it prints the wake-up reason, and checks if it has any incoming UART data to read.

print_wakeup_reason();
// Clear the internal wake-up indication by sending some extra data
Serial.write(' ');   // Send a single space character

while (Serial.available()) {
  char incomingChar = Serial.read();  // Read each character from the buffer
    
  if (incomingChar == '\n') {  // Check if the user pressed Enter (new line character)
    // Print the message
    Serial.print("You sent: ");
    Serial.println(receivedMessage);
      
    // Clear the message buffer for the next input
    receivedMessage = "";
  } else {
    // Append the character to the message string
    receivedMessage += incomingChar;
  }
}  

Then, it starts running the loop() from the start. It increases the counter number and lights up the onboard LED before going to sleep again.

Serial.printf("Counter: %d\n", counter);
counter++;

digitalWrite(ledPin, HIGH); // LED on to indicate wake-up
delay(5000);
digitalWrite(ledPin, LOW); // LED on to indicate wake-up

Serial.println("Going into light sleep...");
delay(500);
esp_light_sleep_start(); 

Testing the Code

After uploading the code to the board, open the Serial Monitor at a baud rate of 115200. The ESP32 will go into light sleep mode.

Send something to the ESP32 via Serial using the Serial Monitor—a single character will do. The ESP32 will instantly wake up and resume the code where it left. If you want it to read serial data, you need to send it right after waking up.

ESP32 light sleep wake-up using UART

Wrapping Up

In this tutorial, we’ve looked at light sleep with the ESP32 and different ways to wake it up. It supports timer wake-up, external wake-up using the GPIOs, using the touch pins, and even via UART.

We hope you’ve found this tutorial useful. If you need a more power-saving option, you may want to take a look at deep sleep with the ESP32 instead:

Learn more about the ESP32 with our resources:

Thanks for reading.



Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »
Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »

Enjoyed this project? Stay updated by subscribing our newsletter!

1 thought on “ESP32 Light Sleep Mode and Wake-Up Sources (Arduino IDE)”

  1. Thanks for your article.
    It is very useful.

    I am wondering what to do in environment using FreeRTOS. (in Arduino IDE).
    I keep trying, but I can’t seem to solve it.

    Best regards,
    JongOk, Baek

    Reply

Leave a Comment

Download Our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.