ESPHome 2026.11.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
7ESPHOME_LOG_TAG(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
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 // Whole package but checksum
48 const uint8_t checksum =
49 sum_bytes(reinterpret_cast<const uint8_t *>(data), sizeof(*data) - 1, this->read_command_) ^ 0xFF;
50 if (checksum != data->checksum) {
51 ESP_LOGW(TAG, "Invalid checksum! 0x%02X != 0x%02X", checksum, data->checksum);
52 }
53 return checksum == data->checksum;
54}
55
57 this->flush();
58 this->write_byte(this->read_command_);
59 this->write_byte(BL0940_FULL_PACKET);
60}
61
63#ifdef USE_NUMBER
64 // add calibration callbacks
65 if (this->voltage_calibration_number_ != nullptr) {
67 [this](float state) { this->voltage_calibration_callback_(state); });
70 }
71 }
72
73 if (this->current_calibration_number_ != nullptr) {
75 [this](float state) { this->current_calibration_callback_(state); });
78 }
79 }
80
81 if (this->power_calibration_number_ != nullptr) {
83 [this](float state) { this->power_calibration_callback_(state); });
86 }
87 }
88
89 if (this->energy_calibration_number_ != nullptr) {
91 [this](float state) { this->energy_calibration_callback_(state); });
94 }
95 }
96#endif
97
98 // calculate calibrated reference values
103
104 for (auto *i : BL0940_INIT) {
105 this->write_byte(this->write_command_), this->write_array(i, 5);
106 delay(1);
107 }
108 this->flush();
109}
110
112 // calculate power reference based on voltage and current reference
113 return this->voltage_reference_cal_ * this->current_reference_cal_ * 4046 / 324004 / 79931;
114}
115
117 // formula: 3600000 * 4046 * RL * R1 * 1000 / (1638.4 * 256) / Vref² / (R1 + R2)
118 // or: power_reference_ * 3600000 / (1638.4 * 256)
119 return this->power_reference_cal_ * 3600000 / (1638.4f * 256);
120}
121
122float BL0940::calculate_calibration_value_(float state) { return (100 + state) / 100; }
123
125 ESP_LOGI(TAG, "Resetting calibration defaults...");
126#ifdef USE_NUMBER
127 if (this->current_calibration_number_ != nullptr && this->current_cal_ != 1) {
129 }
130 if (this->voltage_calibration_number_ != nullptr && this->voltage_cal_ != 1) {
132 }
133 if (this->power_calibration_number_ != nullptr && this->power_cal_ != 1) {
135 }
136 if (this->energy_calibration_number_ != nullptr && this->energy_cal_ != 1) {
138 }
139#endif
140 ESP_LOGD(TAG, "external calibration values restored to initial state");
141}
142
144 this->current_cal_ = this->calculate_calibration_value_(state);
145 ESP_LOGV(TAG, "update current calibration state: %f", this->current_cal_);
146 this->recalibrate_();
147}
149 this->voltage_cal_ = this->calculate_calibration_value_(state);
150 ESP_LOGV(TAG, "update voltage calibration state: %f", this->voltage_cal_);
151 this->recalibrate_();
152}
154 this->power_cal_ = this->calculate_calibration_value_(state);
155 ESP_LOGV(TAG, "update power calibration state: %f", this->power_cal_);
156 this->recalibrate_();
157}
159 this->energy_cal_ = this->calculate_calibration_value_(state);
160 ESP_LOGV(TAG, "update energy calibration state: %f", this->energy_cal_);
161 this->recalibrate_();
162}
163
165 ESP_LOGV(TAG, "Recalibrating reference values");
168
169 if (this->voltage_cal_ != 1 || this->current_cal_ != 1) {
171 }
173
174 if (this->voltage_cal_ != 1 || this->current_cal_ != 1 || this->power_cal_ != 1) {
176 }
178
179 ESP_LOGD(TAG,
180 "Recalibrated reference values:\n"
181 " Voltage: %f\n"
182 " Current: %f\n"
183 " Power: %f\n"
184 " Energy: %f",
187}
188
190 auto tb = (float) temperature;
191 float converted_temp = ((float) 170 / 448) * (tb / 2 - 32) - 45;
192 if (sensor != nullptr) {
193 if (sensor->has_state() && std::abs(converted_temp - sensor->get_state()) > max_temperature_diff_) {
194 ESP_LOGD(TAG, "Invalid temperature change. Sensor: '%s', Old temperature: %f, New temperature: %f",
195 LOG_STR_ARG(sensor->get_log_name()), sensor->get_state(), converted_temp);
196 return 0.0f;
197 }
198 sensor->publish_state(converted_temp);
199 }
200 return converted_temp;
201}
202
204 // Bad header
205 if (data->frame_header != BL0940_PACKET_HEADER) {
206 ESP_LOGI(TAG, "Invalid data. Header mismatch: %d", data->frame_header);
207 return;
208 }
209
210 // cf_cnt is only 24 bits, so track overflows
211 uint32_t cf_cnt = (uint24_t) data->cf_cnt;
212 cf_cnt |= this->prev_cf_cnt_ & 0xff000000;
214 cf_cnt += 0x1000000;
215 }
216 this->prev_cf_cnt_ = cf_cnt;
217
218 float v_rms = (uint24_t) data->v_rms / this->voltage_reference_cal_;
219 float i_rms = (uint24_t) data->i_rms / this->current_reference_cal_;
220 float watt = (int24_t) data->watt / this->power_reference_cal_;
221 float total_energy_consumption = cf_cnt / this->energy_reference_cal_;
222
225
226 if (this->voltage_sensor_ != nullptr) {
227 this->voltage_sensor_->publish_state(v_rms);
228 }
229 if (this->current_sensor_ != nullptr) {
230 this->current_sensor_->publish_state(i_rms);
231 }
232 if (this->power_sensor_ != nullptr) {
233 this->power_sensor_->publish_state(watt);
234 }
235 if (this->energy_sensor_ != nullptr) {
236 this->energy_sensor_->publish_state(total_energy_consumption);
237 }
238
239 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,
240 total_energy_consumption, tps1, tps2);
241}
242
243void BL0940::dump_config() { // NOLINT(readability-function-cognitive-complexity)
244 ESP_LOGCONFIG(TAG,
245 "BL0940:\n"
246 " LEGACY MODE: %s\n"
247 " READ CMD: 0x%02X\n"
248 " WRITE CMD: 0x%02X\n"
249 " ------------------\n"
250 " Current reference: %f\n"
251 " Energy reference: %f\n"
252 " Power reference: %f\n"
253 " Voltage reference: %f\n",
254 TRUEFALSE(this->legacy_mode_enabled_), this->read_command_, this->write_command_,
256#ifdef USE_NUMBER
257 ESP_LOGCONFIG(TAG,
258 "BL0940:\n"
259 " Current calibration: %f\n"
260 " Energy calibration: %f\n"
261 " Power calibration: %f\n"
262 " Voltage calibration: %f\n",
263 this->current_cal_, this->energy_cal_, this->power_cal_, this->voltage_cal_);
264#endif
265 LOG_SENSOR("", "Voltage", this->voltage_sensor_);
266 LOG_SENSOR("", "Current", this->current_sensor_);
267 LOG_SENSOR("", "Power", this->power_sensor_);
268 LOG_SENSOR("", "Energy", this->energy_sensor_);
269 LOG_SENSOR("", "Internal temperature", this->internal_temperature_sensor_);
270 LOG_SENSOR("", "External temperature", this->external_temperature_sensor_);
271}
272
273} // 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
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 LogString * get_log_name() const
Get the name for a "%s" log argument (LOG_STR_ARG).
bool has_state() const
float calculate_calibration_value_(float state)
Definition bl0940.cpp:122
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:189
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:143
void dump_config() override
Definition bl0940.cpp:243
void voltage_calibration_callback_(float state)
Definition bl0940.cpp:148
sensor::Sensor * voltage_sensor_
Definition bl0940.h:82
sensor::Sensor * energy_sensor_
Definition bl0940.h:85
void update() override
Definition bl0940.cpp:56
void setup() override
Definition bl0940.cpp:62
number::Number * voltage_calibration_number_
Definition bl0940.h:91
float calculate_energy_reference_()
Definition bl0940.cpp:116
void energy_calibration_callback_(float state)
Definition bl0940.cpp:158
float calculate_power_reference_()
Definition bl0940.cpp:111
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:203
void loop() override
Definition bl0940.cpp:30
void power_calibration_callback_(float state)
Definition bl0940.cpp:153
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:65
float get_state() const
Getter-syntax for .state.
Definition sensor.h:98
UARTFlushResult flush()
Definition uart.h:96
optional< std::array< uint8_t, N > > read_array()
Definition uart.h:43
void write_byte(uint8_t data)
Definition uart.h:22
void write_array(const uint8_t *data, size_t len)
Definition uart.h:30
bool state
Definition fan.h:2
uint32_t buffer
Definition gif.cpp:50
ESPHOME_LOG_TAG(TAG, "bl0940")
constexpr uint8_t sum_bytes(const uint8_t *data, size_t len, uint8_t sum=0)
Add up the bytes of data with size len, truncated to 8 bits: the checksum many serial and IR protocol...
Definition helpers.h:894
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