Energy
Power the ESP32
To power the ESP32 you can use several methods skteched below:
USB Port (Easiest & Most Common)
The most straightforward method is to plug a micro-USB or USB-C cable (depending on your board) into a computer, a 5V wall adapter (like an old phone charger), or a portable power bank.
Pros: Safe, provides both power and data for programming, and is regulated on-board. Note: Ensure you use a data-capable USB cable if you also need to upload code.
The 5V (or VIN) Pin
You can provide an external unregulated power supply directly to the 5V or VIN pin and a GND (ground) pin. Voltage Range: Typically 5V to 12V.
Efficiency: Higher voltages (like 9V or 12V) will cause the onboard regulator to generate more heat. Warning: There is often no reverse polarity protection on these pins. Connecting power backward can instantly fry the board.
The 3.3V Pin (Regulated Power)
If you already have a stable, regulated 3.3V supply, you can connect it directly to the 3.3V and GND pins.
Critical Danger: This method bypasses the onboard voltage regulator. If your supply exceeds 3.6V, you will damage the ESP32 chip. Use Case: This is common for battery-powered projects using a dedicated external regulator or specific battery types.
Tip
if you don't need 5V in you project, use 3.3V, it will save energy. However, finding suitable batteries is not easy! if the battery does not exceed 3.6V as in the LiFePO4, you can connect the battery to the 3.3V and bypass the voltage regulator

Power consumption
The following picture from https://doi.org/10.3390/s18072104 depicts a typical sensor working scenario, where a node alternates different activities each with a different power consumption

