From 545ee03f42e830b8ea0899d06886ab4b8f22ba85 Mon Sep 17 00:00:00 2001 From: John <34163498+CircuitSetup@users.noreply.github.com> Date: Wed, 6 May 2026 10:15:04 -0400 Subject: [PATCH] [atm90e32] Fix calibration instance not saving in flash properly (#14152) Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com> --- esphome/components/atm90e32/atm90e32.cpp | 139 ++++++++++++++++------- esphome/components/atm90e32/atm90e32.h | 3 + esphome/components/atm90e32/sensor.py | 1 + 3 files changed, 105 insertions(+), 38 deletions(-) diff --git a/esphome/components/atm90e32/atm90e32.cpp b/esphome/components/atm90e32/atm90e32.cpp index db29702c54..693b1b4961 100644 --- a/esphome/components/atm90e32/atm90e32.cpp +++ b/esphome/components/atm90e32/atm90e32.cpp @@ -1,6 +1,7 @@ #include "atm90e32.h" #include #include +#include #include #include "esphome/core/log.h" @@ -8,6 +9,25 @@ namespace esphome { namespace atm90e32 { static const char *const TAG = "atm90e32"; + +static uint32_t pref_hash(const char *prefix, const char *name_space) { + auto hash = fnv1_hash(prefix); + return fnv1_hash_extend(hash, name_space); +} + +template +static int migrate_legacy_pref_if_needed(ESPPreferenceObject ¤t_pref, ESPPreferenceObject &legacy_pref, + T *scratch) { + T current{}; + if (current_pref.load(¤t)) { + return 0; + } + if (!legacy_pref.load(scratch)) { + return 0; + } + return current_pref.save(scratch) ? 1 : -1; +} + void ATM90E32Component::loop() { if (this->get_publish_interval_flag_()) { this->set_publish_interval_flag_(false); @@ -112,10 +132,14 @@ void ATM90E32Component::get_cs_summary_(std::span bu this->cs_->dump_summary(buffer.data(), buffer.size()); } +const char *ATM90E32Component::get_calibration_id_() { return this->instance_id_; } + void ATM90E32Component::setup() { this->spi_setup(); - char cs[GPIO_SUMMARY_MAX_LEN]; - this->get_cs_summary_(cs); + const char *cs = this->get_calibration_id_(); + char legacy_cs[GPIO_SUMMARY_MAX_LEN]; + this->get_cs_summary_(legacy_cs); + const bool has_distinct_legacy_namespace = strcmp(cs, legacy_cs) != 0; uint16_t mmode0 = 0x87; // 3P4W 50Hz uint16_t high_thresh = 0; @@ -162,15 +186,46 @@ void ATM90E32Component::setup() { if (this->enable_offset_calibration_) { // Initialize flash storage for offset calibrations - uint32_t o_hash = fnv1_hash("_offset_calibration_"); - o_hash = fnv1_hash_extend(o_hash, cs); + uint32_t o_hash = pref_hash("_offset_calibration_", cs); this->offset_pref_ = global_preferences->make_preference(o_hash, true); - this->restore_offset_calibrations_(); + bool migrated_offset = false; + if (has_distinct_legacy_namespace) { + uint32_t legacy_o_hash = pref_hash("_offset_calibration_", legacy_cs); + auto legacy_offset_pref = global_preferences->make_preference(legacy_o_hash, true); + OffsetCalibration offset_data[3]{}; + int migration_status = migrate_legacy_pref_if_needed(this->offset_pref_, legacy_offset_pref, &offset_data); + migrated_offset = migration_status > 0; + if (migration_status > 0) { + ESP_LOGI(TAG, "[CALIBRATION][%s] Migrated offset calibrations from legacy storage.", cs); + } else if (migration_status < 0) { + ESP_LOGW(TAG, "[CALIBRATION][%s] Failed to migrate offset calibrations from legacy storage.", cs); + } + } // Initialize flash storage for power offset calibrations - uint32_t po_hash = fnv1_hash("_power_offset_calibration_"); - po_hash = fnv1_hash_extend(po_hash, cs); + uint32_t po_hash = pref_hash("_power_offset_calibration_", cs); this->power_offset_pref_ = global_preferences->make_preference(po_hash, true); + bool migrated_power_offset = false; + if (has_distinct_legacy_namespace) { + uint32_t legacy_po_hash = pref_hash("_power_offset_calibration_", legacy_cs); + auto legacy_power_offset_pref = + global_preferences->make_preference(legacy_po_hash, true); + PowerOffsetCalibration power_offset_data[3]{}; + int migration_status = + migrate_legacy_pref_if_needed(this->power_offset_pref_, legacy_power_offset_pref, &power_offset_data); + migrated_power_offset = migration_status > 0; + if (migration_status > 0) { + ESP_LOGI(TAG, "[CALIBRATION][%s] Migrated power offset calibrations from legacy storage.", cs); + } else if (migration_status < 0) { + ESP_LOGW(TAG, "[CALIBRATION][%s] Failed to migrate power offset calibrations from legacy storage.", cs); + } + } + + if (migrated_offset || migrated_power_offset) { + global_preferences->sync(); + } + + this->restore_offset_calibrations_(); this->restore_power_offset_calibrations_(); } else { ESP_LOGI(TAG, "[CALIBRATION][%s] Power & Voltage/Current offset calibration is disabled. Using config file values.", @@ -189,9 +244,27 @@ void ATM90E32Component::setup() { if (this->enable_gain_calibration_) { // Initialize flash storage for gain calibration - uint32_t g_hash = fnv1_hash("_gain_calibration_"); - g_hash = fnv1_hash_extend(g_hash, cs); + uint32_t g_hash = pref_hash("_gain_calibration_", cs); this->gain_calibration_pref_ = global_preferences->make_preference(g_hash, true); + bool migrated_gain = false; + if (has_distinct_legacy_namespace) { + uint32_t legacy_g_hash = pref_hash("_gain_calibration_", legacy_cs); + auto legacy_gain_calibration_pref = global_preferences->make_preference(legacy_g_hash, true); + GainCalibration gain_data[3]{}; + int migration_status = + migrate_legacy_pref_if_needed(this->gain_calibration_pref_, legacy_gain_calibration_pref, &gain_data); + migrated_gain = migration_status > 0; + if (migration_status > 0) { + ESP_LOGI(TAG, "[CALIBRATION][%s] Migrated gain calibrations from legacy storage.", cs); + } else if (migration_status < 0) { + ESP_LOGW(TAG, "[CALIBRATION][%s] Failed to migrate gain calibrations from legacy storage.", cs); + } + } + + if (migrated_gain) { + global_preferences->sync(); + } + this->restore_gain_calibrations_(); if (!this->using_saved_calibrations_) { @@ -221,8 +294,7 @@ void ATM90E32Component::setup() { } void ATM90E32Component::log_calibration_status_() { - char cs[GPIO_SUMMARY_MAX_LEN]; - this->get_cs_summary_(cs); + const char *cs = this->get_calibration_id_(); bool offset_mismatch = false; bool power_mismatch = false; @@ -573,8 +645,7 @@ float ATM90E32Component::get_chip_temperature_() { } void ATM90E32Component::run_gain_calibrations() { - char cs[GPIO_SUMMARY_MAX_LEN]; - this->get_cs_summary_(cs); + const char *cs = this->get_calibration_id_(); if (!this->enable_gain_calibration_) { ESP_LOGW(TAG, "[CALIBRATION][%s] Gain calibration is disabled! Enable it first with enable_gain_calibration: true", cs); @@ -674,8 +745,7 @@ void ATM90E32Component::run_gain_calibrations() { } void ATM90E32Component::save_gain_calibration_to_memory_() { - char cs[GPIO_SUMMARY_MAX_LEN]; - this->get_cs_summary_(cs); + const char *cs = this->get_calibration_id_(); bool success = this->gain_calibration_pref_.save(&this->gain_phase_); global_preferences->sync(); if (success) { @@ -688,8 +758,7 @@ void ATM90E32Component::save_gain_calibration_to_memory_() { } void ATM90E32Component::save_offset_calibration_to_memory_() { - char cs[GPIO_SUMMARY_MAX_LEN]; - this->get_cs_summary_(cs); + const char *cs = this->get_calibration_id_(); bool success = this->offset_pref_.save(&this->offset_phase_); global_preferences->sync(); if (success) { @@ -705,8 +774,7 @@ void ATM90E32Component::save_offset_calibration_to_memory_() { } void ATM90E32Component::save_power_offset_calibration_to_memory_() { - char cs[GPIO_SUMMARY_MAX_LEN]; - this->get_cs_summary_(cs); + const char *cs = this->get_calibration_id_(); bool success = this->power_offset_pref_.save(&this->power_offset_phase_); global_preferences->sync(); if (success) { @@ -722,8 +790,7 @@ void ATM90E32Component::save_power_offset_calibration_to_memory_() { } void ATM90E32Component::run_offset_calibrations() { - char cs[GPIO_SUMMARY_MAX_LEN]; - this->get_cs_summary_(cs); + const char *cs = this->get_calibration_id_(); if (!this->enable_offset_calibration_) { ESP_LOGW(TAG, "[CALIBRATION][%s] Offset calibration is disabled! Enable it first with enable_offset_calibration: true", @@ -753,8 +820,7 @@ void ATM90E32Component::run_offset_calibrations() { } void ATM90E32Component::run_power_offset_calibrations() { - char cs[GPIO_SUMMARY_MAX_LEN]; - this->get_cs_summary_(cs); + const char *cs = this->get_calibration_id_(); if (!this->enable_offset_calibration_) { ESP_LOGW( TAG, @@ -827,15 +893,16 @@ void ATM90E32Component::write_power_offsets_to_registers_(uint8_t phase, int16_t } void ATM90E32Component::restore_gain_calibrations_() { - char cs[GPIO_SUMMARY_MAX_LEN]; - this->get_cs_summary_(cs); + const char *cs = this->get_calibration_id_(); for (uint8_t i = 0; i < 3; ++i) { this->config_gain_phase_[i].voltage_gain = this->phase_[i].voltage_gain_; this->config_gain_phase_[i].current_gain = this->phase_[i].ct_gain_; this->gain_phase_[i] = this->config_gain_phase_[i]; } - if (this->gain_calibration_pref_.load(&this->gain_phase_)) { + bool have_data = this->gain_calibration_pref_.load(&this->gain_phase_); + + if (have_data) { bool all_zero = true; bool same_as_config = true; for (uint8_t phase = 0; phase < 3; ++phase) { @@ -882,12 +949,12 @@ void ATM90E32Component::restore_gain_calibrations_() { } void ATM90E32Component::restore_offset_calibrations_() { - char cs[GPIO_SUMMARY_MAX_LEN]; - this->get_cs_summary_(cs); + const char *cs = this->get_calibration_id_(); for (uint8_t i = 0; i < 3; ++i) this->config_offset_phase_[i] = this->offset_phase_[i]; bool have_data = this->offset_pref_.load(&this->offset_phase_); + bool all_zero = true; if (have_data) { for (auto &phase : this->offset_phase_) { @@ -925,12 +992,12 @@ void ATM90E32Component::restore_offset_calibrations_() { } void ATM90E32Component::restore_power_offset_calibrations_() { - char cs[GPIO_SUMMARY_MAX_LEN]; - this->get_cs_summary_(cs); + const char *cs = this->get_calibration_id_(); for (uint8_t i = 0; i < 3; ++i) this->config_power_offset_phase_[i] = this->power_offset_phase_[i]; bool have_data = this->power_offset_pref_.load(&this->power_offset_phase_); + bool all_zero = true; if (have_data) { for (auto &phase : this->power_offset_phase_) { @@ -968,8 +1035,7 @@ void ATM90E32Component::restore_power_offset_calibrations_() { } void ATM90E32Component::clear_gain_calibrations() { - char cs[GPIO_SUMMARY_MAX_LEN]; - this->get_cs_summary_(cs); + const char *cs = this->get_calibration_id_(); if (!this->using_saved_calibrations_) { ESP_LOGI(TAG, "[CALIBRATION][%s] No stored gain calibrations to clear. Current values:", cs); ESP_LOGI(TAG, "[CALIBRATION][%s] ----------------------------------------------------------", cs); @@ -1018,8 +1084,7 @@ void ATM90E32Component::clear_gain_calibrations() { } void ATM90E32Component::clear_offset_calibrations() { - char cs[GPIO_SUMMARY_MAX_LEN]; - this->get_cs_summary_(cs); + const char *cs = this->get_calibration_id_(); if (!this->restored_offset_calibration_) { ESP_LOGI(TAG, "[CALIBRATION][%s] No stored offset calibrations to clear. Current values:", cs); ESP_LOGI(TAG, "[CALIBRATION][%s] --------------------------------------------------------------", cs); @@ -1061,8 +1126,7 @@ void ATM90E32Component::clear_offset_calibrations() { } void ATM90E32Component::clear_power_offset_calibrations() { - char cs[GPIO_SUMMARY_MAX_LEN]; - this->get_cs_summary_(cs); + const char *cs = this->get_calibration_id_(); if (!this->restored_power_offset_calibration_) { ESP_LOGI(TAG, "[CALIBRATION][%s] No stored power offsets to clear. Current values:", cs); ESP_LOGI(TAG, "[CALIBRATION][%s] ---------------------------------------------------------------------", cs); @@ -1137,8 +1201,7 @@ int16_t ATM90E32Component::calibrate_power_offset(uint8_t phase, bool reactive) } bool ATM90E32Component::verify_gain_writes_() { - char cs[GPIO_SUMMARY_MAX_LEN]; - this->get_cs_summary_(cs); + const char *cs = this->get_calibration_id_(); bool success = true; for (uint8_t phase = 0; phase < 3; phase++) { uint16_t read_voltage = this->read16_(voltage_gain_registers[phase]); diff --git a/esphome/components/atm90e32/atm90e32.h b/esphome/components/atm90e32/atm90e32.h index 95154812cb..62c7bada86 100644 --- a/esphome/components/atm90e32/atm90e32.h +++ b/esphome/components/atm90e32/atm90e32.h @@ -102,6 +102,7 @@ class ATM90E32Component : public PollingComponent, void clear_gain_calibrations(); void set_enable_offset_calibration(bool flag) { enable_offset_calibration_ = flag; } void set_enable_gain_calibration(bool flag) { enable_gain_calibration_ = flag; } + void set_instance_id(const char *id) { instance_id_ = id; } int16_t calibrate_offset(uint8_t phase, bool voltage); int16_t calibrate_power_offset(uint8_t phase, bool reactive); void run_gain_calibrations(); @@ -183,6 +184,7 @@ class ATM90E32Component : public PollingComponent, bool verify_gain_writes_(); bool validate_spi_read_(uint16_t expected, const char *context = nullptr); void log_calibration_status_(); + const char *get_calibration_id_(); void get_cs_summary_(std::span buffer); struct ATM90E32Phase { @@ -263,6 +265,7 @@ class ATM90E32Component : public PollingComponent, bool peak_current_signed_{false}; bool enable_offset_calibration_{false}; bool enable_gain_calibration_{false}; + const char *instance_id_{nullptr}; bool restored_offset_calibration_{false}; bool restored_power_offset_calibration_{false}; bool restored_gain_calibration_{false}; diff --git a/esphome/components/atm90e32/sensor.py b/esphome/components/atm90e32/sensor.py index 7e5d85c57a..dc46138add 100644 --- a/esphome/components/atm90e32/sensor.py +++ b/esphome/components/atm90e32/sensor.py @@ -193,6 +193,7 @@ CONFIG_SCHEMA = ( async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) + cg.add(var.set_instance_id(str(config[CONF_ID]))) await cg.register_component(var, config) await spi.register_spi_device(var, config)