ESP32 practical power saving: preserve gpio status, external and ULP wake up – 5
With the esp32, when using light sleep, deep sleep, or hibernation, you need to select the wake-up source to turn the device back on.
There are many solutions; some are the standard RTC or external; others are new entries like touch or ULP.
Suppose you have already read the previous chapter. In that case, you know that there are significant differences in power consumption if you select an external or timer wake-up source, so pay attention to your choice.
We are now going to test an external wake-up source.
External wake up (ext0), single gpio management
RTC IO module contains logic to trigger wake-up when one of RTC GPIOs is set to a predefined logic level. RTC IO is part of the RTC peripherals power domain, so RTC peripherals will be kept powered on during deep sleep if this wake-up source is requested.
Internal pullup or pulldown resistors can also be used because the RTC IO module is enabled in this mode. They need to be configured by the application using rtc_gpio_pullup_en
and rtc_gpio_pulldown_en
functions, before calling esp_deep_sleep_start
.
In revisions 0 and 1 of the ESP32, this wake-up source is incompatible with ULP and touch wake-up sources.
Warning: after waking up from deep sleep, the IO pin used for wake-up will be configured as RTC IO. Before using this pin as a digital GPIO, reconfigure it using rtc_gpio_deinit(gpio_num)
the function.
The command to activate the wake up from a GPIO is
esp_err_t esp_deep_sleep_enable_ext0_wakeup(gpio_num_t gpio_num, int level)
This function uses the external wake-up feature of the RTC_IO peripheral. It will work only if RTC peripherals are kept on during deep sleep.
This feature can monitor any pin which is an RTC IO. Once the pin transitions into the state given by level the the
argument, It will wake the chip up.
Return
ESP_OK
on successESP_ERR_INVALID_ARG
if the selected GPIO is not an RTC GPIO, or the mode is invalidESP_ERR_INVALID_STATE
if wakeup triggers conflict
For more information on the device, refer to “DOIT ESP32 DEV KIT v1 high-resolution pinout and specs“.
Parameters
- gpio_num: GPIO number used as wakeup source. Only GPIOs which are have RTC functionality can be used: 0,2,4,12-15,25-27,32-39.
- level: input level which will trigger wakeup (0=low, 1=high)
Here is a basic example sketch:
/*
* ESP32
* DEEP Sleep and external wake up (ext0) on GPIO4
* by Mischianti Renzo <https://mischianti.org>
*
* https://mischianti.org/esp32-practical-power-saving-preserve-gpio-status-external-and-ulp-wake-up-5/
*
*/
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 : Serial2.println("Wakeup caused by external signal using RTC_IO"); break;
case ESP_SLEEP_WAKEUP_EXT1 : Serial2.println("Wakeup caused by external signal using RTC_CNTL"); break;
case ESP_SLEEP_WAKEUP_TIMER : Serial2.println("Wakeup caused by timer"); break;
case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial2.println("Wakeup caused by touchpad"); break;
case ESP_SLEEP_WAKEUP_ULP : Serial2.println("Wakeup caused by ULP program"); break;
default : Serial2.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
}
}
void setup(){
Serial2.begin(115200);
delay(1000); //Take some time to open up the Serial Monitor
//Increment boot number and print it every reboot
++bootCount;
Serial2.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.
Note that using internal pullups/pulldowns also requires
RTC peripherals to be turned on.
*/
esp_sleep_enable_ext0_wakeup(GPIO_NUM_4,HIGH);
//Go to sleep now
Serial2.println("Going to sleep now");
esp_deep_sleep_start();
Serial2.println("This will never be printed");
}
void loop(){
//This is not going to be called
}
Preserve GPIOs status
It’s also possible to preserve the status of GPIO, which is very useful to preserve open a MOSFET or set the status to the external device.
if (ESP_SLEEP_WAKEUP_EXT0 == wakeup_reason) {
Serial.println("Waked up from external GPIO!");
gpio_hold_dis(GPIO_NUM_21);
gpio_hold_dis(GPIO_NUM_19);
gpio_deep_sleep_hold_dis();
}else{
delay(1000);
Serial.println();
Serial.println("Start sleep!");
delay(100);
if (ESP_OK == gpio_hold_en(GPIO_NUM_21)){
Serial.println("HOLD 21");
}else{
Serial.println("NO HOLD 21");
}
if (ESP_OK == gpio_hold_en(GPIO_NUM_19)){
Serial.println("HOLD 19");
}else{
Serial.println("NO HOLD 19");
}
esp_sleep_enable_ext0_wakeup(GPIO_NUM_15,LOW);
gpio_deep_sleep_hold_en();
//Go to sleep now
Serial.println("Going to sleep now");
esp_deep_sleep_start();
delay(1);
}
As you can see, you can force the value of GPIO with gpio_hold_en
and you need to call gpio_hold_dis
to allow it to be changed again.
To enable value preservation after the wake from a deep sleep, you must add gpio_deep_sleep_hold_en()
a function to your code.
External wake up (ext1), management of multiple GPIOs
The RTC controller contains the logic for waking up using multiple RTC GPIOs. One of the two logic states can be used to trigger wake-up:
- wake up if any of the selected pins is high (
ESP_EXT1_WAKEUP_ANY_HIGH
) - wake up if all the selected pins are low (
ESP_EXT1_WAKEUP_ALL_LOW
)
The RTC controller implements this wake-up source. RTC peripherals and RTC memories can be powered down in this mode. However, if RTC peripherals are powered down, internal pullup and pulldown resistors will be disabled.
esp_sleep_enable_ext1_wakeup()
the function can be used to enable this wake-up source.
To set the bitmask, you can use this command:
esp_err_t esp_sleep_enable_ext1_wakeup(uint64_t mask, esp_sleep_ext1_wakeup_mode_t mode);
This feature can monitor any number of pins that are in RTC IOs. Once any selected pins go into the state given by the mode argument, the chip will be woken up.
Return
ESP_OK
on successESP_ERR_INVALID_ARG
if any of the selected GPIOs is not an RTC GPIO, or mode is invalid
Parameters
mask
: bit mask of GPIO numbers which will cause wakeup. Only GPIOs which are have RTC functionality can be used in this bit map: 0,2,4,12-15,25-27,32-39. I create a bitmask generator so simplify the magement.
There is a problem with GPIOs from 32 to 39, that are not identify from status and can’t be retrieved.mode
: select logic function used to determine wakeup condition:ESP_EXT1_WAKEUP_ALL_LOW
: wake up when all selected GPIOs are lowESP_EXT1_WAKEUP_ANY_HIGH
: wake up when any of the selected GPIOs is high
GPIO Bitmask |
---|
You can retrieve which pin is moved with esp_sleep_get_ext1_wakeup_status() that return the bitmask that had to generate the wake-up, return 0 identify that the source isn’t ext1. Many people have a problem with pins from 32 to 39 that return 0, so pay attention.
/*
Method to print the GPIO by which ESP32
has been awaken from sleep
*/
void print_wakeup_GPIO(){
GPIObitmask = esp_sleep_get_ext1_wakeup_status();
Serial2.print(GPIObitmask);
Serial2.print(" Wake UP GPIO: GPIO ");
Serial2.println((log(GPIObitmask))/log(2), 0);
}
Here is the complete sketch:
/*
* ESP32
* DEEP Sleep and external wake up (ext1) with bitmask
* set on GPIO2 GPIO4 GPIO12
* by Mischianti Renzo <https://mischianti.org>
*
* https://mischianti.org/esp32-practical-power-saving-preserve-gpio-status-external-and-ulp-wake-up-5/
*
*/
// Refer to my GPIO bitmask generator on relativa rticle
#define BUTTON_PIN_BITMASK 0x1014 // 2^2 + 2^4 + 2^12 in hex
RTC_DATA_ATTR int bootCount = 0;
int GPIObitmask;
/*
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 : Serial2.println("Wakeup caused by external signal using RTC_IO"); break;
case ESP_SLEEP_WAKEUP_EXT1 : Serial2.println("Wakeup caused by external signal using RTC_CNTL"); break;
case ESP_SLEEP_WAKEUP_TIMER : Serial2.println("Wakeup caused by timer"); break;
case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial2.println("Wakeup caused by touchpad"); break;
case ESP_SLEEP_WAKEUP_ULP : Serial2.println("Wakeup caused by ULP program"); break;
default : Serial2.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
}
}
/*
Method to print the GPIO by which ESP32
has been awaken from sleep
*/
void print_wakeup_GPIO(){
GPIObitmask = esp_sleep_get_ext1_wakeup_status();
Serial2.print(GPIObitmask);
Serial2.print(" Wake UP GPIO: GPIO ");
Serial2.println((log(GPIObitmask))/log(2), 0);
}
void setup(){
Serial2.begin(115200);
delay(1000); //Take some time to open up the Serial Monitor
//Increment boot number and print it every reboot
++bootCount;
Serial2.println("Boot number: " + String(bootCount));
//Print the wakeup reason for ESP32
print_wakeup_reason();
//Print the wakeup GPIO for ESP32
print_wakeup_GPIO();
//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
Serial2.println("Going to sleep now");
esp_deep_sleep_start();
Serial2.println("This will never be printed");
}
void loop(){
//This is not going to be called
}
ULP Wake UP
ULP coprocessor is part of the RTC peripherals power domain, and it runs the program stored in RTC slow memory. ULP coprocessor can run while the chip is in sleep mode and may be used to poll sensors, monitor ADC or touch sensor values, and wake up the chip when a specific event is detected. RTC slow memory will be powered on during sleep if this wake-up mode is requested. RTC peripherals will be automatically powered on before the ULP coprocessor starts running the program; once the program stops running, RTC peripherals are automatically powered down again.
esp_sleep_enable_ulp_wakeup()
the function is used to enable this wake-up source.
You can upload the ULP program with these commands:
void init_ulp_program() {
const ulp_insn_t program[] = {
// initiate wakeup of the SoC
I_WAKE(),
// stop the ULP program
I_HALT()
};
size_t load_addr = 0;
size_t size = sizeof(program)/sizeof(ulp_insn_t);
ulp_process_macros_and_load(load_addr, program, &size);
ulp_run(0);
}
The command to set wake up period of ULP is:
esp_err_t ulp_set_wakeup_period(size_t period_index, uint32_t period_us);
Params:period_index
index of the alarm period setting (0 – 4)period_us a
wake-up period in µs
Return ESP_OK
on successESP_ERR_INVALID_ARG
if period_index is out of range
The ULP command to wake the esp32 are:
// initiate wakeup of the SoC
I_WAKE(),
// stop the ULP program
I_HALT()
Here is the complete sketch:
/*
* ESP32
* DEEP Sleep and ULP wake up
* by Mischianti Renzo <https://mischianti.org>
*
* https://mischianti.org/esp32-practical-power-saving-preserve-gpio-status-external-and-ulp-wake-up-5/
*
*/
#include "esp32/ulp.h"
#include "soc/rtc_cntl_reg.h"
#include "driver/rtc_io.h"
void init_ulp_program();
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 : Serial2.println("Wakeup caused by external signal using RTC_IO"); break;
case ESP_SLEEP_WAKEUP_EXT1 : Serial2.println("Wakeup caused by external signal using RTC_CNTL"); break;
case ESP_SLEEP_WAKEUP_TIMER : Serial2.println("Wakeup caused by timer"); break;
case ESP_SLEEP_WAKEUP_TOUCHPAD : Serial2.println("Wakeup caused by touchpad"); break;
case ESP_SLEEP_WAKEUP_ULP : Serial2.println("Wakeup caused by ULP program"); break;
default : Serial2.printf("Wakeup was not caused by deep sleep: %d\n",wakeup_reason); break;
}
}
void setup() {
Serial2.begin(115200);
while( !Serial ){}
Serial2.println("Init");
//Increment boot number and print it every reboot
++bootCount;
Serial2.println("Boot number: " + String(bootCount));
//Print the wakeup reason for ESP32
print_wakeup_reason();
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
if (cause != ESP_SLEEP_WAKEUP_ULP) {
Serial2.println("Initializing ULP");
init_ulp_program();
/* Set ULP wake up period to 5s */
ulp_set_wakeup_period(0, 5 * 1000 * 1000);
}
Serial2.println("Entering deep sleep\n");
ESP_ERROR_CHECK( esp_sleep_enable_ulp_wakeup() );
esp_deep_sleep_start();
}
void loop(){
}
void init_ulp_program() {
const ulp_insn_t program[] = {
// initiate wakeup of the SoC
I_WAKE(),
// stop the ULP program
I_HALT()
};
size_t load_addr = 0;
size_t size = sizeof(program)/sizeof(ulp_insn_t);
ulp_process_macros_and_load(load_addr, program, &size);
ulp_run(0);
}
The Serial2 output is
Init
Boot number: 1
Wakeup was not caused by deep sleep: 0
Entering deep sleep
Init
Boot number: 2
Wakeup caused by ULP program
ULP wakeup
Entering deep sleep
Init
Boot number: 3
Wakeup caused by ULP program
ULP wakeup
Entering deep sleep
Thanks
- ESP32: pinout, specs and Arduino IDE configuration
- ESP32: integrated SPIFFS Filesystem
- ESP32: manage multiple Serial and logging
- ESP32 practical power saving
- ESP32 practical power saving: manage WiFi and CPU
- ESP32 practical power saving: modem and light sleep
- ESP32 practical power saving: deep sleep and hibernation
- ESP32 practical power saving: preserve data, timer and touch wake up
- ESP32 practical power saving: external and ULP wake up
- ESP32 practical power saving: UART and GPIO wake up
- ESP32: integrated LittleFS FileSystem
- ESP32: integrated FFat (Fat/exFAT) FileSystem
- ESP32-wroom-32
- ESP32-CAM
- ESP32: use ethernet w5500 with plain (HTTP) and SSL (HTTPS)
- ESP32: use ethernet enc28j60 with plain (HTTP) and SSL (HTTPS)
- How to use SD card with esp32
- esp32 and esp8266: FAT filesystem on external SPI flash memory
- Firmware and OTA update management
- Firmware management
- OTA update with Arduino IDE
- OTA update with Web Browser
- Self OTA uptate from HTTP server
- Non-standard Firmware update
- Integrating LAN8720 with ESP32 for Ethernet Connectivity with plain (HTTP) and SSL (HTTPS)
- Connecting the EByte E70 to ESP32 c3/s3 devices and a simple sketch example
- ESP32-C3: pinout, specs and Arduino IDE configuration
- Integrating W5500 with ESP32 Using Core 3: Native Ethernet Protocol Support with SSL and Other Features
- Integrating LAN8720 with ESP32 Using Core 3: Native Ethernet Protocol Support with SSL and Other Features