ESP32/Pins and signals/14. Reading the ADC
Your chip
Your language
Pins and signals · 14 of 81

Reading the ADC

analogRead gives you a number between 0 and 4095, and turning that into volts is where everyone goes wrong. The range is not 0 to 3.3 V, the top is flat, the bottom is dead, and on the classic ESP32 half the pins stop working when Wi-Fi starts.

/esp32/reading-the-adc · arduino · S3

The three surprises, in one line

analogRead() with ADC_11db
usable 0–3.1 V
Attenuation
Voltage on the pin1.80 V
analogRead()
2204
If you assume 0–3.3 V
1.78 V
Error
-0.02 V
2204 counts at 1.80 V. The count only means a voltage once you know the attenuation: with ADC_11db full scale is 3.9 V, so the conversion is raw / 4095 × 3.9, not × 3.3. Better still, call analogReadMilliVolts(), which uses the calibration burnt into your particular chip at the factory.

ADC1 or ADC2

This one matters and it is invisible until it breaks.

  • ADC1 — GPIO 32–39 on the classic ESP32. Works whether the radio is on or off. Use these.
  • ADC2 — GPIO 0, 2, 4, 12–15, 25–27. Shares hardware with Wi-Fi. Once WiFi.begin() runs, reads from these return errors or zeros.

The S3, C3, C6 and P4 do not have this restriction, but the habit of putting analog sensors on ADC1 costs nothing and survives a change of chip.

When 12 bits is not enough

The ESP32's ADC is fine for a light sensor, a potentiometer or a battery divider. It is not fine for a load cell, a thermocouple, or anything where you care about the third digit. For those, an external ADC on I2C or SPI — an ADS1115 or an HX711 — is a two-pound part that solves the whole problem.

On your S3
ChipXtensa LX7 · 2 × 240 MHz
Board settingESP32S3 Dev Module
Default I2CSDA 8 · SCL 9
Watch out forThe port vanishes after upload

The code

Two things make readings usable - averaging away the noise, and using the factory calibration instead of dividing by 4095 yourself.

read_voltage.ino
const int PIN = 34;        // an ADC1 pin - works with Wi-Fi on

void setup() {
  Serial.begin(115200);
  analogSetPinAttenuation(PIN, ADC_11db);   // 0 to about 3.1 V
}

int readAveraged(int pin, int n = 16) {
  long sum = 0;
  for (int i = 0; i < n; i++) sum += analogReadMilliVolts(pin);
  return sum / n;
}

void loop() {
  Serial.printf("%d mV  raw=%d\n", readAveraged(PIN), analogRead(PIN));
  delay(500);
}

analogReadMilliVolts applies the calibration data burnt into your particular chip at the factory. It is more accurate than any formula you can write, and it is one function call.

When it does not work

Readings collapse to zero the moment Wi-Fi starts

You are on an ADC2 pin. On the classic ESP32 the second ADC block shares hardware with the radio, and the radio wins. Move the sensor to an ADC1 pin - GPIO 32 to 39. Later chips do not have this problem.

A full battery and an empty one read the same

The reading is clipping. Above the attenuation's full scale the line goes flat, so every voltage returns the same number. Use a divider that puts your maximum near the middle of the range.

The number jumps around by 30 counts

Normal. The ADC is noisy. Average 16 or 64 samples, and put a 100 nF capacitor from the pin to ground for anything with a long wire on it.

It reads low near zero and I cannot calibrate it out

Below about 100 mV the ADC is not linear at all. That part of the range is unusable, so shift your signal up rather than trying to correct it.

Where this goes next

That was reading a voltage. The classic ESP32 can also produce a real one, from two pins only.

DAC output

Edit this page — content/esp32/reading-the-adc.mdx

Discuss this article

Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.

Browse ESP32 on the forum