Power Management

Power management is critical for battery-powered products. This guide covers ESP32 power consumption, sleep modes, battery selection, and optimization techniques.

ESP32 Power Consumption Overview

Current Draw by Mode

ModeCurrentDescription
Active (WiFi TX)160-260 mATransmitting WiFi data
Active (WiFi RX)80-95 mAReceiving WiFi data
Active (BLE TX)27-40 mATransmitting BLE data
Active (CPU only)20-40 mANo wireless, CPU running
Modem Sleep3-20 mAWiFi disabled, CPU active
Light Sleep0.8 mACPU paused, RAM retained
Deep Sleep10 µARTC memory only
Hibernation2.5 µARTC timer only

Measuring Power Consumption

For accurate measurements:

Equipment needed:
- Multimeter with µA range
- Current sense resistor (0.1-1Ω)
- Oscilloscope (for transients)
- USB power meter (basic measurements)

Important: Development boards have LEDs, voltage regulators, and USB bridges that consume 10-30mA. For accurate measurements, use the bare module.


Sleep Modes

Light Sleep

CPU paused, fast wake-up, RAM retained.

#include "esp_sleep.h"

void enterLightSleep(int seconds) {
    // Configure wake-up source
    esp_sleep_enable_timer_wakeup(seconds * 1000000ULL);  // microseconds

    Serial.println("Entering light sleep...");
    Serial.flush();

    // Enter light sleep
    esp_light_sleep_start();

    // Code continues here after wake-up
    Serial.println("Woke up from light sleep!");
}

void setup() {
    Serial.begin(115200);
    delay(1000);

    enterLightSleep(5);  // Sleep for 5 seconds
}

void loop() {
    Serial.println("Running...");
    delay(2000);
    enterLightSleep(5);
}

Deep Sleep

Lowest power with timer/GPIO wake-up. RTC memory preserved.

#include "esp_sleep.h"

// Store data in RTC memory (survives deep sleep)
RTC_DATA_ATTR int bootCount = 0;

void setup() {
    Serial.begin(115200);
    delay(1000);

    bootCount++;
    Serial.printf("Boot count: %d\n", bootCount);

    // Print wake-up reason
    printWakeupReason();

    // Do your work here
    Serial.println("Doing some work...");
    delay(2000);

    // Configure wake-up
    esp_sleep_enable_timer_wakeup(10 * 1000000ULL);  // 10 seconds

    Serial.println("Going to deep sleep...");
    Serial.flush();

    esp_deep_sleep_start();
    // Code after this never executes - ESP32 resets on wake
}

void printWakeupReason() {
    esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();

    switch (wakeup_reason) {
        case ESP_SLEEP_WAKEUP_EXT0:
            Serial.println("Wakeup: External signal (RTC_IO)");
            break;
        case ESP_SLEEP_WAKEUP_EXT1:
            Serial.println("Wakeup: External signal (RTC_CNTL)");
            break;
        case ESP_SLEEP_WAKEUP_TIMER:
            Serial.println("Wakeup: Timer");
            break;
        case ESP_SLEEP_WAKEUP_TOUCHPAD:
            Serial.println("Wakeup: Touchpad");
            break;
        case ESP_SLEEP_WAKEUP_ULP:
            Serial.println("Wakeup: ULP program");
            break;
        default:
            Serial.printf("Wakeup: Other (%d)\n", wakeup_reason);
            break;
    }
}

void loop() {
    // Never reached in deep sleep pattern
}

GPIO Wake-up (Deep Sleep)

#include "esp_sleep.h"

#define WAKEUP_PIN GPIO_NUM_33  // Must be RTC GPIO

RTC_DATA_ATTR int bootCount = 0;

void setup() {
    Serial.begin(115200);
    bootCount++;
    Serial.printf("Boot count: %d\n", bootCount);

    // Configure GPIO wake-up
    // ESP_EXT1_WAKEUP_ANY_HIGH: Wake when ANY pin goes HIGH
    // ESP_EXT1_WAKEUP_ALL_LOW: Wake when ALL pins go LOW

    // Single pin wake-up (ext0)
    esp_sleep_enable_ext0_wakeup(WAKEUP_PIN, 1);  // 1 = HIGH, 0 = LOW

    // Or multiple pins (ext1)
    // uint64_t mask = (1ULL << GPIO_NUM_33) | (1ULL << GPIO_NUM_32);
    // esp_sleep_enable_ext1_wakeup(mask, ESP_EXT1_WAKEUP_ANY_HIGH);

    Serial.println("Going to sleep. Press button to wake...");
    Serial.flush();

    esp_deep_sleep_start();
}

