ESPHome 2025.5.0
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 "esphome/core/log.h"
5
6namespace esphome {
7namespace atm90e32 {
8
9static const char *const TAG = "atm90e32";
11 if (this->get_publish_interval_flag_()) {
12 this->set_publish_interval_flag_(false);
13 for (uint8_t phase = 0; phase < 3; phase++) {
14 if (this->phase_[phase].voltage_sensor_ != nullptr)
15 this->phase_[phase].voltage_ = this->get_phase_voltage_(phase);
16
17 if (this->phase_[phase].current_sensor_ != nullptr)
18 this->phase_[phase].current_ = this->get_phase_current_(phase);
19
20 if (this->phase_[phase].power_sensor_ != nullptr)
21 this->phase_[phase].active_power_ = this->get_phase_active_power_(phase);
22
23 if (this->phase_[phase].power_factor_sensor_ != nullptr)
24 this->phase_[phase].power_factor_ = this->get_phase_power_factor_(phase);
25
26 if (this->phase_[phase].reactive_power_sensor_ != nullptr)
27 this->phase_[phase].reactive_power_ = this->get_phase_reactive_power_(phase);
28
29 if (this->phase_[phase].apparent_power_sensor_ != nullptr)
30 this->phase_[phase].apparent_power_ = this->get_phase_apparent_power_(phase);
31
32 if (this->phase_[phase].forward_active_energy_sensor_ != nullptr)
34
35 if (this->phase_[phase].reverse_active_energy_sensor_ != nullptr)
37
38 if (this->phase_[phase].phase_angle_sensor_ != nullptr)
39 this->phase_[phase].phase_angle_ = this->get_phase_angle_(phase);
40
41 if (this->phase_[phase].harmonic_active_power_sensor_ != nullptr)
43
44 if (this->phase_[phase].peak_current_sensor_ != nullptr)
45 this->phase_[phase].peak_current_ = this->get_phase_peak_current_(phase);
46
47 // After the local store is collected we can publish them trusting they are within +-1 hardware sampling
48 if (this->phase_[phase].voltage_sensor_ != nullptr)
50
51 if (this->phase_[phase].current_sensor_ != nullptr)
53
54 if (this->phase_[phase].power_sensor_ != nullptr)
56
57 if (this->phase_[phase].power_factor_sensor_ != nullptr)
59
60 if (this->phase_[phase].reactive_power_sensor_ != nullptr)
62
63 if (this->phase_[phase].apparent_power_sensor_ != nullptr)
65
66 if (this->phase_[phase].forward_active_energy_sensor_ != nullptr) {
69 }
70
71 if (this->phase_[phase].reverse_active_energy_sensor_ != nullptr) {
74 }
75
76 if (this->phase_[phase].phase_angle_sensor_ != nullptr)
78
79 if (this->phase_[phase].harmonic_active_power_sensor_ != nullptr) {
82 }
83
84 if (this->phase_[phase].peak_current_sensor_ != nullptr)
86 }
87 if (this->freq_sensor_ != nullptr)
89
90 if (this->chip_temperature_sensor_ != nullptr)
92 }
93}
94
96 if (this->read16_(ATM90E32_REGISTER_METEREN) != 1) {
97 this->status_set_warning();
98 return;
99 }
100 this->set_publish_interval_flag_(true);
101 this->status_clear_warning();
102
103#ifdef USE_TEXT_SENSOR
104 this->check_phase_status();
105 this->check_over_current();
106 this->check_freq_status();
107#endif
108}
109
111 ESP_LOGCONFIG(TAG, "Setting up ATM90E32 Component...");
112 this->spi_setup();
113
114 uint16_t mmode0 = 0x87; // 3P4W 50Hz
115 uint16_t high_thresh = 0;
116 uint16_t low_thresh = 0;
117
118 if (line_freq_ == 60) {
119 mmode0 |= 1 << 12; // sets 12th bit to 1, 60Hz
120 // for freq threshold registers
121 high_thresh = 6300; // 63.00 Hz
122 low_thresh = 5700; // 57.00 Hz
123 } else {
124 high_thresh = 5300; // 53.00 Hz
125 low_thresh = 4700; // 47.00 Hz
126 }
127
128 if (current_phases_ == 2) {
129 mmode0 |= 1 << 8; // sets 8th bit to 1, 3P3W
130 mmode0 |= 0 << 1; // sets 1st bit to 0, phase b is not counted into the all-phase sum energy/power (P/Q/S)
131 }
132
133 this->write16_(ATM90E32_REGISTER_SOFTRESET, 0x789A); // Perform soft reset
134 delay(6); // Wait for the minimum 5ms + 1ms
135 this->write16_(ATM90E32_REGISTER_CFGREGACCEN, 0x55AA); // enable register config access
136 if (!this->validate_spi_read_(0x55AA, "setup()")) {
137 ESP_LOGW(TAG, "Could not initialize ATM90E32 IC, check SPI settings");
138 this->mark_failed();
139 return;
140 }
141
142 this->write16_(ATM90E32_REGISTER_METEREN, 0x0001); // Enable Metering
143 this->write16_(ATM90E32_REGISTER_SAGPEAKDETCFG, 0xFF3F); // Peak Detector time (15:8) 255ms, Sag Period (7:0) 63ms
144 this->write16_(ATM90E32_REGISTER_PLCONSTH, 0x0861); // PL Constant MSB (default) = 140625000
145 this->write16_(ATM90E32_REGISTER_PLCONSTL, 0xC468); // PL Constant LSB (default)
146 this->write16_(ATM90E32_REGISTER_ZXCONFIG, 0xD654); // Zero crossing (ZX2, ZX1, ZX0) pin config
147 this->write16_(ATM90E32_REGISTER_MMODE0, mmode0); // Mode Config (frequency set in main program)
148 this->write16_(ATM90E32_REGISTER_MMODE1, pga_gain_); // PGA Gain Configuration for Current Channels
149 this->write16_(ATM90E32_REGISTER_FREQHITH, high_thresh); // Frequency high threshold
150 this->write16_(ATM90E32_REGISTER_FREQLOTH, low_thresh); // Frequency low threshold
151 this->write16_(ATM90E32_REGISTER_PSTARTTH, 0x1D4C); // All Active Startup Power Threshold - 0.02A/0.00032 = 7500
152 this->write16_(ATM90E32_REGISTER_QSTARTTH, 0x1D4C); // All Reactive Startup Power Threshold - 50%
153 this->write16_(ATM90E32_REGISTER_SSTARTTH, 0x1D4C); // All Reactive Startup Power Threshold - 50%
154 this->write16_(ATM90E32_REGISTER_PPHASETH, 0x02EE); // Each Phase Active Phase Threshold - 0.002A/0.00032 = 750
155 this->write16_(ATM90E32_REGISTER_QPHASETH, 0x02EE); // Each phase Reactive Phase Threshold - 10%
156
157 if (this->enable_offset_calibration_) {
158 // Initialize flash storage for offset calibrations
159 uint32_t o_hash = fnv1_hash(std::string("_offset_calibration_") + this->cs_->dump_summary());
162
163 // Initialize flash storage for power offset calibrations
164 uint32_t po_hash = fnv1_hash(std::string("_power_offset_calibration_") + this->cs_->dump_summary());
167 } else {
168 ESP_LOGI(TAG, "[CALIBRATION] Power & Voltage/Current offset calibration is disabled. Using config file values.");
169 for (uint8_t phase = 0; phase < 3; ++phase) {
170 this->write16_(this->voltage_offset_registers[phase],
171 static_cast<uint16_t>(this->offset_phase_[phase].voltage_offset_));
172 this->write16_(this->current_offset_registers[phase],
173 static_cast<uint16_t>(this->offset_phase_[phase].current_offset_));
174 this->write16_(this->power_offset_registers[phase],
175 static_cast<uint16_t>(this->power_offset_phase_[phase].active_power_offset));
176 this->write16_(this->reactive_power_offset_registers[phase],
177 static_cast<uint16_t>(this->power_offset_phase_[phase].reactive_power_offset));
178 }
179 }
180
181 if (this->enable_gain_calibration_) {
182 // Initialize flash storage for gain calibration
183 uint32_t g_hash = fnv1_hash(std::string("_gain_calibration_") + this->cs_->dump_summary());
186
187 if (this->using_saved_calibrations_) {
188 ESP_LOGI(TAG, "[CALIBRATION] Successfully restored gain calibration from memory.");
189 } else {
190 for (uint8_t phase = 0; phase < 3; ++phase) {
191 this->write16_(voltage_gain_registers[phase], this->phase_[phase].voltage_gain_);
192 this->write16_(current_gain_registers[phase], this->phase_[phase].ct_gain_);
193 }
194 }
195 } else {
196 ESP_LOGI(TAG, "[CALIBRATION] Gain calibration is disabled. Using config file values.");
197
198 for (uint8_t phase = 0; phase < 3; ++phase) {
199 this->write16_(voltage_gain_registers[phase], this->phase_[phase].voltage_gain_);
200 this->write16_(current_gain_registers[phase], this->phase_[phase].ct_gain_);
201 }
202 }
203
204 // Sag threshold (78%)
205 uint16_t sagth = calculate_voltage_threshold(line_freq_, this->phase_[0].voltage_gain_, 0.78f);
206 // Overvoltage threshold (122%)
207 uint16_t ovth = calculate_voltage_threshold(line_freq_, this->phase_[0].voltage_gain_, 1.22f);
208
209 // Write to registers
210 this->write16_(ATM90E32_REGISTER_SAGTH, sagth);
211 this->write16_(ATM90E32_REGISTER_OVTH, ovth);
212
213 this->write16_(ATM90E32_REGISTER_CFGREGACCEN, 0x0000); // end configuration
214}
215
217 ESP_LOGCONFIG("", "ATM90E32:");
218 LOG_PIN(" CS Pin: ", this->cs_);
219 if (this->is_failed()) {
220 ESP_LOGE(TAG, "Communication with ATM90E32 failed!");
221 }
222 LOG_UPDATE_INTERVAL(this);
223 LOG_SENSOR(" ", "Voltage A", this->phase_[PHASEA].voltage_sensor_);
224 LOG_SENSOR(" ", "Current A", this->phase_[PHASEA].current_sensor_);
225 LOG_SENSOR(" ", "Power A", this->phase_[PHASEA].power_sensor_);
226 LOG_SENSOR(" ", "Reactive Power A", this->phase_[PHASEA].reactive_power_sensor_);
227 LOG_SENSOR(" ", "Apparent Power A", this->phase_[PHASEA].apparent_power_sensor_);
228 LOG_SENSOR(" ", "PF A", this->phase_[PHASEA].power_factor_sensor_);
229 LOG_SENSOR(" ", "Active Forward Energy A", this->phase_[PHASEA].forward_active_energy_sensor_);
230 LOG_SENSOR(" ", "Active Reverse Energy A", this->phase_[PHASEA].reverse_active_energy_sensor_);
231 LOG_SENSOR(" ", "Harmonic Power A", this->phase_[PHASEA].harmonic_active_power_sensor_);
232 LOG_SENSOR(" ", "Phase Angle A", this->phase_[PHASEA].phase_angle_sensor_);
233 LOG_SENSOR(" ", "Peak Current A", this->phase_[PHASEA].peak_current_sensor_);
234 LOG_SENSOR(" ", "Voltage B", this->phase_[PHASEB].voltage_sensor_);
235 LOG_SENSOR(" ", "Current B", this->phase_[PHASEB].current_sensor_);
236 LOG_SENSOR(" ", "Power B", this->phase_[PHASEB].power_sensor_);
237 LOG_SENSOR(" ", "Reactive Power B", this->phase_[PHASEB].reactive_power_sensor_);
238 LOG_SENSOR(" ", "Apparent Power B", this->phase_[PHASEB].apparent_power_sensor_);
239 LOG_SENSOR(" ", "PF B", this->phase_[PHASEB].power_factor_sensor_);
240 LOG_SENSOR(" ", "Active Forward Energy B", this->phase_[PHASEB].forward_active_energy_sensor_);
241 LOG_SENSOR(" ", "Active Reverse Energy B", this->phase_[PHASEB].reverse_active_energy_sensor_);
242 LOG_SENSOR(" ", "Harmonic Power B", this->phase_[PHASEB].harmonic_active_power_sensor_);
243 LOG_SENSOR(" ", "Phase Angle B", this->phase_[PHASEB].phase_angle_sensor_);
244 LOG_SENSOR(" ", "Peak Current B", this->phase_[PHASEB].peak_current_sensor_);
245 LOG_SENSOR(" ", "Voltage C", this->phase_[PHASEC].voltage_sensor_);
246 LOG_SENSOR(" ", "Current C", this->phase_[PHASEC].current_sensor_);
247 LOG_SENSOR(" ", "Power C", this->phase_[PHASEC].power_sensor_);
248 LOG_SENSOR(" ", "Reactive Power C", this->phase_[PHASEC].reactive_power_sensor_);
249 LOG_SENSOR(" ", "Apparent Power C", this->phase_[PHASEC].apparent_power_sensor_);
250 LOG_SENSOR(" ", "PF C", this->phase_[PHASEC].power_factor_sensor_);
251 LOG_SENSOR(" ", "Active Forward Energy C", this->phase_[PHASEC].forward_active_energy_sensor_);
252 LOG_SENSOR(" ", "Active Reverse Energy C", this->phase_[PHASEC].reverse_active_energy_sensor_);
253 LOG_SENSOR(" ", "Harmonic Power C", this->phase_[PHASEC].harmonic_active_power_sensor_);
254 LOG_SENSOR(" ", "Phase Angle C", this->phase_[PHASEC].phase_angle_sensor_);
255 LOG_SENSOR(" ", "Peak Current C", this->phase_[PHASEC].peak_current_sensor_);
256 LOG_SENSOR(" ", "Frequency", this->freq_sensor_);
257 LOG_SENSOR(" ", "Chip Temp", this->chip_temperature_sensor_);
258}
259
261
262// R/C registers can conly be cleared after the LastSPIData register is updated (register 78H)
263// Peakdetect period: 05H. Bit 15:8 are PeakDet_period in ms. 7:0 are Sag_period
264// Default is 143FH (20ms, 63ms)
265uint16_t ATM90E32Component::read16_(uint16_t a_register) {
266 uint8_t addrh = (1 << 7) | ((a_register >> 8) & 0x03);
267 uint8_t addrl = (a_register & 0xFF);
268 uint8_t data[2];
269 uint16_t output;
270 this->enable();
271 delay_microseconds_safe(1); // min delay between CS low and first SCK is 200ns - 1ms is plenty
272 this->write_byte(addrh);
273 this->write_byte(addrl);
274 this->read_array(data, 2);
275 this->disable();
276
277 output = (uint16_t(data[0] & 0xFF) << 8) | (data[1] & 0xFF);
278 ESP_LOGVV(TAG, "read16_ 0x%04" PRIX16 " output 0x%04" PRIX16, a_register, output);
279 return output;
280}
281
282int ATM90E32Component::read32_(uint16_t addr_h, uint16_t addr_l) {
283 const uint16_t val_h = this->read16_(addr_h);
284 const uint16_t val_l = this->read16_(addr_l);
285 const int32_t val = (val_h << 16) | val_l;
286
287 ESP_LOGVV(TAG,
288 "read32_ addr_h 0x%04" PRIX16 " val_h 0x%04" PRIX16 " addr_l 0x%04" PRIX16 " val_l 0x%04" PRIX16
289 " = %" PRId32,
290 addr_h, val_h, addr_l, val_l, val);
291
292 return val;
293}
294
295void ATM90E32Component::write16_(uint16_t a_register, uint16_t val) {
296 ESP_LOGVV(TAG, "write16_ 0x%04" PRIX16 " val 0x%04" PRIX16, a_register, val);
297 this->enable();
298 this->write_byte16(a_register);
299 this->write_byte16(val);
300 this->disable();
301 this->validate_spi_read_(val, "write16()");
302}
303
304float ATM90E32Component::get_local_phase_voltage_(uint8_t phase) { return this->phase_[phase].voltage_; }
305
306float ATM90E32Component::get_local_phase_current_(uint8_t phase) { return this->phase_[phase].current_; }
307
308float ATM90E32Component::get_local_phase_active_power_(uint8_t phase) { return this->phase_[phase].active_power_; }
309
311
313
314float ATM90E32Component::get_local_phase_power_factor_(uint8_t phase) { return this->phase_[phase].power_factor_; }
315
319
323
324float ATM90E32Component::get_local_phase_angle_(uint8_t phase) { return this->phase_[phase].phase_angle_; }
325
329
330float ATM90E32Component::get_local_phase_peak_current_(uint8_t phase) { return this->phase_[phase].peak_current_; }
331
333 const uint16_t voltage = this->read16_(ATM90E32_REGISTER_URMS + phase);
334 this->validate_spi_read_(voltage, "get_phase_voltage()");
335 return (float) voltage / 100;
336}
337
339 const uint8_t reads = 10;
340 uint32_t accumulation = 0;
341 uint16_t voltage = 0;
342 for (uint8_t i = 0; i < reads; i++) {
343 voltage = this->read16_(ATM90E32_REGISTER_URMS + phase);
344 this->validate_spi_read_(voltage, "get_phase_voltage_avg_()");
345 accumulation += voltage;
346 }
347 voltage = accumulation / reads;
348 this->phase_[phase].voltage_ = (float) voltage / 100;
349 return this->phase_[phase].voltage_;
350}
351
353 const uint8_t reads = 10;
354 uint32_t accumulation = 0;
355 uint16_t current = 0;
356 for (uint8_t i = 0; i < reads; i++) {
357 current = this->read16_(ATM90E32_REGISTER_IRMS + phase);
358 this->validate_spi_read_(current, "get_phase_current_avg_()");
359 accumulation += current;
360 }
361 current = accumulation / reads;
362 this->phase_[phase].current_ = (float) current / 1000;
363 return this->phase_[phase].current_;
364}
365
367 const uint16_t current = this->read16_(ATM90E32_REGISTER_IRMS + phase);
368 this->validate_spi_read_(current, "get_phase_current_()");
369 return (float) current / 1000;
370}
371
373 const int val = this->read32_(ATM90E32_REGISTER_PMEAN + phase, ATM90E32_REGISTER_PMEANLSB + phase);
374 return val * 0.00032f;
375}
376
378 const int val = this->read32_(ATM90E32_REGISTER_QMEAN + phase, ATM90E32_REGISTER_QMEANLSB + phase);
379 return val * 0.00032f;
380}
381
383 const int val = this->read32_(ATM90E32_REGISTER_SMEAN + phase, ATM90E32_REGISTER_SMEANLSB + phase);
384 return val * 0.00032f;
385}
386
388 uint16_t powerfactor = this->read16_(ATM90E32_REGISTER_PFMEAN + phase); // unsigned to compare to lastspidata
389 this->validate_spi_read_(powerfactor, "get_phase_power_factor_()");
390 return (float) ((int16_t) powerfactor) / 1000; // make it signed again
391}
392
394 const uint16_t val = this->read16_(ATM90E32_REGISTER_APENERGY + phase);
395 if ((UINT32_MAX - this->phase_[phase].cumulative_forward_active_energy_) > val) {
397 } else {
399 }
400 // 0.01CF resolution = 0.003125 Wh per count
401 return ((float) this->phase_[phase].cumulative_forward_active_energy_ * (10.0f / 3200.0f));
402}
403
405 const uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGY + phase);
406 if (UINT32_MAX - this->phase_[phase].cumulative_reverse_active_energy_ > val) {
408 } else {
410 }
411 // 0.01CF resolution = 0.003125 Wh per count
412 return ((float) this->phase_[phase].cumulative_reverse_active_energy_ * (10.0f / 3200.0f));
413}
414
416 int val = this->read32_(ATM90E32_REGISTER_PMEANH + phase, ATM90E32_REGISTER_PMEANHLSB + phase);
417 return val * 0.00032f;
418}
419
421 uint16_t val = this->read16_(ATM90E32_REGISTER_PANGLE + phase) / 10.0;
422 return (val > 180) ? (float) (val - 360.0f) : (float) val;
423}
424
426 int16_t val = (float) this->read16_(ATM90E32_REGISTER_IPEAK + phase);
427 if (!this->peak_current_signed_)
428 val = std::abs(val);
429 // phase register * phase current gain value / 1000 * 2^13
430 return (val * this->phase_[phase].ct_gain_ / 8192000.0);
431}
432
434 const uint16_t freq = this->read16_(ATM90E32_REGISTER_FREQ);
435 return (float) freq / 100;
436}
437
439 const uint16_t ctemp = this->read16_(ATM90E32_REGISTER_TEMP);
440 return (float) ctemp;
441}
442
444 if (!this->enable_gain_calibration_) {
445 ESP_LOGW(TAG, "[CALIBRATION] Gain calibration is disabled! Enable it first with enable_gain_calibration: true");
446 return;
447 }
448
449 float ref_voltages[3] = {
450 this->get_reference_voltage(0),
451 this->get_reference_voltage(1),
452 this->get_reference_voltage(2),
453 };
454 float ref_currents[3] = {this->get_reference_current(0), this->get_reference_current(1),
455 this->get_reference_current(2)};
456
457 ESP_LOGI(TAG, "[CALIBRATION] ");
458 ESP_LOGI(TAG, "[CALIBRATION] ========================= Gain Calibration =========================");
459 ESP_LOGI(TAG, "[CALIBRATION] ---------------------------------------------------------------------");
460 ESP_LOGI(TAG,
461 "[CALIBRATION] | Phase | V_meas (V) | I_meas (A) | V_ref | I_ref | V_gain (old→new) | I_gain (old→new) |");
462 ESP_LOGI(TAG, "[CALIBRATION] ---------------------------------------------------------------------");
463
464 for (uint8_t phase = 0; phase < 3; phase++) {
465 float measured_voltage = this->get_phase_voltage_avg_(phase);
466 float measured_current = this->get_phase_current_avg_(phase);
467
468 float ref_voltage = ref_voltages[phase];
469 float ref_current = ref_currents[phase];
470
471 uint16_t current_voltage_gain = this->read16_(voltage_gain_registers[phase]);
472 uint16_t current_current_gain = this->read16_(current_gain_registers[phase]);
473
474 bool did_voltage = false;
475 bool did_current = false;
476
477 // Voltage calibration
478 if (ref_voltage <= 0.0f) {
479 ESP_LOGW(TAG, "[CALIBRATION] Phase %s - Skipping voltage calibration: reference voltage is 0.",
480 phase_labels[phase]);
481 } else if (measured_voltage == 0.0f) {
482 ESP_LOGW(TAG, "[CALIBRATION] Phase %s - Skipping voltage calibration: measured voltage is 0.",
483 phase_labels[phase]);
484 } else {
485 uint32_t new_voltage_gain = static_cast<uint16_t>((ref_voltage / measured_voltage) * current_voltage_gain);
486 if (new_voltage_gain == 0) {
487 ESP_LOGW(TAG, "[CALIBRATION] Phase %s - Voltage gain would be 0. Check reference and measured voltage.",
488 phase_labels[phase]);
489 } else {
490 if (new_voltage_gain >= 65535) {
491 ESP_LOGW(
492 TAG,
493 "[CALIBRATION] Phase %s - Voltage gain exceeds 65535. You may need a higher output voltage transformer.",
494 phase_labels[phase]);
495 new_voltage_gain = 65535;
496 }
497 this->gain_phase_[phase].voltage_gain = static_cast<uint16_t>(new_voltage_gain);
498 did_voltage = true;
499 }
500 }
501
502 // Current calibration
503 if (ref_current == 0.0f) {
504 ESP_LOGW(TAG, "[CALIBRATION] Phase %s - Skipping current calibration: reference current is 0.",
505 phase_labels[phase]);
506 } else if (measured_current == 0.0f) {
507 ESP_LOGW(TAG, "[CALIBRATION] Phase %s - Skipping current calibration: measured current is 0.",
508 phase_labels[phase]);
509 } else {
510 uint32_t new_current_gain = static_cast<uint16_t>((ref_current / measured_current) * current_current_gain);
511 if (new_current_gain == 0) {
512 ESP_LOGW(TAG, "[CALIBRATION] Phase %s - Current gain would be 0. Check reference and measured current.",
513 phase_labels[phase]);
514 } else {
515 if (new_current_gain >= 65535) {
516 ESP_LOGW(TAG, "[CALIBRATION] Phase %s - Current gain exceeds 65535. You may need to turn up pga gain.",
517 phase_labels[phase]);
518 new_current_gain = 65535;
519 }
520 this->gain_phase_[phase].current_gain = static_cast<uint16_t>(new_current_gain);
521 did_current = true;
522 }
523 }
524
525 // Final row output
526 ESP_LOGI(TAG, "[CALIBRATION] | %c | %9.2f | %9.4f | %5.2f | %6.4f | %5u → %-5u | %5u → %-5u |",
527 'A' + phase, measured_voltage, measured_current, ref_voltage, ref_current, current_voltage_gain,
528 did_voltage ? this->gain_phase_[phase].voltage_gain : current_voltage_gain, current_current_gain,
529 did_current ? this->gain_phase_[phase].current_gain : current_current_gain);
530 }
531
532 ESP_LOGI(TAG, "[CALIBRATION] =====================================================================\n");
533
536 this->verify_gain_writes_();
537}
538
540 bool success = this->gain_calibration_pref_.save(&this->gain_phase_);
541 if (success) {
542 this->using_saved_calibrations_ = true;
543 ESP_LOGI(TAG, "[CALIBRATION] Gain calibration saved to memory.");
544 } else {
545 this->using_saved_calibrations_ = false;
546 ESP_LOGE(TAG, "[CALIBRATION] Failed to save gain calibration to memory!");
547 }
548}
549
551 if (!this->enable_offset_calibration_) {
552 ESP_LOGW(TAG, "[CALIBRATION] Offset calibration is disabled! Enable it first with enable_offset_calibration: true");
553 return;
554 }
555
556 for (uint8_t phase = 0; phase < 3; phase++) {
557 int16_t voltage_offset = calibrate_offset(phase, true);
558 int16_t current_offset = calibrate_offset(phase, false);
559
560 this->write_offsets_to_registers_(phase, voltage_offset, current_offset);
561
562 ESP_LOGI(TAG, "[CALIBRATION] Phase %c - offset_voltage: %d, offset_current: %d", 'A' + phase, voltage_offset,
563 current_offset);
564 }
565
566 this->offset_pref_.save(&this->offset_phase_); // Save to flash
567}
568
570 if (!this->enable_offset_calibration_) {
571 ESP_LOGW(
572 TAG,
573 "[CALIBRATION] Offset power calibration is disabled! Enable it first with enable_offset_calibration: true");
574 return;
575 }
576
577 for (uint8_t phase = 0; phase < 3; ++phase) {
578 int16_t active_offset = calibrate_power_offset(phase, false);
579 int16_t reactive_offset = calibrate_power_offset(phase, true);
580
581 this->write_power_offsets_to_registers_(phase, active_offset, reactive_offset);
582
583 ESP_LOGI(TAG, "[CALIBRATION] Phase %c - offset_active_power: %d, offset_reactive_power: %d", 'A' + phase,
584 active_offset, reactive_offset);
585 }
586
587 this->power_offset_pref_.save(&this->power_offset_phase_); // Save to flash
588}
589
591 this->write16_(ATM90E32_REGISTER_CFGREGACCEN, 0x55AA);
592
593 for (int phase = 0; phase < 3; phase++) {
594 this->write16_(voltage_gain_registers[phase], this->gain_phase_[phase].voltage_gain);
595 this->write16_(current_gain_registers[phase], this->gain_phase_[phase].current_gain);
596 }
597
598 this->write16_(ATM90E32_REGISTER_CFGREGACCEN, 0x0000);
599}
600
601void ATM90E32Component::write_offsets_to_registers_(uint8_t phase, int16_t voltage_offset, int16_t current_offset) {
602 // Save to runtime
603 this->offset_phase_[phase].voltage_offset_ = voltage_offset;
604 this->phase_[phase].voltage_offset_ = voltage_offset;
605
606 // Save to flash-storable struct
607 this->offset_phase_[phase].current_offset_ = current_offset;
608 this->phase_[phase].current_offset_ = current_offset;
609
610 // Write to registers
611 this->write16_(ATM90E32_REGISTER_CFGREGACCEN, 0x55AA);
612 this->write16_(voltage_offset_registers[phase], static_cast<uint16_t>(voltage_offset));
613 this->write16_(current_offset_registers[phase], static_cast<uint16_t>(current_offset));
614 this->write16_(ATM90E32_REGISTER_CFGREGACCEN, 0x0000);
615}
616
617void ATM90E32Component::write_power_offsets_to_registers_(uint8_t phase, int16_t p_offset, int16_t q_offset) {
618 // Save to runtime
619 this->phase_[phase].active_power_offset_ = p_offset;
620 this->phase_[phase].reactive_power_offset_ = q_offset;
621
622 // Save to flash-storable struct
623 this->power_offset_phase_[phase].active_power_offset = p_offset;
624 this->power_offset_phase_[phase].reactive_power_offset = q_offset;
625
626 // Write to registers
627 this->write16_(ATM90E32_REGISTER_CFGREGACCEN, 0x55AA);
628 this->write16_(this->power_offset_registers[phase], static_cast<uint16_t>(p_offset));
629 this->write16_(this->reactive_power_offset_registers[phase], static_cast<uint16_t>(q_offset));
630 this->write16_(ATM90E32_REGISTER_CFGREGACCEN, 0x0000);
631}
632
634 if (this->gain_calibration_pref_.load(&this->gain_phase_)) {
635 ESP_LOGI(TAG, "[CALIBRATION] Restoring saved gain calibrations to registers:");
636
637 for (uint8_t phase = 0; phase < 3; phase++) {
638 uint16_t v_gain = this->gain_phase_[phase].voltage_gain;
639 uint16_t i_gain = this->gain_phase_[phase].current_gain;
640 ESP_LOGI(TAG, "[CALIBRATION] Phase %c - Voltage Gain: %u, Current Gain: %u", 'A' + phase, v_gain, i_gain);
641 }
642
644
645 if (this->verify_gain_writes_()) {
646 this->using_saved_calibrations_ = true;
647 ESP_LOGI(TAG, "[CALIBRATION] Gain calibration loaded and verified successfully.");
648 } else {
649 this->using_saved_calibrations_ = false;
650 ESP_LOGE(TAG, "[CALIBRATION] Gain verification failed! Calibration may not be applied correctly.");
651 }
652 } else {
653 this->using_saved_calibrations_ = false;
654 ESP_LOGW(TAG, "[CALIBRATION] No stored gain calibrations found. Using config file values.");
655 }
656}
657
659 if (this->offset_pref_.load(&this->offset_phase_)) {
660 ESP_LOGI(TAG, "[CALIBRATION] Successfully restored offset calibration from memory.");
661
662 for (uint8_t phase = 0; phase < 3; phase++) {
663 auto &offset = this->offset_phase_[phase];
664 write_offsets_to_registers_(phase, offset.voltage_offset_, offset.current_offset_);
665 ESP_LOGI(TAG, "[CALIBRATION] Phase %c - offset_voltage:: %d, offset_current: %d", 'A' + phase,
666 offset.voltage_offset_, offset.current_offset_);
667 }
668 } else {
669 ESP_LOGW(TAG, "[CALIBRATION] No stored offset calibrations found. Using default values.");
670 }
671}
672
674 if (this->power_offset_pref_.load(&this->power_offset_phase_)) {
675 ESP_LOGI(TAG, "[CALIBRATION] Successfully restored power offset calibration from memory.");
676
677 for (uint8_t phase = 0; phase < 3; ++phase) {
678 auto &offset = this->power_offset_phase_[phase];
679 write_power_offsets_to_registers_(phase, offset.active_power_offset, offset.reactive_power_offset);
680 ESP_LOGI(TAG, "[CALIBRATION] Phase %c - offset_active_power: %d, offset_reactive_power: %d", 'A' + phase,
681 offset.active_power_offset, offset.reactive_power_offset);
682 }
683 } else {
684 ESP_LOGW(TAG, "[CALIBRATION] No stored power offsets found. Using default values.");
685 }
686}
687
689 ESP_LOGI(TAG, "[CALIBRATION] Clearing stored gain calibrations and restoring config-defined values...");
690
691 for (int phase = 0; phase < 3; phase++) {
692 gain_phase_[phase].voltage_gain = this->phase_[phase].voltage_gain_;
693 gain_phase_[phase].current_gain = this->phase_[phase].ct_gain_;
694 }
695
696 bool success = this->gain_calibration_pref_.save(&this->gain_phase_);
697 this->using_saved_calibrations_ = false;
698
699 if (success) {
700 ESP_LOGI(TAG, "[CALIBRATION] Gain calibrations cleared. Config values restored:");
701 for (int phase = 0; phase < 3; phase++) {
702 ESP_LOGI(TAG, "[CALIBRATION] Phase %c - Voltage Gain: %u, Current Gain: %u", 'A' + phase,
703 gain_phase_[phase].voltage_gain, gain_phase_[phase].current_gain);
704 }
705 } else {
706 ESP_LOGE(TAG, "[CALIBRATION] Failed to clear gain calibrations!");
707 }
708
709 this->write_gains_to_registers_(); // Apply them to the chip immediately
710}
711
713 for (uint8_t phase = 0; phase < 3; phase++) {
714 this->write_offsets_to_registers_(phase, 0, 0);
715 }
716
717 this->offset_pref_.save(&this->offset_phase_); // Save cleared values to flash memory
718
719 ESP_LOGI(TAG, "[CALIBRATION] Offsets cleared.");
720}
721
723 for (uint8_t phase = 0; phase < 3; phase++) {
724 this->write_power_offsets_to_registers_(phase, 0, 0);
725 }
726
728
729 ESP_LOGI(TAG, "[CALIBRATION] Power offsets cleared.");
730}
731
732int16_t ATM90E32Component::calibrate_offset(uint8_t phase, bool voltage) {
733 const uint8_t num_reads = 5;
734 uint64_t total_value = 0;
735
736 for (uint8_t i = 0; i < num_reads; ++i) {
737 uint32_t reading = voltage ? this->read32_(ATM90E32_REGISTER_URMS + phase, ATM90E32_REGISTER_URMSLSB + phase)
738 : this->read32_(ATM90E32_REGISTER_IRMS + phase, ATM90E32_REGISTER_IRMSLSB + phase);
739 total_value += reading;
740 }
741
742 const uint32_t average_value = total_value / num_reads;
743 const uint32_t shifted = average_value >> 7;
744 const uint32_t offset = ~shifted + 1;
745 return static_cast<int16_t>(offset); // Takes lower 16 bits
746}
747
748int16_t ATM90E32Component::calibrate_power_offset(uint8_t phase, bool reactive) {
749 const uint8_t num_reads = 5;
750 uint64_t total_value = 0;
751
752 for (uint8_t i = 0; i < num_reads; ++i) {
753 uint32_t reading = reactive ? this->read32_(ATM90E32_REGISTER_QMEAN + phase, ATM90E32_REGISTER_QMEANLSB + phase)
754 : this->read32_(ATM90E32_REGISTER_PMEAN + phase, ATM90E32_REGISTER_PMEANLSB + phase);
755 total_value += reading;
756 }
757
758 const uint32_t average_value = total_value / num_reads;
759 const uint32_t power_offset = ~average_value + 1;
760 return static_cast<int16_t>(power_offset); // Takes the lower 16 bits
761}
762
764 bool success = true;
765 for (uint8_t phase = 0; phase < 3; phase++) {
766 uint16_t read_voltage = this->read16_(voltage_gain_registers[phase]);
767 uint16_t read_current = this->read16_(current_gain_registers[phase]);
768
769 if (read_voltage != this->gain_phase_[phase].voltage_gain ||
770 read_current != this->gain_phase_[phase].current_gain) {
771 ESP_LOGE(TAG, "[CALIBRATION] Mismatch detected for Phase %s!", phase_labels[phase]);
772 success = false;
773 }
774 }
775 return success; // Return true if all writes were successful, false otherwise
776}
777
778#ifdef USE_TEXT_SENSOR
780 uint16_t state0 = this->read16_(ATM90E32_REGISTER_EMMSTATE0);
781 uint16_t state1 = this->read16_(ATM90E32_REGISTER_EMMSTATE1);
782
783 for (int phase = 0; phase < 3; phase++) {
784 std::string status;
785
786 if (state0 & over_voltage_flags[phase])
787 status += "Over Voltage; ";
788 if (state1 & voltage_sag_flags[phase])
789 status += "Voltage Sag; ";
790 if (state1 & phase_loss_flags[phase])
791 status += "Phase Loss; ";
792
793 auto *sensor = this->phase_status_text_sensor_[phase];
794 const char *phase_name = sensor ? sensor->get_name().c_str() : "Unknown Phase";
795 if (!status.empty()) {
796 status.pop_back(); // remove space
797 status.pop_back(); // remove semicolon
798 ESP_LOGW(TAG, "%s: %s", phase_name, status.c_str());
799 if (sensor != nullptr)
800 sensor->publish_state(status);
801 } else {
802 if (sensor != nullptr)
803 sensor->publish_state("Okay");
804 }
805 }
806}
807
809 uint16_t state1 = this->read16_(ATM90E32_REGISTER_EMMSTATE1);
810
811 std::string freq_status;
812
813 if (state1 & ATM90E32_STATUS_S1_FREQHIST) {
814 freq_status = "HIGH";
815 } else if (state1 & ATM90E32_STATUS_S1_FREQLOST) {
816 freq_status = "LOW";
817 } else {
818 freq_status = "Normal";
819 }
820 ESP_LOGW(TAG, "Frequency status: %s", freq_status.c_str());
821
822 if (this->freq_status_text_sensor_ != nullptr) {
823 this->freq_status_text_sensor_->publish_state(freq_status);
824 }
825}
826
828 constexpr float max_current_threshold = 65.53f;
829
830 for (uint8_t phase = 0; phase < 3; phase++) {
831 float current_val =
832 this->phase_[phase].current_sensor_ != nullptr ? this->phase_[phase].current_sensor_->state : 0.0f;
833
834 if (current_val > max_current_threshold) {
835 ESP_LOGW(TAG, "Over current detected on Phase %c: %.2f A", 'A' + phase, current_val);
836 ESP_LOGW(TAG, "You may need to half your gain_ct: value & multiply the current and power values by 2");
837 if (this->phase_status_text_sensor_[phase] != nullptr) {
838 this->phase_status_text_sensor_[phase]->publish_state("Over Current; ");
839 }
840 }
841 }
842}
843#endif
844
845uint16_t ATM90E32Component::calculate_voltage_threshold(int line_freq, uint16_t ugain, float multiplier) {
846 // this assumes that 60Hz electrical systems use 120V mains,
847 // which is usually, but not always the case
848 float nominal_voltage = (line_freq == 60) ? 120.0f : 220.0f;
849 float target_voltage = nominal_voltage * multiplier;
850
851 float peak_01v = target_voltage * 100.0f * std::sqrt(2.0f); // convert RMS → peak, scale to 0.01V
852 float divider = (2.0f * ugain) / 32768.0f;
853
854 float threshold = peak_01v / divider;
855
856 return static_cast<uint16_t>(threshold);
857}
858
859bool ATM90E32Component::validate_spi_read_(uint16_t expected, const char *context) {
860 uint16_t last = this->read16_(ATM90E32_REGISTER_LASTSPIDATA);
861 if (last != expected) {
862 if (context != nullptr) {
863 ESP_LOGW(TAG, "[%s] SPI read mismatch: expected 0x%04X, got 0x%04X", context, expected, last);
864 } else {
865 ESP_LOGW(TAG, "SPI read mismatch: expected 0x%04X, got 0x%04X", expected, last);
866 }
867 return false;
868 }
869 return true;
870}
871
872} // namespace atm90e32
873} // namespace esphome
uint8_t status
Definition bl0942.h:8
virtual void mark_failed()
Mark this component as failed.
bool is_failed() const
void status_set_warning(const char *message="unspecified")
void status_clear_warning()
bool save(const T *src)
Definition preferences.h:21
virtual ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash)=0
const StringRef & get_name() const
virtual std::string dump_summary() const =0
constexpr const char * c_str() const
Definition string_ref.h:68
float get_local_phase_reactive_power_(uint8_t phase)
Definition atm90e32.cpp:310
float get_phase_forward_active_energy_(uint8_t phase)
Definition atm90e32.cpp:393
float get_phase_current_avg_(uint8_t phase)
Definition atm90e32.cpp:352
float get_local_phase_apparent_power_(uint8_t phase)
Definition atm90e32.cpp:312
text_sensor::TextSensor * freq_status_text_sensor_
Definition atm90e32.h:224
ESPPreferenceObject power_offset_pref_
Definition atm90e32.h:218
const uint16_t voltage_gain_registers[3]
Definition atm90e32.h:24
float get_phase_voltage_avg_(uint8_t phase)
Definition atm90e32.cpp:338
void write_power_offsets_to_registers_(uint8_t phase, int16_t p_offset, int16_t q_offset)
Definition atm90e32.cpp:617
const uint16_t current_gain_registers[3]
Definition atm90e32.h:26
float get_reference_voltage(uint8_t phase)
Definition atm90e32.h:96
struct esphome::atm90e32::ATM90E32Component::GainCalibration gain_phase_[3]
const uint16_t current_offset_registers[3]
Definition atm90e32.h:30
static const uint8_t PHASEB
Definition atm90e32.h:20
float get_phase_reverse_active_energy_(uint8_t phase)
Definition atm90e32.cpp:404
float get_local_phase_harmonic_active_power_(uint8_t phase)
Definition atm90e32.cpp:326
float get_phase_angle_(uint8_t phase)
Definition atm90e32.cpp:420
float get_local_phase_current_(uint8_t phase)
Definition atm90e32.cpp:306
bool validate_spi_read_(uint16_t expected, const char *context=nullptr)
Definition atm90e32.cpp:859
const uint16_t reactive_power_offset_registers[3]
Definition atm90e32.h:34
const uint16_t over_voltage_flags[3]
Definition atm90e32.h:36
float get_phase_voltage_(uint8_t phase)
Definition atm90e32.cpp:332
int16_t calibrate_offset(uint8_t phase, bool voltage)
Definition atm90e32.cpp:732
float get_local_phase_reverse_active_energy_(uint8_t phase)
Definition atm90e32.cpp:320
float get_local_phase_forward_active_energy_(uint8_t phase)
Definition atm90e32.cpp:316
struct esphome::atm90e32::ATM90E32Component::OffsetCalibration offset_phase_[3]
uint16_t calculate_voltage_threshold(int line_freq, uint16_t ugain, float multiplier)
Definition atm90e32.cpp:845
float get_local_phase_power_factor_(uint8_t phase)
Definition atm90e32.cpp:314
float get_phase_reactive_power_(uint8_t phase)
Definition atm90e32.cpp:377
const uint16_t phase_loss_flags[3]
Definition atm90e32.h:40
float get_phase_apparent_power_(uint8_t phase)
Definition atm90e32.cpp:382
float get_local_phase_voltage_(uint8_t phase)
Definition atm90e32.cpp:304
void write16_(uint16_t a_register, uint16_t val)
Definition atm90e32.cpp:295
ESPPreferenceObject gain_calibration_pref_
Definition atm90e32.h:219
void write_offsets_to_registers_(uint8_t phase, int16_t voltage_offset, int16_t current_offset)
Definition atm90e32.cpp:601
static const uint8_t PHASEA
Definition atm90e32.h:19
struct esphome::atm90e32::ATM90E32Component::PowerOffsetCalibration power_offset_phase_[3]
float get_reference_current(uint8_t phase)
Definition atm90e32.h:103
float get_phase_peak_current_(uint8_t phase)
Definition atm90e32.cpp:425
float get_phase_harmonic_active_power_(uint8_t phase)
Definition atm90e32.cpp:415
const uint16_t voltage_sag_flags[3]
Definition atm90e32.h:38
float get_phase_active_power_(uint8_t phase)
Definition atm90e32.cpp:372
static const uint8_t PHASEC
Definition atm90e32.h:21
const uint16_t power_offset_registers[3]
Definition atm90e32.h:32
float get_setup_priority() const override
Definition atm90e32.cpp:260
uint16_t read16_(uint16_t a_register)
Definition atm90e32.cpp:265
int read32_(uint16_t addr_h, uint16_t addr_l)
Definition atm90e32.cpp:282
sensor::Sensor * chip_temperature_sensor_
Definition atm90e32.h:226
float get_phase_power_factor_(uint8_t phase)
Definition atm90e32.cpp:387
float get_local_phase_angle_(uint8_t phase)
Definition atm90e32.cpp:324
const uint16_t voltage_offset_registers[3]
Definition atm90e32.h:28
text_sensor::TextSensor * phase_status_text_sensor_[3]
Definition atm90e32.h:223
float get_phase_current_(uint8_t phase)
Definition atm90e32.cpp:366
float get_local_phase_peak_current_(uint8_t phase)
Definition atm90e32.cpp:330
void set_publish_interval_flag_(bool flag)
Definition atm90e32.h:158
int16_t calibrate_power_offset(uint8_t phase, bool reactive)
Definition atm90e32.cpp:748
struct esphome::atm90e32::ATM90E32Component::ATM90E32Phase phase_[3]
ESPPreferenceObject offset_pref_
Definition atm90e32.h:217
float get_local_phase_active_power_(uint8_t phase)
Definition atm90e32.cpp:308
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:39
float state
This member variable stores the last state that has passed through all filters.
Definition sensor.h:131
void publish_state(const std::string &state)
mopeka_std_values val[4]
const float IO
For components that represent GPIO pins like PCF8573.
Definition component.cpp:17
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
uint32_t fnv1_hash(const std::string &str)
Calculate a FNV-1 hash of str.
Definition helpers.cpp:186
ESPPreferences * global_preferences
void IRAM_ATTR HOT delay_microseconds_safe(uint32_t us)
Delay for the given amount of microseconds, possibly yielding to other processes during the wait.
Definition helpers.cpp:773
void IRAM_ATTR HOT delay(uint32_t ms)
Definition core.cpp:28