An Analog-to-Digital Converter (ADC) is a hardware circuit that translates a continuous real-world voltage into a discrete binary number your microcontroller can process. In a physical circuit, the ADC changes an infinitely variable analog signal into a finite, stepped integer, acting as the critical bridge between real-world sensors and digital logic.

Without an ADC, a microcontroller is blind to the physical world. It can only see digital HIGHs and LOWs (1s and 0s). The ADC samples the voltage on a specific pin at a given moment and assigns it a numeric value based on its internal reference voltage and bit resolution.

The Core Math: Resolution, Reference Voltage, and Step Size

To understand how an ADC works, you need to understand quantization. Think of an ADC like a tape measure that only has marks every 1/4 inch. If a board measures 5 and 3/16 inches, you are forced to round to the nearest 1/4-inch mark. You lose a tiny bit of precision, but you get a usable number.

In electronics, the 'marks on the tape measure' are determined by the ADC's bit resolution and its reference voltage (Vref).

Worked Numeric Example: Arduino Uno R3

The ATmega328P chip on a standard Arduino Uno R3 features a 10-bit ADC with a default 5V reference.

  • Total Steps: A 10-bit ADC has 2^10 = 1,024 discrete steps (numbered 0 to 1023).
  • Step Size (Resolution): 5.0V / 1024 = 4.88 mV per step.

If you connect a temperature sensor that outputs 2.15V, the ADC calculates the digital value by dividing the input voltage by the step size:

2.15V / 0.00488V = 440.57

Because the ADC can only output whole integers, it rounds down. Your microcontroller registers a raw value of 440. In your code, you reverse the math to find the actual voltage: 440 * 0.00488 = 2.147V. That 0.003V difference is your quantization error.

Where You Meet ADCs in Practice

You will use ADCs whenever a sensor outputs a variable voltage rather than a simple digital on/off signal. Common bench encounters include:

  • Potentiometers & Joysticks: These act as variable voltage dividers. As you turn the knob, the wiper pin outputs a voltage anywhere between GND and VCC.
  • NTC Thermistors & LDRs: Resistive sensors change resistance based on temperature or light. You place them in a voltage divider circuit with a fixed resistor so the changing resistance translates into a changing voltage the ADC can read.
  • Current Shunts: Measuring DC current often involves reading the tiny millivolt drop across a low-value shunt resistor (though dedicated chips like the INA219 handle this internally).
  • Audio & AC Signals: Microphones output alternating analog voltages. Reading these requires an ADC with a high sampling rate and a DC bias to keep the signal above 0V.

Bench Scenario: When the ESP32 ADC Lies to You

Warning: The internal ADC on the original ESP32-WROOM-32 is notoriously non-linear. If you are designing a product that requires precise voltage measurements, do not rely on the raw internal ADC pins without calibration.

Let's walk through a real-world scenario that trips up almost every maker upgrading from an Arduino to an ESP32.

The Setup

You wire a 10k potentiometer across the 3.3V and GND pins of an ESP32 DevKit v1. The wiper is connected to GPIO 34 (an input-only pin tied to ADC1_CH6). You write a simple sketch using analogRead(34) and map the 12-bit result (0-4095) to a 0-3.3V range.

The Numbers

You turn the potentiometer to its physical maximum. You put your multimeter probes on the wiper and the multimeter reads 3.28V. You expect the serial monitor to print a raw ADC value close to 4095.

The Outcome

The serial monitor prints 3150. When your code maps 3150 back to a voltage using the standard 3.3V reference math, the software thinks the pin is only seeing 2.54V, even though the multimeter proves it is seeing 3.28V.

What Went Wrong

The Espressif ESP32 ADC architecture has internal transistor voltage drops and an 11dB attenuation curve that compresses the top and bottom of the reading range. The usable linear range on an ESP32 internal ADC is roughly 0.15V to 3.15V, not 0V to 3.3V. Furthermore, the default Arduino core mapping historically applied a flawed linear assumption to this non-linear curve.

The Fix

  1. Software Fix (Quick): Stop using raw analogRead() math. Use the ESP32 Arduino core's built-in analogReadMilliVolts(pin) function, which applies factory-stored eFuse calibration data to correct the non-linearity.
  2. Hardware Fix (Robust): Bypass the internal ADC entirely. Wire an external I2C ADC like the Texas Instruments ADS1115 to your I2C bus. It provides true 16-bit resolution, a stable internal reference, and perfect linearity.

What People Commonly Confuse With ADC

When debugging embedded systems, it is easy to mix up analog concepts. Here is what ADC is not:

  • DAC (Digital-to-Analog Converter): The exact opposite. A DAC takes a digital number from your code and outputs a true, continuous analog voltage. (The ESP32 has a built-in DAC on GPIO 25 and 26; the Arduino Uno does not).
  • PWM (Pulse Width Modulation): PWM is a digital signal that switches between 0V and VCC very fast to simulate an analog average. An LED dimmed via PWM is actually flashing on and off. An ADC reads true continuous voltage, not a PWM duty cycle (unless you filter the PWM with an RC low-pass filter first).
  • Digital Interrupts: Reading a pushbutton is a digital operation. You are just checking if the pin is above a logic threshold (e.g., >2.0V for HIGH). You do not need an ADC to read a standard switch.

Internal vs. External ADC Hardware Comparison

Choosing between your microcontroller's built-in ADC and an external chip depends on your precision requirements and budget.

Hardware Resolution Channels Vref / Range Best Use Case
ATmega328P (Uno R3) 10-bit (1024 steps) 6 5.0V default Basic hobby sensors, potentiometers, LDRs.
ESP32-WROOM-32 12-bit (4095 steps) 15 (ADC1 + ADC2) ~0.1V to 3.1V usable General IoT sensing where 5% error is acceptable.
ADS1115 (External I2C) 16-bit (65536 steps) 4 (Mux) Programmable (±256mV to ±6.144V) Precision current shunts, load cells, lab equipment.
STM32F103 (Blue Pill) 12-bit (4095 steps) 10 3.3V (highly linear) Motor control, fast sampling, audio inputs.

FAQ: Common ADC Debugging Questions

Why is my ADC reading jumping around by 10-20 steps when the sensor isn't moving?

This is ADC noise, usually caused by electromagnetic interference (EMI) from nearby digital lines, switching power supplies, or a floating ground. The bench fix: Solder a 100nF (0.1µF) ceramic decoupling capacitor directly between the ADC input pin and GND, as close to the microcontroller as possible. This creates a low-pass filter that smooths out high-frequency noise before the ADC samples it.

Can I use an ADC to read negative voltages?

No. Standard microcontroller ADC pins are referenced to GND (0V). If you feed a negative voltage into an Arduino or ESP32 ADC pin, the internal protection diodes will conduct, potentially frying the pin or the entire chip. To measure negative voltages, you must use an op-amp level-shifter circuit to bias the signal into the 0V–Vref range, or use an external ADC like the ADS1115 which supports differential measurements and negative rails.

My ADC reads 1023 (max) even when the sensor is disconnected. Why?

You have a floating pin. When an ADC pin is not connected to a defined voltage source, it acts like an antenna, picking up ambient electrical noise and floating to the highest impedance state. Always ensure your sensor circuit has a pull-down resistor to GND or a pull-up resistor to VCC so the pin has a default state when the sensor is removed.