ESP32 ADC with ESP-IDF Measure Analog Inputs

In this tutorial, we will learn to use ADC of ESP32 using ESP-IDF. In other words, we will learn to read Analog values using Analog to Digital Converter module of ESP32 using VS Code with ESP-IDF extension. To provide an analog signal to ADC channel, we will interface a potentiometer with the ESP32 board and read its Analog value respectively. We will show you how to output ESP32 ADC values and convert them into analog voltage readings with the help of two example sketches.

Before we move forward, make sure you have the latest version of VS Code installed on your system with the ESP-IDF extension configured.

ESP32 ADC

ESP32 ADC can read analog values between 0-3.3V. ADC Converts analog signals into digital values. The measured voltage signal is given a value between 0 and 4095. Accordingly, a value of 0 equals to 0V, while the maximum value 4095 equals to 3.3V. Any intermediate related values will likewise be assigned in the appropriate manner.
There is a tiny drawback, though. The ADC module is not particularly sensitive to value changes. Its nature is non-linear. Very close values will therefore match up with similar voltages. For instance, the voltage readings 3.3V and 3.2V will both be represented as the ADC value of 4095.

The non-linear relationship between the voltage value and the ADC value is seen in the following graph.

ESP32 ADC vs Voltage Graph

 ESP32 has two 12-bit ADCs (ADC 1 & ADC 2) and supports a maximum of 18 analog channels.

Resolution12 bit
Voltage0-3.3V
Assigned Value0-4095
Analog PinGPIO0, GPIO2, GPIO4, GPIO12, GPIO13, GPIO14, GPIO15,GPIO 25,GPIO 26, GPIO27, GPIO32, GPIO33,GPIO34, GPIO35, GPIO36, and GPIO39
Changing ResolutionCan change

The ESP32 board has 8 channels for ADC1 but the DEVKIT V1 only supports 6 of them. ADC2 has 10 analog channels. The analog pins of both of these channels are listed below:

ADC1 Pins

  • ADC1_CH0 :GPIO 36
  • ADC1_CH1 :GPIO 37  (NOT AVAILABLE)
  • ADC1_CH2 :GPIO 38  (NOT AVAILABLE)
  • ADC1_CH3 :GPIO 39
  • ADC1_CH4 :GPIO 32
  • ADC1_CH5 :GPIO 33
  • ADC1_CH6 :GPIO 34
  • ADC1_CH7 :GPIO 35

ADC2 Pins

  • ADC2_CH0 :GPIO 4
  • ADC2_CH1 :GPIO 0
  • ADC2_CH2 :GPIO 2
  • ADC2_CH3 :GPIO 15
  • ADC2_CH4 :GPIO 13
  • ADC2_CH5 :GPIO 12
  • ADC2_CH6 :GPIO 14
  • ADC2_CH7 :GPIO 27
  • ADC2_CH8 :GPIO 25
  • ADC2_CH9 :GPIO 26

The following diagram shows the analog pins for ESP32 DEVKIT V1:

ESP32 Pinout ADC Channels

ESP32 ESP-IDF ADC APIs

Before we move ahead, let us discuss some important functions that are required to access ADC channels of ESP32.

ESP-IDF provides driver/adc.h and esp_adc_cal.h libraries that are required for the ADC driver and ADC calibration respectively. For this project, we will be required to set the ADC input voltage range, ADC calibration, ADC bit width, obtain ADC value and convert it to voltage reading. Let’s see how it will be accomplished. 

The first step is to include the header files:

#include "driver/adc.h"
#include "esp_adc_cal.h"

Set ESP32 ADC Input Voltage Range

The next step is to set the ADC input voltage range. This range corresponds to the attenuation parameter that is set. The range of ADC thus changes with this parameter. We have an option of setting the attenuation parameter from four options which are listed below. Notice how the ADC input range changes with the attenuation that is selected.

AttenuationAttenuation ParameterInput Voltage Range
0dBADC_ATTEN_DB_0~100-950 mV
2.5dBADC_ATTEN_DB_2_5~100-1250mV
6dBADC_ATTEN_DB_6~150-1750mV
11dBADC_ATTEN_DB_11~150-2450mV

To set an input voltage range for ADC1, we use the function, adc1_config_channel_atten() and specify two arguments inside it. The first argument is the ADC1 channel written in the form ADC1_CHANNEL_X where ‘X’ denotes the channel number. If you are measuring analog signal from pin GPIO35, it will be referred to as ADC1_CHANNEL_7 The second argument is the attenuation parameter as listed in the table above.

In the following line of code, we set the attenuation parameter of ADC1 channel 0 which is GPIO36 to 11db.

adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_DB_11);

Similarly, to set an input voltage range for ADC2, we use the function, adc2_config_channel_atten() and specify two arguments inside it. The first argument is the ADC2 channel written in the form ADC2_CHANNEL_X where ‘X’ denotes the channel number. If you are measuring analog signal from pin GPIO26, it will be referred as ADC1_CHANNEL_9. The second argument is the attenuation parameter as listed in the table above.

In the following line of code, we are setting the attenuation parameter of ADC2 channel 0 which is GPIO4 to 11db.

