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