An Analog-to-Digital Converter (ADC) is an electronic circuit that translates continuous real-world voltage signals into discrete binary numbers a microcontroller can process. In a physical circuit, the ADC changes smooth, infinitely variable analog waves (like a rising temperature or a fading light level) into stepped, quantized digital values that a CPU can use for logic, displays, or data logging. When reading about embedded systems, beginners commonly confuse ADC resolution (the bit-depth or number of steps) with ADC accuracy (how close the reading is to the true physical voltage), or they mix up ADCs with DACs (Digital-to-Analog Converters), which perform the exact reverse operation.
The Core Mechanics of an ADC (and a Worked Numeric Example)
Microcontrollers cannot understand continuous voltage; they only understand binary ones and zeros. The ADC bridges this gap through a process called quantization. Think of measuring the height of a ramp using a ruler that only has 1-inch marks. If the ramp is 3.4 inches high, your ruler forces you to round it to 3 inches. The ADC does the same thing with voltage, rounding the input to the nearest available digital "step" based on its reference voltage and bit-resolution.
Worked Numeric Example: Arduino Uno vs. ESP32
Let us look at a real-world scenario using an Arduino Uno (ATmega328P) reading a TMP36 analog temperature sensor. The Arduino Uno features a 10-bit ADC with a default 5.0V reference.
- Total Steps: 2^10 = 1024 steps.
- Step Size (Voltage per bit): 5.0V / 1024 = 4.88 mV per step.
- The Scenario: The TMP36 sensor outputs exactly 0.75V at 25°C (77°F).
- The Math: 0.75V / 0.00488V = 153.6.
The microcontroller rounds this and returns an integer value of 153 when you call analogRead(A0). If you need the voltage back in your code, you multiply the reading by the step size: 153 * 0.00488 = 0.746V.
Contrast this with the ESP32, which uses a 12-bit SAR (Successive Approximation Register) ADC referenced to 3.3V. Theoretically, it offers 4096 steps, yielding a much finer step size of 0.8 mV. However, as we will cover below, theoretical resolution does not always equal practical accuracy.
| Microcontroller | Resolution | Default VREF | Step Size (LSB) | Max Sampling Rate |
|---|---|---|---|---|
| Arduino Uno (ATmega328P) | 10-bit (1024) | 5.0V | 4.88 mV | ~15 kSPS |
| ESP32 (WROOM-32) | 12-bit (4096) | 3.3V | 0.80 mV | ~1 MSPS (theoretical) |
| Raspberry Pi Pico (RP2040) | 12-bit (4096) | 3.3V | 0.80 mV | 500 kSPS |
Where You Meet ADCs in Practice
You will encounter ADCs constantly when bridging physical sensors to digital logic. Here are the three most common jobsite and workbench applications:
- Battery Voltage Monitoring: You cannot feed a 12V LiFePO4 battery directly into a 3.3V ESP32 GPIO; it will instantly fry the silicon. Instead, you use a resistor voltage divider (e.g., 100kΩ and 33kΩ) to scale the 12V down to a safe ~2.97V. The ADC reads this scaled voltage, and your code multiplies it by the divider ratio to calculate the actual battery state of charge (SoC).
- Variable Resistive Sensors: Components like NTC thermistors, photoresistors (LDRs), and flex sensors change resistance, not voltage. To read them, you place them in a voltage divider with a fixed resistor. The ADC measures the changing voltage at the midpoint of the divider.
- Potentiometers and Joysticks: Analog joysticks output a variable voltage from 0V to VCC as you move the stick. The ADC translates this physical position into X/Y coordinates for controlling servos, motor speeds via PWM, or UI navigation.
Microcontroller ADCs use an internal sample-and-hold capacitor (typically 10pF to 14pF). If your voltage divider uses massive resistors (e.g., 1MΩ), the internal capacitor will not have enough time to charge fully during the sampling window, resulting in artificially low readings. Keep your total source impedance under 10kΩ, or solder a 100nF ceramic bypass capacitor directly between the ADC pin and GND to act as a local charge reservoir.
Resolution vs. Accuracy: The Datasheet Reality
The most painful lesson in embedded electronics is learning that a 12-bit ADC does not give you 12 bits of usable, noise-free data. This is where the concept of ENOB (Effective Number of Bits) comes in. Thermal noise, clock jitter, and internal silicon imperfections eat away at your theoretical resolution.
The ESP32 is notorious for this. While it boasts a 12-bit ADC, the Espressif ESP-IDF documentation explicitly notes that the ADC is highly non-linear, particularly near the voltage rails. Readings near 0 (0-100) and near 4095 (3900-4095) are compressed and inaccurate. If you are building a precision battery monitor with an ESP32, you must avoid the rails by scaling your voltage divider so your maximum expected voltage lands around 2.5V to 3.0V (roughly an ADC reading of 3100 to 3700).
Furthermore, to get true millivolt accuracy on the ESP32, you cannot rely on raw analogRead() values. You must use the esp_adc/adc_cali library to read the factory-programmed eFuse calibration values burned into your specific chip during manufacturing. This applies a mathematical curve to correct the silicon-level non-linearity.
ADC Definition FAQ: Common Long-Tail Questions
What is the difference between ADC resolution and sampling rate?
Resolution (measured in bits, like 10-bit or 12-bit) dictates how finely the ADC can slice the voltage range into discrete steps. A higher resolution means smaller voltage increments per step. Sampling rate (measured in kSPS or MSPS - kilosamples/megasamples per second) dictates how many times per second the ADC can take a measurement. According to the Nyquist-Shannon sampling theorem, your sampling rate must be at least twice the maximum frequency of the analog signal you are trying to capture. For example, to digitize a 20 kHz audio signal, your ADC must sample at a minimum of 40 kSPS. The Arduino Uno's ~15 kSPS limit makes it terrible for audio, while the ESP32 can handle it easily.
Why does my ESP32 ADC read 4095 when the pin is disconnected?
This is caused by a floating pin. When an ADC input is not connected to a defined voltage source (VCC or GND), its extremely high input impedance (often >100 MΩ) makes it act like an antenna. It picks up stray electromagnetic interference (EMI) from your body, Wi-Fi routers, and mains wiring, charging the internal sample-and-hold capacitor to the positive rail. To fix this, never leave analog inputs unconnected in your circuit. If a pin is unused in hardware, configure it as a digital output in your code, or tie it to GND with a 10kΩ pull-down resistor.
How do I calculate the resistor divider values for an ADC input?
Use the standard voltage divider formula: Vout = Vin * (R2 / (R1 + R2)), where R1 is the top resistor (connected to the high voltage) and R2 is the bottom resistor (connected to GND).
Example: You want to measure a 14.4V battery using an ESP32 (3.3V max ADC input). You want a target Vout of 2.8V to leave headroom for voltage spikes.
Let's choose R2 = 10,000Ω (10kΩ).
2.8 = 14.4 * (10000 / (R1 + 10000))
Solving for R1 gives approximately 41,428Ω. The closest standard 1% resistor value is 41.2kΩ.
Always verify with your multimeter: 14.4 * (10000 / (41200 + 10000)) = 2.81V. This is safely below the 3.3V absolute maximum rating. For a deeper look at standard Arduino analogRead functions and scaling, check the official language reference.