Tip
It is always a valuable exercise to estimate the power consumption of your app with a diagram similar to the one above. Nonetheless, it is then necessary to confirm your estimation by measurements.
ESP32 Sleep Modes
ESP32 supports two major power saving modes: Light-sleep and Deep-sleep.
A nice pictorial representation of these modes is available at lastminuteengineers
Light Sleep
In Light-sleep mode, the digital peripherals, most of the RAM, and CPUs are clock-gated and their supply voltage is reduced. Upon exit from Light-sleep, the digital peripherals, RAM, and CPUs resume operation and their internal states are preserved.
Clock gating is a power-saving technique used in digital circuits. A processor works because a clock signal tells all components when to update their state. If the clock keeps ticking, circuits keep switching—and switching consumes power. Clock gating means temporarily stopping the clock signal to parts of the circuit that aren’t doing anything. When a block (e.g. digital peripherals, RAM, and CPUs) is clock-gated, it is effectively “paused”. Its internal data (state) is kept intact, but it doesn’t switch or compute, so it saves energy.
Deep Sleep
In Deep-sleep mode, the CPUs, most of the RAM, and all digital peripherals that are clocked from APB_CLK are powered off. The only parts of the chip that remain powered on are: RTC controller, ULP coprocessor, RTC FAST memory, RTC SLOW memory. Deep-sleep is a much more aggressive power-saving mode than Light-sleep, and the key difference is power is actually removed, not just paused.
- CPUs are completely off → The main processors stop running entirely, All execution halts. When the chip wakes up, it restarts from reset (like powering it on again), not from where it left off
- Most RAM is lost → Regular RAM is powered down → data is erased. Any variables, stack, heap, program state are gone. Only RTC FAST and SLOW memory survive → So anything you need after wake-up must be stored there (or in flash)
- Digital peripherals are off → Peripherals using APB_CLK (timers, UART, SPI, I²C, etc.) are disabled. They stop working and lose configuration → After wake-up, you must reinitialize everything
Only RTC domain stays alive. The chip keeps a tiny “always-on” subsystem:
- RTC controller → manages sleep & wake-up
- ULP coprocessor → can run small programs at ultra-low power
- RTC memories → retain small amounts of data
This allows:
- Timed wake-ups (e.g., every 10 seconds)
- Sensor monitoring without waking the main CPU
Connectivity and Sleep Modes
In Deep-sleep and Light-sleep modes, the wireless peripherals are powered down. Before entering Deep-sleep or Light-sleep modes, the application must disable Wi-Fi and Bluetooth using the appropriate calls.
If Wi-Fi/Bluetooth connections need to be maintained, enable Wi-Fi/Bluetooth Modem-sleep mode and automatic Light-sleep feature (see Power Management APIs). This allows the system to wake up from sleep automatically when required by the Wi-Fi/Bluetooth driver, thereby maintaining the connection.
Wakeup
There are several wakeup sources in the sleep modes. The following are excellent sources of information to understand how to ESP32 Sleep Modes and Their Power Consumption.
- Insight Into ESP32 Sleep Modes and Their Power Consumption
- ESP32 Deep Sleep Mode
- ESP32 Deep Sleep with Arduino IDE and Wake Up Sources
Deep Sleep wakeup example
- Deep Sleep: The chip is off. Only the RTC controller watches the GPIO (button).
- Wake Up: The button is pressed. The ESP32 boots.
- Setup: We check why we woke up. If it was the button, we start our tasks.
- Task 1: Handles user interaction or logic.
- Task 2: Performs the "Monitoring Activity" (e.g., reading a sensor).
- Back to Sleep: Once the tasks finish their job, they call for deep sleep again.
#include <Arduino.h>
#define BUTTON_PIN GPIO_NUM_27
// This variable survives deep sleep
RTC_DATA_ATTR bool should_monitor = false;
// Task 2: The Monitor
void taskMonitor(void *pvParameters) {
if (should_monitor) {
Serial.println("[Task 2] Monitoring activity started...");
// Simulate sensor reading or WiFi upload
delay(2000);
Serial.println("[Task 2] Monitoring complete.");
}
Serial.println("Mission accomplished. Going back to deep sleep in 1 second.");
delay(1000);
// Prepare for next sleep
esp_sleep_enable_ext0_wakeup(BUTTON_PIN, 0); // Wake when button is LOW (pressed)
esp_deep_sleep_start();
}
// Task 1: The Coordinator
void taskCoordinator(void *pvParameters) {
Serial.println("[Task 1] Checking wake-up reason...");
esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();
if (wakeup_reason == ESP_SLEEP_WAKEUP_EXT0) {
Serial.println("[Task 1] Woke up by Button! Signaling Task 2.");
should_monitor = true;
} else {
Serial.println("[Task 1] Normal boot or other wake source.");
should_monitor = false;
}
// Task 1 can now delete itself or wait
vTaskDelete(NULL);
}
void setup() {
Serial.begin(115200);
pinMode(BUTTON_PIN, INPUT_PULLUP);
// Create Task 1: Check why we woke up
xTaskCreate(taskCoordinator, "Coordinator", 2048, NULL, 2, NULL);
// Create Task 2: Perform monitoring if needed
xTaskCreate(taskMonitor, "Monitor", 2048, NULL, 1, NULL);
}
void loop() {
// Empty. Everything happens in tasks and then deep sleep.
}
Ensuring Task Completion
If you put the sleep command in setup(), the ESP32 might go to sleep before the FreeRTOS scheduler even has a chance to start your tasks.
-
The Problem:
setup()runs, creates tasks, and then hits the "sleep" command immediately. The tasks never actually execute because the CPU powers down. -
The Solution: By putting the sleep command at the end of the Monitoring Task, we ensure that the "work" is actually finished before the lights go out.
Key Components Explained
| Feature | Function |
|---|---|
RTC_DATA_ATTR |
Stores variables in the RTC slow memory. This is the only way Task 2 knows what Task 1 found out after a sleep cycle. |
esp_sleep_enable_ext0_wakeup |
Configures a specific GPIO to trigger the "wake up" signal while the rest of the chip is powered down. |
esp_deep_sleep_start() |
The "Off" switch. Execution stops here and the power draw drops to ~10µA. |
| ### Important Real-World Note |
In a true FreeRTOS environment, if you want "Task 1" to trigger "Task 2" while the chip is already awake, you would use a Semaphore or Event Group. However, because Deep Sleep triggers a full reboot, the "trigger" here is actually the CPU reset and the shared RTC memory.
How to measure energy consumption
INA219 Power Monitor with ESP32
The INA219 is a high-side current shunt and power monitor with an \(I^2C\) interface. It is ideal for monitoring the power consumption of battery-powered projects.
INA219 Pinout
The INA219 module typically breaks out the following pins for power and communication.
| Pin | Function | Description |
|---|---|---|
| VCC | Power Supply | 3.3V – 5V (3.3V recommended for ESP32) |
| GND | Ground | Common Ground |
| SDA | \(I^2C\) Data | Serial Data line |
| SCL | \(I^2C\) Clock | Serial Clock line |
| VIN+ | Input (+) | Connect to the Positive of the Power Source |
| VIN- | Output (-) | Connect to the Positive side of the Load |
ESP32 Wiring Diagram
This table shows the standard \(I^2C\) wiring for an ESP32 Development Board.
| INA219 Pin | ESP32 Pin | Note |
|---|---|---|
| VCC | 3.3V | Logic level matching |
| GND | GND | Common Ground |
| SDA | GPIO 21 | Default SDA |
| SCL | GPIO 22 | Default SCL |
Load Connection Logic
To measure current, the sensor must be placed in series on the high side of your circuit.
| Connection | Direction | Target |
|---|---|---|
| Power Source (+) | → | VIN+ |
| VIN- | → | Load (+) |
| Load (-) | → | Ground |
Measurement Principle
The INA219 measures the shunt voltage drop across a precision resistor located between VIN+ and VIN-. It then calculates the current (\(I\)) based on the known resistance.

