ESP32 battery voltage monitoring needs only two resistors and one ADC pin, yet it is one of the most common sources of wrong readings in hobby projects. The ESP32 ADC accepts up to about 3.1–3.3 V, a Li-ion cell reaches 4.2 V, and a 12 V battery is far above that. A voltage divider scales the battery down to a safe range. This guide shows how to choose the resistors, how to keep the divider from draining the battery, and how to get accurate readings from the ESP32’s nonlinear ADC.
The voltage divider formula
Vout = Vin × R2 / (R1 + R2)
R1 goes from the battery to the ADC pin, R2 from the ADC pin to ground. Choose the ratio so that the maximum battery voltage gives no more than about 2.5–3.0 V at the pin, leaving headroom.
| Battery | Max voltage | R1 / R2 example | Max at ADC |
|---|---|---|---|
| 1S Li-ion / LiPo | 4.2 V | 100 kΩ / 100 kΩ | 2.10 V |
| 2S Li-ion | 8.4 V | 220 kΩ / 68 kΩ | 1.98 V |
| 12 V lead-acid | 14.4 V | 330 kΩ / 68 kΩ | 2.46 V |
| 3S LiPo | 12.6 V | 330 kΩ / 82 kΩ | 2.51 V |
| 24 V system | 28.8 V | 680 kΩ / 68 kΩ | 2.62 V |
Use our voltage divider calculator to try other values; it also accounts for the load and picks standard E24/E96 resistors.
Do not drain the battery
A 10 kΩ + 10 kΩ divider on a 4.2 V cell draws 210 µA continuously — over 5 mAh per day, which matters for a device that should sleep for months. With 100 kΩ + 100 kΩ the current drops to 21 µA. Go higher (1 MΩ) and the ADC’s input sampling causes errors. Solutions:
- add a 100 nF capacitor from the ADC pin to ground so the high-impedance divider can charge the sampling capacitor;
- or switch the divider with a MOSFET and power it only during the measurement.
Calibrated readings on ESP32
The ESP32 ADC is nonlinear near 0 V and near the top of its range, and each chip differs. Two rules give good results:
- Use attenuation ADC_11db (called ADC_ATTEN_DB_12 in newer IDF versions) for the 0–2.5+ V range.
- Read calibrated millivolts, not raw counts. In Arduino-ESP32 use
analogReadMilliVolts(); in ESP-IDF use theadc_caliAPI.
const float R1 = 100000.0, R2 = 100000.0;
uint32_t mv = analogReadMilliVolts(BAT_PIN); // calibrated, mV at the pin
float vbat = mv / 1000.0 * (R1 + R2) / R2; // battery voltage
Average 16–64 samples to remove noise, and use ADC1 pins only — ADC2 cannot be used while Wi-Fi is on.
From voltage to percent
Li-ion voltage is not linear with charge. A simple lookup table works far better than a straight line: about 4.2 V = 100%, 3.9 V ≈ 75%, 3.75 V ≈ 50%, 3.6 V ≈ 25%, 3.3 V ≈ 0% under light load. Measure under your real load, because voltage sags while the radio transmits.
Protect the ADC pin
If the battery can be connected while the ESP32 is unpowered, add a series resistor (or rely on a large R1) so the pin’s protection diode is never overloaded. For automotive 12 V systems add a TVS diode or a Zener clamp, since load-dump spikes can exceed 40 V.
Pick divider resistors in seconds
Enter the battery voltage and the ADC range — the free calculator finds standard resistor values, current draw and dissipated power.
Open the voltage divider calculatorFAQ
Can I connect a 1S Li-ion directly to the ESP32 ADC?
No. 4.2 V exceeds the ADC range and can damage the pin. Use at least a 1:2 divider.
Why is my reading 0.1–0.2 V off?
Uncalibrated analogRead() and the ADC’s nonlinearity. Switch to analogReadMilliVolts(), average several samples and calibrate against a multimeter once.
Which pins should I use?
ADC1 pins (GPIO32–39 on the classic ESP32, GPIO1–10 on ESP32-S3). ADC2 is shared with Wi-Fi.
Sources: Espressif: ESP32 ADC.
