ESP8266 vs ESP32 for a Battery Sensor: Why the Older, Slower Chip Still Wins on Idle Current
I have a temperature and humidity sensor on an ESP-12F (bare ESP8266 module, no dev board regulator to worry about) that 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 instead, on the entirely reasonable assumption that a newer chip on a similar process node with more sophisticated power management would do at least as well. It did not. Measured with a shunt resistor and a scope, the ESP32 build was pulling noticeably more current at rest than the eight-year-old design sitting next to it.
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, and 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.
What "deep sleep" actually powers down
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 splits its RTC section into several independently switchable power domains: RTC_PERIPH (touch, ULP, RTC GPIO), RTC_SLOW_MEM, RTC_FAST_MEM, and the crystal oscillator itself. Each one can be powered down individually with esp_sleep_pd_config(), and if you turn everything off that isn't needed for your wake source, 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. Use the default Arduino-ESP32 deepSleep() wrapper without touching pd_config at all, and 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 without any indication that 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 down to its floor
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.
Idle current isn't the whole budget, but it's most of it
For a sensor that sleeps 99.7% of the time, average current is dominated by the sleep figure, not the active burst, but the active period is worth being honest about too, because 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
For a 15 minute report interval, with a couple of seconds active for boot, Wi-Fi association and a short POST, an ESP8266 sitting at 20uA sleep and roughly 70-80mA active comes out around 180-190uA average. A carelessly configured ESP32 sitting at 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, more than double. A carefully configured ESP32 hitting its low tens-of-microamps floor can claw most of that back and even come out ahead on the sleep term alone, but the active term still costs more than the ESP8266's, because there's simply more silicon to bring up and settle every time it wakes.
None of these are datasheet-precision figures, they're the kind of numbers 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. But the shape of the result is consistent: the ESP8266 gets you into a good place by default, the ESP32 gets you into a good place if you do the work.
Where this actually matters
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: cheaper modules, a smaller and better-understood power model, and 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, or genuinely needing the ULP coprocessor to do sensor polling without waking the main CPU at all.
This is scoped to the original ESP32 (the Xtensa LX6, WROOM-32 style part). Espressif's newer RISC-V variants, the C3, C6 and the S3, have different power architectures and 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.