adc2_config_channel_atten(ADC2_CHANNEL_0, ADC_ATTEN_DB_11);

Calibrate ESP32 ADC

Next up we will show you how to do the ADC calibration. This is an important step because the ESP32 chips vary in terms of their internal reference voltages. For ADC calibration, we follow two steps. First create an instance of esp_adc_cal_characteristics_t called ‘adc1_chars.’ This will hold the ADC calibrated value. Next use the function esp_adc_cal_characterize() and specify 4 arguments inside it as shown below.

In the following line of code we are calibrating ADC1 at 11dB attenuation.

esp_adc_cal_characteristics_t adc1_chars;

esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_DEFAULT, 0, &adc1_chars);

Configure ESP32 ADC Bit Width

Another step is to set the ADC bit width. For ADC1, the function adc1_config_width() is used to configure the ADC bit width. This takes in a single argument which is the bit width that we want to set. We can choose the following values for bit width:

  • ADC_WIDTH_BIT_DEFAULT (12-bit)
  • ADC_WIDTH_BIT_9
  • ADC_WIDTH_BIT_10
  • ADC_WIDTH_BIT_11
  • ADC_WIDTH_BIT_12

In the following line of code we are configuring ADC1 at the default bit width i.e. 12 bits.

adc1_config_width(ADC_WIDTH_BIT_DEFAULT);

Obtain raw ADC Value

Now to obtain the ADC reading for ADC1, we use the adc1_get_raw() function and specify as an argument, the ADC1 channel written in the form ADC1_CHANNEL_X where ‘X’ denotes the channel number.

In the following line of code we are obtaining the raw ADC1 value of channel 0. This value is saved in the integer variable ‘adc_value’.

int adc_value = adc1_get_raw(ADC1_CHANNEL_0);

Similarly, to obtain the ADC reading for ADC2, we use the adc2_get_raw() function and specify as an argument, the ADC2 channel written in the form ADC2_CHANNEL_X where ‘X’ denotes the channel number.

In the following line of code we are obtaining the raw ADC2 value of channel 0. This value is saved in the integer variable ‘adc_value’.

int adc_value = adc2_get_raw(ADC2_CHANNEL_0);

Note: Make sure to configure the ADC bit width before obtaining the raw ADC value.

Voltage Reading from raw ADC Value

Finally, to convert the raw ADC value to a voltage reading in millivolts, we use the esp_adc_cal_raw_to_voltage() function. This takes in two arguments. The first argument is the raw ADC value acquired from the function: adc1_get_raw() for ADC1 and adc2_get_raw for ADC2. The second argument is the pointer to the ADC calibration result obtained from esp_adc_cal_characterize() which we used to calibrate ADC.

In the following line of code we are obtaining the voltage reading (mV) of ADC1 channel 0.

uint32_t mV = esp_adc_cal_raw_to_voltage(adc1_get_raw(ADC1_CHANNEL_0), &adc1_chars);

Read Analog Inputs with ESP32 ESP-IDF

We will now discover how to use a variable resistor, or potentiometer, to read the analog inputs from ESP32. The connections of the potentiometer are shown in the diagram below. The two terminals on the sides are connected to VCC and ground, respectively, while the middle terminal provides the output.

Potentiometer Pinout

The potentiometer will be connected to an analog channel on our ESP32 board, and its voltage will then be measured. Varying input voltages could be generated by rotating the potentiometer’s top knob, and the ADC module in our ESP32 board would then convert those variable input voltages to their corresponding digital values.

The following components are required for our ESP32 ADC project:

  1. ESP32 board
  2. Breadboard
  3. Potentiometer
  4. Connecting Wire

The diagram below shows how to setup the circuit for this project.

ESP32 with Potentiometer ADC Measurement connection diagram

The potentiometer is powered by 3.3V from ESP32. Its center terminal is connected to GPIO32 which is ADC1_CH4. The last terminal is connected with the GND pin of ESP32.

This is how our circuit looks like after connecting all the components.

ESP32 ADC ESP-IDF

VS Code ESP32 Measure Raw ADC Values with ESP-IDF

Open your VS Code and create a new ESP-IDF project. Now head over to the main.c file. We will define the functions and the program code here.

EPS32 ADC Code

We will use this script to measure the raw ADC values acquired from ADC1 Channel 4.

#include <stdio.h>
#include <stdlib.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/adc.h"
#include "esp_adc_cal.h"


static esp_adc_cal_characteristics_t adc1_chars;

void app_main(void)
{

    esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_DEFAULT, 0, &adc1_chars);

    adc1_config_width(ADC_WIDTH_BIT_DEFAULT);
    adc1_config_channel_atten(ADC1_CHANNEL_4, ADC_ATTEN_DB_11);

    while (1) 
    {
        int adc_value = adc1_get_raw(ADC1_CHANNEL_4);
        printf("ADC Value: %d", adc_value);
        printf("\n");
        vTaskDelay(500/ portTICK_PERIOD_MS);
    }
}
ESP32 with Potentiometer Measure ADC Values script

How Code Works?

