mirror of
https://github.com/esphome/esphome.git
synced 2024-11-10 09:17:46 +01:00
CSE7766: fix power and current measurements at low loads (#6180)
This commit is contained in:
parent
fe789c8beb
commit
0ede4a3095
1 changed files with 75 additions and 35 deletions
|
@ -1,6 +1,8 @@
|
||||||
#include "cse7766.h"
|
#include "cse7766.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace cse7766 {
|
namespace cse7766 {
|
||||||
|
@ -68,20 +70,26 @@ bool CSE7766Component::check_byte_() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
void CSE7766Component::parse_data_() {
|
void CSE7766Component::parse_data_() {
|
||||||
ESP_LOGVV(TAG, "CSE7766 Data: ");
|
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
|
||||||
for (uint8_t i = 0; i < 23; i++) {
|
{
|
||||||
ESP_LOGVV(TAG, " %u: 0b" BYTE_TO_BINARY_PATTERN " (0x%02X)", i + 1, BYTE_TO_BINARY(this->raw_data_[i]),
|
std::stringstream ss;
|
||||||
this->raw_data_[i]);
|
ss << "Raw data:" << std::hex << std::uppercase << std::setfill('0');
|
||||||
|
for (uint8_t i = 0; i < 23; i++) {
|
||||||
|
ss << ' ' << std::setw(2) << static_cast<unsigned>(this->raw_data_[i]);
|
||||||
|
}
|
||||||
|
ESP_LOGVV(TAG, "%s", ss.str().c_str());
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Parse header
|
||||||
uint8_t header1 = this->raw_data_[0];
|
uint8_t header1 = this->raw_data_[0];
|
||||||
|
|
||||||
if (header1 == 0xAA) {
|
if (header1 == 0xAA) {
|
||||||
ESP_LOGE(TAG, "CSE7766 not calibrated!");
|
ESP_LOGE(TAG, "CSE7766 not calibrated!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool power_cycle_exceeds_range = false;
|
bool power_cycle_exceeds_range = false;
|
||||||
|
|
||||||
if ((header1 & 0xF0) == 0xF0) {
|
if ((header1 & 0xF0) == 0xF0) {
|
||||||
if (header1 & 0xD) {
|
if (header1 & 0xD) {
|
||||||
ESP_LOGE(TAG, "CSE7766 reports abnormal external circuit or chip damage: (0x%02X)", header1);
|
ESP_LOGE(TAG, "CSE7766 reports abnormal external circuit or chip damage: (0x%02X)", header1);
|
||||||
|
@ -94,74 +102,106 @@ void CSE7766Component::parse_data_() {
|
||||||
if (header1 & (1 << 0)) {
|
if (header1 & (1 << 0)) {
|
||||||
ESP_LOGE(TAG, " Coefficient storage area is abnormal.");
|
ESP_LOGE(TAG, " Coefficient storage area is abnormal.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Datasheet: voltage or current cycle exceeding range means invalid values
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
power_cycle_exceeds_range = header1 & (1 << 1);
|
power_cycle_exceeds_range = header1 & (1 << 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t voltage_calib = this->get_24_bit_uint_(2);
|
// Parse data frame
|
||||||
|
uint32_t voltage_coeff = this->get_24_bit_uint_(2);
|
||||||
uint32_t voltage_cycle = this->get_24_bit_uint_(5);
|
uint32_t voltage_cycle = this->get_24_bit_uint_(5);
|
||||||
uint32_t current_calib = this->get_24_bit_uint_(8);
|
uint32_t current_coeff = this->get_24_bit_uint_(8);
|
||||||
uint32_t current_cycle = this->get_24_bit_uint_(11);
|
uint32_t current_cycle = this->get_24_bit_uint_(11);
|
||||||
uint32_t power_calib = this->get_24_bit_uint_(14);
|
uint32_t power_coeff = this->get_24_bit_uint_(14);
|
||||||
uint32_t power_cycle = this->get_24_bit_uint_(17);
|
uint32_t power_cycle = this->get_24_bit_uint_(17);
|
||||||
|
|
||||||
uint8_t adj = this->raw_data_[20];
|
uint8_t adj = this->raw_data_[20];
|
||||||
uint32_t cf_pulses = (this->raw_data_[21] << 8) + this->raw_data_[22];
|
uint32_t cf_pulses = (this->raw_data_[21] << 8) + this->raw_data_[22];
|
||||||
|
|
||||||
|
bool have_power = adj & 0x10;
|
||||||
|
bool have_current = adj & 0x20;
|
||||||
bool have_voltage = adj & 0x40;
|
bool have_voltage = adj & 0x40;
|
||||||
|
|
||||||
|
float voltage = 0.0f;
|
||||||
if (have_voltage) {
|
if (have_voltage) {
|
||||||
// voltage cycle of serial port outputted is a complete cycle;
|
voltage = voltage_coeff / float(voltage_cycle);
|
||||||
float voltage = voltage_calib / float(voltage_cycle);
|
if (this->voltage_sensor_ != nullptr) {
|
||||||
if (this->voltage_sensor_ != nullptr)
|
|
||||||
this->voltage_sensor_->publish_state(voltage);
|
this->voltage_sensor_->publish_state(voltage);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool have_power = adj & 0x10;
|
|
||||||
float power = 0.0f;
|
float power = 0.0f;
|
||||||
|
float energy = 0.0f;
|
||||||
if (have_power) {
|
if (power_cycle_exceeds_range) {
|
||||||
// power cycle of serial port outputted is a complete cycle;
|
// Datasheet: power cycle exceeding range means active power is 0
|
||||||
// According to the user manual, power cycle exceeding range means the measured power is 0
|
if (this->power_sensor_ != nullptr) {
|
||||||
if (!power_cycle_exceeds_range) {
|
this->power_sensor_->publish_state(0.0f);
|
||||||
power = power_calib / float(power_cycle);
|
|
||||||
}
|
}
|
||||||
if (this->power_sensor_ != nullptr)
|
} else if (have_power) {
|
||||||
|
power = power_coeff / float(power_cycle);
|
||||||
|
if (this->power_sensor_ != nullptr) {
|
||||||
this->power_sensor_->publish_state(power);
|
this->power_sensor_->publish_state(power);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add CF pulses to the total energy only if we have Power coefficient to multiply by
|
||||||
|
|
||||||
uint32_t difference;
|
|
||||||
if (this->cf_pulses_last_ == 0) {
|
if (this->cf_pulses_last_ == 0) {
|
||||||
this->cf_pulses_last_ = cf_pulses;
|
this->cf_pulses_last_ = cf_pulses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t cf_diff;
|
||||||
if (cf_pulses < this->cf_pulses_last_) {
|
if (cf_pulses < this->cf_pulses_last_) {
|
||||||
difference = cf_pulses + (0x10000 - this->cf_pulses_last_);
|
cf_diff = cf_pulses + (0x10000 - this->cf_pulses_last_);
|
||||||
} else {
|
} else {
|
||||||
difference = cf_pulses - this->cf_pulses_last_;
|
cf_diff = cf_pulses - this->cf_pulses_last_;
|
||||||
}
|
}
|
||||||
this->cf_pulses_last_ = cf_pulses;
|
this->cf_pulses_last_ = cf_pulses;
|
||||||
this->energy_total_ += difference * float(power_calib) / 1000000.0f / 3600.0f;
|
|
||||||
|
energy = cf_diff * float(power_coeff) / 1000000.0f / 3600.0f;
|
||||||
|
this->energy_total_ += energy;
|
||||||
if (this->energy_sensor_ != nullptr)
|
if (this->energy_sensor_ != nullptr)
|
||||||
this->energy_sensor_->publish_state(this->energy_total_);
|
this->energy_sensor_->publish_state(this->energy_total_);
|
||||||
} else if ((this->energy_sensor_ != nullptr) && !this->energy_sensor_->has_state()) {
|
} else if ((this->energy_sensor_ != nullptr) && !this->energy_sensor_->has_state()) {
|
||||||
this->energy_sensor_->publish_state(0);
|
this->energy_sensor_->publish_state(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (adj & 0x20) {
|
float current = 0.0f;
|
||||||
// indicates current cycle of serial port outputted is a complete cycle;
|
float calculated_current = 0.0f;
|
||||||
float current = 0.0f;
|
if (have_current) {
|
||||||
if (have_voltage && !have_power) {
|
// Assumption: if we don't have power measurement, then current is likely below 50mA
|
||||||
// Testing has shown that when we have voltage and current but not power, that means the power is 0.
|
if (have_power && voltage > 1.0f) {
|
||||||
// We report a power of 0, which in turn means we should report a current of 0.
|
calculated_current = power / voltage;
|
||||||
if (this->power_sensor_ != nullptr)
|
|
||||||
this->power_sensor_->publish_state(0);
|
|
||||||
} else if (power != 0.0f) {
|
|
||||||
current = current_calib / float(current_cycle);
|
|
||||||
}
|
}
|
||||||
if (this->current_sensor_ != nullptr)
|
// Datasheet: minimum measured current is 50mA
|
||||||
|
if (calculated_current > 0.05f) {
|
||||||
|
current = current_coeff / float(current_cycle);
|
||||||
|
}
|
||||||
|
if (this->current_sensor_ != nullptr) {
|
||||||
this->current_sensor_->publish_state(current);
|
this->current_sensor_->publish_state(current);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "Parsed:";
|
||||||
|
if (have_voltage) {
|
||||||
|
ss << " V=" << voltage << "V";
|
||||||
|
}
|
||||||
|
if (have_current) {
|
||||||
|
ss << " I=" << current * 1000.0f << "mA (~" << calculated_current * 1000.0f << "mA)";
|
||||||
|
}
|
||||||
|
if (have_power) {
|
||||||
|
ss << " P=" << power << "W";
|
||||||
|
}
|
||||||
|
if (energy != 0.0f) {
|
||||||
|
ss << " E=" << energy << "kWh (" << cf_pulses << ")";
|
||||||
|
}
|
||||||
|
ESP_LOGVV(TAG, "%s", ss.str().c_str());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t CSE7766Component::get_24_bit_uint_(uint8_t start_index) {
|
uint32_t CSE7766Component::get_24_bit_uint_(uint8_t start_index) {
|
||||||
|
|
Loading…
Reference in a new issue