Introduction: Saving Battery Life With Deep Sleep
Are you interested in using a battery with your ESP32? If so, I’ll be discussing today some important technical information concerning this subject. We know that this microcontroller spends a lot of energy when it transmits information. It consumes close to 190 milliamps. In this video, I’ll show how to conserve energy from the ESP32 with the so-called "DEEP SLEEP" function. We’ll set the chip to enter this mode, learn the ways to exit this mode, and create an example showing three different ways to wake the ESP32.
It’s important to remember that the radio spends a lot of energy, rather than the processer. Energy saving is very important. This is because Endpoints (the circuits that are sending information) are often battery-powered and should last up to five years. There are some manufacturers that promise up to ten years in duration, and this is valid for high quality batteries that don’t use the Endpoints as much. In all other cases, I advise you to use Deep Sleep to save energy from your circuit.
Step 1: Introduction
The ESP32 has a power-saving mode, called “Deep Sleep.” In this mode, CPUs, most RAM, and all digital clocked peripherals are turned off. The only parts of the chip that can still be connected are the RTC controller, RTC peripherals (including ULP coprocessor), and RTC memories.
We have several ways to wake up the ESP32 when asleep. Wake up sources can be set up at any time before entering the Deep Sleep mode.
Step 2: Ways to Wake the ESP32
There are five ways to wake ESP32:
• Timer
• External wakeup (ext0)
• External wakeup (ext1)
• ULP coprocessor wakeup
• Touchpad
Step 3: Timer
The RTC controller has a built-in timer that can be used to activate the chip after a predefined period of time. The time is specified with microsecond precision.
esp_deep_sleep_enable_timer_wakeup( uint64_t time_in_us )
time_in_us> is the time in microseconds
Step 4: External Wakeup (ext0)
The RTC IO module contains logic to trigger the alarm when one of the RTC GPIOs enters a predefined logic level. The RTC IO is part of the power domain of the RTC peripherals, so the RTC peripherals will be kept alive during Deep Sleep if this source of activation is requested.
esp_deep_sleep_enable_ext0_wakeup( gpio_num_t gpio_num , int level)
gpio_num> GPIO number used as activation source. Only RTC-functional GPIOs can be used: 0,2,4,12-15,25-27,32-39.
level> input level that will trigger the alarm (0 = LOW, 1 = HIGH)
Step 5: External Wakeup (ext1)
The RTC controller contains logic to trigger the alarm clock using multiple RTC GPIOs.
esp_deep_sleep_enable_ext1_wakeup(uint64_t mask, esp_ext1_wakeup_mode_t mode)
mask> bit mask of GPIO numbers that will cause activation. Only RTC-enabled GPIOs can be used in this bitmap: 0,2,4,12-15,25-27,32-39.
mode> select the logic function used to determine the activation condition:
• ESP_EXT1_WAKEUP_ALL_LOW: awakens when all selected GPIOs are in LOW
• ESP_EXT1_WAKEUP_ANY_HIGH: awakens when any of the selected GPIOs are HIGH
Step 6: ULP Coprocessor Wakeup
The ULP coprocessor can operate while the chip is in Deep Sleep and can be used to search sensors, monitor ADC or capacitive touch sensor values, and activate the chip when a specific event is detected.
The ULP coprocessor is part of the power domain of the RTC peripherals and runs the program stored in slow memory RTC. Therefore, RTC peripherals and RTC slow memory will be activated during Deep Sleep if this activation mode is requested.
Step 7: Touchpad
The RTC controller contains logic to trigger the alarm using the capacitive touch sensors. The definition of the touch pin, however, is different. We must use the touch interrupt for each of the desired pins.
After setting the interrupts, we enabled the wake-up mode to use the sensors.
//Configure Touchpad as wakeup source
esp_sleep_enable_touchpad_wakeup();
Step 8: Entering Deep Sleep Mode
After setting a wake-up mode, a single command to put the ESP32 in Deep Sleep mode (spending 2.5 μA or less) is enough. I emphasize here that this expense is from the ESP chip and not the plate, as the latter spends more.
esp_deep_sleep_start();
From this command, the ESP32 falls asleep and does not execute the next lines of code for example.
Important: All wake-up settings must be made before executing the above command.
Step 9: Here Is Some More Important Information
The call below returns the cause of ESP32 wakeup.
1: EXT0 2: EXT1 3: TIMER 4: TOUCHPAD 5: ULP
esp_sleep_get_wakeup_cause();
If we set the wake up by the touchpad, we can recover which GPIO that the touch occurred through the command
esp_sleep_get_touchpad_wakeup_status();
Every time the ESP32 wakes up,
it will cycle through the setup again. Thus all variables that are not defined in the RTC memory will return to their home state.
To keep the variables in memory even after falling asleep, use the variable declaration in the example below:
//RTC_DATA_ATTR aloca a variável na memória RTC
RTC_DATA_ATTR int bootCount = 0;
Step 10: Demonstration
The video shows the program working, in accordance with the image.
Step 11: WiFi NodeMCU-32S ESP-WROOM-32
Step 12: Assembly
Step 13: Program
We’ll now make a program where we’ll configure the ESP32 to enter Deep Sleep mode. This will be awakened in three different ways: one for External wakeup (ext0), one for Timer, and one for Touchpad. They cannot work together, so we'll use a variable that will be a counter for the number of times the ESP32 gave Boot to configure the way to wake up.
Step 14: Library Required
To control the OLED display, we need an external library. For this, we’ll download the U8g2 library.
In the Arduino IDE, go to the Sketch menu >> Include Library >> Manage Libraries ....
Step 15: Libraries and Variables
We have included the library to control the OLED display, as well as a constructor of the display controller instance. Also, we allocate the variable in the RTC memory. We set the sensitivity for touch acceptance, the microsecond conversion factor for seconds, and the time the ESP32 goes to sleep mode (in seconds).
#include <U8x8lib.h>//biblioteca para controle do display oled
//construtor da instancia do controlador do display //SDA = 21 e SCL = 22 U8X8_SSD1306_128X64_NONAME_SW_I2C display(SCL, SDA, U8X8_PIN_NONE); //RTC_DATA_ATTR aloca a variável na memoria RTC RTC_DATA_ATTR int bootCount = 0; //sensibilidade para aceitação do toque #define Threshold 40 //fator de conversão de microsegundos para segundos #define uS_TO_S_FACTOR 1000000 //tempo que o ESP32 ficará em modo sleep (em segundos) #define TIME_TO_SLEEP 3
Step 16: Setup
In Setup, we increment the number of times Boot occurred. We call the function to print the Boot motif. If the Boot number is PAR, we set the ESP32 to wake up via the (EXT0) button. If it is a multiple of 3, we set the ESP32 to wake up after a set time. Otherwise, we set up the capacitive touch pins to wake up the ESP32. Finally, we set the Touchpad as the wakeup source and force the ESP32 to enter sleep mode.
void setup() {
Serial.begin(115200); delay(1000); //incrementa o numero de vezes que o BOOT ocorreu ++bootCount; configureDisplay(); //chama a função para imprimir o motivo do BOOT print_wakeup_reason(); //se o numero de boot for PAR configuramos o ESP32 para despertar através do botão (EXT0) if(bootCount % 2 == 0) { esp_sleep_enable_ext0_wakeup(GPIO_NUM_39,1); //1 = High, 0 = Low } //se for multiplo de 3 configuramos o ESP32 para despertar depois de um tempo definido else if(bootCount % 3 == 0) { esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR); } //caso contrario configuramos os pinos de touch capacitivo para despertar o ESP32 else { //Setup interrupt on Touch Pad 5 (GPIO12) touchAttachInterrupt(T5, callback, Threshold); //Configure Touchpad as wakeup source esp_sleep_enable_touchpad_wakeup(); } Serial.println(“entrando em modo sleep"); esp_deep_sleep_start(); //força o ESP32 entrar em modo SLEEP }
Step 17: Loop, Callback & ConfigureDisplay
In the Loop, we have nothing to do. We then proceed to interrupt callback if we have something to do when the interruption occurs. Regarding configureDisplay, we initialize the display and configure some parameters. We print on the screen the number of times the Boot happened.
//nada a se fazer no loop
void loop() { } //callback das interrupções void callback(){ //caso queira fazer algo ao ocorrer a interrupção } void configureDisplay() { //inicializa o display e configura alguns parametros display.begin(); display.setPowerSave(0); //modo powerSave (0-Off ? 1-On) display.setFont(u8x8_font_torussansbold8_u); //fonte utilizada //imprime no display os numero de vezes que aconteceu o BOOT display.drawString(0,0, "BOOT NUM:"); display.drawString(0,2,String(bootCount).c_str()); display.drawString(0,4, "MOTIVO:"); }
Step 18: Print_wakeup_reason (knowing the Cause of the Awakening)
Here, we have the function to print the cause of the ESP32 wake up. Check the pin and print on the display.
//função para imprimir a causa do ESP32 despertar
void print_wakeup_reason( ){ esp_sleep_wakeup_cause_t wakeup_reason; String reason = ""; wakeup_reason = esp_sleep_get_wakeup_cause(); //recupera a causa do despertar switch(wakeup_reason) { case 1 :reason = "EXT0 RTC_IO BTN"; break; case 2 :reason = "EXT1 RTC_CNTL"; break; case 3 :reason = "TIMER"; break; case 4 :reason = "TOUCHPAD"; break; case 5 :reason = "ULP PROGRAM"; break; default :reason = "NO DS CAUSE"; break; } Serial.println(reason); display.clearLine(6); //apaga a linha 6 do display display.drawString(0,6, reason.c_str()); //imprime a causa do despertar no display //se despertou por TOUCHPAD, então vamos verificar em qual dos pinos ocorreu if(wakeup_reason == 4) { print_wakeup_touchpad(); //verifica o pino e imprime no display } }
Step 19: Print_wakeup_touchpad (know the GPIO Touch)
Now, in this step, we have the function to print the pin that was touched. We recovered the GPIO that awoke the ESP32 and printed it on the display.
//função para imprimir o pino que foi tocado
void print_wakeup_touchpad() { touch_pad_t touchPin; touchPin = esp_sleep_get_touchpad_wakeup_status(); //recupera o GPIO que despertou o ESP32 String GPIO = ""; switch(touchPin) { case 0 : GPIO = "4"; break; case 1 : GPIO = "0"; break; case 2 : GPIO = "2"; break; case 3 : GPIO = "15"; break; case 4 : GPIO = "13"; break; case 5 : GPIO = "12"; break; case 6 : GPIO = "14"; break; case 7 : GPIO = "27"; break; case 8 : GPIO = "33"; break; case 9 : GPIO = "32"; break; default : Serial.println("Wakeup not by touchpad"); break; } Serial.println("GPIO: "+GPIO); display.clearLine(7);//apaga a linha 7 do display display.drawString(0,7, "GPIO: "); display.drawString(6,7, GPIO.c_str()); //imprime o GPIO }