ESPHome 2026.1.0-dev
Loading...
Searching...
No Matches
adc_sensor_esp32.cpp
Go to the documentation of this file.
1#ifdef USE_ESP32
2
3#include "adc_sensor.h"
4#include "esphome/core/log.h"
5
6namespace esphome {
7namespace adc {
8
9static const char *const TAG = "adc.esp32";
10
11adc_oneshot_unit_handle_t ADCSensor::shared_adc_handles[2] = {nullptr, nullptr};
12
13const LogString *attenuation_to_str(adc_atten_t attenuation) {
14 switch (attenuation) {
15 case ADC_ATTEN_DB_0:
16 return LOG_STR("0 dB");
17 case ADC_ATTEN_DB_2_5:
18 return LOG_STR("2.5 dB");
19 case ADC_ATTEN_DB_6:
20 return LOG_STR("6 dB");
21 case ADC_ATTEN_DB_12_COMPAT:
22 return LOG_STR("12 dB");
23 default:
24 return LOG_STR("Unknown Attenuation");
25 }
26}
27
28const LogString *adc_unit_to_str(adc_unit_t unit) {
29 switch (unit) {
30 case ADC_UNIT_1:
31 return LOG_STR("ADC1");
32 case ADC_UNIT_2:
33 return LOG_STR("ADC2");
34 default:
35 return LOG_STR("Unknown ADC Unit");
36 }
37}
38
40 // Check if another sensor already initialized this ADC unit
41 if (ADCSensor::shared_adc_handles[this->adc_unit_] == nullptr) {
42 adc_oneshot_unit_init_cfg_t init_config = {}; // Zero initialize
43 init_config.unit_id = this->adc_unit_;
44 init_config.ulp_mode = ADC_ULP_MODE_DISABLE;
45#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 || \
46 USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2
47 init_config.clk_src = ADC_DIGI_CLK_SRC_DEFAULT;
48#endif // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 ||
49 // USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2
50 esp_err_t err = adc_oneshot_new_unit(&init_config, &ADCSensor::shared_adc_handles[this->adc_unit_]);
51 if (err != ESP_OK) {
52 ESP_LOGE(TAG, "Error initializing %s: %d", LOG_STR_ARG(adc_unit_to_str(this->adc_unit_)), err);
53 this->mark_failed();
54 return;
55 }
56 }
57 this->adc_handle_ = ADCSensor::shared_adc_handles[this->adc_unit_];
58
60
61 adc_oneshot_chan_cfg_t config = {
62 .atten = this->attenuation_,
63 .bitwidth = ADC_BITWIDTH_DEFAULT,
64 };
65 esp_err_t err = adc_oneshot_config_channel(this->adc_handle_, this->channel_, &config);
66 if (err != ESP_OK) {
67 ESP_LOGE(TAG, "Error configuring channel: %d", err);
68 this->mark_failed();
69 return;
70 }
71 this->setup_flags_.config_complete = true;
72
73 // Initialize ADC calibration
74 if (this->calibration_handle_ == nullptr) {
75 adc_cali_handle_t handle = nullptr;
76
77#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 || \
78 USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2 || USE_ESP32_VARIANT_ESP32P4 || USE_ESP32_VARIANT_ESP32S3
79 // RISC-V variants and S3 use curve fitting calibration
80 adc_cali_curve_fitting_config_t cali_config = {}; // Zero initialize first
81#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
82 cali_config.chan = this->channel_;
83#endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
84 cali_config.unit_id = this->adc_unit_;
85 cali_config.atten = this->attenuation_;
86 cali_config.bitwidth = ADC_BITWIDTH_DEFAULT;
87
88 err = adc_cali_create_scheme_curve_fitting(&cali_config, &handle);
89 if (err == ESP_OK) {
90 this->calibration_handle_ = handle;
92 ESP_LOGV(TAG, "Using curve fitting calibration");
93 } else {
94 ESP_LOGW(TAG, "Curve fitting calibration failed with error %d, will use uncalibrated readings", err);
96 }
97#else // Other ESP32 variants use line fitting calibration
98 adc_cali_line_fitting_config_t cali_config = {
99 .unit_id = this->adc_unit_,
100 .atten = this->attenuation_,
101 .bitwidth = ADC_BITWIDTH_DEFAULT,
102#if !defined(USE_ESP32_VARIANT_ESP32S2)
103 .default_vref = 1100, // Default reference voltage in mV
104#endif // !defined(USE_ESP32_VARIANT_ESP32S2)
105 };
106 err = adc_cali_create_scheme_line_fitting(&cali_config, &handle);
107 if (err == ESP_OK) {
108 this->calibration_handle_ = handle;
110 ESP_LOGV(TAG, "Using line fitting calibration");
111 } else {
112 ESP_LOGW(TAG, "Line fitting calibration failed with error %d, will use uncalibrated readings", err);
114 }
115#endif // USE_ESP32_VARIANT_ESP32C3 || ESP32C5 || ESP32C6 || ESP32C61 || ESP32H2 || ESP32P4 || ESP32S3
116 }
117
118 this->setup_flags_.init_complete = true;
119}
120
122 LOG_SENSOR("", "ADC Sensor", this);
123 LOG_PIN(" Pin: ", this->pin_);
124 ESP_LOGCONFIG(TAG,
125 " Channel: %d\n"
126 " Unit: %s\n"
127 " Attenuation: %s\n"
128 " Samples: %i\n"
129 " Sampling mode: %s",
130 this->channel_, LOG_STR_ARG(adc_unit_to_str(this->adc_unit_)),
131 this->autorange_ ? "Auto" : LOG_STR_ARG(attenuation_to_str(this->attenuation_)), this->sample_count_,
132 LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
133
134 ESP_LOGCONFIG(
135 TAG,
136 " Setup Status:\n"
137 " Handle Init: %s\n"
138 " Config: %s\n"
139 " Calibration: %s\n"
140 " Overall Init: %s",
141 this->setup_flags_.handle_init_complete ? "OK" : "FAILED", this->setup_flags_.config_complete ? "OK" : "FAILED",
142 this->setup_flags_.calibration_complete ? "OK" : "FAILED", this->setup_flags_.init_complete ? "OK" : "FAILED");
143
144 LOG_UPDATE_INTERVAL(this);
145}
146
148 if (this->autorange_) {
149 return this->sample_autorange_();
150 } else {
151 return this->sample_fixed_attenuation_();
152 }
153}
154
156 auto aggr = Aggregator<uint32_t>(this->sampling_mode_);
157
158 for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
159 int raw;
160 esp_err_t err = adc_oneshot_read(this->adc_handle_, this->channel_, &raw);
161
162 if (err != ESP_OK) {
163 ESP_LOGW(TAG, "ADC read failed with error %d", err);
164 continue;
165 }
166
167 if (raw == -1) {
168 ESP_LOGW(TAG, "Invalid ADC reading");
169 continue;
170 }
171
172 aggr.add_sample(raw);
173 }
174
175 uint32_t final_value = aggr.aggregate();
176
177 if (this->output_raw_) {
178 return final_value;
179 }
180
181 if (this->calibration_handle_ != nullptr) {
182 int voltage_mv;
183 esp_err_t err = adc_cali_raw_to_voltage(this->calibration_handle_, final_value, &voltage_mv);
184 if (err == ESP_OK) {
185 return voltage_mv / 1000.0f;
186 } else {
187 ESP_LOGW(TAG, "ADC calibration conversion failed with error %d, disabling calibration", err);
188 if (this->calibration_handle_ != nullptr) {
189#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 || \
190 USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2 || USE_ESP32_VARIANT_ESP32P4 || USE_ESP32_VARIANT_ESP32S3
191 adc_cali_delete_scheme_curve_fitting(this->calibration_handle_);
192#else // Other ESP32 variants use line fitting calibration
193 adc_cali_delete_scheme_line_fitting(this->calibration_handle_);
194#endif // USE_ESP32_VARIANT_ESP32C3 || ESP32C5 || ESP32C6 || ESP32C61 || ESP32H2 || ESP32P4 || ESP32S3
195 this->calibration_handle_ = nullptr;
196 }
197 }
198 }
199
200 return final_value * 3.3f / 4095.0f;
201}
202
204 // Auto-range mode
205 auto read_atten = [this](adc_atten_t atten) -> std::pair<int, float> {
206 // First reconfigure the attenuation for this reading
207 adc_oneshot_chan_cfg_t config = {
208 .atten = atten,
209 .bitwidth = ADC_BITWIDTH_DEFAULT,
210 };
211
212 esp_err_t err = adc_oneshot_config_channel(this->adc_handle_, this->channel_, &config);
213
214 if (err != ESP_OK) {
215 ESP_LOGW(TAG, "Error configuring ADC channel for autorange: %d", err);
216 return {-1, 0.0f};
217 }
218
219 // Need to recalibrate for the new attenuation
220 if (this->calibration_handle_ != nullptr) {
221 // Delete old calibration handle
222#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 || \
223 USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2 || USE_ESP32_VARIANT_ESP32P4 || USE_ESP32_VARIANT_ESP32S3
224 adc_cali_delete_scheme_curve_fitting(this->calibration_handle_);
225#else
226 adc_cali_delete_scheme_line_fitting(this->calibration_handle_);
227#endif
228 this->calibration_handle_ = nullptr;
229 }
230
231 // Create new calibration handle for this attenuation
232 adc_cali_handle_t handle = nullptr;
233
234#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 || \
235 USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2 || USE_ESP32_VARIANT_ESP32P4 || USE_ESP32_VARIANT_ESP32S3
236 adc_cali_curve_fitting_config_t cali_config = {};
237#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
238 cali_config.chan = this->channel_;
239#endif
240 cali_config.unit_id = this->adc_unit_;
241 cali_config.atten = atten;
242 cali_config.bitwidth = ADC_BITWIDTH_DEFAULT;
243
244 err = adc_cali_create_scheme_curve_fitting(&cali_config, &handle);
245 ESP_LOGVV(TAG, "Autorange atten=%d: Calibration handle creation %s (err=%d)", atten,
246 (err == ESP_OK) ? "SUCCESS" : "FAILED", err);
247#else
248 adc_cali_line_fitting_config_t cali_config = {
249 .unit_id = this->adc_unit_,
250 .atten = atten,
251 .bitwidth = ADC_BITWIDTH_DEFAULT,
252#if !defined(USE_ESP32_VARIANT_ESP32S2)
253 .default_vref = 1100,
254#endif
255 };
256 err = adc_cali_create_scheme_line_fitting(&cali_config, &handle);
257 ESP_LOGVV(TAG, "Autorange atten=%d: Calibration handle creation %s (err=%d)", atten,
258 (err == ESP_OK) ? "SUCCESS" : "FAILED", err);
259#endif
260
261 int raw;
262 err = adc_oneshot_read(this->adc_handle_, this->channel_, &raw);
263 ESP_LOGVV(TAG, "Autorange atten=%d: Raw ADC read %s, value=%d (err=%d)", atten,
264 (err == ESP_OK) ? "SUCCESS" : "FAILED", raw, err);
265
266 if (err != ESP_OK) {
267 ESP_LOGW(TAG, "ADC read failed in autorange with error %d", err);
268 if (handle != nullptr) {
269#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 || \
270 USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2 || USE_ESP32_VARIANT_ESP32P4 || USE_ESP32_VARIANT_ESP32S3
271 adc_cali_delete_scheme_curve_fitting(handle);
272#else
273 adc_cali_delete_scheme_line_fitting(handle);
274#endif
275 }
276 return {-1, 0.0f};
277 }
278
279 float voltage = 0.0f;
280 if (handle != nullptr) {
281 int voltage_mv;
282 err = adc_cali_raw_to_voltage(handle, raw, &voltage_mv);
283 if (err == ESP_OK) {
284 voltage = voltage_mv / 1000.0f;
285 ESP_LOGVV(TAG, "Autorange atten=%d: CALIBRATED - raw=%d -> %dmV -> %.6fV", atten, raw, voltage_mv, voltage);
286 } else {
287 voltage = raw * 3.3f / 4095.0f;
288 ESP_LOGVV(TAG, "Autorange atten=%d: UNCALIBRATED FALLBACK - raw=%d -> %.6fV (3.3V ref)", atten, raw, voltage);
289 }
290 // Clean up calibration handle
291#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 || \
292 USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2 || USE_ESP32_VARIANT_ESP32P4 || USE_ESP32_VARIANT_ESP32S3
293 adc_cali_delete_scheme_curve_fitting(handle);
294#else
295 adc_cali_delete_scheme_line_fitting(handle);
296#endif
297 } else {
298 voltage = raw * 3.3f / 4095.0f;
299 ESP_LOGVV(TAG, "Autorange atten=%d: NO CALIBRATION - raw=%d -> %.6fV (3.3V ref)", atten, raw, voltage);
300 }
301
302 return {raw, voltage};
303 };
304
305 auto [raw12, mv12] = read_atten(ADC_ATTEN_DB_12);
306 if (raw12 == -1) {
307 ESP_LOGE(TAG, "Failed to read ADC in autorange mode");
308 return NAN;
309 }
310
311 int raw6 = 4095, raw2 = 4095, raw0 = 4095;
312 float mv6 = 0, mv2 = 0, mv0 = 0;
313
314 if (raw12 < 4095) {
315 auto [raw6_val, mv6_val] = read_atten(ADC_ATTEN_DB_6);
316 raw6 = raw6_val;
317 mv6 = mv6_val;
318
319 if (raw6 < 4095 && raw6 != -1) {
320 auto [raw2_val, mv2_val] = read_atten(ADC_ATTEN_DB_2_5);
321 raw2 = raw2_val;
322 mv2 = mv2_val;
323
324 if (raw2 < 4095 && raw2 != -1) {
325 auto [raw0_val, mv0_val] = read_atten(ADC_ATTEN_DB_0);
326 raw0 = raw0_val;
327 mv0 = mv0_val;
328 }
329 }
330 }
331
332 if (raw0 == -1 || raw2 == -1 || raw6 == -1 || raw12 == -1) {
333 return NAN;
334 }
335
336 const int adc_half = 2048;
337 const uint32_t c12 = std::min(raw12, adc_half);
338
339 const int32_t c6_signed = adc_half - std::abs(raw6 - adc_half);
340 const uint32_t c6 = (c6_signed > 0) ? c6_signed : 0; // Clamp to prevent underflow
341
342 const int32_t c2_signed = adc_half - std::abs(raw2 - adc_half);
343 const uint32_t c2 = (c2_signed > 0) ? c2_signed : 0; // Clamp to prevent underflow
344
345 const uint32_t c0 = std::min(4095 - raw0, adc_half);
346 const uint32_t csum = c12 + c6 + c2 + c0;
347
348 ESP_LOGVV(TAG, "Autorange summary:");
349 ESP_LOGVV(TAG, " Raw readings: 12db=%d, 6db=%d, 2.5db=%d, 0db=%d", raw12, raw6, raw2, raw0);
350 ESP_LOGVV(TAG, " Voltages: 12db=%.6f, 6db=%.6f, 2.5db=%.6f, 0db=%.6f", mv12, mv6, mv2, mv0);
351 ESP_LOGVV(TAG, " Coefficients: c12=%u, c6=%u, c2=%u, c0=%u, sum=%u", c12, c6, c2, c0, csum);
352
353 if (csum == 0) {
354 ESP_LOGE(TAG, "Invalid weight sum in autorange calculation");
355 return NAN;
356 }
357
358 const float final_result = (mv12 * c12 + mv6 * c6 + mv2 * c2 + mv0 * c0) / csum;
359 ESP_LOGV(TAG, "Autorange final: (%.6f*%u + %.6f*%u + %.6f*%u + %.6f*%u)/%u = %.6fV", mv12, c12, mv6, c6, mv2, c2, mv0,
360 c0, csum, final_result);
361
362 return final_result;
363}
364
365} // namespace adc
366} // namespace esphome
367
368#endif // USE_ESP32
uint8_t raw[35]
Definition bl0939.h:0
virtual void mark_failed()
Mark this component as failed.
struct esphome::adc::ADCSensor::SetupFlags setup_flags_
float sample() override
Perform a single ADC sampling operation and return the measured value.
adc_oneshot_unit_handle_t adc_handle_
Definition adc_sensor.h:145
void setup() override
Set up the ADC sensor by initializing hardware and calibration parameters.
InternalGPIOPin * pin_
Definition adc_sensor.h:138
void dump_config() override
Output the configuration details of the ADC sensor for debugging purposes.
SamplingMode sampling_mode_
Definition adc_sensor.h:139
static adc_oneshot_unit_handle_t shared_adc_handles[2]
Definition adc_sensor.h:157
adc_channel_t channel_
Definition adc_sensor.h:148
adc_cali_handle_t calibration_handle_
Definition adc_sensor.h:146
const LogString * attenuation_to_str(adc_atten_t attenuation)
const LogString * sampling_mode_to_str(SamplingMode mode)
const LogString * adc_unit_to_str(adc_unit_t unit)
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7