An Analog-to-Digital Converter (ADC) is a hardware peripheral that translates continuous real-world voltage levels into discrete binary numbers a microcontroller can process. If you want an ESP32, Arduino, or Raspberry Pi Pico to read a temperature sensor, a potentiometer, or a battery voltage, the ADC is the bridge between the analog physical world and the digital logic of your code.

The Core Mechanism: Quantization, Sampling, and Resolution

To understand how an ADC works, you have to separate the process into two distinct actions: sampling and quantization. Sampling is the act of taking a snapshot of the voltage at a specific microsecond in time. Quantization is the act of rounding that exact voltage to the nearest available digital 'step' based on the ADC's resolution.

Think of it like measuring rain with a set of stacked buckets. The rain falls continuously (analog), but you can only measure it in whole bucket increments (digital). If a bucket holds 10mm of water, and 14mm falls, your measurement reads either 10mm or 20mm depending on the rounding threshold, introducing a quantization error. In electronics, this error is quantified as ±0.5 Least Significant Bit (LSB).

Microcontroller ADC Hardware Specifications (Nominal vs. Real-World)
Microcontroller Nominal Resolution Default Vref Max Sampling Rate Typical ENOB Input Impedance Limit
Arduino Uno (ATmega328P) 10-bit (1024 steps) 5.0V 15 kSPS ~8.5 bits < 10 kΩ
ESP32-WROOM-32 12-bit (4096 steps) 3.3V (Internal) 1 MSPS (SAR) ~9 to 10 bits < 10 kΩ (varies by attenuation)
Raspberry Pi Pico (RP2040) 12-bit (4096 steps) 3.3V 500 kSPS ~8.5 bits < 50 kΩ
STM32F401 (Black Pill) 12-bit (4096 steps) 3.3V 2.4 MSPS ~11 bits < 50 kΩ
External: TI ADS1115 (I2C) 16-bit (65536 steps) Programmable 860 SPS ~15 bits > 10 MΩ
ENOB vs. Nominal Resolution: Notice the 'Typical ENOB' (Effective Number of Bits) column. A microcontroller might advertise a 12-bit ADC, but internal thermal noise, clock jitter, and non-linear silicon tracing mean the last 2 or 3 bits are often just random noise. When designing precision circuits, always calculate your noise floor using ENOB, not the marketing resolution. For deep-dive theory on this, refer to the Analog Devices Data Converter Basics guide.

Worked Numeric Example: Reading a 12-Bit Sensor

Let's look at the exact math for reading a 5.0V lithium-ion battery pack using an ESP32's 12-bit ADC. Because the ESP32's maximum input voltage is 3.3V, we first pass the battery voltage through a resistor voltage divider (e.g., 10kΩ and 10kΩ) to scale it down by half.

Scenario Parameters:

  • ADC Resolution: 12-bit (Total steps = 2^12 = 4096)
  • Reference Voltage (Vref): 3.3V
  • Actual Battery Voltage: 4.20V (Fully charged 1S Li-ion)
  • Voltage at ADC Pin (after 1:2 divider): 2.10V

Step 1: Calculate the Step Size (LSB)
Each digital step represents a specific voltage increment.
LSB = Vref / Total Steps = 3.3V / 4096 = 0.000805V (0.805 mV).

Step 2: Calculate the Expected Raw ADC Value
Raw Value = Pin Voltage / LSB = 2.10V / 0.000805V = 2608.
When you call analogRead(34) in your Arduino/ESP32 code, the microcontroller returns the integer 2608.

Step 3: Convert Back to Real-World Voltage in Code
To get the battery voltage back in your software, you reverse the math:

const float VREF = 3.3;
const int ADC_MAX = 4095; // 0 to 4095 is 4096 steps
const float DIVIDER_RATIO = 2.0; // 10k/10k voltage divider

int raw_adc = analogRead(34); 
float pin_voltage = (raw_adc * VREF) / ADC_MAX;
float battery_voltage = pin_voltage * DIVIDER_RATIO;

