ESPHome  2023.5.5
cse7766.cpp
Go to the documentation of this file.
1 #include "cse7766.h"
2 #include "esphome/core/log.h"
3 
4 namespace esphome {
5 namespace cse7766 {
6 
7 static const char *const TAG = "cse7766";
8 
10  const uint32_t now = millis();
11  if (now - this->last_transmission_ >= 500) {
12  // last transmission too long ago. Reset RX index.
13  this->raw_data_index_ = 0;
14  }
15 
16  if (this->available() == 0) {
17  return;
18  }
19 
20  this->last_transmission_ = now;
21  while (this->available() != 0) {
22  this->read_byte(&this->raw_data_[this->raw_data_index_]);
23  if (!this->check_byte_()) {
24  this->raw_data_index_ = 0;
25  this->status_set_warning();
26  continue;
27  }
28 
29  if (this->raw_data_index_ == 23) {
30  this->parse_data_();
31  this->status_clear_warning();
32  }
33 
34  this->raw_data_index_ = (this->raw_data_index_ + 1) % 24;
35  }
36 }
38 
40  uint8_t index = this->raw_data_index_;
41  uint8_t byte = this->raw_data_[index];
42  if (index == 0) {
43  return !((byte != 0x55) && ((byte & 0xF0) != 0xF0) && (byte != 0xAA));
44  }
45 
46  if (index == 1) {
47  if (byte != 0x5A) {
48  ESP_LOGV(TAG, "Invalid Header 2 Start: 0x%02X!", byte);
49  return false;
50  }
51  return true;
52  }
53 
54  if (index == 23) {
55  uint8_t checksum = 0;
56  for (uint8_t i = 2; i < 23; i++) {
57  checksum += this->raw_data_[i];
58  }
59 
60  if (checksum != this->raw_data_[23]) {
61  ESP_LOGW(TAG, "Invalid checksum from CSE7766: 0x%02X != 0x%02X", checksum, this->raw_data_[23]);
62  return false;
63  }
64  return true;
65  }
66 
67  return true;
68 }
70  ESP_LOGVV(TAG, "CSE7766 Data: ");
71  for (uint8_t i = 0; i < 23; i++) {
72  ESP_LOGVV(TAG, " %u: 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", i + 1, BYTE_TO_BINARY(this->raw_data_[i]),
73  this->raw_data_[i]);
74  }
75 
76  uint8_t header1 = this->raw_data_[0];
77  if (header1 == 0xAA) {
78  ESP_LOGE(TAG, "CSE7766 not calibrated!");
79  return;
80  }
81 
82  bool power_cycle_exceeds_range = false;
83 
84  if ((header1 & 0xF0) == 0xF0) {
85  if (header1 & 0xD) {
86  ESP_LOGE(TAG, "CSE7766 reports abnormal external circuit or chip damage: (0x%02X)", header1);
87  if (header1 & (1 << 3)) {
88  ESP_LOGE(TAG, " Voltage cycle exceeds range.");
89  }
90  if (header1 & (1 << 2)) {
91  ESP_LOGE(TAG, " Current cycle exceeds range.");
92  }
93  if (header1 & (1 << 0)) {
94  ESP_LOGE(TAG, " Coefficient storage area is abnormal.");
95  }
96  return;
97  }
98 
99  power_cycle_exceeds_range = header1 & (1 << 1);
100  }
101 
102  uint32_t voltage_calib = this->get_24_bit_uint_(2);
103  uint32_t voltage_cycle = this->get_24_bit_uint_(5);
104  uint32_t current_calib = this->get_24_bit_uint_(8);
105  uint32_t current_cycle = this->get_24_bit_uint_(11);
106  uint32_t power_calib = this->get_24_bit_uint_(14);
107  uint32_t power_cycle = this->get_24_bit_uint_(17);
108 
109  uint8_t adj = this->raw_data_[20];
110  uint32_t cf_pulses = (this->raw_data_[21] << 8) + this->raw_data_[22];
111 
112  bool have_voltage = adj & 0x40;
113  if (have_voltage) {
114  // voltage cycle of serial port outputted is a complete cycle;
115  this->voltage_acc_ += voltage_calib / float(voltage_cycle);
116  this->voltage_counts_ += 1;
117  }
118 
119  bool have_power = adj & 0x10;
120  float power = 0.0f;
121 
122  if (have_power) {
123  // power cycle of serial port outputted is a complete cycle;
124  // According to the user manual, power cycle exceeding range means the measured power is 0
125  if (!power_cycle_exceeds_range) {
126  power = power_calib / float(power_cycle);
127  }
128  this->power_acc_ += power;
129  this->power_counts_ += 1;
130 
131  uint32_t difference;
132  if (this->cf_pulses_last_ == 0) {
133  this->cf_pulses_last_ = cf_pulses;
134  }
135 
136  if (cf_pulses < this->cf_pulses_last_) {
137  difference = cf_pulses + (0x10000 - this->cf_pulses_last_);
138  } else {
139  difference = cf_pulses - this->cf_pulses_last_;
140  }
141  this->cf_pulses_last_ = cf_pulses;
142  this->energy_total_ += difference * float(power_calib) / 1000000.0f / 3600.0f;
143  this->energy_total_counts_ += 1;
144  }
145 
146  if (adj & 0x20) {
147  // indicates current cycle of serial port outputted is a complete cycle;
148  float current = 0.0f;
149  if (have_voltage && !have_power) {
150  // Testing has shown that when we have voltage and current but not power, that means the power is 0.
151  // We report a power of 0, which in turn means we should report a current of 0.
152  this->power_counts_ += 1;
153  } else if (power != 0.0f) {
154  current = current_calib / float(current_cycle);
155  }
156  this->current_acc_ += current;
157  this->current_counts_ += 1;
158  }
159 }
161  const auto publish_state = [](const char *name, sensor::Sensor *sensor, float &acc, uint32_t &counts) {
162  if (counts != 0) {
163  const auto avg = acc / counts;
164 
165  ESP_LOGV(TAG, "Got %s_acc=%.2f %s_counts=%d %s=%.1f", name, acc, name, counts, name, avg);
166 
167  if (sensor != nullptr) {
168  sensor->publish_state(avg);
169  }
170 
171  acc = 0.0f;
172  counts = 0;
173  }
174  };
175 
176  publish_state("voltage", this->voltage_sensor_, this->voltage_acc_, this->voltage_counts_);
177  publish_state("current", this->current_sensor_, this->current_acc_, this->current_counts_);
178  publish_state("power", this->power_sensor_, this->power_acc_, this->power_counts_);
179 
180  if (this->energy_total_counts_ != 0) {
181  ESP_LOGV(TAG, "Got energy_total=%.2f energy_total_counts=%d", this->energy_total_, this->energy_total_counts_);
182 
183  if (this->energy_sensor_ != nullptr) {
185  }
186  this->energy_total_counts_ = 0;
187  }
188 }
189 
190 uint32_t CSE7766Component::get_24_bit_uint_(uint8_t start_index) {
191  return (uint32_t(this->raw_data_[start_index]) << 16) | (uint32_t(this->raw_data_[start_index + 1]) << 8) |
192  uint32_t(this->raw_data_[start_index + 2]);
193 }
194 
196  ESP_LOGCONFIG(TAG, "CSE7766:");
197  LOG_UPDATE_INTERVAL(this);
198  LOG_SENSOR(" ", "Voltage", this->voltage_sensor_);
199  LOG_SENSOR(" ", "Current", this->current_sensor_);
200  LOG_SENSOR(" ", "Power", this->power_sensor_);
201  LOG_SENSOR(" ", "Energy", this->energy_sensor_);
202  this->check_uart_settings(4800);
203 }
204 
205 } // namespace cse7766
206 } // namespace esphome
sensor::Sensor * power_sensor_
Definition: cse7766.h:32
const char * name
Definition: stm32flash.h:78
const float DATA
For components that import data from directly connected sensors like DHT.
Definition: component.cpp:18
uint32_t get_24_bit_uint_(uint8_t start_index)
Definition: cse7766.cpp:190
sensor::Sensor * energy_sensor_
Definition: cse7766.h:33
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:27
void check_uart_settings(uint32_t baud_rate, uint8_t stop_bits=1, UARTParityOptions parity=UART_CONFIG_PARITY_NONE, uint8_t data_bits=8)
Check that the configuration of the UART bus matches the provided values and otherwise print a warnin...
Definition: uart.cpp:12
bool read_byte(uint8_t *data)
Definition: uart.h:29
void status_clear_warning()
Definition: component.cpp:153
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:39
uint8_t checksum
Definition: bl0939.h:35
void status_set_warning()
Definition: component.cpp:145
float get_setup_priority() const override
Definition: cse7766.cpp:37
Definition: a4988.cpp:4
sensor::Sensor * current_sensor_
Definition: cse7766.h:31
Base-class for all sensors.
Definition: sensor.h:57
sensor::Sensor * voltage_sensor_
Definition: cse7766.h:30