ESPHome 2025.6.3
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
6namespace esphome {
7namespace growatt_solar {
8
9static const char *const TAG = "growatt_solar";
10
11static const uint8_t MODBUS_CMD_READ_IN_REGISTERS = 0x04;
12static const uint8_t MODBUS_REGISTER_COUNT[] = {33, 95}; // indexed with enum GrowattProtocolVersion
13
15 // If update() was unable to send we retry until we can send.
16 if (!this->waiting_to_update_)
17 return;
18 update();
19}
20
22 // If our last send has had no reply yet, and it wasn't that long ago, do nothing.
23 const uint32_t now = App.get_loop_component_start_time();
24 if (now - this->last_send_ < this->get_update_interval() / 2) {
25 return;
26 }
27
28 // The bus might be slow, or there might be other devices, or other components might be talking to our device.
29 if (this->waiting_for_response()) {
30 this->waiting_to_update_ = true;
31 return;
32 }
33
34 this->waiting_to_update_ = false;
35 this->send(MODBUS_CMD_READ_IN_REGISTERS, 0, MODBUS_REGISTER_COUNT[this->protocol_version_]);
36 this->last_send_ = millis();
37}
38
39void GrowattSolar::on_modbus_data(const std::vector<uint8_t> &data) {
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_, 0, 1);
68
69 publish_2_reg_sensor_state(this->pv_active_power_sensor_, 1, 2, ONE_DEC_UNIT);
70
71 publish_1_reg_sensor_state(this->pvs_[0].voltage_sensor_, 3, ONE_DEC_UNIT);
72 publish_1_reg_sensor_state(this->pvs_[0].current_sensor_, 4, ONE_DEC_UNIT);
73 publish_2_reg_sensor_state(this->pvs_[0].active_power_sensor_, 5, 6, ONE_DEC_UNIT);
74
75 publish_1_reg_sensor_state(this->pvs_[1].voltage_sensor_, 7, ONE_DEC_UNIT);
76 publish_1_reg_sensor_state(this->pvs_[1].current_sensor_, 8, ONE_DEC_UNIT);
77 publish_2_reg_sensor_state(this->pvs_[1].active_power_sensor_, 9, 10, ONE_DEC_UNIT);
78
79 publish_2_reg_sensor_state(this->grid_active_power_sensor_, 11, 12, ONE_DEC_UNIT);
80 publish_1_reg_sensor_state(this->grid_frequency_sensor_, 13, TWO_DEC_UNIT);
81
82 publish_1_reg_sensor_state(this->phases_[0].voltage_sensor_, 14, ONE_DEC_UNIT);
83 publish_1_reg_sensor_state(this->phases_[0].current_sensor_, 15, ONE_DEC_UNIT);
84 publish_2_reg_sensor_state(this->phases_[0].active_power_sensor_, 16, 17, ONE_DEC_UNIT);
85
86 publish_1_reg_sensor_state(this->phases_[1].voltage_sensor_, 18, ONE_DEC_UNIT);
87 publish_1_reg_sensor_state(this->phases_[1].current_sensor_, 19, ONE_DEC_UNIT);
88 publish_2_reg_sensor_state(this->phases_[1].active_power_sensor_, 20, 21, ONE_DEC_UNIT);
89
90 publish_1_reg_sensor_state(this->phases_[2].voltage_sensor_, 22, ONE_DEC_UNIT);
91 publish_1_reg_sensor_state(this->phases_[2].current_sensor_, 23, ONE_DEC_UNIT);
92 publish_2_reg_sensor_state(this->phases_[2].active_power_sensor_, 24, 25, ONE_DEC_UNIT);
93
94 publish_2_reg_sensor_state(this->today_production_, 26, 27, ONE_DEC_UNIT);
95 publish_2_reg_sensor_state(this->total_energy_production_, 28, 29, ONE_DEC_UNIT);
96
97 publish_1_reg_sensor_state(this->inverter_module_temp_, 32, ONE_DEC_UNIT);
98 break;
99 }
100 case RTU2: {
101 publish_1_reg_sensor_state(this->inverter_status_, 0, 1);
102
103 publish_2_reg_sensor_state(this->pv_active_power_sensor_, 1, 2, ONE_DEC_UNIT);
104
105 publish_1_reg_sensor_state(this->pvs_[0].voltage_sensor_, 3, ONE_DEC_UNIT);
106 publish_1_reg_sensor_state(this->pvs_[0].current_sensor_, 4, ONE_DEC_UNIT);
107 publish_2_reg_sensor_state(this->pvs_[0].active_power_sensor_, 5, 6, ONE_DEC_UNIT);
108
109 publish_1_reg_sensor_state(this->pvs_[1].voltage_sensor_, 7, ONE_DEC_UNIT);
110 publish_1_reg_sensor_state(this->pvs_[1].current_sensor_, 8, ONE_DEC_UNIT);
111 publish_2_reg_sensor_state(this->pvs_[1].active_power_sensor_, 9, 10, ONE_DEC_UNIT);
112
113 publish_2_reg_sensor_state(this->grid_active_power_sensor_, 35, 36, ONE_DEC_UNIT);
114 publish_1_reg_sensor_state(this->grid_frequency_sensor_, 37, TWO_DEC_UNIT);
115
116 publish_1_reg_sensor_state(this->phases_[0].voltage_sensor_, 38, ONE_DEC_UNIT);
117 publish_1_reg_sensor_state(this->phases_[0].current_sensor_, 39, ONE_DEC_UNIT);
118 publish_2_reg_sensor_state(this->phases_[0].active_power_sensor_, 40, 41, ONE_DEC_UNIT);
119
120 publish_1_reg_sensor_state(this->phases_[1].voltage_sensor_, 42, ONE_DEC_UNIT);
121 publish_1_reg_sensor_state(this->phases_[1].current_sensor_, 43, ONE_DEC_UNIT);
122 publish_2_reg_sensor_state(this->phases_[1].active_power_sensor_, 44, 45, ONE_DEC_UNIT);
123
124 publish_1_reg_sensor_state(this->phases_[2].voltage_sensor_, 46, ONE_DEC_UNIT);
125 publish_1_reg_sensor_state(this->phases_[2].current_sensor_, 47, ONE_DEC_UNIT);
126 publish_2_reg_sensor_state(this->phases_[2].active_power_sensor_, 48, 49, ONE_DEC_UNIT);
127
128 publish_2_reg_sensor_state(this->today_production_, 53, 54, ONE_DEC_UNIT);
129 publish_2_reg_sensor_state(this->total_energy_production_, 55, 56, ONE_DEC_UNIT);
130
131 publish_1_reg_sensor_state(this->inverter_module_temp_, 93, ONE_DEC_UNIT);
132 break;
133 }
134 }
135}
136
138 ESP_LOGCONFIG(TAG,
139 "GROWATT Solar:\n"
140 " Address: 0x%02X",
141 this->address_);
142}
143
144} // namespace growatt_solar
145} // namespace esphome
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_modbus_data(const std::vector< uint8_t > &data) 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:62
Base-class for all sensors.
Definition sensor.h:62
void publish_state(float state)
Publish a new state to the front-end.
Definition sensor.cpp:39
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
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:192
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:28
Application App
Global storage of Application pointer - only one Application can exist.