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