[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>
This commit is contained in:
John
2026-05-06 10:15:04 -04:00
committed by GitHub
parent caaa1aefc7
commit 545ee03f42
3 changed files with 105 additions and 38 deletions
+101 -38
View File
@@ -1,6 +1,7 @@
#include "atm90e32.h"
#include <cinttypes>
#include <cmath>
#include <cstring>
#include <numbers>
#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<typename T>
static int migrate_legacy_pref_if_needed(ESPPreferenceObject &current_pref, ESPPreferenceObject &legacy_pref,
T *scratch) {
T current{};
if (current_pref.load(&current)) {
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<char, GPIO_SUMMARY_MAX_LEN> 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<OffsetCalibration[3]>(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<OffsetCalibration[3]>(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<PowerOffsetCalibration[3]>(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<PowerOffsetCalibration[3]>(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<GainCalibration[3]>(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<GainCalibration[3]>(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]);
+3
View File
@@ -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<char, GPIO_SUMMARY_MAX_LEN> 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};
+1
View File
@@ -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)