Phase 3: GPIO & Hardware 14 min read

ADC Explained on ESP32: Resolution, Attenuation, and Calibration

Deep dive into ESP32 ADC — understand SAR ADC architecture, ADC1 vs ADC2, 12-bit resolution, attenuation, calibration methods, and how to get accurate voltage readings.

Updated June 19, 2026

What is an ADC?

An ADC (Analog-to-Digital Converter) bridges the gap between the continuous analog world (temperatures, pressures, light levels) and the discrete digital world inside a microcontroller. It samples an input voltage at a moment in time and produces a binary number representing that voltage.

ESP32 has two built-in 12-bit SAR ADCs capable of reading up to 18 channels. They are fundamental to interfacing with potentiometers, temperature sensors, pH sensors, accelerometers with analog output, and many other transducers.

How SAR ADC Works (Simplified)

ESP32 uses a Successive Approximation Register (SAR) architecture:

  1. The ADC samples the input voltage and holds it steady (sample-and-hold)
  2. It starts with a guess of half the full-scale range (2048 for 12-bit)
  3. It compares the guess to the actual voltage: too high → go lower; too low → go higher
  4. Repeating 12 times, it narrows down to the exact 12-bit number
  5. The result (0–4095) is returned to your code via analogRead()
SAR ADC Example: 1.65V on 3.3V range
Step 1: Guess 2048 (1.65V) — exact match for this example
Result: 2048 out of 4095

Voltage resolution = 3.3V / 4095 = 0.000806V per step ≈ 0.8 mV/LSB

In practice, with noise: expect ±2–5 LSB variation on stable inputs.

ADC1 vs ADC2 Channel Map

GPIO ADC Channel Wi-Fi Safe?
GPIO36 (VP) ADC1 CH0 Yes ✓
GPIO37 ADC1 CH1 Yes ✓
GPIO38 ADC1 CH2 Yes ✓
GPIO39 (VN) ADC1 CH3 Yes ✓
GPIO32 ADC1 CH4 Yes ✓
GPIO33 ADC1 CH5 Yes ✓
GPIO34 ADC1 CH6 Yes ✓
GPIO35 ADC1 CH7 Yes ✓
GPIO4 ADC2 CH0 No ✗
GPIO25 ADC2 CH8 No ✗
GPIO26 ADC2 CH9 No ✗

Resolution and Voltage Mapping

Arduino (C++) — Resolution configuration
void setup() {
  Serial.begin(115200);

  analogReadResolution(12);         // 12-bit: 0–4095 (default)
  // analogReadResolution(10);      // 10-bit: 0–1023 (Arduino style)
  // analogReadResolution(9);       //  9-bit: 0–511

  analogSetAttenuation(ADC_11db);   // Input range: 0–3.3V
}

void loop() {
  int raw12  = analogRead(34);               // 12-bit raw
  float volts = raw12 * (3.3f / 4095.0f);   // Convert to voltage
  Serial.printf("12-bit raw=%4d  Voltage=%.4f Vn", raw12, volts);
  delay(500);
}

Attenuation Deep Dive

Arduino (C++) — All attenuation modes
void setup() {
  Serial.begin(115200);
  analogReadResolution(12);

  // Set different attenuation on different pins:
  analogSetPinAttenuation(34, ADC_0db);     // GPIO34: 0–1.1V (highest precision)
  analogSetPinAttenuation(35, ADC_6db);     // GPIO35: 0–2.2V
  analogSetPinAttenuation(36, ADC_11db);    // GPIO36: 0–3.3V (full range)
}

void loop() {
  Serial.printf("GPIO34(0dB)=%.3fV  GPIO35(6dB)=%.3fV  GPIO36(11dB)=%.3fVn",
    analogRead(34) * (1.1f/4095.0f),
    analogRead(35) * (2.2f/4095.0f),
    analogRead(36) * (3.3f/4095.0f));
  delay(500);
}

ADC Non-Linearity Problem

ESP32 ADC has a documented non-linearity, especially near 0V and 3.3V. The characteristic S-curve means readings in the 0–100 and 3900–4095 ranges are unreliable:

ADC Raw Range Accuracy Notes
0–100 Poor (non-linear) Avoid reading near 0V
100–3900 Good (±1–3%) Safe operating range
3900–4095 Poor (saturates) Avoid readings near 3.3V

Workaround: Ensure your sensor signal stays between 0.1V and 3.1V (raw 100–3900) for the best accuracy.

ADC Calibration with esp_adc_cal

Espressif provides a calibration library that dramatically improves accuracy:

Arduino (C++) — ADC calibration
#include <esp_adc_cal.h>
#include <driver/adc.h>

esp_adc_cal_characteristics_t adcChars;

void setup() {
  Serial.begin(115200);

  adc1_config_width(ADC_WIDTH_BIT_12);
  adc1_config_channel_atten(ADC1_CHANNEL_6, ADC_ATTEN_DB_11);  // GPIO34

  esp_adc_cal_characterize(
    ADC_UNIT_1,
    ADC_ATTEN_DB_11,
    ADC_WIDTH_BIT_12,
    1100,              // Default Vref (mV) — use 1100 if no eFuse calibration
    &adcChars
  );
}

