The deep sleep mode is a mode that shuts down almost the entire microcontroller until a wake-up event occurs.
Up until now, our programs have been running at full throttle. The processor was always active, Wi-Fi was connected, and LEDs were on.
If you have the board plugged into the wall, great. But if you want to power your sensor with a battery (LiPo or AA batteries), you have a serious problem:
- ESP32 active with Wi-Fi: ~150 mA - 240 mA.
- Typical battery (2000 mAh): Approximate duration: 8 hours.
8 hours? That’s not an IoT device; that’s a toy. To achieve durations of months or years, we need to use Deep Sleep mode.
In this mode, the ESP32 turns off the CPU, RAM, Wi-Fi, and Bluetooth. It only leaves a small internal clock (RTC) and an ultra-low-power coprocessor running.
- ESP32 in Deep Sleep: ~10 µA (0.01 mA).
- Theoretical duration: Years.
The philosophy of waking up
The most important thing to understand is that when the ESP32 wakes from deep sleep, it does not continue where it left off.
For the microcontroller, waking from Deep Sleep is the same as a Reset. The program starts again from the first line of boot.py and main.py.
Therefore, the logic of our low-power programs changes radically:
- Start.
- Read sensor.
- Connect WiFi and send data.
- Go to sleep.
- (On wake up, return to step 1).
Sleeping and waking up by time
This is the most common case: “I want to measure the temperature every 10 minutes.”
We use the machine.deepsleep(milliseconds) function.
import machine
import time
# 1. Code for "Doing things"
print("I have woken up. Going to work...")
# (Here you would read the sensor and send via MQTT)
time.sleep(1) # Simulate work
print("Work finished. Going to sleep for 10 seconds.")
# 2. Configure wake-up
# If we don't pass arguments, it sleeps forever until a manual reset.
# By passing milliseconds, we configure the RTC Timer.
machine.deepsleep(10000)
Knowing why we woke up
Sometimes we need to know if the ESP32 just turned on because someone pressed the Reset button or because it woke from sleep.
import machine
reason = machine.reset_cause()
if reason == machine.DEEPSLEEP_RESET:
print("I came from a deep sleep!")
else:
print("I started for the first time (Power On or Reset).")
Sleeping and waking up using a button
Imagine a smart doorbell. It must always be asleep, except when someone presses the button. A timer won’t work; we need an external interrupt.
The ESP32 has special hardware in the RTC to monitor pins while it sleeps. In MicroPython, we use the esp32 module.
import machine
import esp32
from machine import Pin
# Button on GPIO 33 (Must be an RTC pin: 0, 2, 4, 12-15, 25-27, 32-39)
btn = Pin(33, Pin.IN, Pin.PULL_UP)
# Configure wake up by pin (wake_on_ext0)
# Pin: The pin object
# Level: esp32.WAKEUP_ALL_LOW (if it's pull-up and we press to ground)
# esp32.WAKEUP_ANY_HIGH (if it's pull-down and we apply 3.3v)
esp32.wake_on_ext0(pin=btn, level=esp32.WAKEUP_ALL_LOW)
print("Sleeping... Press GPIO 33 button to wake up.")
machine.deepsleep()
On the original ESP32, ext0 can only monitor a single pin. If you need to wake up with several different buttons, you must use wake_on_ext1, which is a bit more complex to configure.
Preserving data in RTC memory
As we said, everything resets upon waking up. Variables are erased. counter = 0.
If we want to keep track of how many times we’ve woken up, or save the last sensor value, we cannot use normal variables. Should we use Flash memory (files)? No, because it is slow and wears out if we write every minute.
The solution is the RTC Memory. It is a small area of RAM memory (about 8KB) that remains powered during Deep Sleep.
import machine
# Access the RTC memory
rtc = machine.RTC()
# Read what is stored (returns bytes)
data = rtc.memory()
if not data:
# If it's empty (first boot)
counter = 0
else:
# Convert bytes to integer
counter = int(data)
print(f"This is execution number: {counter}")
# Increment
counter += 1
# Save in RTC memory (must be bytes)
rtc.memory(str(counter))
print("Going to sleep...")
machine.deepsleep(2000)
The power consumption of development boards
Here comes the cold shower. If you load these codes onto a typical ESP32 DevKit V1 or NodeMCU and measure the consumption, you will see it is around 10 mA, not 10 µA.
Why?
- Voltage Regulator: The AMS1117 chip these boards use is very inefficient and consumes current even without load.
- USB-Serial Converter: The CP2102/CH340 chip also consumes power.
- Power LED: That red LED that is always on consumes about 5-10 mA by itself.
For real low power To deploy real projects, you need boards designed for this purpose (like the FireBeetle from DFRobot or the EzSBC), or use the “bare” ESP32 chip with an efficient regulator (like the MCP1700) and without unnecessary LEDs.
Strategy for an IoT node
To make a WiFi temperature sensor that lasts a year on batteries:
- Activate the Wi-Fi interface and call
WLAN.connect()as soon as possible. - Use a static IP (saves 2-3 seconds of DHCP negotiation).
- Send data via MQTT or UDP (faster than HTTP).
- If the temperature change is small, send nothing, just go back to sleep (saves WiFi).
- Use
machine.deepsleep()99.9% of the time.
Mastering deep sleep is what turns a “homemade invention” into a professional device.