ESPHome 2025.5.0
Loading...
Searching...
No Matches
ade7880.cpp
Go to the documentation of this file.
1// This component was developed using knowledge gathered by a number
2// of people who reverse-engineered the Shelly 3EM:
3//
4// @AndreKR on GitHub
5// Axel (@Axel830 on GitHub)
6// Marko (@goodkiller on GitHub)
7// Michaƫl Piron (@michaelpiron on GitHub)
8// Theo Arends (@arendst on GitHub)
9
10#include "ade7880.h"
11#include "ade7880_registers.h"
12#include "esphome/core/log.h"
13
14#include <cinttypes>
15
16namespace esphome {
17namespace ade7880 {
18
19static const char *const TAG = "ade7880";
20
21void IRAM_ATTR ADE7880Store::gpio_intr(ADE7880Store *arg) { arg->reset_done = true; }
22
24 if (this->irq0_pin_ != nullptr) {
25 this->irq0_pin_->setup();
26 }
27 this->irq1_pin_->setup();
28 if (this->reset_pin_ != nullptr) {
29 this->reset_pin_->setup();
30 }
31 this->store_.irq1_pin = this->irq1_pin_->to_isr();
33
34 // if IRQ1 is already asserted, the cause must be determined
35 if (this->irq1_pin_->digital_read() == 0) {
36 ESP_LOGD(TAG, "IRQ1 found asserted during setup()");
37 auto status1 = read_u32_register16_(STATUS1);
38 if ((status1 & ~STATUS1_RSTDONE) != 0) {
39 // not safe to proceed, must initiate reset
40 ESP_LOGD(TAG, "IRQ1 asserted for !RSTDONE, resetting device");
41 this->reset_device_();
42 return;
43 }
44 if ((status1 & STATUS1_RSTDONE) == STATUS1_RSTDONE) {
45 // safe to proceed, device has just completed reset cycle
46 ESP_LOGD(TAG, "Acknowledging RSTDONE");
47 this->write_u32_register16_(STATUS0, 0xFFFF);
48 this->write_u32_register16_(STATUS1, 0xFFFF);
49 this->init_device_();
50 return;
51 }
52 }
53
54 this->reset_device_();
55}
56
58 // check for completion of a reset cycle
59 if (!this->store_.reset_done) {
60 return;
61 }
62
63 ESP_LOGD(TAG, "Acknowledging RSTDONE");
64 this->write_u32_register16_(STATUS0, 0xFFFF);
65 this->write_u32_register16_(STATUS1, 0xFFFF);
66 this->init_device_();
67 this->store_.reset_done = false;
68 this->store_.reset_pending = false;
69}
70
71template<typename F>
72void ADE7880::update_sensor_from_s24zp_register16_(sensor::Sensor *sensor, uint16_t a_register, F &&f) {
73 if (sensor == nullptr) {
74 return;
75 }
76
77 float val = this->read_s24zp_register16_(a_register);
78 sensor->publish_state(f(val));
79}
80
81template<typename F>
82void ADE7880::update_sensor_from_s16_register16_(sensor::Sensor *sensor, uint16_t a_register, F &&f) {
83 if (sensor == nullptr) {
84 return;
85 }
86
87 float val = this->read_s16_register16_(a_register);
88 sensor->publish_state(f(val));
89}
90
91template<typename F>
92void ADE7880::update_sensor_from_s32_register16_(sensor::Sensor *sensor, uint16_t a_register, F &&f) {
93 if (sensor == nullptr) {
94 return;
95 }
96
97 float val = this->read_s32_register16_(a_register);
98 sensor->publish_state(f(val));
99}
100
102 if (this->store_.reset_pending) {
103 return;
104 }
105
106 auto start = millis();
107
108 if (this->channel_n_ != nullptr) {
109 auto *chan = this->channel_n_;
110 this->update_sensor_from_s24zp_register16_(chan->current, NIRMS, [](float val) { return val / 100000.0f; });
111 }
112
113 if (this->channel_a_ != nullptr) {
114 auto *chan = this->channel_a_;
115 this->update_sensor_from_s24zp_register16_(chan->current, AIRMS, [](float val) { return val / 100000.0f; });
116 this->update_sensor_from_s24zp_register16_(chan->voltage, BVRMS, [](float val) { return val / 10000.0f; });
117 this->update_sensor_from_s24zp_register16_(chan->active_power, AWATT, [](float val) { return val / 100.0f; });
118 this->update_sensor_from_s24zp_register16_(chan->apparent_power, AVA, [](float val) { return val / 100.0f; });
119 this->update_sensor_from_s16_register16_(chan->power_factor, APF,
120 [](float val) { return std::abs(val / -327.68f); });
121 this->update_sensor_from_s32_register16_(chan->forward_active_energy, AFWATTHR, [&chan](float val) {
122 return chan->forward_active_energy_total += val / 14400.0f;
123 });
124 this->update_sensor_from_s32_register16_(chan->reverse_active_energy, AFWATTHR, [&chan](float val) {
125 return chan->reverse_active_energy_total += val / 14400.0f;
126 });
127 }
128
129 if (this->channel_b_ != nullptr) {
130 auto *chan = this->channel_b_;
131 this->update_sensor_from_s24zp_register16_(chan->current, BIRMS, [](float val) { return val / 100000.0f; });
132 this->update_sensor_from_s24zp_register16_(chan->voltage, BVRMS, [](float val) { return val / 10000.0f; });
133 this->update_sensor_from_s24zp_register16_(chan->active_power, BWATT, [](float val) { return val / 100.0f; });
134 this->update_sensor_from_s24zp_register16_(chan->apparent_power, BVA, [](float val) { return val / 100.0f; });
135 this->update_sensor_from_s16_register16_(chan->power_factor, BPF,
136 [](float val) { return std::abs(val / -327.68f); });
137 this->update_sensor_from_s32_register16_(chan->forward_active_energy, BFWATTHR, [&chan](float val) {
138 return chan->forward_active_energy_total += val / 14400.0f;
139 });
140 this->update_sensor_from_s32_register16_(chan->reverse_active_energy, BFWATTHR, [&chan](float val) {
141 return chan->reverse_active_energy_total += val / 14400.0f;
142 });
143 }
144
145 if (this->channel_c_ != nullptr) {
146 auto *chan = this->channel_c_;
147 this->update_sensor_from_s24zp_register16_(chan->current, CIRMS, [](float val) { return val / 100000.0f; });
148 this->update_sensor_from_s24zp_register16_(chan->voltage, CVRMS, [](float val) { return val / 10000.0f; });
149 this->update_sensor_from_s24zp_register16_(chan->active_power, CWATT, [](float val) { return val / 100.0f; });
150 this->update_sensor_from_s24zp_register16_(chan->apparent_power, CVA, [](float val) { return val / 100.0f; });
151 this->update_sensor_from_s16_register16_(chan->power_factor, CPF,
152 [](float val) { return std::abs(val / -327.68f); });
153 this->update_sensor_from_s32_register16_(chan->forward_active_energy, CFWATTHR, [&chan](float val) {
154 return chan->forward_active_energy_total += val / 14400.0f;
155 });
156 this->update_sensor_from_s32_register16_(chan->reverse_active_energy, CFWATTHR, [&chan](float val) {
157 return chan->reverse_active_energy_total += val / 14400.0f;
158 });
159 }
160
161 ESP_LOGD(TAG, "update took %" PRIu32 " ms", millis() - start);
162}
163
165 ESP_LOGCONFIG(TAG, "ADE7880:");
166 LOG_PIN(" IRQ0 Pin: ", this->irq0_pin_);
167 LOG_PIN(" IRQ1 Pin: ", this->irq1_pin_);
168 LOG_PIN(" RESET Pin: ", this->reset_pin_);
169 ESP_LOGCONFIG(TAG, " Frequency: %.0f Hz", this->frequency_);
170
171 if (this->channel_a_ != nullptr) {
172 ESP_LOGCONFIG(TAG, " Phase A:");
173 LOG_SENSOR(" ", "Current", this->channel_a_->current);
174 LOG_SENSOR(" ", "Voltage", this->channel_a_->voltage);
175 LOG_SENSOR(" ", "Active Power", this->channel_a_->active_power);
176 LOG_SENSOR(" ", "Apparent Power", this->channel_a_->apparent_power);
177 LOG_SENSOR(" ", "Power Factor", this->channel_a_->power_factor);
178 LOG_SENSOR(" ", "Forward Active Energy", this->channel_a_->forward_active_energy);
179 LOG_SENSOR(" ", "Reverse Active Energy", this->channel_a_->reverse_active_energy);
180 ESP_LOGCONFIG(TAG, " Calibration:");
181 ESP_LOGCONFIG(TAG, " Current: %" PRId32, this->channel_a_->current_gain_calibration);
182 ESP_LOGCONFIG(TAG, " Voltage: %" PRId32, this->channel_a_->voltage_gain_calibration);
183 ESP_LOGCONFIG(TAG, " Power: %" PRId32, this->channel_a_->power_gain_calibration);
184 ESP_LOGCONFIG(TAG, " Phase Angle: %u", this->channel_a_->phase_angle_calibration);
185 }
186
187 if (this->channel_b_ != nullptr) {
188 ESP_LOGCONFIG(TAG, " Phase B:");
189 LOG_SENSOR(" ", "Current", this->channel_b_->current);
190 LOG_SENSOR(" ", "Voltage", this->channel_b_->voltage);
191 LOG_SENSOR(" ", "Active Power", this->channel_b_->active_power);
192 LOG_SENSOR(" ", "Apparent Power", this->channel_b_->apparent_power);
193 LOG_SENSOR(" ", "Power Factor", this->channel_b_->power_factor);
194 LOG_SENSOR(" ", "Forward Active Energy", this->channel_b_->forward_active_energy);
195 LOG_SENSOR(" ", "Reverse Active Energy", this->channel_b_->reverse_active_energy);
196 ESP_LOGCONFIG(TAG, " Calibration:");
197 ESP_LOGCONFIG(TAG, " Current: %" PRId32, this->channel_b_->current_gain_calibration);
198 ESP_LOGCONFIG(TAG, " Voltage: %" PRId32, this->channel_b_->voltage_gain_calibration);
199 ESP_LOGCONFIG(TAG, " Power: %" PRId32, this->channel_b_->power_gain_calibration);
200 ESP_LOGCONFIG(TAG, " Phase Angle: %u", this->channel_b_->phase_angle_calibration);
201 }
202
203 if (this->channel_c_ != nullptr) {
204 ESP_LOGCONFIG(TAG, " Phase C:");
205 LOG_SENSOR(" ", "Current", this->channel_c_->current);
206 LOG_SENSOR(" ", "Voltage", this->channel_c_->voltage);
207 LOG_SENSOR(" ", "Active Power", this->channel_c_->active_power);
208 LOG_SENSOR(" ", "Apparent Power", this->channel_c_->apparent_power);
209 LOG_SENSOR(" ", "Power Factor", this->channel_c_->power_factor);
210 LOG_SENSOR(" ", "Forward Active Energy", this->channel_c_->forward_active_energy);
211 LOG_SENSOR(" ", "Reverse Active Energy", this->channel_c_->reverse_active_energy);
212 ESP_LOGCONFIG(TAG, " Calibration:");
213 ESP_LOGCONFIG(TAG, " Current: %" PRId32, this->channel_c_->current_gain_calibration);
214 ESP_LOGCONFIG(TAG, " Voltage: %" PRId32, this->channel_c_->voltage_gain_calibration);
215 ESP_LOGCONFIG(TAG, " Power: %" PRId32, this->channel_c_->power_gain_calibration);
216 ESP_LOGCONFIG(TAG, " Phase Angle: %u", this->channel_c_->phase_angle_calibration);
217 }
218
219 if (this->channel_n_ != nullptr) {
220 ESP_LOGCONFIG(TAG, " Neutral:");
221 LOG_SENSOR(" ", "Current", this->channel_n_->current);
222 ESP_LOGCONFIG(TAG, " Calibration:");
223 ESP_LOGCONFIG(TAG, " Current: %" PRId32, this->channel_n_->current_gain_calibration);
224 }
225
226 LOG_I2C_DEVICE(this);
227 LOG_UPDATE_INTERVAL(this);
228}
229
230void ADE7880::calibrate_s10zp_reading_(uint16_t a_register, int16_t calibration) {
231 if (calibration == 0) {
232 return;
233 }
234
235 this->write_s10zp_register16_(a_register, calibration);
236}
237
238void ADE7880::calibrate_s24zpse_reading_(uint16_t a_register, int32_t calibration) {
239 if (calibration == 0) {
240 return;
241 }
242
243 this->write_s24zpse_register16_(a_register, calibration);
244}
245
248
249 this->write_u16_register16_(GAIN, 0);
250
251 if (this->frequency_ > 55) {
253 }
254
255 if (this->channel_n_ != nullptr) {
257 }
258
259 if (this->channel_a_ != nullptr) {
264 }
265
266 if (this->channel_b_ != nullptr) {
271 }
272
273 if (this->channel_c_ != nullptr) {
278 }
279
280 // write three default values to data memory RAM to flush the I2C write queue
284
288}
289
291 if (this->reset_pin_ != nullptr) {
292 ESP_LOGD(TAG, "Reset device using RESET pin");
293 this->reset_pin_->digital_write(false);
294 delay(1);
295 this->reset_pin_->digital_write(true);
296 } else {
297 ESP_LOGD(TAG, "Reset device using SWRST command");
299 }
300 this->store_.reset_pending = true;
301}
302
303} // namespace ade7880
304} // namespace esphome
virtual void setup()=0
virtual void digital_write(bool value)=0
virtual bool digital_read()=0
void attach_interrupt(void(*func)(T *), T *arg, gpio::InterruptType type) const
Definition gpio.h:88
virtual ISRInternalGPIOPin to_isr() const =0
void update_sensor_from_s24zp_register16_(sensor::Sensor *sensor, uint16_t a_register, F &&f)
Definition ade7880.cpp:72
int16_t read_s16_register16_(uint16_t a_register)
int32_t read_s32_register16_(uint16_t a_register)
PowerChannel * channel_b_
Definition ade7880.h:98
void dump_config() override
Definition ade7880.cpp:164
void write_u8_register16_(uint16_t a_register, uint8_t value)
InternalGPIOPin * irq1_pin_
Definition ade7880.h:93
void write_s32_register16_(uint16_t a_register, int32_t value)
int32_t read_s24zp_register16_(uint16_t a_register)
void update_sensor_from_s32_register16_(sensor::Sensor *sensor, uint16_t a_register, F &&f)
Definition ade7880.cpp:92
void calibrate_s24zpse_reading_(uint16_t a_register, int32_t calibration)
Definition ade7880.cpp:238
void write_s24zpse_register16_(uint16_t a_register, int32_t value)
void update_sensor_from_s16_register16_(sensor::Sensor *sensor, uint16_t a_register, F &&f)
Definition ade7880.cpp:82
uint32_t read_u32_register16_(uint16_t a_register)
PowerChannel * channel_a_
Definition ade7880.h:97
ADE7880Store store_
Definition ade7880.h:91
void update() override
Definition ade7880.cpp:101
void setup() override
Definition ade7880.cpp:23
InternalGPIOPin * irq0_pin_
Definition ade7880.h:92
void loop() override
Definition ade7880.cpp:57
void write_s10zp_register16_(uint16_t a_register, int16_t value)
void write_u16_register16_(uint16_t a_register, uint16_t value)
void write_u32_register16_(uint16_t a_register, uint32_t value)
void calibrate_s10zp_reading_(uint16_t a_register, int16_t calibration)
Definition ade7880.cpp:230
InternalGPIOPin * reset_pin_
Definition ade7880.h:94
NeutralChannel * channel_n_
Definition ade7880.h:96
PowerChannel * channel_c_
Definition ade7880.h:99
Base-class for all sensors.
Definition sensor.h:57
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:39
mopeka_std_values val[4]
constexpr uint16_t CIRMS
constexpr uint16_t APGAIN
constexpr uint16_t CIGAIN
constexpr uint16_t CWATT
constexpr uint16_t AIGAIN
constexpr uint16_t DSPWP_SEL
constexpr uint16_t AWATT
constexpr uint16_t CPHCAL
constexpr uint16_t GAIN
constexpr uint8_t DSPWP_SEL_SET
constexpr uint16_t APHCAL
constexpr uint16_t BPHCAL
constexpr uint16_t VLEVEL
constexpr uint16_t AFWATTHR
constexpr uint16_t AVGAIN
constexpr uint16_t NIGAIN
constexpr uint16_t BVA
constexpr uint16_t APF
constexpr uint8_t DSPWP_SET_RO
constexpr uint16_t AIRMS
constexpr uint16_t BVGAIN
constexpr uint16_t CVRMS
constexpr uint16_t BWATT
constexpr uint16_t BVRMS
constexpr uint16_t NIRMS
constexpr uint16_t RUN_ENABLE
constexpr uint16_t CFWATTHR
constexpr uint16_t BPGAIN
constexpr uint16_t CVA
constexpr uint16_t BIGAIN
constexpr uint16_t CONFIG
constexpr uint16_t CONFIG_SWRST
constexpr uint16_t AVA
constexpr uint16_t RUN
constexpr uint16_t CONFIG2
constexpr uint16_t BIRMS
constexpr uint16_t STATUS0
constexpr uint16_t CPF
constexpr uint16_t BPF
constexpr uint16_t COMPMODE
constexpr uint8_t CONFIG2_I2C_LOCK
constexpr uint16_t COMPMODE_SELFREQ
constexpr uint16_t BFWATTHR
constexpr uint16_t DSPWP_SET
constexpr uint16_t CVGAIN
constexpr uint16_t COMPMODE_DEFAULT
constexpr uint16_t STATUS1
constexpr uint16_t CPGAIN
constexpr uint32_t STATUS1_RSTDONE
@ INTERRUPT_FALLING_EDGE
Definition gpio.h:42
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
void IRAM_ATTR HOT delay(uint32_t ms)
Definition core.cpp:28
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:27
static void gpio_intr(ADE7880Store *arg)
Definition ade7880.cpp:21
ISRInternalGPIOPin irq1_pin
Definition ade7880.h:64
sensor::Sensor * active_power
Definition ade7880.h:47
sensor::Sensor * reverse_active_energy
Definition ade7880.h:51
sensor::Sensor * current
Definition ade7880.h:45
sensor::Sensor * voltage
Definition ade7880.h:46
sensor::Sensor * forward_active_energy
Definition ade7880.h:50
sensor::Sensor * apparent_power
Definition ade7880.h:48
sensor::Sensor * power_factor
Definition ade7880.h:49