ESPHome 2025.9.0-dev
Loading...
Searching...
No Matches
atm90e26.cpp
Go to the documentation of this file.
1#include "atm90e26.h"
2#include "atm90e26_reg.h"
3#include "esphome/core/log.h"
4
5namespace esphome {
6namespace atm90e26 {
7
8static const char *const TAG = "atm90e26";
9
11 if (this->read16_(ATM90E26_REGISTER_FUNCEN) != 0x0030) {
12 this->status_set_warning();
13 return;
14 }
15
16 if (this->voltage_sensor_ != nullptr) {
18 }
19 if (this->current_sensor_ != nullptr) {
21 }
22 if (this->power_sensor_ != nullptr) {
24 }
25 if (this->reactive_power_sensor_ != nullptr) {
27 }
28 if (this->power_factor_sensor_ != nullptr) {
30 }
31 if (this->forward_active_energy_sensor_ != nullptr) {
33 }
34 if (this->reverse_active_energy_sensor_ != nullptr) {
36 }
37 if (this->freq_sensor_ != nullptr) {
39 }
41}
42
44 this->spi_setup();
45
46 uint16_t mmode = 0x422; // default values for everything but L/N line current gains
47 mmode |= (gain_pga_ & 0x7) << 13;
48 mmode |= (n_line_gain_ & 0x3) << 11;
49
50 this->write16_(ATM90E26_REGISTER_SOFTRESET, 0x789A); // Perform soft reset
51 this->write16_(ATM90E26_REGISTER_FUNCEN,
52 0x0030); // Voltage sag irq=1, report on warnout pin=1, energy dir change irq=0
53 uint16_t read = this->read16_(ATM90E26_REGISTER_LASTDATA);
54 if (read != 0x0030) {
55 ESP_LOGW(TAG, "Could not initialize ATM90E26 IC, check SPI settings: %d", read);
56 this->mark_failed();
57 return;
58 }
59 // TODO: 100 * <nominal voltage, e.g. 230> * sqrt(2) * <fraction of nominal, e.g. 0.9> / (4 * gain_voltage/32768)
60 this->write16_(ATM90E26_REGISTER_SAGTH, 0x17DD); // Voltage sag threshhold 0x1F2F
61
62 // Set metering calibration values
63 this->write16_(ATM90E26_REGISTER_CALSTART, 0x5678); // CAL Metering calibration startup command
64
65 // Configure
66 this->write16_(ATM90E26_REGISTER_MMODE, mmode); // Metering Mode Configuration (see above)
67
68 this->write16_(ATM90E26_REGISTER_PLCONSTH, (pl_const_ >> 16)); // PL Constant MSB
69 this->write16_(ATM90E26_REGISTER_PLCONSTL, pl_const_ & 0xFFFF); // PL Constant LSB
70
71 // Calibrate this to be 1 pulse per Wh
72 this->write16_(ATM90E26_REGISTER_LGAIN, gain_metering_); // L Line Calibration Gain (active power metering)
73 this->write16_(ATM90E26_REGISTER_LPHI, 0x0000); // L Line Calibration Angle
74 this->write16_(ATM90E26_REGISTER_NGAIN, 0x0000); // N Line Calibration Gain
75 this->write16_(ATM90E26_REGISTER_NPHI, 0x0000); // N Line Calibration Angle
76 this->write16_(ATM90E26_REGISTER_PSTARTTH, 0x08BD); // Active Startup Power Threshold (default) = 2237
77 this->write16_(ATM90E26_REGISTER_PNOLTH, 0x0000); // Active No-Load Power Threshold
78 this->write16_(ATM90E26_REGISTER_QSTARTTH, 0x0AEC); // Reactive Startup Power Threshold (default) = 2796
79 this->write16_(ATM90E26_REGISTER_QNOLTH, 0x0000); // Reactive No-Load Power Threshold
80
81 // Compute Checksum for the registers we set above
82 // low byte = sum of all bytes
83 uint16_t cs =
84 ((mmode >> 8) + (mmode & 0xFF) + (pl_const_ >> 24) + ((pl_const_ >> 16) & 0xFF) + ((pl_const_ >> 8) & 0xFF) +
85 (pl_const_ & 0xFF) + (gain_metering_ >> 8) + (gain_metering_ & 0xFF) + 0x08 + 0xBD + 0x0A + 0xEC) &
86 0xFF;
87 // high byte = XOR of all bytes
88 cs |= ((mmode >> 8) ^ (mmode & 0xFF) ^ (pl_const_ >> 24) ^ ((pl_const_ >> 16) & 0xFF) ^ ((pl_const_ >> 8) & 0xFF) ^
89 (pl_const_ & 0xFF) ^ (gain_metering_ >> 8) ^ (gain_metering_ & 0xFF) ^ 0x08 ^ 0xBD ^ 0x0A ^ 0xEC)
90 << 8;
91
92 this->write16_(ATM90E26_REGISTER_CS1, cs);
93 ESP_LOGVV(TAG, "Set CS1 to: 0x%04X", cs);
94
95 // Set measurement calibration values
96 this->write16_(ATM90E26_REGISTER_ADJSTART, 0x5678); // Measurement calibration startup command, registers 31-3A
97 this->write16_(ATM90E26_REGISTER_UGAIN, gain_voltage_); // Voltage RMS gain
98 this->write16_(ATM90E26_REGISTER_IGAINL, gain_ct_); // L line current RMS gain
99 this->write16_(ATM90E26_REGISTER_IGAINN, 0x7530); // N Line Current RMS Gain
100 this->write16_(ATM90E26_REGISTER_UOFFSET, 0x0000); // Voltage Offset
101 this->write16_(ATM90E26_REGISTER_IOFFSETL, 0x0000); // L Line Current Offset
102 this->write16_(ATM90E26_REGISTER_IOFFSETN, 0x0000); // N Line Current Offse
103 this->write16_(ATM90E26_REGISTER_POFFSETL, 0x0000); // L Line Active Power Offset
104 this->write16_(ATM90E26_REGISTER_QOFFSETL, 0x0000); // L Line Reactive Power Offset
105 this->write16_(ATM90E26_REGISTER_POFFSETN, 0x0000); // N Line Active Power Offset
106 this->write16_(ATM90E26_REGISTER_QOFFSETN, 0x0000); // N Line Reactive Power Offset
107
108 // Compute Checksum for the registers we set above
109 cs = ((gain_voltage_ >> 8) + (gain_voltage_ & 0xFF) + (gain_ct_ >> 8) + (gain_ct_ & 0xFF) + 0x75 + 0x30) & 0xFF;
110 cs |= ((gain_voltage_ >> 8) ^ (gain_voltage_ & 0xFF) ^ (gain_ct_ >> 8) ^ (gain_ct_ & 0xFF) ^ 0x75 ^ 0x30) << 8;
111 this->write16_(ATM90E26_REGISTER_CS2, cs);
112 ESP_LOGVV(TAG, "Set CS2 to: 0x%04X", cs);
113
114 this->write16_(ATM90E26_REGISTER_CALSTART,
115 0x8765); // Checks correctness of 21-2B registers and starts normal metering if ok
116 this->write16_(ATM90E26_REGISTER_ADJSTART,
117 0x8765); // Checks correctness of 31-3A registers and starts normal measurement if ok
118
119 const uint16_t sys_status = this->read16_(ATM90E26_REGISTER_SYSSTATUS);
120 if (sys_status & 0xC000) { // Checksum 1 Error
121
122 ESP_LOGW(TAG, "Could not initialize ATM90E26 IC: CS1 was incorrect, expected: 0x%04X",
123 this->read16_(ATM90E26_REGISTER_CS1));
124 this->mark_failed();
125 }
126 if (sys_status & 0x3000) { // Checksum 2 Error
127 ESP_LOGW(TAG, "Could not initialize ATM90E26 IC: CS2 was incorrect, expected: 0x%04X",
128 this->read16_(ATM90E26_REGISTER_CS2));
129 this->mark_failed();
130 }
131}
132
134 ESP_LOGCONFIG("", "ATM90E26:");
135 LOG_PIN(" CS Pin: ", this->cs_);
136 if (this->is_failed()) {
137 ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
138 }
139 LOG_UPDATE_INTERVAL(this);
140 LOG_SENSOR(" ", "Voltage A", this->voltage_sensor_);
141 LOG_SENSOR(" ", "Current A", this->current_sensor_);
142 LOG_SENSOR(" ", "Power A", this->power_sensor_);
143 LOG_SENSOR(" ", "Reactive Power A", this->reactive_power_sensor_);
144 LOG_SENSOR(" ", "PF A", this->power_factor_sensor_);
145 LOG_SENSOR(" ", "Active Forward Energy A", this->forward_active_energy_sensor_);
146 LOG_SENSOR(" ", "Active Reverse Energy A", this->reverse_active_energy_sensor_);
147 LOG_SENSOR(" ", "Frequency", this->freq_sensor_);
148}
150
151uint16_t ATM90E26Component::read16_(uint8_t a_register) {
152 uint8_t data[2];
153 uint16_t output;
154
155 this->enable();
157 this->write_byte(a_register | 0x80);
159 this->read_array(data, 2);
160 this->disable();
161
162 output = (uint16_t(data[0] & 0xFF) << 8) | (data[1] & 0xFF);
163 ESP_LOGVV(TAG, "read16_ 0x%04X output 0x%04X", a_register, output);
164 return output;
165}
166
167void ATM90E26Component::write16_(uint8_t a_register, uint16_t val) {
168 ESP_LOGVV(TAG, "write16_ 0x%04X val 0x%04X", a_register, val);
169 this->enable();
171 this->write_byte(a_register & 0x7F);
173 this->write_byte((val >> 8) & 0xFF);
174 this->write_byte(val & 0xFF);
175 this->disable();
176}
177
179 const uint16_t current = this->read16_(ATM90E26_REGISTER_IRMS);
180 return current / 1000.0f;
181}
182
184 const uint16_t voltage = this->read16_(ATM90E26_REGISTER_URMS);
185 return voltage / 100.0f;
186}
187
189 const int16_t val = this->read16_(ATM90E26_REGISTER_PMEAN); // two's complement
190 return (float) val;
191}
192
194 const int16_t val = this->read16_(ATM90E26_REGISTER_QMEAN); // two's complement
195 return (float) val;
196}
197
199 const uint16_t val = this->read16_(ATM90E26_REGISTER_POWERF); // signed
200 if (val & 0x8000) {
201 return -(val & 0x7FF) / 1000.0f;
202 } else {
203 return val / 1000.0f;
204 }
205}
206
208 const uint16_t val = this->read16_(ATM90E26_REGISTER_APENERGY);
209 if ((UINT32_MAX - this->cumulative_forward_active_energy_) > val) {
211 } else {
213 }
214 // The register holds thenths of pulses, we want to output Wh
215 return (this->cumulative_forward_active_energy_ * 100.0f / meter_constant_);
216}
217
219 const uint16_t val = this->read16_(ATM90E26_REGISTER_ANENERGY);
220 if (UINT32_MAX - this->cumulative_reverse_active_energy_ > val) {
222 } else {
224 }
225 return (this->cumulative_reverse_active_energy_ * 100.0f / meter_constant_);
226}
227
229 const uint16_t freq = this->read16_(ATM90E26_REGISTER_FREQ);
230 return freq / 100.0f;
231}
232
233} // namespace atm90e26
234} // namespace esphome
virtual void mark_failed()
Mark this component as failed.
bool is_failed() const
void status_set_warning(const char *message=nullptr)
void status_clear_warning()
sensor::Sensor * power_factor_sensor_
Definition atm90e26.h:56
uint16_t read16_(uint8_t a_register)
Definition atm90e26.cpp:151
void write16_(uint8_t a_register, uint16_t val)
Definition atm90e26.cpp:167
float get_setup_priority() const override
Definition atm90e26.cpp:149
sensor::Sensor * reverse_active_energy_sensor_
Definition atm90e26.h:58
sensor::Sensor * reactive_power_sensor_
Definition atm90e26.h:55
sensor::Sensor * forward_active_energy_sensor_
Definition atm90e26.h:57
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:45
mopeka_std_values val[4]
const float DATA
For components that import data from directly connected sensors like DHT.
Definition component.cpp:50
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
void IRAM_ATTR HOT delayMicroseconds(uint32_t us)
Definition core.cpp:31