Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124

There’s no shortage of ESP32 boards on the market, but very few are built with power efficiency in mind. Even boards that advertise “deep sleep support” tend to draw hundreds of microamps when idle, fine for short-term battery projects, but completely unsuitable for multi-year operation.
This project began with a simple challenge:
Could I design and build a fully functional ESP32-S3 board capable of running for years on a single lithium cell, while still performing real work?
The answer, surprisingly, is yes. After a few iterations, measurements, and a lot of head-scratching over sleep currents, I’ve managed to bring the total average draw down to around 27 µA, translating to roughly four years of operation on a 3000 mAh 18650 Li-ion battery.
This post covers everything: the design, component choices, firmware logic, measurement methodology, and test results. I’ve included a detailed breakdown of power consumption and some reflections on what worked (and what didn’t).
(All design files, firmware, and data logging tools are available on my GitHub, see links at the end.)
Like many, I’ve used ESP32 modules for everything from home automation to robotics. But nearly every off-the-shelf board suffers from the same fatal flaw: they’re built for convenience, not efficiency.
Here’s what typically kills battery life:
I wanted to strip away everything non-essential and rebuild the platform from the ground up, keeping all the ESP32’s capabilities, but minimising waste wherever possible.
I wanted a platform that could:
The heart of the board is the ESP32-S3-WROOM-1 module, chosen for its low sleep current and solid Wi-Fi performance. It’s mounted on a custom PCB designed in EasyEDA, which integrates power management, charging, battery measurement, and I/O headers.
Key Specs
| Subsystem | Component | Key Features |
|---|---|---|
| MCU | ESP32-S3-WROOM-1 | Dual-core, 240 MHz, Wi-Fi + BLE, deep sleep <10 µA |
| Regulator | TPS63802DLAT | Buck-boost converter, 11 µA Iq |
| Charger | MCP73831 | Linear Li-ion charger (up to 500 mA charge rate) |
| Battery Monitor | Resistor divider (510 kΩ / 510 kΩ) | Reads via ADC on GPIO8 |
| Indicator | None! | PCB has space for a charging LED, but unpopulated in video |
| Connectors | 2.54 mm header rows | Compatible with standard breadboards |
| Sensors and buttons | UART, ADC and I2C pins broken out, 2 general purpose push buttons. | Pull-ups disabled in sleep mode |
Power design was the defining part of this project. I spent far more time here than on any firmware.
The TPS63802 from Texas Instruments is a true gem. It’s a synchronous buck-boost converter capable of handling inputs from 1.8 V to 5.5 V, outputting a regulated 3.3 V even as the Li-ion battery discharges below 3 V. The beauty is its 11 µA quiescent current, an order of magnitude lower than most off-the-shelf LDOs.
The alternative would have been a pure LDO (like the MCP1700), which has a slightly lower Iq, but then efficiency collapses when the input voltage drops. The buck-boost maintains >90% efficiency across the usable range, vital when chasing multi-year life.
As shown in Figure 1, The feedback divider sets the output voltage:
This combination minimises leakage through the divider (<5 µA total) while still keeping feedback noise within spec.

