ESPHome 2025.8.0b2
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 || USE_ESP32_VARIANT_ESP32H2
46 init_config.clk_src = ADC_DIGI_CLK_SRC_DEFAULT;
47#endif // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 ||
48 // USE_ESP32_VARIANT_ESP32H2
49 esp_err_t err = adc_oneshot_new_unit(&init_config, &ADCSensor::shared_adc_handles[this->adc_unit_]);
50 if (err != ESP_OK) {
51 ESP_LOGE(TAG, "Error initializing %s: %d", LOG_STR_ARG(adc_unit_to_str(this->adc_unit_)), err);
52 this->mark_failed();
53 return;
54 }
55 }
56 this->adc_handle_ = ADCSensor::shared_adc_handles[this->adc_unit_];
57
59
60 adc_oneshot_chan_cfg_t config = {
61 .atten = this->attenuation_,
62 .bitwidth = ADC_BITWIDTH_DEFAULT,
63 };
64 esp_err_t err = adc_oneshot_config_channel(this->adc_handle_, this->channel_, &config);
65 if (err != ESP_OK) {
66 ESP_LOGE(TAG, "Error configuring channel: %d", err);
67 this->mark_failed();
68 return;
69 }
70 this->setup_flags_.config_complete = true;
71
72 // Initialize ADC calibration
73 if (this->calibration_handle_ == nullptr) {
74 adc_cali_handle_t handle = nullptr;
75
76#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 || \
77 USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32H2 || USE_ESP32_VARIANT_ESP32P4
78 // RISC-V variants and S3 use curve fitting calibration
79 adc_cali_curve_fitting_config_t cali_config = {}; // Zero initialize first
80#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
81 cali_config.chan = this->channel_;
82#endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
83 cali_config.unit_id = this->adc_unit_;
84 cali_config.atten = this->attenuation_;
85 cali_config.bitwidth = ADC_BITWIDTH_DEFAULT;
86
87 err = adc_cali_create_scheme_curve_fitting(&cali_config, &handle);
88 if (err == ESP_OK) {
89 this->calibration_handle_ = handle;
91 ESP_LOGV(TAG, "Using curve fitting calibration");
92 } else {
93 ESP_LOGW(TAG, "Curve fitting calibration failed with error %d, will use uncalibrated readings", err);
95 }
96#else // Other ESP32 variants use line fitting calibration
97 adc_cali_line_fitting_config_t cali_config = {
98 .unit_id = this->adc_unit_,
99 .atten = this->attenuation_,
100 .bitwidth = ADC_BITWIDTH_DEFAULT,
101#if !defined(USE_ESP32_VARIANT_ESP32S2)
102 .default_vref = 1100, // Default reference voltage in mV
103#endif // !defined(USE_ESP32_VARIANT_ESP32S2)
104 };
105 err = adc_cali_create_scheme_line_fitting(&cali_config, &handle);
106 if (err == ESP_OK) {
107 this->calibration_handle_ = handle;
109 ESP_LOGV(TAG, "Using line fitting calibration");
110 } else {
111 ESP_LOGW(TAG, "Line fitting calibration failed with error %d, will use uncalibrated readings", err);
113 }
114#endif // USE_ESP32_VARIANT_ESP32C3 || ESP32C5 || ESP32C6 || ESP32S3 || ESP32H2
115 }
116
117 this->setup_flags_.init_complete = true;
118}
119
121 LOG_SENSOR("", "ADC Sensor", this);
122 LOG_PIN(" Pin: ", this->pin_);
123 ESP_LOGCONFIG(TAG,
124 " Channel: %d\n"
125 " Unit: %s\n"
126 " Attenuation: %s\n"
127 " Samples: %i\n"
128 " Sampling mode: %s",
129 this->channel_, LOG_STR_ARG(adc_unit_to_str(this->adc_unit_)),
130 this->autorange_ ? "Auto" : LOG_STR_ARG(attenuation_to_str(this->attenuation_)), this->sample_count_,
131 LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
132
133 ESP_LOGCONFIG(
134 TAG,
135 " Setup Status:\n"
136 " Handle Init: %s\n"
137 " Config: %s\n"
138 " Calibration: %s\n"
139 " Overall Init: %s",
140 this->setup_flags_.handle_init_complete ? "OK" : "FAILED", this->setup_flags_.config_complete ? "OK" : "FAILED",
141 this->setup_flags_.calibration_complete ? "OK" : "FAILED", this->setup_flags_.init_complete ? "OK" : "FAILED");
142
143 LOG_UPDATE_INTERVAL(this);
144}
145
147 if (this->autorange_) {
148 return this->sample_autorange_();
149 } else {
150 return this->sample_fixed_attenuation_();
151 }
152}
153
155 auto aggr = Aggregator<uint32_t>(this->sampling_mode_);
156
157 for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
158 int raw;
159 esp_err_t err = adc_oneshot_read(this->adc_handle_, this->channel_, &raw);
160
161 if (err != ESP_OK) {
162 ESP_LOGW(TAG, "ADC read failed with error %d", err);
163 continue;
164 }
165
166 if (raw == -1) {
167 ESP_LOGW(TAG, "Invalid ADC reading");
168 continue;
169 }
170
171 aggr.add_sample(raw);
172 }
173
174 uint32_t final_value = aggr.aggregate();
175
176 if (this->output_raw_) {
177 return final_value;
178 }
179
180 if (this->calibration_handle_ != nullptr) {
181 int voltage_mv;
182 esp_err_t err = adc_cali_raw_to_voltage(this->calibration_handle_, final_value, &voltage_mv);
183 if (err == ESP_OK) {
184 return voltage_mv / 1000.0f;
185 } else {
186 ESP_LOGW(TAG, "ADC calibration conversion failed with error %d, disabling calibration", err);
187 if (this->calibration_handle_ != nullptr) {
188#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 || \
189 USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32H2 || USE_ESP32_VARIANT_ESP32P4
190 adc_cali_delete_scheme_curve_fitting(this->calibration_handle_);
191#else // Other ESP32 variants use line fitting calibration
192 adc_cali_delete_scheme_line_fitting(this->calibration_handle_);
193#endif // USE_ESP32_VARIANT_ESP32C3 || ESP32C5 || ESP32C6 || ESP32S3 || ESP32H2
194 this->calibration_handle_ = nullptr;
195 }
196 }
197 }
198
199 return final_value * 3.3f / 4095.0f;
200}
201
203 // Auto-range mode
204 auto read_atten = [this](adc_atten_t atten) -> std::pair<int, float> {
205 // First reconfigure the attenuation for this reading
206 adc_oneshot_chan_cfg_t config = {
207 .atten = atten,
208 .bitwidth = ADC_BITWIDTH_DEFAULT,
209 };
210
211 esp_err_t err = adc_oneshot_config_channel(this->adc_handle_, this->channel_, &config);
212
213 if (err != ESP_OK) {
214 ESP_LOGW(TAG, "Error configuring ADC channel for autorange: %d", err);
215 return {-1, 0.0f};
216 }
217
218 // Need to recalibrate for the new attenuation
219 if (this->calibration_handle_ != nullptr) {
220 // Delete old calibration handle
221#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 || \
222 USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32H2 || USE_ESP32_VARIANT_ESP32P4
223 adc_cali_delete_scheme_curve_fitting(this->calibration_handle_);
224#else
225 adc_cali_delete_scheme_line_fitting(this->calibration_handle_);
226#endif
227 this->calibration_handle_ = nullptr;
228 }
229
230 // Create new calibration handle for this attenuation
231 adc_cali_handle_t handle = nullptr;
232
233#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 || \
234 USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32H2 || USE_ESP32_VARIANT_ESP32P4
235 adc_cali_curve_fitting_config_t cali_config = {};
236#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
237 cali_config.chan = this->channel_;
238#endif
239 cali_config.unit_id = this->adc_unit_;
240 cali_config.atten = atten;
241 cali_config.bitwidth = ADC_BITWIDTH_DEFAULT;
242
243 err = adc_cali_create_scheme_curve_fitting(&cali_config, &handle);
244#else
245 adc_cali_line_fitting_config_t cali_config = {
246 .unit_id = this->adc_unit_,
247 .atten = atten,
248 .bitwidth = ADC_BITWIDTH_DEFAULT,
249#if !defined(USE_ESP32_VARIANT_ESP32S2)
250 .default_vref = 1100,
251#endif
252 };
253 err = adc_cali_create_scheme_line_fitting(&cali_config, &handle);
254#endif
255
256 int raw;
257 err = adc_oneshot_read(this->adc_handle_, this->channel_, &raw);
258
259 if (err != ESP_OK) {
260 ESP_LOGW(TAG, "ADC read failed in autorange with error %d", err);
261 if (handle != nullptr) {
262#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 || \
263 USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32H2 || USE_ESP32_VARIANT_ESP32P4
264 adc_cali_delete_scheme_curve_fitting(handle);
265#else
266 adc_cali_delete_scheme_line_fitting(handle);
267#endif
268 }
269 return {-1, 0.0f};
270 }
271
272 float voltage = 0.0f;
273 if (handle != nullptr) {
274 int voltage_mv;
275 err = adc_cali_raw_to_voltage(handle, raw, &voltage_mv);
276 if (err == ESP_OK) {
277 voltage = voltage_mv / 1000.0f;
278 } else {
279 voltage = raw * 3.3f / 4095.0f;
280 }
281 // Clean up calibration handle
282#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 || \
283 USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32H2 || USE_ESP32_VARIANT_ESP32P4
284 adc_cali_delete_scheme_curve_fitting(handle);
285#else
286 adc_cali_delete_scheme_line_fitting(handle);
287#endif
288 } else {
289 voltage = raw * 3.3f / 4095.0f;
290 }
291
292 return {raw, voltage};
293 };
294
295 auto [raw12, mv12] = read_atten(ADC_ATTEN_DB_12);
296 if (raw12 == -1) {
297 ESP_LOGE(TAG, "Failed to read ADC in autorange mode");
298 return NAN;
299 }
300
301 int raw6 = 4095, raw2 = 4095, raw0 = 4095;
302 float mv6 = 0, mv2 = 0, mv0 = 0;
303
304 if (raw12 < 4095) {
305 auto [raw6_val, mv6_val] = read_atten(ADC_ATTEN_DB_6);
306 raw6 = raw6_val;
307 mv6 = mv6_val;
308
309 if (raw6 < 4095 && raw6 != -1) {
310 auto [raw2_val, mv2_val] = read_atten(ADC_ATTEN_DB_2_5);
311 raw2 = raw2_val;
312 mv2 = mv2_val;
313
314 if (raw2 < 4095 && raw2 != -1) {
315 auto [raw0_val, mv0_val] = read_atten(ADC_ATTEN_DB_0);
316 raw0 = raw0_val;
317 mv0 = mv0_val;
318 }
319 }
320 }
321
322 if (raw0 == -1 || raw2 == -1 || raw6 == -1 || raw12 == -1) {
323 return NAN;
324 }
325
326 const int adc_half = 2048;
327 uint32_t c12 = std::min(raw12, adc_half);
328 uint32_t c6 = adc_half - std::abs(raw6 - adc_half);
329 uint32_t c2 = adc_half - std::abs(raw2 - adc_half);
330 uint32_t c0 = std::min(4095 - raw0, adc_half);
331 uint32_t csum = c12 + c6 + c2 + c0;
332
333 if (csum == 0) {
334 ESP_LOGE(TAG, "Invalid weight sum in autorange calculation");
335 return NAN;
336 }
337
338 return (mv12 * c12 + mv6 * c6 + mv2 * c2 + mv0 * c0) / csum;
339}
340
341} // namespace adc
342} // namespace esphome
343
344#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