ESPHome 2026.6.0-dev
Loading...
Searching...
No Matches
bl0942.h
Go to the documentation of this file.
1#pragma once
8namespace esphome::bl0942 {
10// The BL0942 IC is "calibration-free", which means that it doesn't care
11// at all about calibration, and that's left to software. It measures a
12// voltage differential on its IP/IN pins which linearly proportional to
13// the current flow, and another on its VP pin which is proportional to
14// the line voltage. It never knows the actual calibration; the values
15// it reports are solely in terms of those inputs.
16//
17// The datasheet refers to the input voltages as I(A) and V(V), both
18// in millivolts. It measures them against a reference voltage Vref,
19// which is typically 1.218V (but that absolute value is meaningless
20// without the actual calibration anyway).
21//
22// The reported I_RMS value is 305978 I(A)/Vref, and the reported V_RMS
23// value is 73989 V(V)/Vref. So we can calibrate those by applying a
24// simple meter with a resistive load.
25//
26// The chip also measures the phase difference between voltage and
27// current, and uses it to calculate the power factor (cos φ). It
28// reports the WATT value of 3537 * I_RMS * V_RMS * cos φ).
29//
30// It also integrates total energy based on the WATT value. The time for
31// one CF_CNT pulse is 1638.4*256 / WATT.
32//
33// So... how do we calibrate that?
34//
35// Using a simple resistive load and an external meter, we can measure
36// the true voltage and current for a given V_RMS and I_RMS reading,
37// to calculate BL0942_UREF and BL0942_IREF. Those are in units of
38// "305978 counts per amp" or "73989 counts per volt" respectively.
39//
40// We can derive BL0942_PREF from those. Let's eliminate the weird
41// factors and express the calibration in plain counts per volt/amp:
42// UREF1 = UREF/73989, IREF1 = IREF/305978.
43//
44// Next... the true power in Watts is V * I * cos φ, so that's equal
45// to WATT/3537 * IREF1 * UREF1. Which means
46// BL0942_PREF = BL0942_UREF * BL0942_IREF * 3537 / 305978 / 73989.
47//
48// Finally the accumulated energy. The period of a CF_CNT count is
49// 1638.4*256 / WATT seconds, or 419230.4 / WATT seconds. Which means
50// the energy represented by a CN_CNT pulse is 419230.4 WATT-seconds.
51// Factoring in the calibration, that's 419230.4 / BL0942_PREF actual
52// Watt-seconds (or Joules, as the physicists like to call them).
53//
54// But we're not being physicists today; we we're being engineers, so
55// we want to convert to kWh instead. Which we do by dividing by 1000
56// and then by 3600, so the energy in kWh is
57// CF_CNT * 419230.4 / BL0942_PREF / 3600000
58//
59// Which makes BL0952_EREF = BL0942_PREF * 3600000 / 419430.4
60
61static const float BL0942_PREF = 623.0270705; // calculated using UREF and IREF
62static const float BL0942_UREF = 15883.34116; // calculated for (390k x 5 / 510R) voltage divider
63static const float BL0942_IREF = 251065.6814; // calculated for 1mR shunt
64static const float BL0942_EREF = 5347.484240; // calculated using UREF and IREF
65
80
85
87 public:
88 void set_voltage_sensor(sensor::Sensor *voltage_sensor) { this->voltage_sensor_ = voltage_sensor; }
89 void set_current_sensor(sensor::Sensor *current_sensor) { this->current_sensor_ = current_sensor; }
90 void set_power_sensor(sensor::Sensor *power_sensor) { this->power_sensor_ = power_sensor; }
91 void set_energy_sensor(sensor::Sensor *energy_sensor) { this->energy_sensor_ = energy_sensor; }
92 void set_frequency_sensor(sensor::Sensor *frequency_sensor) { this->frequency_sensor_ = frequency_sensor; }
93 void set_line_freq(LineFrequency freq) { this->line_freq_ = freq; }
94 void set_address(uint8_t address) { this->address_ = address; }
95 void set_reset(bool reset) { this->reset_ = reset; }
96 void set_current_reference(float current_ref) {
97 this->current_reference_ = current_ref;
98 this->current_reference_set_ = true;
99 }
100 void set_energy_reference(float energy_ref) {
101 this->energy_reference_ = energy_ref;
102 this->energy_reference_set_ = true;
103 }
104 void set_power_reference(float power_ref) {
105 this->power_reference_ = power_ref;
106 this->power_reference_set_ = true;
107 }
108 void set_voltage_reference(float voltage_ref) {
109 this->voltage_reference_ = voltage_ref;
110 this->voltage_reference_set_ = true;
111 }
112
113 void loop() override;
114 void update() override;
115 void setup() override;
116 void dump_config() override;
117
118 protected:
121 // NB This may be negative as the circuits is seemingly able to measure
122 // power in both directions
126
127 // Divide by this to turn into Watt
128 float power_reference_ = BL0942_PREF;
130 // Divide by this to turn into Volt
131 float voltage_reference_ = BL0942_UREF;
133 // Divide by this to turn into Ampere
134 float current_reference_ = BL0942_IREF;
136 // Divide by this to turn into kWh
137 float energy_reference_ = BL0942_EREF;
139 uint8_t address_ = 0;
140 bool reset_ = false;
142 optional<uint32_t> rx_start_{};
143
144 bool validate_checksum_(DataPacket *data);
145 int read_reg_(uint8_t reg);
146 void write_reg_(uint8_t reg, uint32_t val);
147 void received_package_(DataPacket *data);
148};
149} // namespace esphome::bl0942
uint8_t address
Definition bl0906.h:4
This class simplifies creating components that periodically check a state.
Definition component.h:585
sensor::Sensor * frequency_sensor_
Definition bl0942.h:125
sensor::Sensor * energy_sensor_
Definition bl0942.h:124
void set_frequency_sensor(sensor::Sensor *frequency_sensor)
Definition bl0942.h:92
void update() override
Definition bl0942.cpp:118
void write_reg_(uint8_t reg, uint32_t val)
Definition bl0942.cpp:86
void set_power_reference(float power_ref)
Definition bl0942.h:104
int read_reg_(uint8_t reg)
Definition bl0942.cpp:100
void set_power_sensor(sensor::Sensor *power_sensor)
Definition bl0942.h:90
void set_energy_sensor(sensor::Sensor *energy_sensor)
Definition bl0942.h:91
void set_line_freq(LineFrequency freq)
Definition bl0942.h:93
sensor::Sensor * current_sensor_
Definition bl0942.h:120
void set_voltage_reference(float voltage_ref)
Definition bl0942.h:108
void set_reset(bool reset)
Definition bl0942.h:95
void setup() override
Definition bl0942.cpp:123
void set_current_sensor(sensor::Sensor *current_sensor)
Definition bl0942.h:89
void set_current_reference(float current_ref)
Definition bl0942.h:96
void set_energy_reference(float energy_ref)
Definition bl0942.h:100
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
void set_voltage_sensor(sensor::Sensor *voltage_sensor)
Definition bl0942.h:88
void set_address(uint8_t address)
Definition bl0942.h:94
LineFrequency line_freq_
Definition bl0942.h:141
bool validate_checksum_(DataPacket *data)
Definition bl0942.cpp:72
void dump_config() override
Definition bl0942.cpp:193
Base-class for all sensors.
Definition sensor.h:47
uint16_t reset
Definition ina226.h:5
mopeka_std_values val[3]
@ LINE_FREQUENCY_50HZ
Definition bl0942.h:82
@ LINE_FREQUENCY_60HZ
Definition bl0942.h:83
enum esphome::bl0942::LineFrequency __attribute__
static void uint32_t