Articles / Embedded

  • esp32
  • esp8266
  • embedded
  • low-power
  • battery
  • iot

ESP8266 vs ESP32 on Battery: Why the Older Chip Wins at Idle

My eight-year-old ESP8266 design draws less current asleep than the ESP32 I built to replace it. Measured with a shunt resistor and a scope, the ESP32 build was pulling noticeably more at rest than the old chip sitting next to it.

The original is a temperature and humidity sensor on an ESP-12F (bare ESP8266 module, no dev board regulator to worry about). It has been running on a single 18650 cell for the best part of two years, reporting every fifteen minutes.

When I built a second one for another room, I reached for an ESP32. The assumption seemed entirely reasonable: a newer chip on a similar process node, with more sophisticated power management, should do at least as well. It did not.

The datasheet numbers say the ESP32 should win. In practice, for a "wake, read a sensor, send a packet, sleep for a long time" workload, the ESP8266 is often the better chip. The reason is instructive: it isn't that the ESP32 is worse, it's that it gives you far more ways to get it wrong.

The ESP8266 has one sleep mode and nothing to forget

The ESP8266 has one deep sleep mode and no meaningful knobs. When it sleeps, everything shuts off except a single RTC domain: the timer that wakes the chip and 512 bytes of RTC user memory that survive the sleep. That's it.

There's nothing to configure and nothing to forget to configure, so you get the same figure every time. Commonly quoted, and consistent with what I measure, is somewhere around 20 microamps.

The one hardware gotcha is that GPIO16 has to be wired to RST for a timer-based wake to work at all. Most dev boards (NodeMCU, Wemos D1 mini) do this for you; bare modules like the ESP-12F do not.

The ESP32 has a lower floor, if you switch off every domain

The ESP32 splits its RTC section into several independently switchable power domains:

  • RTC_PERIPH (touch, ULP, RTC GPIO)
  • RTC_SLOW_MEM
  • RTC_FAST_MEM
  • the crystal oscillator itself

Each one can be powered down individually with esp_sleep_pd_config(). Turn off everything that isn't needed for your wake source and the chip really can get down into the low tens of microamps or better, comfortably beating the ESP8266 on paper.

The catch is that "if you turn everything off" is doing a lot of work in that sentence. Leave RTC_SLOW_MEM powered because you wanted a couple of RTC_DATA_ATTR variables to survive the sleep, and you've added a domain's worth of leakage.

Worse, use the default Arduino-ESP32 deepSleep() wrapper without touching pd_config at all. Depending on the framework version and which wake sources you've enabled, you can easily end up an order of magnitude above the ESP8266's baseline. Nothing tells you anything is wrong, because the board still works; it just eats the battery faster.

This is really a story about surface area. The ESP8266 has almost no configuration space for this problem, so there's nothing to misconfigure. The ESP32 has a genuinely lower floor, but reaching it takes deliberate, correct configuration, and most example code and most tutorials don't bother.

Getting the ESP32 to its floor means naming every domain

If you actually want the ESP32 to compete, you need to be explicit about every domain, and think about what you're willing to give up to get there:

#include "esp_sleep.h"

void enter_low_power_sleep(uint64_t sleep_us) {
    // Turn off everything not required to wake on a timer.
    // If you use RTC_DATA_ATTR variables, RTC_SLOW_MEM has to
    // stay on, which costs you some of this saving back.
    esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH,  ESP_PD_OPTION_OFF);
    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);

    esp_sleep_enable_timer_wakeup(sleep_us);
    esp_deep_sleep_start();
}

Compare that with the entirety of the ESP8266 equivalent:

void setup() {
  // GPIO16 must be wired to RST for this to actually wake the chip
  ESP.deepSleep(15ULL * 60 * 1000000);
}

There's no version of the ESP8266 code that accidentally leaves a power domain on, because there's no domain to leave on. That's a real advantage for a battery sensor project you're going to set and forget for a year.

Sleep current dominates the budget, but the wake-up still counts

For a sensor that sleeps 99.7% of the time, average current is dominated by the sleep figure, not the active burst. The active period is worth being honest about too, though. It's where the ESP32's extra capability (dual core, Bluetooth stack, more RF front end) shows up as extra draw. Roughly:

avg_current = (t_sleep * I_sleep + t_active * I_active) / T_total

Take a 15 minute report interval, with a couple of seconds active for boot, Wi-Fi association and a short POST. Plugging in typical figures gives:

  • ESP8266: 20uA sleep and roughly 70-80mA active comes out around 180-190uA average.
  • Carelessly configured ESP32: 100-150uA sleep, with a slightly longer, slightly higher active burst (more time to bring up two cores and calibrate RF, typically 60-90mA higher), can easily land at 400-500uA average. That's more than double.
  • Carefully configured ESP32: hitting its low tens-of-microamps floor claws most of that back, and can even come out ahead on the sleep term alone.

Even the careful ESP32 still pays more on the active term than the ESP8266, because there's simply more silicon to bring up and settle every time it wakes.

Quick detour on those numbers: none of them are datasheet-precision figures. They're the kind you'll actually measure on real boards with real regulators, and they'll shift with board choice, firmware version and how tightly you trim the active-time code path.

The shape of the result is consistent, though. The ESP8266 gets you into a good place by default; the ESP32 gets you into a good place if you do the work.

When each chip is the right call

If the whole job is wake, read one or two sensors over I2C, send a small payload, sleep for minutes or hours, the ESP8266 is still a defensible, slightly boring choice. You get:

  • cheaper modules
  • a smaller and better-understood power model
  • a sleep current that doesn't depend on you remembering four function calls

The ESP32 earns its keep when you actually need what it adds:

  • Bluetooth alongside Wi-Fi
  • more ADC channels or capacitive touch wake sources
  • hardware crypto for a TLS handshake that would otherwise be painfully slow on the ESP8266's single core
  • genuinely needing the ULP coprocessor to do sensor polling without waking the main CPU at all

This only covers the original ESP32

This is scoped to the original ESP32 (the Xtensa LX6, WROOM-32 style part). Espressif's newer parts, the RISC-V C3 and C6 and the Xtensa LX7 S3, have different power architectures. I haven't measured them closely enough to make the same claim either way, so don't assume this generalises across the whole product line.

For the classic ESP32 sitting next to an ESP8266 on a breadboard, though, "newer" and "lower power" turned out not to be the same thing.