Support high update rates and fix several bugs in the cse7766 component. (#3675)

This commit is contained in:
Frank Riley 2022-08-24 21:12:45 -07:00 committed by GitHub
parent 8fa18ca7c7
commit 2819166539
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 75 additions and 62 deletions

View file

@ -13,8 +13,9 @@ void CSE7766Component::loop() {
this->raw_data_index_ = 0;
}
if (this->available() == 0)
if (this->available() == 0) {
return;
}
this->last_transmission_ = now;
while (this->available() != 0) {
@ -22,6 +23,7 @@ void CSE7766Component::loop() {
if (!this->check_byte_()) {
this->raw_data_index_ = 0;
this->status_set_warning();
continue;
}
if (this->raw_data_index_ == 23) {
@ -51,8 +53,9 @@ bool CSE7766Component::check_byte_() {
if (index == 23) {
uint8_t checksum = 0;
for (uint8_t i = 2; i < 23; i++)
for (uint8_t i = 2; i < 23; i++) {
checksum += this->raw_data_[i];
}
if (checksum != this->raw_data_[23]) {
ESP_LOGW(TAG, "Invalid checksum from CSE7766: 0x%02X != 0x%02X", checksum, this->raw_data_[23]);
@ -66,20 +69,34 @@ bool CSE7766Component::check_byte_() {
void CSE7766Component::parse_data_() {
ESP_LOGVV(TAG, "CSE7766 Data: ");
for (uint8_t i = 0; i < 23; i++) {
ESP_LOGVV(TAG, " i=%u: 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", i, BYTE_TO_BINARY(this->raw_data_[i]),
ESP_LOGVV(TAG, " %u: 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", i + 1, BYTE_TO_BINARY(this->raw_data_[i]),
this->raw_data_[i]);
}
uint8_t header1 = this->raw_data_[0];
if (header1 == 0xAA) {
ESP_LOGW(TAG, "CSE7766 not calibrated!");
ESP_LOGE(TAG, "CSE7766 not calibrated!");
return;
}
if ((header1 & 0xF0) == 0xF0 && ((header1 >> 0) & 1) == 1) {
ESP_LOGW(TAG, "CSE7766 reports abnormal hardware: (0x%02X)", header1);
ESP_LOGW(TAG, " Coefficient storage area is abnormal.");
return;
bool power_cycle_exceeds_range = false;
if ((header1 & 0xF0) == 0xF0) {
if (header1 & 0xD) {
ESP_LOGE(TAG, "CSE7766 reports abnormal external circuit or chip damage: (0x%02X)", header1);
if (header1 & (1 << 3)) {
ESP_LOGE(TAG, " Voltage cycle exceeds range.");
}
if (header1 & (1 << 2)) {
ESP_LOGE(TAG, " Current cycle exceeds range.");
}
if (header1 & (1 << 0)) {
ESP_LOGE(TAG, " Coefficient storage area is abnormal.");
}
return;
}
power_cycle_exceeds_range = header1 & (1 << 1);
}
uint32_t voltage_calib = this->get_24_bit_uint_(2);
@ -92,46 +109,29 @@ void CSE7766Component::parse_data_() {
uint8_t adj = this->raw_data_[20];
uint32_t cf_pulses = (this->raw_data_[21] << 8) + this->raw_data_[22];
bool power_ok = true;
bool voltage_ok = true;
bool current_ok = true;
if (header1 > 0xF0) {
// ESP_LOGV(TAG, "CSE7766 reports abnormal hardware: (0x%02X)", byte);
if ((header1 >> 3) & 1) {
ESP_LOGV(TAG, " Voltage cycle exceeds range.");
voltage_ok = false;
}
if ((header1 >> 2) & 1) {
ESP_LOGV(TAG, " Current cycle exceeds range.");
current_ok = false;
}
if ((header1 >> 1) & 1) {
ESP_LOGV(TAG, " Power cycle exceeds range.");
power_ok = false;
}
if ((header1 >> 0) & 1) {
ESP_LOGV(TAG, " Coefficient storage area is abnormal.");
return;
}
}
if ((adj & 0x40) == 0x40 && voltage_ok && current_ok) {
bool have_voltage = adj & 0x40;
if (have_voltage) {
// voltage cycle of serial port outputted is a complete cycle;
this->voltage_acc_ += voltage_calib / float(voltage_cycle);
this->voltage_counts_ += 1;
}
float power = 0;
if ((adj & 0x10) == 0x10 && voltage_ok && current_ok && power_ok) {
bool have_power = adj & 0x10;
float power = 0.0f;
if (have_power) {
// power cycle of serial port outputted is a complete cycle;
power = power_calib / float(power_cycle);
// According to the user manual, power cycle exceeding range means the measured power is 0
if (!power_cycle_exceeds_range) {
power = power_calib / float(power_cycle);
}
this->power_acc_ += power;
this->power_counts_ += 1;
uint32_t difference;
if (this->cf_pulses_last_ == 0)
if (this->cf_pulses_last_ == 0) {
this->cf_pulses_last_ = cf_pulses;
}
if (cf_pulses < this->cf_pulses_last_) {
difference = cf_pulses + (0x10000 - this->cf_pulses_last_);
@ -139,41 +139,52 @@ void CSE7766Component::parse_data_() {
difference = cf_pulses - this->cf_pulses_last_;
}
this->cf_pulses_last_ = cf_pulses;
this->energy_total_ += difference * float(power_calib) / 1000000.0 / 3600.0;
this->energy_total_ += difference * float(power_calib) / 1000000.0f / 3600.0f;
this->energy_total_counts_ += 1;
}
if ((adj & 0x20) == 0x20 && current_ok && voltage_ok && power != 0.0) {
if (adj & 0x20) {
// indicates current cycle of serial port outputted is a complete cycle;
this->current_acc_ += current_calib / float(current_cycle);
float current = 0.0f;
if (have_voltage && !have_power) {
// Testing has shown that when we have voltage and current but not power, that means the power is 0.
// We report a power of 0, which in turn means we should report a current of 0.
this->power_counts_ += 1;
} else if (power != 0.0f) {
current = current_calib / float(current_cycle);
}
this->current_acc_ += current;
this->current_counts_ += 1;
}
}
void CSE7766Component::update() {
float voltage = this->voltage_counts_ > 0 ? this->voltage_acc_ / this->voltage_counts_ : 0.0f;
float current = this->current_counts_ > 0 ? this->current_acc_ / this->current_counts_ : 0.0f;
float power = this->power_counts_ > 0 ? this->power_acc_ / this->power_counts_ : 0.0f;
const auto publish_state = [](const char *name, sensor::Sensor *sensor, float &acc, uint32_t &counts) {
if (counts != 0) {
const auto avg = acc / counts;
ESP_LOGV(TAG, "Got voltage_acc=%.2f current_acc=%.2f power_acc=%.2f", this->voltage_acc_, this->current_acc_,
this->power_acc_);
ESP_LOGV(TAG, "Got voltage_counts=%d current_counts=%d power_counts=%d", this->voltage_counts_, this->current_counts_,
this->power_counts_);
ESP_LOGD(TAG, "Got voltage=%.1fV current=%.1fA power=%.1fW", voltage, current, power);
ESP_LOGV(TAG, "Got %s_acc=%.2f %s_counts=%d %s=%.1f", name, acc, name, counts, name, avg);
if (this->voltage_sensor_ != nullptr)
this->voltage_sensor_->publish_state(voltage);
if (this->current_sensor_ != nullptr)
this->current_sensor_->publish_state(current);
if (this->power_sensor_ != nullptr)
this->power_sensor_->publish_state(power);
if (this->energy_sensor_ != nullptr)
this->energy_sensor_->publish_state(this->energy_total_);
if (sensor != nullptr) {
sensor->publish_state(avg);
}
this->voltage_acc_ = 0.0f;
this->current_acc_ = 0.0f;
this->power_acc_ = 0.0f;
this->voltage_counts_ = 0;
this->power_counts_ = 0;
this->current_counts_ = 0;
acc = 0.0f;
counts = 0;
}
};
publish_state("voltage", this->voltage_sensor_, this->voltage_acc_, this->voltage_counts_);
publish_state("current", this->current_sensor_, this->current_acc_, this->current_counts_);
publish_state("power", this->power_sensor_, this->power_acc_, this->power_counts_);
if (this->energy_total_counts_ != 0) {
ESP_LOGV(TAG, "Got energy_total=%.2f energy_total_counts=%d", this->energy_total_, this->energy_total_counts_);
if (this->energy_sensor_ != nullptr) {
this->energy_sensor_->publish_state(this->energy_total_);
}
this->energy_total_counts_ = 0;
}
}
uint32_t CSE7766Component::get_24_bit_uint_(uint8_t start_index) {

View file

@ -39,6 +39,8 @@ class CSE7766Component : public PollingComponent, public uart::UARTDevice {
uint32_t voltage_counts_{0};
uint32_t current_counts_{0};
uint32_t power_counts_{0};
// Setting this to 1 means it will always publish 0 once at startup
uint32_t energy_total_counts_{1};
};
} // namespace cse7766