ESP32 power saving: modem and light sleep – 2
ESP32 can activate light sleep and deep sleep modes with a specific command, but there is another modem sleep mode; this is not a well-defined suspend like the others. It may be necessary to perform more than one operation to implement it.
So remember that modem sleep mode is virtual, not executed with a specific command, and to understand better, it is helpful to read the previous chapter.
The light sleep mode is, on the other hand, well defined, and there is a specific command to activate it. With this mode, the digital devices, most of the RAM, and the CPUs have a reduced clock frequency, and also the voltage is reduced. Upon exiting light sleep mode, peripherals and CPUs resume functioning, preserving their internal state.
In this table, taken from the esp32 datasheet, there is information on how to group sleep modes.
Power mode | Description | Power consumption | ||
Active (RF working) |
Wi-Fi Tx packet |
78 mA ~ 90 mA without communication For TX RX more info in the next table |
||
Wi-Fi/BT Tx packet | ||||
Wi-Fi/BT Rx and listening | ||||
Modem-sleep |
The CPU is powered on. |
240 MHz * | Dual-core chip(s) | 30 mA ~ 68 mA |
Single-core chip(s) | N/A | |||
160 MHz * | Dual-core chip(s) | 27 mA ~ 44 mA | ||
Single-core chip(s) | 27 mA ~ 34 mA | |||
Normal speed: 80 MHz | Dual-core chip(s) | 20 mA ~ 31 mA | ||
Single-core chip(s) | 20 mA ~ 25 mA | |||
Light-sleep | – | 0.8 mA | ||
Deep-sleep |
The ULP co-processor is powered on. | 150 µA 100 µA @1% duty 10 µA | ||
ULP sensor-monitored pattern | ||||
RTC timer + RTC memory | ||||
Hibernation | RTC timer only | 5 µA | ||
Power off | CHIP_PU is set to low level, the chip is powered off. | 1 µA |
How to measure ampere
Now we will check how much power consumes the ESP32 in many situations and configurations. Here is the connection schema we used.
Power the esp32 to the 5v pin and GND pin with an external power supply and disconnect the USB. To log, we use the Serial2 port, but if you want to use the Serial port, you must only move the FTDI converter to the TX pin instead TX2 pin. You can’t use USB because it powers the device, and the multimeter gets the wrong amperage.
Modem sleep
If WiFi connection needs to be maintained, enable WiFi modem sleep, and enable automatic light sleep feature. This will allow the system to wake up from sleep automatically when required by WiFi driver, thereby maintaining connection to the AP.
From Espressif documentation
The modem sleep mode is quite ambiguous. Some people/blogs claim it’s WiFi sleep mode (and Espressif confirms it), others that it’s when all radios (WiFi and Bluetooth) are disabled.
Real
So if we are going to read the Espressif documentation, the real modem sleep It’s the regular operation of WiFi (check the previous chapter), here is a code example:
/*
* ESP32
* REAL Modem Sleep and wake up
* by Mischianti Renzo <https://mischianti.org>
*
* https://mischianti.org/category/tutorial/esp32-tutorial/esp32-practical-power-saving/
*
*/
#include <WiFi.h>
#include <BluetoothSerial.h>
#include "driver/adc.h"
#include <esp_bt.h>
#define STA_SSID "<YOUR-SSID>"
#define STA_PASS "<YOUR-PASSWD>"
BluetoothSerial SerialBT;
void setModemSleep();
void wakeModemSleep();
void setup() {
Serial2.begin(115200);
while(!Serial2){delay(500);}
SerialBT.begin("ESP32test"); //Bluetooth device name
SerialBT.println("START BT");
Serial2.println("START WIFI");
WiFi.begin(STA_SSID, STA_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial2.print(".");
}
Serial2.println("");
Serial2.println("WiFi connected");
Serial2.println("IP address: ");
Serial2.println(WiFi.localIP());
setModemSleep();
Serial2.println("MODEM SLEEP ENABLED FOR 5secs");
}
//void loop() {}
unsigned long startLoop = millis();
bool started = false;
void loop() {
if (!started && startLoop+5000<millis()){
// Not use delay It has the own policy
wakeModemSleep();
Serial2.println("MODEM SLEEP DISABLED");
started = true;
}
}
void setModemSleep() {
WiFi.setSleep(true);
if (!setCpuFrequencyMhz(40)){
Serial2.println("Not valid frequency!");
}
// Use this if 40Mhz is not supported
// setCpuFrequencyMhz(80);
}
void wakeModemSleep() {
setCpuFrequencyMhz(240);
}
And result in power consumption of 40mA ~ 68mA, the minimum when automatic sleep has activated the max when receiving the beacon message.
Fake
Here is the most common (fake) interpretation of the meaning of the term modem sleep.
If you have already read the first chapter, you know that you can disable WiFi with these commands
void disableWiFi(){
adc_power_off();
WiFi.disconnect(true); // Disconnect from the network
WiFi.mode(WIFI_OFF); // Switch WiFi off
}
To reduce consumption, you can also disable other units in the Radio group like the BlueTooth with this command
void disableBluetooth(){
btStop();
}
But as we have seen, the only advantage of turning off WiFi is that it is susceptible to beacon messages from the network because the default is a WiFi sleep mode which puts the WiFi into automatic sleep mode for inactivity and is equivalent to disabling the modem.
Then, you must pay attention to the datasheet table that you can obtain better modem sleep by modifying the CPU frequencies (view the previous chapter), reducing the MHz of the CPU
setCpuFrequencyMhz(40);
So now we know what we must do to go on Modem Sleep:
- Disable WiFi;
- Disable Bluetooth;
- Reduce CPU frequency.
The resulting code is
/*
* ESP32
* Modem Sleep and wake up
* by Mischianti Renzo <https://mischianti.org>
*
* https://mischianti.org/category/tutorial/esp32-tutorial/esp32-practical-power-saving/
*
*/
#include <WiFi.h>
#include <BluetoothSerial.h>
#include "driver/adc.h"
#include <esp_bt.h>
#define STA_SSID "<YOUR-SSID>"
#define STA_PASS "<YOUR-PASSWD>"
BluetoothSerial SerialBT;
void setModemSleep();
void wakeModemSleep();
void setup() {
Serial2.begin(115200);
while(!Serial2){delay(500);}
SerialBT.begin("ESP32test"); //Bluetooth device name
SerialBT.println("START BT");
Serial2.println("START WIFI");
WiFi.begin(STA_SSID, STA_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial2.print(".");
}
Serial2.println("");
Serial2.println("WiFi connected");
Serial2.println("IP address: ");
Serial2.println(WiFi.localIP());
setModemSleep();
Serial2.println("MODEM SLEEP ENABLED FOR 5secs");
}
//void loop() {}
unsigned long startLoop = millis();
bool started = false;
void loop() {
if (!started && startLoop+5000<millis()){
// Not use delay It has the own policy
wakeModemSleep();
Serial2.println("MODEM SLEEP DISABLED");
started = true;
}
}
void disableWiFi(){
adc_power_off();
WiFi.disconnect(true); // Disconnect from the network
WiFi.mode(WIFI_OFF); // Switch WiFi off
Serial2.println("");
Serial2.println("WiFi disconnected!");
}
void disableBluetooth(){
// Quite unusefully, no relevable power consumption
btStop();
Serial2.println("");
Serial2.println("Bluetooth stop!");
}
void setModemSleep() {
disableWiFi();
disableBluetooth();
setCpuFrequencyMhz(40);
// Use this if 40Mhz is not supported
// setCpuFrequencyMhz(80);
}
void enableWiFi(){
adc_power_on();
delay(200);
WiFi.disconnect(false); // Reconnect the network
WiFi.mode(WIFI_STA); // Switch WiFi off
delay(200);
Serial2.println("START WIFI");
WiFi.begin(STA_SSID, STA_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial2.print(".");
}
Serial2.println("");
Serial2.println("WiFi connected");
Serial2.println("IP address: ");
Serial2.println(WiFi.localIP());
}
void wakeModemSleep() {
setCpuFrequencyMhz(240);
enableWiFi();
}
With Modem Sleep, you can obtain a considerable power reduction, and you can reach 20mA and lesser with a 40Mhz (check the previous chapter).
WeMos LOLIN32 and TTGO testing
I want to use other microcontrollers for the next test because manufacturers often create these boards without thinking about energy saving. Therefore we must always check if these devices are suitable for production or are just testing.
And we need to pay attention to microcontrollers with built-in battery management because they can be satisfying when battery powered. Manufacturers manage functional units to get better battery performance, but let’s check the difference with the multimeter.
Light sleep
Light sleep is different from modem sleep because you are about to pause the CPU, so you have to manage that state with a particular command, and you must first give a system wake-up mode.
Light sleep can fail if there are some active connections (WiFi or Bluetooth), so you probably must disable WiFi and Bluetooth to ensure they do not fail.
In detail, in light sleep mode digital peripherals, most of the RAM and CPUs have a reduced clock frequency, and also the voltage is reduced. Upon exiting soft sleep mode, peripherals and CPUs resume functioning, preserving their internal state.
If you do not select a peripheral as the wake-up mode
Please pay attention to these two sketches; the first seems similar to the second, but there is a big difference in power consumption, and now we will explain better.
DOIT esp32 DEV KIT v1
Now we are going to put in light sleep a DOIT esp32 DEV KIT v1 with this sketch:
/*
* ESP32
* Light Sleep and wake up
* by Mischianti Renzo <https://mischianti.org>
*
* https://mischianti.org/category/tutorial/esp32-tutorial/esp32-practical-power-saving/
*
*/
#include <WiFi.h>
#include <BluetoothSerial.h>
#include "driver/adc.h"
#include <esp_bt.h>
#include <esp_wifi.h>
#include <esp_sleep.h>
#define STA_SSID "<YOUR-SSID>"
#define STA_PASS "<YOUR-PASSWD>"
BluetoothSerial SerialBT;
int variable = 0;
void setup() {
Serial2.begin(115200);
while(!Serial2){delay(500);}
SerialBT.begin("ESP32test"); //Bluetooth device name
SerialBT.println("START BT");
Serial2.println("START WIFI");
WiFi.begin(STA_SSID, STA_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial2.print(".");
}
Serial2.println("");
Serial2.println("WiFi connected");
Serial2.println("IP address: ");
Serial2.println(WiFi.localIP());
delay(1000);
variable += 10;
Serial2.println();
Serial2.println("LIGHT SLEEP ENABLED FOR 5secs");
delay(100);
esp_sleep_enable_timer_wakeup(5 * 1000 * 1000);
esp_light_sleep_start();
Serial2.println();
Serial2.println("LIGHT SLEEP WAKE UP");
Serial2.print("Variable = ");
Serial2.println(variable);
}
void loop() {
}
void disableWiFi(){
adc_power_off();
WiFi.disconnect(true); // Disconnect from the network
WiFi.mode(WIFI_OFF); // Switch WiFi off
Serial2.println("");
Serial2.println("WiFi disconnected!");
}
void enableWiFi(){
adc_power_on();
WiFi.disconnect(false); // Reconnect the network
WiFi.mode(WIFI_STA); // Switch WiFi off
Serial2.println("START WIFI");
WiFi.begin(STA_SSID, STA_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial2.print(".");
}
Serial2.println("");
Serial2.println("WiFi connected");
Serial2.println("IP address: ");
Serial2.println(WiFi.localIP());
}
void disableBluetooth(){
btStop();
esp_bt_controller_disable();
delay(1000);
Serial2.println("BT STOP");
}
DOIT DEV KIT v1
But the result is not so good; this device in light sleep consumes 4.89mA, very far from the 0.8 mA declared from Espressif.
As you can see, the variable’s value is ten that was set before entering the light sleep. These are the most critical features of light sleep, the program is suspended, and the state is restored.
At first, I was depressed, but then I reflected on the fact that that figure was related to the esp32 chip, and that’s it.
But in a dev board, there are a lot of other components that can change the result. Think of LEDs, voltage regulators, etc.
So I try to do the same test with two famous boards vendors that have devices with battery management and other features, not only for dev.
TTGO T8
First, I want to try a TTGO, one of the most famous productor of this type of device, with many variants; I use one of the better of Its products, the TTGO T8.
The power source at 5v from the specified pin gives a lousy result of 8.24mA. Still, the board, when powered only with the battery, excludes some functional units like a led voltage regulator, etc., so the same test with battery power reach 3.73mA, relatively better than the first result and much better than the DOIT DEV KIT v1.
WeMos LOLIN32
Then the WeMos LOLIN32, I love WeMos as a manufacturer; I think It has a good set of devices and is very versatile with different sizes.
The power source at 5v from the specified pin gives a relatively better result, 2.60mA. Still, when powered only with the battery, the board reaches 1.52mA, which is a satisfactory result.
Result
The data is obvious, the DOIT DEV KIT v1 is the loser, and the LOLIN32 is the winner; TTGO has the highest price, I don’t think it’s satisfactory.
Device | Mode | Power |
---|---|---|
DOIT DEV KIT v1 | Power to VIN pin | 4.98mA |
TTGO T8 | Power to 5V pin | 8.24mA |
Power via Battery | 3.73mA | |
WeMos LOLIN32 | Power via 5V pin | 2.60mA |
Power via Battery | 1.52mA |
RTC and other wake up source
But we didn’t use RTC as a wake-up source, and if you pay attention to the schematic, RTC has a different position in the functional modules.
But other wake-up sources (such as the external one) are located in different functional blocks. So what happens if we use a wake source like RTC block?
We will use the previous sketch, but we will add an external wake-up source.
esp_sleep_enable_timer_wakeup(5 * 1000 * 1000);
esp_sleep_enable_ext0_wakeup(GPIO_NUM_33,1);
esp_light_sleep_start();
and here is the result
Device | Mode | Power |
---|---|---|
DOIT DEV KIT v1 | Power to VIN pin | 6.85mA (+1,87mA) |
TTGO T8 | Power to 5V pin | 10.05mA (+1,81mA) |
Power via Battery | 5.04mA (+1,31mA) | |
WeMos LOLIN32 | Power via 5V pin | 4.35mA (+1,75mA) |
Power via Battery | 3.54mA (+2,02mA) |
RTC IO module contains logic to trigger wakeup when one of RTC GPIOs is set to a predefined logic level. RTC IO is part of RTC peripherals power domain, so RTC peripherals will be kept powered on during deep sleep if this wakeup source is requested.
From Espressif documentation
Please pay attention to these words as they will explain the difference between hibernation and deep sleep.
Thanks
In this article, we learn that different devices have different results, so when you buy a device, remember to check the quality.
- 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
Hi!! Thanks for this article it is very usefull!!
I have a problem, I create a sketch using the code you proposed as the “Real modem sleep”.
I am a new user for the ESP32.
Everything compiles perfectly in arduino IDE 2.2.1 but the executable is larger thatn the available memory.
I receive this error:
“Sketch uses 1452793 bytes (110%) of program storage space. Maximum is 1310720 bytes.
Global variables use 55968 bytes (17%) of dynamic memory, leaving 271712 bytes for local variables. Maximum is 327680 bytes.
text section exceeds available space in board”
as the partition scheme I am using the default 4MBytes with spiffs 1.2MB APPs/1.5MB SPIFFS).
I have also tried other partition scheme but without success. With partition scheme different than the default the ESP32 is continously rebooting.
I am using an ESP32 board from AZdelivery that maounts an ESP32.wroom-32 with 4Mbytes of flash
Do you have any suggestions?
Thanks,
reghards,
Corrado
Hi Corrado,
It’s very strange, the flash usage is an anomaly, from the report you paste I think you use Visual Studio code, check if you have some library on you platformio.ini.
Or check witch library the project import.
Bye Renzo
Thanks for writing this article Renzo, I found it much better than any other resource on the subject.
I’m using ESP-NOW and BLE (Advertisement scanning only) on a project, the ESP32 enables BLE, does a 20 second scan for known devices and gets data from the advert. It then switches Bluetooth off and enables ESP-NOW to transmit the data before turning off the modem.
Have you done any testing for ESP-NOW? I’m wondering if the above would be applicable for some power savings, I’ll attempt to modify my code to implement what you’ve documented.
To enable I use
Hi Alan,
Thank you for your kind words! I’m glad you found the article helpful.
While I haven’t done extensive testing with ESP-NOW specifically in the context of power-saving modes, the general principles you’re applying—shutting down components when they’re not in use—are in line with best practices for minimizing power consumption.
One thing you might consider is the timing and sequencing of disabling these components to ensure you’re not inadvertently leaving anything active. The delay you’ve included after btStop() is a good idea, as it gives the system time to fully shut down Bluetooth before moving on.
If you manage to gather any data on the power savings from your modifications, I’d be very interested to hear about it. Your approach could be a valuable addition to the discussion on power optimization for ESP32 projects.
Thanks again for sharing your insights, and good luck with your project!
Bye Renzo