Figure 1: Schematic of low power ESP32 S3 development board.
Measuring battery voltage in low-power systems is tricky because any permanent divider wastes current. My divider uses 510 kΩ (high side – R14) and 510 kΩ (low side – R15), labelled erroneously as 5.1k in Fig 1. The divider draws only a few µA when enabled.
When the ESP32 enters deep sleep, the ADC pin is switched to high-impedance, cutting that path off. In future revisions I might add a P-channel MOSFET or analog switch to physically isolate the divider, but for now the draw is small enough not to matter.
The charger operates from the USB-C port and charges at 500 mA by default (configurable via a resistor). One subtle but important choice: I routed the output so that the MCP73831 is completely isolated when USB is disconnected, meaning its internal leakage paths don’t drain the cell during battery-only operation. Many cheap boards miss this.
Firmware is written in Arduino for portability and ease of flashing. Two variants exist:
Each client periodically wakes from deep sleep, reads temperature, humidity, and pressure from a BMP280, and stores the results in a circular buffer. Every tenth wake-up (50 minutes), it transmits the accumulated ten readings via ESP-NOW.
Pseudocode:
loop:
read BMP280
store reading in buffer
if (cycle_count % 10 == 0):
send all 10 readings via ESP-NOW
sleep(5 minutes)
This drastically reduces Wi-Fi-on time while maintaining time resolution.
During a sensor read, the board consumes ~0.85 mA averaged over 10 seconds. On the tenth sensor read, all ten readings are sent via ESP-NOW, consuming 7.56 mA over ten seconds. Otherwise, the board spends 99.99% of its life in deep sleep, consuming just 27 µA.
Achieving a reliable deep sleep took some iteration. I think there are still some gains to be had by adjusting the potential divider values and looking into the pull-up resistors on the sensor board.
Measured deep sleep current: 27.4 µA, matching theoretical estimates:
All unnecessary peripherals, brown-out detection, and flash caches are disabled.
ESP-NOW is ideal for this type of network: no routers, no DHCP, just peer-to-peer 2.4 GHz packets. I locked all devices to Wi-Fi Channel 11, matching my home network but avoiding association with it.
Each packet includes:
A typical packet is <60 bytes, trivial for ESP-NOW.
The base station is another ESP32-S3 connected to the same Wi-Fi channel. It receives ESP-NOW packets from multiple nodes, timestamps them, and serves a live-updating web dashboard.
The web interface uses plain HTML + JavaScript + Chart.js for plotting temperature, pressure, and battery voltage in real time. Each node’s data is stored in a circular buffer for one week. When full, the oldest data points are discarded automatically.
You can clear the buffer via a simple “Clear” button on the page.
The dashboard looks and feels like a lightweight IoT portal but runs entirely from the ESP32’s internal web server. No cloud, no dependencies.
Each ESP-NOW node includes its unique 32-bit UID in every packet. The base dynamically assigns a color to each node’s plot (blue, red, green, orange, etc.) so new sensors appear automatically without reconfiguration.
To validate the design, I measured current consumption under all modes using the same battery and a Nordic semiconductor Power Profiler Kit II data logger.
Measurements were averaged over 10-second windows for consistency.
| Mode | Duration | Avg Current | Charge Used |
|---|---|---|---|
| Read only | 10 s | 0.849 mA | 8.49 mC |
| Read + Transmit | 10 s | 7.56 mA | 75.6 mC |
| Deep Sleep | 2900 s | 27.42 µA | 79.5 mC |
| Cycle Total | 3000 s (50 min) | 0.077 mA avg | 0.2315 C |
Battery capacity (derated to 85% of 3000 mAh) = 2550 mAh = 9,180 C
Life (s) = 9180 / 0.0000772 = 118.9×106 s
≈ 33,000 hours, or 1,377 days, i.e. 3.77 years
With realistic variations and a slightly higher usable fraction (~90%), it’s fair to call this a 4-year battery life!
To put the design into perspective, I decided to compare it against one of the most recent commercial options available: the ESP32-C6 Super Mini.
It’s a compact, mass-produced board based on Espressif’s newer RISC-V core, designed to be efficient, inexpensive, and easy to integrate. On paper, it’s the natural rival to my custom ESP32-S3 board.
But as always, the real test comes from the measurements.
Hardware Overview
| Feature | ESP32-S3 Custom Board | ESP32-C6 Super Mini |
|---|---|---|
| MCU Core | Dual-core Xtensa LX7 (240 MHz) | Single-core RISC-V (160 MHz) |
| Radio | Wi-Fi 4 (2.4 GHz) + BLE 5 | Wi-Fi 6 (2.4 GHz) + BLE 5 |
| Flash / PSRAM | 8 MB / none | 4 MB / none |
| Voltage Regulator | TPS63802 buck-boost, 11 µA Iq | Generic AMS1117-type LDO, ~100–200 µA Iq |
| Charging | MCP73831 Li-ion | Unknown |
| Battery Measurement | Precision 510 kΩ / 510 kΩ divider on GPIO8 | None on-board |
| Indicator | Unsoldered | Neopixel |
| Board Layout | 22 general purpose I/O broken out | 15 general purpose I/O broken out |
| Power LED | None | Blink when battery disconnected (off during test) |
| Programming | USB | USB |
| Dimensions | 60 x 32 mm | 25 x 13 mm |
To keep things fair, I tested both boards under identical firmware conditions:
| Parameter | ESP32-S3 Custom Board | ESP32-C6 Super Mini |
|---|---|---|
| Sleep current | 27.4 µA | 278 µA |
| Sensor-read current (10 s avg) | 0.85 mA | 1.1 mA |
| TX current (10 s avg) | 7.56 mA | 7.28 mA |
| Avg current (50 min cycle) | 0.077 mA | 0.103 mA |
| 3000 mAh @ 85 % usable | 3.8 years | 2.8 years |
| 3000 mAh @ 90 % usable | 4.0 years | 3.0 years |
| Category | Winner |
|---|---|
| Size and simplicity | C6 Super Mini |
| Power efficiency | Custom S3 Board |
| Low-voltage tolerance | Custom S3 Board |
| Cost | C6 Super Mini |
| Thermal performance | Custom S3 Board |
| Expandability | Custom S3 Board |
If you’re building a quick IoT sensor, the C6 is hard to beat for price and convenience. But if you care about longevity, reliability, and serious battery life, the custom S3 is in a different league. Four years of unattended operation isn’t a dream anymore, it’s something you can build, measure, and verify.
All the files you need to make your own board can be found on my Github page.