ESP32 power saving: modem and light sleep – 2

Spread the love

ESP32 practical power saving modem and light sleep
ESP32 practical power-saving modem and light sleep

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.

Function Block diagram Espressif esp32 Wi-Fi Bluetooth Microcontroller
Function Block diagram Espressif esp32 WiFi Bluetooth Microcontroller

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

esp32 dev kit pinout v1
esp32 dev kit pinout v1

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.

Function Block diagram Espressif esp32 modem sleep
Function Block diagram Espressif esp32 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

ESP32 WeMos LOLIN32 pinout
ESP32 WeMos LOLIN32 pinout

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.

ESP32 WeMos LOLIN32 powered to 5V and Serial2 to debug amperage multimerter check
ESP32 WeMos LOLIN32 powered to 5V and Serial2 to debug amperage multimeter check

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.

ESP32 WeMos LOLIN32 powered with battery and Serial2 to debug amperage multimerter check
ESP32 WeMos LOLIN32 powered with battery and Serial2 to debug amperage multimeter check

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.

Function Block diagram Espressif esp32 light sleep
Function Block diagram Espressif esp32 light sleep

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.

DeviceModePower
DOIT DEV KIT v1Power to VIN pin4.98mA
TTGO T8Power to 5V pin8.24mA
Power via Battery3.73mA
WeMos LOLIN32Power via 5V pin2.60mA
Power via Battery1.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

DeviceModePower
DOIT DEV KIT v1Power to VIN pin6.85mA (+1,87mA)
TTGO T8Power to 5V pin10.05mA (+1,81mA)
Power via Battery5.04mA (+1,31mA)
WeMos LOLIN32Power via 5V pin4.35mA (+1,75mA)
Power via Battery3.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.

  1. ESP32: pinout, specs and Arduino IDE configuration
  2. ESP32: integrated SPIFFS Filesystem
  3. ESP32: manage multiple Serial and logging
  4. ESP32 practical power saving
    1. ESP32 practical power saving: manage WiFi and CPU
    2. ESP32 practical power saving: modem and light sleep
    3. ESP32 practical power saving: deep sleep and hibernation
    4. ESP32 practical power saving: preserve data, timer and touch wake up
    5. ESP32 practical power saving: external and ULP wake up
    6. ESP32 practical power saving: UART and GPIO wake up
  5. ESP32: integrated LittleFS FileSystem
  6. ESP32: integrated FFat (Fat/exFAT) FileSystem
  7. ESP32-wroom-32
    1. ESP32-wroom-32: flash, pinout, specs and IDE configuration
  8. ESP32-CAM
    1. ESP32-CAM: pinout, specs and Arduino IDE configuration
    2. ESP32-CAM: upgrade CamerWebServer with flash features
  9. ESP32: use ethernet w5500 with plain (HTTP) and SSL (HTTPS)
  10. ESP32: use ethernet enc28j60 with plain (HTTP) and SSL (HTTPS)
  11. How to use SD card with esp32
  12. esp32 and esp8266: FAT filesystem on external SPI flash memory
  1. Firmware and OTA update management
    1. Firmware management
      1. ESP32: flash compiled firmware (.bin)
      2. ESP32: flash compiled firmware and filesystem (.bin) with GUI tools
    2. OTA update with Arduino IDE
      1. ESP32 OTA update with Arduino IDE: filesystem, firmware, and password
    3. OTA update with Web Browser
      1. ESP32 OTA update with Web Browser: firmware, filesystem, and authentication
      2. ESP32 OTA update with Web Browser: upload in HTTPS (SSL/TLS) with self-signed certificate
      3. ESP32 OTA update with Web Browser: custom web interface
    4. Self OTA uptate from HTTP server
      1. ESP32 self OTA update firmware from the server
      2. ESP32 self OTA update firmware from the server with version check
      3. ESP32 self-OTA update in HTTPS (SSL/TLS) with trusted self-signed certificate
    5. Non-standard Firmware update
      1. ESP32 firmware and filesystem update from SD card
      2. ESP32 firmware and filesystem update with FTP client
  1. Integrating LAN8720 with ESP32 for Ethernet Connectivity with plain (HTTP) and SSL (HTTPS)
  2. Connecting the EByte E70 to ESP32 c3/s3 devices and a simple sketch example
  3. ESP32-C3: pinout, specs and Arduino IDE configuration
  4. Integrating W5500 with ESP32 Using Core 3: Native Ethernet Protocol Support with SSL and Other Features
  5. Integrating LAN8720 with ESP32 Using Core 3: Native Ethernet Protocol Support with SSL and Other Features

Spread the love

4 Responses

  1. Corrado says:

    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

  2. Alan says:

    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.

    void stopBLEScan()
    {
        BLEDevice::deinit();
        NimBLEDevice::deinit(true);
        btStop();
        delay(1000);
        WiFi.disconnect(true);
        WiFi.mode(WIFI_OFF);
    }
    
    
    void stopESPNow()
    {
        esp_now_deinit();
        WiFi.mode(WIFI_OFF);
        WiFi.disconnect(true);
    }
    

    To enable I use

    ESP-NOW: WiFi.mode(WIFI_STA);
    BLE:  NimBLEDevice::init(""); and BLEDevice::getScan();
    
    • 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

Leave a Reply

Your email address will not be published. Required fields are marked *