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