void loop() {}

Touch Pad Wake-up

#include "esp_sleep.h"

#define TOUCH_PIN T0  // GPIO 4
#define TOUCH_THRESHOLD 40

RTC_DATA_ATTR int bootCount = 0;

void callback() {
    // Touch detected
}

void setup() {
    Serial.begin(115200);
    bootCount++;
    Serial.printf("Boot count: %d\n", bootCount);

    // Configure touch wake-up
    touchAttachInterrupt(TOUCH_PIN, callback, TOUCH_THRESHOLD);
    esp_sleep_enable_touchpad_wakeup();

    Serial.println("Going to sleep. Touch pad to wake...");
    Serial.flush();

    esp_deep_sleep_start();
}

void loop() {}

Hibernation Mode

Absolute minimum power. Only RTC timer can wake.

#include "esp_sleep.h"

void setup() {
    Serial.begin(115200);
    Serial.println("Preparing for hibernation...");

    // Only timer wake-up in hibernation
    esp_sleep_enable_timer_wakeup(60 * 1000000ULL);  // 60 seconds

    // Disable all RTC peripherals for minimum power
    esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_SLOW_MEM, ESP_PD_OPTION_OFF);
    esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_FAST_MEM, ESP_PD_OPTION_OFF);
    esp_sleep_pd_config(ESP_PD_DOMAIN_XTAL, ESP_PD_OPTION_OFF);

    Serial.println("Entering hibernation...");
    Serial.flush();

    esp_deep_sleep_start();
}

void loop() {}

Power Optimization Strategies

WiFi Power Optimization

#include <WiFi.h>

void optimizedWiFiConnect() {
    // Disable WiFi power saving initially for fast connect
    WiFi.setSleep(false);

    WiFi.mode(WIFI_STA);
    WiFi.begin("ssid", "password");

    while (WiFi.status() != WL_CONNECTED) {
        delay(100);
    }

    // Enable power saving after connection
    WiFi.setSleep(true);  // Modem sleep between beacon intervals
}

// For maximum power saving
void maxPowerSaving() {
    // Configure WiFi for power saving
    esp_wifi_set_ps(WIFI_PS_MAX_MODEM);

    // Reduce TX power (less range, less power)
    WiFi.setTxPower(WIFI_POWER_MINUS_1dBm);  // Options: 19.5, 18.5, ... -1 dBm
}

CPU Frequency Scaling

void setup() {
    Serial.begin(115200);

    // Get current frequency
    Serial.printf("CPU Frequency: %d MHz\n", getCpuFrequencyMhz());

    // Set lower frequency for power saving
    setCpuFrequencyMhz(80);  // Options: 240, 160, 80, 40, 20, 10
    Serial.printf("New CPU Frequency: %d MHz\n", getCpuFrequencyMhz());
}

Disabling Unused Peripherals

#include "driver/adc.h"
#include "esp_wifi.h"
#include "esp_bt.h"

void disableUnusedPeripherals() {
    // Disable WiFi
    esp_wifi_stop();
    esp_wifi_deinit();

    // Disable Bluetooth
    esp_bt_controller_disable();
    esp_bt_controller_deinit();

    // Disable ADC
    adc_power_release();
}

Efficient Data Transmission Pattern

#include <WiFi.h>
#include <HTTPClient.h>
#include "esp_sleep.h"

RTC_DATA_ATTR int readingCount = 0;
RTC_DATA_ATTR float readings[10];

void setup() {
    Serial.begin(115200);

    // Take reading
    readings[readingCount++] = readSensor();

    // Only transmit every 10 readings
    if (readingCount >= 10) {
        connectAndSendData();
        readingCount = 0;
    }

    // Deep sleep for 5 minutes
    esp_sleep_enable_timer_wakeup(5 * 60 * 1000000ULL);
    esp_deep_sleep_start();
}

float readSensor() {
    // Simulated sensor reading
    return analogRead(34) * 0.001;
}

void connectAndSendData() {
    WiFi.begin("ssid", "password");
    while (WiFi.status() != WL_CONNECTED) delay(100);

    HTTPClient http;
    http.begin("http://your-server.com/data");
    http.addHeader("Content-Type", "application/json");

    // Build JSON with all readings
    String json = "[";
    for (int i = 0; i < 10; i++) {
        if (i > 0) json += ",";
        json += String(readings[i]);
    }
    json += "]";

    http.POST(json);
    http.end();

    WiFi.disconnect(true);
}

void loop() {}

Battery Selection and Management

