Аналоговые входы ESP32: особенности, подводные камни и калибровка
ADC (Analog-to-Digital Converter) в ESP32 позволяет считывать аналоговые сигналы (напряжение) и переводить их в цифровые значения. Это основа для работы с потенциометрами, фоторезисторами, датчиками напряжения аккумулятора и др.
Особенности АЦП ESP32
- Количество каналов: до 18 входов (ADC1 и ADC2). Часть входов (ADC2) недоступна при активном Wi-Fi/BT, т.к. блок разделяется с радиомодулем.
- Разрядность: 12 бит (диапазон отсчётов
0…4095). - Диапазон напряжений: обычно
0…3,3 В(зависит от выбранной аттенюации). Типовая аттенюацияADC_11dbпозволяет измерять до ≈3,6 В (рекомендуется придерживаться 3,3 В).
Таблица пинов ADC (ESP32)
Примечание: пины ADC2 недоступны при включённом Wi-Fi/BT. Пины GPIO34…GPIO39 — только вход, без внутренних подтяжек.
| Блок | Канал | GPIO | Доступен при Wi-Fi/BT | Особенности |
|---|---|---|---|---|
| ADC1 | CH0 | GPIO36 (VP) | Да | Только вход |
| ADC1 | CH1 | GPIO37 | Да | Только вход |
| ADC1 | CH2 | GPIO38 | Да | Только вход |
| ADC1 | CH3 | GPIO39 (VN) | Да | Только вход |
| ADC1 | CH4 | GPIO32 | Да | |
| ADC1 | CH5 | GPIO33 | Да | |
| ADC1 | CH6 | GPIO34 | Да | Только вход |
| ADC1 | CH7 | GPIO35 | Да | Только вход |
| ADC2 | CH0 | GPIO4 | Нет | Страп/переферия на некоторых платах |
| ADC2 | CH1 | GPIO0 | Нет | Стартовый пин (boot) |
| ADC2 | CH2 | GPIO2 | Нет | Стартовый пин (boot) |
| ADC2 | CH3 | GPIO15 | Нет | Стартовый пин (boot) |
| ADC2 | CH4 | GPIO13 | Нет | |
| ADC2 | CH5 | GPIO12 | Нет | Страп VDD_SDIO (осторожно!) |
| ADC2 | CH6 | GPIO14 | Нет | |
| ADC2 | CH7 | GPIO27 | Нет | |
| ADC2 | CH8 | GPIO25 | Нет | |
| ADC2 | CH9 | GPIO26 | Нет |
Итог: для стабильных измерений при активном Wi-Fi/BT используйте каналы ADC1 (GPIO32…GPIO39).
Пример программы (базовое чтение)
// Пример: чтение с ADC1 GPIO34 (только вход)
#include <Arduino.h>
const int adcPin = 34; // ADC1_CH6
void setup() {
Serial.begin(115200);
// Настройка ширины и аттенюации (опционально для Arduino-ESP32 core)
analogReadResolution(12); // 0..4095
analogSetPinAttenuation(adcPin, ADC_11db); // до ≈3.6 В (реалистично 3.3 В)
}
void loop() {
int raw = analogRead(adcPin);
// Грубая оценка напряжения (без калибровки):
float v = (float)raw / 4095.0f * 3.30f; // если питание 3.3 В
Serial.printf("RAW=%d V=%.3f V\n", raw, v);
delay(250);
}
Настройка ADC: analogReadResolution и analogSetPinAttenuation
Для более гибкой работы с АЦП в ESP32 можно использовать дополнительные функции настройки:
analogReadResolution()
Эта функция позволяет задать разрядность результата преобразования.
По умолчанию ESP32 работает с 12 бит (значения от 0 до 4095).
В некоторых случаях можно уменьшить точность для ускорения обработки или упрощения вычислений.
analogReadResolution(9)→ диапазон 0–511.analogReadResolution(10)→ диапазон 0–1023.analogReadResolution(11)→ диапазон 0–2047.analogReadResolution(12)→ диапазон 0–4095 (рекомендуется).
analogSetPinAttenuation()
Эта функция позволяет изменять аттенюацию (ослабление сигнала), чтобы адаптировать входной диапазон напряжений для конкретного пина. По умолчанию ESP32 измеряет напряжение примерно в пределах 0–1.1 В, но диапазон можно расширить.
ADC_0db→ диапазон 0–1.1 В (по умолчанию, наибольшая точность).ADC_2_5db→ диапазон 0–1.5 В.ADC_6db→ диапазон 0–2.2 В.ADC_11db→ диапазон 0–3.3 В (для работы с полным диапазоном питания).
Пример кода
#include <Arduino.h>
void setup() {
Serial.begin(115200);
// Устанавливаем разрядность 12 бит (0–4095)
analogReadResolution(12);
// Расширяем диапазон измерения пина 34 до 3.3 В
analogSetPinAttenuation(34, ADC_11db);
}
void loop() {
int val = analogRead(34);
Serial.println(val);
delay(500);
}
Таким образом можно настроить точность и диапазон измерения для каждого аналогового входа ESP32 отдельно.
Пример программы с программным фильтром сигнала
Для подавления шума применим скользящее среднее и медианный фильтр.
#include <Arduino.h>
const int adcPin = 34; // ADC1_CH6
const int N = 15; // размер окна усреднения
int buf[N];
int idx = 0;
bool filled = false;
int median3(int a, int b, int c) { // медиана из 3
if ((a <= b && b <= c) || (c <= b && b <= a)) return b;
if ((b <= a && a <= c) || (c <= a && a <= b)) return a;
return c;
}
void setup() {
Serial.begin(115200);
analogReadResolution(12);
analogSetPinAttenuation(adcPin, ADC_11db);
}
void loop() {
// Медиана по 3 мгновенным отсчётам
int r1 = analogRead(adcPin);
int r2 = analogRead(adcPin);
int r3 = analogRead(adcPin);
int med = median3(r1, r2, r3);
// Скользящее среднее
buf[idx] = med;
idx = (idx + 1) % N;
if (idx == 0) filled = true;
long sum = 0;
int count = filled ? N : idx;
for (int i = 0; i < count; i++) sum += buf[i];
int avg = (int)(sum / max(1, count));
float v = (float)avg / 4095.0f * 3.30f;
Serial.printf("RAW_med=%d AVG=%d V=%.3f V\n", med, avg, v);
delay(50);
}
Подводные камни ESP32 ADC
- Нелинейность: реальное напряжение часто не совпадает с идеальной прямой преобразования, особенно на краях диапазона или при разных аттенюациях.
- Влияние Wi-Fi: активный Wi-Fi/BT делает ADC2 недоступным и добавляет помехи. Для стабильных измерений используйте ADC1.
- Разные характеристики ADC1/ADC2: шум, чувствительность и доступность отличаются. Рекомендация: для ответственных измерений — только ADC1.
Калибровка и улучшение точности
Калибровка через esp_adc_cal
Для учёта нелинейности и реального опорного напряжения используйте API esp_adc_cal (работает и из Arduino-ядра).
// Калиброванное измерение в мВ с esp_adc_cal
#include <Arduino.h>
#include "esp_adc_cal.h"
#include "driver/adc.h"
#define ADC_CHANNEL ADC1_CHANNEL_6 // GPIO34
#define ADC_UNIT ADC_UNIT_1
#define ADC_ATTEN ADC_ATTEN_DB_11 // до ~3.6 В
#define ADC_WIDTH ADC_WIDTH_BIT_12
esp_adc_cal_characteristics_t adc_chars;
void setup() {
Serial.begin(115200);
// Настройка ширины/аттенюации низкоуровневым API
adc1_config_width(ADC_WIDTH);
adc1_config_channel_atten(ADC_CHANNEL, ADC_ATTEN);
// Характеризация (калибровка): eFuse -> Two Point/ Vref / Default
esp_adc_cal_value_t cal = esp_adc_cal_characterize(
ADC_UNIT, ADC_ATTEN, ADC_WIDTH, 1100, &adc_chars); // 1100 мВ дефолтный Vref
Serial.printf("Calibration source: %d (0:TwoPoint 1:eFuseVref 2:Default)\n", cal);
}
void loop() {
uint32_t raw = adc1_get_raw(ADC_CHANNEL);
uint32_t mv = esp_adc_cal_raw_to_voltage(raw, &adc_chars); // в мВ
Serial.printf("RAW=%lu V=%lu mV\n", raw, mv);
delay(200);
}
Как отключать/включать ADC для экономичности
Если АЦП используется редко, его можно выключать для экономии энергии. Это особенно заметно в проектах на батарее.
#include "driver/adc.h"
void adcPowerSaveExample() {
// Выключить блок АЦП
adc_power_off(); // отключает питание ADC (оба блока)
// ... выполнить другие действия/сон ...
adc_power_on(); // включить обратно перед чтением
}
Советы: отключайте Wi-Fi/BT на время критических измерений; делайте серию быстрых чтений (burst) и усредняйте; подбирайте аттенюацию ADC_0db/2_5db/6db/11db под ваш диапазон.
Выводы
- Для бытовых задач (потенциометр, LDR, мониторинг батареи с делителем) — ADC1 с фильтрацией и калибровкой даёт достаточную точность.
- При активном Wi-Fi/BT используйте только ADC1 и уменьшайте помехи (экранирование, фильтры, усреднение).
- Для задач, где важны высокая точность, повторяемость и линейность (прецизионные датчики), целесообразно применить внешний АЦП с опорным источником (MCP3201/ADS1115/ADS1015 и т.п.).





