From 5e79a1f500b91a5706044b5645af6acde5a607dc Mon Sep 17 00:00:00 2001 From: djwlindenaar <32413299+djwlindenaar@users.noreply.github.com> Date: Sun, 10 Apr 2022 22:06:11 +0200 Subject: [PATCH] Implement newer RTU protocol of Growatt inverters (#3315) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Co-authored-by: Daniel Lindenaar --- .../growatt_solar/growatt_solar.cpp | 89 ++++++++++++++----- .../components/growatt_solar/growatt_solar.h | 8 ++ esphome/components/growatt_solar/sensor.py | 14 ++- 3 files changed, 86 insertions(+), 25 deletions(-) diff --git a/esphome/components/growatt_solar/growatt_solar.cpp b/esphome/components/growatt_solar/growatt_solar.cpp index ed7240ab6c..ed753c4d3f 100644 --- a/esphome/components/growatt_solar/growatt_solar.cpp +++ b/esphome/components/growatt_solar/growatt_solar.cpp @@ -7,9 +7,11 @@ namespace growatt_solar { static const char *const TAG = "growatt_solar"; static const uint8_t MODBUS_CMD_READ_IN_REGISTERS = 0x04; -static const uint8_t MODBUS_REGISTER_COUNT = 33; +static const uint8_t MODBUS_REGISTER_COUNT[] = {33, 95}; // indexed with enum GrowattProtocolVersion -void GrowattSolar::update() { this->send(MODBUS_CMD_READ_IN_REGISTERS, 0, MODBUS_REGISTER_COUNT); } +void GrowattSolar::update() { + this->send(MODBUS_CMD_READ_IN_REGISTERS, 0, MODBUS_REGISTER_COUNT[this->protocol_version_]); +} void GrowattSolar::on_modbus_data(const std::vector &data) { auto publish_1_reg_sensor_state = [&](sensor::Sensor *sensor, size_t i, float unit) -> void { @@ -27,37 +29,76 @@ void GrowattSolar::on_modbus_data(const std::vector &data) { sensor->publish_state(value); }; - publish_1_reg_sensor_state(this->inverter_status_, 0, 1); + switch (this->protocol_version_) { + case RTU: { + publish_1_reg_sensor_state(this->inverter_status_, 0, 1); - publish_2_reg_sensor_state(this->pv_active_power_sensor_, 1, 2, ONE_DEC_UNIT); + publish_2_reg_sensor_state(this->pv_active_power_sensor_, 1, 2, ONE_DEC_UNIT); - publish_1_reg_sensor_state(this->pvs_[0].voltage_sensor_, 3, ONE_DEC_UNIT); - publish_1_reg_sensor_state(this->pvs_[0].current_sensor_, 4, ONE_DEC_UNIT); - publish_2_reg_sensor_state(this->pvs_[0].active_power_sensor_, 5, 6, ONE_DEC_UNIT); + publish_1_reg_sensor_state(this->pvs_[0].voltage_sensor_, 3, ONE_DEC_UNIT); + publish_1_reg_sensor_state(this->pvs_[0].current_sensor_, 4, ONE_DEC_UNIT); + publish_2_reg_sensor_state(this->pvs_[0].active_power_sensor_, 5, 6, ONE_DEC_UNIT); - publish_1_reg_sensor_state(this->pvs_[1].voltage_sensor_, 7, ONE_DEC_UNIT); - publish_1_reg_sensor_state(this->pvs_[1].current_sensor_, 8, ONE_DEC_UNIT); - publish_2_reg_sensor_state(this->pvs_[1].active_power_sensor_, 9, 10, ONE_DEC_UNIT); + publish_1_reg_sensor_state(this->pvs_[1].voltage_sensor_, 7, ONE_DEC_UNIT); + publish_1_reg_sensor_state(this->pvs_[1].current_sensor_, 8, ONE_DEC_UNIT); + publish_2_reg_sensor_state(this->pvs_[1].active_power_sensor_, 9, 10, ONE_DEC_UNIT); - publish_2_reg_sensor_state(this->grid_active_power_sensor_, 11, 12, ONE_DEC_UNIT); - publish_1_reg_sensor_state(this->grid_frequency_sensor_, 13, TWO_DEC_UNIT); + publish_2_reg_sensor_state(this->grid_active_power_sensor_, 11, 12, ONE_DEC_UNIT); + publish_1_reg_sensor_state(this->grid_frequency_sensor_, 13, TWO_DEC_UNIT); - publish_1_reg_sensor_state(this->phases_[0].voltage_sensor_, 14, ONE_DEC_UNIT); - publish_1_reg_sensor_state(this->phases_[0].current_sensor_, 15, ONE_DEC_UNIT); - publish_2_reg_sensor_state(this->phases_[0].active_power_sensor_, 16, 17, ONE_DEC_UNIT); + publish_1_reg_sensor_state(this->phases_[0].voltage_sensor_, 14, ONE_DEC_UNIT); + publish_1_reg_sensor_state(this->phases_[0].current_sensor_, 15, ONE_DEC_UNIT); + publish_2_reg_sensor_state(this->phases_[0].active_power_sensor_, 16, 17, ONE_DEC_UNIT); - publish_1_reg_sensor_state(this->phases_[1].voltage_sensor_, 18, ONE_DEC_UNIT); - publish_1_reg_sensor_state(this->phases_[1].current_sensor_, 19, ONE_DEC_UNIT); - publish_2_reg_sensor_state(this->phases_[1].active_power_sensor_, 20, 21, ONE_DEC_UNIT); + publish_1_reg_sensor_state(this->phases_[1].voltage_sensor_, 18, ONE_DEC_UNIT); + publish_1_reg_sensor_state(this->phases_[1].current_sensor_, 19, ONE_DEC_UNIT); + publish_2_reg_sensor_state(this->phases_[1].active_power_sensor_, 20, 21, ONE_DEC_UNIT); - publish_1_reg_sensor_state(this->phases_[2].voltage_sensor_, 22, ONE_DEC_UNIT); - publish_1_reg_sensor_state(this->phases_[2].current_sensor_, 23, ONE_DEC_UNIT); - publish_2_reg_sensor_state(this->phases_[2].active_power_sensor_, 24, 25, ONE_DEC_UNIT); + publish_1_reg_sensor_state(this->phases_[2].voltage_sensor_, 22, ONE_DEC_UNIT); + publish_1_reg_sensor_state(this->phases_[2].current_sensor_, 23, ONE_DEC_UNIT); + publish_2_reg_sensor_state(this->phases_[2].active_power_sensor_, 24, 25, ONE_DEC_UNIT); - publish_2_reg_sensor_state(this->today_production_, 26, 27, ONE_DEC_UNIT); - publish_2_reg_sensor_state(this->total_energy_production_, 28, 29, ONE_DEC_UNIT); + publish_2_reg_sensor_state(this->today_production_, 26, 27, ONE_DEC_UNIT); + publish_2_reg_sensor_state(this->total_energy_production_, 28, 29, ONE_DEC_UNIT); - publish_1_reg_sensor_state(this->inverter_module_temp_, 32, ONE_DEC_UNIT); + publish_1_reg_sensor_state(this->inverter_module_temp_, 32, ONE_DEC_UNIT); + break; + } + case RTU2: { + publish_1_reg_sensor_state(this->inverter_status_, 0, 1); + + publish_2_reg_sensor_state(this->pv_active_power_sensor_, 1, 2, ONE_DEC_UNIT); + + publish_1_reg_sensor_state(this->pvs_[0].voltage_sensor_, 3, ONE_DEC_UNIT); + publish_1_reg_sensor_state(this->pvs_[0].current_sensor_, 4, ONE_DEC_UNIT); + publish_2_reg_sensor_state(this->pvs_[0].active_power_sensor_, 5, 6, ONE_DEC_UNIT); + + publish_1_reg_sensor_state(this->pvs_[1].voltage_sensor_, 7, ONE_DEC_UNIT); + publish_1_reg_sensor_state(this->pvs_[1].current_sensor_, 8, ONE_DEC_UNIT); + publish_2_reg_sensor_state(this->pvs_[1].active_power_sensor_, 9, 10, ONE_DEC_UNIT); + + publish_2_reg_sensor_state(this->grid_active_power_sensor_, 35, 36, ONE_DEC_UNIT); + publish_1_reg_sensor_state(this->grid_frequency_sensor_, 37, TWO_DEC_UNIT); + + publish_1_reg_sensor_state(this->phases_[0].voltage_sensor_, 38, ONE_DEC_UNIT); + publish_1_reg_sensor_state(this->phases_[0].current_sensor_, 39, ONE_DEC_UNIT); + publish_2_reg_sensor_state(this->phases_[0].active_power_sensor_, 40, 41, ONE_DEC_UNIT); + + publish_1_reg_sensor_state(this->phases_[1].voltage_sensor_, 42, ONE_DEC_UNIT); + publish_1_reg_sensor_state(this->phases_[1].current_sensor_, 43, ONE_DEC_UNIT); + publish_2_reg_sensor_state(this->phases_[1].active_power_sensor_, 44, 45, ONE_DEC_UNIT); + + publish_1_reg_sensor_state(this->phases_[2].voltage_sensor_, 46, ONE_DEC_UNIT); + publish_1_reg_sensor_state(this->phases_[2].current_sensor_, 47, ONE_DEC_UNIT); + publish_2_reg_sensor_state(this->phases_[2].active_power_sensor_, 48, 49, ONE_DEC_UNIT); + + publish_2_reg_sensor_state(this->today_production_, 53, 54, ONE_DEC_UNIT); + publish_2_reg_sensor_state(this->total_energy_production_, 55, 56, ONE_DEC_UNIT); + + publish_1_reg_sensor_state(this->inverter_module_temp_, 93, ONE_DEC_UNIT); + break; + } + } } void GrowattSolar::dump_config() { diff --git a/esphome/components/growatt_solar/growatt_solar.h b/esphome/components/growatt_solar/growatt_solar.h index 5356ac907a..0067998133 100644 --- a/esphome/components/growatt_solar/growatt_solar.h +++ b/esphome/components/growatt_solar/growatt_solar.h @@ -10,12 +10,19 @@ namespace growatt_solar { static const float TWO_DEC_UNIT = 0.01; static const float ONE_DEC_UNIT = 0.1; +enum GrowattProtocolVersion { + RTU = 0, + RTU2, +}; + class GrowattSolar : public PollingComponent, public modbus::ModbusDevice { public: void update() override; void on_modbus_data(const std::vector &data) override; void dump_config() override; + void set_protocol_version(GrowattProtocolVersion protocol_version) { this->protocol_version_ = protocol_version; } + void set_inverter_status_sensor(sensor::Sensor *sensor) { this->inverter_status_ = sensor; } void set_grid_frequency_sensor(sensor::Sensor *sensor) { this->grid_frequency_sensor_ = sensor; } @@ -67,6 +74,7 @@ class GrowattSolar : public PollingComponent, public modbus::ModbusDevice { sensor::Sensor *today_production_{nullptr}; sensor::Sensor *total_energy_production_{nullptr}; sensor::Sensor *inverter_module_temp_{nullptr}; + GrowattProtocolVersion protocol_version_; }; } // namespace growatt_solar diff --git a/esphome/components/growatt_solar/sensor.py b/esphome/components/growatt_solar/sensor.py index 99936c33ee..4961595505 100644 --- a/esphome/components/growatt_solar/sensor.py +++ b/esphome/components/growatt_solar/sensor.py @@ -39,7 +39,7 @@ UNIT_MILLIAMPERE = "mA" CONF_INVERTER_STATUS = "inverter_status" CONF_PV_ACTIVE_POWER = "pv_active_power" CONF_INVERTER_MODULE_TEMP = "inverter_module_temp" - +CONF_PROTOCOL_VERSION = "protocol_version" AUTO_LOAD = ["modbus"] CODEOWNERS = ["@leeuwte"] @@ -95,10 +95,20 @@ PV_SCHEMA = cv.Schema( {cv.Optional(sensor): schema for sensor, schema in PV_SENSORS.items()} ) +GrowattProtocolVersion = growatt_solar_ns.enum("GrowattProtocolVersion") +PROTOCOL_VERSIONS = { + "RTU": GrowattProtocolVersion.RTU, + "RTU2": GrowattProtocolVersion.RTU2, +} + + CONFIG_SCHEMA = ( cv.Schema( { cv.GenerateID(): cv.declare_id(GrowattSolar), + cv.Optional(CONF_PROTOCOL_VERSION, default="RTU"): cv.enum( + PROTOCOL_VERSIONS, upper=True + ), cv.Optional(CONF_PHASE_A): PHASE_SCHEMA, cv.Optional(CONF_PHASE_B): PHASE_SCHEMA, cv.Optional(CONF_PHASE_C): PHASE_SCHEMA, @@ -152,6 +162,8 @@ async def to_code(config): await cg.register_component(var, config) await modbus.register_modbus_device(var, config) + cg.add(var.set_protocol_version(config[CONF_PROTOCOL_VERSION])) + if CONF_INVERTER_STATUS in config: sens = await sensor.new_sensor(config[CONF_INVERTER_STATUS]) cg.add(var.set_inverter_status_sensor(sens))