A year on one 18650: ESP32 battery life is a firmware decision

Wrong question, common answer

Every battery-powered ESP32 thread eventually gets the same reply: “just use a bigger battery.” It’s the wrong lever. Whether a sensor lasts six weeks or sixteen months on one charge is decided almost entirely in firmware and board choice — the battery is the last variable you touch, not the first. A recent roundup of ESP32 builds that run a year on batteries got me doing the arithmetic behind my own shelf sensors, and the numbers are more instructive than the project list.

The one number that rules them all

Take a standard 18650 cell at roughly 3,000 mAh. A year is 8,760 hours. Divide and your entire power budget is about 340 µA of average draw. Not peak — average, sleep included, always. Every design decision from here is just negotiating against that number.

Now the uncomfortable part: a typical dev board can’t meet it. The bare ESP32-C3 module sips a couple of dozen microamps in deep sleep, but the devkit wrapped around it adds a power LED, a USB-to-serial chip, and a cheap LDO with sloppy quiescent current. Perfectly good boards sleep at 1–2 mA because of that overhead — which is a dead cell in about two months while your firmware is doing absolutely nothing. Your code can be flawless and the board still loses.

So rule one: for battery builds, use a bare module or a battery-oriented board, or get out the soldering iron and lift the LED and regulator. Rule two is where the real leverage lives:

Your radio-time is your battery bill

Deep sleep is nearly free. Awake time is expensive, and radio time is the most expensive of all. Compare two honest duty cycles:

  • The event-driven doorbell. Button wired to an RTC GPIO, ESP-NOW instead of Wi-Fi: wake, send one packet, sleep — under 100 ms per ring. Ten rings a day is one second of radio time daily. Averaged out, that’s under 2 µA. The battery’s own self-discharge dwarfs your firmware. This build runs until the cell ages out.
  • The naïve temperature sensor. Publish over Wi-Fi every five minutes, and every cycle pays the full association tax: join, DHCP, MQTT connect, publish, tear-down — call it four seconds awake at ~120 mA. That’s a ~1.6 mA average, and the same cell dies in under three months despite “sleeping” 98% of the time.

Same battery, same chip, roughly a 40× difference in life, and not one milliamp of it was decided by hardware. The fixes are boring and effective: sample cheaply and batch readings in RTC memory (which survives deep sleep), keep the Wi-Fi connection parameters warm instead of renegotiating every wake, transmit a UDP burst every fifteen minutes instead of MQTT every five — or get the radio off Wi-Fi entirely.

Pick the protocol like it costs money, because it does

This is the part listicles gloss over. There’s a ladder, and each rung buys you an order of magnitude:

  • Wi-Fi — most flexible, most expensive. Justified only when the device must speak to infrastructure directly and rarely.
  • ESP-NOW — connectionless peer-to-peer between Espressif chips. Send-and-sleep in under 100 ms. Costs you one always-powered gateway board near your router; every battery node after that is nearly free to run. This is the right default inside a house.
  • LoRa — when the distance is real: paddocks, sheds, the letterbox three houses of signal away. Low bitrate, but a soil sensor reporting twice a day will comfortably run a year on one cell.

The minimum viable firmware is smaller than you’d think:

// event node: wake on reed switch, report, die quietly
RTC_DATA_ATTR int opens = 0;                 // survives deep sleep
esp_sleep_enable_ext0_wakeup(REED_PIN, 0);   // magnet away = low
send_espnow_broadcast(opens);                // <100 ms of radio
esp_deep_sleep_start();

What a year-on-battery build actually looks like

The patterns that earn their keep, all of which appear in the roundup that started this:

  • Door/mailbox sensors — magnetic reed switch on an RTC GPIO, event-driven wake, report to Home Assistant, sleep. No cable run to a letterbox ever again.
  • Batching temperature nodes — sample locally, transmit on a schedule. RTC memory means you never rebuild state after a wake.
  • E-paper dashboards — e-paper only draws power while refreshing, so a forecast card updated three times a day is nearly free. The moment you want hourly updates or colour, the budget changes species.
  • Soil sensors — two readings a day, a MOSFET cutting power to the sensor between them (sensors that “idle” at a milliamp are silent budget killers), LoRa backhaul.

Know when not to bother

Engineering hours aren’t free either. If there’s an outlet within cable reach, USB power is cheaper than a power-budget optimisation campaign. The year-on-battery discipline pays off precisely where wiring can’t go: letterboxes, door frames, ceilings, garden beds, the wall you don’t own. Those are the builds worth doing the duty-cycle arithmetic for.

And do the arithmetic before the build, not after — a five-minute estimate of average draw against 340 µA tells you whether the project is a battery project or a USB project. That check has saved me more abandoned builds than any component purchase ever has. Most “it died after a month” ESP32 posts weren’t hardware failures; they were firmware that never met its own power budget.

Leave a Reply

Your email address will not be published. Required fields are marked *

WordPress Appliance - Powered by TurnKey Linux