What is an ADC?
An ADC (Analog-to-Digital Converter) reads a real-world voltage on a pin and converts it to a number your code can use. The ESP32-S3’s built-in ADC measures voltages between 0V and ~3.1V and outputs an integer from 0 to 4095.
Why This Matters for Your Projects
Section titled “Why This Matters for Your Projects”Digital GPIO pins only tell you HIGH or LOW — on or off. But many sensors output varying voltages. Potentiometers, light sensors, analog microphones, flex sensors, soil moisture probes, and temperature sensors all produce continuous analog signals. The ADC is how your code reads those in-between values and turns them into data you can work with.
How It Works: 12-Bit Resolution
Section titled “How It Works: 12-Bit Resolution”The ESP32-S3 ADC has 12-bit resolution, which means 2¹² = 4,096 possible values (numbered 0 to 4095). Each step represents approximately 0.8 mV. When the ADC returns 2048, the input voltage is roughly 1.65V — halfway between 0V and 3.3V. When it returns 0, the pin is at 0V. When it returns 4095, the pin is at ~3.1V.
Reading an Analog Value
Section titled “Reading an Analog Value”const int sensorPin = 1; // GPIO 1, an ADC1 pin
void setup() { Serial.begin(115200);}
void loop() { int raw = analogRead(sensorPin); // uncalibrated: 0–4095 int mV = analogReadMilliVolts(sensorPin); // calibrated: actual millivolts
Serial.print("Raw: "); Serial.print(raw); Serial.print(" | Voltage: "); Serial.print(mV); Serial.println(" mV"); delay(200);}On the tinyCore (ESP32-S3)
Section titled “On the tinyCore (ESP32-S3)”ADC Pin Map
Section titled “ADC Pin Map”The ESP32-S3 has two ADC units with 10 channels each (20 total):
| Unit | GPIO Pins | Channels |
|---|---|---|
| ADC1 | GPIO 1–10 | 10 |
| ADC2 | GPIO 11–20 | 10 |
Attenuation: Voltage Range
Section titled “Attenuation: Voltage Range”Attenuation controls the maximum voltage the ADC can measure. The default (11 dB) covers the full 0–3.1V range. Lower attenuation gives better accuracy over a narrower range.
| Setting | Arduino Constant | Measurable Range |
|---|---|---|
| 0 dB | ADC_0db | 0 – 950 mV |
| 2.5 dB | ADC_2_5db | 0 – 1250 mV |
| 6 dB | ADC_6db | 0 – 1750 mV |
| 11 dB | ADC_11db | 0 – 3100 mV (default) |
If your signal never goes above 1V, using ADC_0db gives you better precision across that range:
analogSetPinAttenuation(1, ADC_0db); // GPIO 1: high accuracy 0–950 mVNon-Linearity at the Extremes
Section titled “Non-Linearity at the Extremes”The ESP32-S3’s ADC becomes inaccurate above approximately 2750 mV. Past this point, readings compress and stop tracking the actual voltage linearly. At ~3100 mV the output saturates at 4095 — the ADC can’t distinguish 3.1V from 3.3V.
The good news: the ESP32-S3 is much better than the original ESP32 near 0V. The S3 can accurately read voltages very close to ground, while the original ESP32 had a dead zone below ~100 mV.
Practical guideline: Keep input signals between 0V and 2.9V at 11 dB attenuation for reliable results. Expect accuracy within ±30 mV across this range after factory calibration.
Practical Uses
Section titled “Practical Uses”Potentiometer (volume knob / position control). Connect the wiper (middle pin) to an ADC1 pin, outer pins to 3.3V and GND. analogRead() gives you the knob position as 0–4095.
Light sensor (LDR). Wire the LDR in a voltage divider with a fixed resistor (10kΩ is common). The ADC reads the changing voltage as light levels shift.
Battery voltage monitoring. LiPo batteries (3.0–4.2V) exceed the ADC’s 3.1V limit. Use a resistor voltage divider — two equal resistors (e.g., 100kΩ each) halve the voltage so 4.2V becomes 2.1V at the ADC pin. Scale the reading back in code:
int mV = analogReadMilliVolts(1);float batteryV = (mV / 1000.0) * 2.0; // 2.0 = divider ratioAny analog sensor outputting 0–3.1V — soil moisture, flex sensors, gas sensors, force-sensitive resistors — can be read directly on an ADC1 pin.
Tips for Better Readings
Section titled “Tips for Better Readings”Average multiple samples. A single ADC read can be noisy. Reading 16–64 samples and averaging gives much more stable values:
int readAvg(int pin, int samples) { long sum = 0; for (int i = 0; i < samples; i++) { sum += analogReadMilliVolts(pin); } return sum / samples;}Add a 100 nF capacitor. Solder or place a small ceramic capacitor between your ADC input pin and GND. This filters out high-frequency noise at the hardware level and is recommended by Espressif.
Know when to use an external ADC. The built-in ADC is great for potentiometers, approximate battery levels, and anything where ±2–5% accuracy is fine. For precision measurement (current sensing, load cells, scientific instruments), use an external ADC like the ADS1115 — a 16-bit I2C module (~$4 breakout) with 65,536 levels of resolution and excellent linearity. The tradeoff: the ADS1115 maxes out at 860 samples/second versus ~100,000 for the built-in ADC.
Quick Reference
Section titled “Quick Reference”| Feature | ESP32-S3 |
|---|---|
| Resolution | 12-bit (0–4095) |
| ADC1 pins (WiFi-safe) | GPIO 1–10 |
| ADC2 pins (no WiFi) | GPIO 11–20 |
| Default voltage range | 0 – 3.1V (11 dB attenuation) |
| Accuracy (calibrated) | ±30 mV in usable range |
| Non-linear above | ~2750 mV |
| Best function | analogReadMilliVolts(pin) |
| Max sample rate | ~100,000 samples/sec |
Learn More
Section titled “Learn More”- Read Analog Sensor Values — hands-on ADC tutorial
- What is GPIO? — the pins that ADC reads from
- What is DMA/FastADC? — high-speed continuous ADC sampling
- What is a DAC? — the opposite of an ADC (digital to analog)
- Random Nerd Tutorials: ESP32 ADC — detailed external guide with wiring diagrams
Video: ESP32 ADC with a Potentiometer
Section titled “Video: ESP32 ADC with a Potentiometer”Robojax — step-by-step walkthrough of ADC pins, potentiometer wiring, analogRead() code, and a live Serial Monitor demo. About 12 minutes.