Firstly, we will start by including the necessary libraries for this project. This includes the ADC driver, ADC calibration and FreeRTOS libraries. The driver/adc.h and esp_adc_cal.h libraries are required for the ADC driver and ADC calibration respectively.

#include <stdio.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/adc.h"
#include "esp_adc_cal.h"

Now we calibrate ADC1 at 11dB attenuation using esp_adc_cal_characterize() function. Create an instance of esp_adc_cal_characteristics_t called ‘adc1_chars’ outside the loop. This will hold the ADC calibrated value.

static esp_adc_cal_characteristics_t adc1_chars;
esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_DEFAULT, 0, &adc1_chars);

Next set the ADC bit width for ADC1 at the default bit width using the function adc1_config_width().

adc1_config_width(ADC_WIDTH_BIT_DEFAULT);

Configure the attenuation parameter of ADC1 channel 4 to 11db. This sets the ADC input voltage range to approximately 150- 2450mV.

adc1_config_channel_atten(ADC1_CHANNEL_4, ADC_ATTEN_DB_11);

Inside the infinite loop, we will acquire the raw ADC value using ad1_get_raw() function and specify the ADC channel number as a parameter inside it. This gets saved in the integer variable called ‘adc_value’. The ADC value will get printed in the Serial Terminal after a delay of 500ms.


    while (1) 
    {
        int adc_value = adc1_get_raw(ADC1_CHANNEL_4);
        printf("ADC Value: %d", adc_value);
        printf("\n");
        vTaskDelay(500/ portTICK_PERIOD_MS);
    }

Compiling the Sketch

To flash your chip, type the following command in the serial terminal. Remember to replace the COM port with the one through which your board is connected.

idf.py -p COMX flash monitor
ESP32 with Potentiometer Measure ADC Values flash code

After the code flashes successfully, the ADC values (0-4095) will appear on the Terminal. Rotate the potentiometer knob to vary the readings.

ESP32 with Potentiometer Measure ADC Values serial terminal

VS Code ESP32 Convert Raw ADC Values to Voltage Readings with ESP-IDF

Next, we will show you how to obtain voltage readings(mV) from the raw ADC values that we obtained in the previous example. Open your VS Code and create a new ESP-IDF project. Now head over to the main.c file. We will define the functions and the program code here.

ESP32 ADC Voltage Measurement Code

We will use this script to measure the raw ADC values acquired from ADC1 Channel 4 and convert them to voltage readings in millivolts.

#include <stdio.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/adc.h"
#include "esp_adc_cal.h"


static esp_adc_cal_characteristics_t adc1_chars;

void app_main(void)
{
    esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_DEFAULT, 0, &adc1_chars);

    adc1_config_width(ADC_WIDTH_BIT_DEFAULT);
    adc1_config_channel_atten(ADC1_CHANNEL_4, ADC_ATTEN_DB_11);
    uint32_t voltage;

    while (1) 
    {
        int adc_value = adc1_get_raw(ADC1_CHANNEL_4);
        voltage = esp_adc_cal_raw_to_voltage(adc_value, &adc1_chars);
        printf("Voltage: %d mV", voltage);
        printf("\n");
        vTaskDelay(500/ portTICK_PERIOD_MS);
    }
}
ESP32 with Potentiometer Convert ADC Values to Voltages script

How Code Works?

Most of the code is similar to the one in the previous example. We will just show you how we converted the raw ADC values to obtain the voltage readings.

Inside the infinite loop, we will acquire the raw ADC value using ad1_get_raw() function and specify the ADC channel number as a parameter inside it. This gets saved in the integer variable called ‘adc_value’.

Next we will convert the raw ADC value to voltage(mV) through the esp_adc_cal_raw_to_voltage() function. This takes in two arguments. The first argument is the raw ADC value saved in the integer variable ‘adc_value’. The second argument is the pointer to the ADC calibration result obtained from esp_adc_cal_characterize() which we used to calibrate ADC. The voltage readings are printed on the Serial Terminal after every 500ms.

    while (1) 
    {
        int adc_value = adc1_get_raw(ADC1_CHANNEL_4);
        voltage = esp_adc_cal_raw_to_voltage(adc_value, &adc1_chars);
        printf("Voltage: %d mV", voltage);
        printf("\n");
        vTaskDelay(500/ portTICK_PERIOD_MS);
    }

Compiling the Sketch

To flash your chip, type the following command in the serial terminal. Remember to replace the COM port with the one through which your board is connected.

idf.py -p COMX flash monitor

After the code flashes successfully, the Voltage readings in millivolts will appear on the Terminal. Rotate the potentiometer knob to see how the analog voltage varies.

ESP32 with Potentiometer Convert ADC Values to Voltages serial terminal

You may also like to read:

7 thoughts on “ESP32 ADC with ESP-IDF Measure Analog Inputs”

  1. Honestly this is the best site on the entire internet to learn how to program Esp32 using the esp-id framework. Congratulations

    Reply
  2. How can I modify the sample rate of the ADC channel? Do you know any configuration register for the sampling frequency? By the way, thanks for your nice explanation.

    Reply

Leave a Comment