ESP32-C3 Deep Sleep Current: What the Datasheet Doesn't Tell You About Your Dev Board
Hardware used in this build
- ESP32-C3 dev board
The shed node MCU. Runs the AI_optimized_shed_TX.ino sketch. Seeed's own board. - HC-12 433 MHz wireless module
HiLetgo HC-12 433 MHz SI4438 module with antenna. Reliable for 100–1 000 m links. - INA219
Adafruit INA219 High Side DC Current Sensor Breakout. ±26 V, ±3.2 A, I2C. The one I use. - Solar panel + battery
The ESP32-C3 datasheet specifies a deep-sleep current of approximately 5–7 µA with the RTC timer on (ESP32-C3 Technical Reference Manual, Section 3.5.3). On my Seeed Studio XIAO ESP32C3, that number is useless. My node is part of a 100-yard shed link running off a 12 V solar panel and a tractor battery, connected to the house over an HC-12 433 MHz serial radio, and the XIAO’s on-board components keep the MCU from ever approaching the silicon floor. This post breaks down exactly where those milliamps go, names the guilty parts, and explains how to isolate the leaks on your own bench.
What the datasheet actually specifies
The ESP32-C3 Technical Reference Manual (TRM) gives chip-level current figures for the VDD3P3_RTC_0 and VDD3P3_DIG_0 pins under Espressif’s own test conditions:
| Sleep mode | Typical current |
|---|---|
| Deep sleep, RTC on | ~5–7 µA |
| Deep sleep, RTC off | ~1 µA |
| Modem sleep | ~15 mA |
| Active, CPU @ 160 MHz | ~30 mA |
Those figures are the silicon floor. They assume the chip alone, minimal peripherals, specific temperature and voltage corners. They are not a dev-board spec. Community measurements on bare ESP32-C3 modules corroborate the sub-10 µA range when the RTC domain is the only thing alive.
What your dev board adds
My shed node uses a Seeed Studio XIAO ESP32C3. The XIAO form factor adds several current paths that do not exist on the die. I identified four major ones by tracing the 3.3 V rail.
1. On-board LDO
The XIAO ESP32C3 uses a Torex XC6210 fixed 3.3 V LDO. The XC6210 datasheet specifies a quiescent current of 30–45 µA typical depending on load and temperature. That is already five to nine times the chip’s own deep-sleep spec before you attach a sensor or radio. The LDO draws this current simply to keep itself regulated — it does not care that the ESP32-C3 is asleep.
2. USB-to-serial bridge
The CH340C on the XIAO is always powered when USB is connected. Typical idle current for the CH340C is in the 1–2 mA range, with peaks up to ~8 mA under load when VCC is present and no serial communication is active. Disconnect USB and that path goes away — but then you cannot program the board without desoldering or reconnecting.
3. Power LED
Wired straight to the 3.3 V rail through a current-limiting resistor. A standard SMD green or red LED at 2.2 V forward drop across 3.3 V draws roughly 2–3 mA. It exists to tell you the board is alive. It also tells your battery the board is alive.
4. Peripherals you left floating
The DHT22 data pin, the HC-12 UART lines, the I2C bus — every GPIO that is not explicitly tri-stated or powered down can keep a peripheral in a partial-active state. The ESP32-C3 keeps GPIO state alive in deep sleep by default. A floating pin can keep a sensor biased.
A comparison of typical configurations:
| Configuration | Estimated deep-sleep current | Source |
|---|---|---|
| Bare ESP32-C3 module, RTC on, no peripherals | ~5–7 µA | |
| XIAO ESP32C3 stock, USB disconnected | ~50–200 µA | LDO + LED + bridge leakage (XC6210/CH340C datasheets) |
| XIAO + DHT22 + HC-12, USB disconnected | Depends on GPIO state and HC-12 power mode | Measure your own node |
| Stock dev board with USB connected | 5–15 mA | CH340C dominates (datasheet + community) |
The jump from ~7 µA to ~200 µA is the difference between a 18650 lasting 16 years and one lasting 7 months. At 10 mA — the stock XIAO with the USB bridge active — a 2,500 mAh 18650 is flat in 10 days.
How to measure it yourself
You need a shunt resistor and a differential ADC, or a multimeter with a µA range that does not burden the circuit. The burden voltage on a cheap meter’s µA range can be hundreds of millivolts — enough to collapse the ESP32-C3’s supply and force it out of deep sleep, which will give you a number that is too high.
The INA219 on the shed’s solar bus is useful for the whole-node budget, not for isolating the ESP32-C3. The INA219’s current-sense resolution depends on its programmable gain array (PGA) and calibration. In practice the smallest reliably resolvable step with a 0.01 Ω shunt is on the order of 0.1 mA (100 µA) with standard calibration, not the µA scale. Use it only to confirm you are in the right ballpark; for actual µA-scale numbers, use a dedicated low-offset shunt amplifier or a calibrated DMM on its µA range. If your meter reads unstable on µA, try mA — a jump means your meter burden was collapsing the node’s supply.
Battery 3.3V → shunt (0.01 Ω, 1 %) → [INA219](https://www.amazon.com/dp/B09CBSLXN7?tag=kayparpart-20) VIN+ → [INA219](https://www.amazon.com/dp/B09CBSLXN7?tag=kayparpart-20) VIN- → XIAO 3V3 pin
[INA219](https://www.amazon.com/dp/B09CBSLXN7?tag=kayparpart-20) GND → XIAO GND
A 0.01 Ω shunt gives 1 µV per µA. The INA219 cannot resolve that. Use it only to confirm you are in the right ballpark; for actual µA-scale numbers, use a dedicated low-offset shunt amplifier or a calibrated DMM on its µA range. If your meter reads unstable on µA, try mA — a jump means your meter burden was collapsing the node’s supply.
Safety note on the INA219: The INA219 is a high-side shunt monitor rated for up to 26 V common-mode. The shed’s solar rail can exceed 12 V under charge, and inductive spikes from the HC-12 or relay can push it higher. Keep the INA219 powered from the 3.3 V rail, not from the raw solar input, and respect the common-mode limit. If you move the shunt to the battery side, add transient suppression.
The deep-sleep sketch
The firmware running on the shed node uses esp_deep_sleep_start() after a single sensor read and radio burst:
#include <Arduino.h>
#include <Wire.h>
#include <DHT.h>
#define DHTPIN 0
#define DHTTYPE DHT22
#define HC12_RX 20
#define HC12_TX 21
#define SLEEP_US 900000000ULL // 15 minutes
DHT dht(DHTPIN, DHTTYPE);
void setup() {
Serial1.begin(9600, SERIAL_8N1, HC12_RX, HC12_TX);
dht.begin();
float h = dht.readHumidity();
float t = dht.readTemperature();
// Frame: <NODE_ID>,<SENSOR>,<VALUE>,<CRC8>
Serial1.printf("SHD,%0.1f,%0.1f,0\n", h, t);
Serial1.flush();
// Power down the GPIO domain explicitly.
// Leaving VDDSLIO alive keeps pins biased.
esp_sleep_pd_config(ESP_PD_DOMAIN_VDDSLIO, ESP_PD_OPTION_OFF);
esp_sleep_enable_timer_wakeup(SLEEP_US);
esp_deep_sleep_start();
}
void loop() {
// never reached
}
The esp_sleep_pd_config(ESP_PD_DOMAIN_VDDSLIO, ESP_PD_OPTION_OFF) line matters. By default the ESP32-C3 keeps GPIO state alive in deep sleep. Every pin that floats or drives a peripheral keeps that peripheral biased. On my node the difference is measurable but not yet quantified with a µA-class instrument.
Battery life math
This is where the measured number becomes load-bearing. The arithmetic is simple:
battery_life_hours = battery_capacity_mAh × 1000 / measured_sleep_current_µA
For an 18650 Li-ion (3,000 mAh nominal, ~2,400 mAh usable down to 20 %):
- At 7 µA (bare chip): ~140,000 hours ≈ 16 years. Self-discharge (~1 %/month ≈ 40 µA continuous) dominates.
- At 200 µA (optimised XIAO, USB disconnected): ~5,000 hours ≈ 208 days.
- At 10 mA (stock XIAO with USB bridge active): ~96 hours. Four days.
My shed node runs from a 12 V solar panel buffered by a tractor battery, with an INA219 logging bus voltage and power. The node’s steady-state idle current after all peripherals have settled is the residual between the HC-12 transmit burst and the DHT22 read cycle, measured from the battery side with the INA219 in high-resolution mode. At a 15-minute wake interval, runtime is calculated from your current battery capacity divided by that measured sleep current. The INA219 sees the HC-12’s transmit bursts plus the DHT22 read cycle. The deep-sleep baseline is the floor between bursts, and it is the only number that matters for long-term battery projection.
What went wrong
HC-12 TX burst brown-out. The HC-12 draws up to ~100 mA peak during transmit at AT+P8. My first LDO could not hold 3.3 V under that load. The ESP32-C3’s brown-out detector fired, the node reset, the sketch retransmitted, and the brown-out fired again. A 470 µF electrolytic across the 3.3 V rail ended a week of “random” resets. The lesson: your deep-sleep current is only half the story. Your peak current during sensor read + radio TX is what selects your capacitor and regulator.
Floating GPIO keeping peripherals alive. I left the DHT22 data pin floating when the ESP32-C3 entered deep sleep. The sensor’s internal pull-up was enough to keep it from sleeping fully. Explicitly powering down the DHT22 via a GPIO-controlled P-channel MOSFET removed that current. Without the MOSFET, the node’s idle current was measurably higher: the delta is the difference between the DHT22 powered and left floating during sleep, measured with the same shunt setup.
Forgetting the LED. The XIAO’s power LED is always on when USB is connected. If you ever plug the node into USB for debugging and forget to disconnect it before going off-grid, you just turned your 200 µA node into a 10 mA node. I logged the node current with USB connected but idle and compared it to USB disconnected; the difference is the LED plus any bridge leakage on the 3.3 V rail.
Auto-sleep assumption. The HC-12 does not sleep automatically. AT+DLOW reduces the module’s own draw, but the UART lines must still be driven. If the ESP32-C3 enters deep sleep while UART is active, the TX pin can hold the HC-12 in a partial-active state. Disconnect or tri-state the UART before sleeping, or use a GPIO to cut power to the HC-12 entirely.
The honest version
I do not have a single clean measurement for you yet. My shed node is in a wooden box on a 12 V solar rail with an INA219 logging bus voltage and power, and the ESP32-C3’s contribution is the residual after the HC-12 transmit burst and the DHT22 read cycle settles. That residual is measured from the battery side with the INA219 in high-resolution mode after all peripherals have gone idle.
What I do have is a method that isolates the variables:
- Shunt at the regulator output for the ESP32-C3 alone.
- Shunt at the battery for the whole node.
- Delta between idle and deep-sleep gives you the peripheral leakage.
- Datasheet for the chip itself (5–7 µA with RTC on, ESP32-C3 Technical Reference Manual Section 3.5.3).
Measure your own board. Report your own numbers. The 7 µA figure is real, but it belongs to the silicon die, not the plastic blob you bought on Amazon.
Related reading
- HC-12 vs WiFi for Remote Sensor Nodes: What 433 MHz Actually Buys You
Why I moved my outdoor ESP32-C3 sensor nodes off WiFi and onto HC-12 433 MHz serial radio, the settings that mattered, and the failure modes nobody warns you about.
- SQLite Rollups and Retention: Stop Your Sensor DB From Eating the SD Card
How rollup tables and a retention policy keep a Raspberry Pi sensor SQLite database from growing unbounded — and why SD-card wear is rarely the real problem.
- CT Clamp Burden Resistor Calibration for ADS1115: Avoiding Voltage Drift in Home Sensor Projects
A practical guide to calibrating CT clamp sensors with ADS1115 ADC, covering burden resistor selection, capacitor stabilization, and common failure modes in home sensor projects.