mirror of
https://github.com/esphome/esphome.git
synced 2026-10-14 10:12:52 +00:00
[cse7761] Add current_input option for current transformer boards such as the Sonoff POW CT (#7836)
Co-authored-by: Clyde Stubbs <2366188+clydebarrow@users.noreply.github.com> Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Co-authored-by: J. Nick Koston <nick@koston.org> Co-authored-by: J. Nick Koston <nick@home-assistant.io>
This commit is contained in:
co-authored by
Clyde Stubbs
pre-commit-ci-lite[bot]
Jesse Hills
J. Nick Koston
J. Nick Koston
parent
6095d9d610
commit
d19bb7f4be
@@ -18,6 +18,14 @@ static constexpr int CSE7761_UREF = 42563; // RmsUc
|
||||
static constexpr int CSE7761_IREF = 52241; // RmsIAC
|
||||
static constexpr int CSE7761_PREF = 44513; // PowerPAC
|
||||
|
||||
// Empirical Sonoff POW CT factor taken from Tasmota
|
||||
static constexpr uint32_t CURRENT_TRANSFORMER_COEFFICIENT_SCALE = 5;
|
||||
|
||||
// PGA 16x on both current channels
|
||||
static constexpr uint16_t SYSCON_SHUNT = 0xFF04;
|
||||
// All PGAs 1x, ADC2ON
|
||||
static constexpr uint16_t SYSCON_CURRENT_TRANSFORMER = 0xFE00;
|
||||
|
||||
// System clock (3.579545MHz) / 8, divided by Ufreq gives the line frequency in Hz
|
||||
static constexpr float CSE7761_UFREQ_CLOCK = 3579545.0f / 8.0f;
|
||||
|
||||
@@ -68,6 +76,8 @@ void CSE7761Component::dump_config() {
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
|
||||
}
|
||||
ESP_LOGCONFIG(TAG, " Current input: %s",
|
||||
LOG_STR_ARG(this->is_current_transformer_() ? LOG_STR("current transformer") : LOG_STR("shunt")));
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
LOG_SENSOR(" ", "Voltage", this->voltage_sensor_);
|
||||
LOG_SENSOR(" ", "Current 1", this->current_sensor_1_);
|
||||
@@ -93,7 +103,7 @@ void CSE7761Component::write_(uint8_t reg, uint16_t data) {
|
||||
buffer[1] = reg;
|
||||
uint32_t len = 2;
|
||||
if (data) {
|
||||
if (data < 0xFF) {
|
||||
if (data <= 0xFF) {
|
||||
buffer[2] = data & 0xFF;
|
||||
len = 3;
|
||||
} else {
|
||||
@@ -158,16 +168,17 @@ uint32_t CSE7761Component::read_(uint8_t reg, uint8_t size) {
|
||||
}
|
||||
|
||||
uint32_t CSE7761Component::coefficient_by_unit_(uint32_t unit) {
|
||||
const uint32_t scale = this->is_current_transformer_() ? CURRENT_TRANSFORMER_COEFFICIENT_SCALE : 1;
|
||||
uint32_t coeff = 0;
|
||||
switch (unit) {
|
||||
case RMS_UC:
|
||||
coeff = this->data_.coefficient[RMS_UC];
|
||||
return coeff ? 0x400000 * 100 / coeff : 0;
|
||||
case RMS_IAC:
|
||||
coeff = this->data_.coefficient[RMS_IAC];
|
||||
coeff = this->data_.coefficient[RMS_IAC] * scale;
|
||||
return coeff ? (0x800000 * 100 / coeff) * 10 : 0; // Stay within 32 bits
|
||||
case POWER_PAC:
|
||||
coeff = this->data_.coefficient[POWER_PAC];
|
||||
coeff = this->data_.coefficient[POWER_PAC] * scale;
|
||||
return coeff ? 0x80000000 / coeff : 0;
|
||||
}
|
||||
return 0;
|
||||
@@ -195,9 +206,11 @@ bool CSE7761Component::chip_init_() {
|
||||
if (!this->read_(CSE7761_REG_HFCONST, 2, &hf_const)) {
|
||||
ESP_LOGW(TAG, "HFConst read failed, energy unavailable");
|
||||
}
|
||||
// The pulse counters follow the raw power, so they need the same front end factor as power
|
||||
const float input_scale = this->is_current_transformer_() ? CURRENT_TRANSFORMER_COEFFICIENT_SCALE : 1.0f;
|
||||
for (uint8_t channel = 0; channel < 2; channel++) {
|
||||
uint32_t scale = this->data_.coefficient[ENERGY_AC + channel] * hf_const;
|
||||
this->energy_[channel].wh_per_pulse = static_cast<float>(scale) * CSE7761_ENERGY_WH_SCALE;
|
||||
this->energy_[channel].wh_per_pulse = static_cast<float>(scale) * CSE7761_ENERGY_WH_SCALE * input_scale;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,7 +218,8 @@ bool CSE7761Component::chip_init_() {
|
||||
|
||||
uint8_t sys_status = this->read_(CSE7761_REG_SYSSTATUS, 1);
|
||||
if (sys_status & 0x10) { // Write enable to protected registers (WREN)
|
||||
this->write_(CSE7761_REG_SYSCON | 0x80, 0xFF04);
|
||||
this->write_(CSE7761_REG_SYSCON | 0x80,
|
||||
this->is_current_transformer_() ? SYSCON_CURRENT_TRANSFORMER : SYSCON_SHUNT);
|
||||
this->write_(CSE7761_REG_EMUCON | 0x80, 0x1183);
|
||||
// WaveEN|ZxEN, needed for Ufreq
|
||||
this->write_(CSE7761_REG_EMUCON2 | 0x80, this->frequency_sensor_ != nullptr ? 0x0FE5 : 0x0FC1);
|
||||
@@ -226,14 +240,15 @@ void CSE7761Component::get_data_() {
|
||||
uint32_t value = this->read_(CSE7761_REG_RMSU, 3);
|
||||
this->data_.voltage_rms = (value >= 0x800000) ? 0 : value;
|
||||
|
||||
// No load threshold of 10mA with a shunt; the raw value stands for 5 times more with a current transformer
|
||||
value = this->read_(CSE7761_REG_RMSIA, 3);
|
||||
this->data_.current_rms[0] = ((value >= 0x800000) || (value < 1600)) ? 0 : value; // No load threshold of 10mA
|
||||
this->data_.current_rms[0] = ((value >= 0x800000) || (value < 1600)) ? 0 : value;
|
||||
value = this->read_(CSE7761_REG_POWERPA, 4);
|
||||
// PowerPA is two's complement signed 32-bit per datasheet
|
||||
this->data_.active_power[0] = (0 == this->data_.current_rms[0]) ? 0 : static_cast<int32_t>(value);
|
||||
|
||||
value = this->read_(CSE7761_REG_RMSIB, 3);
|
||||
this->data_.current_rms[1] = ((value >= 0x800000) || (value < 1600)) ? 0 : value; // No load threshold of 10mA
|
||||
this->data_.current_rms[1] = ((value >= 0x800000) || (value < 1600)) ? 0 : value;
|
||||
value = this->read_(CSE7761_REG_POWERPB, 4);
|
||||
// PowerPB is two's complement signed 32-bit per datasheet
|
||||
this->data_.active_power[1] = (0 == this->data_.current_rms[1]) ? 0 : static_cast<int32_t>(value);
|
||||
|
||||
@@ -21,9 +21,15 @@ struct CSE7761EnergyChannel {
|
||||
float wh_per_pulse{0.0f};
|
||||
};
|
||||
|
||||
enum class CSE7761CurrentInput : uint8_t {
|
||||
CSE7761_CURRENT_INPUT_SHUNT,
|
||||
CSE7761_CURRENT_INPUT_CURRENT_TRANSFORMER,
|
||||
};
|
||||
|
||||
/// This class implements support for the CSE7761 UART power sensor.
|
||||
class CSE7761Component final : public PollingComponent, public uart::UARTDevice {
|
||||
public:
|
||||
explicit CSE7761Component(CSE7761CurrentInput current_input) : current_input_(current_input) {}
|
||||
void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
|
||||
void set_frequency_sensor(sensor::Sensor *frequency_sensor) { frequency_sensor_ = frequency_sensor; }
|
||||
void set_power_factor_1_sensor(sensor::Sensor *power_factor_sensor_1) {
|
||||
@@ -51,7 +57,11 @@ class CSE7761Component final : public PollingComponent, public uart::UARTDevice
|
||||
sensor::Sensor *energy_sensors_[2]{nullptr, nullptr};
|
||||
CSE7761DataStruct data_;
|
||||
CSE7761EnergyChannel energy_[2];
|
||||
const CSE7761CurrentInput current_input_;
|
||||
|
||||
bool is_current_transformer_() const {
|
||||
return this->current_input_ == CSE7761CurrentInput::CSE7761_CURRENT_INPUT_CURRENT_TRANSFORMER;
|
||||
}
|
||||
void write_(uint8_t reg, uint16_t data);
|
||||
bool read_once_(uint8_t reg, uint8_t size, uint32_t *value);
|
||||
bool read_(uint8_t reg, uint8_t size, uint32_t *value);
|
||||
|
||||
@@ -36,11 +36,21 @@ CONF_ACTIVE_POWER_2 = "active_power_2"
|
||||
CONF_ENERGY_1 = "energy_1"
|
||||
CONF_ENERGY_2 = "energy_2"
|
||||
CONF_POWER_FACTOR_1 = "power_factor_1"
|
||||
CONF_CURRENT_INPUT = "current_input"
|
||||
|
||||
CSE7761CurrentInput = cse7761_ns.enum("CSE7761CurrentInput", is_class=True)
|
||||
CURRENT_INPUTS = {
|
||||
"shunt": CSE7761CurrentInput.CSE7761_CURRENT_INPUT_SHUNT,
|
||||
"current_transformer": CSE7761CurrentInput.CSE7761_CURRENT_INPUT_CURRENT_TRANSFORMER,
|
||||
}
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(CSE7761Component),
|
||||
cv.Optional(CONF_CURRENT_INPUT, default="shunt"): cv.enum(
|
||||
CURRENT_INPUTS, lower=True
|
||||
),
|
||||
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
accuracy_decimals=1,
|
||||
@@ -112,7 +122,7 @@ FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema(
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(config[CONF_ID], config[CONF_CURRENT_INPUT])
|
||||
await cg.register_component(var, config)
|
||||
await uart.register_uart_device(var, config)
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
sensor:
|
||||
- platform: cse7761
|
||||
current_input: current_transformer
|
||||
voltage:
|
||||
name: CSE7761 CT Voltage
|
||||
current_1:
|
||||
name: CSE7761 CT Current 1
|
||||
active_power_1:
|
||||
name: CSE7761 CT Active Power 1
|
||||
energy_1:
|
||||
name: CSE7761 CT Energy 1
|
||||
@@ -4,5 +4,4 @@ substitutions:
|
||||
|
||||
packages:
|
||||
uart_38400_even: !include ../../test_build_components/common/uart_38400_even/esp32-idf.yaml
|
||||
|
||||
<<: !include common.yaml
|
||||
cse7761: !include common.yaml
|
||||
|
||||
@@ -4,5 +4,4 @@ substitutions:
|
||||
|
||||
packages:
|
||||
uart_38400_even: !include ../../test_build_components/common/uart_38400_even/esp8266-ard.yaml
|
||||
|
||||
<<: !include common.yaml
|
||||
cse7761: !include common.yaml
|
||||
|
||||
@@ -4,5 +4,4 @@ substitutions:
|
||||
|
||||
packages:
|
||||
uart_38400_even: !include ../../test_build_components/common/uart_38400_even/rp2040-ard.yaml
|
||||
|
||||
<<: !include common.yaml
|
||||
cse7761: !include common.yaml
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
substitutions:
|
||||
tx_pin: GPIO12
|
||||
rx_pin: GPIO14
|
||||
|
||||
packages:
|
||||
uart_38400_even: !include ../../test_build_components/common/uart_38400_even/esp32-idf.yaml
|
||||
cse7761: !include common-current-transformer.yaml
|
||||
Reference in New Issue
Block a user