From 281916653933fb5c457ed7201c195299ea8cc03b Mon Sep 17 00:00:00 2001 From: Frank Riley Date: Wed, 24 Aug 2022 21:12:45 -0700 Subject: [PATCH] Support high update rates and fix several bugs in the cse7766 component. (#3675) --- esphome/components/cse7766/cse7766.cpp | 135 +++++++++++++------------ esphome/components/cse7766/cse7766.h | 2 + 2 files changed, 75 insertions(+), 62 deletions(-) diff --git a/esphome/components/cse7766/cse7766.cpp b/esphome/components/cse7766/cse7766.cpp index 25d75da3e6..f232f35ea6 100644 --- a/esphome/components/cse7766/cse7766.cpp +++ b/esphome/components/cse7766/cse7766.cpp @@ -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) { diff --git a/esphome/components/cse7766/cse7766.h b/esphome/components/cse7766/cse7766.h index d6062c251c..2f30eec09f 100644 --- a/esphome/components/cse7766/cse7766.h +++ b/esphome/components/cse7766/cse7766.h @@ -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