Chapter 126: ADC Configuration and Reading with ESP32
Chapter Objectives
By the end of this chapter, you will be able to:
- Understand the fundamental principles of Analog-to-Digital Conversion.
- Describe the ADC architecture of ESP32, ESP32-S2, ESP32-S3, ESP32-C3, ESP32-C6, and ESP32-H2 variants.
- Configure and initialize an ADC unit using the ESP-IDF
adc_oneshot
driver. - Configure specific ADC channels for reading.
- Read raw analog values from an ADC channel.
- Understand the concept of ADC attenuation and bitwidth.
- Recognize potential issues like ADC2 and Wi-Fi conflicts.
- Implement basic ADC reading in an ESP-IDF project.
Introduction
In the world of microcontrollers, the ability to interact with the analog world is crucial. Many sensors, such as temperature sensors, potentiometers, light sensors, and microphones, produce analog signals—voltages that vary continuously. Microcontrollers, however, operate in the digital domain, understanding only discrete values (0s and 1s). The Analog-to-Digital Converter (ADC) is the bridge between these two worlds. It converts a continuous analog voltage into a discrete digital number that the microcontroller can process.
The ESP32 family of System-on-Chips (SoCs) comes equipped with versatile ADCs, enabling a wide range of applications, from simple sensor readings in IoT devices to more complex signal processing tasks. This chapter will guide you through the essential steps of configuring and reading analog values using the ESP-IDF’s adc_oneshot
driver, which is the recommended approach for single, on-demand ADC readings in ESP-IDF v5.x.
Theory
What is an Analog-to-Digital Converter (ADC)?
An ADC is an electronic circuit that converts a continuous analog voltage input into a discrete digital representation. Key characteristics of an ADC include:
- Resolution: This determines the number of discrete digital values the ADC can produce. It’s typically expressed in bits. An N-bit ADC can represent 2N distinct digital levels. For example, a 12-bit ADC can produce 212 = 4096 discrete values. A higher resolution means smaller voltage steps can be detected, leading to a more accurate digital representation of the analog signal.
- Sampling Rate: This is the number of times per second the ADC converts the analog input to a digital value. For this chapter, we’ll focus on “one-shot” or “single” conversions, where we trigger a conversion when needed, rather than continuous sampling at a high rate.
- Input Voltage Range: This is the range of analog voltages (e.g., 0V to 3.3V) that the ADC can convert. Voltages outside this range may not be accurately converted or could even damage the ADC.
- Reference Voltage (Vref): The ADC compares the input analog voltage to a reference voltage to perform the conversion. The accuracy of Vref directly impacts the accuracy of the conversion. The maximum digital value (e.g., 4095 for a 12-bit ADC) typically corresponds to Vref.
- Conversion Formula (Simplified):Digital Value = (Input Analog Voltage / Vref) * (2Resolution – 1)
Characteristic | Description | Impact on Measurement |
---|---|---|
Resolution | The number of discrete digital values an ADC can produce, typically expressed in bits (e.g., 12-bit means 212 = 4096 values). | Higher resolution allows for detection of smaller voltage changes, leading to more accurate digital representation. |
Sampling Rate | How many times per second the ADC converts an analog input to a digital value. | Determines how quickly the ADC can capture changes in the analog signal. Important for dynamic signals. |
Input Voltage Range | The range of analog voltages (e.g., 0V to 3.3V) that the ADC can accurately convert. | Voltages outside this range may be clipped, inaccurate, or potentially damage the ADC. Must match sensor output. |
Reference Voltage (Vref) | The voltage used by the ADC as a comparison point for conversion. The maximum digital value corresponds to Vref. | The accuracy and stability of Vref directly impact the accuracy of the entire conversion process. |
Attenuation | An internal scaling mechanism that allows the ADC to measure input voltages higher than its internal reference by scaling them down. | Configuring correct attenuation ensures the input voltage falls within the ADC’s effective measurement range without saturation. |
ESP32 ADC Architecture
Most ESP32 variants are equipped with two Successive Approximation Register (SAR) ADCs: ADC1 and ADC2. However, their availability and channel count can vary.
- ADC Units:
ADC_UNIT_1
(ADC1)ADC_UNIT_2
(ADC2)
- Channels: Each ADC unit has multiple input channels. A channel is typically connected to a specific GPIO pin that can be configured as an analog input. The number of available channels varies by ESP32 variant (see Variant Notes).
- Resolution: The ADCs on ESP32 variants typically support configurable resolutions, commonly up to 12-bit or 13-bit. This means they can output digital values from 0 up to 4095 (for 12-bit) or 8191 (for 13-bit).
- Attenuation: The ESP32 ADCs have a configurable input attenuation. Attenuation allows the ADC to measure voltages higher than its internal reference voltage by scaling them down. Common attenuation levels are:
ADC_ATTEN_DB_0
: No attenuation (input voltage range typically around 0-1.1V, varies slightly by chip).ADC_ATTEN_DB_2_5
: 2.5 dB attenuation (input voltage range typically around 0-1.5V).ADC_ATTEN_DB_6
: 6 dB attenuation (input voltage range typically around 0-2.2V).- ADC_ATTEN_DB_11: 11 dB attenuation (input voltage range typically around 0-3.3V, or VDD_A for the chip). This is the most common setting for measuring signals up to the supply voltage.The exact voltage ranges for each attenuation level can be found in the respective ESP32 variant’s datasheet and are subject to calibration for accuracy.
Attenuation Level | Approximate Input Voltage Range (V) | Description |
---|---|---|
ADC_ATTEN_DB_0 | 0 – 1.1V | No attenuation. Suitable for low-voltage signals. |
ADC_ATTEN_DB_2_5 | 0 – 1.5V | 2.5 dB attenuation. Slightly extends the measurable range. |
ADC_ATTEN_DB_6 | 0 – 2.2V | 6 dB attenuation. Provides a wider range for common sensor outputs. |
ADC_ATTEN_DB_11 | 0 – 3.3V (VDD_A) | 11 dB attenuation. Most common setting for measuring signals up to the ESP32’s supply voltage. |
ADC2 and Wi-Fi:
Warning: On ESP32, ESP32-S2, and ESP32-S3, ADC2 has a significant limitation: its channels cannot be reliably used when Wi-Fi is active. The Wi-Fi driver uses ADC2 for its internal operations. If you need to use ADC while Wi-Fi is enabled, you must use ADC1 channels. For ESP32-C3, ESP32-C6, and ESP32-H2, this limitation is less pronounced or ADC2 has fewer general-purpose channels. Always consult the latest ESP-IDF documentation for your specific variant.
graph TD A[<b>Start ADC Reading Operation</b>] --> B{<b>Target ESP32 Variant?</b>} B -- ESP32, S2, S3 --> C{<b>Is Wi-Fi Currently Active?</b>} B -- ESP32-C3, C6, H2 --> F["<b>Use ADC1 or ADC2 Channels</b><br><i>(ADC2 has fewer general-purpose channels or less conflict)</i>"] C -- Yes --> D{<b>Need ADC2 Channel?</b>} C -- No --> E["<b>Use ADC1 or ADC2 Channels</b><br><i>(No Wi-Fi conflict)</i>"] D -- Yes --> D1["<b>Option 1: Stop Wi-Fi Temporarily</b><br><i>(esp_wifi_stop())</i>"] D1 --> D2[<b>Perform ADC2 Read</b>] D2 --> D3["<b>Restart Wi-Fi</b><br><i>(esp_wifi_start())</i>"] D3 --> G[<b>Continue Application</b>] D -- No --> E E --> G F --> G G --> H[<b>End ADC Operation</b>] %% Styling classDef primary fill:#EDE9FE,stroke:#5B21B6,stroke-width:2px,color:#5B21B6; classDef decision fill:#FEF3C7,stroke:#D97706,stroke-width:1px,color:#92400E; classDef process fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF; classDef endo fill:#D1FAE5,stroke:#059669,stroke-width:2px,color:#065F46; classDef warning fill:#FEE2E2,stroke:#DC2626,stroke-width:1px,color:#991B1B; class A primary; class B decision; class C decision; class D decision; class D1 process; class D2 process; class D3 process; class E process; class F process; class G process; class H endo;
ESP-IDF ADC adc_oneshot
Driver
ESP-IDF v5.x provides the adc_oneshot
driver for performing single, on-demand ADC readings. This is simpler to use than the continuous mode driver when you only need occasional readings.
Key components of the adc_oneshot
driver:
Initialization Configuration (adc_oneshot_unit_init_cfg_t):
This structure is used to configure an ADC unit (ADC1 or ADC2) before it can be used.
typedef struct {
adc_unit_t unit_id; /*!< ADC unit id */
adc_ulp_mode_t ulp_mode; /*!< ADC ULP mode, not used in one-shot mode, set to ADC_ULP_MODE_DISABLE */
// ESP-IDF v5.1 and later might have more fields for unit level config like bitwidth.
// For older v5.x, bitwidth is often set at channel config or defaults.
} adc_oneshot_unit_init_cfg_t;
For ESP-IDF 5.0, the bitwidth was typically configured per channel. In later versions (e.g., 5.1+), there might be options to set a default unit-level bitwidth or other unit-level parameters. Always refer to the esp_adc/adc_oneshot.h
header for the exact structure definition in your ESP-IDF version.
Channel Configuration (adc_oneshot_chan_cfg_t):
This structure is used to configure a specific channel within an initialized ADC unit.
typedef struct {
adc_atten_t atten; /*!< ADC attenuation */
adc_bitwidth_t bitwidth; /*!< ADC conversion result bits */
} adc_oneshot_chan_cfg_t;
atten
: Sets the attenuation for the channel (e.g.,ADC_ATTEN_DB_11
).bitwidth
: Sets the resolution for the channel (e.g.,ADC_BITWIDTH_12
orADC_BITWIDTH_DEFAULT
).ADC_BITWIDTH_DEFAULT
usually corresponds to the maximum hardware resolution of the ADC unit.
Key Functions:
esp_err_t adc_oneshot_new_unit(const adc_oneshot_unit_init_cfg_t *init_config, adc_oneshot_unit_handle_t *ret_handle)
: Initializes an ADC unit and returns a handle.esp_err_t adc_oneshot_config_channel(adc_oneshot_unit_handle_t handle, adc_channel_t chan, const adc_oneshot_chan_cfg_t *config)
: Configures a specific channel on the initialized ADC unit.esp_err_t adc_oneshot_read(adc_oneshot_unit_handle_t handle, adc_channel_t chan, int *out_raw)
: Reads a raw digital value from the configured ADC channel.esp_err_t adc_oneshot_del_unit(adc_oneshot_unit_handle_t handle)
: Deinitializes an ADC unit and frees resources.
ADC Calibration
Raw ADC readings can be affected by variations in the chip’s internal reference voltage and other non-linearities. For accurate voltage measurements, ADC calibration is essential. ESP-IDF provides an adc_cali
component to help compensate for these variations.
While this chapter focuses on obtaining raw ADC values, it’s important to know that these raw values might not directly correspond to precise voltage levels without calibration. Chapter 127, “Advanced ADC Techniques and Calibration,” will delve into this topic in detail. For now, we will work with raw values, understanding that they represent a proportional measure of the input voltage.
graph TD subgraph Input & Pre-processing A["<b>Analog Sensor Input</b><br><i>(Continuous Voltage)</i>"] --> B("<b>GPIO Pin</b><br><i>(Configured as ADC Channel)</i>") B --> C{"<b>ADC Unit Selection</b><br><i>(ADC1 or ADC2)</i>"} end subgraph ADC Configuration C --> C1["<b>ADC Unit Initialization</b><br><i>(adc_oneshot_new_unit)</i>"] C1 --> C2["<b>Channel Configuration</b><br><i>(adc_oneshot_config_channel)</i>"] C2 --> C3("<b>Attenuation Setting</b><br><i>(e.g., ADC_ATTEN_DB_11)</i>") C2 --> C4("<b>Bitwidth Setting</b><br><i>(e.g., ADC_BITWIDTH_12)</i>") end subgraph Analog-to-Digital Conversion C3 & C4 --> D["<b>SAR ADC Core</b><br><i>(Successive Approximation Register)</i>"] D --> E["<b>Raw Digital Value</b><br><i>(e.g., 0-4095 for 12-bit)</i>"] end subgraph Post-processing & Application E --> F{"<b>ADC Calibration</b><br><i>(Optional, for accuracy)</i>"} F -- "Raw Value" --> G1["<b>Application Logic</b><br><i>(e.g., display, control)</i>"] F -- "Calibrated Value" --> G2["<b>Accurate Voltage Reading</b><br><i>(V)</i>"] G1 & G2 --> H["<b>System Output</b><br><i>(e.g., Serial Monitor, LED)</i>"] end %% Styling classDef primary fill:#EDE9FE,stroke:#5B21B6,stroke-width:2px,color:#5B21B6; classDef decision fill:#FEF3C7,stroke:#D97706,stroke-width:1px,color:#92400E; classDef process fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF; classDef endo fill:#D1FAE5,stroke:#059669,stroke-width:2px,color:#065F46; class A primary; class B process; class C decision; class C1 process; class C2 process; class C3 process; class C4 process; class D process; class E endo; class F process; class G1 process; class G2 process; class H endo;
Practical Examples
Let’s create a simple project to read an analog value from a potentiometer connected to an ESP32. We will use ADC1 to avoid potential Wi-Fi conflicts.
Prerequisites:
- VS Code with the Espressif IDF Extension installed and configured.
- An ESP32 development board (e.g., ESP32-DevKitC).
- A potentiometer (e.g., 10kΩ).
- Jumper wires.
Hardware Setup:
- Connect the potentiometer to your ESP32 board:
- One outer pin of the potentiometer to GND.
- The other outer pin to 3.3V.
- The middle pin (wiper) to an ADC1 capable GPIO pin. For this example, we’ll assume
ADC1_CHANNEL_6
, which is typically GPIO34 on many ESP32-WROOM modules. Verify the correct ADC pin for your specific board and ESP32 variant.
Example 1: Single Channel Reading on ADC1
This example demonstrates how to initialize ADC1, configure a channel, and read its raw value.
1. Project Setup:
- Open VS Code.
- Create a new ESP-IDF project:
- Press
F1
, typeESP-IDF: Show Examples Projects
, and selectUse Current ESP-IDF
. - Choose
get-started
->hello_world
. - Give your project a name (e.g.,
adc_reader
). - Select a location for your project.
- Press
- Replace the content of
main/hello_world_main.c
(or your main C file) with the code below. - Update
main/CMakeLists.txt
.
2. main/adc_reader_main.c
:
#include <stdio.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_adc/adc_oneshot.h"
#include "hal/adc_types.h" // For adc_channel_t
// It's good practice to define these, especially if you might change them.
// For ESP32, ADC1_CHANNEL_6 is GPIO34.
// Please check your ESP32 variant's datasheet for ADC channel to GPIO mapping.
// For example:
// ESP32: ADC1_CHANNEL_0 (GPIO36), ADC1_CHANNEL_3 (GPIO39), ADC1_CHANNEL_6 (GPIO34)
// ESP32-S2: ADC1_CHANNEL_0 (GPIO1), ADC1_CHANNEL_6 (GPIO7)
// ESP32-S3: ADC1_CHANNEL_0 (GPIO1), ADC1_CHANNEL_6 (GPIO7)
// ESP32-C3: ADC1_CHANNEL_0 (GPIO0), ADC1_CHANNEL_4 (GPIO4)
// ESP32-C6: ADC1_CHANNEL_0 (GPIO0), ADC1_CHANNEL_4 (GPIO4)
// ESP32-H2: ADC1_CHANNEL_0 (GPIO0), ADC1_CHANNEL_4 (GPIO4)
#define ADC_UNIT ADC_UNIT_1
#define ADC_CHANNEL ADC_CHANNEL_6 // Example: GPIO34 on ESP32. CHECK YOUR DATASHEET/BOARD!
#define ADC_ATTEN ADC_ATTEN_DB_11 // For 0-3.3V range approximately
#define ADC_BITWIDTH ADC_BITWIDTH_DEFAULT // Or ADC_BITWIDTH_12 for 12-bit resolution
static const char *TAG = "ADC_EXAMPLE";
// ADC handle is not strictly needed for this simple global example,
// but good practice for more complex scenarios or if passing to functions.
static adc_oneshot_unit_handle_t adc1_handle;
static void adc_init(void)
{
//-------------ADC1 Init---------------//
adc_oneshot_unit_init_cfg_t init_config1 = {
.unit_id = ADC_UNIT,
.ulp_mode = ADC_ULP_MODE_DISABLE, // ULP mode is not used in one-shot mode
};
ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config1, &adc1_handle));
ESP_LOGI(TAG, "ADC1 initialized, handle: %p", adc1_handle);
//-------------ADC1 Channel Config---------------//
adc_oneshot_chan_cfg_t config = {
.atten = ADC_ATTEN,
.bitwidth = ADC_BITWIDTH,
};
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, ADC_CHANNEL, &config));
ESP_LOGI(TAG, "ADC1 Channel %d configured", ADC_CHANNEL);
}
void app_main(void)
{
// Initialize ADC
adc_init();
int adc_raw_reading;
while (1) {
// Read ADC value
esp_err_t ret = adc_oneshot_read(adc1_handle, ADC_CHANNEL, &adc_raw_reading);
if (ret == ESP_OK) {
ESP_LOGI(TAG, "ADC1 Channel[%d] Raw Data: %d", ADC_CHANNEL, adc_raw_reading);
// Note: To get voltage, calibration is needed.
// For a rough estimate without calibration (assuming Vref is around 1100mV and 11dB attenuation gives full scale at VDD_A):
// float voltage = (float)adc_raw_reading * 3.3f / (float)((1 << ADC_BITWIDTH_12) -1) ; // Assuming 12-bit and 3.3V VDD_A
// ESP_LOGI(TAG, "Approx. Voltage: %.2f V", voltage);
} else if (ret == ESP_ERR_TIMEOUT) {
ESP_LOGW(TAG, "ADC Read Timeout!");
} else {
ESP_LOGE(TAG, "ADC Read Failed with error: %s", esp_err_to_name(ret));
}
vTaskDelay(pdMS_TO_TICKS(1000)); // Read every 1 second
}
// Cleanup (not reached in this example due to infinite loop)
// ESP_ERROR_CHECK(adc_oneshot_del_unit(adc1_handle));
// ESP_LOGI(TAG, "ADC1 de-initialized");
}
Tip: The
ADC_CHANNEL_X
macros are defined inhal/adc_types.h
(which is usually included viaesp_adc/adc_oneshot.h
). These are generic channel numbers. The mapping to actual GPIO pins is fixed for each ESP32 variant. For instance,ADC1_CHANNEL_6
on an ESP32 is always GPIO34. You must consult your ESP32 variant’s datasheet or the ESP-IDF documentation to find the correct GPIO for a givenADC_CHANNEL_X
or vice-versa. Some development boards also label ADC pins clearly.
3. main/CMakeLists.txt
:
Make sure your CMakeLists.txt
includes the necessary components. A minimal one would look like this:
idf_component_register(SRCS "adc_reader_main.c"
INCLUDE_DIRS ".")
# Add esp_adc component for ADC functionalities
# Add esp_log for logging
# Add freertos for vTaskDelay
# These are usually automatically linked if ESP_ERROR_CHECK and ESP_LOG are used.
# However, explicitly listing them can be good practice.
# REQUIRES esp_adc esp_log freertos
Usually, ESP-IDF’s build system is smart enough to link esp_adc
if you include its headers and use its functions. If you encounter linking errors, you might need to add esp_adc
to REQUIRES
or PRIV_REQUIRES
in your component’s idf_component.yml
or the main CMakeLists.txt
. For ESP-IDF 5.x, idf_component.yml
is preferred for managing dependencies.
Create idf_component.yml
in the main
directory (if it doesn’t exist):
dependencies:
idf: ">=5.0"
# Add required IDF components here
esp_adc: "*" # For ADC functionalities
# esp_log, freertos are usually core components and don't need explicit listing
# for basic usage, but it doesn't hurt.
4. Build, Flash, and Observe:
- Set Target: In VS Code, press
F1
, typeESP-IDF: Set Espressif device target
, and choose your ESP32 variant (e.g.,esp32
,esp32s3
). - Select Port: Ensure the correct serial port for your device is selected (usually auto-detected, or use
ESP-IDF: Select port to use
). - Build: Press
F1
, typeESP-IDF: Build your project
. - Flash: Press
F1
, typeESP-IDF: Flash your project
. (You can also chooseESP-IDF: Build, Flash and Monitor your project
). - Monitor: Press
F1
, typeESP-IDF: Monitor your device
.
You should see output in the terminal similar to this:
I (XXX) ADC_EXAMPLE: ADC1 initialized, handle: 0x........
I (XXX) ADC_EXAMPLE: ADC1 Channel 6 configured
I (XXX) ADC_EXAMPLE: ADC1 Channel[6] Raw Data: 2048
I (XXX) ADC_EXAMPLE: ADC1 Channel[6] Raw Data: 1500
I (XXX) ADC_EXAMPLE: ADC1 Channel[6] Raw Data: 3000
...
As you turn the potentiometer, the “Raw Data” value should change, typically between 0 and 4095 (for 12-bit resolution with ADC_BITWIDTH_DEFAULT
on many ESP32s when using 11dB attenuation and a 0-3.3V input).
Variant Notes
The ADC capabilities and GPIO mappings differ across ESP32 variants. Here’s a summary:
Feature | ESP32 | ESP32-S2 | ESP32-S3 | ESP32-C3 | ESP32-C6 | ESP32-H2 |
---|---|---|---|---|---|---|
ADC Units | ADC1, ADC2 | ADC1, ADC2 | ADC1, ADC2 | ADC1, ADC2 | ADC1, ADC2 | ADC1 |
ADC1 Channels | 8 (CH0-CH7) | 10 (CH0-CH9) | 10 (CH0-CH9) | 5 (CH0-CH4) | 7 (CH0-CH6) | 10 (CH0-CH9) |
ADC2 Channels | 10 (CH0-CH9) | 10 (CH0-CH9) | 10 (CH0-CH9) | 1 (CH0) | 1 (CH0) | N/A |
Max Resolution | 12-bit | 13-bit | 12-bit | 12-bit | 12-bit | 12-bit |
ADC2 Wi-Fi Conflict | Yes | Yes | Yes | Less critical / Limited channels | Less critical / Limited channels | N/A |
Default Bitwidth | ADC_BITWIDTH_12 | ADC_BITWIDTH_13 | ADC_BITWIDTH_12 | ADC_BITWIDTH_12 | ADC_BITWIDTH_12 | ADC_BITWIDTH_12 |
- GPIO Mapping: The mapping of
ADC_CHANNEL_x
to physical GPIO pins is critical and variant-specific.- ESP32: ADC1_CH0 (GPIO36), ADC1_CH3 (GPIO39), ADC1_CH4 (GPIO32), ADC1_CH5 (GPIO33), ADC1_CH6 (GPIO34), ADC1_CH7 (GPIO35). Pins GPIO37, GPIO38 are not recommended for ADC.
- ESP32-S2/S3: ADC channels are mapped to GPIO1-GPIO10 for ADC1 and GPIO11-GPIO20 for ADC2. For example, on S2/S3,
ADC1_CHANNEL_0
is GPIO1,ADC1_CHANNEL_6
is GPIO7. - ESP32-C3: ADC1_CH0 (GPIO0), ADC1_CH1 (GPIO1), ADC1_CH2 (GPIO2), ADC1_CH3 (GPIO3), ADC1_CH4 (GPIO4). ADC2_CH0 is GPIO5.
- ESP32-C6: ADC1_CH0 (GPIO0) to ADC1_CH6 (GPIO6). ADC2_CH0 (GPIO0, shared).
- ESP32-H2: ADC1_CH0 (GPIO0) to ADC1_CH9 (GPIO9).
hal/adc_types.h
for your target) to confirm ADC channel to GPIO pin mappings. Development board schematics are also invaluable.
ESP32 Variant | ADC Unit / Channel | Corresponding GPIO Pin(s) |
---|---|---|
ESP32 | ADC1_CH0 – ADC1_CH7 | GPIO36, GPIO39, GPIO32, GPIO33, GPIO34, GPIO35 (CH0-CH7 respectively) |
ADC2_CH0 – ADC2_CH9 | GPIO4, GPIO0, GPIO2, GPIO15, GPIO13, GPIO12, GPIO14, GPIO27, GPIO25, GPIO26 (CH0-CH9 respectively) | |
ESP32-S2/S3 | ADC1_CH0 – ADC1_CH9 | GPIO1 – GPIO10 |
ADC2_CH0 – ADC2_CH9 | GPIO11 – GPIO20 | |
ESP32-C3 | ADC1_CH0 – ADC1_CH4 | GPIO0, GPIO1, GPIO2, GPIO3, GPIO4 |
ADC2_CH0 | GPIO5 | |
ESP32-C6 | ADC1_CH0 – ADC1_CH6 | GPIO0 – GPIO6 |
ADC2_CH0 | GPIO0 (shared with ADC1_CH0) | |
ESP32-H2 | ADC1_CH0 – ADC1_CH9 | GPIO0 – GPIO9 |
- Resolution (
adc_bitwidth_t
):ADC_BITWIDTH_DEFAULT
: Uses the maximum native resolution of the ADC (e.g., 12-bit for ESP32/S3/C3/C6/H2, 13-bit for ESP32-S2).- You can also specify lower bitwidths like
ADC_BITWIDTH_9
,ADC_BITWIDTH_10
,ADC_BITWIDTH_11
,ADC_BITWIDTH_12
. Using a lower bitwidth might slightly speed up conversion but reduces precision.
- Attenuation (
adc_atten_t
): The available input voltage ranges for each attenuation level are generally similar across variants but always check the datasheet for precise values (especially the maximum input voltage, which should not exceed VDD_A, typically 3.3V).ADC_ATTEN_DB_0
ADC_ATTEN_DB_2_5
ADC_ATTEN_DB_6
ADC_ATTEN_DB_11
(most common for 0-3.3V sensors)
Common Mistakes & Troubleshooting Tips
Mistake / Issue | Symptom(s) | Troubleshooting / Solution |
---|---|---|
Incorrect GPIO Pin for ADC Channel | Erratic or zero readings; ESP_LOGE messages about invalid channel. | Double-check: Consult your ESP32 variant’s datasheet and development board schematic to confirm the correct GPIO pin for the chosen adc_channel_t. |
Using ADC2 Channels While Wi-Fi is Active (ESP32, S2, S3) | Unreliable ADC readings; Wi-Fi instability or disconnections. | Use ADC1: Prefer ADC1 channels if Wi-Fi is enabled. If ADC2 is critical, ensure Wi-Fi is stopped or use advanced synchronization. |
Floating ADC Input Pin | Highly fluctuating, random, or unpredictable raw ADC values. | Connect input: Ensure the ADC input pin is firmly connected to a stable voltage source (sensor output, potentiometer wiper). For testing, connect to GND or 3.3V. |
Expecting Accurate Voltage Without Calibration | Raw ADC values do not precisely match expected voltage levels (e.g., 2048 for 1.65V on 3.3V range). | Calibrate: Understand raw values are proportional. For precise voltage measurements, implement ADC calibration using the esp_adc_cali component (covered in Chapter 127). |
Forgetting to Initialize or Configure ADC | Application crashes; ESP_ERROR_CHECK failures; invalid handle errors. | Follow sequence: Always initialize the ADC unit (adc_oneshot_new_unit()) first, then configure the specific channel (adc_oneshot_config_channel()) before attempting to read (adc_oneshot_read()). Check all function return values. |
Exercises
- Read from a Different ADC1 Channel:
- Modify the example code to read from a different ADC1 channel available on your ESP32 variant (e.g.,
ADC1_CHANNEL_0
). - Connect your potentiometer or another analog sensor to the corresponding GPIO pin.
- Verify that you get changing readings.
- Modify the example code to read from a different ADC1 channel available on your ESP32 variant (e.g.,
- Read from Two ADC1 Channels:
- Extend the example to configure and read from two different ADC1 channels sequentially in the main loop.
- You’ll need to call
adc_oneshot_config_channel()
for each channel. - Print the raw values for both channels. You might need two potentiometers or sensors.
- ADC Threshold LED Control:
- Connect an LED (with a current-limiting resistor) to a digital GPIO pin.
- Read an ADC value from a potentiometer.
- If the ADC raw value is above a certain threshold (e.g., 2048), turn the LED ON. Otherwise, turn it OFF.
- (Hint: You’ll need to configure the LED GPIO as an output using
gpio_set_direction()
andgpio_set_level()
).
- Simple Averaging Filter:
- Read an ADC channel multiple times (e.g., 10 times) in quick succession within the main loop.
- Calculate the average of these readings.
- Print the averaged raw value. This can help reduce noise in ADC readings.
- Map ADC Value to a Percentage:
- Read the raw ADC value (0-4095 for 12-bit).
- Convert this raw value to a percentage (0-100%).
- Print the percentage. This is useful for user interface displays.
- Formula:
percentage = (float)raw_value / MAX_RAW_VALUE * 100.0f;
(whereMAX_RAW_VALUE
is 4095 for 12-bit, 8191 for 13-bit, etc.).
Summary
- ADCs convert analog voltages to digital values, enabling microcontrollers to interact with analog sensors.
- ESP32 variants typically feature one or two ADC units (ADC1, ADC2) with multiple channels, configurable attenuation, and resolution.
- The
adc_oneshot
driver in ESP-IDF v5.x is used for single, on-demand ADC readings. - Key steps: Initialize ADC unit (
adc_oneshot_new_unit
), configure channel (adc_oneshot_config_channel
), read value (adc_oneshot_read
). - ADC2 channels on ESP32, ESP32-S2, and ESP32-S3 conflict with Wi-Fi; use ADC1 if Wi-Fi is active.
- GPIO pin to ADC channel mapping is variant-specific and must be verified from datasheets.
- Raw ADC values provide a proportional measure; calibration (Chapter 127) is needed for precise voltage conversion.
- Common issues include incorrect pin usage, ADC2/Wi-Fi conflicts, floating inputs, and neglecting initialization steps.
Further Reading
- ESP-IDF ADC Documentation:
- One-Shot ADC Driver: https://docs.espressif.com/projects/esp-idf/en/v5.4/esp32/api-reference/peripherals/adc_oneshot.html (Replace
esp32
with your target variant likeesp32s3
,esp32c3
, etc. in the URL for variant-specific docs).
- One-Shot ADC Driver: https://docs.espressif.com/projects/esp-idf/en/v5.4/esp32/api-reference/peripherals/adc_oneshot.html (Replace
- ESP-IDF ADC Calibration Documentation:
- Your Specific ESP32 Variant Datasheet: Available on the Espressif website. This is the ultimate source for pin mappings and electrical characteristics. For example, for ESP32: https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf