ESPHome  2024.4.1
growatt_solar.cpp
Go to the documentation of this file.
1 #include "growatt_solar.h"
2 #include "esphome/core/log.h"
3 
4 namespace esphome {
5 namespace growatt_solar {
6 
7 static const char *const TAG = "growatt_solar";
8 
9 static const uint8_t MODBUS_CMD_READ_IN_REGISTERS = 0x04;
10 static const uint8_t MODBUS_REGISTER_COUNT[] = {33, 95}; // indexed with enum GrowattProtocolVersion
11 
13  // If update() was unable to send we retry until we can send.
14  if (!this->waiting_to_update_)
15  return;
16  update();
17 }
18 
20  // If our last send has had no reply yet, and it wasn't that long ago, do nothing.
21  uint32_t now = millis();
22  if (now - this->last_send_ < this->get_update_interval() / 2) {
23  return;
24  }
25 
26  // The bus might be slow, or there might be other devices, or other components might be talking to our device.
27  if (this->waiting_for_response()) {
28  this->waiting_to_update_ = true;
29  return;
30  }
31 
32  this->waiting_to_update_ = false;
33  this->send(MODBUS_CMD_READ_IN_REGISTERS, 0, MODBUS_REGISTER_COUNT[this->protocol_version_]);
34  this->last_send_ = millis();
35 }
36 
37 void GrowattSolar::on_modbus_data(const std::vector<uint8_t> &data) {
38  // Other components might be sending commands to our device. But we don't get called with enough
39  // context to know what is what. So if we didn't do a send, we ignore the data.
40  if (!this->last_send_)
41  return;
42  this->last_send_ = 0;
43 
44  // Also ignore the data if the message is too short. Otherwise we will publish invalid values.
45  if (data.size() < MODBUS_REGISTER_COUNT[this->protocol_version_] * 2)
46  return;
47 
48  auto publish_1_reg_sensor_state = [&](sensor::Sensor *sensor, size_t i, float unit) -> void {
49  if (sensor == nullptr)
50  return;
51  float value = encode_uint16(data[i * 2], data[i * 2 + 1]) * unit;
52  sensor->publish_state(value);
53  };
54 
55  auto publish_2_reg_sensor_state = [&](sensor::Sensor *sensor, size_t reg1, size_t reg2, float unit) -> void {
56  float value = ((encode_uint16(data[reg1 * 2], data[reg1 * 2 + 1]) << 16) +
57  encode_uint16(data[reg2 * 2], data[reg2 * 2 + 1])) *
58  unit;
59  if (sensor != nullptr)
60  sensor->publish_state(value);
61  };
62 
63  switch (this->protocol_version_) {
64  case RTU: {
65  publish_1_reg_sensor_state(this->inverter_status_, 0, 1);
66 
67  publish_2_reg_sensor_state(this->pv_active_power_sensor_, 1, 2, ONE_DEC_UNIT);
68 
69  publish_1_reg_sensor_state(this->pvs_[0].voltage_sensor_, 3, ONE_DEC_UNIT);
70  publish_1_reg_sensor_state(this->pvs_[0].current_sensor_, 4, ONE_DEC_UNIT);
71  publish_2_reg_sensor_state(this->pvs_[0].active_power_sensor_, 5, 6, ONE_DEC_UNIT);
72 
73  publish_1_reg_sensor_state(this->pvs_[1].voltage_sensor_, 7, ONE_DEC_UNIT);
74  publish_1_reg_sensor_state(this->pvs_[1].current_sensor_, 8, ONE_DEC_UNIT);
75  publish_2_reg_sensor_state(this->pvs_[1].active_power_sensor_, 9, 10, ONE_DEC_UNIT);
76 
77  publish_2_reg_sensor_state(this->grid_active_power_sensor_, 11, 12, ONE_DEC_UNIT);
78  publish_1_reg_sensor_state(this->grid_frequency_sensor_, 13, TWO_DEC_UNIT);
79 
80  publish_1_reg_sensor_state(this->phases_[0].voltage_sensor_, 14, ONE_DEC_UNIT);
81  publish_1_reg_sensor_state(this->phases_[0].current_sensor_, 15, ONE_DEC_UNIT);
82  publish_2_reg_sensor_state(this->phases_[0].active_power_sensor_, 16, 17, ONE_DEC_UNIT);
83 
84  publish_1_reg_sensor_state(this->phases_[1].voltage_sensor_, 18, ONE_DEC_UNIT);
85  publish_1_reg_sensor_state(this->phases_[1].current_sensor_, 19, ONE_DEC_UNIT);
86  publish_2_reg_sensor_state(this->phases_[1].active_power_sensor_, 20, 21, ONE_DEC_UNIT);
87 
88  publish_1_reg_sensor_state(this->phases_[2].voltage_sensor_, 22, ONE_DEC_UNIT);
89  publish_1_reg_sensor_state(this->phases_[2].current_sensor_, 23, ONE_DEC_UNIT);
90  publish_2_reg_sensor_state(this->phases_[2].active_power_sensor_, 24, 25, ONE_DEC_UNIT);
91 
92  publish_2_reg_sensor_state(this->today_production_, 26, 27, ONE_DEC_UNIT);
93  publish_2_reg_sensor_state(this->total_energy_production_, 28, 29, ONE_DEC_UNIT);
94 
95  publish_1_reg_sensor_state(this->inverter_module_temp_, 32, ONE_DEC_UNIT);
96  break;
97  }
98  case RTU2: {
99  publish_1_reg_sensor_state(this->inverter_status_, 0, 1);
100 
101  publish_2_reg_sensor_state(this->pv_active_power_sensor_, 1, 2, ONE_DEC_UNIT);
102 
103  publish_1_reg_sensor_state(this->pvs_[0].voltage_sensor_, 3, ONE_DEC_UNIT);
104  publish_1_reg_sensor_state(this->pvs_[0].current_sensor_, 4, ONE_DEC_UNIT);
105  publish_2_reg_sensor_state(this->pvs_[0].active_power_sensor_, 5, 6, ONE_DEC_UNIT);
106 
107  publish_1_reg_sensor_state(this->pvs_[1].voltage_sensor_, 7, ONE_DEC_UNIT);
108  publish_1_reg_sensor_state(this->pvs_[1].current_sensor_, 8, ONE_DEC_UNIT);
109  publish_2_reg_sensor_state(this->pvs_[1].active_power_sensor_, 9, 10, ONE_DEC_UNIT);
110 
111  publish_2_reg_sensor_state(this->grid_active_power_sensor_, 35, 36, ONE_DEC_UNIT);
112  publish_1_reg_sensor_state(this->grid_frequency_sensor_, 37, TWO_DEC_UNIT);
113 
114  publish_1_reg_sensor_state(this->phases_[0].voltage_sensor_, 38, ONE_DEC_UNIT);
115  publish_1_reg_sensor_state(this->phases_[0].current_sensor_, 39, ONE_DEC_UNIT);
116  publish_2_reg_sensor_state(this->phases_[0].active_power_sensor_, 40, 41, ONE_DEC_UNIT);
117 
118  publish_1_reg_sensor_state(this->phases_[1].voltage_sensor_, 42, ONE_DEC_UNIT);
119  publish_1_reg_sensor_state(this->phases_[1].current_sensor_, 43, ONE_DEC_UNIT);
120  publish_2_reg_sensor_state(this->phases_[1].active_power_sensor_, 44, 45, ONE_DEC_UNIT);
121 
122  publish_1_reg_sensor_state(this->phases_[2].voltage_sensor_, 46, ONE_DEC_UNIT);
123  publish_1_reg_sensor_state(this->phases_[2].current_sensor_, 47, ONE_DEC_UNIT);
124  publish_2_reg_sensor_state(this->phases_[2].active_power_sensor_, 48, 49, ONE_DEC_UNIT);
125 
126  publish_2_reg_sensor_state(this->today_production_, 53, 54, ONE_DEC_UNIT);
127  publish_2_reg_sensor_state(this->total_energy_production_, 55, 56, ONE_DEC_UNIT);
128 
129  publish_1_reg_sensor_state(this->inverter_module_temp_, 93, ONE_DEC_UNIT);
130  break;
131  }
132  }
133 }
134 
136  ESP_LOGCONFIG(TAG, "GROWATT Solar:");
137  ESP_LOGCONFIG(TAG, " Address: 0x%02X", this->address_);
138 }
139 
140 } // namespace growatt_solar
141 } // namespace esphome
struct esphome::growatt_solar::GrowattSolar::GrowattPhase phases_[3]
struct esphome::growatt_solar::GrowattSolar::GrowattPV pvs_[2]
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:25
sensor::Sensor * grid_active_power_sensor_
Definition: growatt_solar.h:76
void publish_state(float state)
Publish a new state to the front-end.
Definition: sensor.cpp:39
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:182
virtual uint32_t get_update_interval() const
Get the update interval in ms of this sensor.
Definition: component.cpp:228
GrowattProtocolVersion protocol_version_
Definition: growatt_solar.h:83
void on_modbus_data(const std::vector< uint8_t > &data) override
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
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:53
Base-class for all sensors.
Definition: sensor.h:57