From f4e4586404dc2b1e3f76a261b01076fa851c9e3e Mon Sep 17 00:00:00 2001 From: Edward Firmo <94725493+edwardtfn@users.noreply.github.com> Date: Thu, 12 Dec 2024 10:37:57 +0100 Subject: [PATCH] Improved error handling --- .../components/adc/adc_sensor_esp32_v5.cpp | 259 ++++++++++++------ 1 file changed, 174 insertions(+), 85 deletions(-) diff --git a/esphome/components/adc/adc_sensor_esp32_v5.cpp b/esphome/components/adc/adc_sensor_esp32_v5.cpp index 6da0ea307c..b2c77aeb3a 100644 --- a/esphome/components/adc/adc_sensor_esp32_v5.cpp +++ b/esphome/components/adc/adc_sensor_esp32_v5.cpp @@ -25,14 +25,24 @@ void ADCSensor::setup() { #if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32H2 init_config1.clk_src = ADC_DIGI_CLK_SRC_DEFAULT; #endif // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32H2 - ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config1, &this->adc1_handle_)); + esp_err_t err = adc_oneshot_new_unit(&init_config1, &this->adc1_handle_); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Error initializing ADC1 unit: %d", err); + this->mark_failed(); + return; + } } adc_oneshot_chan_cfg_t config = { - .atten = this->attenuation_, - .bitwidth = ADC_BITWIDTH_DEFAULT, + .atten = this->attenuation_, + .bitwidth = ADC_BITWIDTH_DEFAULT, }; - ESP_ERROR_CHECK(adc_oneshot_config_channel(this->adc1_handle_, this->channel_, &config)); + esp_err_t err = adc_oneshot_config_channel(this->adc1_handle_, this->channel_, &config); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Error configuring ADC1 channel: %d", err); + this->mark_failed(); + return; + } } else { if (this->adc2_handle_ == nullptr) { @@ -42,48 +52,63 @@ void ADCSensor::setup() { #if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32H2 init_config2.clk_src = ADC_DIGI_CLK_SRC_DEFAULT; #endif // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32H2 - ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config2, &this->adc2_handle_)); + esp_err_t err = adc_oneshot_new_unit(&init_config2, &this->adc2_handle_); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Error initializing ADC2 unit: %d", err); + this->mark_failed(); + return; + } } adc_oneshot_chan_cfg_t config = { - .atten = this->attenuation_, - .bitwidth = ADC_BITWIDTH_DEFAULT, + .atten = this->attenuation_, + .bitwidth = ADC_BITWIDTH_DEFAULT, }; - ESP_ERROR_CHECK(adc_oneshot_config_channel(this->adc2_handle_, this->channel_, &config)); + esp_err_t err = adc_oneshot_config_channel(this->adc2_handle_, this->channel_, &config); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Error configuring ADC2 channel: %d", err); + this->mark_failed(); + return; + } } - // Initialize ADC calibration if (this->calibration_handle_ == nullptr) { adc_cali_handle_t handle = nullptr; adc_unit_t unit_id = this->is_adc1_ ? ADC_UNIT_1 : ADC_UNIT_2; + esp_err_t err; #if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32H2 // RISC-V variants and S3 use curve fitting calibration adc_cali_curve_fitting_config_t cali_config = {}; // Zero initialize first #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) - cali_config.chan = this->channel_; // Set chan first as it's the first field in v5.3+ -#endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) + cali_config.chan = this->channel_; +#endif // ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) cali_config.unit_id = unit_id; cali_config.atten = this->attenuation_; cali_config.bitwidth = ADC_BITWIDTH_DEFAULT; - - if (adc_cali_create_scheme_curve_fitting(&cali_config, &handle) == ESP_OK) { + + err = adc_cali_create_scheme_curve_fitting(&cali_config, &handle); + if (err == ESP_OK) { this->calibration_handle_ = handle; ESP_LOGV(TAG, "Using curve fitting calibration"); + } else { + ESP_LOGW(TAG, "Curve fitting calibration failed with error %d, will use uncalibrated readings", err); } -#else // USE_ESP32_VARIANT_ESP32C3 || ESP32C6 || ESP32S3 || ESP32H2 - // Other ESP32 variants use line fitting calibration +#else // Other ESP32 variants use line fitting calibration adc_cali_line_fitting_config_t cali_config = { .unit_id = unit_id, .atten = this->attenuation_, .bitwidth = ADC_BITWIDTH_DEFAULT, #if !defined(USE_ESP32_VARIANT_ESP32S2) - .default_vref = 1100, // Initialize default_vref to 1100mV -#endif // not USE_ESP32_VARIANT_ESP32S2 + .default_vref = 1100, // Default reference voltage in mV +#endif // !defined(USE_ESP32_VARIANT_ESP32S2) }; - if (adc_cali_create_scheme_line_fitting(&cali_config, &handle) == ESP_OK) { + err = adc_cali_create_scheme_line_fitting(&cali_config, &handle); + if (err == ESP_OK) { this->calibration_handle_ = handle; ESP_LOGV(TAG, "Using line fitting calibration"); + } else { + ESP_LOGW(TAG, "Line fitting calibration failed with error %d, will use uncalibrated readings", err); } #endif // USE_ESP32_VARIANT_ESP32C3 || ESP32C6 || ESP32S3 || ESP32H2 } @@ -121,19 +146,38 @@ void ADCSensor::dump_config() { float ADCSensor::sample() { if (!this->autorange_) { uint32_t sum = 0; + uint8_t success_samples = 0; + for (uint8_t sample = 0; sample < this->sample_count_; sample++) { - int raw = -1; + int raw; + esp_err_t err; + if (this->is_adc1_) { - ESP_ERROR_CHECK(adc_oneshot_read(this->adc1_handle_, this->channel_, &raw)); + err = adc_oneshot_read(this->adc1_handle_, this->channel_, &raw); } else { - ESP_ERROR_CHECK(adc_oneshot_read(this->adc2_handle_, this->channel_, &raw)); + err = adc_oneshot_read(this->adc2_handle_, this->channel_, &raw); } + + if (err != ESP_OK) { + ESP_LOGW(TAG, "ADC read failed with error %d", err); + continue; + } + if (raw == -1) { - return NAN; + ESP_LOGW(TAG, "Invalid ADC reading"); + continue; } + sum += raw; + success_samples++; } - sum = (sum + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero) + + if (success_samples == 0) { + ESP_LOGE(TAG, "All ADC readings failed"); + return NAN; + } + + sum = (sum + (success_samples >> 1)) / success_samples; if (this->output_raw_) { return sum; @@ -141,83 +185,128 @@ float ADCSensor::sample() { if (this->calibration_handle_ != nullptr) { int voltage_mv; - ESP_ERROR_CHECK(adc_cali_raw_to_voltage(this->calibration_handle_, sum, &voltage_mv)); - return voltage_mv / 1000.0f; + esp_err_t err = adc_cali_raw_to_voltage(this->calibration_handle_, sum, &voltage_mv); + if (err == ESP_OK) { + return voltage_mv / 1000.0f; + } else { + ESP_LOGW(TAG, "ADC calibration conversion failed with error %d, disabling calibration", err); + if (this->calibration_handle_ != nullptr) { +#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32S3 || USE_ESP32_VARIANT_ESP32H2 + adc_cali_delete_scheme_curve_fitting(this->calibration_handle_); +#else // Other ESP32 variants use line fitting calibration + adc_cali_delete_scheme_line_fitting(this->calibration_handle_); +#endif // USE_ESP32_VARIANT_ESP32C3 || ESP32C6 || ESP32S3 || ESP32H2 + this->calibration_handle_ = nullptr; + } + } } - return sum * 3.3f / 4095.0f; // Fallback if no calibration - } + + return sum * 3.3f / 4095.0f; - // Auto-range mode - int raw12 = 4095, raw6 = 4095, raw2 = 4095, raw0 = 4095; - float mv12 = 0, mv6 = 0, mv2 = 0, mv0 = 0; - - // Helper lambda for reading with different attenuations - auto read_atten = [this](adc_atten_t atten) -> std::pair { - if (this->is_adc1_) { - adc_oneshot_chan_cfg_t config = { + } else { + auto read_atten = [this](adc_atten_t atten) -> std::pair { + if (this->is_adc1_) { + adc_oneshot_chan_cfg_t config = { .atten = atten, .bitwidth = ADC_BITWIDTH_DEFAULT, - }; - ESP_ERROR_CHECK(adc_oneshot_config_channel(this->adc1_handle_, this->channel_, &config)); - int raw; - ESP_ERROR_CHECK(adc_oneshot_read(this->adc1_handle_, this->channel_, &raw)); - if (this->calibration_handle_ != nullptr) { - int voltage_mv; - ESP_ERROR_CHECK(adc_cali_raw_to_voltage(this->calibration_handle_, raw, &voltage_mv)); - return {raw, voltage_mv / 1000.0f}; - } - return {raw, raw * 3.3f / 4095.0f}; - } else { - adc_oneshot_chan_cfg_t config = { + }; + esp_err_t err = adc_oneshot_config_channel(this->adc1_handle_, this->channel_, &config); + if (err != ESP_OK) { + ESP_LOGW(TAG, "Error configuring ADC1 channel for autorange: %d", err); + return {-1, 0.0f}; + } + + int raw; + err = adc_oneshot_read(this->adc1_handle_, this->channel_, &raw); + if (err != ESP_OK) { + ESP_LOGW(TAG, "ADC1 read failed in autorange with error %d", err); + return {-1, 0.0f}; + } + + if (this->calibration_handle_ != nullptr) { + int voltage_mv; + err = adc_cali_raw_to_voltage(this->calibration_handle_, raw, &voltage_mv); + if (err == ESP_OK) { + return {raw, voltage_mv / 1000.0f}; + } + ESP_LOGW(TAG, "ADC calibration conversion failed in autorange with error %d", err); + } + return {raw, raw * 3.3f / 4095.0f}; + } else { + adc_oneshot_chan_cfg_t config = { .atten = atten, .bitwidth = ADC_BITWIDTH_DEFAULT, - }; - ESP_ERROR_CHECK(adc_oneshot_config_channel(this->adc2_handle_, this->channel_, &config)); - int raw; - ESP_ERROR_CHECK(adc_oneshot_read(this->adc2_handle_, this->channel_, &raw)); - if (this->calibration_handle_ != nullptr) { - int voltage_mv; - ESP_ERROR_CHECK(adc_cali_raw_to_voltage(this->calibration_handle_, raw, &voltage_mv)); - return {raw, voltage_mv / 1000.0f}; + }; + esp_err_t err = adc_oneshot_config_channel(this->adc2_handle_, this->channel_, &config); + if (err != ESP_OK) { + ESP_LOGW(TAG, "Error configuring ADC2 channel for autorange: %d", err); + return {-1, 0.0f}; + } + + int raw; + err = adc_oneshot_read(this->adc2_handle_, this->channel_, &raw); + if (err != ESP_OK) { + ESP_LOGW(TAG, "ADC2 read failed in autorange with error %d", err); + return {-1, 0.0f}; + } + + if (this->calibration_handle_ != nullptr) { + int voltage_mv; + err = adc_cali_raw_to_voltage(this->calibration_handle_, raw, &voltage_mv); + if (err == ESP_OK) { + return {raw, voltage_mv / 1000.0f}; + } + ESP_LOGW(TAG, "ADC calibration conversion failed in autorange with error %d", err); + } + return {raw, raw * 3.3f / 4095.0f}; } - return {raw, raw * 3.3f / 4095.0f}; + }; + + auto [raw12, mv12] = read_atten(ADC_ATTEN_DB_12); + if (raw12 == -1) { + ESP_LOGE(TAG, "Failed to read ADC in autorange mode"); + return NAN; } - }; - auto [raw12_val, mv12_val] = read_atten(ADC_ATTEN_DB_12); - raw12 = raw12_val; - mv12 = mv12_val; + int raw6 = 4095, raw2 = 4095, raw0 = 4095; + float mv6 = 0, mv2 = 0, mv0 = 0; - if (raw12 < 4095) { - auto [raw6_val, mv6_val] = read_atten(ADC_ATTEN_DB_6); - raw6 = raw6_val; - mv6 = mv6_val; + if (raw12 < 4095) { + auto [raw6_val, mv6_val] = read_atten(ADC_ATTEN_DB_6); + raw6 = raw6_val; + mv6 = mv6_val; - if (raw6 < 4095) { - auto [raw2_val, mv2_val] = read_atten(ADC_ATTEN_DB_2_5); - raw2 = raw2_val; - mv2 = mv2_val; + if (raw6 < 4095 && raw6 != -1) { + auto [raw2_val, mv2_val] = read_atten(ADC_ATTEN_DB_2_5); + raw2 = raw2_val; + mv2 = mv2_val; - if (raw2 < 4095) { - auto [raw0_val, mv0_val] = read_atten(ADC_ATTEN_DB_0); - raw0 = raw0_val; - mv0 = mv0_val; + if (raw2 < 4095 && raw2 != -1) { + auto [raw0_val, mv0_val] = read_atten(ADC_ATTEN_DB_0); + raw0 = raw0_val; + mv0 = mv0_val; + } } } + + if (raw0 == -1 || raw2 == -1 || raw6 == -1 || raw12 == -1) { + return NAN; + } + + const int ADC_HALF = 2048; + uint32_t c12 = std::min(raw12, ADC_HALF); + uint32_t c6 = ADC_HALF - std::abs(raw6 - ADC_HALF); + uint32_t c2 = ADC_HALF - std::abs(raw2 - ADC_HALF); + uint32_t c0 = std::min(4095 - raw0, ADC_HALF); + uint32_t csum = c12 + c6 + c2 + c0; + + if (csum == 0) { + ESP_LOGE(TAG, "Invalid weight sum in autorange calculation"); + return NAN; + } + + return (mv12 * c12 + mv6 * c6 + mv2 * c2 + mv0 * c0) / csum; } - - if (raw0 == -1 || raw2 == -1 || raw6 == -1 || raw12 == -1) { - return NAN; - } - - const int ADC_HALF = 2048; - uint32_t c12 = std::min(raw12, ADC_HALF); - uint32_t c6 = ADC_HALF - std::abs(raw6 - ADC_HALF); - uint32_t c2 = ADC_HALF - std::abs(raw2 - ADC_HALF); - uint32_t c0 = std::min(4095 - raw0, ADC_HALF); - uint32_t csum = c12 + c6 + c2 + c0; - - return (mv12 * c12 + mv6 * c6 + mv2 * c2 + mv0 * c0) / csum; } } // namespace adc