Symptom
A voltage divider or battery monitor reads noticeably low in the middle of the range, can't distinguish small voltages near 0 V, and saturates at 4095 before the input reaches 3.3 V. Two boards give different readings for the same voltage.
Why
- The ESP32 ADC front end is non-linear. An Espressif team member confirmed this in the long-running arduino-esp32 issue about inconsistent
analogRead()values. - The ADC reference is nominally 1100 mV but varies between 1000 and 1200 mV from chip to chip.
- Each attenuation setting has a limited usable input range; outside it, readings flatten out.
Fix
-
Read calibrated millivolts, not raw counts:
analogSetPinAttenuation(PIN, ADC_11db); // pick the range for your signal uint32_t mv = analogReadMilliVolts(PIN); // calibratedIn ESP-IDF, use the ADC calibration driver (
adc_cali_raw_to_voltage()), which uses the calibration values stored in eFuse. -
Keep the signal inside the attenuation's range (ESP32):
Attenuation Usable input 0 dB ~100–950 mV 2.5 dB ~100–1250 mV 6 dB ~150–1750 mV 11 dB ~150–3100 mV Scale larger voltages (battery, 5 V sensors) with a divider so they land inside the range, not at its edges.
-
Reduce noise: average several samples, and add a small capacitor at the ADC pin when the source impedance is high (large dividers).
-
Remember ADC2: on the classic ESP32, ADC2 pins can't be used while Wi-Fi is active; use ADC1 pins for anything that must work with Wi-Fi on.
-
Need precision? Calibrate against a multimeter on your own board, or use an external ADC.