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