ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
bl0939.h
Go to the documentation of this file.
1#pragma once
7namespace esphome::bl0939 {
9// https://datasheet.lcsc.com/lcsc/2108071830_BL-Shanghai-Belling-BL0939_C2841044.pdf
10// (unfortunately chinese, but the formulas can be easily understood)
11// The datasheet has one set of formulas for direct mode (current shunt) and one for current transformer mode.
13// Direct mode.
14// Sonoff Dual R3 V2 has the exact same resistor values for the current shunts (RL=1miliOhm)
15// and for the voltage divider (R1=0.51kOhm, R2=5*390kOhm)
16// as in the manufacturer's reference circuit, so the same formulas were used here (Vref=1.218V)
17static constexpr float BL0939_IREF = 324004 * 1 / 1.218;
18static constexpr float BL0939_UREF = 79931 * 0.51 * 1000 / (1.218 * (5 * 390 + 0.51));
19static constexpr float BL0939_PREF = 4046 * 1 * 0.51 * 1000 / (1.218 * 1.218 * (5 * 390 + 0.51));
20static constexpr float BL0939_EREF = 3.6e6 * 4046 * 1 * 0.51 * 1000 / (1638.4 * 256 * 1.218 * 1.218 * (5 * 390 + 0.51));
21
22// Current transformer mode.
23// Values from the Seeed Studio XIAO 2-Channel Wi-Fi AC Energy Meter V1.0 schematic (Vref=1.218V).
24// Current: 100A:50mA CT (2000:1) into a 0.6Ohm burden (2 * 0.3Ohm), so 0.3mV per primary amp.
25// That is the same as a 0.3mOhm shunt in the direct mode formula.
26// Voltage: ZMPT107-1 2mA:2mA voltage transformer with R2=5*22kOhm on the primary and R1=24.9Ohm on
27// the secondary. The transformer isolates R1 from R2, so the divider is R1/R2 instead of R1/(R1+R2).
28static constexpr float BL0939_IREF_CT = 324004 * 0.3 / 1.218;
29static constexpr float BL0939_UREF_CT = 79931 * 24.9 * 1000 / (1.218 * 5 * 22000);
30static constexpr float BL0939_PREF_CT = 4046 * 0.3 * 24.9 * 1000 / (1.218 * 1.218 * 5 * 22000);
31static constexpr float BL0939_EREF_CT = 3.6e6 * 4046 * 0.3 * 24.9 * 1000 / (1638.4 * 256 * 1.218 * 1.218 * 5 * 22000);
32
33enum class BL0939Mode : uint8_t {
36};
37
38struct ube24_t { // NOLINT(readability-identifier-naming,altera-struct-pack-align)
39 uint8_t l;
40 uint8_t m;
41 uint8_t h;
42} __attribute__((packed));
43
44struct ube16_t { // NOLINT(readability-identifier-naming,altera-struct-pack-align)
45 uint8_t l;
46 uint8_t h;
47} __attribute__((packed));
48
49struct sbe24_t { // NOLINT(readability-identifier-naming,altera-struct-pack-align)
50 uint8_t l;
51 uint8_t m;
52 int8_t h;
53} __attribute__((packed));
54
55// Caveat: All these values are big endian (low - middle - high)
56
57union DataPacket { // NOLINT(altera-struct-pack-align)
58 uint8_t raw[35];
59 struct {
60 uint8_t frame_header; // 0x55 according to docs
71 uint8_t RESERVED1; // value of 0x00
73 uint8_t RESERVED2; // value of 0x00
74 uint8_t checksum; // checksum
75 };
76} __attribute__((packed));
77
78class BL0939 final : public PollingComponent, public uart::UARTDevice {
79 public:
80 void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
81 void set_current_sensor_1(sensor::Sensor *current_sensor_1) { current_sensor_1_ = current_sensor_1; }
82 void set_current_sensor_2(sensor::Sensor *current_sensor_2) { current_sensor_2_ = current_sensor_2; }
83 void set_power_sensor_1(sensor::Sensor *power_sensor_1) { power_sensor_1_ = power_sensor_1; }
84 void set_power_sensor_2(sensor::Sensor *power_sensor_2) { power_sensor_2_ = power_sensor_2; }
85 void set_energy_sensor_1(sensor::Sensor *energy_sensor_1) { energy_sensor_1_ = energy_sensor_1; }
86 void set_energy_sensor_2(sensor::Sensor *energy_sensor_2) { energy_sensor_2_ = energy_sensor_2; }
87 void set_energy_sensor_sum(sensor::Sensor *energy_sensor_sum) { energy_sensor_sum_ = energy_sensor_sum; }
89 // Inline so a constant mode from codegen folds down to plain stores.
91 this->mode_ = mode;
92 const bool current_transformer = mode == BL0939Mode::BL0939_MODE_CURRENT_TRANSFORMER;
93 this->power_reference_ = current_transformer ? BL0939_PREF_CT : BL0939_PREF;
94 this->voltage_reference_ = current_transformer ? BL0939_UREF_CT : BL0939_UREF;
95 this->current_reference_ = current_transformer ? BL0939_IREF_CT : BL0939_IREF;
96 this->energy_reference_ = current_transformer ? BL0939_EREF_CT : BL0939_EREF;
97 }
98
99 void loop() override;
100
101 void update() override;
102 void setup() override;
103 void dump_config() override;
104
105 protected:
109 // NB This may be negative as the circuits is seemingly able to measure
110 // power in both directions
117
118 // Divide by this to turn into Watt
119 float power_reference_ = BL0939_PREF;
120 // Divide by this to turn into Volt
121 float voltage_reference_ = BL0939_UREF;
122 // Divide by this to turn into Ampere
123 float current_reference_ = BL0939_IREF;
124 // Divide by this to turn into kWh
125 float energy_reference_ = BL0939_EREF;
127
128 static uint32_t to_uint32_t(ube24_t input);
129
130 static int32_t to_int32_t(sbe24_t input);
131
132 static bool validate_checksum(const DataPacket *data);
133
134 void received_package_(const DataPacket *data) const;
136} // namespace esphome::bl0939
BedjetMode mode
BedJet operating mode.
This class simplifies creating components that periodically check a state.
Definition component.h:519
void update() override
Definition bl0939.cpp:62
sensor::Sensor * internal_temperature_sensor_
Definition bl0939.h:116
void set_power_sensor_2(sensor::Sensor *power_sensor_2)
Definition bl0939.h:84
void set_current_sensor_2(sensor::Sensor *current_sensor_2)
Definition bl0939.h:82
void set_energy_sensor_1(sensor::Sensor *energy_sensor_1)
Definition bl0939.h:85
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
void set_power_sensor_1(sensor::Sensor *power_sensor_1)
Definition bl0939.h:83
void set_energy_sensor_2(sensor::Sensor *energy_sensor_2)
Definition bl0939.h:86
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
void set_internal_temperature_sensor(sensor::Sensor *sensor)
Definition bl0939.h:88
sensor::Sensor * energy_sensor_2_
Definition bl0939.h:114
void set_voltage_sensor(sensor::Sensor *voltage_sensor)
Definition bl0939.h:80
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
void set_energy_sensor_sum(sensor::Sensor *energy_sensor_sum)
Definition bl0939.h:87
void set_mode(BL0939Mode mode)
Definition bl0939.h:90
sensor::Sensor * energy_sensor_1_
Definition bl0939.h:113
void dump_config() override
Definition bl0939.cpp:131
void set_current_sensor_1(sensor::Sensor *current_sensor_1)
Definition bl0939.h:81
sensor::Sensor * power_sensor_2_
Definition bl0939.h:112
sensor::Sensor * current_sensor_1_
Definition bl0939.h:107
Base-class for all sensors.
Definition sensor.h:47
esphome::bl0939::BL0939 __attribute__
static void uint32_t