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