ESPHome 2026.6.4
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
17
18static const char *const TAG = "ade7880";
19
20void IRAM_ATTR ADE7880Store::gpio_intr(ADE7880Store *arg) { arg->reset_done = true; }
21
23 if (this->irq0_pin_ != nullptr) {
24 this->irq0_pin_->setup();
25 }
26 this->irq1_pin_->setup();
27 if (this->reset_pin_ != nullptr) {
28 this->reset_pin_->setup();
29 }
30 this->store_.irq1_pin = this->irq1_pin_->to_isr();
32
33 // if IRQ1 is already asserted, the cause must be determined
34 if (this->irq1_pin_->digital_read() == 0) {
35 ESP_LOGD(TAG, "IRQ1 found asserted during setup()");
36 auto status1 = read_u32_register16_(STATUS1);
37 if ((status1 & ~STATUS1_RSTDONE) != 0) {
38 // not safe to proceed, must initiate reset
39 ESP_LOGD(TAG, "IRQ1 asserted for !RSTDONE, resetting device");
40 this->reset_device_();
41 return;
42 }
43 if ((status1 & STATUS1_RSTDONE) == STATUS1_RSTDONE) {
44 // safe to proceed, device has just completed reset cycle
45 ESP_LOGD(TAG, "Acknowledging RSTDONE");
46 this->write_u32_register16_(STATUS0, 0xFFFF);
47 this->write_u32_register16_(STATUS1, 0xFFFF);
48 this->init_device_();
49 return;
50 }
51 }
52
53 this->reset_device_();
54}
55
57 // check for completion of a reset cycle
58 if (!this->store_.reset_done) {
59 return;
60 }
61
62 ESP_LOGD(TAG, "Acknowledging RSTDONE");
63 this->write_u32_register16_(STATUS0, 0xFFFF);
64 this->write_u32_register16_(STATUS1, 0xFFFF);
65 this->init_device_();
66 this->store_.reset_done = false;
67 this->store_.reset_pending = false;
68}
69
70template<typename F>
71void ADE7880::update_sensor_from_s24zp_register16_(sensor::Sensor *sensor, uint16_t a_register, F &&f) {
72 if (sensor == nullptr) {
73 return;
74 }
75
76 float val = this->read_s24zp_register16_(a_register);
77 sensor->publish_state(f(val));
78}
79
80template<typename F>
81void ADE7880::update_sensor_from_s16_register16_(sensor::Sensor *sensor, uint16_t a_register, F &&f) {
82 if (sensor == nullptr) {
83 return;
84 }
85
86 float val = this->read_s16_register16_(a_register);
87 sensor->publish_state(f(val));
88}
89
90void ADE7880::update_active_energy_(PowerChannel *channel, uint16_t a_register) {
91 if (channel->forward_active_energy == nullptr && channel->reverse_active_energy == nullptr) {
92 return;
93 }
94
95 // The ADE7880 has no separate forward/reverse active energy accumulators. The xWATTHR registers
96 // accumulate signed energy since the last read (positive = imported/forward, negative = exported/
97 // reverse), so split the value by sign into the forward and reverse running totals.
98 float val = this->read_s32_register16_(a_register) / 14400.0f;
99 if (val >= 0.0f) {
100 if (channel->forward_active_energy != nullptr) {
102 }
103 } else {
104 if (channel->reverse_active_energy != nullptr) {
106 }
107 }
108}
109
111 if (this->store_.reset_pending) {
112 return;
113 }
114
115 auto start = millis();
116
117 if (this->channel_n_ != nullptr) {
118 auto *chan = this->channel_n_;
119 this->update_sensor_from_s24zp_register16_(chan->current, NIRMS, [](float val) { return val / 100000.0f; });
120 }
121
122 if (this->channel_a_ != nullptr) {
123 auto *chan = this->channel_a_;
124 this->update_sensor_from_s24zp_register16_(chan->current, AIRMS, [](float val) { return val / 100000.0f; });
125 this->update_sensor_from_s24zp_register16_(chan->voltage, AVRMS, [](float val) { return val / 10000.0f; });
126 this->update_sensor_from_s24zp_register16_(chan->active_power, AWATT, [](float val) { return val / 100.0f; });
127 this->update_sensor_from_s24zp_register16_(chan->apparent_power, AVA, [](float val) { return val / 100.0f; });
128 this->update_sensor_from_s16_register16_(chan->power_factor, APF,
129 [](float val) { return std::abs(val / -327.68f); });
130 this->update_active_energy_(chan, AWATTHR);
131 }
132
133 if (this->channel_b_ != nullptr) {
134 auto *chan = this->channel_b_;
135 this->update_sensor_from_s24zp_register16_(chan->current, BIRMS, [](float val) { return val / 100000.0f; });
136 this->update_sensor_from_s24zp_register16_(chan->voltage, BVRMS, [](float val) { return val / 10000.0f; });
137 this->update_sensor_from_s24zp_register16_(chan->active_power, BWATT, [](float val) { return val / 100.0f; });
138 this->update_sensor_from_s24zp_register16_(chan->apparent_power, BVA, [](float val) { return val / 100.0f; });
139 this->update_sensor_from_s16_register16_(chan->power_factor, BPF,
140 [](float val) { return std::abs(val / -327.68f); });
141 this->update_active_energy_(chan, BWATTHR);
142 }
143
144 if (this->channel_c_ != nullptr) {
145 auto *chan = this->channel_c_;
146 this->update_sensor_from_s24zp_register16_(chan->current, CIRMS, [](float val) { return val / 100000.0f; });
147 this->update_sensor_from_s24zp_register16_(chan->voltage, CVRMS, [](float val) { return val / 10000.0f; });
148 this->update_sensor_from_s24zp_register16_(chan->active_power, CWATT, [](float val) { return val / 100.0f; });
149 this->update_sensor_from_s24zp_register16_(chan->apparent_power, CVA, [](float val) { return val / 100.0f; });
150 this->update_sensor_from_s16_register16_(chan->power_factor, CPF,
151 [](float val) { return std::abs(val / -327.68f); });
152 this->update_active_energy_(chan, CWATTHR);
153 }
154
155 ESP_LOGD(TAG, "update took %" PRIu32 " ms", millis() - start);
156}
157
159 ESP_LOGCONFIG(TAG,
160 "ADE7880:\n"
161 " Frequency: %.0f Hz",
162 this->frequency_);
163 LOG_PIN(" IRQ0 Pin: ", this->irq0_pin_);
164 LOG_PIN(" IRQ1 Pin: ", this->irq1_pin_);
165 LOG_PIN(" RESET Pin: ", this->reset_pin_);
166
167 if (this->channel_a_ != nullptr) {
168 ESP_LOGCONFIG(TAG, " Phase A:");
169 LOG_SENSOR(" ", "Current", this->channel_a_->current);
170 LOG_SENSOR(" ", "Voltage", this->channel_a_->voltage);
171 LOG_SENSOR(" ", "Active Power", this->channel_a_->active_power);
172 LOG_SENSOR(" ", "Apparent Power", this->channel_a_->apparent_power);
173 LOG_SENSOR(" ", "Power Factor", this->channel_a_->power_factor);
174 LOG_SENSOR(" ", "Forward Active Energy", this->channel_a_->forward_active_energy);
175 LOG_SENSOR(" ", "Reverse Active Energy", this->channel_a_->reverse_active_energy);
176 ESP_LOGCONFIG(TAG,
177 " Calibration:\n"
178 " Current: %" PRId32 "\n"
179 " Voltage: %" PRId32 "\n"
180 " Power: %" PRId32 "\n"
181 " Phase Angle: %u",
182 this->channel_a_->current_gain_calibration, this->channel_a_->voltage_gain_calibration,
183 this->channel_a_->power_gain_calibration, this->channel_a_->phase_angle_calibration);
184 }
185
186 if (this->channel_b_ != nullptr) {
187 ESP_LOGCONFIG(TAG, " Phase B:");
188 LOG_SENSOR(" ", "Current", this->channel_b_->current);
189 LOG_SENSOR(" ", "Voltage", this->channel_b_->voltage);
190 LOG_SENSOR(" ", "Active Power", this->channel_b_->active_power);
191 LOG_SENSOR(" ", "Apparent Power", this->channel_b_->apparent_power);
192 LOG_SENSOR(" ", "Power Factor", this->channel_b_->power_factor);
193 LOG_SENSOR(" ", "Forward Active Energy", this->channel_b_->forward_active_energy);
194 LOG_SENSOR(" ", "Reverse Active Energy", this->channel_b_->reverse_active_energy);
195 ESP_LOGCONFIG(TAG,
196 " Calibration:\n"
197 " Current: %" PRId32 "\n"
198 " Voltage: %" PRId32 "\n"
199 " Power: %" PRId32 "\n"
200 " Phase Angle: %u",
201 this->channel_b_->current_gain_calibration, this->channel_b_->voltage_gain_calibration,
202 this->channel_b_->power_gain_calibration, this->channel_b_->phase_angle_calibration);
203 }
204
205 if (this->channel_c_ != nullptr) {
206 ESP_LOGCONFIG(TAG, " Phase C:");
207 LOG_SENSOR(" ", "Current", this->channel_c_->current);
208 LOG_SENSOR(" ", "Voltage", this->channel_c_->voltage);
209 LOG_SENSOR(" ", "Active Power", this->channel_c_->active_power);
210 LOG_SENSOR(" ", "Apparent Power", this->channel_c_->apparent_power);
211 LOG_SENSOR(" ", "Power Factor", this->channel_c_->power_factor);
212 LOG_SENSOR(" ", "Forward Active Energy", this->channel_c_->forward_active_energy);
213 LOG_SENSOR(" ", "Reverse Active Energy", this->channel_c_->reverse_active_energy);
214 ESP_LOGCONFIG(TAG,
215 " Calibration:\n"
216 " Current: %" PRId32 "\n"
217 " Voltage: %" PRId32 "\n"
218 " Power: %" PRId32 "\n"
219 " Phase Angle: %u",
220 this->channel_c_->current_gain_calibration, this->channel_c_->voltage_gain_calibration,
221 this->channel_c_->power_gain_calibration, this->channel_c_->phase_angle_calibration);
222 }
223
224 if (this->channel_n_ != nullptr) {
225 ESP_LOGCONFIG(TAG, " Neutral:");
226 LOG_SENSOR(" ", "Current", this->channel_n_->current);
227 ESP_LOGCONFIG(TAG,
228 " Calibration:\n"
229 " Current: %" PRId32,
231 }
232
233 LOG_I2C_DEVICE(this);
234 LOG_UPDATE_INTERVAL(this);
235}
236
237void ADE7880::calibrate_s10zp_reading_(uint16_t a_register, int16_t calibration) {
238 if (calibration == 0) {
239 return;
240 }
241
242 this->write_s10zp_register16_(a_register, calibration);
243}
244
245void ADE7880::calibrate_s24zpse_reading_(uint16_t a_register, int32_t calibration) {
246 if (calibration == 0) {
247 return;
248 }
249
250 this->write_s24zpse_register16_(a_register, calibration);
251}
252
255
256 this->write_u16_register16_(GAIN, 0);
257
258 if (this->frequency_ > 55) {
260 }
261
262 if (this->channel_n_ != nullptr) {
264 }
265
266 if (this->channel_a_ != nullptr) {
271 }
272
273 if (this->channel_b_ != nullptr) {
278 }
279
280 if (this->channel_c_ != nullptr) {
285 }
286
287 // write three default values to data memory RAM to flush the I2C write queue
291
295}
296
298 if (this->reset_pin_ != nullptr) {
299 ESP_LOGD(TAG, "Reset device using RESET pin");
300 this->reset_pin_->digital_write(false);
301 delay(1);
302 this->reset_pin_->digital_write(true);
303 } else {
304 ESP_LOGD(TAG, "Reset device using SWRST command");
306 }
307 this->store_.reset_pending = true;
308}
309
310} // namespace esphome::ade7880
ESPDEPRECATED("set_retry is deprecated and will be removed in 2026.8.0. Use set_timeout or set_interval instead.", "2026.2.0") void set_retry(const std uint32_t uint8_t std::function< RetryResult(uint8_t)> && f
Definition component.h:437
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:107
virtual ISRInternalGPIOPin to_isr() const =0
void update_sensor_from_s24zp_register16_(sensor::Sensor *sensor, uint16_t a_register, F &&f)
Definition ade7880.cpp:71
int16_t read_s16_register16_(uint16_t a_register)
int32_t read_s32_register16_(uint16_t a_register)
PowerChannel * channel_b_
Definition ade7880.h:95
void dump_config() override
Definition ade7880.cpp:158
void write_u8_register16_(uint16_t a_register, uint8_t value)
InternalGPIOPin * irq1_pin_
Definition ade7880.h:90
void write_s32_register16_(uint16_t a_register, int32_t value)
int32_t read_s24zp_register16_(uint16_t a_register)
void calibrate_s24zpse_reading_(uint16_t a_register, int32_t calibration)
Definition ade7880.cpp:245
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:81
uint32_t read_u32_register16_(uint16_t a_register)
PowerChannel * channel_a_
Definition ade7880.h:94
void update_active_energy_(PowerChannel *channel, uint16_t a_register)
Definition ade7880.cpp:90
ADE7880Store store_
Definition ade7880.h:88
void update() override
Definition ade7880.cpp:110
void setup() override
Definition ade7880.cpp:22
InternalGPIOPin * irq0_pin_
Definition ade7880.h:89
void loop() override
Definition ade7880.cpp:56
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:237
InternalGPIOPin * reset_pin_
Definition ade7880.h:91
NeutralChannel * channel_n_
Definition ade7880.h:93
PowerChannel * channel_c_
Definition ade7880.h:96
Base-class for all sensors.
Definition sensor.h:47
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:68
mopeka_std_values val[3]
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 uint16_t BWATTHR
constexpr uint8_t DSPWP_SEL_SET
constexpr uint16_t APHCAL
constexpr uint16_t BPHCAL
constexpr uint16_t VLEVEL
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 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 uint16_t CWATTHR
constexpr uint8_t CONFIG2_I2C_LOCK
constexpr uint16_t COMPMODE_SELFREQ
constexpr uint16_t DSPWP_SET
constexpr uint16_t CVGAIN
constexpr uint16_t AVRMS
constexpr uint16_t COMPMODE_DEFAULT
constexpr uint16_t AWATTHR
constexpr uint16_t STATUS1
constexpr uint16_t CPGAIN
constexpr uint32_t STATUS1_RSTDONE
@ INTERRUPT_FALLING_EDGE
Definition gpio.h:51
void HOT delay(uint32_t ms)
Definition hal.cpp:85
uint32_t IRAM_ATTR HOT millis()
Definition hal.cpp:28
static void gpio_intr(ADE7880Store *arg)
Definition ade7880.cpp:20
ISRInternalGPIOPin irq1_pin
Definition ade7880.h:63
sensor::Sensor * active_power
Definition ade7880.h:46
sensor::Sensor * reverse_active_energy
Definition ade7880.h:50
sensor::Sensor * current
Definition ade7880.h:44
sensor::Sensor * voltage
Definition ade7880.h:45
sensor::Sensor * forward_active_energy
Definition ade7880.h:49
sensor::Sensor * apparent_power
Definition ade7880.h:47
sensor::Sensor * power_factor
Definition ade7880.h:48