diff --git a/esphome/components/adc/__init__.py b/esphome/components/adc/__init__.py index 5c763a4f4c..c397e746b0 100644 --- a/esphome/components/adc/__init__.py +++ b/esphome/components/adc/__init__.py @@ -13,6 +13,7 @@ from esphome.components.esp32 import ( VARIANT_ESP32P4, VARIANT_ESP32S2, VARIANT_ESP32S3, + VARIANT_ESP32S31, get_esp32_variant, ) import esphome.config_validation as cv @@ -156,6 +157,17 @@ ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = { 9: adc_channel_t.ADC_CHANNEL_8, 10: adc_channel_t.ADC_CHANNEL_9, }, + # https://github.com/espressif/esp-idf/blob/master/components/soc/esp32s31/include/soc/adc_channel.h + VARIANT_ESP32S31: { + 42: adc_channel_t.ADC_CHANNEL_0, + 43: adc_channel_t.ADC_CHANNEL_1, + 44: adc_channel_t.ADC_CHANNEL_2, + 45: adc_channel_t.ADC_CHANNEL_3, + 46: adc_channel_t.ADC_CHANNEL_4, + 47: adc_channel_t.ADC_CHANNEL_5, + 48: adc_channel_t.ADC_CHANNEL_6, + 49: adc_channel_t.ADC_CHANNEL_7, + }, } # pin to adc2 channel mapping @@ -225,6 +237,17 @@ ESP32_VARIANT_ADC2_PIN_TO_CHANNEL = { 19: adc_channel_t.ADC_CHANNEL_8, 20: adc_channel_t.ADC_CHANNEL_9, }, + # https://github.com/espressif/esp-idf/blob/master/components/soc/esp32s31/include/soc/adc_channel.h + VARIANT_ESP32S31: { + 50: adc_channel_t.ADC_CHANNEL_0, + 51: adc_channel_t.ADC_CHANNEL_1, + 52: adc_channel_t.ADC_CHANNEL_2, + 53: adc_channel_t.ADC_CHANNEL_3, + 54: adc_channel_t.ADC_CHANNEL_4, + 55: adc_channel_t.ADC_CHANNEL_5, + 56: adc_channel_t.ADC_CHANNEL_6, + 57: adc_channel_t.ADC_CHANNEL_7, + }, } diff --git a/esphome/components/adc/adc_sensor_esp32.cpp b/esphome/components/adc/adc_sensor_esp32.cpp index a0f7a1ed08..c9887cea7c 100644 --- a/esphome/components/adc/adc_sensor_esp32.cpp +++ b/esphome/components/adc/adc_sensor_esp32.cpp @@ -74,9 +74,7 @@ void ADCSensor::setup() { if (this->calibration_handle_ == nullptr) { adc_cali_handle_t handle = nullptr; -#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 || \ - USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2 || USE_ESP32_VARIANT_ESP32P4 || USE_ESP32_VARIANT_ESP32S3 - // RISC-V variants (except C2) and S3 use curve fitting calibration +#if defined(ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED) 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_; @@ -94,7 +92,7 @@ void ADCSensor::setup() { ESP_LOGW(TAG, "Curve fitting calibration failed with error %d, will use uncalibrated readings", err); this->setup_flags_.calibration_complete = false; } -#else // ESP32, ESP32-S2, and ESP32-C2 use line fitting calibration +#elif defined(ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED) adc_cali_line_fitting_config_t cali_config = { .unit_id = this->adc_unit_, .atten = this->attenuation_, @@ -112,7 +110,11 @@ void ADCSensor::setup() { ESP_LOGW(TAG, "Line fitting calibration failed with error %d, will use uncalibrated readings", err); this->setup_flags_.calibration_complete = false; } -#endif // ESP32C3 || ESP32C5 || ESP32C6 || ESP32C61 || ESP32H2 || ESP32P4 || ESP32S3 +#else // No calibration scheme available + (void) handle; + ESP_LOGD(TAG, "No calibration scheme for this variant, readings are uncalibrated"); + this->setup_flags_.calibration_complete = false; +#endif } this->setup_flags_.init_complete = true; @@ -121,23 +123,28 @@ void ADCSensor::setup() { void ADCSensor::dump_config() { LOG_SENSOR("", "ADC Sensor", this); LOG_PIN(" Pin: ", this->pin_); - ESP_LOGCONFIG( - TAG, - " Channel: %d\n" - " Unit: %s\n" - " Attenuation: %s\n" - " Samples: %i\n" - " Sampling mode: %s\n" - " Setup Status:\n" - " Handle Init: %s\n" - " Config: %s\n" - " Calibration: %s\n" - " Overall Init: %s", - this->channel_, LOG_STR_ARG(adc_unit_to_str(this->adc_unit_)), - this->autorange_ ? "Auto" : LOG_STR_ARG(attenuation_to_str(this->attenuation_)), this->sample_count_, - LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)), - this->setup_flags_.handle_init_complete ? "OK" : "FAILED", this->setup_flags_.config_complete ? "OK" : "FAILED", - this->setup_flags_.calibration_complete ? "OK" : "FAILED", this->setup_flags_.init_complete ? "OK" : "FAILED"); +#if defined(ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED) || defined(ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED) + const char *calibration_status = this->setup_flags_.calibration_complete ? "OK" : "FAILED"; +#else + const char *calibration_status = "N/A"; // This variant has no calibration scheme +#endif + ESP_LOGCONFIG(TAG, + " Channel: %d\n" + " Unit: %s\n" + " Attenuation: %s\n" + " Samples: %i\n" + " Sampling mode: %s\n" + " Setup Status:\n" + " Handle Init: %s\n" + " Config: %s\n" + " Calibration: %s\n" + " Overall Init: %s", + this->channel_, LOG_STR_ARG(adc_unit_to_str(this->adc_unit_)), + this->autorange_ ? "Auto" : LOG_STR_ARG(attenuation_to_str(this->attenuation_)), this->sample_count_, + LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)), + this->setup_flags_.handle_init_complete ? "OK" : "FAILED", + this->setup_flags_.config_complete ? "OK" : "FAILED", calibration_status, + this->setup_flags_.init_complete ? "OK" : "FAILED"); LOG_UPDATE_INTERVAL(this); } @@ -184,12 +191,11 @@ float ADCSensor::sample_fixed_attenuation_() { } 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_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 || \ - USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2 || USE_ESP32_VARIANT_ESP32P4 || USE_ESP32_VARIANT_ESP32S3 +#if defined(ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED) adc_cali_delete_scheme_curve_fitting(this->calibration_handle_); -#else // Other ESP32 variants use line fitting calibration +#elif defined(ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED) adc_cali_delete_scheme_line_fitting(this->calibration_handle_); -#endif // ESP32C3 || ESP32C5 || ESP32C6 || ESP32C61 || ESP32H2 || ESP32P4 || ESP32S3 +#endif this->calibration_handle_ = nullptr; } } @@ -217,10 +223,9 @@ float ADCSensor::sample_autorange_() { // Need to recalibrate for the new attenuation if (this->calibration_handle_ != nullptr) { // Delete old calibration handle -#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 || \ - USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2 || USE_ESP32_VARIANT_ESP32P4 || USE_ESP32_VARIANT_ESP32S3 +#if defined(ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED) adc_cali_delete_scheme_curve_fitting(this->calibration_handle_); -#else +#elif defined(ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED) adc_cali_delete_scheme_line_fitting(this->calibration_handle_); #endif this->calibration_handle_ = nullptr; @@ -229,8 +234,7 @@ float ADCSensor::sample_autorange_() { // Create new calibration handle for this attenuation adc_cali_handle_t handle = nullptr; -#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 || \ - USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2 || USE_ESP32_VARIANT_ESP32P4 || USE_ESP32_VARIANT_ESP32S3 +#if defined(ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED) adc_cali_curve_fitting_config_t cali_config = {}; #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) cali_config.chan = this->channel_; @@ -242,7 +246,7 @@ float ADCSensor::sample_autorange_() { err = adc_cali_create_scheme_curve_fitting(&cali_config, &handle); ESP_LOGVV(TAG, "Autorange atten=%d: Calibration handle creation %s (err=%d)", atten, (err == ESP_OK) ? "SUCCESS" : "FAILED", err); -#else +#elif defined(ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED) adc_cali_line_fitting_config_t cali_config = { .unit_id = this->adc_unit_, .atten = atten, @@ -264,10 +268,9 @@ float ADCSensor::sample_autorange_() { if (err != ESP_OK) { ESP_LOGW(TAG, "ADC read failed in autorange with error %d", err); if (handle != nullptr) { -#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 || \ - USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2 || USE_ESP32_VARIANT_ESP32P4 || USE_ESP32_VARIANT_ESP32S3 +#if defined(ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED) adc_cali_delete_scheme_curve_fitting(handle); -#else +#elif defined(ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED) adc_cali_delete_scheme_line_fitting(handle); #endif } @@ -286,10 +289,9 @@ float ADCSensor::sample_autorange_() { ESP_LOGVV(TAG, "Autorange atten=%d: UNCALIBRATED FALLBACK - raw=%d -> %.6fV (3.3V ref)", atten, raw, voltage); } // Clean up calibration handle -#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C5 || USE_ESP32_VARIANT_ESP32C6 || \ - USE_ESP32_VARIANT_ESP32C61 || USE_ESP32_VARIANT_ESP32H2 || USE_ESP32_VARIANT_ESP32P4 || USE_ESP32_VARIANT_ESP32S3 +#if defined(ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED) adc_cali_delete_scheme_curve_fitting(handle); -#else +#elif defined(ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED) adc_cali_delete_scheme_line_fitting(handle); #endif } else { diff --git a/esphome/components/adc/sensor.py b/esphome/components/adc/sensor.py index 5d1031825e..8cdea4f01a 100644 --- a/esphome/components/adc/sensor.py +++ b/esphome/components/adc/sensor.py @@ -3,6 +3,7 @@ import logging import esphome.codegen as cg from esphome.components import sensor, voltage_sampler from esphome.components.esp32 import ( + VARIANT_ESP32S31, get_esp32_variant, include_builtin_idf_component, require_adc_oneshot_iram, @@ -56,6 +57,14 @@ def validate_config(config: ConfigType) -> ConfigType: if config[CONF_RAW] and config.get(CONF_ATTENUATION, None) == "auto": raise cv.Invalid("Automatic attenuation cannot be used when raw output is set") + # The S31 ADC supports a single attenuation level (SOC_ADC_ATTEN_NUM is 1) + if ( + CORE.is_esp32 + and get_esp32_variant() == VARIANT_ESP32S31 + and config.get(CONF_ATTENUATION, "0db") != "0db" + ): + raise cv.Invalid("ESP32-S31 only supports 'attenuation: 0db'") + if config.get(CONF_ATTENUATION, None) == "auto" and config.get(CONF_SAMPLES, 1) > 1: raise cv.Invalid( "Automatic attenuation cannot be used when multisampling is set"