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 has two 12-bit ADCs (ADC 1 & ADC 2) and supports a maximum of 18 analog channels.
Resolution | 12 bit |
Voltage | 0-3.3V |
Assigned Value | 0-4095 |
Analog Pin | GPIO0, GPIO2, GPIO4, GPIO12, GPIO13, GPIO14, GPIO15,GPIO 25,GPIO 26, GPIO27, GPIO32, GPIO33,GPIO34, GPIO35, GPIO36, and GPIO39 |
Changing Resolution | Can 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 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.
Attenuation | Attenuation Parameter | Input Voltage Range |
---|---|---|
0dB | ADC_ATTEN_DB_0 | ~100-950 mV |
2.5dB | ADC_ATTEN_DB_2_5 | ~100-1250mV |
6dB | ADC_ATTEN_DB_6 | ~150-1750mV |
11dB | ADC_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.
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:
- ESP32 board
- Breadboard
- Potentiometer
- Connecting Wire
The diagram below shows how to setup the circuit for this project.
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.
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);
}
}
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
After the code flashes successfully, the ADC values (0-4095) will appear on the Terminal. Rotate the potentiometer knob to vary the readings.
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);
}
}
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.
You may also like to read:
- Getting Started with ESP32 using ESP-IDF
- ESP32 GPIO with ESP-IDF with LED Blinking example
- ESP32 Push Button with ESP-IDF (Digital Input)
- ESP32 PWM ESP-IDF LED Brightness Control Example
We are a team of experienced Embedded Software Developers with skills in developing Embedded Linux, RTOS, and IoT products from scratch to deployment with a demonstrated history of working in the embedded software industry. Contact us for your projects: admin@esp32tutorials.com
thank you soo much.
Very clear guide, I learn a lot from this guide. Thank you!
Hi, very clear, but is this useful with esp32-C6 board? Thank you!
Does this work for ECP32-C6?
Thans.
It should work with ESP32-C6.
Honestly this is the best site on the entire internet to learn how to program Esp32 using the esp-id framework. Congratulations
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.