If you have ever wired a potentiometer to an ESP32, seen it work perfectly, then watched it return nothing but zeros the moment you added WiFi, you have met the most famous quirk of this chip: the analog-to-digital converter is split in two, and one half switches off when the radio is on.
That is only the first surprise. The ESP32 ADC also has a limited voltage range that depends on a setting called attenuation, a visible non-linearity at both ends of its scale, and a noisy output that needs filtering. None of this makes the ADC useless. It makes it something you have to configure properly. This guide covers the pin map, the right way to read voltages in millivolts, how to measure higher voltages such as a battery, and how to clean up noisy readings.
ADC1 vs ADC2: The Rule You Must Know
The ESP32 has two 12-bit successive-approximation ADCs. Together they provide 18 analog channels, and they are not equal.
| ADC | Channel | GPIO |
|---|---|---|
| ADC1 | CH0 | 36 (VP) |
| CH3 | 39 (VN) | |
| CH4 | 32 | |
| CH5 | 33 | |
| CH6 | 34 | |
| CH7 | 35 | |
| ADC2 | CH0 | 4 |
| CH1 | 0 | |
| CH2 | 2 | |
| CH3 | 15 | |
| CH4 | 13 | |
| CH5 | 12 | |
| CH6 | 14 | |
| CH7 | 27 | |
| CH8 | 25 | |
| CH9 | 26 |
ADC1 also has CH1 and CH2 on GPIO 37 and 38, but those pins are not broken out on common dev boards.
ADC2 is shared with the WiFi driver. While WiFi is running, the radio takes control of ADC2, and calling analogRead() on an ADC2 pin returns invalid data or fails. Bluetooth does not cause this, but WiFi does, including a sketch that merely connected once.
The practical rule: if your project uses WiFi, MQTT, a web server or any network feature, read analog sensors on ADC1 pins (GPIO 32, 33, 34, 35, 36, 39). ADC2 pins are only safe for analog input in sketches that never start WiFi.
GPIO 34, 35, 36 and 39 are also input-only, which suits sensors well, but remember they have no internal pull-up or pull-down.
Reading an Analog Value
The simplest reading is one line:
const int SENSOR_PIN = 34; // ADC1 channel 6
void setup() {
Serial.begin(115200);
}
void loop() {
int raw = analogRead(SENSOR_PIN); // 0 to 4095
Serial.println(raw);
delay(200);
}
The default resolution is 12 bits, so the output ranges from 0 to 4095, unlike the 10-bit 0 to 1023 range on an Arduino Uno. You can change it between 9 and 12 bits:
analogReadResolution(10); // now returns 0 to 1023
Wire a 10 kΩ potentiometer with the outer legs to 3.3V and GND and the middle leg to GPIO 34. The raw value should run from near 0 to near 4095 as you turn it.
Never connect more than 3.3V to an ADC pin. The inputs are not 5V tolerant, and exceeding about 3.6V can damage the chip.
Attenuation: Choosing Your Voltage Range
The ESP32 ADC natively measures only a small voltage range, about 0 to 1.1V. To read larger voltages, an input attenuator scales the signal down first. You choose how much with analogSetAttenuation() (all pins) or analogSetPinAttenuation() (one pin).
| Setting | Constant | Approximate usable range |
|---|---|---|
| 0 dB | ADC_0db | 100 to 950 mV |
| 2.5 dB | ADC_2_5db | 100 to 1250 mV |
| 6 dB | ADC_6db | 150 to 1750 mV |
| 11 dB | ADC_11db | 150 to 2450 mV |
The Arduino core defaults to 11 dB, which is why a full-scale reading appears around 3.1V. These ranges are the accurate, linear region. Beyond them the converter keeps going, but it saturates and the numbers stop meaning anything. In practice, an input of 3.3V and an input of 3.1V both read near 4095.
analogSetPinAttenuation(34, ADC_11db); // best for sensors up to ~3.1V
Choose the smallest attenuation that covers your signal. A sensor that only swings between 0 and 1V reads more accurately at 0 dB or 2.5 dB than at 11 dB.
Getting Real Voltages: analogReadMilliVolts
Raw counts are not volts, and the relationship is not a straight line. The ESP32 ADC is noticeably non-linear near 0V and near the top of the range. A reading of 2048 does not mean exactly half scale.
Espressif calibrates each chip at the factory and stores correction data in eFuse. The Arduino function analogReadMilliVolts() uses that data and returns a calibrated value directly in millivolts:
const int SENSOR_PIN = 34;
void setup() {
Serial.begin(115200);
analogSetPinAttenuation(SENSOR_PIN, ADC_11db);
}
void loop() {
int mv = analogReadMilliVolts(SENSOR_PIN);
Serial.printf("%d mV (%.2f V)\n", mv, mv / 1000.0);
delay(500);
}
Compare it with a multimeter reading on a known voltage. A difference of tens of millivolts is normal on this chip. If you need better than about 1 to 2 percent accuracy, add an external ADC such as the ADS1115 over I2C, which offers 16 bits and a stable reference.
On the original ESP32 the non-linear region is the weak point, so avoid designing a circuit whose important values sit below about 150 mV or above about 2.45V at 11 dB.
Cleaning Up a Noisy Reading
ESP32 analog readings jitter, often by 10 to 50 counts even on a steady voltage. Three fixes work well together.
1. Average multiple samples.
int readAveraged(int pin, int samples = 32) {
long sum = 0;
for (int i = 0; i < samples; i++) {
sum += analogReadMilliVolts(pin);
delayMicroseconds(100);
}
return sum / samples;
}
2. Add a small capacitor. A 100 nF ceramic capacitor between the ADC pin and GND filters high-frequency noise, especially on a long wire or a high-impedance source.
3. Smooth over time with an exponential filter.
float smoothed = 0;
const float ALPHA = 0.1; // lower = smoother but slower
void loop() {
int mv = analogReadMilliVolts(34);
smoothed = ALPHA * mv + (1 - ALPHA) * smoothed;
Serial.println(smoothed);
delay(50);
}
Also keep analog wires short, route them away from the WiFi antenna and away from motor or relay wiring, and power the sensor from the stable 3.3V pin rather than from a noisy supply.
Measuring Higher Voltages With a Voltage Divider
To read a battery or any source above 3.3V, scale it down with two resistors before it reaches the pin.
Vin --- R1 ---+--- ADC pin
|
R2
|
GND
The voltage at the pin is Vout = Vin × R2 / (R1 + R2).
Example: monitoring a single-cell lithium battery (4.2V full, 3.0V empty) with two equal 100 kΩ resistors halves the voltage to a safe 2.1V at full charge.
const int BATT_PIN = 35;
const float DIVIDER_RATIO = 2.0; // (R1 + R2) / R2 with 100k + 100k
float readBatteryVoltage() {
int mv = analogReadMilliVolts(BATT_PIN);
return (mv * DIVIDER_RATIO) / 1000.0;
}
void setup() {
Serial.begin(115200);
}
void loop() {
Serial.printf("Battery: %.2f V\n", readBatteryVoltage());
delay(2000);
}
Use resistors of 100 kΩ or higher to keep the divider's constant current draw tiny on a battery project, and add a 100 nF capacitor from the ADC pin to GND so the high-impedance divider has a stable source for the sampling capacitor. Higher resistor values give more noise, so adjust to your needs.
For a 12V measurement, choose resistors so that the maximum output stays below 3.1V, for example 100 kΩ on top and 33 kΩ on the bottom gives 12V × 33 / 133 = 2.98V.
Common Sensors on the ESP32 ADC
| Sensor | Wiring | Notes |
|---|---|---|
| Potentiometer | 3.3V, GND, wiper to ADC1 pin | Ideal first test |
| LDR (light) | Voltage divider with a 10 kΩ resistor | Reads brightness; calibrate by observation |
| Soil moisture (analog) | 3.3V supply, signal to ADC1 pin | Power from 3.3V, not 5V, so the signal stays within range |
| MQ gas sensors | Often 5V powered | Use a divider on the analog output |
| Thermistor | Divider with a fixed resistor | Needs the Steinhart-Hart equation for degrees |
| TMP36 | 3.3V supply | Output is 10 mV per degree plus a 500 mV offset |
Always power analog modules from 3.3V, or divide their output if they run on 5V. A 5V module outputting 4.5V into an ESP32 pin will damage it.
Common Problems and Fixes
analogRead returns 0 or garbage when WiFi is connected. The pin is on ADC2. Move the sensor to GPIO 32, 33, 34, 35, 36 or 39.
Reading is stuck at 4095. The input voltage exceeds the selected attenuation range. Lower the voltage with a divider or use 11 dB.
Reading jumps around. Average samples, add a 100 nF capacitor, and keep wires short.
Voltage reads differently from my multimeter. Use analogReadMilliVolts() for calibration, and expect tens of millivolts of error. For precision, use an ADS1115.
Floating pin shows random values. An unconnected ADC input picks up noise. That is normal. Connect a source or a pull resistor.
Hall sensor or deep-sleep conflicts. GPIO 36 and 39 are tied to internal circuits that can add noise. If these two pins misbehave, use another ADC1 pin first.
Frequently Asked Questions
How many analog pins does the ESP32 have? Up to 18 channels in total, but on common 30-pin boards you can access 15: six on ADC1 and nine on ADC2. Only the ADC1 pins are dependable with WiFi.
Is the ESP32 ADC accurate? Good enough for hobby sensors after calibration, typically within a few percent. It is not a precision instrument. Add an external ADC for anything that needs real accuracy.
Can I use ADC2 at all? Yes, in projects that never turn on WiFi, or if you stop WiFi before reading. For anything connected, pick ADC1.
What is the maximum input voltage? 3.3V on the pin, with accurate readings only up to about 3.1V at the default attenuation.
Does the ESP32-S3 or C3 behave the same? They have different ADC channel mappings and better linearity, but ADC2 still conflicts with WiFi. Check the pinout for your chip: ESP32-S3, ESP32-C3.
For the full map of every pin, return to the ESP32 pinout guide.