In a previous article, we covered sleep modes in ESP8266. ESP32 is another popular Wi-Fi development board from Expressif systems. It’s more feature-rich than ESP8266, but also more power-hungry. The power consumption of ESP32 can reach up to 790 mA when the Wi-Fi and Bluetooth are both operational — nearly double that of ESP8266, which maxes out at about 400 mA when highly active. The components on the breakout board can also add a few hundred mAs to the net power consumption.
Many IoT developers prefer to use the feature-packed SoC in their applications so that minimum external components are required on the board. This is why ESP32 is the ideal choice for many choosing between the two Expressif boards. However, ESP32’s energy consumption can pose a challenge for battery-powered IoT devices. Fortunately, it offers several power-management options that help.
Next, we’ll discuss ESP32’s power and sleep modes, covering the different wake-up sources and how both (the sleep and wake-up sources) are managed within MicroPython’s framework. Using ESP32’s proper sleep modes within a battery-powered networking application makes it possible to reduce the power strain on the system from hundreds to a few mAs.
Sleep modes in ESP32
In ESP32, there are five power modes available: active, modem sleep, light sleep, deep sleep, and hibernation modes.
Active sleep is the default mode of ESP32, where all the peripherals, including Wi-Fi, Bluetooth module, CPU, system clock, and RTC are on. The typical power consumption by the SoC in this mode is 240 mA, although it can increase to 790 mA when Wi-Fi and Bluetooth function together at the peak of operation. However, the power consumption in active mode can be adjusted by configuring the Wi-Fi settings.
Typically, when the Wi-Fi module transmits an output between 13 and 21dBm, the SoC consumes between 160 to 260 mAs. The output power is the sum of the radio transmit power and antenna gain minus the cable losses.
If the Wi-Fi output is set to 0dBm, consumption is reduced to 120 mA. If the application only receives data over the network, the Wi-Fi module can be set to the receive and listening mode. In this configuration, the SoC’s consumption in active mode is reduced to 80~90 mA.
In the modem sleep mode, the CPU, system clock, and RTC are on while the Wi-Fi, Bluetooth, and radio are off. The consumption in this mode is 20 mA if the CPU clocks at a high speed and can be as little as 3 mA if the CPU clocks more slowly.
In this mode, the SoC uses the DTIM beacon mechanism to stay connected to the network. The Wi-Fi is disabled between the two beacon intervals and is turned on before the next one arrives. The sleep time is determined by the Wi-Fi router’s DTIM beacon interval, which ranges between 100 and 1000 ms. ESP32’s Wi-Fi module can be configured to publish a DTIM after one, three, or 10 beacon intervals. The default DTIM value is three.
In light sleep mode, the CPU goes into a pending state, meaning it’s stopped by powering off its clock pulses. The Wi-Fi, Bluetooth, and radio are also turned off. The Wi-Fi remains connected to the network by the DTIM beacon mechanism, where it periodically goes into sleep and wake-up modes and reconnects to the modem.
The current state of RAM is preserved before entering the light sleep mode, which is resumed as the chip wakes up. The RTC is the only thing that stays on, which is necessary to maintain the date-time. The power consumption of ESP32’s SoC is reduced to 0.8 mA in this mode.
In the deep sleep mode, the Wi-Fi, Bluetooth, radio, system clock, CPU, and RAM are turned off. The only peripheral that’s on is the RTC module. The Wi-Fi and Bluetooth connection data is preserved in the RTC memory as the main memory is wiped out whenever the SoC enters a deep sleep.
The ULP co-processor also stays active and continues to aggregate sensor data. This sensor data is used to wake the chip from a deep sleep. ESP32’s power consumption in a deep sleep is reduced to 0.15 mA ~ 10 uA, depending on the activities of the ULP co-processor.
In the hibernation mode, everything is turned off, including the Wi-Fi, Bluetooth, radio, system clock, CPU, ULP co-processor, and the RTC. Only the RTC timer is kept on a slow clock. ESP32’s power consumption is reduced to 2.5 uA in this mode.
By default, MicroPython only supports light and deep sleep modes for supported ports. These are the only two modes supported when using ESP32 in MicroPython.
ESP32’s wake-up sources
There are four wake-up sources in ESP32: timer, external, touch pins, and the ULP processor.
In the timer wake-up, the built-in RTC is used to wake the SoC from a deep sleep after a specified period. In the external wake-up, the SoC goes into an indefinite deep sleep until a signal is detected by the RTC GPIO. There are two external “wake-ups” in ESP32: ext0 and ext1.
In ext0, only one RTC GPIO is used to wake the SoC from a deep sleep. In ext1, two RTC GPIO is used to wake the chip. In the touch pin wake-up, the chip wakes from a deep sleep by one of its capacitive touch pins. The ULP co-processor is used to wake the main processor via the sensor data.
MicroPython supports the wake-up of ESP32 using a timer, external signals, and touch pins. What’s not supported is the wake-up from the ULP co-processor. There are functions available to access the ULP co-processor, load a binary into the ULP co-processor, run the ULP from an entry point, or wake the ULP co-processor at periodic intervals. These functions are not related to waking from deep or light sleep modes. This means that even though the ULP co-processor can be used to wake ESP32, it’s not currently accessible in MicroPython.
How deep sleep is used in ESP32
As mentioned, ESP32 is a power-hungry chip. So, its deep sleep mode is particularly useful in battery-powered networking applications. It lets the chip mostly remain in deep sleep or idle state, waking periodically to execute assigned tasks. These tasks include taking readings from a sensor, sending the sensor data to a server via an IoT protocol (such as MQTT or CoAP), and/or executing an on-spot action based on sensor readings or insights received over the network.
After executing a task per the user application, the chip returns to deep sleep for a specified period. Even in a deep sleep, the Wi-Fi and Bluetooth network details are stored within the RTC memory. This lets the board easily reconnect with the network each time it wakes from a deep sleep. The cycle of deep sleep, waking up, executing embedded/IoT tasks, and returning to sleep continues endlessly.
MicroPython power-related functions
The machine module of MicroPython provides the following functions for the power management of supported boards.
machine.lightsleep([time_ms]): puts the port into a light sleep mode. If the time in milliseconds is specified as an argument, the light sleep mode lasts for the specified time. The port can wake before the set time triggered by a source. If no argument is passed, the light sleep mode lasts indefinitely or until interrupted by a wake-up source.
machine.deepsleep([time_ms]): puts the port into a deep sleep mode. If the time in milliseconds is specified as an argument, the deep sleep mode lasts for the specified time. The port can wake before the set time triggered by a source. If no argument is passed, the deep sleep mode lasts indefinitely, until interrupted by a wake-up source. Waking from deep sleep mode is the same as a hardware reset.
machine.wake_reason(): returns the wake-up source. The returned value can be machine.WLAN_WAKE, machine.PIN_WAKE, or machine.RTC_WAKE — depending on if the wake-up source is WLAN, pin change, or RTC. This method can either log a wake-up reason into a variable or print it to the console.
The wake-up source must be configured before calling for light or deep sleep. And the machine.wake_reason() can only be called after calling for light or deep sleep.
The machine.idle() is another function that’s useful for the power management of the port. When called, the clock’s gates (for the CPU) cause the port to go into a sleep mode where the port wakes up if it receives an interrupt.
MicroPython implementation of sleep modes in ESP32
MicroPython only supports light and deep sleep modes in ESP32. They’re implemented by the machine.lightsleep() and machine.deepsleep() methods. There’s an ESP32-specific module, esp32, in MicroPython that provides additional functions to configure the wake-up source.
These methods are:
esp32.wake_on_touch(wake): configures whether or not “touch” will wake the ESP32. It uses a boolean argument. If set to ‘1,’ ESP32 wakes upon touch. If set to ‘0,’ ESP32 does not wake upon touch.
esp32.wake_on_ext0(pin, level): configures the external wake-up, EXT0. This wake-up can be applied on one pin, which should be RTC GPIO. It requires two arguments: a pin object and a level. The level can be set to esp32.WAKEUP_ALL_LOW or esp32.WAKEUP_ANY_HIGH.
esp32.wake_on_ext1(pins, level): configures the external wake-up, EXT1. This wake-up can be applied on several pins, provided they’re RTC GPIO. It requires two arguments: a list or tuple of pin objects and a level. The level can be set to esp32.WAKEUP_ALL_LOW or esp32.WAKEUP_ANY_HIGH.
esp32.gpio_deep_sleep_hold(enable): determines whether or not the non-RTC GPIO pin configuration is retained for held pads during the deep sleep mode. It uses a boolean argument, which can be set ‘0’ or ‘1.’
Timer wake-up in ESP32
ESP32’s timer wake-up requires no special arrangement. Simply call the deepsleep() or lightsleep() methods with a specified time in milliseconds to put ESP32 into a deep or light sleep.
For this project, we’ll use the timer wake-up in ESP32 to blink an LED. To do so, connect an LED to ESP32’s at GPIO2 as shown in the image below.
This MicroPython script places ESP8266 into a light sleep mode. It blinks an LED, which is connected to the GPIO2 pin, each time ESP8266 wakes from the timer.
import machine
from machine import Pin
from time import sleep
led = Pin (2, Pin.OUT) #GPIO2 as output for LED
led.value(1) #LED is ON
sleep(1) #delay of 1 second
led.value(0) #LED is OFF
sleep(1) #delay of 1 second
# wait 5 seconds so that you can catch the ESP awake to establish a serial communication later
# you should remove this sleep line in your final script
sleep(5) #delay of 10 seconds
print(‘Going into Light Sleep Mode’)
machine.lightsleep(10000) #10000ms sleep time
The following MicroPython script places ESP8266 into a deep sleep mode. It blinks an LED, which is connected to the GPIO2 pin, each time ESP8266 wakes from the timer.
import machine
from machine import Pin
from time import sleep
led = Pin (2, Pin.OUT) #GPIO2 as output for LED
led.value(1) #LED is ON
sleep(1) #delay of 1 second
led.value(0) #LED is OFF
sleep(1) #delay of 1 second
# wait 5 seconds so that you can catch the ESP awake to establish a serial communication later
# you should remove this sleep line in your final script
sleep(5) #delay of 10 seconds
print(‘Going into Deep Sleep Mode’)
machine.deepsleep(10000) #10000ms sleep time
EXT0 external wake-up in ESP32
The external wake-up is triggered by a pin change. There are two external wake-up sources in ESP32. EXT0 the applies wake-up with a pin change at only one of the RTC GPIOs. The RTC GPIOs in ESP32 are shown below.
Next, let’s blink the LED upon the EXT0’s wake-up. To do so, connect the LED to GPIO2 and then a push button to GPIO14 — pulled high as shown below.
The following MicroPython script places ESP8266 into a light sleep mode. It blinks an LED, which is connected to the GPIO2 pin, each time ESP8266 wakes from an external pin change.
import machine
import esp32
from machine import Pin
from time import sleep
led = Pin (2, Pin.OUT) #GPIO2 as output for LED
led.value(1) #LED is ON
sleep(1) #delay of 1 second
led.value(0) #LED is OFF
sleep(1) #delay of 1 second
# check if the device woke from a light sleep
if machine.reset_cause() == machine.DEEPSLEEP_RESET:
print(‘woke up from a light sleep’)
push_button = Pin(14, mode = Pin.IN) #setting push_button as wake up source
esp32.wake_on_ext0(pin = push_button, level = esp32.WAKEUP_ANY_HIGH) #initializing wake up
sleep(5)
print(‘Going into Light Sleep Mode’)
machine.lightsleep()
The following MicroPython script places ESP8266 into a deep sleep mode. It blinks an LED, which is connected to the GPIO2 pin, each time ESP8266 wakes from an external pin change.
import machine
import esp32
from machine import Pin
from time import sleep
led = Pin (2, Pin.OUT) #GPIO2 as output for LED
led.value(1) #LED is ON
sleep(1) #delay of 1 second
led.value(0) #LED is OFF
sleep(1) #delay of 1 second
# check if the device woke from a deep sleep
if machine.reset_cause() == machine.DEEPSLEEP_RESET:
print(‘woke up from a deep sleep’)
push_button = Pin(14, mode = Pin.IN) #setting push_button as wake up source
esp32.wake_on_ext0(pin = push_button, level = esp32.WAKEUP_ANY_HIGH) #initializing wake up
sleep(5)
print(‘Going into Deep Sleep Mode’)
machine.deepsleep()
Note: the machine.lightsleep() and machine.deepsleep() methods are called without an argument for the external wake-up.
EXT1 external wake-up in ESP32
For the EXT1 external wake-up, more than one RTC GPIOs can be configured as the wake-up source.
For our project, we’ll blink an LED upon the EXT1’s wake-up call. To do so, connect the LED to GPIO22. Also, connect one push button to GPIO2 and a second push button to GPIO4, both pulled high as shown below.
This MicroPython script place the ESP8266 into a light sleep mode. It blinks an LED connected to GPIO2 each time ESP8266 wakes from an external pin change on more than one of the RTC GPIOs.
import machine
import esp32
from machine import Pin
from time import sleep
led = Pin (22, Pin.OUT) #GPIO22 as output for LED
led.value(1) #LED is ON
sleep(1) #delay of 1 second
led.value(0) #LED is OFF
sleep(1) #delay of 1 second
# check if the device woke from a light sleep
if machine.reset_cause() == machine.DEEPSLEEP_RESET:
print(‘woke up from a light sleep’)
push_button1 = Pin(2, mode = Pin.IN) #setting push_button1 as wake up source
push_button2 = Pin(4, mode = Pin.IN) #setting push_button2 as wake up source
esp32.wake_on_ext1(pins = (push_button1, push_button2), level = esp32.WAKEUP_ANY_HIGH) #initializing ext1
sleep(5)
print(‘Going to Light Sleep Mode’)
machine.lightsleep()
This MicroPython script place the ESP8266 into a deep sleep mode. It blinks an LED connected to GPIO2 each time ESP8266 wakes from an external pin change on more than one of the RTC GPIOs.
import machine
import esp32
from machine import Pin
from time import sleep
led = Pin (22, Pin.OUT) #GPIO22 as output for LED
led.value(1) #LED is ON
sleep(1) #delay of 1 second
led.value(0) #LED is OFF
sleep(1) #delay of 1 second
# check if the device woke from a deep sleep
if machine.reset_cause() == machine.DEEPSLEEP_RESET:
print(‘woke up from a deep sleep’)
push_button1 = Pin(2, mode = Pin.IN) #setting push_button1 as wake up source
push_button2 = Pin(4, mode = Pin.IN) #setting push_button2 as wake up source
esp32.wake_on_ext1(pins = (push_button1, push_button2), level = esp32.WAKEUP_ANY_HIGH) #initializing ext1
sleep(5)
print(‘Going to Deep Sleep Mode’)
machine.deepsleep()
Touch pin wake-up in ESP32
ESP32 has touch-sensitive pins that can be used to wake-up.
You can use GPIO4 or Touch0 for the touch sensing. Do so by connecting a wire or jumper wire to GPIO4 and attach a piece of aluminum foil with it for touch sensing. The wire or jumper should be as short as possible.
The following MicroPython script places ESP8266 into a light sleep mode. It blinks an LED that’s connected to GPIO22 each time ESP8266 wakes from touch sense.
from machine import Pin, TouchPad, deepsleep
import time
import esp32
led = Pin (22, Pin.OUT) #GPIO22 as output for LED
led.value(1) #LED is ON
sleep(1) #delay of 1 second
led.value(0) #LED is OFF
sleep(1) #delay of 1 second
wake = Pin(4, mode = Pin.IN)
touch = TouchPad(wake)
touch.config(500)
esp32.wake_on_touch(True)
time.sleep(5)
lightsleep(5000)
The following MicroPython script places ESP8266 into a deep sleep mode. It blinks an LED that’s connected to GPIO22 each time ESP8266 wakes from touch sense.
from machine import Pin, TouchPad, deepsleep
import time
import esp32
led = Pin (22, Pin.OUT) #GPIO22 as output for LED
led.value(1) #LED is ON
sleep(1) #delay of 1 second
led.value(0) #LED is OFF
sleep(1) #delay of 1 second
wake = Pin(4, mode = Pin.IN)
touch = TouchPad(wake)
touch.config(500)
esp32.wake_on_touch(True)
time.sleep(5)
deepsleep(5000)
Conclusion
ESP32 is a power-hungry chip. When used with battery-powered networking applications, it’s important to use ESP32’s sleep modes to preserve the battery life. However, MicroPython only supports ESP32’s light and deep sleep modes. The functions for implementing either mode come from the machine module. These functions are shared by all of the supported ports.
In contrast to ESP8266, ESP32 has several wake-up sources. These include the RTC timer, external pin change, touch pins, and the ULP co-processor. MicroPython’s ESP32-specific module is esp32, which allows for configuring the timer, EXT0, EXT1, and touch as the wake-up sources.
ESP32’s original firmware supports the configuration of the ULP co-processor as a wake-up source, but MicroPython does not currently allow for the use of the ULP co-processor as a wake-up source.
You may also like:
Filed Under: Electronic Projects, ESP8266., Python, Tech Articles, Tutorials
Questions related to this article?
👉Ask and discuss on EDAboard.com and Electro-Tech-Online.com forums.
Tell Us What You Think!!
You must be logged in to post a comment.