// If raw_adc is 2608:
// pin_voltage = (2608 * 3.3) / 4095 = 2.102V
// battery_voltage = 2.102 * 2.0 = 4.204V

This conversion assumes a perfectly linear ADC and a perfectly stable 3.3V reference. In reality, the Espressif ESP32 ADC documentation explicitly warns about non-linearity near the 0V and 3.3V rails, which leads us to practical hardware design.

Where You Meet ADC in Practice (And What It Changes)

Understanding how ADC works fundamentally changes how you design the analog front-end of your circuit. You cannot simply wire a high-impedance sensor to a microcontroller pin and expect accurate data. The ADC architecture forces you to manage three specific physical constraints:

1. Source Impedance and the Sample-and-Hold Capacitor
Inside the microcontroller, the ADC uses a tiny internal capacitor (typically 10pF to 14pF) to 'catch' and hold the voltage during the conversion process. If your external circuit has a high resistance (e.g., a voltage divider using 1MΩ resistors to save battery), the internal capacitor won't have enough time to charge up to the actual voltage before the sampling window closes. The result? Your ADC reads falsely low. The fix: Keep your source impedance under 10kΩ, or place a 100nF X7R ceramic capacitor directly between the ADC pin and GND to act as an external charge reservoir.

2. Reference Voltage Stability
The ADC doesn't measure absolute voltage; it measures the ratio of the input voltage to the Reference Voltage (Vref). If your microcontroller is powered via a noisy USB cable and Vref sags from 3.3V to 3.2V, your ADC reading will spike, even if the sensor's output hasn't changed a single millivolt. For precision work, bypass the internal Vref and feed a dedicated, low-noise voltage reference IC (like the LM4040) into the microcontroller's VREF pin.

3. The Nyquist Limit and Signal Filtering
If you are sampling an AC waveform or a rapidly changing signal, the ADC's sampling rate dictates what you can see. According to the Nyquist-Shannon sampling theorem, you must sample at least twice as fast as the highest frequency component in your signal. If you are reading a 60Hz AC mains signal via a current transformer, sampling at 100Hz will yield aliasing (garbage data). You must sample at >120Hz, and ideally place an analog RC low-pass filter before the ADC pin to block high-frequency EMI noise that the ADC would otherwise digitize.

Common Confusions and Hardware Gotchas

Do people confuse ADC with PWM?

Yes, constantly. Beginners often confuse analogRead() (ADC) with analogWrite() (PWM). PWM does not output a true analog voltage; it outputs a digital 5V/0V square wave and relies on the duty cycle to simulate an average voltage for things like LED dimming or motor speed. ADC, conversely, is an input mechanism that measures actual continuous voltage. If you need a true analog voltage output, you need a Digital-to-Analog Converter (DAC), which the ESP32 has on pins 25 and 26, but the Arduino Uno lacks entirely.

Why is my ESP32 ADC reading jumping around by 20-30 points?

The ESP32's internal SAR ADC is notoriously noisy and non-linear, especially when WiFi or Bluetooth is active, due to internal RF interference coupling into the ADC traces. If your raw value is jumping between 2000 and 2030, you are seeing the ENOB limits. The fix: In software, take 64 samples and average them. In hardware, if you need true 12-bit or 16-bit precision (like for a precision load cell or lab equipment), bypass the internal ADC entirely and use an external I2C ADC like the Texas Instruments ADS1115 (typically $3 to $5 on a breakout board).

Can I measure negative voltages with a microcontroller ADC?

No. Standard microcontroller ADCs are unipolar, meaning they only measure voltages between 0V (GND) and Vref. Feeding a negative voltage into an Arduino or ESP32 pin will forward-bias the internal ESD protection diodes, potentially destroying the silicon. To measure AC or negative DC signals, you must use an op-amp circuit to level-shift and bias the signal so it sits entirely within the 0V to 3.3V window before it reaches the ADC pin. For more on safe pin limits, see the official Arduino analogRead reference.