ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
atm90e32.cpp
Go to the documentation of this file.
1#include "atm90e32.h"
2#include <cinttypes>
3#include <cmath>
4#include <cstring>
5#include <numbers>
6#include "esphome/core/log.h"
7
9
10ESPHOME_LOG_TAG(TAG, "atm90e32");
11
12static const LogString *offset_calibration_name(bool power_offsets) {
13 return power_offsets ? LOG_STR("Power offset") : LOG_STR("Offset");
14}
15
16static uint32_t pref_hash(const char *prefix, const char *name_space) {
17 auto hash = fnv1_hash(prefix);
18 return fnv1_hash_extend(hash, name_space);
19}
20
21template<typename T>
22static int migrate_legacy_pref_if_needed(ESPPreferenceObject &current_pref, ESPPreferenceObject &legacy_pref,
23 T *scratch) {
24 T current{};
25 if (current_pref.load(&current)) {
26 return 0;
27 }
28 if (!legacy_pref.load(scratch)) {
29 return 0;
30 }
31 return current_pref.save(scratch) ? 1 : -1;
32}
33
35 if (this->get_publish_interval_flag_()) {
36 this->set_publish_interval_flag_(false);
37 for (uint8_t phase = 0; phase < 3; phase++) {
38 if (this->phase_[phase].voltage_sensor_ != nullptr)
39 this->phase_[phase].voltage_ = this->get_phase_voltage_(phase);
40
41 if (this->phase_[phase].current_sensor_ != nullptr)
42 this->phase_[phase].current_ = this->get_phase_current_(phase);
43
44 if (this->phase_[phase].power_sensor_ != nullptr)
45 this->phase_[phase].active_power_ = this->get_phase_active_power_(phase);
46
47 if (this->phase_[phase].power_factor_sensor_ != nullptr)
48 this->phase_[phase].power_factor_ = this->get_phase_power_factor_(phase);
49
50 if (this->phase_[phase].reactive_power_sensor_ != nullptr)
51 this->phase_[phase].reactive_power_ = this->get_phase_reactive_power_(phase);
52
53 if (this->phase_[phase].apparent_power_sensor_ != nullptr)
54 this->phase_[phase].apparent_power_ = this->get_phase_apparent_power_(phase);
55
56 if (this->phase_[phase].forward_active_energy_sensor_ != nullptr)
58
59 if (this->phase_[phase].reverse_active_energy_sensor_ != nullptr)
61
62 if (this->phase_[phase].phase_angle_sensor_ != nullptr)
63 this->phase_[phase].phase_angle_ = this->get_phase_angle_(phase);
64
65 if (this->phase_[phase].harmonic_active_power_sensor_ != nullptr)
67
68 if (this->phase_[phase].peak_current_sensor_ != nullptr)
69 this->phase_[phase].peak_current_ = this->get_phase_peak_current_(phase);
70
71 // After the local store is collected we can publish them trusting they are within +-1 hardware sampling
72 if (this->phase_[phase].voltage_sensor_ != nullptr)
74
75 if (this->phase_[phase].current_sensor_ != nullptr)
77
78 if (this->phase_[phase].power_sensor_ != nullptr)
80
81 if (this->phase_[phase].power_factor_sensor_ != nullptr)
83
84 if (this->phase_[phase].reactive_power_sensor_ != nullptr)
86
87 if (this->phase_[phase].apparent_power_sensor_ != nullptr)
89
90 if (this->phase_[phase].forward_active_energy_sensor_ != nullptr) {
93 }
94
95 if (this->phase_[phase].reverse_active_energy_sensor_ != nullptr) {
98 }
99
100 if (this->phase_[phase].phase_angle_sensor_ != nullptr)
102
103 if (this->phase_[phase].harmonic_active_power_sensor_ != nullptr) {
106 }
107
108 if (this->phase_[phase].peak_current_sensor_ != nullptr)
110 }
111 if (this->freq_sensor_ != nullptr)
113
114 if (this->chip_temperature_sensor_ != nullptr)
116 }
117}
118
120 if (!this->status_comm_result(this->read16_(ATM90E32_REGISTER_METEREN) == 1, nullptr))
121 return;
122 this->set_publish_interval_flag_(true);
123
124#ifdef USE_TEXT_SENSOR
125 this->check_phase_status();
126 this->check_over_current();
127 this->check_freq_status();
128#endif
129}
130
131void ATM90E32Component::get_cs_summary_(std::span<char, GPIO_SUMMARY_MAX_LEN> buffer) {
132 this->cs_->dump_summary(buffer.data(), buffer.size());
133}
134
136
138 this->spi_setup();
139 const char *cs = this->get_calibration_id_();
140 char legacy_cs[GPIO_SUMMARY_MAX_LEN];
141 this->get_cs_summary_(legacy_cs);
142 const bool has_distinct_legacy_namespace = strcmp(cs, legacy_cs) != 0;
143
144 uint16_t mmode0 = 0x87; // 3P4W 50Hz
145 uint16_t high_thresh = 0;
146 uint16_t low_thresh = 0;
147
148 if (line_freq_ == 60) {
149 mmode0 |= 1 << 12; // sets 12th bit to 1, 60Hz
150 // for freq threshold registers
151 high_thresh = 6300; // 63.00 Hz
152 low_thresh = 5700; // 57.00 Hz
153 } else {
154 high_thresh = 5300; // 53.00 Hz
155 low_thresh = 4700; // 47.00 Hz
156 }
157
158 if (current_phases_ == 2) {
159 mmode0 |= 1 << 8; // sets 8th bit to 1, 3P3W
160 mmode0 |= 0 << 1; // sets 1st bit to 0, phase b is not counted into the all-phase sum energy/power (P/Q/S)
161 }
162
163 this->write16_(ATM90E32_REGISTER_SOFTRESET, 0x789A, false); // Perform soft reset
164 delay(6); // Wait for the minimum 5ms + 1ms
165 this->write16_(ATM90E32_REGISTER_CFGREGACCEN, 0x55AA); // enable register config access
166 if (!this->validate_spi_read_(0x55AA, "setup()")) {
167 ESP_LOGW(TAG, "Could not initialize ATM90E32 IC, check SPI settings");
168 this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
169 return;
170 }
171
172 this->write16_(ATM90E32_REGISTER_METEREN, 0x0001); // Enable Metering
173 this->write16_(ATM90E32_REGISTER_SAGPEAKDETCFG, 0xFF3F); // Peak Detector time (15:8) 255ms, Sag Period (7:0) 63ms
174 this->write16_(ATM90E32_REGISTER_PLCONSTH, 0x0861); // PL Constant MSB (default) = 140625000
175 this->write16_(ATM90E32_REGISTER_PLCONSTL, 0xC468); // PL Constant LSB (default)
176 this->write16_(ATM90E32_REGISTER_ZXCONFIG, 0xD654); // Zero crossing (ZX2, ZX1, ZX0) pin config
177 this->write16_(ATM90E32_REGISTER_MMODE0, mmode0); // Mode Config (frequency set in main program)
178 this->write16_(ATM90E32_REGISTER_MMODE1, pga_gain_); // PGA Gain Configuration for Current Channels
179 this->write16_(ATM90E32_REGISTER_FREQHITH, high_thresh); // Frequency high threshold
180 this->write16_(ATM90E32_REGISTER_FREQLOTH, low_thresh); // Frequency low threshold
181 this->write16_(ATM90E32_REGISTER_PSTARTTH, 0x1D4C); // All Active Startup Power Threshold - 0.02A/0.00032 = 7500
182 this->write16_(ATM90E32_REGISTER_QSTARTTH, 0x1D4C); // All Reactive Startup Power Threshold - 50%
183 this->write16_(ATM90E32_REGISTER_SSTARTTH, 0x1D4C); // All Reactive Startup Power Threshold - 50%
184 this->write16_(ATM90E32_REGISTER_PPHASETH, 0x02EE); // Each Phase Active Phase Threshold - 0.002A/0.00032 = 750
185 this->write16_(ATM90E32_REGISTER_QPHASETH, 0x02EE); // Each phase Reactive Phase Threshold - 10%
186
187 if (this->enable_offset_calibration_) {
188 // Initialize flash storage for offset calibrations
189 uint32_t o_hash = pref_hash("_offset_calibration_", cs);
191 bool migrated_offset = false;
192 if (has_distinct_legacy_namespace) {
193 uint32_t legacy_o_hash = pref_hash("_offset_calibration_", legacy_cs);
194 auto legacy_offset_pref = global_preferences->make_preference<OffsetCalibration[3]>(legacy_o_hash, true);
195 OffsetCalibration offset_data[3]{};
196 int migration_status = migrate_legacy_pref_if_needed(this->offset_pref_, legacy_offset_pref, &offset_data);
197 migrated_offset = migration_status > 0;
198 if (migration_status > 0) {
199 ESP_LOGI(TAG, "[CALIBRATION][%s] Migrated offset calibrations from legacy storage.", cs);
200 } else if (migration_status < 0) {
201 ESP_LOGW(TAG, "[CALIBRATION][%s] Failed to migrate offset calibrations from legacy storage.", cs);
202 }
203 }
204
205 // Initialize flash storage for power offset calibrations
206 uint32_t po_hash = pref_hash("_power_offset_calibration_", cs);
208 bool migrated_power_offset = false;
209 if (has_distinct_legacy_namespace) {
210 uint32_t legacy_po_hash = pref_hash("_power_offset_calibration_", legacy_cs);
211 auto legacy_power_offset_pref = global_preferences->make_preference<OffsetCalibration[3]>(legacy_po_hash, true);
212 OffsetCalibration power_offset_data[3]{};
213 int migration_status =
214 migrate_legacy_pref_if_needed(this->power_offset_pref_, legacy_power_offset_pref, &power_offset_data);
215 migrated_power_offset = migration_status > 0;
216 if (migration_status > 0) {
217 ESP_LOGI(TAG, "[CALIBRATION][%s] Migrated power offset calibrations from legacy storage.", cs);
218 } else if (migration_status < 0) {
219 ESP_LOGW(TAG, "[CALIBRATION][%s] Failed to migrate power offset calibrations from legacy storage.", cs);
220 }
221 }
222
223 if (migrated_offset || migrated_power_offset) {
225 }
226
229 } else {
230 ESP_LOGI(TAG, "[CALIBRATION][%s] Power & Voltage/Current offset calibration is disabled. Using config file values.",
231 cs);
232 for (uint8_t phase = 0; phase < 3; ++phase) {
233 this->write16_(this->voltage_offset_registers[phase],
234 static_cast<uint16_t>(this->offset_phase_[phase].first_offset));
235 this->write16_(this->current_offset_registers[phase],
236 static_cast<uint16_t>(this->offset_phase_[phase].second_offset));
237 this->write16_(this->power_offset_registers[phase],
238 static_cast<uint16_t>(this->power_offset_phase_[phase].first_offset));
239 this->write16_(this->reactive_power_offset_registers[phase],
240 static_cast<uint16_t>(this->power_offset_phase_[phase].second_offset));
241 }
242 }
243
244 if (this->enable_gain_calibration_) {
245 // Initialize flash storage for gain calibration
246 uint32_t g_hash = pref_hash("_gain_calibration_", cs);
248 bool migrated_gain = false;
249 if (has_distinct_legacy_namespace) {
250 uint32_t legacy_g_hash = pref_hash("_gain_calibration_", legacy_cs);
251 auto legacy_gain_calibration_pref = global_preferences->make_preference<GainCalibration[3]>(legacy_g_hash, true);
252 GainCalibration gain_data[3]{};
253 int migration_status =
254 migrate_legacy_pref_if_needed(this->gain_calibration_pref_, legacy_gain_calibration_pref, &gain_data);
255 migrated_gain = migration_status > 0;
256 if (migration_status > 0) {
257 ESP_LOGI(TAG, "[CALIBRATION][%s] Migrated gain calibrations from legacy storage.", cs);
258 } else if (migration_status < 0) {
259 ESP_LOGW(TAG, "[CALIBRATION][%s] Failed to migrate gain calibrations from legacy storage.", cs);
260 }
261 }
262
263 if (migrated_gain) {
265 }
266
268
269 if (!this->using_saved_calibrations_) {
270 for (uint8_t phase = 0; phase < 3; ++phase) {
271 this->write16_(voltage_gain_registers[phase], this->phase_[phase].voltage_gain_);
272 this->write16_(current_gain_registers[phase], this->phase_[phase].ct_gain_);
273 }
274 }
275 } else {
276 ESP_LOGI(TAG, "[CALIBRATION][%s] Gain calibration is disabled. Using config file values.", cs);
277 for (uint8_t phase = 0; phase < 3; ++phase) {
278 this->write16_(voltage_gain_registers[phase], this->phase_[phase].voltage_gain_);
279 this->write16_(current_gain_registers[phase], this->phase_[phase].ct_gain_);
280 }
281 }
282
283 // Sag threshold (78%)
284 uint16_t sagth = calculate_voltage_threshold(line_freq_, this->phase_[0].voltage_gain_, 0.78f);
285 // Overvoltage threshold (122%)
286 uint16_t ovth = calculate_voltage_threshold(line_freq_, this->phase_[0].voltage_gain_, 1.22f);
287
288 // Write to registers
289 this->write16_(ATM90E32_REGISTER_SAGTH, sagth);
290 this->write16_(ATM90E32_REGISTER_OVTH, ovth);
291
292 this->write16_(ATM90E32_REGISTER_CFGREGACCEN, 0x0000); // end configuration
293}
294
296 const char *cs = this->get_calibration_id_();
297
298 bool offset_mismatch = false;
299 bool power_mismatch = false;
300 bool gain_mismatch = false;
301
302 for (uint8_t phase = 0; phase < 3; ++phase) {
303 offset_mismatch |= this->offset_calibration_mismatch_[phase];
304 power_mismatch |= this->power_offset_calibration_mismatch_[phase];
305 gain_mismatch |= this->gain_calibration_mismatch_[phase];
306 }
307
308 if (offset_mismatch) {
309 ESP_LOGW(TAG, "[CALIBRATION][%s] ", cs);
310 ESP_LOGW(TAG,
311 "[CALIBRATION][%s] ===================== Offset mismatch: using flash values =====================", cs);
312 ESP_LOGW(TAG, "[CALIBRATION][%s] ------------------------------------------------------------------------------",
313 cs);
314 ESP_LOGW(TAG, "[CALIBRATION][%s] | Phase | offset_voltage | offset_current |", cs);
315 ESP_LOGW(TAG, "[CALIBRATION][%s] | | config | flash | config | flash |", cs);
316 ESP_LOGW(TAG, "[CALIBRATION][%s] ------------------------------------------------------------------------------",
317 cs);
318 for (uint8_t phase = 0; phase < 3; ++phase) {
319 ESP_LOGW(TAG, "[CALIBRATION][%s] | %c | %6d | %6d | %6d | %6d |", cs, 'A' + phase,
320 this->config_offset_phase_[phase].first_offset, this->offset_phase_[phase].first_offset,
321 this->config_offset_phase_[phase].second_offset, this->offset_phase_[phase].second_offset);
322 }
323 ESP_LOGW(TAG,
324 "[CALIBRATION][%s] ===============================================================================", cs);
325 }
326 if (power_mismatch) {
327 ESP_LOGW(TAG, "[CALIBRATION][%s] ", cs);
328 ESP_LOGW(TAG,
329 "[CALIBRATION][%s] ================= Power offset mismatch: using flash values =================", cs);
330 ESP_LOGW(TAG, "[CALIBRATION][%s] ------------------------------------------------------------------------------",
331 cs);
332 ESP_LOGW(TAG, "[CALIBRATION][%s] | Phase | offset_active_power|offset_reactive_power|", cs);
333 ESP_LOGW(TAG, "[CALIBRATION][%s] | | config | flash | config | flash |", cs);
334 ESP_LOGW(TAG, "[CALIBRATION][%s] ------------------------------------------------------------------------------",
335 cs);
336 for (uint8_t phase = 0; phase < 3; ++phase) {
337 ESP_LOGW(TAG, "[CALIBRATION][%s] | %c | %6d | %6d | %6d | %6d |", cs, 'A' + phase,
338 this->config_power_offset_phase_[phase].first_offset, this->power_offset_phase_[phase].first_offset,
339 this->config_power_offset_phase_[phase].second_offset, this->power_offset_phase_[phase].second_offset);
340 }
341 ESP_LOGW(TAG,
342 "[CALIBRATION][%s] ===============================================================================", cs);
343 }
344 if (gain_mismatch) {
345 ESP_LOGW(TAG, "[CALIBRATION][%s] ", cs);
346 ESP_LOGW(TAG,
347 "[CALIBRATION][%s] ====================== Gain mismatch: using flash values =====================", cs);
348 ESP_LOGW(TAG, "[CALIBRATION][%s] ------------------------------------------------------------------------------",
349 cs);
350 ESP_LOGW(TAG, "[CALIBRATION][%s] | Phase | voltage_gain | current_gain |", cs);
351 ESP_LOGW(TAG, "[CALIBRATION][%s] | | config | flash | config | flash |", cs);
352 ESP_LOGW(TAG, "[CALIBRATION][%s] ------------------------------------------------------------------------------",
353 cs);
354 for (uint8_t phase = 0; phase < 3; ++phase) {
355 ESP_LOGW(TAG, "[CALIBRATION][%s] | %c | %6u | %6u | %6u | %6u |", cs, 'A' + phase,
356 this->config_gain_phase_[phase].voltage_gain, this->gain_phase_[phase].voltage_gain,
357 this->config_gain_phase_[phase].current_gain, this->gain_phase_[phase].current_gain);
358 }
359 ESP_LOGW(TAG,
360 "[CALIBRATION][%s] ===============================================================================", cs);
361 }
362 if (!this->enable_offset_calibration_) {
363 ESP_LOGI(TAG, "[CALIBRATION][%s] Power & Voltage/Current offset calibration is disabled. Using config file values.",
364 cs);
365 } else if (this->restored_offset_calibration_ && !offset_mismatch) {
366 ESP_LOGI(TAG, "[CALIBRATION][%s] ", cs);
367 ESP_LOGI(TAG, "[CALIBRATION][%s] ============== Restored offset calibration from memory ==============", cs);
368 ESP_LOGI(TAG, "[CALIBRATION][%s] --------------------------------------------------------------", cs);
369 ESP_LOGI(TAG, "[CALIBRATION][%s] | Phase | offset_voltage | offset_current |", cs);
370 ESP_LOGI(TAG, "[CALIBRATION][%s] --------------------------------------------------------------", cs);
371 for (uint8_t phase = 0; phase < 3; phase++) {
372 ESP_LOGI(TAG, "[CALIBRATION][%s] | %c | %6d | %6d |", cs, 'A' + phase,
373 this->offset_phase_[phase].first_offset, this->offset_phase_[phase].second_offset);
374 }
375 ESP_LOGI(TAG, "[CALIBRATION][%s] ==============================================================\\n", cs);
376 }
377
378 if (this->restored_power_offset_calibration_ && !power_mismatch) {
379 ESP_LOGI(TAG, "[CALIBRATION][%s] ", cs);
380 ESP_LOGI(TAG, "[CALIBRATION][%s] ============ Restored power offset calibration from memory ============", cs);
381 ESP_LOGI(TAG, "[CALIBRATION][%s] ---------------------------------------------------------------------", cs);
382 ESP_LOGI(TAG, "[CALIBRATION][%s] | Phase | offset_active_power | offset_reactive_power |", cs);
383 ESP_LOGI(TAG, "[CALIBRATION][%s] ---------------------------------------------------------------------", cs);
384 for (uint8_t phase = 0; phase < 3; phase++) {
385 ESP_LOGI(TAG, "[CALIBRATION][%s] | %c | %6d | %6d |", cs, 'A' + phase,
386 this->power_offset_phase_[phase].first_offset, this->power_offset_phase_[phase].second_offset);
387 }
388 ESP_LOGI(TAG, "[CALIBRATION][%s] =====================================================================\n", cs);
389 }
390 if (!this->enable_gain_calibration_) {
391 ESP_LOGI(TAG, "[CALIBRATION][%s] Gain calibration is disabled. Using config file values.", cs);
392 } else if (this->restored_gain_calibration_ && !gain_mismatch) {
393 ESP_LOGI(TAG, "[CALIBRATION][%s] ", cs);
394 ESP_LOGI(TAG, "[CALIBRATION][%s] ============ Restoring saved gain calibrations to registers ============", cs);
395 ESP_LOGI(TAG, "[CALIBRATION][%s] ---------------------------------------------------------------------", cs);
396 ESP_LOGI(TAG, "[CALIBRATION][%s] | Phase | voltage_gain | current_gain |", cs);
397 ESP_LOGI(TAG, "[CALIBRATION][%s] ---------------------------------------------------------------------", cs);
398 for (uint8_t phase = 0; phase < 3; phase++) {
399 ESP_LOGI(TAG, "[CALIBRATION][%s] | %c | %6u | %6u |", cs, 'A' + phase,
400 this->gain_phase_[phase].voltage_gain, this->gain_phase_[phase].current_gain);
401 }
402 ESP_LOGI(TAG, "[CALIBRATION][%s] =====================================================================\\n", cs);
403 ESP_LOGI(TAG, "[CALIBRATION][%s] Gain calibration loaded and verified successfully.\n", cs);
404 }
405 this->calibration_message_printed_ = true;
406}
407
409 ESP_LOGCONFIG("", "ATM90E32:");
410 LOG_PIN(" CS Pin: ", this->cs_);
411 LOG_UPDATE_INTERVAL(this);
412 LOG_SENSOR(" ", "Voltage A", this->phase_[PHASEA].voltage_sensor_);
413 LOG_SENSOR(" ", "Current A", this->phase_[PHASEA].current_sensor_);
414 LOG_SENSOR(" ", "Power A", this->phase_[PHASEA].power_sensor_);
415 LOG_SENSOR(" ", "Reactive Power A", this->phase_[PHASEA].reactive_power_sensor_);
416 LOG_SENSOR(" ", "Apparent Power A", this->phase_[PHASEA].apparent_power_sensor_);
417 LOG_SENSOR(" ", "PF A", this->phase_[PHASEA].power_factor_sensor_);
418 LOG_SENSOR(" ", "Active Forward Energy A", this->phase_[PHASEA].forward_active_energy_sensor_);
419 LOG_SENSOR(" ", "Active Reverse Energy A", this->phase_[PHASEA].reverse_active_energy_sensor_);
420 LOG_SENSOR(" ", "Harmonic Power A", this->phase_[PHASEA].harmonic_active_power_sensor_);
421 LOG_SENSOR(" ", "Phase Angle A", this->phase_[PHASEA].phase_angle_sensor_);
422 LOG_SENSOR(" ", "Peak Current A", this->phase_[PHASEA].peak_current_sensor_);
423 LOG_SENSOR(" ", "Voltage B", this->phase_[PHASEB].voltage_sensor_);
424 LOG_SENSOR(" ", "Current B", this->phase_[PHASEB].current_sensor_);
425 LOG_SENSOR(" ", "Power B", this->phase_[PHASEB].power_sensor_);
426 LOG_SENSOR(" ", "Reactive Power B", this->phase_[PHASEB].reactive_power_sensor_);
427 LOG_SENSOR(" ", "Apparent Power B", this->phase_[PHASEB].apparent_power_sensor_);
428 LOG_SENSOR(" ", "PF B", this->phase_[PHASEB].power_factor_sensor_);
429 LOG_SENSOR(" ", "Active Forward Energy B", this->phase_[PHASEB].forward_active_energy_sensor_);
430 LOG_SENSOR(" ", "Active Reverse Energy B", this->phase_[PHASEB].reverse_active_energy_sensor_);
431 LOG_SENSOR(" ", "Harmonic Power B", this->phase_[PHASEB].harmonic_active_power_sensor_);
432 LOG_SENSOR(" ", "Phase Angle B", this->phase_[PHASEB].phase_angle_sensor_);
433 LOG_SENSOR(" ", "Peak Current B", this->phase_[PHASEB].peak_current_sensor_);
434 LOG_SENSOR(" ", "Voltage C", this->phase_[PHASEC].voltage_sensor_);
435 LOG_SENSOR(" ", "Current C", this->phase_[PHASEC].current_sensor_);
436 LOG_SENSOR(" ", "Power C", this->phase_[PHASEC].power_sensor_);
437 LOG_SENSOR(" ", "Reactive Power C", this->phase_[PHASEC].reactive_power_sensor_);
438 LOG_SENSOR(" ", "Apparent Power C", this->phase_[PHASEC].apparent_power_sensor_);
439 LOG_SENSOR(" ", "PF C", this->phase_[PHASEC].power_factor_sensor_);
440 LOG_SENSOR(" ", "Active Forward Energy C", this->phase_[PHASEC].forward_active_energy_sensor_);
441 LOG_SENSOR(" ", "Active Reverse Energy C", this->phase_[PHASEC].reverse_active_energy_sensor_);
442 LOG_SENSOR(" ", "Harmonic Power C", this->phase_[PHASEC].harmonic_active_power_sensor_);
443 LOG_SENSOR(" ", "Phase Angle C", this->phase_[PHASEC].phase_angle_sensor_);
444 LOG_SENSOR(" ", "Peak Current C", this->phase_[PHASEC].peak_current_sensor_);
445 LOG_SENSOR(" ", "Frequency", this->freq_sensor_);
446 LOG_SENSOR(" ", "Chip Temp", this->chip_temperature_sensor_);
450 }
451}
452
454
455// R/C registers can conly be cleared after the LastSPIData register is updated (register 78H)
456// Peakdetect period: 05H. Bit 15:8 are PeakDet_period in ms. 7:0 are Sag_period
457// Default is 143FH (20ms, 63ms)
458uint16_t ATM90E32Component::read16_(uint16_t a_register) {
459 this->enable();
460 delay_microseconds_safe(1); // min delay between CS low and first SCK is 200ns - 1us is plenty
461 uint8_t addrh = (1 << 7) | ((a_register >> 8) & 0x03);
462 uint8_t addrl = (a_register & 0xFF);
463 uint8_t data[4] = {addrh, addrl, 0x00, 0x00};
464 this->transfer_array(data, 4);
465 uint16_t output = encode_uint16(data[2], data[3]);
466 ESP_LOGVV(TAG, "read16_ 0x%04" PRIX16 " output 0x%04" PRIX16, a_register, output);
467 delay_microseconds_safe(1); // allow the last clock to propagate before releasing CS
468 this->disable();
469 delay_microseconds_safe(1); // meet minimum CS high time before next transaction
470 return output;
471}
472
473int ATM90E32Component::read32_(uint16_t addr_h, uint16_t addr_l) {
474 const uint16_t val_h = this->read16_(addr_h);
475 const uint16_t val_l = this->read16_(addr_l);
476 const int32_t val = (val_h << 16) | val_l;
477
478 ESP_LOGVV(TAG,
479 "read32_ addr_h 0x%04" PRIX16 " val_h 0x%04" PRIX16 " addr_l 0x%04" PRIX16 " val_l 0x%04" PRIX16
480 " = %" PRId32,
481 addr_h, val_h, addr_l, val_l, val);
482
483 return val;
484}
485
486void ATM90E32Component::write16_(uint16_t a_register, uint16_t val, bool validate) {
487 ESP_LOGVV(TAG, "write16_ 0x%04" PRIX16 " val 0x%04" PRIX16, a_register, val);
488 uint8_t addrh = ((a_register >> 8) & 0x03);
489 uint8_t addrl = (a_register & 0xFF);
490 uint8_t data[4] = {addrh, addrl, uint8_t((val >> 8) & 0xFF), uint8_t(val & 0xFF)};
491 this->enable();
492 delay_microseconds_safe(1); // ensure CS setup time
493 this->write_array(data, 4);
494 delay_microseconds_safe(1); // allow clock to settle before raising CS
495 this->disable();
496 delay_microseconds_safe(1); // ensure minimum CS high time
497 if (validate)
498 this->validate_spi_read_(val, "write16()");
499}
500
501float ATM90E32Component::get_local_phase_voltage_(uint8_t phase) { return this->phase_[phase].voltage_; }
502
503float ATM90E32Component::get_local_phase_current_(uint8_t phase) { return this->phase_[phase].current_; }
504
505float ATM90E32Component::get_local_phase_active_power_(uint8_t phase) { return this->phase_[phase].active_power_; }
506
508
510
511float ATM90E32Component::get_local_phase_power_factor_(uint8_t phase) { return this->phase_[phase].power_factor_; }
512
516
520
521float ATM90E32Component::get_local_phase_angle_(uint8_t phase) { return this->phase_[phase].phase_angle_; }
522
526
527float ATM90E32Component::get_local_phase_peak_current_(uint8_t phase) { return this->phase_[phase].peak_current_; }
528
530 const uint16_t voltage = this->read16_(ATM90E32_REGISTER_URMS + phase);
531 this->validate_spi_read_(voltage, "get_phase_voltage()");
532 return (float) voltage / 100;
533}
534
536 const uint8_t reads = 10;
537 uint32_t accumulation = 0;
538 uint16_t voltage = 0;
539 for (uint8_t i = 0; i < reads; i++) {
540 voltage = this->read16_(ATM90E32_REGISTER_URMS + phase);
541 this->validate_spi_read_(voltage, "get_phase_voltage_avg_()");
542 accumulation += voltage;
543 }
544 voltage = accumulation / reads;
545 this->phase_[phase].voltage_ = (float) voltage / 100;
546 return this->phase_[phase].voltage_;
547}
548
550 const uint8_t reads = 10;
551 uint32_t accumulation = 0;
552 uint16_t current = 0;
553 for (uint8_t i = 0; i < reads; i++) {
554 current = this->read16_(ATM90E32_REGISTER_IRMS + phase);
555 this->validate_spi_read_(current, "get_phase_current_avg_()");
556 accumulation += current;
557 }
558 current = accumulation / reads;
559 this->phase_[phase].current_ = (float) current / 1000;
560 return this->phase_[phase].current_;
561}
562
564 const uint16_t current = this->read16_(ATM90E32_REGISTER_IRMS + phase);
565 this->validate_spi_read_(current, "get_phase_current_()");
566 return (float) current / 1000;
567}
568
570 const int val = this->read32_(ATM90E32_REGISTER_PMEAN + phase, ATM90E32_REGISTER_PMEANLSB + phase);
571 return val * 0.00032f;
572}
573
575 const int val = this->read32_(ATM90E32_REGISTER_QMEAN + phase, ATM90E32_REGISTER_QMEANLSB + phase);
576 return val * 0.00032f;
577}
578
580 const int val = this->read32_(ATM90E32_REGISTER_SMEAN + phase, ATM90E32_REGISTER_SMEANLSB + phase);
581 return val * 0.00032f;
582}
583
585 uint16_t powerfactor = this->read16_(ATM90E32_REGISTER_PFMEAN + phase); // unsigned to compare to lastspidata
586 this->validate_spi_read_(powerfactor, "get_phase_power_factor_()");
587 return (float) ((int16_t) powerfactor) / 1000; // make it signed again
588}
589
591 const uint16_t val = this->read16_(ATM90E32_REGISTER_APENERGY + phase);
592 if ((UINT32_MAX - this->phase_[phase].cumulative_forward_active_energy_) > val) {
594 } else {
596 }
597 // 0.01CF resolution = 0.003125 Wh per count
598 return ((float) this->phase_[phase].cumulative_forward_active_energy_ * (10.0f / 3200.0f));
599}
600
602 const uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGY + phase);
603 if (UINT32_MAX - this->phase_[phase].cumulative_reverse_active_energy_ > val) {
605 } else {
607 }
608 // 0.01CF resolution = 0.003125 Wh per count
609 return ((float) this->phase_[phase].cumulative_reverse_active_energy_ * (10.0f / 3200.0f));
610}
611
613 int val = this->read32_(ATM90E32_REGISTER_PMEANH + phase, ATM90E32_REGISTER_PMEANHLSB + phase);
614 return val * 0.00032f;
615}
616
618 float val = this->read16_(ATM90E32_REGISTER_PANGLE + phase) / 10.0f;
619 return (val > 180.0f) ? val - 360.0f : val;
620}
621
623 int16_t val = (float) this->read16_(ATM90E32_REGISTER_IPEAK + phase);
624 if (!this->peak_current_signed_)
625 val = std::abs(val);
626 // phase register * phase current gain value / 1000 * 2^13
627 return (val * this->phase_[phase].ct_gain_ / 8192000.0);
628}
629
631 const uint16_t freq = this->read16_(ATM90E32_REGISTER_FREQ);
632 return (float) freq / 100;
633}
634
636 const uint16_t ctemp = this->read16_(ATM90E32_REGISTER_TEMP);
637 return (float) ctemp;
638}
639
641 const LogString *hint, const LogString *hint2) {
642 ESP_LOGI(TAG, "%s", LOG_STR_ARG(entity->get_log_name()));
643 if (hint != nullptr) {
644 ESP_LOGI(TAG, "%s", LOG_STR_ARG(hint));
645 }
646 if (hint2 != nullptr) {
647 ESP_LOGI(TAG, "%s", LOG_STR_ARG(hint2));
648 }
649 (this->*action)();
650}
651
653 const char *cs = this->get_calibration_id_();
654 if (!this->enable_gain_calibration_) {
655 ESP_LOGW(TAG, "[CALIBRATION][%s] Gain calibration is disabled! Enable it first with enable_gain_calibration: true",
656 cs);
657 return;
658 }
659
660 float ref_voltages[3] = {
661 this->get_reference_voltage(0),
662 this->get_reference_voltage(1),
663 this->get_reference_voltage(2),
664 };
665 float ref_currents[3] = {this->get_reference_current(0), this->get_reference_current(1),
666 this->get_reference_current(2)};
667
668 ESP_LOGI(TAG, "[CALIBRATION][%s] ", cs);
669 ESP_LOGI(TAG, "[CALIBRATION][%s] ========================= Gain Calibration =========================", cs);
670 ESP_LOGI(TAG, "[CALIBRATION][%s] ---------------------------------------------------------------------", cs);
671 ESP_LOGI(
672 TAG,
673 "[CALIBRATION][%s] | Phase | V_meas (V) | I_meas (A) | V_ref | I_ref | V_gain (old→new) | I_gain (old→new) |",
674 cs);
675 ESP_LOGI(TAG, "[CALIBRATION][%s] ---------------------------------------------------------------------", cs);
676
677 for (uint8_t phase = 0; phase < 3; phase++) {
678 float measured_voltage = this->get_phase_voltage_avg_(phase);
679 float measured_current = this->get_phase_current_avg_(phase);
680
681 float ref_voltage = ref_voltages[phase];
682 float ref_current = ref_currents[phase];
683
684 uint16_t current_voltage_gain = this->read16_(voltage_gain_registers[phase]);
685 uint16_t current_current_gain = this->read16_(current_gain_registers[phase]);
686
687 bool did_voltage = false;
688 bool did_current = false;
689
690 // Voltage calibration
691 if (ref_voltage <= 0.0f) {
692 ESP_LOGW(TAG, "[CALIBRATION][%s] Phase %s - Skipping voltage calibration: reference voltage is 0.", cs,
693 phase_labels[phase]);
694 } else if (measured_voltage == 0.0f) {
695 ESP_LOGW(TAG, "[CALIBRATION][%s] Phase %s - Skipping voltage calibration: measured voltage is 0.", cs,
696 phase_labels[phase]);
697 } else {
698 uint32_t new_voltage_gain = static_cast<uint32_t>((ref_voltage / measured_voltage) * current_voltage_gain);
699 if (new_voltage_gain == 0) {
700 ESP_LOGW(TAG, "[CALIBRATION][%s] Phase %s - Voltage gain would be 0. Check reference and measured voltage.", cs,
701 phase_labels[phase]);
702 } else {
703 if (new_voltage_gain >= 65535) {
704 ESP_LOGW(TAG,
705 "[CALIBRATION][%s] Phase %s - Voltage gain exceeds 65535. You may need a higher output voltage "
706 "transformer.",
707 cs, phase_labels[phase]);
708 new_voltage_gain = 65535;
709 }
710 this->gain_phase_[phase].voltage_gain = static_cast<uint16_t>(new_voltage_gain);
711 did_voltage = true;
712 }
713 }
714
715 // Current calibration
716 if (ref_current == 0.0f) {
717 ESP_LOGW(TAG, "[CALIBRATION][%s] Phase %s - Skipping current calibration: reference current is 0.", cs,
718 phase_labels[phase]);
719 } else if (measured_current == 0.0f) {
720 ESP_LOGW(TAG, "[CALIBRATION][%s] Phase %s - Skipping current calibration: measured current is 0.", cs,
721 phase_labels[phase]);
722 } else {
723 uint32_t new_current_gain = static_cast<uint32_t>((ref_current / measured_current) * current_current_gain);
724 if (new_current_gain == 0) {
725 ESP_LOGW(TAG, "[CALIBRATION][%s] Phase %s - Current gain would be 0. Check reference and measured current.", cs,
726 phase_labels[phase]);
727 } else {
728 if (new_current_gain >= 65535) {
729 ESP_LOGW(TAG, "[CALIBRATION][%s] Phase %s - Current gain exceeds 65535. You may need to turn up pga gain.",
730 cs, phase_labels[phase]);
731 new_current_gain = 65535;
732 }
733 this->gain_phase_[phase].current_gain = static_cast<uint16_t>(new_current_gain);
734 did_current = true;
735 }
736 }
737
738 // Final row output
739 ESP_LOGI(TAG, "[CALIBRATION][%s] | %c | %9.2f | %9.4f | %5.2f | %6.4f | %5u → %-5u | %5u → %-5u |", cs,
740 'A' + phase, measured_voltage, measured_current, ref_voltage, ref_current, current_voltage_gain,
741 did_voltage ? this->gain_phase_[phase].voltage_gain : current_voltage_gain, current_current_gain,
742 did_current ? this->gain_phase_[phase].current_gain : current_current_gain);
743 }
744
745 ESP_LOGI(TAG, "[CALIBRATION][%s] =====================================================================\n", cs);
746
749 this->verify_gain_writes_();
750}
751
753 const char *cs = this->get_calibration_id_();
754 bool success = this->gain_calibration_pref_.save(&this->gain_phase_);
756 if (success) {
757 this->using_saved_calibrations_ = true;
758 ESP_LOGI(TAG, "[CALIBRATION][%s] Gain calibration saved to memory.", cs);
759 } else {
760 this->using_saved_calibrations_ = false;
761 ESP_LOGE(TAG, "[CALIBRATION][%s] Failed to save gain calibration to memory!", cs);
762 }
763}
764
765void ATM90E32Component::finish_offset_calibration_(const OffsetCalibration (&previous)[3], bool previous_restored,
766 bool previous_using_saved, OffsetCalibrationType type) {
768 const char *cs = this->get_calibration_id_();
769 const LogString *name = offset_calibration_name(power_offsets);
770 OffsetCalibration(*offsets)[3] = power_offsets ? &this->power_offset_phase_ : &this->offset_phase_;
771 ESPPreferenceObject *preference = power_offsets ? &this->power_offset_pref_ : &this->offset_pref_;
772 bool *has_stored =
774 bool *restored = power_offsets ? &this->restored_power_offset_calibration_ : &this->restored_offset_calibration_;
775 bool *mismatches = power_offsets ? this->power_offset_calibration_mismatch_ : this->offset_calibration_mismatch_;
776
777 const bool writes_verified = this->verify_offset_writes_(type);
778 bool saved = false;
779 bool synced = false;
780 if (writes_verified) {
781 saved = preference->save(offsets);
782 synced = global_preferences->sync();
783 }
784
785 if (writes_verified && saved && synced) {
786 this->using_saved_calibrations_ = true;
787 *has_stored = true;
788 *restored = true;
789 for (uint8_t phase = 0; phase < 3; phase++)
790 mismatches[phase] = false;
791 ESP_LOGI(TAG, "[CALIBRATION][%s] %s calibration saved to memory. %s calibration completed and verified.", cs,
792 LOG_STR_ARG(name), LOG_STR_ARG(name));
793 return;
794 }
795
796 if (writes_verified) {
797 ESP_LOGE(TAG, "[CALIBRATION][%s] Failed to save %s calibration to memory!", cs, LOG_STR_ARG(name));
798 }
799
800 for (uint8_t phase = 0; phase < 3; phase++) {
801 this->write_offsets_to_registers_(phase, previous[phase].first_offset, previous[phase].second_offset, type);
802 }
803 const bool rollback_verified = this->verify_offset_writes_(type);
804
805 bool rollback_persisted = false;
806 if (writes_verified) {
807 OffsetCalibration rollback[3]{};
808 prepare_offset_rollback(previous, previous_restored, rollback);
809 const bool rollback_saved = preference->save(&rollback);
810 const bool rollback_synced = global_preferences->sync();
811 rollback_persisted = rollback_saved && rollback_synced;
812 if (!rollback_saved || !rollback_synced) {
813 ESP_LOGE(TAG, "[CALIBRATION][%s] Failed to persist restored %s calibration values!", cs, LOG_STR_ARG(name));
814 }
815 }
816
817 *restored = previous_restored;
818 if (rollback_persisted)
819 *has_stored = previous_restored;
820 this->using_saved_calibrations_ = previous_using_saved;
821 if (!rollback_verified) {
822 ESP_LOGE(TAG, "[CALIBRATION][%s] %s calibration failed; rollback readback verification failed.", cs,
823 LOG_STR_ARG(name));
824 return;
825 }
826 ESP_LOGE(TAG, "[CALIBRATION][%s] %s calibration failed; previous values restored.", cs, LOG_STR_ARG(name));
827}
828
830 const char *cs = this->get_calibration_id_();
831 if (!this->enable_offset_calibration_) {
832 ESP_LOGW(TAG,
833 "[CALIBRATION][%s] Offset calibration is disabled! Enable it first with enable_offset_calibration: true",
834 cs);
835 return;
836 }
837
838 ESP_LOGI(TAG, "[CALIBRATION][%s] ", cs);
839 ESP_LOGI(TAG, "[CALIBRATION][%s] ======================== Offset Calibration ========================", cs);
840 ESP_LOGI(TAG, "[CALIBRATION][%s] ------------------------------------------------------------------", cs);
841 ESP_LOGI(TAG, "[CALIBRATION][%s] | Phase | offset_voltage | offset_current |", cs);
842 ESP_LOGI(TAG, "[CALIBRATION][%s] ------------------------------------------------------------------", cs);
843
844 OffsetCalibration previous_offsets[3] = {this->offset_phase_[0], this->offset_phase_[1], this->offset_phase_[2]};
845 const bool previous_restored = this->restored_offset_calibration_;
846 const bool previous_using_saved = this->using_saved_calibrations_;
847
848 for (uint8_t phase = 0; phase < 3; phase++) {
849 int16_t voltage_offset = calibrate_offset(phase, true);
850 int16_t current_offset = calibrate_offset(phase, false);
851
852 this->write_offsets_to_registers_(phase, voltage_offset, current_offset,
854
855 ESP_LOGI(TAG, "[CALIBRATION][%s] | %c | %6d | %6d |", cs, 'A' + phase, voltage_offset,
856 current_offset);
857 }
858
859 ESP_LOGI(TAG, "[CALIBRATION][%s] ==================================================================\n", cs);
860
861 this->finish_offset_calibration_(previous_offsets, previous_restored, previous_using_saved,
863}
864
866 const char *cs = this->get_calibration_id_();
867 if (!this->enable_offset_calibration_) {
868 ESP_LOGW(
869 TAG,
870 "[CALIBRATION][%s] Offset power calibration is disabled! Enable it first with enable_offset_calibration: true",
871 cs);
872 return;
873 }
874
875 ESP_LOGI(TAG, "[CALIBRATION][%s] ", cs);
876 ESP_LOGI(TAG, "[CALIBRATION][%s] ===================== Power Offset Calibration =====================", cs);
877 ESP_LOGI(TAG, "[CALIBRATION][%s] ---------------------------------------------------------------------", cs);
878 ESP_LOGI(TAG, "[CALIBRATION][%s] | Phase | offset_active_power | offset_reactive_power |", cs);
879 ESP_LOGI(TAG, "[CALIBRATION][%s] ---------------------------------------------------------------------", cs);
880
881 OffsetCalibration previous_offsets[3] = {this->power_offset_phase_[0], this->power_offset_phase_[1],
882 this->power_offset_phase_[2]};
883 const bool previous_restored = this->restored_power_offset_calibration_;
884 const bool previous_using_saved = this->using_saved_calibrations_;
885
886 for (uint8_t phase = 0; phase < 3; ++phase) {
887 int16_t active_offset = calibrate_power_offset(phase, false);
888 int16_t reactive_offset = calibrate_power_offset(phase, true);
889
890 this->write_offsets_to_registers_(phase, active_offset, reactive_offset,
892
893 ESP_LOGI(TAG, "[CALIBRATION][%s] | %c | %6d | %6d |", cs, 'A' + phase, active_offset,
894 reactive_offset);
895 }
896 ESP_LOGI(TAG, "[CALIBRATION][%s] =====================================================================\n", cs);
897
898 this->finish_offset_calibration_(previous_offsets, previous_restored, previous_using_saved,
900}
901
903 this->write16_(ATM90E32_REGISTER_CFGREGACCEN, 0x55AA);
904
905 for (int phase = 0; phase < 3; phase++) {
906 this->write16_(voltage_gain_registers[phase], this->gain_phase_[phase].voltage_gain);
907 this->write16_(current_gain_registers[phase], this->gain_phase_[phase].current_gain);
908 }
909
910 this->write16_(ATM90E32_REGISTER_CFGREGACCEN, 0x0000);
911}
912
913void ATM90E32Component::write_offsets_to_registers_(uint8_t phase, int16_t first_offset, int16_t second_offset,
916 OffsetCalibration &offsets = power_offsets ? this->power_offset_phase_[phase] : this->offset_phase_[phase];
917 offsets.first_offset = first_offset;
918 offsets.second_offset = second_offset;
919 if (power_offsets) {
920 this->phase_[phase].active_power_offset_ = first_offset;
921 this->phase_[phase].reactive_power_offset_ = second_offset;
922 } else {
923 this->phase_[phase].voltage_offset_ = first_offset;
924 this->phase_[phase].current_offset_ = second_offset;
925 }
926
927 const uint16_t *first_registers = power_offsets ? this->power_offset_registers : this->voltage_offset_registers;
928 const uint16_t *second_registers =
929 power_offsets ? this->reactive_power_offset_registers : this->current_offset_registers;
930 this->write16_(ATM90E32_REGISTER_CFGREGACCEN, 0x55AA);
931 this->write16_(first_registers[phase], static_cast<uint16_t>(first_offset));
932 this->write16_(second_registers[phase], static_cast<uint16_t>(second_offset));
933 this->write16_(ATM90E32_REGISTER_CFGREGACCEN, 0x0000);
934}
935
937 const char *cs = this->get_calibration_id_();
938 for (uint8_t i = 0; i < 3; ++i) {
941 this->gain_phase_[i] = this->config_gain_phase_[i];
942 }
943
944 bool have_data = this->gain_calibration_pref_.load(&this->gain_phase_);
945
946 if (have_data) {
947 bool all_zero = true;
948 bool same_as_config = true;
949 for (uint8_t phase = 0; phase < 3; ++phase) {
950 const auto &cfg = this->config_gain_phase_[phase];
951 const auto &saved = this->gain_phase_[phase];
952 if (saved.voltage_gain != 0 || saved.current_gain != 0)
953 all_zero = false;
954 if (saved.voltage_gain != cfg.voltage_gain || saved.current_gain != cfg.current_gain)
955 same_as_config = false;
956 }
957
958 if (!all_zero && !same_as_config) {
959 for (uint8_t phase = 0; phase < 3; ++phase) {
960 bool mismatch = false;
961 if (this->has_config_voltage_gain_[phase] &&
962 this->gain_phase_[phase].voltage_gain != this->config_gain_phase_[phase].voltage_gain)
963 mismatch = true;
964 if (this->has_config_current_gain_[phase] &&
965 this->gain_phase_[phase].current_gain != this->config_gain_phase_[phase].current_gain)
966 mismatch = true;
967 if (mismatch)
968 this->gain_calibration_mismatch_[phase] = true;
969 }
970
972
973 if (this->verify_gain_writes_()) {
974 this->using_saved_calibrations_ = true;
975 this->restored_gain_calibration_ = true;
976 return;
977 }
978
979 this->using_saved_calibrations_ = false;
980 ESP_LOGE(TAG, "[CALIBRATION][%s] Gain verification failed! Calibration may not be applied correctly.", cs);
981 }
982 }
983
984 this->using_saved_calibrations_ = false;
985 for (uint8_t i = 0; i < 3; ++i)
986 this->gain_phase_[i] = this->config_gain_phase_[i];
988
989 ESP_LOGW(TAG, "[CALIBRATION][%s] No stored gain calibrations found. Using config file values.", cs);
990}
991
994 const char *cs = this->get_calibration_id_();
995 const LogString *name = power_offsets ? LOG_STR("power offset") : LOG_STR("offset");
996 OffsetCalibration(*offsets)[3] = power_offsets ? &this->power_offset_phase_ : &this->offset_phase_;
997 OffsetCalibration(*config_offsets)[3] =
998 power_offsets ? &this->config_power_offset_phase_ : &this->config_offset_phase_;
999 ESPPreferenceObject *preference = power_offsets ? &this->power_offset_pref_ : &this->offset_pref_;
1000 bool *has_stored =
1002 bool *restored = power_offsets ? &this->restored_power_offset_calibration_ : &this->restored_offset_calibration_;
1003 bool *mismatches = power_offsets ? this->power_offset_calibration_mismatch_ : this->offset_calibration_mismatch_;
1004 const bool *has_first = power_offsets ? this->has_config_active_power_offset_ : this->has_config_voltage_offset_;
1005 const bool *has_second = power_offsets ? this->has_config_reactive_power_offset_ : this->has_config_current_offset_;
1006
1007 for (uint8_t i = 0; i < 3; ++i)
1008 (*config_offsets)[i] = (*offsets)[i];
1009
1010 const bool have_data = preference->load(offsets);
1011 bool all_zero = true;
1012 if (have_data) {
1013 for (const auto &phase : *offsets) {
1014 if (phase.first_offset != 0 || phase.second_offset != 0) {
1015 all_zero = false;
1016 break;
1017 }
1018 }
1019 }
1020
1021 *has_stored = have_data && !all_zero;
1022 *restored = false;
1023 for (uint8_t phase = 0; phase < 3; phase++) {
1024 mismatches[phase] = false;
1025 if (*has_stored) {
1026 mismatches[phase] =
1027 (has_first[phase] && (*offsets)[phase].first_offset != (*config_offsets)[phase].first_offset) ||
1028 (has_second[phase] && (*offsets)[phase].second_offset != (*config_offsets)[phase].second_offset);
1029 }
1030 }
1031
1032 if (!*has_stored) {
1033 for (uint8_t phase = 0; phase < 3; phase++)
1034 (*offsets)[phase] = (*config_offsets)[phase];
1035 ESP_LOGW(TAG, "[CALIBRATION][%s] No stored %s calibrations found. Using default values.", cs, LOG_STR_ARG(name));
1036 }
1037
1038 for (uint8_t phase = 0; phase < 3; phase++) {
1039 this->write_offsets_to_registers_(phase, (*offsets)[phase].first_offset, (*offsets)[phase].second_offset, type);
1040 }
1041 const bool initial_values_verified = this->verify_offset_writes_(type);
1042 if (initial_values_verified) {
1043 const auto state = resolve_offset_restore_state(*has_stored, true, false);
1044 *restored = state.restored;
1045 ESP_LOGI(TAG, "[CALIBRATION][%s] %s calibration values verified.", cs, LOG_STR_ARG(name));
1046 return;
1047 }
1048
1049 this->using_saved_calibrations_ = false;
1050 for (uint8_t phase = 0; phase < 3; phase++)
1051 mismatches[phase] = false;
1052 for (uint8_t phase = 0; phase < 3; phase++) {
1053 (*offsets)[phase] = (*config_offsets)[phase];
1054 this->write_offsets_to_registers_(phase, (*offsets)[phase].first_offset, (*offsets)[phase].second_offset, type);
1055 }
1056 const auto state = resolve_offset_restore_state(*has_stored, false, this->verify_offset_writes_(type));
1057 *restored = state.restored;
1058 if (state.values_verified) {
1059 ESP_LOGE(TAG, "[CALIBRATION][%s] %s calibration restore failed verification; config values verified.", cs,
1060 LOG_STR_ARG(name));
1061 } else {
1062 ESP_LOGE(TAG, "[CALIBRATION][%s] %s calibration restore and config fallback both failed verification.", cs,
1063 LOG_STR_ARG(name));
1064 }
1065}
1066
1068 const char *cs = this->get_calibration_id_();
1069 if (!this->using_saved_calibrations_) {
1070 ESP_LOGI(TAG, "[CALIBRATION][%s] No stored gain calibrations to clear. Current values:", cs);
1071 ESP_LOGI(TAG, "[CALIBRATION][%s] ----------------------------------------------------------", cs);
1072 ESP_LOGI(TAG, "[CALIBRATION][%s] | Phase | voltage_gain | current_gain |", cs);
1073 ESP_LOGI(TAG, "[CALIBRATION][%s] ----------------------------------------------------------", cs);
1074 for (int phase = 0; phase < 3; phase++) {
1075 ESP_LOGI(TAG, "[CALIBRATION][%s] | %c | %6u | %6u |", cs, 'A' + phase,
1076 this->gain_phase_[phase].voltage_gain, this->gain_phase_[phase].current_gain);
1077 }
1078 ESP_LOGI(TAG, "[CALIBRATION][%s] ==========================================================\n", cs);
1079 return;
1080 }
1081
1082 ESP_LOGI(TAG, "[CALIBRATION][%s] Clearing stored gain calibrations and restoring config-defined values", cs);
1083 ESP_LOGI(TAG, "[CALIBRATION][%s] ----------------------------------------------------------", cs);
1084 ESP_LOGI(TAG, "[CALIBRATION][%s] | Phase | voltage_gain | current_gain |", cs);
1085 ESP_LOGI(TAG, "[CALIBRATION][%s] ----------------------------------------------------------", cs);
1086
1087 for (int phase = 0; phase < 3; phase++) {
1088 uint16_t voltage_gain = this->phase_[phase].voltage_gain_;
1089 uint16_t current_gain = this->phase_[phase].ct_gain_;
1090
1091 this->config_gain_phase_[phase].voltage_gain = voltage_gain;
1092 this->config_gain_phase_[phase].current_gain = current_gain;
1093 this->gain_phase_[phase].voltage_gain = voltage_gain;
1094 this->gain_phase_[phase].current_gain = current_gain;
1095
1096 ESP_LOGI(TAG, "[CALIBRATION][%s] | %c | %6u | %6u |", cs, 'A' + phase, voltage_gain, current_gain);
1097 }
1098 ESP_LOGI(TAG, "[CALIBRATION][%s] ==========================================================\n", cs);
1099
1100 GainCalibration zero_gains[3]{{0, 0}, {0, 0}, {0, 0}};
1101 bool success = this->gain_calibration_pref_.save(&zero_gains);
1103
1104 this->using_saved_calibrations_ = false;
1105 this->restored_gain_calibration_ = false;
1106 for (bool &phase : this->gain_calibration_mismatch_)
1107 phase = false;
1108
1109 if (!success) {
1110 ESP_LOGE(TAG, "[CALIBRATION][%s] Failed to clear gain calibrations!", cs);
1111 }
1112
1113 this->write_gains_to_registers_(); // Apply them to the chip immediately
1114}
1115
1117 const char *cs = this->get_calibration_id_();
1118 if (!this->has_stored_offset_calibration_) {
1119 ESP_LOGI(TAG, "[CALIBRATION][%s] No stored offset calibrations to clear. Current values:", cs);
1120 ESP_LOGI(TAG, "[CALIBRATION][%s] --------------------------------------------------------------", cs);
1121 ESP_LOGI(TAG, "[CALIBRATION][%s] | Phase | offset_voltage | offset_current |", cs);
1122 ESP_LOGI(TAG, "[CALIBRATION][%s] --------------------------------------------------------------", cs);
1123 for (uint8_t phase = 0; phase < 3; phase++) {
1124 ESP_LOGI(TAG, "[CALIBRATION][%s] | %c | %6d | %6d |", cs, 'A' + phase,
1125 this->offset_phase_[phase].first_offset, this->offset_phase_[phase].second_offset);
1126 }
1127 ESP_LOGI(TAG, "[CALIBRATION][%s] ==============================================================\n", cs);
1128 return;
1129 }
1130
1131 ESP_LOGI(TAG, "[CALIBRATION][%s] Clearing stored offset calibrations and restoring config-defined values", cs);
1132 ESP_LOGI(TAG, "[CALIBRATION][%s] --------------------------------------------------------------", cs);
1133 ESP_LOGI(TAG, "[CALIBRATION][%s] | Phase | offset_voltage | offset_current |", cs);
1134 ESP_LOGI(TAG, "[CALIBRATION][%s] --------------------------------------------------------------", cs);
1135
1136 for (uint8_t phase = 0; phase < 3; phase++) {
1137 int16_t voltage_offset =
1138 this->has_config_voltage_offset_[phase] ? this->config_offset_phase_[phase].first_offset : 0;
1139 int16_t current_offset =
1140 this->has_config_current_offset_[phase] ? this->config_offset_phase_[phase].second_offset : 0;
1141 this->write_offsets_to_registers_(phase, voltage_offset, current_offset,
1143 ESP_LOGI(TAG, "[CALIBRATION][%s] | %c | %6d | %6d |", cs, 'A' + phase, voltage_offset,
1144 current_offset);
1145 }
1146 ESP_LOGI(TAG, "[CALIBRATION][%s] ==============================================================\n", cs);
1147
1148 OffsetCalibration zero_offsets[3]{{0, 0}, {0, 0}, {0, 0}};
1149 this->offset_pref_.save(&zero_offsets); // Clear stored values in flash
1151
1152 this->has_stored_offset_calibration_ = false;
1153 this->restored_offset_calibration_ = false;
1154 for (bool &phase : this->offset_calibration_mismatch_)
1155 phase = false;
1156
1157 ESP_LOGI(TAG, "[CALIBRATION][%s] Offsets cleared.", cs);
1158}
1159
1161 const char *cs = this->get_calibration_id_();
1163 ESP_LOGI(TAG, "[CALIBRATION][%s] No stored power offsets to clear. Current values:", cs);
1164 ESP_LOGI(TAG, "[CALIBRATION][%s] ---------------------------------------------------------------------", cs);
1165 ESP_LOGI(TAG, "[CALIBRATION][%s] | Phase | offset_active_power | offset_reactive_power |", cs);
1166 ESP_LOGI(TAG, "[CALIBRATION][%s] ---------------------------------------------------------------------", cs);
1167 for (uint8_t phase = 0; phase < 3; phase++) {
1168 ESP_LOGI(TAG, "[CALIBRATION][%s] | %c | %6d | %6d |", cs, 'A' + phase,
1169 this->power_offset_phase_[phase].first_offset, this->power_offset_phase_[phase].second_offset);
1170 }
1171 ESP_LOGI(TAG, "[CALIBRATION][%s] =====================================================================\n", cs);
1172 return;
1173 }
1174
1175 ESP_LOGI(TAG, "[CALIBRATION][%s] Clearing stored power offsets and restoring config-defined values", cs);
1176 ESP_LOGI(TAG, "[CALIBRATION][%s] ---------------------------------------------------------------------", cs);
1177 ESP_LOGI(TAG, "[CALIBRATION][%s] | Phase | offset_active_power | offset_reactive_power |", cs);
1178 ESP_LOGI(TAG, "[CALIBRATION][%s] ---------------------------------------------------------------------", cs);
1179
1180 for (uint8_t phase = 0; phase < 3; phase++) {
1181 int16_t active_offset =
1183 int16_t reactive_offset =
1185 this->write_offsets_to_registers_(phase, active_offset, reactive_offset,
1187 ESP_LOGI(TAG, "[CALIBRATION][%s] | %c | %6d | %6d |", cs, 'A' + phase, active_offset,
1188 reactive_offset);
1189 }
1190 ESP_LOGI(TAG, "[CALIBRATION][%s] =====================================================================\n", cs);
1191
1192 OffsetCalibration zero_power_offsets[3]{{0, 0}, {0, 0}, {0, 0}};
1193 this->power_offset_pref_.save(&zero_power_offsets);
1195
1198 for (bool &phase : this->power_offset_calibration_mismatch_)
1199 phase = false;
1200
1201 ESP_LOGI(TAG, "[CALIBRATION][%s] Power offsets cleared.", cs);
1202}
1203
1204int16_t ATM90E32Component::calibrate_offset(uint8_t phase, bool voltage) {
1205 const uint8_t num_reads = 5;
1206 uint64_t total_value = 0;
1207
1208 for (uint8_t i = 0; i < num_reads; ++i) {
1209 uint32_t reading = voltage ? this->read32_(ATM90E32_REGISTER_URMS + phase, ATM90E32_REGISTER_URMSLSB + phase)
1210 : this->read32_(ATM90E32_REGISTER_IRMS + phase, ATM90E32_REGISTER_IRMSLSB + phase);
1211 total_value += reading;
1212 }
1213
1214 const uint32_t average_value = total_value / num_reads;
1215 const uint32_t shifted = average_value >> 7;
1216 const uint32_t offset = ~shifted + 1;
1217 return static_cast<int16_t>(offset); // Takes lower 16 bits
1218}
1219
1220int16_t ATM90E32Component::calibrate_power_offset(uint8_t phase, bool reactive) {
1221 const uint8_t num_reads = 5;
1222 int64_t total_value = 0;
1223
1224 for (uint8_t i = 0; i < num_reads; ++i) {
1225 int32_t reading = reactive ? this->read32_(ATM90E32_REGISTER_QMEAN + phase, ATM90E32_REGISTER_QMEANLSB + phase)
1226 : this->read32_(ATM90E32_REGISTER_PMEAN + phase, ATM90E32_REGISTER_PMEANLSB + phase);
1227 total_value += reading;
1228 }
1229
1230 int32_t average_value = total_value / num_reads;
1231 int32_t power_offset = -average_value;
1232 return static_cast<int16_t>(power_offset); // Takes the lower 16 bits
1233}
1234
1236 const char *cs = this->get_calibration_id_();
1237 bool success = true;
1238 for (uint8_t phase = 0; phase < 3; phase++) {
1239 uint16_t read_voltage = this->read16_(voltage_gain_registers[phase]);
1240 uint16_t read_current = this->read16_(current_gain_registers[phase]);
1241
1242 if (read_voltage != this->gain_phase_[phase].voltage_gain ||
1243 read_current != this->gain_phase_[phase].current_gain) {
1244 ESP_LOGE(TAG, "[CALIBRATION][%s] Mismatch detected for Phase %s!", cs, phase_labels[phase]);
1245 success = false;
1246 }
1247 }
1248 return success; // Return true if all writes were successful, false otherwise
1249}
1250
1252 const bool power_offsets = type == OffsetCalibrationType::OFFSET_CALIBRATION_TYPE_POWER;
1253 const char *cs = this->get_calibration_id_();
1254 const LogString *name = offset_calibration_name(power_offsets);
1255 const LogString *first_name = power_offsets ? LOG_STR("active") : LOG_STR("voltage");
1256 const LogString *second_name = power_offsets ? LOG_STR("reactive") : LOG_STR("current");
1257 const OffsetCalibration *offsets = power_offsets ? this->power_offset_phase_ : this->offset_phase_;
1258 const uint16_t *first_registers = power_offsets ? this->power_offset_registers : this->voltage_offset_registers;
1259 const uint16_t *second_registers =
1260 power_offsets ? this->reactive_power_offset_registers : this->current_offset_registers;
1261 bool success = true;
1262 for (uint8_t phase = 0; phase < 3; phase++) {
1263 const uint16_t first = this->read16_(first_registers[phase]);
1264 const uint16_t second = this->read16_(second_registers[phase]);
1265 if (!offset_register_value_matches(first, offsets[phase].first_offset) ||
1266 !offset_register_value_matches(second, offsets[phase].second_offset)) {
1267 ESP_LOGE(TAG, "[CALIBRATION][%s] %s readback failed for Phase %s: %s %d/%d, %s %d/%d.", cs, LOG_STR_ARG(name),
1268 phase_labels[phase], LOG_STR_ARG(first_name), static_cast<int16_t>(first), offsets[phase].first_offset,
1269 LOG_STR_ARG(second_name), static_cast<int16_t>(second), offsets[phase].second_offset);
1270 success = false;
1271 }
1272 }
1273 return success;
1274}
1275
1276#ifdef USE_TEXT_SENSOR
1278 uint16_t state0 = this->read16_(ATM90E32_REGISTER_EMMSTATE0);
1279 uint16_t state1 = this->read16_(ATM90E32_REGISTER_EMMSTATE1);
1280
1281 for (int phase = 0; phase < 3; phase++) {
1282 std::string status;
1283
1284 if (state0 & over_voltage_flags[phase])
1285 status += "Over Voltage; ";
1286 if (state1 & voltage_sag_flags[phase])
1287 status += "Voltage Sag; ";
1288 if (state1 & phase_loss_flags[phase])
1289 status += "Phase Loss; ";
1290
1291 auto *sensor = this->phase_status_text_sensor_[phase];
1292 if (sensor == nullptr)
1293 continue;
1294
1295 if (!status.empty()) {
1296 status.pop_back(); // remove space
1297 status.pop_back(); // remove semicolon
1298 ESP_LOGW(TAG, "%s: %s", LOG_STR_ARG(sensor->get_log_name()), status.c_str());
1299 sensor->publish_state(status);
1300 } else {
1301 sensor->publish_state("Okay");
1302 }
1303 }
1304}
1305
1307 uint16_t state1 = this->read16_(ATM90E32_REGISTER_EMMSTATE1);
1308
1309 std::string freq_status;
1310
1311 if (state1 & ATM90E32_STATUS_S1_FREQHIST) {
1312 freq_status = "HIGH";
1313 } else if (state1 & ATM90E32_STATUS_S1_FREQLOST) {
1314 freq_status = "LOW";
1315 } else {
1316 freq_status = "Normal";
1317 }
1318 if (this->freq_status_text_sensor_ != nullptr) {
1319 if (freq_status == "Normal") {
1320 ESP_LOGD(TAG, "Frequency status: %s", freq_status.c_str());
1321 } else {
1322 ESP_LOGW(TAG, "Frequency status: %s", freq_status.c_str());
1323 }
1324 this->freq_status_text_sensor_->publish_state(freq_status);
1325 }
1326}
1327
1329 constexpr float max_current_threshold = 65.53f;
1330
1331 for (uint8_t phase = 0; phase < 3; phase++) {
1332 float current_val =
1333 this->phase_[phase].current_sensor_ != nullptr ? this->phase_[phase].current_sensor_->state : 0.0f;
1334
1335 if (current_val > max_current_threshold) {
1336 ESP_LOGW(TAG, "Over current detected on Phase %c: %.2f A", 'A' + phase, current_val);
1337 ESP_LOGW(TAG, "You may need to half your gain_ct: value & multiply the current and power values by 2");
1338 if (this->phase_status_text_sensor_[phase] != nullptr) {
1339 this->phase_status_text_sensor_[phase]->publish_state("Over Current; ");
1340 }
1341 }
1342 }
1343}
1344#endif
1345
1346uint16_t ATM90E32Component::calculate_voltage_threshold(int line_freq, uint16_t ugain, float multiplier) {
1347 // this assumes that 60Hz electrical systems use 120V mains,
1348 // which is usually, but not always the case
1349 float nominal_voltage = (line_freq == 60) ? 120.0f : 220.0f;
1350 float target_voltage = nominal_voltage * multiplier;
1351
1352 float peak_01v = target_voltage * 100.0f * std::numbers::sqrt2_v<float>; // convert RMS → peak, scale to 0.01V
1353 float divider = (2.0f * ugain) / 32768.0f;
1354
1355 float threshold = peak_01v / divider;
1356
1357 return static_cast<uint16_t>(threshold);
1358}
1359
1360bool ATM90E32Component::validate_spi_read_(uint16_t expected, const char *context) {
1361 uint16_t last = this->read16_(ATM90E32_REGISTER_LASTSPIDATA);
1362 if (last != expected) {
1363 if (context != nullptr) {
1364 ESP_LOGW(TAG, "[%s] SPI read mismatch: expected 0x%04X, got 0x%04X", context, expected, last);
1365 } else {
1366 ESP_LOGW(TAG, "SPI read mismatch: expected 0x%04X, got 0x%04X", expected, last);
1367 }
1368 return false;
1369 }
1370 return true;
1371}
1372
1373} // namespace esphome::atm90e32
uint8_t status
Definition bl0942.h:8
void mark_failed()
Mark this component as failed.
bool status_comm_result(bool ok)
Clears the warning when ok, else sets it with message (default "Communication failed"); returns ok.
const LogString * get_log_name() const
Get the name for a "%s" log argument (LOG_STR_ARG).
virtual size_t dump_summary(char *buffer, size_t len) const
Write a summary of this pin to the provided buffer.
Definition gpio_pin.h:126
float get_local_phase_reactive_power_(uint8_t phase)
Definition atm90e32.cpp:507
float get_phase_forward_active_energy_(uint8_t phase)
Definition atm90e32.cpp:590
OffsetCalibration config_power_offset_phase_[3]
Definition atm90e32.h:262
float get_phase_current_avg_(uint8_t phase)
Definition atm90e32.cpp:549
float get_local_phase_apparent_power_(uint8_t phase)
Definition atm90e32.cpp:509
void write16_(uint16_t a_register, uint16_t val, bool validate=true)
Definition atm90e32.cpp:486
text_sensor::TextSensor * freq_status_text_sensor_
Definition atm90e32.h:285
ESPPreferenceObject power_offset_pref_
Definition atm90e32.h:279
const uint16_t voltage_gain_registers[3]
Definition atm90e32.h:59
void restore_offset_calibrations_(OffsetCalibrationType type)
Definition atm90e32.cpp:992
float get_phase_voltage_avg_(uint8_t phase)
Definition atm90e32.cpp:535
const uint16_t current_gain_registers[3]
Definition atm90e32.h:61
float get_reference_voltage(uint8_t phase)
Definition atm90e32.h:149
struct esphome::atm90e32::ATM90E32Component::GainCalibration gain_phase_[3]
void press_calibration_button(EntityBase *entity, void(ATM90E32Component::*action)(), const LogString *hint=nullptr, const LogString *hint2=nullptr)
Definition atm90e32.cpp:640
const uint16_t current_offset_registers[3]
Definition atm90e32.h:65
static const uint8_t PHASEB
Definition atm90e32.h:55
float get_phase_reverse_active_energy_(uint8_t phase)
Definition atm90e32.cpp:601
float get_local_phase_harmonic_active_power_(uint8_t phase)
Definition atm90e32.cpp:523
float get_phase_angle_(uint8_t phase)
Definition atm90e32.cpp:617
float get_local_phase_current_(uint8_t phase)
Definition atm90e32.cpp:503
bool validate_spi_read_(uint16_t expected, const char *context=nullptr)
const uint16_t reactive_power_offset_registers[3]
Definition atm90e32.h:69
const uint16_t over_voltage_flags[3]
Definition atm90e32.h:71
float get_phase_voltage_(uint8_t phase)
Definition atm90e32.cpp:529
GainCalibration config_gain_phase_[3]
Definition atm90e32.h:269
int16_t calibrate_offset(uint8_t phase, bool voltage)
float get_local_phase_reverse_active_energy_(uint8_t phase)
Definition atm90e32.cpp:517
void finish_offset_calibration_(const OffsetCalibration(&previous)[3], bool previous_restored, bool previous_using_saved, OffsetCalibrationType type)
Definition atm90e32.cpp:765
float get_local_phase_forward_active_energy_(uint8_t phase)
Definition atm90e32.cpp:513
OffsetCalibration config_offset_phase_[3]
Definition atm90e32.h:260
uint16_t calculate_voltage_threshold(int line_freq, uint16_t ugain, float multiplier)
float get_local_phase_power_factor_(uint8_t phase)
Definition atm90e32.cpp:511
float get_phase_reactive_power_(uint8_t phase)
Definition atm90e32.cpp:574
const uint16_t phase_loss_flags[3]
Definition atm90e32.h:75
float get_phase_apparent_power_(uint8_t phase)
Definition atm90e32.cpp:579
float get_local_phase_voltage_(uint8_t phase)
Definition atm90e32.cpp:501
ESPPreferenceObject gain_calibration_pref_
Definition atm90e32.h:280
static const uint8_t PHASEA
Definition atm90e32.h:54
float get_reference_current(uint8_t phase)
Definition atm90e32.h:156
float get_phase_peak_current_(uint8_t phase)
Definition atm90e32.cpp:622
float get_phase_harmonic_active_power_(uint8_t phase)
Definition atm90e32.cpp:612
const uint16_t voltage_sag_flags[3]
Definition atm90e32.h:73
float get_phase_active_power_(uint8_t phase)
Definition atm90e32.cpp:569
void get_cs_summary_(std::span< char, GPIO_SUMMARY_MAX_LEN > buffer)
Definition atm90e32.cpp:131
static const uint8_t PHASEC
Definition atm90e32.h:56
const uint16_t power_offset_registers[3]
Definition atm90e32.h:67
float get_setup_priority() const override
Definition atm90e32.cpp:453
uint16_t read16_(uint16_t a_register)
Definition atm90e32.cpp:458
int read32_(uint16_t addr_h, uint16_t addr_l)
Definition atm90e32.cpp:473
sensor::Sensor * chip_temperature_sensor_
Definition atm90e32.h:287
void write_offsets_to_registers_(uint8_t phase, int16_t first_offset, int16_t second_offset, OffsetCalibrationType type)
Definition atm90e32.cpp:913
float get_phase_power_factor_(uint8_t phase)
Definition atm90e32.cpp:584
OffsetCalibration power_offset_phase_[3]
Definition atm90e32.h:261
float get_local_phase_angle_(uint8_t phase)
Definition atm90e32.cpp:521
const uint16_t voltage_offset_registers[3]
Definition atm90e32.h:63
OffsetCalibration offset_phase_[3]
Definition atm90e32.h:259
text_sensor::TextSensor * phase_status_text_sensor_[3]
Definition atm90e32.h:284
float get_phase_current_(uint8_t phase)
Definition atm90e32.cpp:563
float get_local_phase_peak_current_(uint8_t phase)
Definition atm90e32.cpp:527
void set_publish_interval_flag_(bool flag)
Definition atm90e32.h:210
int16_t calibrate_power_offset(uint8_t phase, bool reactive)
struct esphome::atm90e32::ATM90E32Component::ATM90E32Phase phase_[3]
bool verify_offset_writes_(OffsetCalibrationType type)
ESPPreferenceObject offset_pref_
Definition atm90e32.h:278
float get_local_phase_active_power_(uint8_t phase)
Definition atm90e32.cpp:505
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:65
float state
This member variable stores the last state that has passed through all filters.
Definition sensor.h:140
void publish_state(const std::string &state)
Definition text_sensor.h:42
uint8_t second
uint8_t type
bool state
Definition fan.h:2
uint32_t buffer
Definition gif.cpp:50
mopeka_std_values val[3]
OffsetRestoreState resolve_offset_restore_state(bool has_stored_values, bool initial_values_verified, bool fallback_values_verified)
Definition atm90e32.h:37
bool offset_register_value_matches(uint16_t actual, int16_t expected)
Definition atm90e32.h:16
ESPHOME_LOG_TAG(TAG, "atm90e32")
void prepare_offset_rollback(const OffsetCalibration(&previous)[3], bool had_stored_values, OffsetCalibration(&rollback)[3])
Definition atm90e32.h:44
constexpr float IO
For components that represent GPIO pins like PCF8573.
Definition component.h:41
constexpr uint32_t fnv1_hash_extend(uint32_t hash, T value)
Extend a FNV-1 hash with an integer (hashes each byte).
Definition helpers.h:928
ESPPreferences * global_preferences
uint32_t fnv1_hash(const char *str)
Calculate a FNV-1 hash of str.
Definition helpers.cpp:169
void delay_microseconds_safe(uint32_t us)
Delay for the given amount of microseconds, possibly yielding to other processes during the wait.
Definition helpers.cpp:889
constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb)
Encode a 16-bit value given the most and least significant byte.
Definition helpers.h:998
constexpr size_t GPIO_SUMMARY_MAX_LEN
Maximum buffer size for dump_summary output.
Definition gpio_pin.h:14
void HOT delay(uint32_t ms)
Definition hal.cpp:85
static void uint32_t
watchdog_hw scratch[0]
ESPPreferenceObject make_preference(size_t, uint32_t, bool)
Definition preferences.h:24
bool sync()
Commit pending writes to flash.
Definition preferences.h:33