ESPHome 2026.11.0-dev
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
9ESPHOME_LOG_TAG(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
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 // Whole package but checksum
74 const uint8_t checksum =
75 sum_bytes(reinterpret_cast<const uint8_t *>(data), sizeof(*data) - 1, BL0942_READ_COMMAND | this->address_) ^
76 0xFF;
77 if (checksum != data->checksum) {
78 ESP_LOGW(TAG, "BL0942 invalid checksum! 0x%02X != 0x%02X", checksum, data->checksum);
79 }
80 return checksum == data->checksum;
81}
82
83void BL0942::write_reg_(uint8_t reg, uint32_t val) {
84 uint8_t pkt[6];
85
86 this->flush();
87 pkt[0] = BL0942_WRITE_COMMAND | this->address_;
88 pkt[1] = reg;
89 pkt[2] = (val & 0xff);
90 pkt[3] = (val >> 8) & 0xff;
91 pkt[4] = (val >> 16) & 0xff;
92 pkt[5] = (pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4]) ^ 0xff;
93 this->write_array(pkt, 6);
94 delay(1);
95}
96
97int BL0942::read_reg_(uint8_t reg) {
98 union {
99 uint8_t b[4];
100 uint32_le_t le32;
101 } resp;
102
103 this->write_byte(BL0942_READ_COMMAND | this->address_);
104 this->write_byte(reg);
105 this->flush();
106 if (this->read_array(resp.b, 4) &&
107 resp.b[3] ==
108 (uint8_t) ((BL0942_READ_COMMAND + this->address_ + reg + resp.b[0] + resp.b[1] + resp.b[2]) ^ 0xff)) {
109 resp.b[3] = 0;
110 return resp.le32;
111 }
112 return -1;
113}
114
116 this->write_byte(BL0942_READ_COMMAND | this->address_);
117 this->write_byte(BL0942_FULL_PACKET);
118}
119
121 // If either current or voltage references are set explicitly by the user,
122 // calculate the power reference from it unless that is also explicitly set.
124 this->power_reference_ = (this->voltage_reference_ * this->current_reference_ * 3537.0f / 305978.0f) / 73989.0f;
125 this->power_reference_set_ = true;
126 }
127
128 // Similarly for energy reference, if the power reference was set by the user
129 // either implicitly or explicitly.
130 if (this->power_reference_set_ && !this->energy_reference_set_) {
131 this->energy_reference_ = this->power_reference_ * 3600000 / 419430.4f;
132 this->energy_reference_set_ = true;
133 }
134
135 this->write_reg_(BL0942_REG_USR_WRPROT, BL0942_REG_USR_WRPROT_MAGIC);
136 if (this->reset_)
137 this->write_reg_(BL0942_REG_SOFT_RESET, BL0942_REG_SOFT_RESET_MAGIC);
138
139 uint32_t mode = BL0942_REG_MODE_DEFAULT;
140 mode |= BL0942_REG_MODE_RMS_UPDATE_SEL; /* 800ms refresh time */
141 if (this->line_freq_ == LINE_FREQUENCY_60HZ)
142 mode |= BL0942_REG_MODE_AC_FREQ_SEL;
143 this->write_reg_(BL0942_REG_MODE, mode);
144
145 this->write_reg_(BL0942_REG_USR_WRPROT, 0);
146
147 if (static_cast<uint32_t>(this->read_reg_(BL0942_REG_MODE)) != mode)
148 this->status_set_warning(LOG_STR("BL0942 setup failed!"));
149
150 this->flush();
151}
152
154 // Bad header
155 if (data->frame_header != BL0942_PACKET_HEADER) {
156 ESP_LOGI(TAG, "Invalid data. Header mismatch: %d", data->frame_header);
157 return;
158 }
159
160 // cf_cnt wraps at 24 bits; total_increasing on the energy sensor handles the
161 // wrap (and any spurious chip resets) downstream.
162 uint32_t cf_cnt = (uint24_t) data->cf_cnt;
163
164 float v_rms = (uint24_t) data->v_rms / voltage_reference_;
165 float i_rms = (uint24_t) data->i_rms / current_reference_;
166 float watt = (int24_t) data->watt / power_reference_;
167 float total_energy_consumption = cf_cnt / energy_reference_;
168 float frequency = data->frequency != 0 ? 1000000.0f / data->frequency : NAN;
169
170 if (voltage_sensor_ != nullptr) {
172 }
173 if (current_sensor_ != nullptr) {
175 }
176 if (power_sensor_ != nullptr) {
178 }
179 if (energy_sensor_ != nullptr) {
180 energy_sensor_->publish_state(total_energy_consumption);
181 }
182 if (frequency_sensor_ != nullptr) {
184 }
185 this->status_clear_warning();
186 ESP_LOGV(TAG, "BL0942: U %fV, I %fA, P %fW, Cnt %" PRId32 ", ∫P %fkWh, frequency %fHz, status 0x%08X", v_rms, i_rms,
187 watt, cf_cnt, total_energy_consumption, frequency, data->status);
188}
189
190void BL0942::dump_config() { // NOLINT(readability-function-cognitive-complexity)
191 ESP_LOGCONFIG(TAG,
192 "BL0942:\n"
193 " Reset: %s\n"
194 " Address: %d\n"
195 " Nominal line frequency: %d Hz\n"
196 " Current reference: %f\n"
197 " Energy reference: %f\n"
198 " Power reference: %f\n"
199 " Voltage reference: %f",
200 TRUEFALSE(this->reset_), this->address_, this->line_freq_, this->current_reference_,
202 LOG_SENSOR("", "Voltage", this->voltage_sensor_);
203 LOG_SENSOR("", "Current", this->current_sensor_);
204 LOG_SENSOR("", "Power", this->power_sensor_);
205 LOG_SENSOR("", "Energy", this->energy_sensor_);
206 LOG_SENSOR("", "Frequency", this->frequency_sensor_);
207}
208
209} // namespace esphome::bl0942
BedjetMode mode
BedJet operating mode.
uint8_t checksum
Definition bl0906.h:3
ube24_t v_rms
Definition bl0939.h:6
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:296
sensor::Sensor * frequency_sensor_
Definition bl0942.h:125
sensor::Sensor * energy_sensor_
Definition bl0942.h:124
void update() override
Definition bl0942.cpp:115
void write_reg_(uint8_t reg, uint32_t val)
Definition bl0942.cpp:83
int read_reg_(uint8_t reg)
Definition bl0942.cpp:97
sensor::Sensor * current_sensor_
Definition bl0942.h:120
void setup() override
Definition bl0942.cpp:120
optional< uint32_t > rx_start_
Definition bl0942.h:142
void received_package_(DataPacket *data)
Definition bl0942.cpp:153
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:190
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: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
mopeka_std_values val[3]
@ LINE_FREQUENCY_60HZ
Definition bl0942.h:83
ESPHOME_LOG_TAG(TAG, "bl0942")
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
uint32_t IRAM_ATTR HOT millis()
Definition hal.cpp:25
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