void loop() {
  uint32_t raw     = adc1_get_raw(ADC1_CHANNEL_6);  // GPIO34
  uint32_t voltage = esp_adc_cal_raw_to_voltage(raw, &adcChars);  // mV
  Serial.printf("Raw: %4d  Calibrated: %4d mV  (%.3f V)n",
                raw, voltage, voltage / 1000.0f);
  delay(500);
}

Oversampling for Better Effective Resolution

Arduino (C++) — 16× oversampling (+2 bits)
// Oversampling: 4^n samples → n extra bits of resolution
// 16 samples → 2 extra bits (12+2 = 14-bit effective)

uint16_t oversample(int pin, int samples = 16) {
  uint32_t acc = 0;
  for (int i = 0; i < samples; i++) {
    acc += analogRead(pin);
    delayMicroseconds(50);
  }
  return acc / samples;   // Average (for 16 samples, decimation gives +2 bits)
}

void loop() {
  Serial.printf("12-bit: %4d   Oversampled: %5dn",
    analogRead(34), oversample(34, 64));
  delay(200);
}

External ADC: When ESP32 Built-in is Not Enough

ADC Resolution Interface Best For
ESP32 Built-in 12-bit (~10 eff) Native General sensing
ADS1115 16-bit I2C Precision sensors
ADS1256 24-bit SPI Scientific/industrial
MCP3208 12-bit SPI Multiple channels

Summary

ESP32's built-in 12-bit SAR ADC is capable and convenient. For best results: use ADC1 channels (GPIO32–GPIO39), set 11 dB attenuation for full 3.3V range, average multiple samples, and apply factory calibration when accuracy matters. For measurements needing better than ±1% accuracy, use an external I2C ADC like the ADS1115.

Frequently Asked Questions

ADC stands for Analog-to-Digital Converter. It converts a continuous analog voltage into a discrete digital number that a microcontroller can process. ESP32 ADC converts voltages between 0V and 3.3V into integers between 0 and 4095 (12-bit resolution).
ESP32 supports 9-bit, 10-bit, 11-bit, and 12-bit ADC resolution. At 12-bit (default), the output ranges from 0 to 4095, giving 4096 discrete voltage steps. Effective resolution is around 10–11 bits due to built-in noise.
SAR (Successive Approximation Register) ADC is the architecture used in ESP32. It works by binary-searching the input voltage: starting from the midpoint, it halves the search range in each step, requiring N steps for N-bit resolution. ESP32 performs 12 comparison steps to produce a 12-bit result.
ADC1 uses GPIO32–GPIO39 and is fully independent of Wi-Fi/Bluetooth. ADC2 (GPIO0, GPIO2, GPIO4, GPIO12–GPIO15, GPIO25–GPIO27) is shared with the RF subsystem. When Wi-Fi is enabled, ADC2 is taken over by the RF hardware and returns errors. Always use ADC1 in wireless projects.
Techniques: 1) Use 11dB attenuation for full 0–3.3V range. 2) Average 8–64 samples to reduce noise. 3) Apply Espressif ADC calibration (esp_adc_cal_characterize) for offset correction. 4) Add a 100nF capacitor from the ADC input pin to GND. 5) Use GPIO34–GPIO39 which have cleaner ADC channels.
Attenuation scales the input voltage range to match the ADC full-scale. At 0dB the ADC reads 0–1.1V. At 11dB it reads 0–3.3V. Change attenuation when your sensor output is in a specific range: use 0dB for precision low-voltage signals (thermocouples, strain gauges) and 11dB for most common sensors.
Known causes: ADC non-linearity (especially near 0V and 3.3V), power supply noise coupling into the ADC reference, switching noise from PWM or motors, Wi-Fi RF interference when using ADC2, and ground loops between sensors and ESP32. Use ADC calibration, averaging, and good power supply decoupling to mitigate.
Yes, but with limitations. ESP32 ADC maximum sample rate is about 100 kSPS, sufficient for audio up to 50 kHz. However, ADC noise levels limit usable dynamic range to about 50 dB. For quality audio input use an external I2S ADC (ES8388, PCM1808) connected to the I2S peripheral.
ESP32 stores factory calibration data in eFuses (or external flash) that corrects for reference voltage variations between individual chips. The esp_adc_cal library reads this data and computes a correction polynomial. Result: voltage readings accurate to ±5% instead of the ±30% raw ADC error.
Practical limits: 12-bit resolution but ~10-bit effective; non-linear near 0V and 3.3V; ADC2 unavailable during Wi-Fi; maximum input voltage 3.6V; no differential input mode; no built-in oversampling hardware; sample rate limited to ~100 kSPS. For higher precision use an external ADC via SPI or I2C.

Projects to Build

Put this knowledge to work — try one of these hands-on projects.