Measuring analog inputs with ESP32 ADC (ESP-IDF)

Introduction

In this article, you will learn about ESP32 Analog to Digital Converter. ESP32 has two ADC modules (named ADC1 and ADC2). There are 8 IO pins that are connected to ADC1 and 10 pins that are connected to ADC2. To measure analog input signals, you have to connect them to IO pins that are connected to either of the ADC modules. You will learn what those pins are, how to do calibration for ADC, how to use APIs in ESP-IDF to configure the ADCs. You will implement a simple project using ADC to read an analog input pin and print values to a console.

ESP-IDF ADC APIs

ADC libraries

When working with ADC using ESP-IDF, you need to include two libraries: the adc driver defined in components/driver/include/driver/adc.h and the library for ADC calibration esp_adc_cal.h. You need to include the following headers in your code

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

ADC Pins

The following table describes the IO pins that are associated with ADC1 and ADC2 modules. As can be seen from the table, there are 8 ADC1 channels, numbered from 0 to 7 and 10 ADC2 channels, numbered from 0 to 9. If you are measuring analog signal from pin GPIO34, for example, you will refer to it as ADC1_CHANNEL_6 in your code.

ADC1 channelGPIO pinADC2 channelGPIO pin
ADC1_CHANNEL_0GPIO36ADC2_CHANNEL_0GPIO4
ADC1_CHANNEL_1GPIO37ADC2_CHANNEL_1GPIO0
ADC1_CHANNEL_2GPIO38ADC2_CHANNEL_2GPIO2
ADC1_CHANNEL_3GPIO39ADC2_CHANNEL_3GPIO15
ADC1_CHANNEL_4GPIO32ADC2_CHANNEL_4GPIO13
ADC1_CHANNEL_5GPIO33ADC2_CHANNEL_5GPIO12
ADC1_CHANNEL_6GPIO34ADC2_CHANNEL_6GPIO14
ADC1_CHANNEL_7GPIO35ADC2_CHANNEL_7GPIO27
ADC2_CHANNEL_8GPIO25
ADC2_CHANNEL_9GPIO26

ADC Input Voltage range

Before feeding into the ADC modules, the analog signal is attenuated. Depending on the attenuation parameter, the range of ADC is different. The table below listed the attenuation parameters and their respective input ranges. The table shows that the ESP32 ADC only works reliably in certain range. This is a limitation of the ESP32 ADC.

Attenuation parameterInput range (mV)Note
ADC_ATTEN_DB_0100 ~ 950No attenuation (0dB)
ADC_ATTEN_DB_2_5100 ~ 12502.5 dB attenuation
ADC_ATTEN_DB_6150 ~ 17506 dB attenuation
ADC_ATTEN_DB_11150 ~ 245011 dB attenuation

To setup attenuation parameter for a channel of ADC1, you use the API adc1_config_channel_atten(). For example, to set 11 dB attenuation for ADC1 channel 6, you call

adc1_config_channel_atten(ADC1_CHANNEL_6, ADC_ATTEN_DB_11);

Similarly, to configure attenuation for a ADC2 channel, you use adc2_config_channel_atten() function in the adc driver.

ADC Calibration

Due to variation in internal reference voltage of different ESP32 chips, the ESP32 ADCs needs to be calibrated before using. One way of doing calibration is using the true ADC reference voltage stored in eFuse. This value is measured and written to the ESP32 when it was made at factory.

To calibrate the ADCs, you use the API esp_adc_cal_characterize(). For example, to calibrate ADC1 at 11 dB attenuation, you call the following function

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);

The ADC calibration result is stored in adc1_chars.

Configure ADC bit width

To configure the ADC1 bit width, you use the API adc1_config_width() and pass in the value of bit width. Accepted values are ADC_WIDTH_BIT_9, ADC_WIDTH_BIT_10, ADC_WIDTH_BIT_11, ADC_WIDTH_BIT_12 or ADC_WIDTH_BIT_DEFAULT. By default, ADC1 use 12-bit.

Capture raw ADC value

To get raw ADC1 value, you call adc1_get_raw() and specify the ADC1 channel you want to capture. You need to call adc1_config_width() before the first time this function is called. For example, to get raw channel 6 ADC1 raw value, you use

adc1_config_width(ADC_WIDTH_BIT_DEFAULT);
int raw_value = adc1_get_raw(ADC1_CHANNEL_6);

Similarly, to get raw ADC2 value, you use adc2_get_raw().

Convert raw value to mV (after calibration)

To convert the raw value to milivolt value and take into account calibration, you use the API esp_adc_cal_raw_to_voltage(). This function takes two parameters:

  • adc_reading is the raw value captured by adc1_get_raw() or adc2_get_raw()
  • char is a pointer to ADC characteristics obtained after calibration (after calling esp_adc_cal_characterize())

For example, you can use this snippet to get the mV value from ADC1 channel 6

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

ESP-IDF ADC Example

Project description

In this section, you will implement a simple project using ADC. Connect a potentiometer to GND, 3.3V and GPIO34 of ESP32. You will read and log the ADC value measured from GPIO34. As mentioned previously, GPIO34 corresponds to ADC1 Channel 6. So in the project, you will try to calibrate ADC1, then read raw value from its channel 6 and convert to milivolt value, and finally logs it to console every 100 ms.

Component Used

The following table describes the components used in the project

QTYComponent NameBuy on amazon.com
1ESP32 DevKit CAmazon
1PotentiometerAmazon
1BreadboardAmazon
1Jumper Wire KitAmazon

Affiliate Disclosure: When you click on links in this section and make a purchase, this may result in this site earning a commission at no extra cost to you.

Code

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

static const char *TAG = "ADC EXAMPLE";

static esp_adc_cal_characteristics_t adc1_chars;

void app_main(void)
{
    uint32_t voltage;

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

    ESP_ERROR_CHECK(adc1_config_width(ADC_WIDTH_BIT_DEFAULT));
    ESP_ERROR_CHECK(adc1_config_channel_atten(ADC1_CHANNEL_6, ADC_ATTEN_DB_11));

    while (1) 
    {
        voltage = esp_adc_cal_raw_to_voltage(adc1_get_raw(ADC1_CHANNEL_6), &adc1_chars);
        ESP_LOGI(TAG, "ADC1_CHANNEL_6: %d mV", voltage);
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

How the code works

If you have followed along with previous section, you can understand this piece of code easily. The code starts with calibrating ADC1 for 11dB attenuation using esp_adc_cal_characterize(). Then it configures ADC1 bit width and attenuation parameter using adc1_config_width() and adc1_config_channel_atten(), respectively. In the main loop, it repeatedly call adc1_get_raw() and esp_adc_cal_raw_to_voltage() to capture raw value and convert it to milivolt. Finally, it print logs using ESP_LOG module and delay 100 ms using vTaskDelay() API from freeRTOS.

Build, flash and run program

To build, flash and run program, you can run the command on a terminal

idf.py build flash monitor -p [PORT]

You should see output logs like this

Wrapping Up

In this article, you have learnt about ESP32 ADC and APIs in ESP-IDF to work with it. If you want to learn more, check out ESP32 official documentation. Thanks for reading.

1 thought on “Measuring analog inputs with ESP32 ADC (ESP-IDF)”

  1. Thank you for providing this article, but it appears to be out of date. The APIs have changed. It would be great if you could update the article to the latest-esp-idf and add some info on one-shot vs. continuous mode.

Leave a Comment