Battery Types Comparison

TypeVoltageEnergy DensitySelf-DischargeTemp RangeCost
LiPo (3.7V)3.0-4.2VHigh5%/month-20 to 60°CMedium
Li-Ion (18650)2.5-4.2VHigh2-3%/month-20 to 60°CLow
LiFePO42.5-3.65VMedium3%/month-20 to 70°CMedium
AA Alkaline1.5V (×2=3V)Medium2%/year-18 to 55°CVery Low
AA Lithium1.5V (×2=3V)High<1%/year-40 to 60°CMedium
CR123A3VMedium<1%/year-40 to 60°CMedium

Battery Capacity Calculation

Runtime = Battery Capacity (mAh) / Average Current (mA)

Example:
- 2000mAh LiPo battery
- Deep sleep: 10µA for 59 minutes
- Active WiFi: 150mA for 1 minute (each hour)

Hourly consumption:
- Sleep: 0.01mA × 59min = 0.59mAh
- Active: 150mA × 1min = 2.5mAh
- Total: ~3.1mAh per hour

Runtime: 2000mAh / 3.1mAh = ~645 hours = ~27 days

LiPo Battery Circuit

                    ┌──────────────┐
                    │   TP4056     │
USB 5V ────────────►│  Charging    │
                    │   Module     │
                    └──────┬───────┘
                           │
                    ┌──────┴───────┐
                    │    LiPo      │
                    │   Battery    │
                    │   3.7V       │
                    └──────┬───────┘
                           │
                    ┌──────┴───────┐
                    │   HT7333     │──────► 3.3V to ESP32
                    │  or AMS1117  │
                    └──────────────┘

Battery Voltage Monitoring

#define VBAT_PIN 35  // ADC pin
#define VBAT_DIVIDER_RATIO 2.0  // If using voltage divider

// Calibration factor (adjust based on measurement)
#define ADC_CALIBRATION 1.073

float readBatteryVoltage() {
    int raw = 0;

    // Average multiple readings
    for (int i = 0; i < 10; i++) {
        raw += analogRead(VBAT_PIN);
        delay(10);
    }
    raw /= 10;

    // Convert to voltage
    float voltage = (raw / 4095.0) * 3.3 * VBAT_DIVIDER_RATIO * ADC_CALIBRATION;

    return voltage;
}

int getBatteryPercentage(float voltage) {
    // LiPo discharge curve (approximate)
    // 4.2V = 100%, 3.0V = 0%
    if (voltage >= 4.2) return 100;
    if (voltage <= 3.0) return 0;

    // Non-linear mapping
    if (voltage >= 4.0) return map(voltage * 100, 400, 420, 80, 100);
    if (voltage >= 3.7) return map(voltage * 100, 370, 400, 30, 80);
    return map(voltage * 100, 300, 370, 0, 30);
}

void setup() {
    Serial.begin(115200);

    // Configure ADC
    analogReadResolution(12);
    analogSetAttenuation(ADC_11db);
}

void loop() {
    float voltage = readBatteryVoltage();
    int percent = getBatteryPercentage(voltage);

    Serial.printf("Battery: %.2fV (%d%%)\n", voltage, percent);

    // Low battery warning
    if (voltage < 3.3) {
        Serial.println("WARNING: Low battery!");
        // Take action: send alert, deep sleep, etc.
    }

    delay(5000);
}

Voltage Divider for Battery Monitoring

VBAT (3.0-4.2V)
    │
    ├── 100kΩ ──┬── To ADC (GPIO 35)
    │           │
    └─────────── 100kΩ
                │
               GND

Divider ratio: 2:1
ADC sees: 1.5V - 2.1V

Important: Use high-value resistors (100kΩ+) to minimize battery drain.


Power Supply Design

LDO vs Switching Regulator

FeatureLDOSwitching (Buck)
Efficiency60-80%85-95%
NoiseVery lowHigher
HeatMoreLess
SizeSmallerLarger
CostLowerHigher
Use WhenLow current, clean powerBattery-powered, high current

LDO (Linear):

  • AMS1117-3.3: 1A, dropout 1.2V
  • HT7333: 250mA, dropout 0.1V, low quiescent current
  • AP2112K-3.3: 600mA, low dropout, low noise

Switching (Buck):

  • MP2359: 1.2A, 92% efficient
  • TPS62200: 300mA, 95% efficient, tiny
  • RT8059: 600mA, very low quiescent

Decoupling Capacitors

3.3V ──┬── 10µF ──┬── ESP32 VDD
       │          │
      GND        100nF
                  │
                 GND