// use https://github.com/nathandunk/BetterSerialPlotter to visualize the data
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_INA219.h>
Adafruit_INA219 ina219;
void setup(void)
{
Serial.begin(115200);
while (!Serial) {
// will pause Zero, Leonardo, etc until serial console opens
delay(1);
}
uint32_t currentFrequency;
// Initialize the INA219.
// By default the initialization will use the largest range (32V, 2A). However
// you can call a setCalibration function to change this range (see comments).
if (! ina219.begin()) {
Serial.println("Failed to find INA219 chip");
while (1) { delay(10); }
}
// To use a slightly lower 32V, 1A range (higher precision on amps):
//ina219.setCalibration_32V_1A();
// Or to use a lower 16V, 400mA range (higher precision on volts and amps):
//ina219.setCalibration_16V_400mA();
Serial.println("Measuring voltage and current with INA219 ...");
}
void loop(void)
{
float shuntvoltage = 0;
float busvoltage = 0;
float current_mA = 0;
float loadvoltage = 0;
float power_mW = 0;
shuntvoltage = ina219.getShuntVoltage_mV();
busvoltage = ina219.getBusVoltage_V();
current_mA = ina219.getCurrent_mA();
power_mW = ina219.getPower_mW();
loadvoltage = busvoltage + (shuntvoltage / 1000);
Serial.print(busvoltage);
//Serial.print(",");
Serial.print("\t");
Serial.print(shuntvoltage);
//Serial.print(",");
Serial.print("\t");
Serial.print(loadvoltage);
//Serial.print(",");
Serial.print("\t");
Serial.print(current_mA);
//Serial.print(",");
Serial.print("\t");
Serial.println(power_mW);
//Serial.println("");
/*
delay(50);
}
/*
ESP32 Deep Sleep Mode Timer Wake UP
http:://www.electronicwings.com
*/
#define Time_To_Sleep 5 //Time ESP32 will go to sleep (in seconds)
#define S_To_uS_Factor 1000000ULL //Conversion factor for micro seconds to seconds
RTC_DATA_ATTR int bootCount= 0;
void setup() {
Serial.begin(115200);
delay(1000); //Take some time to open up the Serial Monitor
//Increment boot number and print it every reboot
++bootCount;
Serial.println("Boot number: " + String(bootCount));
//Set timer to 5 seconds
esp_sleep_enable_timer_wakeup(Time_To_Sleep * S_To_uS_Factor);
Serial.println("Setup ESP32 to sleep for every " + String(Time_To_Sleep) +
" Seconds");
//Go to sleep now
esp_deep_sleep_start();
Serial.println("This will not print!!"); // This will not get print,as ESP32 goes in Sleep mode.
}
void loop() {} // We don't need loop as ESP32 will initilize each time.
We use Better Serial Plotter to visualize the data.

A powerful alternative is Serial-Studio
[!EXERCISE] Write a simple code that mimics the diagram at the beginning of this section of a typical sensor working scenario and measure the consumption of each activity