ESPHome 2026.8.0-dev
Loading...
Searching...
No Matches
growatt_solar.cpp
Go to the documentation of this file.
1#include "growatt_solar.h"
4#include "esphome/core/log.h"
5
7
8static const char *const TAG = "growatt_solar";
9
10static const uint8_t MODBUS_CMD_READ_IN_REGISTERS = 0x04;
11static const uint8_t MODBUS_REGISTER_COUNT[] = {33, 95}; // indexed with enum GrowattProtocolVersion
12
14 // If update() was unable to send we retry until we can send.
15 if (!this->waiting_to_update_)
16 return;
17 update();
18}
19
21 // If our last send has had no reply yet, and it wasn't that long ago, do nothing.
23 if (now - this->last_send_ < this->get_update_interval() / 2) {
24 return;
25 }
26
27 // The bus might be slow, or there might be other devices, or other components might be talking to our device.
28 if (!this->ready_for_immediate_send()) {
29 this->waiting_to_update_ = true;
30 return;
31 }
32
33 this->waiting_to_update_ = false;
34 this->send(MODBUS_CMD_READ_IN_REGISTERS, 0, MODBUS_REGISTER_COUNT[this->protocol_version_]);
35 this->last_send_ = millis();
36}
37
38void GrowattSolar::on_response(std::span<const uint8_t> request_pdu, std::span<const uint8_t> response_pdu) {
39 auto data = modbus::helpers::server_pdu_payload(response_pdu);
40 // Other components might be sending commands to our device. But we don't get called with enough
41 // context to know what is what. So if we didn't do a send, we ignore the data.
42 if (!this->last_send_)
43 return;
44 this->last_send_ = 0;
45
46 // Also ignore the data if the message is too short. Otherwise we will publish invalid values.
47 if (data.size() < MODBUS_REGISTER_COUNT[this->protocol_version_] * 2)
48 return;
49
50 auto publish_1_reg_sensor_state = [&](sensor::Sensor *sensor, size_t i, float unit) -> void {
51 if (sensor == nullptr)
52 return;
53 float value = encode_uint16(data[i * 2], data[i * 2 + 1]) * unit;
54 sensor->publish_state(value);
55 };
56
57 auto publish_2_reg_sensor_state = [&](sensor::Sensor *sensor, size_t reg1, size_t reg2, float unit) -> void {
58 float value = ((encode_uint16(data[reg1 * 2], data[reg1 * 2 + 1]) << 16) +
59 encode_uint16(data[reg2 * 2], data[reg2 * 2 + 1])) *
60 unit;
61 if (sensor != nullptr)
62 sensor->publish_state(value);
63 };
64
65 switch (this->protocol_version_) {
66 case RTU: {
67 publish_1_reg_sensor_state(this->inverter_status_, RTU_INVERTER_STATUS, 1);
68
69 publish_2_reg_sensor_state(this->pv_active_power_sensor_, RTU_PV_ACTIVE_POWER, RTU_PV_ACTIVE_POWER + 1,
70 ONE_DEC_UNIT);
71
72 publish_1_reg_sensor_state(this->pvs_[0].voltage_sensor_, RTU_PV1_VOLTAGE, ONE_DEC_UNIT);
73 publish_1_reg_sensor_state(this->pvs_[0].current_sensor_, RTU_PV1_CURRENT, ONE_DEC_UNIT);
74 publish_2_reg_sensor_state(this->pvs_[0].active_power_sensor_, RTU_PV1_ACTIVE_POWER, RTU_PV1_ACTIVE_POWER + 1,
75 ONE_DEC_UNIT);
76
77 publish_1_reg_sensor_state(this->pvs_[1].voltage_sensor_, RTU_PV2_VOLTAGE, ONE_DEC_UNIT);
78 publish_1_reg_sensor_state(this->pvs_[1].current_sensor_, RTU_PV2_CURRENT, ONE_DEC_UNIT);
79 publish_2_reg_sensor_state(this->pvs_[1].active_power_sensor_, RTU_PV2_ACTIVE_POWER, RTU_PV2_ACTIVE_POWER + 1,
80 ONE_DEC_UNIT);
81
82 publish_2_reg_sensor_state(this->grid_active_power_sensor_, RTU_GRID_ACTIVE_POWER, RTU_GRID_ACTIVE_POWER + 1,
83 ONE_DEC_UNIT);
84 publish_1_reg_sensor_state(this->grid_frequency_sensor_, RTU_GRID_FREQUENCY, TWO_DEC_UNIT);
85
86 publish_1_reg_sensor_state(this->phases_[0].voltage_sensor_, RTU_PHASE1_VOLTAGE, ONE_DEC_UNIT);
87 publish_1_reg_sensor_state(this->phases_[0].current_sensor_, RTU_PHASE1_CURRENT, ONE_DEC_UNIT);
88 publish_2_reg_sensor_state(this->phases_[0].active_power_sensor_, RTU_PHASE1_ACTIVE_POWER,
89 RTU_PHASE1_ACTIVE_POWER + 1, ONE_DEC_UNIT);
90
91 publish_1_reg_sensor_state(this->phases_[1].voltage_sensor_, RTU_PHASE2_VOLTAGE, ONE_DEC_UNIT);
92 publish_1_reg_sensor_state(this->phases_[1].current_sensor_, RTU_PHASE2_CURRENT, ONE_DEC_UNIT);
93 publish_2_reg_sensor_state(this->phases_[1].active_power_sensor_, RTU_PHASE2_ACTIVE_POWER,
94 RTU_PHASE2_ACTIVE_POWER + 1, ONE_DEC_UNIT);
95
96 publish_1_reg_sensor_state(this->phases_[2].voltage_sensor_, RTU_PHASE3_VOLTAGE, ONE_DEC_UNIT);
97 publish_1_reg_sensor_state(this->phases_[2].current_sensor_, RTU_PHASE3_CURRENT, ONE_DEC_UNIT);
98 publish_2_reg_sensor_state(this->phases_[2].active_power_sensor_, RTU_PHASE3_ACTIVE_POWER,
99 RTU_PHASE3_ACTIVE_POWER + 1, ONE_DEC_UNIT);
100
101 publish_2_reg_sensor_state(this->today_production_, RTU_TODAY_PRODUCTION, RTU_TODAY_PRODUCTION + 1, ONE_DEC_UNIT);
102 publish_2_reg_sensor_state(this->total_energy_production_, RTU_TOTAL_ENERGY_PRODUCTION,
103 RTU_TOTAL_ENERGY_PRODUCTION + 1, ONE_DEC_UNIT);
104
105 publish_1_reg_sensor_state(this->inverter_module_temp_, RTU_INVERTER_MODULE_TEMP, ONE_DEC_UNIT);
106 break;
107 }
108 case RTU2: {
109 publish_1_reg_sensor_state(this->inverter_status_, RTU2_INVERTER_STATUS, 1);
110
111 publish_2_reg_sensor_state(this->pv_active_power_sensor_, RTU2_PV_ACTIVE_POWER, RTU2_PV_ACTIVE_POWER + 1,
112 ONE_DEC_UNIT);
113
114 publish_1_reg_sensor_state(this->pvs_[0].voltage_sensor_, RTU2_PV1_VOLTAGE, ONE_DEC_UNIT);
115 publish_1_reg_sensor_state(this->pvs_[0].current_sensor_, RTU2_PV1_CURRENT, ONE_DEC_UNIT);
116 publish_2_reg_sensor_state(this->pvs_[0].active_power_sensor_, RTU2_PV1_ACTIVE_POWER, RTU2_PV1_ACTIVE_POWER + 1,
117 ONE_DEC_UNIT);
118
119 publish_1_reg_sensor_state(this->pvs_[1].voltage_sensor_, RTU2_PV2_VOLTAGE, ONE_DEC_UNIT);
120 publish_1_reg_sensor_state(this->pvs_[1].current_sensor_, RTU2_PV2_CURRENT, ONE_DEC_UNIT);
121 publish_2_reg_sensor_state(this->pvs_[1].active_power_sensor_, RTU2_PV2_ACTIVE_POWER, RTU2_PV2_ACTIVE_POWER + 1,
122 ONE_DEC_UNIT);
123
124 publish_2_reg_sensor_state(this->grid_active_power_sensor_, RTU2_GRID_ACTIVE_POWER, RTU2_GRID_ACTIVE_POWER + 1,
125 ONE_DEC_UNIT);
126 publish_1_reg_sensor_state(this->grid_frequency_sensor_, RTU2_GRID_FREQUENCY, TWO_DEC_UNIT);
127
128 publish_1_reg_sensor_state(this->phases_[0].voltage_sensor_, RTU2_PHASE1_VOLTAGE, ONE_DEC_UNIT);
129 publish_1_reg_sensor_state(this->phases_[0].current_sensor_, RTU2_PHASE1_CURRENT, ONE_DEC_UNIT);
130 publish_2_reg_sensor_state(this->phases_[0].active_power_sensor_, RTU2_PHASE1_ACTIVE_POWER,
131 RTU2_PHASE1_ACTIVE_POWER + 1, ONE_DEC_UNIT);
132
133 publish_1_reg_sensor_state(this->phases_[1].voltage_sensor_, RTU2_PHASE2_VOLTAGE, ONE_DEC_UNIT);
134 publish_1_reg_sensor_state(this->phases_[1].current_sensor_, RTU2_PHASE2_CURRENT, ONE_DEC_UNIT);
135 publish_2_reg_sensor_state(this->phases_[1].active_power_sensor_, RTU2_PHASE2_ACTIVE_POWER,
136 RTU2_PHASE2_ACTIVE_POWER + 1, ONE_DEC_UNIT);
137
138 publish_1_reg_sensor_state(this->phases_[2].voltage_sensor_, RTU2_PHASE3_VOLTAGE, ONE_DEC_UNIT);
139 publish_1_reg_sensor_state(this->phases_[2].current_sensor_, RTU2_PHASE3_CURRENT, ONE_DEC_UNIT);
140 publish_2_reg_sensor_state(this->phases_[2].active_power_sensor_, RTU2_PHASE3_ACTIVE_POWER,
141 RTU2_PHASE3_ACTIVE_POWER + 1, ONE_DEC_UNIT);
142
143 publish_2_reg_sensor_state(this->today_production_, RTU2_TODAY_PRODUCTION, RTU2_TODAY_PRODUCTION + 1,
144 ONE_DEC_UNIT);
145 publish_2_reg_sensor_state(this->total_energy_production_, RTU2_TOTAL_ENERGY_PRODUCTION,
146 RTU2_TOTAL_ENERGY_PRODUCTION + 1, ONE_DEC_UNIT);
147
148 publish_1_reg_sensor_state(this->inverter_module_temp_, RTU2_INVERTER_MODULE_TEMP, ONE_DEC_UNIT);
149 break;
150 }
151 }
152}
153
155 ESP_LOGCONFIG(TAG,
156 "GROWATT Solar:\n"
157 " Address: 0x%02X",
158 this->address_);
159}
160
161} // namespace esphome::growatt_solar
uint32_t IRAM_ATTR HOT get_loop_component_start_time() const
Get the cached time in milliseconds from when the current component started its loop execution.
virtual uint32_t get_update_interval() const
Get the update interval in ms of this sensor.
void on_response(std::span< const uint8_t > request_pdu, std::span< const uint8_t > response_pdu) override
struct esphome::growatt_solar::GrowattSolar::GrowattPhase phases_[3]
GrowattProtocolVersion protocol_version_
struct esphome::growatt_solar::GrowattSolar::GrowattPV pvs_[2]
void send(uint8_t function, uint16_t start_address, uint16_t number_of_entities, uint8_t payload_len=0, const uint8_t *payload=nullptr)
Definition modbus.h:212
Base-class for all sensors.
Definition sensor.h:47
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:68
constexpr size_t RTU_PV1_CURRENT
constexpr size_t RTU2_PV1_CURRENT
constexpr size_t RTU2_GRID_FREQUENCY
constexpr size_t RTU_INVERTER_STATUS
constexpr size_t RTU_PHASE3_VOLTAGE
constexpr size_t RTU_PV2_CURRENT
constexpr size_t RTU2_PV_ACTIVE_POWER
constexpr size_t RTU2_PHASE3_ACTIVE_POWER
constexpr size_t RTU_PV2_ACTIVE_POWER
constexpr size_t RTU2_PHASE2_ACTIVE_POWER
constexpr size_t RTU_PHASE2_ACTIVE_POWER
constexpr size_t RTU_PHASE2_VOLTAGE
constexpr size_t RTU2_PV1_ACTIVE_POWER
constexpr size_t RTU2_PV2_VOLTAGE
constexpr size_t RTU_TOTAL_ENERGY_PRODUCTION
constexpr size_t RTU_GRID_ACTIVE_POWER
constexpr size_t RTU_PHASE3_CURRENT
constexpr size_t RTU2_PV1_VOLTAGE
constexpr size_t RTU2_PHASE1_ACTIVE_POWER
constexpr size_t RTU2_PHASE3_VOLTAGE
constexpr size_t RTU_PHASE1_CURRENT
constexpr size_t RTU2_PHASE1_CURRENT
constexpr size_t RTU2_TODAY_PRODUCTION
constexpr size_t RTU_PV2_VOLTAGE
constexpr size_t RTU_PV_ACTIVE_POWER
constexpr size_t RTU2_INVERTER_MODULE_TEMP
constexpr size_t RTU_PV1_VOLTAGE
constexpr size_t RTU2_PHASE2_CURRENT
constexpr size_t RTU2_TOTAL_ENERGY_PRODUCTION
constexpr size_t RTU2_GRID_ACTIVE_POWER
constexpr size_t RTU2_PHASE1_VOLTAGE
constexpr size_t RTU_PV1_ACTIVE_POWER
constexpr size_t RTU2_PHASE2_VOLTAGE
constexpr size_t RTU2_PHASE3_CURRENT
constexpr size_t RTU_PHASE1_VOLTAGE
constexpr size_t RTU2_PV2_CURRENT
constexpr size_t RTU_GRID_FREQUENCY
constexpr size_t RTU_PHASE1_ACTIVE_POWER
constexpr size_t RTU_PHASE2_CURRENT
constexpr size_t RTU2_PV2_ACTIVE_POWER
constexpr size_t RTU2_INVERTER_STATUS
constexpr size_t RTU_TODAY_PRODUCTION
constexpr size_t RTU_PHASE3_ACTIVE_POWER
constexpr size_t RTU_INVERTER_MODULE_TEMP
std::span< const uint8_t > server_pdu_payload(std::span< const uint8_t > pdu)
Returns the payload portion of a server response PDU: the bytes after the function code,...
constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb)
Encode a 16-bit value given the most and least significant byte.
Definition helpers.h:873
uint32_t IRAM_ATTR HOT millis()
Definition hal.cpp:28
Application App
Global storage of Application pointer - only one Application can exist.
static void uint32_t