Place 100nF as close to ESP32 as possible.
10µF can be slightly further away.

Solar Power Integration

Basic Solar Setup

                    ┌──────────────┐
Solar Panel ───────►│   CN3065     │
(5-6V, 1W+)         │  or TP4056   │
                    │ with solar   │
                    │   input      │
                    └──────┬───────┘
                           │
                    ┌──────┴───────┐
                    │    LiPo      │
                    │   Battery    │
                    └──────┬───────┘
                           │
                    ┌──────┴───────┐
                    │   3.3V LDO   │──────► ESP32
                    └──────────────┘

Solar Sizing

Daily energy needed:
- Deep sleep 23 hours: 0.01mA × 23h = 0.23mAh
- Active 1 hour total: 100mA × 1h = 100mAh
- Total: ~100mAh/day @ 3.3V = 330mWh

Solar panel sizing (assuming 4 peak sun hours):
- Account for 50% system losses
- Required: 330mWh / 4h / 0.5 = 165mW panel minimum
- Recommended: 250-500mW panel for margin

Solar Power Code

#include "esp_sleep.h"

#define SOLAR_PIN 34
#define VBAT_PIN 35

RTC_DATA_ATTR int bootCount = 0;

bool hasSufficientPower() {
    float batteryVoltage = readBatteryVoltage();
    float solarVoltage = analogRead(SOLAR_PIN) * (3.3 / 4095.0) * 2;  // With divider

    Serial.printf("Battery: %.2fV, Solar: %.2fV\n", batteryVoltage, solarVoltage);

    // Need battery above 3.5V or active solar charging
    return (batteryVoltage > 3.5) || (solarVoltage > 4.5);
}

void setup() {
    Serial.begin(115200);
    bootCount++;

    if (hasSufficientPower()) {
        // Do work
        doWork();

        // Short sleep when solar available
        esp_sleep_enable_timer_wakeup(5 * 60 * 1000000ULL);  // 5 minutes
    } else {
        // Long sleep to conserve battery
        Serial.println("Low power - extended sleep");
        esp_sleep_enable_timer_wakeup(30 * 60 * 1000000ULL);  // 30 minutes
    }

    esp_deep_sleep_start();
}

void doWork() {
    // Connect, send data, etc.
}

float readBatteryVoltage() {
    return analogRead(VBAT_PIN) * (3.3 / 4095.0) * 2;  // With voltage divider
}

void loop() {}

Power Budget Template

Use this template to estimate battery life:

┌─────────────────────────────────────────────────────────────┐
│                    POWER BUDGET                             │
├──────────────────┬──────────┬──────────┬───────────────────┤
│ Mode             │ Current  │ Duration │ Energy (mAh)      │
├──────────────────┼──────────┼──────────┼───────────────────┤
│ Deep Sleep       │ 10 µA    │ 58 min   │ 0.01              │
│ Wake + Sensor    │ 30 mA    │ 100 ms   │ 0.0008            │
│ WiFi Connect     │ 150 mA   │ 3 sec    │ 0.125             │
│ WiFi TX/RX       │ 180 mA   │ 2 sec    │ 0.1               │
│ WiFi Disconnect  │ 50 mA    │ 100 ms   │ 0.0014            │
├──────────────────┼──────────┼──────────┼───────────────────┤
│ Total per cycle  │          │ ~1 hour  │ 0.237 mAh         │
│ Daily (24 cycles)│          │ 24 hours │ 5.7 mAh           │
├──────────────────┴──────────┴──────────┴───────────────────┤
│ Battery: 2000 mAh LiPo                                      │
│ Estimated Runtime: 2000 / 5.7 = 350 days                   │
│ With 80% usable capacity: 280 days                         │
└─────────────────────────────────────────────────────────────┘

Production Power Considerations

Development vs Production Current

ComponentDevKit CurrentProduction Module
USB-UART chip15-20 mANot present
Power LED2-5 mARemove or change
LDO quiescent5-10 mA1-5 µA (better LDO)
Pull-ups/downsVariableOptimize values

Checklist for Low-Power Products

  1. Use bare module, not DevKit for final power measurements
  2. Choose low-quiescent LDO (HT7333, AP2112)
  3. Remove or control indicator LEDs
  4. Use high-value pull resistors (100kΩ+)
  5. Disable unused peripherals in code
  6. Optimize WiFi connection time (static IP, channel hints)
  7. Batch data transmissions instead of frequent small ones
  8. Use ESP32-C3 or H2 for lowest power WiFi/BLE

Next: Sensors and Peripherals →