ESPHome 2026.6.0-dev
Loading...
Searching...
No Matches
bl0940.cpp
Go to the documentation of this file.
1#include "bl0940.h"
2#include "esphome/core/log.h"
3#include <cinttypes>
4
5namespace esphome::bl0940 {
6
7static const char *const TAG = "bl0940";
8
9static const uint8_t BL0940_FULL_PACKET = 0xAA;
10static const uint8_t BL0940_PACKET_HEADER = 0x55; // 0x58 according to en doc but 0x55 in cn doc
11
12static const uint8_t BL0940_REG_I_FAST_RMS_CTRL = 0x10;
13static const uint8_t BL0940_REG_MODE = 0x18;
14static const uint8_t BL0940_REG_SOFT_RESET = 0x19;
15static const uint8_t BL0940_REG_USR_WRPROT = 0x1A;
16static const uint8_t BL0940_REG_TPS_CTRL = 0x1B;
17
18static const uint8_t BL0940_INIT[5][5] = {
19 // Reset to default
20 {BL0940_REG_SOFT_RESET, 0x5A, 0x5A, 0x5A, 0x38},
21 // Enable User Operation Write
22 {BL0940_REG_USR_WRPROT, 0x55, 0x00, 0x00, 0xF0},
23 // 0x0100 = CF_UNABLE energy pulse, AC_FREQ_SEL 50Hz, RMS_UPDATE_SEL 800mS
24 {BL0940_REG_MODE, 0x00, 0x10, 0x00, 0x37},
25 // 0x47FF = Over-current and leakage alarm on, Automatic temperature measurement, Interval 100mS
26 {BL0940_REG_TPS_CTRL, 0xFF, 0x47, 0x00, 0xFE},
27 // 0x181C = Half cycle, Fast RMS threshold 6172
28 {BL0940_REG_I_FAST_RMS_CTRL, 0x1C, 0x18, 0x00, 0x1B}};
29
31 DataPacket buffer;
32 if (!this->available()) {
33 return;
34 }
35 if (read_array((uint8_t *) &buffer, sizeof(buffer))) {
36 if (this->validate_checksum_(&buffer)) {
37 this->received_package_(&buffer);
38 }
39 } else {
40 ESP_LOGW(TAG, "Junk on wire. Throwing away partial message");
41 while (read() >= 0)
42 ;
43 }
44}
45
47 uint8_t checksum = this->read_command_;
48 // Whole package but checksum
49 uint8_t *raw = (uint8_t *) data;
50 for (uint32_t i = 0; i < sizeof(*data) - 1; i++) {
51 checksum += raw[i];
52 }
53 checksum ^= 0xFF;
54 if (checksum != data->checksum) {
55 ESP_LOGW(TAG, "Invalid checksum! 0x%02X != 0x%02X", checksum, data->checksum);
56 }
57 return checksum == data->checksum;
58}
59
61 this->flush();
62 this->write_byte(this->read_command_);
63 this->write_byte(BL0940_FULL_PACKET);
64}
65
67#ifdef USE_NUMBER
68 // add calibration callbacks
69 if (this->voltage_calibration_number_ != nullptr) {
71 [this](float state) { this->voltage_calibration_callback_(state); });
74 }
75 }
76
77 if (this->current_calibration_number_ != nullptr) {
79 [this](float state) { this->current_calibration_callback_(state); });
82 }
83 }
84
85 if (this->power_calibration_number_ != nullptr) {
87 [this](float state) { this->power_calibration_callback_(state); });
90 }
91 }
92
93 if (this->energy_calibration_number_ != nullptr) {
95 [this](float state) { this->energy_calibration_callback_(state); });
98 }
99 }
100#endif
101
102 // calculate calibrated reference values
107
108 for (auto *i : BL0940_INIT) {
109 this->write_byte(this->write_command_), this->write_array(i, 5);
110 delay(1);
111 }
112 this->flush();
113}
114
116 // calculate power reference based on voltage and current reference
117 return this->voltage_reference_cal_ * this->current_reference_cal_ * 4046 / 324004 / 79931;
118}
119
121 // formula: 3600000 * 4046 * RL * R1 * 1000 / (1638.4 * 256) / Vref² / (R1 + R2)
122 // or: power_reference_ * 3600000 / (1638.4 * 256)
123 return this->power_reference_cal_ * 3600000 / (1638.4 * 256);
124}
125
126float BL0940::calculate_calibration_value_(float state) { return (100 + state) / 100; }
127
129#ifdef USE_NUMBER
130 if (this->current_calibration_number_ != nullptr && this->current_cal_ != 1) {
132 }
133 if (this->voltage_calibration_number_ != nullptr && this->voltage_cal_ != 1) {
135 }
136 if (this->power_calibration_number_ != nullptr && this->power_cal_ != 1) {
138 }
139 if (this->energy_calibration_number_ != nullptr && this->energy_cal_ != 1) {
141 }
142#endif
143 ESP_LOGD(TAG, "external calibration values restored to initial state");
144}
145
147 this->current_cal_ = this->calculate_calibration_value_(state);
148 ESP_LOGV(TAG, "update current calibration state: %f", this->current_cal_);
149 this->recalibrate_();
150}
152 this->voltage_cal_ = this->calculate_calibration_value_(state);
153 ESP_LOGV(TAG, "update voltage calibration state: %f", this->voltage_cal_);
154 this->recalibrate_();
155}
157 this->power_cal_ = this->calculate_calibration_value_(state);
158 ESP_LOGV(TAG, "update power calibration state: %f", this->power_cal_);
159 this->recalibrate_();
160}
162 this->energy_cal_ = this->calculate_calibration_value_(state);
163 ESP_LOGV(TAG, "update energy calibration state: %f", this->energy_cal_);
164 this->recalibrate_();
165}
166
168 ESP_LOGV(TAG, "Recalibrating reference values");
171
172 if (this->voltage_cal_ != 1 || this->current_cal_ != 1) {
174 }
176
177 if (this->voltage_cal_ != 1 || this->current_cal_ != 1 || this->power_cal_ != 1) {
179 }
181
182 ESP_LOGD(TAG,
183 "Recalibrated reference values:\n"
184 " Voltage: %f\n"
185 " Current: %f\n"
186 " Power: %f\n"
187 " Energy: %f",
190}
191
193 auto tb = (float) temperature;
194 float converted_temp = ((float) 170 / 448) * (tb / 2 - 32) - 45;
195 if (sensor != nullptr) {
196 if (sensor->has_state() && std::abs(converted_temp - sensor->get_state()) > max_temperature_diff_) {
197 ESP_LOGD(TAG, "Invalid temperature change. Sensor: '%s', Old temperature: %f, New temperature: %f",
198 sensor->get_name().c_str(), sensor->get_state(), converted_temp);
199 return 0.0f;
200 }
201 sensor->publish_state(converted_temp);
202 }
203 return converted_temp;
204}
205
207 // Bad header
208 if (data->frame_header != BL0940_PACKET_HEADER) {
209 ESP_LOGI(TAG, "Invalid data. Header mismatch: %d", data->frame_header);
210 return;
211 }
212
213 // cf_cnt is only 24 bits, so track overflows
214 uint32_t cf_cnt = (uint24_t) data->cf_cnt;
215 cf_cnt |= this->prev_cf_cnt_ & 0xff000000;
217 cf_cnt += 0x1000000;
218 }
219 this->prev_cf_cnt_ = cf_cnt;
220
221 float v_rms = (uint24_t) data->v_rms / this->voltage_reference_cal_;
222 float i_rms = (uint24_t) data->i_rms / this->current_reference_cal_;
223 float watt = (int24_t) data->watt / this->power_reference_cal_;
224 float total_energy_consumption = cf_cnt / this->energy_reference_cal_;
225
228
229 if (this->voltage_sensor_ != nullptr) {
230 this->voltage_sensor_->publish_state(v_rms);
231 }
232 if (this->current_sensor_ != nullptr) {
233 this->current_sensor_->publish_state(i_rms);
234 }
235 if (this->power_sensor_ != nullptr) {
236 this->power_sensor_->publish_state(watt);
237 }
238 if (this->energy_sensor_ != nullptr) {
239 this->energy_sensor_->publish_state(total_energy_consumption);
240 }
241
242 ESP_LOGV(TAG, "BL0940: U %fV, I %fA, P %fW, Cnt %" PRId32 ", ∫P %fkWh, T1 %f°C, T2 %f°C", v_rms, i_rms, watt, cf_cnt,
243 total_energy_consumption, tps1, tps2);
244}
245
246void BL0940::dump_config() { // NOLINT(readability-function-cognitive-complexity)
247 ESP_LOGCONFIG(TAG,
248 "BL0940:\n"
249 " LEGACY MODE: %s\n"
250 " READ CMD: 0x%02X\n"
251 " WRITE CMD: 0x%02X\n"
252 " ------------------\n"
253 " Current reference: %f\n"
254 " Energy reference: %f\n"
255 " Power reference: %f\n"
256 " Voltage reference: %f\n",
257 TRUEFALSE(this->legacy_mode_enabled_), this->read_command_, this->write_command_,
259#ifdef USE_NUMBER
260 ESP_LOGCONFIG(TAG,
261 "BL0940:\n"
262 " Current calibration: %f\n"
263 " Energy calibration: %f\n"
264 " Power calibration: %f\n"
265 " Voltage calibration: %f\n",
266 this->current_cal_, this->energy_cal_, this->power_cal_, this->voltage_cal_);
267#endif
268 LOG_SENSOR("", "Voltage", this->voltage_sensor_);
269 LOG_SENSOR("", "Current", this->current_sensor_);
270 LOG_SENSOR("", "Power", this->power_sensor_);
271 LOG_SENSOR("", "Energy", this->energy_sensor_);
272 LOG_SENSOR("", "Internal temperature", this->internal_temperature_sensor_);
273 LOG_SENSOR("", "External temperature", this->external_temperature_sensor_);
274}
275
276} // namespace esphome::bl0940
uint8_t checksum
Definition bl0906.h:3
ube16_t tps1
Definition bl0939.h:12
ube24_t v_rms
Definition bl0939.h:6
uint8_t raw[35]
Definition bl0939.h:0
ube16_t tps2
Definition bl0939.h:14
uint24_le_t i_rms
Definition bl0940.h:2
uint24_le_t cf_cnt
Definition bl0940.h:8
int24_le_t watt
Definition bl0940.h:6
const StringRef & get_name() const
Definition entity_base.h:71
bool has_state() const
constexpr const char * c_str() const
Definition string_ref.h:73
float calculate_calibration_value_(float state)
Definition bl0940.cpp:126
number::Number * current_calibration_number_
Definition bl0940.h:92
sensor::Sensor * external_temperature_sensor_
Definition bl0940.h:87
sensor::Sensor * current_sensor_
Definition bl0940.h:83
sensor::Sensor * internal_temperature_sensor_
Definition bl0940.h:86
float update_temp_(sensor::Sensor *sensor, uint16_le_t packed_temperature) const
Definition bl0940.cpp:192
number::Number * energy_calibration_number_
Definition bl0940.h:94
number::Number * power_calibration_number_
Definition bl0940.h:93
float max_temperature_diff_
Definition bl0940.h:99
void current_calibration_callback_(float state)
Definition bl0940.cpp:146
void dump_config() override
Definition bl0940.cpp:246
void voltage_calibration_callback_(float state)
Definition bl0940.cpp:151
sensor::Sensor * voltage_sensor_
Definition bl0940.h:82
sensor::Sensor * energy_sensor_
Definition bl0940.h:85
void update() override
Definition bl0940.cpp:60
void setup() override
Definition bl0940.cpp:66
number::Number * voltage_calibration_number_
Definition bl0940.h:91
float calculate_energy_reference_()
Definition bl0940.cpp:120
void energy_calibration_callback_(float state)
Definition bl0940.cpp:161
float calculate_power_reference_()
Definition bl0940.cpp:115
bool validate_checksum_(DataPacket *data)
Definition bl0940.cpp:46
uint32_t prev_cf_cnt_
Definition bl0940.h:98
sensor::Sensor * power_sensor_
Definition bl0940.h:84
void received_package_(DataPacket *data)
Definition bl0940.cpp:206
void loop() override
Definition bl0940.cpp:30
void power_calibration_callback_(float state)
Definition bl0940.cpp:156
NumberCall & set_value(float value)
NumberCall make_call()
Definition number.h:35
void add_on_state_callback(F &&callback)
Definition number.h:37
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
float get_state() const
Getter-syntax for .state.
Definition sensor.h:98
UARTFlushResult flush()
Definition uart.h:48
optional< std::array< uint8_t, N > > read_array()
Definition uart.h:38
void write_byte(uint8_t data)
Definition uart.h:18
void write_array(const uint8_t *data, size_t len)
Definition uart.h:26
bool state
Definition fan.h:2
void HOT delay(uint32_t ms)
Definition hal.cpp:85
static void uint32_t
24-bit signed integer type, transparently converting to 32-bit.
Definition datatypes.h:38
24-bit unsigned integer type, transparently converting to 32-bit.
Definition datatypes.h:32
uint16_t temperature
Definition sun_gtil2.cpp:12