ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
bl0939.cpp
Go to the documentation of this file.
1#include "bl0939.h"
2#include "esphome/core/log.h"
3#include <cinttypes>
4
5namespace esphome::bl0939 {
6
7ESPHOME_LOG_TAG(TAG, "bl0939");
8
9// https://www.belling.com.cn/media/file_object/bel_product/BL0939/datasheet/BL0939_V1.2_cn.pdf
10// (unfortunately chinese, but the protocol can be understood with some translation tool)
11static const uint8_t BL0939_READ_COMMAND = 0x55; // 0x5{A4,A3,A2,A1}
12static const uint8_t BL0939_FULL_PACKET = 0xAA;
13static const uint8_t BL0939_PACKET_HEADER = 0x55;
14
15static const uint8_t BL0939_WRITE_COMMAND = 0xA5; // 0xA{A4,A3,A2,A1}
16static const uint8_t BL0939_REG_IA_FAST_RMS_CTRL = 0x10;
17static const uint8_t BL0939_REG_IB_FAST_RMS_CTRL = 0x1E;
18static const uint8_t BL0939_REG_MODE = 0x18;
19static const uint8_t BL0939_REG_SOFT_RESET = 0x19;
20static const uint8_t BL0939_REG_USR_WRPROT = 0x1A;
21static const uint8_t BL0939_REG_TPS_CTRL = 0x1B;
22
23const uint8_t BL0939_INIT[6][6] = {
24 // Reset to default
25 {BL0939_WRITE_COMMAND, BL0939_REG_SOFT_RESET, 0x5A, 0x5A, 0x5A, 0x33},
26 // Enable User Operation Write
27 {BL0939_WRITE_COMMAND, BL0939_REG_USR_WRPROT, 0x55, 0x00, 0x00, 0xEB},
28 // 0x0100 = CF_UNABLE energy pulse, AC_FREQ_SEL 50Hz, RMS_UPDATE_SEL 800mS
29 {BL0939_WRITE_COMMAND, BL0939_REG_MODE, 0x00, 0x10, 0x00, 0x32},
30 // 0x47FF = Over-current and leakage alarm on, Automatic temperature measurement, Interval 100mS
31 {BL0939_WRITE_COMMAND, BL0939_REG_TPS_CTRL, 0xFF, 0x47, 0x00, 0xF9},
32 // 0x181C = Half cycle, Fast RMS threshold 6172
33 {BL0939_WRITE_COMMAND, BL0939_REG_IA_FAST_RMS_CTRL, 0x1C, 0x18, 0x00, 0x16},
34 // 0x181C = Half cycle, Fast RMS threshold 6172
35 {BL0939_WRITE_COMMAND, BL0939_REG_IB_FAST_RMS_CTRL, 0x1C, 0x18, 0x00, 0x08}};
36
39 if (!this->available()) {
40 return;
41 }
42 if (read_array((uint8_t *) &buffer, sizeof(buffer))) {
45 }
46 } else {
47 ESP_LOGW(TAG, "Junk on wire. Throwing away partial message");
48 while (read() >= 0)
49 ;
50 }
51}
52
54 // Whole package but checksum
55 const uint8_t checksum = sum_bytes(data->raw, sizeof(data->raw) - 1, BL0939_READ_COMMAND) ^ 0xFF;
56 if (checksum != data->checksum) {
57 ESP_LOGW(TAG, "BL0939 invalid checksum! 0x%02X != 0x%02X", checksum, data->checksum);
58 }
59 return checksum == data->checksum;
60}
61
63 this->flush();
64 this->write_byte(BL0939_READ_COMMAND);
65 this->write_byte(BL0939_FULL_PACKET);
66}
67
69 for (auto *i : BL0939_INIT) {
70 this->write_array(i, 6);
71 delay(1);
72 }
73 this->flush();
74}
75
76void BL0939::received_package_(const DataPacket *data) const {
77 // Bad header
78 if (data->frame_header != BL0939_PACKET_HEADER) {
79 ESP_LOGI(TAG, "Invalid data. Header mismatch: %d", data->frame_header);
80 return;
81 }
82
83 float v_rms = (float) to_uint32_t(data->v_rms) / voltage_reference_;
84 float ia_rms = (float) to_uint32_t(data->ia_rms) / current_reference_;
85 float ib_rms = (float) to_uint32_t(data->ib_rms) / current_reference_;
86 float a_watt = (float) to_int32_t(data->a_watt) / power_reference_;
87 float b_watt = (float) to_int32_t(data->b_watt) / power_reference_;
88 int32_t cfa_cnt = to_int32_t(data->cfa_cnt);
89 int32_t cfb_cnt = to_int32_t(data->cfb_cnt);
90 float a_energy_consumption = (float) cfa_cnt / energy_reference_;
91 float b_energy_consumption = (float) cfb_cnt / energy_reference_;
92 float total_energy_consumption = a_energy_consumption + b_energy_consumption;
93
94 if (voltage_sensor_ != nullptr) {
96 }
97 if (current_sensor_1_ != nullptr) {
99 }
100 if (current_sensor_2_ != nullptr) {
102 }
103 if (power_sensor_1_ != nullptr) {
105 }
106 if (power_sensor_2_ != nullptr) {
108 }
109 if (energy_sensor_1_ != nullptr) {
110 energy_sensor_1_->publish_state(a_energy_consumption);
111 }
112 if (energy_sensor_2_ != nullptr) {
113 energy_sensor_2_->publish_state(b_energy_consumption);
114 }
115 if (energy_sensor_sum_ != nullptr) {
116 energy_sensor_sum_->publish_state(total_energy_consumption);
117 }
118
119 if (this->internal_temperature_sensor_ != nullptr) {
120 const uint16_t raw_temperature = ((uint16_t(data->tps1.h) << 8) | data->tps1.l) & 0x3FF;
121 // Datasheet temperature conversion: Tx = (170 / 448) * (TB / 2 - 32) - 45.
122 const float temperature = (170.0f / 448.0f) * (raw_temperature / 2.0f - 32.0f) - 45.0f;
124 }
125
126 ESP_LOGV(TAG,
127 "BL0939: U %fV, I1 %fA, I2 %fA, P1 %fW, P2 %fW, CntA %" PRId32 ", CntB %" PRId32 ", ∫P1 %fkWh, ∫P2 %fkWh",
128 v_rms, ia_rms, ib_rms, a_watt, b_watt, cfa_cnt, cfb_cnt, a_energy_consumption, b_energy_consumption);
129}
130
131void BL0939::dump_config() { // NOLINT(readability-function-cognitive-complexity)
132 ESP_LOGCONFIG(TAG,
133 "BL0939:\n"
134 " Mode: %s",
135 this->mode_ == BL0939Mode::BL0939_MODE_CURRENT_TRANSFORMER ? LOG_STR_LITERAL("current_transformer")
136 : LOG_STR_LITERAL("direct"));
137 LOG_SENSOR("", "Voltage", this->voltage_sensor_);
138 LOG_SENSOR("", "Current 1", this->current_sensor_1_);
139 LOG_SENSOR("", "Current 2", this->current_sensor_2_);
140 LOG_SENSOR("", "Power 1", this->power_sensor_1_);
141 LOG_SENSOR("", "Power 2", this->power_sensor_2_);
142 LOG_SENSOR("", "Energy 1", this->energy_sensor_1_);
143 LOG_SENSOR("", "Energy 2", this->energy_sensor_2_);
144 LOG_SENSOR("", "Energy sum", this->energy_sensor_sum_);
145 LOG_SENSOR("", "Internal Temperature", this->internal_temperature_sensor_);
146}
147
148uint32_t BL0939::to_uint32_t(ube24_t input) { return input.h << 16 | input.m << 8 | input.l; }
149
150int32_t BL0939::to_int32_t(sbe24_t input) { return input.h << 16 | input.m << 8 | input.l; }
151
152} // namespace esphome::bl0939
uint8_t checksum
Definition bl0906.h:3
sbe24_t cfb_cnt
Definition bl0939.h:11
sbe24_t a_watt
Definition bl0939.h:8
sbe24_t cfa_cnt
Definition bl0939.h:10
ube24_t ia_rms
Definition bl0939.h:4
sbe24_t b_watt
Definition bl0939.h:9
ube24_t v_rms
Definition bl0939.h:6
ube24_t ib_rms
Definition bl0939.h:5
void update() override
Definition bl0939.cpp:62
sensor::Sensor * internal_temperature_sensor_
Definition bl0939.h:116
static int32_t to_int32_t(sbe24_t input)
Definition bl0939.cpp:150
void setup() override
Definition bl0939.cpp:68
void loop() override
Definition bl0939.cpp:37
sensor::Sensor * energy_sensor_sum_
Definition bl0939.h:115
sensor::Sensor * voltage_sensor_
Definition bl0939.h:106
static bool validate_checksum(const DataPacket *data)
Definition bl0939.cpp:53
sensor::Sensor * current_sensor_2_
Definition bl0939.h:108
sensor::Sensor * energy_sensor_2_
Definition bl0939.h:114
sensor::Sensor * power_sensor_1_
Definition bl0939.h:111
static uint32_t to_uint32_t(ube24_t input)
Definition bl0939.cpp:148
void received_package_(const DataPacket *data) const
Definition bl0939.cpp:76
sensor::Sensor * energy_sensor_1_
Definition bl0939.h:113
void dump_config() override
Definition bl0939.cpp:131
sensor::Sensor * power_sensor_2_
Definition bl0939.h:112
sensor::Sensor * current_sensor_1_
Definition bl0939.h:107
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 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
ESPHOME_LOG_TAG(TAG, "bl0939")
const uint8_t BL0939_INIT[6][6]
Definition bl0939.cpp:23
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
uint16_t temperature
Definition sun_gtil2.cpp:12