1
0
mirror of https://github.com/esphome/esphome.git synced 2025-02-28 15:58:16 +00:00

Handle multiple ADC instances

This commit is contained in:
Edward Firmo 2024-12-12 11:28:20 +01:00
parent e30642eda1
commit ec366fe086
2 changed files with 73 additions and 25 deletions

View File

@ -200,6 +200,12 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
adc_channel_t channel_;
bool is_adc1_{true};
bool do_setup_{false};
bool init_complete_{false};
bool config_complete_{false};
bool handle_init_complete_{false};
bool calibration_complete_{false};
static adc_oneshot_unit_handle_t shared_adc1_handle_;
static adc_oneshot_unit_handle_t shared_adc2_handle_;
#else
adc_atten_t attenuation_{ADC_ATTEN_DB_0};
adc1_channel_t channel1_{ADC1_CHANNEL_MAX};

View File

@ -10,6 +10,9 @@ namespace adc {
static const char *const TAG = "adc.esp32";
adc_oneshot_unit_handle_t ADCSensor::shared_adc1_handle_ = nullptr;
adc_oneshot_unit_handle_t ADCSensor::shared_adc2_handle_ = nullptr;
void ADCSensor::setup() {
ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
@ -17,19 +20,33 @@ void ADCSensor::setup() {
return;
}
this->init_complete_ = false;
this->handle_init_complete_ = false;
this->config_complete_ = false;
this->calibration_complete_ = false;
if (this->is_adc1_) {
if (this->adc1_handle_ == nullptr) {
adc_oneshot_unit_init_cfg_t init_config1 = {}; // Zero initialize
init_config1.unit_id = ADC_UNIT_1;
init_config1.ulp_mode = ADC_ULP_MODE_DISABLE;
// Check if another sensor already initialized ADC1
if (shared_adc1_handle_ != nullptr) {
this->adc1_handle_ = shared_adc1_handle_;
this->handle_init_complete_ = true;
} else {
adc_oneshot_unit_init_cfg_t init_config1 = {};
init_config1.unit_id = ADC_UNIT_1;
init_config1.ulp_mode = ADC_ULP_MODE_DISABLE;
#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32H2
init_config1.clk_src = ADC_DIGI_CLK_SRC_DEFAULT;
init_config1.clk_src = ADC_DIGI_CLK_SRC_DEFAULT;
#endif // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32H2
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;
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->handle_init_complete_ = false;
this->mark_failed();
return;
}
shared_adc1_handle_ = this->adc1_handle_;
this->handle_init_complete_ = true;
}
}
@ -40,23 +57,34 @@ void ADCSensor::setup() {
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->config_complete_ = false;
this->mark_failed();
return;
}
this->config_complete_ = true;
} else {
if (this->adc2_handle_ == nullptr) {
adc_oneshot_unit_init_cfg_t init_config2 = {}; // Zero initialize
init_config2.unit_id = ADC_UNIT_2;
init_config2.ulp_mode = ADC_ULP_MODE_DISABLE;
// Check if another sensor already initialized ADC2
if (shared_adc2_handle_ != nullptr) {
this->adc2_handle_ = shared_adc2_handle_;
this->handle_init_complete_ = true;
} else {
adc_oneshot_unit_init_cfg_t init_config2 = {};
init_config2.unit_id = ADC_UNIT_2;
init_config2.ulp_mode = ADC_ULP_MODE_DISABLE;
#if USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32H2
init_config2.clk_src = ADC_DIGI_CLK_SRC_DEFAULT;
init_config2.clk_src = ADC_DIGI_CLK_SRC_DEFAULT;
#endif // USE_ESP32_VARIANT_ESP32C3 || USE_ESP32_VARIANT_ESP32C6 || USE_ESP32_VARIANT_ESP32H2
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;
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->handle_init_complete_ = false;
this->mark_failed();
return;
}
shared_adc2_handle_ = this->adc2_handle_;
this->handle_init_complete_ = true;
}
}
@ -67,11 +95,14 @@ void ADCSensor::setup() {
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->config_complete_ = false;
this->mark_failed();
return;
}
this->config_complete_ = true;
}
// Initialize ADC calibration if not already done
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;
@ -81,44 +112,55 @@ void ADCSensor::setup() {
// 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_;
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.unit_id = unit_id;
cali_config.atten = this->attenuation_;
cali_config.bitwidth = ADC_BITWIDTH_DEFAULT;
err = adc_cali_create_scheme_curve_fitting(&cali_config, &handle);
if (err == ESP_OK) {
this->calibration_handle_ = handle;
this->calibration_complete_ = true;
ESP_LOGV(TAG, "Using curve fitting calibration");
} else {
ESP_LOGW(TAG, "Curve fitting calibration failed with error %d, will use uncalibrated readings", err);
this->calibration_complete_ = false;
}
#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,
.unit_id = unit_id,
.atten = this->attenuation_,
.bitwidth = ADC_BITWIDTH_DEFAULT,
#if !defined(USE_ESP32_VARIANT_ESP32S2)
.default_vref = 1100, // Default reference voltage in mV
.default_vref = 1100, // Default reference voltage in mV
#endif // !defined(USE_ESP32_VARIANT_ESP32S2)
};
err = adc_cali_create_scheme_line_fitting(&cali_config, &handle);
if (err == ESP_OK) {
this->calibration_handle_ = handle;
this->calibration_complete_ = true;
ESP_LOGV(TAG, "Using line fitting calibration");
} else {
ESP_LOGW(TAG, "Line fitting calibration failed with error %d, will use uncalibrated readings", err);
this->calibration_complete_ = false;
}
#endif // USE_ESP32_VARIANT_ESP32C3 || ESP32C6 || ESP32S3 || ESP32H2
}
this->init_complete_ = true;
this->do_setup_ = false;
}
void ADCSensor::dump_config() {
LOG_SENSOR("", "ADC Sensor", this);
LOG_PIN(" Pin: ", this->pin_);
ESP_LOGCONFIG(TAG, " Channel: %d (Unit: %s)", this->channel_, this->is_adc1_ ? "ADC1" : "ADC2");
ESP_LOGCONFIG(TAG, " Setup Status:");
ESP_LOGCONFIG(TAG, " Handle Init: %s", this->handle_init_complete_ ? "OK" : "FAILED");
ESP_LOGCONFIG(TAG, " Config: %s", this->config_complete_ ? "OK" : "FAILED");
ESP_LOGCONFIG(TAG, " Calibration: %s", this->calibration_complete_ ? "OK" : "FAILED");
ESP_LOGCONFIG(TAG, " Overall Init: %s", this->init_complete_ ? "OK" : "FAILED");
if (this->autorange_) {
ESP_LOGCONFIG(TAG, " Attenuation: auto");
} else {
@ -193,7 +235,7 @@ float ADCSensor::sample() {
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
#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;