Language: EN

esp32-adc

How to use the ADC analog inputs in an ESP32

All ESP32 models have two 12-bit SAR (Successive Approximation Register) ADC (analog-to-digital converter), called ADC1 and ADC2.

With a 12-bit resolution, a resolution of 3.3 volts / 4096 units is equivalent to 0.8 mV per step. In addition, we can programmatically configure the ADC resolution and channel range.

The maximum allowable voltage is 3V3. Although many ESP32 GPIOs are tolerant to 5V, the ADC is not. So, do not input more than 3.6V into a pin used as an analog input, or you will damage the ADC.

The number of pins will depend on the model of ESP32 being used. For example, the ESP32 has 18 pins, organized as follows:

  • ADC1 connected to 8 GPIOs (32-39)
  • ADC2 connected to 10 GPIOs (0, 2, 4, 12-15 and 25-27)

While an ESP32-S3 has 20 analog pins, organized as:

  • ADC1 connected to 10 GPIO (1-10)
  • ADC2 connected to 10 GPIO (11-20)

Additionally, depending on your development board, not all pins may be available on your board. When in doubt, consult the documentation for your specific model. [read more ⯈]{.badge .grey}

On the other hand, very important, the ADC2 is used by the Wi-Fi module, so we cannot use the ADC2 pins when Wi-Fi is enabled. If your project requires Wi-Fi, only use the ADC1 pins.

How to read the analog input of the ESP32

Under the Arduino environment, reading an analog input in the ESP32 is exactly the same as we would do it in a “conventional” Arduino.

We just have to use the analogRead(GPIO) function. Which accepts as a single argument the pin to which the sensor is connected.

For example, this is how we would read the analog value of pin A0 and display it via serial port,

void setup() 
{
  Serial.begin(9600);
}

void loop() 
{
  int sensorValue = analogRead(A0);
  Serial.println(sensorValue);
  delay(100);
}

Otherwise, the operation is similar to the analog inputs in Arduino. We can read voltages and resistances or potentiometers.

More useful functions

In addition to the normal functionality we would find in a “regular” Arduino, the ESP32 offers these additional functions,

// Set the sample bits and resolution.
// It can be a value between 9 (0 – 511) and 12 bits (0 – 4095).
// Default resolution: 12 bits.
analogReadResolution(resolution);

// Set the sample bits and resolution.
// It can be a value between 9 (0 – 511) and 12 bits (0 – 4095).
// Default resolution: 12 bits.
analogSetWidth(width);

// Set the number of cycles per sample.
// Default value: 8. Range: 1 to 255.
analogSetCycles(cycles);

// Set the number of samples in the range.
// It has the effect of increasing sensitivity.
// Default value: 1 sample.
analogSetSamples(samples);

// Set the divisor for the ADC clock.
// Default value: 1. Range: 1 to 255.
analogSetClockDiv(attenuation);

// Set the input attenuation for all ADC pins.
// Default value: ADC_11db.
// Accepted values:
// - ADC_0db: no attenuation
// - ADC_2_5db: extended attenuation
// - ADC_6db: extended attenuation
// - ADC_11db: extended attenuation
analogSetAttenuation(attenuation);

// Set the input attenuation for the specified pin.
// Default value: ADC_11db.
// Attenuation values equal to the previous function.
analogSetPinAttenuation(pin, attenuation);

// Attach a pin to the ADC (also clears any other active analog mode).
// Returns a TRUE or FALSE result.
adcAttachPin(pin);

// Start an ADC conversion on the pin bus associated.
// Verify whether the ADC conversion on the pin bus is currently in progress.
// Returns TRUE or FALSE.
// Get the result of the conversion: returns a 16-bit integer.
adcStart(pin);
adcBusy(pin);
adcEnd(pin);

ADC attenuation values

To perform the measurement, the ESP32 compares the voltage we want to measure with a reference value Vref which, by design, is 1.1V.

To allow measuring a range of higher voltages, the ESP32 uses a variable gain attenuation circuit. At -11dB, which is the default value to measure up to 3V3.

The behavior of the ESP32 ADC in its different variants has never been one of its strong points. See below ‘ADC Precision’. Therefore, the manufacturer recommends “optimal” values for the voltages that we can measure with the ADC.

ESP32 Attenuation Values

AttenuationPreferred voltage range
ADC_ATTEN_DB_0100 mV ~ 950 mV
ADC_ATTEN_DB_2_5100 mV ~ 1250 mV
ADC_ATTEN_DB_6150 mV ~ 1750 mV
ADC_ATTEN_DB_11150 mV ~ 2450 mV

ESP32-S3 Attenuation Values

AttenuationPreferred voltage range
ADC_ATTEN_DB_0100 mV ~ 950 mV
ADC_ATTEN_DB_2_5100 mV ~ 1250 mV
ADC_ATTEN_DB_6150 mV ~ 1750 mV
ADC_ATTEN_DB_11150 mV ~ 2450 mV

ADC Precision in the ESP32

Much has been said, and not for the better, about the precision of the ESP32 ADC. The reality is that, traditionally, it is not one of the strong points of the ESP32.

However, many of the things that have been said are not current today and only apply to the “normal” ESP32 (not the S2, S3… etc).

The problem with the ESP32 is that, as we mentioned before, the ESP32 compares with an internal Vref value of 1.1V. However, the boards come out with different variations that make that 1.1V really be 1.0 to 1.2V.

On the other hand, at -11dB, the default attenuation value to measure up to 3V3, the attenuation circuit introduces a strongly non-linear behavior.

That is, the response we have is something like this. Quite poor when operating at -11dB.

esp32-adc-response

It is possible to calibrate the ESP32 to improve the behavior of the ADC. Here is a good repository where they explain how to calibrate it easily GitHub - e-tinkers/esp32-adc-calibrate.

However, what is usually not said is that from 2019, the ESP32s come precalibrated from the factory and the code was changed to add a software correction. So the behavior is much better.

On the other hand, in the case of the ESP32-S3, the situation is different. This model includes an internal hardware calibration chip, so the response of the ESP32-S3 is something like this.

esp32-s3-adc-response

Which is much better than what we had in the old (non-version) ESP32.

Finally, even in the old model, the importance of the lack of precision of the ESP32 should be put into perspective, depending on what you really need in your project.

That is, if you want the ADC, for example, to read a potentiometer and change the lighting of an LED, or the speed of a motor, it probably works perfectly even if it is not calibrated.

If you need a really precise measurement, you probably shouldn’t use the ADC of any processor “as is”, neither the ESP32 nor a “conventional” Arduino.

In that case, you can calibrate the ADC, or use a high-precision ADC like the AD1115 we saw in this post 16-bit analog input with Arduino and ADC ADS1115



References: