ESPHome 2025.5.2
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Modules Pages
bl0942.cpp
Go to the documentation of this file.
1#include "bl0942.h"
2#include "esphome/core/log.h"
3#include <cinttypes>
4
5// Datasheet: https://www.belling.com.cn/media/file_object/bel_product/BL0942/datasheet/BL0942_V1.06_en.pdf
6
7namespace esphome {
8namespace bl0942 {
9
10static const char *const TAG = "bl0942";
11
12static const uint8_t BL0942_READ_COMMAND = 0x58;
13static const uint8_t BL0942_FULL_PACKET = 0xAA;
14static const uint8_t BL0942_PACKET_HEADER = 0x55;
15
16static const uint8_t BL0942_WRITE_COMMAND = 0xA8;
17
18static const uint8_t BL0942_REG_I_RMSOS = 0x12;
19static const uint8_t BL0942_REG_WA_CREEP = 0x14;
20static const uint8_t BL0942_REG_I_FAST_RMS_TH = 0x15;
21static const uint8_t BL0942_REG_I_FAST_RMS_CYC = 0x16;
22static const uint8_t BL0942_REG_FREQ_CYC = 0x17;
23static const uint8_t BL0942_REG_OT_FUNX = 0x18;
24static const uint8_t BL0942_REG_MODE = 0x19;
25static const uint8_t BL0942_REG_SOFT_RESET = 0x1C;
26static const uint8_t BL0942_REG_USR_WRPROT = 0x1D;
27static const uint8_t BL0942_REG_TPS_CTRL = 0x1B;
28
29static const uint32_t BL0942_REG_MODE_RESV = 0x03;
30static const uint32_t BL0942_REG_MODE_CF_EN = 0x04;
31static const uint32_t BL0942_REG_MODE_RMS_UPDATE_SEL = 0x08;
32static const uint32_t BL0942_REG_MODE_FAST_RMS_SEL = 0x10;
33static const uint32_t BL0942_REG_MODE_AC_FREQ_SEL = 0x20;
34static const uint32_t BL0942_REG_MODE_CF_CNT_CLR_SEL = 0x40;
35static const uint32_t BL0942_REG_MODE_CF_CNT_ADD_SEL = 0x80;
36static const uint32_t BL0942_REG_MODE_UART_RATE_19200 = 0x200;
37static const uint32_t BL0942_REG_MODE_UART_RATE_38400 = 0x300;
38static const uint32_t BL0942_REG_MODE_DEFAULT =
39 BL0942_REG_MODE_RESV | BL0942_REG_MODE_CF_EN | BL0942_REG_MODE_CF_CNT_ADD_SEL;
40
41static const uint32_t BL0942_REG_SOFT_RESET_MAGIC = 0x5a5a5a;
42static const uint32_t BL0942_REG_USR_WRPROT_MAGIC = 0x55;
43
44// 23-byte packet, 11 bits per byte, 2400 baud: about 105ms
45static const uint32_t PKT_TIMEOUT_MS = 200;
46
48 DataPacket buffer;
49 int avail = this->available();
50
51 if (!avail) {
52 return;
53 }
54 if (avail < sizeof(buffer)) {
55 if (!this->rx_start_) {
56 this->rx_start_ = millis();
57 } else if (millis() > this->rx_start_ + PKT_TIMEOUT_MS) {
58 ESP_LOGW(TAG, "Junk on wire. Throwing away partial message (%d bytes)", avail);
59 this->read_array((uint8_t *) &buffer, avail);
60 this->rx_start_ = 0;
61 }
62 return;
63 }
64
65 if (this->read_array((uint8_t *) &buffer, sizeof(buffer))) {
66 if (this->validate_checksum_(&buffer)) {
67 this->received_package_(&buffer);
68 }
69 }
70 this->rx_start_ = 0;
71}
72
74 uint8_t checksum = BL0942_READ_COMMAND | this->address_;
75 // Whole package but checksum
76 uint8_t *raw = (uint8_t *) data;
77 for (uint32_t i = 0; i < sizeof(*data) - 1; i++) {
78 checksum += raw[i];
79 }
80 checksum ^= 0xFF;
81 if (checksum != data->checksum) {
82 ESP_LOGW(TAG, "BL0942 invalid checksum! 0x%02X != 0x%02X", checksum, data->checksum);
83 }
84 return checksum == data->checksum;
85}
86
87void BL0942::write_reg_(uint8_t reg, uint32_t val) {
88 uint8_t pkt[6];
89
90 this->flush();
91 pkt[0] = BL0942_WRITE_COMMAND | this->address_;
92 pkt[1] = reg;
93 pkt[2] = (val & 0xff);
94 pkt[3] = (val >> 8) & 0xff;
95 pkt[4] = (val >> 16) & 0xff;
96 pkt[5] = (pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4]) ^ 0xff;
97 this->write_array(pkt, 6);
98 delay(1);
99}
100
101int BL0942::read_reg_(uint8_t reg) {
102 union {
103 uint8_t b[4];
104 uint32_le_t le32;
105 } resp;
106
107 this->write_byte(BL0942_READ_COMMAND | this->address_);
108 this->write_byte(reg);
109 this->flush();
110 if (this->read_array(resp.b, 4) &&
111 resp.b[3] ==
112 (uint8_t) ((BL0942_READ_COMMAND + this->address_ + reg + resp.b[0] + resp.b[1] + resp.b[2]) ^ 0xff)) {
113 resp.b[3] = 0;
114 return resp.le32;
115 }
116 return -1;
117}
118
120 this->write_byte(BL0942_READ_COMMAND | this->address_);
121 this->write_byte(BL0942_FULL_PACKET);
122}
123
125 // If either current or voltage references are set explicitly by the user,
126 // calculate the power reference from it unless that is also explicitly set.
128 this->power_reference_ = (this->voltage_reference_ * this->current_reference_ * 3537.0 / 305978.0) / 73989.0;
129 this->power_reference_set_ = true;
130 }
131
132 // Similarly for energy reference, if the power reference was set by the user
133 // either implicitly or explicitly.
134 if (this->power_reference_set_ && !this->energy_reference_set_) {
135 this->energy_reference_ = this->power_reference_ * 3600000 / 419430.4;
136 this->energy_reference_set_ = true;
137 }
138
139 this->write_reg_(BL0942_REG_USR_WRPROT, BL0942_REG_USR_WRPROT_MAGIC);
140 if (this->reset_)
141 this->write_reg_(BL0942_REG_SOFT_RESET, BL0942_REG_SOFT_RESET_MAGIC);
142
143 uint32_t mode = BL0942_REG_MODE_DEFAULT;
144 mode |= BL0942_REG_MODE_RMS_UPDATE_SEL; /* 800ms refresh time */
145 if (this->line_freq_ == LINE_FREQUENCY_60HZ)
146 mode |= BL0942_REG_MODE_AC_FREQ_SEL;
147 this->write_reg_(BL0942_REG_MODE, mode);
148
149 this->write_reg_(BL0942_REG_USR_WRPROT, 0);
150
151 if (this->read_reg_(BL0942_REG_MODE) != mode)
152 this->status_set_warning("BL0942 setup failed!");
153
154 this->flush();
155}
156
158 // Bad header
159 if (data->frame_header != BL0942_PACKET_HEADER) {
160 ESP_LOGI(TAG, "Invalid data. Header mismatch: %d", data->frame_header);
161 return;
162 }
163
164 // cf_cnt is only 24 bits, so track overflows
165 uint32_t cf_cnt = (uint24_t) data->cf_cnt;
166 cf_cnt |= this->prev_cf_cnt_ & 0xff000000;
168 cf_cnt += 0x1000000;
169 }
170 this->prev_cf_cnt_ = cf_cnt;
171
172 float v_rms = (uint24_t) data->v_rms / voltage_reference_;
173 float i_rms = (uint24_t) data->i_rms / current_reference_;
174 float watt = (int24_t) data->watt / power_reference_;
175 float total_energy_consumption = cf_cnt / energy_reference_;
176 float frequency = 1000000.0f / data->frequency;
177
178 if (voltage_sensor_ != nullptr) {
180 }
181 if (current_sensor_ != nullptr) {
183 }
184 if (power_sensor_ != nullptr) {
186 }
187 if (energy_sensor_ != nullptr) {
188 energy_sensor_->publish_state(total_energy_consumption);
189 }
190 if (frequency_sensor_ != nullptr) {
192 }
193 this->status_clear_warning();
194 ESP_LOGV(TAG, "BL0942: U %fV, I %fA, P %fW, Cnt %" PRId32 ", ∫P %fkWh, frequency %fHz, status 0x%08X", v_rms, i_rms,
195 watt, cf_cnt, total_energy_consumption, frequency, data->status);
196}
197
198void BL0942::dump_config() { // NOLINT(readability-function-cognitive-complexity)
199 ESP_LOGCONFIG(TAG, "BL0942:");
200 ESP_LOGCONFIG(TAG, " Reset: %s", TRUEFALSE(this->reset_));
201 ESP_LOGCONFIG(TAG, " Address: %d", this->address_);
202 ESP_LOGCONFIG(TAG, " Nominal line frequency: %d Hz", this->line_freq_);
203 ESP_LOGCONFIG(TAG, " Current reference: %f", this->current_reference_);
204 ESP_LOGCONFIG(TAG, " Energy reference: %f", this->energy_reference_);
205 ESP_LOGCONFIG(TAG, " Power reference: %f", this->power_reference_);
206 ESP_LOGCONFIG(TAG, " Voltage reference: %f", this->voltage_reference_);
207 LOG_SENSOR("", "Voltage", this->voltage_sensor_);
208 LOG_SENSOR("", "Current", this->current_sensor_);
209 LOG_SENSOR("", "Power", this->power_sensor_);
210 LOG_SENSOR("", "Energy", this->energy_sensor_);
211 LOG_SENSOR("", "Frequency", this->frequency_sensor_);
212}
213
214} // namespace bl0942
215} // namespace esphome
BedjetMode mode
BedJet operating mode.
uint8_t checksum
Definition bl0906.h:3
ube24_t v_rms
Definition bl0939.h:6
uint8_t raw[35]
Definition bl0939.h:0
ube24_t i_rms
Definition bl0940.h:4
ube24_t cf_cnt
Definition bl0940.h:10
sbe24_t watt
Definition bl0940.h:8
uint16_le_t frequency
Definition bl0942.h:6
void status_set_warning(const char *message="unspecified")
void status_clear_warning()
sensor::Sensor * frequency_sensor_
Definition bl0942.h:126
sensor::Sensor * energy_sensor_
Definition bl0942.h:125
void update() override
Definition bl0942.cpp:119
void write_reg_(uint8_t reg, uint32_t val)
Definition bl0942.cpp:87
int read_reg_(uint8_t reg)
Definition bl0942.cpp:101
sensor::Sensor * current_sensor_
Definition bl0942.h:121
void setup() override
Definition bl0942.cpp:124
void received_package_(DataPacket *data)
Definition bl0942.cpp:157
void loop() override
Definition bl0942.cpp:47
sensor::Sensor * voltage_sensor_
Definition bl0942.h:120
sensor::Sensor * power_sensor_
Definition bl0942.h:124
LineFrequency line_freq_
Definition bl0942.h:142
bool validate_checksum_(DataPacket *data)
Definition bl0942.cpp:73
void dump_config() override
Definition bl0942.cpp:198
Wrapper class for memory using big endian data layout, transparently converting it to native order.
Definition datatypes.h:21
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:39
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
mopeka_std_values val[4]
@ LINE_FREQUENCY_60HZ
Definition bl0942.h:84
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
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:28
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