ESPHome  2024.6.1
bl0940.cpp
Go to the documentation of this file.
1 #include "bl0940.h"
2 #include "esphome/core/log.h"
3 #include <cinttypes>
4 
5 namespace esphome {
6 namespace bl0940 {
7 
8 static const char *const TAG = "bl0940";
9 
10 static const uint8_t BL0940_READ_COMMAND = 0x50; // 0x58 according to documentation
11 static const uint8_t BL0940_FULL_PACKET = 0xAA;
12 static const uint8_t BL0940_PACKET_HEADER = 0x55; // 0x58 according to documentation
13 
14 static const uint8_t BL0940_WRITE_COMMAND = 0xA0; // 0xA8 according to documentation
15 static const uint8_t BL0940_REG_I_FAST_RMS_CTRL = 0x10;
16 static const uint8_t BL0940_REG_MODE = 0x18;
17 static const uint8_t BL0940_REG_SOFT_RESET = 0x19;
18 static const uint8_t BL0940_REG_USR_WRPROT = 0x1A;
19 static const uint8_t BL0940_REG_TPS_CTRL = 0x1B;
20 
21 const uint8_t BL0940_INIT[5][6] = {
22  // Reset to default
23  {BL0940_WRITE_COMMAND, BL0940_REG_SOFT_RESET, 0x5A, 0x5A, 0x5A, 0x38},
24  // Enable User Operation Write
25  {BL0940_WRITE_COMMAND, BL0940_REG_USR_WRPROT, 0x55, 0x00, 0x00, 0xF0},
26  // 0x0100 = CF_UNABLE energy pulse, AC_FREQ_SEL 50Hz, RMS_UPDATE_SEL 800mS
27  {BL0940_WRITE_COMMAND, BL0940_REG_MODE, 0x00, 0x10, 0x00, 0x37},
28  // 0x47FF = Over-current and leakage alarm on, Automatic temperature measurement, Interval 100mS
29  {BL0940_WRITE_COMMAND, BL0940_REG_TPS_CTRL, 0xFF, 0x47, 0x00, 0xFE},
30  // 0x181C = Half cycle, Fast RMS threshold 6172
31  {BL0940_WRITE_COMMAND, BL0940_REG_I_FAST_RMS_CTRL, 0x1C, 0x18, 0x00, 0x1B}};
32 
33 void BL0940::loop() {
34  DataPacket buffer;
35  if (!this->available()) {
36  return;
37  }
38  if (read_array((uint8_t *) &buffer, sizeof(buffer))) {
39  if (validate_checksum(&buffer)) {
40  received_package_(&buffer);
41  }
42  } else {
43  ESP_LOGW(TAG, "Junk on wire. Throwing away partial message");
44  while (read() >= 0)
45  ;
46  }
47 }
48 
50  uint8_t checksum = BL0940_READ_COMMAND;
51  // Whole package but checksum
52  for (uint32_t i = 0; i < sizeof(data->raw) - 1; i++) {
53  checksum += data->raw[i];
54  }
55  checksum ^= 0xFF;
56  if (checksum != data->checksum) {
57  ESP_LOGW(TAG, "BL0940 invalid checksum! 0x%02X != 0x%02X", checksum, data->checksum);
58  }
59  return checksum == data->checksum;
60 }
61 
63  this->flush();
64  this->write_byte(BL0940_READ_COMMAND);
65  this->write_byte(BL0940_FULL_PACKET);
66 }
67 
68 void BL0940::setup() {
69  for (auto *i : BL0940_INIT) {
70  this->write_array(i, 6);
71  delay(1);
72  }
73  this->flush();
74 }
75 
77  auto tb = (float) (temperature.h << 8 | temperature.l);
78  float converted_temp = ((float) 170 / 448) * (tb / 2 - 32) - 45;
79  if (sensor != nullptr) {
80  if (sensor->has_state() && std::abs(converted_temp - sensor->get_state()) > max_temperature_diff_) {
81  ESP_LOGD(TAG, "Invalid temperature change. Sensor: '%s', Old temperature: %f, New temperature: %f",
82  sensor->get_name().c_str(), sensor->get_state(), converted_temp);
83  return 0.0f;
84  }
85  sensor->publish_state(converted_temp);
86  }
87  return converted_temp;
88 }
89 
90 void BL0940::received_package_(const DataPacket *data) const {
91  // Bad header
92  if (data->frame_header != BL0940_PACKET_HEADER) {
93  ESP_LOGI(TAG, "Invalid data. Header mismatch: %d", data->frame_header);
94  return;
95  }
96 
97  float v_rms = (float) to_uint32_t(data->v_rms) / voltage_reference_;
98  float i_rms = (float) to_uint32_t(data->i_rms) / current_reference_;
99  float watt = (float) to_int32_t(data->watt) / power_reference_;
100  uint32_t cf_cnt = to_uint32_t(data->cf_cnt);
101  float total_energy_consumption = (float) cf_cnt / energy_reference_;
102 
105 
106  if (voltage_sensor_ != nullptr) {
108  }
109  if (current_sensor_ != nullptr) {
111  }
112  if (power_sensor_ != nullptr) {
114  }
115  if (energy_sensor_ != nullptr) {
116  energy_sensor_->publish_state(total_energy_consumption);
117  }
118 
119  ESP_LOGV(TAG, "BL0940: U %fV, I %fA, P %fW, Cnt %" PRId32 ", ∫P %fkWh, T1 %f°C, T2 %f°C", v_rms, i_rms, watt, cf_cnt,
120  total_energy_consumption, tps1, tps2);
121 }
122 
123 void BL0940::dump_config() { // NOLINT(readability-function-cognitive-complexity)
124  ESP_LOGCONFIG(TAG, "BL0940:");
125  LOG_SENSOR("", "Voltage", this->voltage_sensor_);
126  LOG_SENSOR("", "Current", this->current_sensor_);
127  LOG_SENSOR("", "Power", this->power_sensor_);
128  LOG_SENSOR("", "Energy", this->energy_sensor_);
129  LOG_SENSOR("", "Internal temperature", this->internal_temperature_sensor_);
130  LOG_SENSOR("", "External temperature", this->external_temperature_sensor_);
131 }
132 
133 uint32_t BL0940::to_uint32_t(ube24_t input) { return input.h << 16 | input.m << 8 | input.l; }
134 
135 int32_t BL0940::to_int32_t(sbe24_t input) { return input.h << 16 | input.m << 8 | input.l; }
136 
137 } // namespace bl0940
138 } // namespace esphome
ube24_t i_rms
Definition: bl0940.h:21
float current_reference_
Definition: bl0940.h:94
ube24_t cf_cnt
Definition: bl0940.h:27
optional< std::array< uint8_t, N > > read_array()
Definition: uart.h:33
void write_array(const uint8_t *data, size_t len)
Definition: uart.h:21
static bool validate_checksum(const DataPacket *data)
Definition: bl0940.cpp:49
void write_byte(uint8_t data)
Definition: uart.h:19
sensor::Sensor * current_sensor_
Definition: bl0940.h:79
ube16_t tps1
Definition: bl0939.h:31
float update_temp_(sensor::Sensor *sensor, ube16_t packed_temperature) const
Definition: bl0940.cpp:76
static int32_t to_int32_t(sbe24_t input)
Definition: bl0940.cpp:135
void dump_config() override
Definition: bl0940.cpp:123
void received_package_(const DataPacket *data) const
Definition: bl0940.cpp:90
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:39
uint16_t temperature
Definition: sun_gtil2.cpp:26
sensor::Sensor * internal_temperature_sensor_
Definition: bl0940.h:84
void update() override
Definition: bl0940.cpp:62
uint8_t checksum
Definition: bl0939.h:35
const uint8_t BL0940_INIT[5][6]
Definition: bl0940.cpp:21
sensor::Sensor * energy_sensor_
Definition: bl0940.h:83
static uint32_t to_uint32_t(ube24_t input)
Definition: bl0940.cpp:133
constexpr const char * c_str() const
Definition: string_ref.h:68
float get_state() const
Getter-syntax for .state.
Definition: sensor.cpp:86
float voltage_reference_
Definition: bl0940.h:92
void setup() override
Definition: bl0940.cpp:68
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
bool has_state() const
Return whether this sensor has gotten a full state (that passed through all filters) yet...
Definition: sensor.cpp:97
sensor::Sensor * voltage_sensor_
Definition: bl0940.h:78
void loop() override
Definition: bl0940.cpp:33
ube16_t tps2
Definition: bl0939.h:33
Base-class for all sensors.
Definition: sensor.h:57
float max_temperature_diff_
Definition: bl0940.h:88
sensor::Sensor * power_sensor_
Definition: bl0940.h:82
sbe24_t watt
Definition: bl0940.h:25
const StringRef & get_name() const
Definition: entity_base.cpp:10
ube24_t v_rms
Definition: bl0939.h:25
sensor::Sensor * external_temperature_sensor_
Definition: bl0940.h:85
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:26