ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
bl0906.cpp
Go to the documentation of this file.
1#include "bl0906.h"
2#include "constants.h"
3
5#include "esphome/core/log.h"
6
7namespace esphome::bl0906 {
8
9ESPHOME_LOG_TAG(TAG, "bl0906");
10
11constexpr uint32_t to_uint32_t(ube24_t input) { return encode_uint24(input.h, input.m, input.l); }
12
13constexpr int32_t to_int32_t(sbe24_t input) {
14 return static_cast<int32_t>(encode_uint32((uint8_t) input.h, input.m, input.l, 0)) >> 8;
15}
16
17// The SUM byte is (Addr+Data_L+Data_M+Data_H)&0xFF negated;
18constexpr uint8_t bl0906_checksum(const uint8_t address, const DataPacket *data) {
19 return (address + data->l + data->m + data->h) ^ 0xFF;
20}
21
22void BL0906::loop() {
23 this->discard_available();
24
25 if (this->current_stage_ == STAGE_IDLE) {
26 // Woken up between cycles to drain the action queue. Go back to sleep.
27 this->handle_actions_();
28 this->disable_loop();
29 return;
30 }
31
32 if (this->current_stage_ == STAGE_TEMP) {
33 // Temperature
34 this->read_data_(BL0906_TEMPERATURE, BL0906_TREF, this->temperature_sensor_);
35 } else if (this->current_stage_ == STAGE_CHANNEL_1) {
36 this->read_data_(BL0906_I_1_RMS, BL0906_IREF, this->current_1_sensor_);
37 this->read_data_(BL0906_WATT_1, BL0906_PREF, this->power_1_sensor_);
38 this->read_data_(BL0906_CF_1_CNT, BL0906_EREF, this->energy_1_sensor_);
39 } else if (this->current_stage_ == STAGE_CHANNEL_2) {
40 this->read_data_(BL0906_I_2_RMS, BL0906_IREF, this->current_2_sensor_);
41 this->read_data_(BL0906_WATT_2, BL0906_PREF, this->power_2_sensor_);
42 this->read_data_(BL0906_CF_2_CNT, BL0906_EREF, this->energy_2_sensor_);
43 } else if (this->current_stage_ == STAGE_CHANNEL_3) {
44 this->read_data_(BL0906_I_3_RMS, BL0906_IREF, this->current_3_sensor_);
45 this->read_data_(BL0906_WATT_3, BL0906_PREF, this->power_3_sensor_);
46 this->read_data_(BL0906_CF_3_CNT, BL0906_EREF, this->energy_3_sensor_);
47 } else if (this->current_stage_ == STAGE_CHANNEL_4) {
48 this->read_data_(BL0906_I_4_RMS, BL0906_IREF, this->current_4_sensor_);
49 this->read_data_(BL0906_WATT_4, BL0906_PREF, this->power_4_sensor_);
50 this->read_data_(BL0906_CF_4_CNT, BL0906_EREF, this->energy_4_sensor_);
51 } else if (this->current_stage_ == STAGE_CHANNEL_5) {
52 this->read_data_(BL0906_I_5_RMS, BL0906_IREF, this->current_5_sensor_);
53 this->read_data_(BL0906_WATT_5, BL0906_PREF, this->power_5_sensor_);
54 this->read_data_(BL0906_CF_5_CNT, BL0906_EREF, this->energy_5_sensor_);
55 } else if (this->current_stage_ == STAGE_CHANNEL_6) {
56 this->read_data_(BL0906_I_6_RMS, BL0906_IREF, this->current_6_sensor_);
57 this->read_data_(BL0906_WATT_6, BL0906_PREF, this->power_6_sensor_);
58 this->read_data_(BL0906_CF_6_CNT, BL0906_EREF, this->energy_6_sensor_);
59 } else if (this->current_stage_ == STAGE_FREQ) {
60 // Frequency
61 this->read_data_(BL0906_FREQUENCY, BL0906_FREF, this->frequency_sensor_);
62 // Voltage
63 this->read_data_(BL0906_V_RMS, BL0906_UREF, this->voltage_sensor_);
64 } else if (this->current_stage_ == STAGE_POWER) {
65 // Total power
66 this->read_data_(BL0906_WATT_SUM, BL0906_WATT, this->total_power_sensor_);
67 // Total Energy
68 this->read_data_(BL0906_CF_SUM_CNT, BL0906_CF, this->total_energy_sensor_);
69 }
70 this->advance_stage_();
71 this->handle_actions_();
72}
73
75 switch (this->current_stage_) {
76 case STAGE_CHANNEL_6:
78 break;
79 case STAGE_FREQ:
81 break;
82 case STAGE_POWER:
83 // Cycle complete; sleep until the next update().
85 this->disable_loop();
86 break;
87 default:
88 this->current_stage_ = static_cast<BL0906Stage>(this->current_stage_ + 1);
89 break;
90 }
91}
92
93void BL0906::setup() {
94 this->discard_available();
95 this->write_array(USR_WRPROT_WITABLE, sizeof(USR_WRPROT_WITABLE));
96 // Calibration (1: register address; 2: value before calibration; 3: value after calibration)
97 this->bias_correction_(BL0906_RMSOS_1, 0.01600, 0); // Calibration current_1
98 this->bias_correction_(BL0906_RMSOS_2, 0.01500, 0);
99 this->bias_correction_(BL0906_RMSOS_3, 0.01400, 0);
100 this->bias_correction_(BL0906_RMSOS_4, 0.01300, 0);
101 this->bias_correction_(BL0906_RMSOS_5, 0.01200, 0);
102 this->bias_correction_(BL0906_RMSOS_6, 0.01200, 0); // Calibration current_6
103
104 this->write_array(USR_WRPROT_ONLYREAD, sizeof(USR_WRPROT_ONLYREAD));
105
106 // Loop stays idle until the first update() or enqueued action.
107 this->disable_loop();
108}
109
110void BL0906::update() {
112 this->enable_loop();
113}
114
116 this->action_queue_.push_back(function);
117 // Ensure the queue is serviced even if the read cycle has already completed.
118 this->enable_loop();
119 return this->action_queue_.size();
120}
121
123 if (this->action_queue_.empty()) {
124 return;
125 }
126 ActionCallbackFuncPtr ptr_func = nullptr;
127 for (size_t i = 0; i < this->action_queue_.size(); i++) {
128 ptr_func = this->action_queue_[i];
129 if (ptr_func) {
130 ESP_LOGI(TAG, "HandleActionCallback[%zu]", i);
131 (this->*ptr_func)();
132 }
133 }
134
135 this->discard_available();
136
137 this->action_queue_.clear();
138}
139
140// Reset energy
142 this->write_array(BL0906_INIT[0], 6);
143 delay(1);
144 this->flush();
145
146 ESP_LOGW(TAG, "RMSOS:%02X%02X%02X%02X%02X%02X", BL0906_INIT[0][0], BL0906_INIT[0][1], BL0906_INIT[0][2],
147 BL0906_INIT[0][3], BL0906_INIT[0][4], BL0906_INIT[0][5]);
148}
149
150// Read data
151void BL0906::read_data_(const uint8_t address, const float reference, sensor::Sensor *sensor) {
152 if (sensor == nullptr) {
153 return;
154 }
156 ube24_t data_u24;
157 sbe24_t data_s24;
158 float value = 0;
159
160 bool signed_result = reference == BL0906_TREF || reference == BL0906_WATT || reference == BL0906_PREF;
161
162 this->write_byte(BL0906_READ_COMMAND);
163 this->write_byte(address);
164 if (!this->read_array((uint8_t *) &buffer, sizeof(buffer) - 1)) {
165 ESP_LOGW(TAG, "Read failed");
166 return;
167 }
168 if (bl0906_checksum(address, &buffer) != buffer.checksum) {
169 ESP_LOGW(TAG, "Junk on wire. Throwing away partial message");
170 while (read() >= 0)
171 ;
172 return;
173 }
174 if (signed_result) {
175 data_s24.l = buffer.l;
176 data_s24.m = buffer.m;
177 data_s24.h = buffer.h;
178 } else {
179 data_u24.l = buffer.l;
180 data_u24.m = buffer.m;
181 data_u24.h = buffer.h;
182 }
183 // Power
184 if (reference == BL0906_PREF) {
185 value = (float) to_int32_t(data_s24) * reference;
186 }
187
188 // Total power
189 if (reference == BL0906_WATT) {
190 value = (float) to_int32_t(data_s24) * reference;
191 }
192
193 // Voltage, current, power, total power
194 if (reference == BL0906_UREF || reference == BL0906_IREF || reference == BL0906_EREF || reference == BL0906_CF) {
195 value = (float) to_uint32_t(data_u24) * reference;
196 }
197
198 // Frequency
199 if (reference == BL0906_FREF) {
200 value = reference / (float) to_uint32_t(data_u24);
201 }
202 // Chip temperature
203 if (reference == BL0906_TREF) {
204 value = (float) to_int32_t(data_s24);
205 value = (value - 64) * 12.5f / 59 - 40;
206 }
207 sensor->publish_state(value);
208}
209
210// RMS offset correction
211void BL0906::bias_correction_(uint8_t address, float measurements, float correction) {
212 DataPacket data;
213 float ki = 12875 * 1 * (5.1 + 5.1) * 1000 / 2000 / 1.097; // Current coefficient
214 float i_rms0 = measurements * ki;
215 float i_rms = correction * ki;
216 int32_t value = (i_rms * i_rms - i_rms0 * i_rms0) / 256;
217 data.l = value & 0xFF;
218 data.m = (value >> 8) & 0xFF;
219 data.h = (value >> 16) & 0xFF;
220 data.address = bl0906_checksum(address, &data);
221 ESP_LOGV(TAG, "RMSOS:%02X%02X%02X%02X%02X%02X", BL0906_WRITE_COMMAND, address, data.l, data.m, data.h, data.address);
222 this->write_byte(BL0906_WRITE_COMMAND);
223 this->write_byte(address);
224 this->write_byte(data.l);
225 this->write_byte(data.m);
226 this->write_byte(data.h);
227 this->write_byte(data.address);
228}
229
230void BL0906::dump_config() {
231 ESP_LOGCONFIG(TAG, "BL0906:");
232 LOG_SENSOR(" ", "Voltage", this->voltage_sensor_);
233
234 LOG_SENSOR(" ", "Current1", this->current_1_sensor_);
235 LOG_SENSOR(" ", "Current2", this->current_2_sensor_);
236 LOG_SENSOR(" ", "Current3", this->current_3_sensor_);
237 LOG_SENSOR(" ", "Current4", this->current_4_sensor_);
238 LOG_SENSOR(" ", "Current5", this->current_5_sensor_);
239 LOG_SENSOR(" ", "Current6", this->current_6_sensor_);
240
241 LOG_SENSOR(" ", "Power1", this->power_1_sensor_);
242 LOG_SENSOR(" ", "Power2", this->power_2_sensor_);
243 LOG_SENSOR(" ", "Power3", this->power_3_sensor_);
244 LOG_SENSOR(" ", "Power4", this->power_4_sensor_);
245 LOG_SENSOR(" ", "Power5", this->power_5_sensor_);
246 LOG_SENSOR(" ", "Power6", this->power_6_sensor_);
247
248 LOG_SENSOR(" ", "Energy1", this->energy_1_sensor_);
249 LOG_SENSOR(" ", "Energy2", this->energy_2_sensor_);
250 LOG_SENSOR(" ", "Energy3", this->energy_3_sensor_);
251 LOG_SENSOR(" ", "Energy4", this->energy_4_sensor_);
252 LOG_SENSOR(" ", "Energy5", this->energy_5_sensor_);
253 LOG_SENSOR(" ", "Energy6", this->energy_6_sensor_);
254
255 LOG_SENSOR(" ", "Total Power", this->total_power_sensor_);
256 LOG_SENSOR(" ", "Total Energy", this->total_energy_sensor_);
257 LOG_SENSOR(" ", "Frequency", this->frequency_sensor_);
258 LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
259}
260
261} // namespace esphome::bl0906
uint8_t address
Definition bl0906.h:4
uint24_le_t i_rms
Definition bl0940.h:2
void enable_loop()
Enable this component's loop.
Definition component.h:248
void disable_loop()
Disable this component's loop.
virtual void loop()
This method will be called repeatedly.
Definition component.cpp:86
void read_data_(uint8_t address, float reference, sensor::Sensor *sensor)
Definition bl0906.cpp:151
BL0906Stage current_stage_
Definition bl0906.h:95
void bias_correction_(uint8_t address, float measurements, float correction)
Definition bl0906.cpp:211
size_t enqueue_action_(ActionCallbackFuncPtr function)
Definition bl0906.cpp:115
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:65
UARTFlushResult flush()
Definition uart.h:96
optional< std::array< uint8_t, N > > read_array()
Definition uart.h:43
void discard_available()
Read and drop every byte waiting in the receive buffer.
Definition uart.cpp:36
void write_byte(uint8_t data)
Definition uart.h:22
void write_array(const uint8_t *data, size_t len)
Definition uart.h:30
uint32_t buffer
Definition gif.cpp:50
constexpr uint8_t bl0906_checksum(const uint8_t address, const DataPacket *data)
Definition bl0906.cpp:18
void(BL0906::*)() ActionCallbackFuncPtr
Definition bl0906.h:51
const uint8_t BL0906_INIT[2][6]
Definition constants.h:114
ESPHOME_LOG_TAG(TAG, "bl0906")
constexpr int32_t to_int32_t(sbe24_t input)
Definition bl0906.cpp:13
constexpr uint32_t to_uint32_t(ube24_t input)
Definition bl0906.cpp:11
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:1002
constexpr uint32_t encode_uint32(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint8_t byte4)
Encode a 32-bit value given four bytes in most to least significant byte order.
Definition helpers.h:1006
void HOT delay(uint32_t ms)
Definition hal.cpp:85
static void uint32_t