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