ESPHome 2026.1.0-dev
Loading...
Searching...
No Matches
hlw8032.cpp
Go to the documentation of this file.
1#include "hlw8032.h"
2#include "esphome/core/log.h"
3#include <cinttypes>
4
5namespace esphome::hlw8032 {
6
7static const char *const TAG = "hlw8032";
8
9static constexpr uint8_t STATE_REG_OFFSET = 0;
10static constexpr uint8_t VOLTAGE_PARAM_OFFSET = 2;
11static constexpr uint8_t VOLTAGE_REG_OFFSET = 5;
12static constexpr uint8_t CURRENT_PARAM_OFFSET = 8;
13static constexpr uint8_t CURRENT_REG_OFFSET = 11;
14static constexpr uint8_t POWER_PARAM_OFFSET = 14;
15static constexpr uint8_t POWER_REG_OFFSET = 17;
16static constexpr uint8_t DATA_UPDATE_REG_OFFSET = 20;
17static constexpr uint8_t CHECKSUM_REG_OFFSET = 23;
18static constexpr uint8_t PARAM_REG_USABLE_BIT = (1 << 0);
19static constexpr uint8_t POWER_OVERFLOW_BIT = (1 << 1);
20static constexpr uint8_t CURRENT_OVERFLOW_BIT = (1 << 2);
21static constexpr uint8_t VOLTAGE_OVERFLOW_BIT = (1 << 3);
22static constexpr uint8_t HAVE_POWER_BIT = (1 << 4);
23static constexpr uint8_t HAVE_CURRENT_BIT = (1 << 5);
24static constexpr uint8_t HAVE_VOLTAGE_BIT = (1 << 6);
25static constexpr uint8_t CHECK_REG = 0x5A;
26static constexpr uint8_t STATE_REG_CORRECTION_FUNC_NORMAL = 0x55;
27static constexpr uint8_t STATE_REG_CORRECTION_FUNC_FAIL = 0xAA;
28static constexpr uint8_t STATE_REG_CORRECTION_MASK = 0xF0;
29static constexpr uint8_t STATE_REG_OVERFLOW_MASK = 0xF;
30static constexpr uint8_t PACKET_LENGTH = 24;
31
33 while (this->available()) {
34 uint8_t data = this->read();
35 if (!this->header_found_) {
36 if ((data == STATE_REG_CORRECTION_FUNC_NORMAL) || (data == STATE_REG_CORRECTION_FUNC_FAIL) ||
37 (data & STATE_REG_CORRECTION_MASK) == STATE_REG_CORRECTION_MASK) {
38 this->header_found_ = true;
39 this->raw_data_[0] = data;
40 }
41 } else if (data == CHECK_REG) {
42 this->raw_data_[1] = data;
43 this->raw_data_index_ = 2;
44 this->check_ = 0;
45 } else if (this->raw_data_index_ >= 2 && this->raw_data_index_ < PACKET_LENGTH) {
46 this->raw_data_[this->raw_data_index_++] = data;
47 if (this->raw_data_index_ < PACKET_LENGTH) {
48 this->check_ += data;
49 } else if (this->raw_data_index_ == PACKET_LENGTH) {
50 if (this->check_ == this->raw_data_[CHECKSUM_REG_OFFSET]) {
51 this->parse_data_();
52 } else {
53 ESP_LOGW(TAG, "Invalid checksum: 0x%02X != 0x%02X", this->check_, this->raw_data_[CHECKSUM_REG_OFFSET]);
54 }
55 this->raw_data_index_ = 0;
56 this->header_found_ = false;
57 memset(this->raw_data_, 0, PACKET_LENGTH);
58 }
59 }
60 }
61}
62
63uint32_t HLW8032Component::read_uint24_(uint8_t offset) {
64 return encode_uint24(this->raw_data_[offset], this->raw_data_[offset + 1], this->raw_data_[offset + 2]);
65}
66
68 // Parse header
69 uint8_t state_reg = this->raw_data_[STATE_REG_OFFSET];
70
71 if (state_reg == STATE_REG_CORRECTION_FUNC_FAIL) {
72 ESP_LOGE(TAG, "The chip's function of error correction fails.");
73 return;
74 }
75
76 // Parse data frame
77 uint32_t voltage_parameter = this->read_uint24_(VOLTAGE_PARAM_OFFSET);
78 uint32_t voltage_reg = this->read_uint24_(VOLTAGE_REG_OFFSET);
79 uint32_t current_parameter = this->read_uint24_(CURRENT_PARAM_OFFSET);
80 uint32_t current_reg = this->read_uint24_(CURRENT_REG_OFFSET);
81 uint32_t power_parameter = this->read_uint24_(POWER_PARAM_OFFSET);
82 uint32_t power_reg = this->read_uint24_(POWER_REG_OFFSET);
83 uint8_t data_update_register = this->raw_data_[DATA_UPDATE_REG_OFFSET];
84
85 bool have_power = data_update_register & HAVE_POWER_BIT;
86 bool have_current = data_update_register & HAVE_CURRENT_BIT;
87 bool have_voltage = data_update_register & HAVE_VOLTAGE_BIT;
88
89 bool power_cycle_exceeds_range = false;
90 bool parameter_regs_usable = true;
91
92 if ((state_reg & STATE_REG_CORRECTION_MASK) == STATE_REG_CORRECTION_MASK) {
93 if (state_reg & STATE_REG_OVERFLOW_MASK) {
94 if (state_reg & VOLTAGE_OVERFLOW_BIT) {
95 have_voltage = false;
96 }
97 if (state_reg & CURRENT_OVERFLOW_BIT) {
98 have_current = false;
99 }
100 if (state_reg & POWER_OVERFLOW_BIT) {
101 have_power = false;
102 }
103 if (state_reg & PARAM_REG_USABLE_BIT) {
104 parameter_regs_usable = false;
105 }
106
107 ESP_LOGW(TAG,
108 "Reports: (0x%02X)\n"
109 " Voltage REG overflows: %s\n"
110 " Current REG overflows: %s\n"
111 " Power REG overflows: %s\n"
112 " Voltage/Current/Power Parameter REGs not usable: %s\n",
113 state_reg, YESNO(!have_voltage), YESNO(!have_current), YESNO(!have_power),
114 YESNO(!parameter_regs_usable));
115
116 if (!parameter_regs_usable) {
117 return;
118 }
119 }
120 power_cycle_exceeds_range = have_power;
121 }
122
123 ESP_LOGVV(TAG,
124 "Parsed data:\n"
125 " Voltage: Parameter REG 0x%06" PRIX32 ", REG 0x%06" PRIX32 "\n"
126 " Current: Parameter REG 0x%06" PRIX32 ", REG 0x%06" PRIX32 "\n"
127 " Power: Parameter REG 0x%06" PRIX32 ", REG 0x%06" PRIX32 "\n"
128 " Data Update: REG 0x%02" PRIX8 "\n",
129 voltage_parameter, voltage_reg, current_parameter, current_reg, power_parameter, power_reg,
130 data_update_register);
131
132 const float current_multiplier = 1 / (this->current_resistor_ * 1000);
133
134 float voltage = 0.0f;
135 if (have_voltage && voltage_reg) {
136 voltage = float(voltage_parameter) * this->voltage_divider_ / float(voltage_reg);
137 }
138 if (this->voltage_sensor_ != nullptr) {
139 this->voltage_sensor_->publish_state(voltage);
140 }
141
142 float power = 0.0f;
143 if (have_power && power_reg && !power_cycle_exceeds_range) {
144 power = (float(power_parameter) / float(power_reg)) * this->voltage_divider_ * current_multiplier;
145 }
146 if (this->power_sensor_ != nullptr) {
147 this->power_sensor_->publish_state(power);
148 }
149
150 float current = 0.0f;
151 if (have_current && current_reg) {
152 current = float(current_parameter) * current_multiplier / float(current_reg);
153 }
154 if (this->current_sensor_ != nullptr) {
155 this->current_sensor_->publish_state(current);
156 }
157
158 float pf = NAN;
159 const float apparent_power = voltage * current;
160 if (have_voltage && have_current) {
161 if (have_power || power_cycle_exceeds_range) {
162 if (apparent_power > 0) {
163 pf = power / apparent_power;
164 if (pf < 0 || pf > 1) {
165 ESP_LOGD(TAG, "Impossible power factor: %.4f not in interval [0, 1]", pf);
166 pf = NAN;
167 }
168 } else if (apparent_power == 0 && power == 0) {
169 // No load, report ideal power factor
170 pf = 1.0f;
171 }
172 }
173 }
174 if (this->apparent_power_sensor_ != nullptr) {
175 this->apparent_power_sensor_->publish_state(apparent_power);
176 }
177 if (this->power_factor_sensor_ != nullptr) {
179 }
180}
181
183 ESP_LOGCONFIG(TAG,
184 "Configuration:\n"
185 " Current resistor: %.1f mΩ\n"
186 " Voltage Divider: %.3f",
187 this->current_resistor_ * 1000.0f, this->voltage_divider_);
188 LOG_SENSOR(" ", "Voltage", this->voltage_sensor_);
189 LOG_SENSOR(" ", "Current", this->current_sensor_);
190 LOG_SENSOR(" ", "Power", this->power_sensor_);
191 LOG_SENSOR(" ", "Apparent Power", this->apparent_power_sensor_);
192 LOG_SENSOR(" ", "Power Factor", this->power_factor_sensor_);
193}
194} // namespace esphome::hlw8032
sensor::Sensor * power_factor_sensor_
Definition hlw8032.h:34
sensor::Sensor * voltage_sensor_
Definition hlw8032.h:30
sensor::Sensor * current_sensor_
Definition hlw8032.h:31
uint32_t read_uint24_(uint8_t offset)
Definition hlw8032.cpp:63
sensor::Sensor * apparent_power_sensor_
Definition hlw8032.h:33
sensor::Sensor * power_sensor_
Definition hlw8032.h:32
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:77
constexpr uint32_t encode_uint24(uint8_t byte1, uint8_t byte2, uint8_t byte3)
Encode a 24-bit value given three bytes in most to least significant byte order.
Definition helpers.h:424