An Analog-to-Digital Converter (ADC) is a circuit that translates continuous real-world voltage signals into discrete binary numbers a microcontroller can process. When you wire a thermistor, potentiometer, or light sensor to a development board, the silicon cannot "feel" the smooth, sweeping voltage changes. It only understands 1s and 0s. The ADC bridges this gap, sampling the analog voltage at a specific GPIO pin and handing your firmware a raw integer. This translation fundamentally changes your circuit from a passive analog network into an active digital data acquisition system, allowing software to make decisions based on physical environmental changes.

The Core Translation: Voltage to Binary

To understand how an ADC works, we have to look at resolution and reference voltage. The ADC compares the input voltage at the pin against a known Reference Voltage ($V_{REF}$). It then chops that voltage range into discrete "steps" based on its bit-resolution.

Let's look at a worked numeric example using the classic Arduino Uno (ATmega328P). The ATmega328P features a 10-bit ADC with a default $V_{REF}$ of 5.0V.

  • Total Steps: $2^{10} = 1024$ steps (numbered 0 to 1023).
  • Step Size (Least Significant Bit, or LSB): The voltage difference between each digital count.
Arduino Uno ADC Math:
Step Size = 5.0V / 1024 = 4.88 mV per step.
If your sensor outputs 2.5V, the ADC reads: (2.5V / 5.0V) * 1023 = 511.
If your sensor outputs 1.2V, the ADC reads: (1.2V / 5.0V) * 1023 = 245.

Because the step size is 4.88 mV, any voltage change smaller than that is invisible to the microcontroller. If your sensor voltage shifts from 2.500V to 2.504V, the ADC will still output 511. This quantization error is the physical limit of your measurement precision.

Where You Meet ADCs in Practice

Not all microcontroller ADCs are created equal. When you move from an 8-bit AVR to a 32-bit ARM or Xtensa architecture, the internal ADC characteristics change drastically. Here is how the most common maker boards handle analog inputs on the bench.

Microcontroller Resolution Logic / $V_{REF}$ Practical ENOB (Effective Bits) Bench Notes
Arduino Uno (ATmega328P) 10-bit 5.0V ~9.6 bits Highly linear, very predictable. 5V tolerant.
ESP32-WROOM-32 12-bit 3.3V ~10 to 11 bits Notoriously non-linear near 3.3V. Suffers from Wi-Fi RF noise coupling.
Raspberry Pi Pico (RP2040) 12-bit 3.3V ~11.5 bits Very clean internal ADC, but requires a stable 3.3V $V_{REF}$.
External: TI ADS1115 16-bit I2C (Up to 5.5V) ~15.5 bits Includes internal PGA (Programmable Gain Amplifier). The gold standard for precision.

Note: ENOB (Effective Number of Bits) is the real-world resolution you get after accounting for internal thermal noise and non-linearity. A "12-bit" ADC rarely gives you 4096 perfectly clean steps.

Real-World Scenario Walkthrough: The Noisy ESP32 Thermistor

Theory is clean; the workbench is messy. Here is a classic scenario that trips up embedded developers moving from Arduino to the ESP32.

  1. The Setup: You are building a smart thermostat using an ESP32-WROOM-32. You wire a 10k NTC thermistor in a voltage divider with a 10k pull-up resistor connected to the 3.3V pin. The midpoint goes to GPIO 34 (an input-only ADC pin).
  2. The Numbers: At room temperature (25°C), the thermistor's resistance is exactly 10k. The voltage divider outputs exactly half of 3.3V, which is 1.65V. The 12-bit ADC (range 0-4095) should theoretically read: $(1.65 / 3.3) * 4095 = 2047$.
  3. The Outcome: You flash the firmware and open the serial monitor. The reading is bouncing randomly between 1980 and 2150. Worse, when you pinch the thermistor to heat it up (dropping its resistance and pushing the voltage up to 2.9V), the ADC reading completely flattens out and stops tracking linearly.
  4. What Went Wrong: You hit the ESP32's internal ADC non-linearity and noise floor.
Bench Gotcha: ESP32 ADC Non-Linearity
The internal ADC on the original ESP32-WROOM-32 is highly non-linear, particularly above 2.6V. Furthermore, when the Wi-Fi radio transmits, internal RF noise couples into the ADC sampling capacitor, causing the massive jitter you saw at 1.65V. According to the Espressif ESP-IDF ADC documentation, you must use internal attenuation (setting the pin attenuation to 11dB) to map higher voltages down to the ADC's linear sweet spot (roughly 0.1V to 1.1V), or use software calibration curves.

The Fix: For a commercial or high-precision DIY thermostat, ditch the internal ADC. Wire a TI ADS1115 16-bit external ADC to the I2C bus. It costs about $3.50 on a breakout board, completely isolates the analog reading from the ESP32's noisy RF environment, and provides rock-solid, jitter-free temperature data.

Common Confusions: ADC vs. DAC and PWM

When reading datasheets or forum posts, it is easy to mix up analog terminology. Here is what people commonly confuse the ADC with:

  • ADC vs. DAC (Digital-to-Analog Converter): An ADC reads voltage and outputs a number. A DAC does the exact opposite: it takes a number from your code and outputs a physical, continuous voltage. The Arduino Uno has an ADC but no DAC. The ESP32 and Raspberry Pi Pico have both.
  • ADC vs. PWM (Pulse Width Modulation): Many makers use PWM (via analogWrite() on Arduino) to dim LEDs or fake an analog output. PWM is not a DAC, and it is certainly not an ADC. PWM rapidly switches a digital pin between 0V and 3.3V/5V. It only looks analog to the human eye or a slow-moving motor due to persistence of vision or mechanical inertia. If you feed a PWM signal into an ADC pin without a low-pass RC filter, the ADC will just read either 0 or the maximum voltage, completely missing the "duty cycle" you intended.

FAQ: Resolution, Sampling, and Reference Voltages

Q: What does "12-bit resolution" actually mean for my sensor?
A: It means the ADC divides the reference voltage into 4096 discrete steps ($2^{12}$). If your $V_{REF}$ is 3.3V, each step is 0.8 mV. If your sensor outputs a 0-50mV signal (like a raw load cell), a 12-bit ADC will only give you about 62 steps of resolution across that entire range. You would need an external ADC with a Programmable Gain Amplifier (PGA) to amplify that 50mV signal to fill the 3.3V range and utilize all 4096 steps.

Q: Why is my ADC reading jittering by 5-10 points even when the sensor is untouched?
A: This is usually caused by a noisy power supply or a high-impedance voltage divider. The internal sampling capacitor inside the microcontroller's ADC needs to charge up in a fraction of a microsecond. If your voltage divider uses massive resistors (e.g., 1MΩ and 1MΩ to save battery), the capacitor cannot charge fast enough, resulting in random, jittery readings. Keep your voltage divider impedance below 10kΩ, or add a 100nF ceramic capacitor between the ADC pin and GND to act as a local charge reservoir.

Q: What is the difference between VCC and VREF?
A: VCC is the power supply running the microchip's digital logic. VREF is the specific voltage the ADC uses as its "100%" ceiling. On an Arduino Uno, they are usually tied together at 5V. However, if your 5V USB supply sags to 4.8V under load, your VREF drops to 4.8V, and all your analog readings will skew. High-precision designs use a dedicated, separate voltage reference IC (like the LM4040) wired directly to the VREF pin, ensuring the ADC's ceiling never moves, even if the main power rail fluctuates.