ESPHome  2024.12.2
pipsolar.cpp
Go to the documentation of this file.
1 #include "pipsolar.h"
2 #include "esphome/core/log.h"
3 #include "esphome/core/helpers.h"
4 
5 namespace esphome {
6 namespace pipsolar {
7 
8 static const char *const TAG = "pipsolar";
9 
10 void Pipsolar::setup() {
11  this->state_ = STATE_IDLE;
12  this->command_start_millis_ = 0;
13 }
14 
16  uint8_t byte;
17  while (this->available()) {
18  this->read_byte(&byte);
19  }
20 }
21 
22 void Pipsolar::loop() {
23  // Read message
24  if (this->state_ == STATE_IDLE) {
25  this->empty_uart_buffer_();
26  switch (this->send_next_command_()) {
27  case 0:
28  // no command send (empty queue) time to poll
29  if (millis() - this->last_poll_ > this->update_interval_) {
30  this->send_next_poll_();
31  this->last_poll_ = millis();
32  }
33  return;
34  break;
35  case 1:
36  // command send
37  return;
38  break;
39  }
40  }
41  if (this->state_ == STATE_COMMAND_COMPLETE) {
42  if (this->check_incoming_length_(4)) {
43  ESP_LOGD(TAG, "response length for command OK");
44  if (this->check_incoming_crc_()) {
45  // crc ok
46  if (this->read_buffer_[1] == 'A' && this->read_buffer_[2] == 'C' && this->read_buffer_[3] == 'K') {
47  ESP_LOGD(TAG, "command successful");
48  } else {
49  ESP_LOGD(TAG, "command not successful");
50  }
51  this->command_queue_[this->command_queue_position_] = std::string("");
53  this->state_ = STATE_IDLE;
54 
55  } else {
56  // crc failed
57  this->command_queue_[this->command_queue_position_] = std::string("");
59  this->state_ = STATE_IDLE;
60  }
61  } else {
62  ESP_LOGD(TAG, "response length for command %s not OK: with length %zu",
63  this->command_queue_[this->command_queue_position_].c_str(), this->read_pos_);
64  this->command_queue_[this->command_queue_position_] = std::string("");
66  this->state_ = STATE_IDLE;
67  }
68  }
69 
70  if (this->state_ == STATE_POLL_DECODED) {
71  std::string mode;
72  switch (this->used_polling_commands_[this->last_polling_command_].identifier) {
73  case POLLING_QPIRI:
74  if (this->grid_rating_voltage_) {
75  this->grid_rating_voltage_->publish_state(value_grid_rating_voltage_);
76  }
77  if (this->grid_rating_current_) {
78  this->grid_rating_current_->publish_state(value_grid_rating_current_);
79  }
80  if (this->ac_output_rating_voltage_) {
81  this->ac_output_rating_voltage_->publish_state(value_ac_output_rating_voltage_);
82  }
83  if (this->ac_output_rating_frequency_) {
84  this->ac_output_rating_frequency_->publish_state(value_ac_output_rating_frequency_);
85  }
86  if (this->ac_output_rating_current_) {
87  this->ac_output_rating_current_->publish_state(value_ac_output_rating_current_);
88  }
89  if (this->ac_output_rating_apparent_power_) {
90  this->ac_output_rating_apparent_power_->publish_state(value_ac_output_rating_apparent_power_);
91  }
92  if (this->ac_output_rating_active_power_) {
93  this->ac_output_rating_active_power_->publish_state(value_ac_output_rating_active_power_);
94  }
95  if (this->battery_rating_voltage_) {
96  this->battery_rating_voltage_->publish_state(value_battery_rating_voltage_);
97  }
98  if (this->battery_recharge_voltage_) {
99  this->battery_recharge_voltage_->publish_state(value_battery_recharge_voltage_);
100  }
101  if (this->battery_under_voltage_) {
102  this->battery_under_voltage_->publish_state(value_battery_under_voltage_);
103  }
104  if (this->battery_bulk_voltage_) {
105  this->battery_bulk_voltage_->publish_state(value_battery_bulk_voltage_);
106  }
107  if (this->battery_float_voltage_) {
108  this->battery_float_voltage_->publish_state(value_battery_float_voltage_);
109  }
110  if (this->battery_type_) {
111  this->battery_type_->publish_state(value_battery_type_);
112  }
113  if (this->current_max_ac_charging_current_) {
114  this->current_max_ac_charging_current_->publish_state(value_current_max_ac_charging_current_);
115  }
116  if (this->current_max_charging_current_) {
117  this->current_max_charging_current_->publish_state(value_current_max_charging_current_);
118  }
119  if (this->input_voltage_range_) {
120  this->input_voltage_range_->publish_state(value_input_voltage_range_);
121  }
122  // special for input voltage range switch
123  if (this->input_voltage_range_switch_) {
124  this->input_voltage_range_switch_->publish_state(value_input_voltage_range_ == 1);
125  }
126  if (this->output_source_priority_) {
127  this->output_source_priority_->publish_state(value_output_source_priority_);
128  }
129  // special for output source priority switches
130  if (this->output_source_priority_utility_switch_) {
131  this->output_source_priority_utility_switch_->publish_state(value_output_source_priority_ == 0);
132  }
133  if (this->output_source_priority_solar_switch_) {
134  this->output_source_priority_solar_switch_->publish_state(value_output_source_priority_ == 1);
135  }
136  if (this->output_source_priority_battery_switch_) {
137  this->output_source_priority_battery_switch_->publish_state(value_output_source_priority_ == 2);
138  }
139  if (this->output_source_priority_hybrid_switch_) {
140  this->output_source_priority_hybrid_switch_->publish_state(value_output_source_priority_ == 3);
141  }
142  if (this->charger_source_priority_) {
143  this->charger_source_priority_->publish_state(value_charger_source_priority_);
144  }
145  if (this->parallel_max_num_) {
146  this->parallel_max_num_->publish_state(value_parallel_max_num_);
147  }
148  if (this->machine_type_) {
149  this->machine_type_->publish_state(value_machine_type_);
150  }
151  if (this->topology_) {
152  this->topology_->publish_state(value_topology_);
153  }
154  if (this->output_mode_) {
155  this->output_mode_->publish_state(value_output_mode_);
156  }
157  if (this->battery_redischarge_voltage_) {
158  this->battery_redischarge_voltage_->publish_state(value_battery_redischarge_voltage_);
159  }
160  if (this->pv_ok_condition_for_parallel_) {
161  this->pv_ok_condition_for_parallel_->publish_state(value_pv_ok_condition_for_parallel_);
162  }
163  // special for pv ok condition switch
164  if (this->pv_ok_condition_for_parallel_switch_) {
165  this->pv_ok_condition_for_parallel_switch_->publish_state(value_pv_ok_condition_for_parallel_ == 1);
166  }
167  if (this->pv_power_balance_) {
168  this->pv_power_balance_->publish_state(value_pv_power_balance_ == 1);
169  }
170  // special for power balance switch
171  if (this->pv_power_balance_switch_) {
172  this->pv_power_balance_switch_->publish_state(value_pv_power_balance_ == 1);
173  }
174  this->state_ = STATE_IDLE;
175  break;
176  case POLLING_QPIGS:
177  if (this->grid_voltage_) {
178  this->grid_voltage_->publish_state(value_grid_voltage_);
179  }
180  if (this->grid_frequency_) {
181  this->grid_frequency_->publish_state(value_grid_frequency_);
182  }
183  if (this->ac_output_voltage_) {
184  this->ac_output_voltage_->publish_state(value_ac_output_voltage_);
185  }
186  if (this->ac_output_frequency_) {
187  this->ac_output_frequency_->publish_state(value_ac_output_frequency_);
188  }
189  if (this->ac_output_apparent_power_) {
190  this->ac_output_apparent_power_->publish_state(value_ac_output_apparent_power_);
191  }
192  if (this->ac_output_active_power_) {
193  this->ac_output_active_power_->publish_state(value_ac_output_active_power_);
194  }
195  if (this->output_load_percent_) {
196  this->output_load_percent_->publish_state(value_output_load_percent_);
197  }
198  if (this->bus_voltage_) {
199  this->bus_voltage_->publish_state(value_bus_voltage_);
200  }
201  if (this->battery_voltage_) {
202  this->battery_voltage_->publish_state(value_battery_voltage_);
203  }
204  if (this->battery_charging_current_) {
205  this->battery_charging_current_->publish_state(value_battery_charging_current_);
206  }
207  if (this->battery_capacity_percent_) {
208  this->battery_capacity_percent_->publish_state(value_battery_capacity_percent_);
209  }
210  if (this->inverter_heat_sink_temperature_) {
211  this->inverter_heat_sink_temperature_->publish_state(value_inverter_heat_sink_temperature_);
212  }
213  if (this->pv_input_current_for_battery_) {
214  this->pv_input_current_for_battery_->publish_state(value_pv_input_current_for_battery_);
215  }
216  if (this->pv_input_voltage_) {
217  this->pv_input_voltage_->publish_state(value_pv_input_voltage_);
218  }
219  if (this->battery_voltage_scc_) {
220  this->battery_voltage_scc_->publish_state(value_battery_voltage_scc_);
221  }
222  if (this->battery_discharge_current_) {
223  this->battery_discharge_current_->publish_state(value_battery_discharge_current_);
224  }
225  if (this->add_sbu_priority_version_) {
226  this->add_sbu_priority_version_->publish_state(value_add_sbu_priority_version_);
227  }
228  if (this->configuration_status_) {
229  this->configuration_status_->publish_state(value_configuration_status_);
230  }
231  if (this->scc_firmware_version_) {
232  this->scc_firmware_version_->publish_state(value_scc_firmware_version_);
233  }
234  if (this->load_status_) {
235  this->load_status_->publish_state(value_load_status_);
236  }
237  if (this->battery_voltage_to_steady_while_charging_) {
238  this->battery_voltage_to_steady_while_charging_->publish_state(
239  value_battery_voltage_to_steady_while_charging_);
240  }
241  if (this->charging_status_) {
242  this->charging_status_->publish_state(value_charging_status_);
243  }
244  if (this->scc_charging_status_) {
245  this->scc_charging_status_->publish_state(value_scc_charging_status_);
246  }
247  if (this->ac_charging_status_) {
248  this->ac_charging_status_->publish_state(value_ac_charging_status_);
249  }
250  if (this->battery_voltage_offset_for_fans_on_) {
251  this->battery_voltage_offset_for_fans_on_->publish_state(value_battery_voltage_offset_for_fans_on_ / 10.0f);
252  } //.1 scale
253  if (this->eeprom_version_) {
254  this->eeprom_version_->publish_state(value_eeprom_version_);
255  }
256  if (this->pv_charging_power_) {
257  this->pv_charging_power_->publish_state(value_pv_charging_power_);
258  }
259  if (this->charging_to_floating_mode_) {
260  this->charging_to_floating_mode_->publish_state(value_charging_to_floating_mode_);
261  }
262  if (this->switch_on_) {
263  this->switch_on_->publish_state(value_switch_on_);
264  }
265  if (this->dustproof_installed_) {
266  this->dustproof_installed_->publish_state(value_dustproof_installed_);
267  }
268  this->state_ = STATE_IDLE;
269  break;
270  case POLLING_QMOD:
271  if (this->device_mode_) {
272  mode = value_device_mode_;
273  this->device_mode_->publish_state(mode);
274  }
275  this->state_ = STATE_IDLE;
276  break;
277  case POLLING_QFLAG:
278  if (this->silence_buzzer_open_buzzer_) {
279  this->silence_buzzer_open_buzzer_->publish_state(value_silence_buzzer_open_buzzer_);
280  }
281  if (this->overload_bypass_function_) {
282  this->overload_bypass_function_->publish_state(value_overload_bypass_function_);
283  }
284  if (this->lcd_escape_to_default_) {
285  this->lcd_escape_to_default_->publish_state(value_lcd_escape_to_default_);
286  }
287  if (this->overload_restart_function_) {
288  this->overload_restart_function_->publish_state(value_overload_restart_function_);
289  }
290  if (this->over_temperature_restart_function_) {
291  this->over_temperature_restart_function_->publish_state(value_over_temperature_restart_function_);
292  }
293  if (this->backlight_on_) {
294  this->backlight_on_->publish_state(value_backlight_on_);
295  }
296  if (this->alarm_on_when_primary_source_interrupt_) {
297  this->alarm_on_when_primary_source_interrupt_->publish_state(value_alarm_on_when_primary_source_interrupt_);
298  }
299  if (this->fault_code_record_) {
300  this->fault_code_record_->publish_state(value_fault_code_record_);
301  }
302  if (this->power_saving_) {
303  this->power_saving_->publish_state(value_power_saving_);
304  }
305  this->state_ = STATE_IDLE;
306  break;
307  case POLLING_QPIWS:
308  if (this->warnings_present_) {
309  this->warnings_present_->publish_state(value_warnings_present_);
310  }
311  if (this->faults_present_) {
312  this->faults_present_->publish_state(value_faults_present_);
313  }
314  if (this->warning_power_loss_) {
315  this->warning_power_loss_->publish_state(value_warning_power_loss_);
316  }
317  if (this->fault_inverter_fault_) {
318  this->fault_inverter_fault_->publish_state(value_fault_inverter_fault_);
319  }
320  if (this->fault_bus_over_) {
321  this->fault_bus_over_->publish_state(value_fault_bus_over_);
322  }
323  if (this->fault_bus_under_) {
324  this->fault_bus_under_->publish_state(value_fault_bus_under_);
325  }
326  if (this->fault_bus_soft_fail_) {
327  this->fault_bus_soft_fail_->publish_state(value_fault_bus_soft_fail_);
328  }
329  if (this->warning_line_fail_) {
330  this->warning_line_fail_->publish_state(value_warning_line_fail_);
331  }
332  if (this->fault_opvshort_) {
333  this->fault_opvshort_->publish_state(value_fault_opvshort_);
334  }
335  if (this->fault_inverter_voltage_too_low_) {
336  this->fault_inverter_voltage_too_low_->publish_state(value_fault_inverter_voltage_too_low_);
337  }
338  if (this->fault_inverter_voltage_too_high_) {
339  this->fault_inverter_voltage_too_high_->publish_state(value_fault_inverter_voltage_too_high_);
340  }
341  if (this->warning_over_temperature_) {
342  this->warning_over_temperature_->publish_state(value_warning_over_temperature_);
343  }
344  if (this->warning_fan_lock_) {
345  this->warning_fan_lock_->publish_state(value_warning_fan_lock_);
346  }
347  if (this->warning_battery_voltage_high_) {
348  this->warning_battery_voltage_high_->publish_state(value_warning_battery_voltage_high_);
349  }
350  if (this->warning_battery_low_alarm_) {
351  this->warning_battery_low_alarm_->publish_state(value_warning_battery_low_alarm_);
352  }
353  if (this->warning_battery_under_shutdown_) {
354  this->warning_battery_under_shutdown_->publish_state(value_warning_battery_under_shutdown_);
355  }
356  if (this->warning_battery_derating_) {
357  this->warning_battery_derating_->publish_state(value_warning_battery_derating_);
358  }
359  if (this->warning_over_load_) {
360  this->warning_over_load_->publish_state(value_warning_over_load_);
361  }
362  if (this->warning_eeprom_failed_) {
363  this->warning_eeprom_failed_->publish_state(value_warning_eeprom_failed_);
364  }
365  if (this->fault_inverter_over_current_) {
366  this->fault_inverter_over_current_->publish_state(value_fault_inverter_over_current_);
367  }
368  if (this->fault_inverter_soft_failed_) {
369  this->fault_inverter_soft_failed_->publish_state(value_fault_inverter_soft_failed_);
370  }
371  if (this->fault_self_test_failed_) {
372  this->fault_self_test_failed_->publish_state(value_fault_self_test_failed_);
373  }
374  if (this->fault_op_dc_voltage_over_) {
375  this->fault_op_dc_voltage_over_->publish_state(value_fault_op_dc_voltage_over_);
376  }
377  if (this->fault_battery_open_) {
378  this->fault_battery_open_->publish_state(value_fault_battery_open_);
379  }
380  if (this->fault_current_sensor_failed_) {
381  this->fault_current_sensor_failed_->publish_state(value_fault_current_sensor_failed_);
382  }
383  if (this->fault_battery_short_) {
384  this->fault_battery_short_->publish_state(value_fault_battery_short_);
385  }
386  if (this->warning_power_limit_) {
387  this->warning_power_limit_->publish_state(value_warning_power_limit_);
388  }
389  if (this->warning_pv_voltage_high_) {
390  this->warning_pv_voltage_high_->publish_state(value_warning_pv_voltage_high_);
391  }
392  if (this->fault_mppt_overload_) {
393  this->fault_mppt_overload_->publish_state(value_fault_mppt_overload_);
394  }
395  if (this->warning_mppt_overload_) {
396  this->warning_mppt_overload_->publish_state(value_warning_mppt_overload_);
397  }
398  if (this->warning_battery_too_low_to_charge_) {
399  this->warning_battery_too_low_to_charge_->publish_state(value_warning_battery_too_low_to_charge_);
400  }
401  if (this->fault_dc_dc_over_current_) {
402  this->fault_dc_dc_over_current_->publish_state(value_fault_dc_dc_over_current_);
403  }
404  if (this->fault_code_) {
405  this->fault_code_->publish_state(value_fault_code_);
406  }
407  if (this->warnung_low_pv_energy_) {
408  this->warnung_low_pv_energy_->publish_state(value_warnung_low_pv_energy_);
409  }
410  if (this->warning_high_ac_input_during_bus_soft_start_) {
411  this->warning_high_ac_input_during_bus_soft_start_->publish_state(
412  value_warning_high_ac_input_during_bus_soft_start_);
413  }
414  if (this->warning_battery_equalization_) {
415  this->warning_battery_equalization_->publish_state(value_warning_battery_equalization_);
416  }
417  this->state_ = STATE_IDLE;
418  break;
419  case POLLING_QT:
420  case POLLING_QMN:
421  this->state_ = STATE_IDLE;
422  break;
423  }
424  }
425 
426  if (this->state_ == STATE_POLL_CHECKED) {
427  bool enabled = true;
428  std::string fc;
429  char tmp[PIPSOLAR_READ_BUFFER_LENGTH];
430  sprintf(tmp, "%s", this->read_buffer_);
431  switch (this->used_polling_commands_[this->last_polling_command_].identifier) {
432  case POLLING_QPIRI:
433  ESP_LOGD(TAG, "Decode QPIRI");
434  sscanf(tmp, "(%f %f %f %f %f %d %d %f %f %f %f %f %d %d %d %d %d %d %d %d %d %d %f %d %d", // NOLINT
435  &value_grid_rating_voltage_, &value_grid_rating_current_, &value_ac_output_rating_voltage_, // NOLINT
436  &value_ac_output_rating_frequency_, &value_ac_output_rating_current_, // NOLINT
437  &value_ac_output_rating_apparent_power_, &value_ac_output_rating_active_power_, // NOLINT
438  &value_battery_rating_voltage_, &value_battery_recharge_voltage_, // NOLINT
439  &value_battery_under_voltage_, &value_battery_bulk_voltage_, &value_battery_float_voltage_, // NOLINT
440  &value_battery_type_, &value_current_max_ac_charging_current_, // NOLINT
441  &value_current_max_charging_current_, &value_input_voltage_range_, // NOLINT
442  &value_output_source_priority_, &value_charger_source_priority_, &value_parallel_max_num_, // NOLINT
443  &value_machine_type_, &value_topology_, &value_output_mode_, // NOLINT
444  &value_battery_redischarge_voltage_, &value_pv_ok_condition_for_parallel_, // NOLINT
445  &value_pv_power_balance_); // NOLINT
446  if (this->last_qpiri_) {
447  this->last_qpiri_->publish_state(tmp);
448  }
449  this->state_ = STATE_POLL_DECODED;
450  break;
451  case POLLING_QPIGS:
452  ESP_LOGD(TAG, "Decode QPIGS");
453  sscanf( // NOLINT
454  tmp, // NOLINT
455  "(%f %f %f %f %d %d %d %d %f %d %d %d %f %f %f %d %1d%1d%1d%1d%1d%1d%1d%1d %d %d %d %1d%1d%1d", // NOLINT
456  &value_grid_voltage_, &value_grid_frequency_, &value_ac_output_voltage_, // NOLINT
457  &value_ac_output_frequency_, // NOLINT
458  &value_ac_output_apparent_power_, &value_ac_output_active_power_, &value_output_load_percent_, // NOLINT
459  &value_bus_voltage_, &value_battery_voltage_, &value_battery_charging_current_, // NOLINT
460  &value_battery_capacity_percent_, &value_inverter_heat_sink_temperature_, // NOLINT
461  &value_pv_input_current_for_battery_, &value_pv_input_voltage_, &value_battery_voltage_scc_, // NOLINT
462  &value_battery_discharge_current_, &value_add_sbu_priority_version_, // NOLINT
463  &value_configuration_status_, &value_scc_firmware_version_, &value_load_status_, // NOLINT
464  &value_battery_voltage_to_steady_while_charging_, &value_charging_status_, // NOLINT
465  &value_scc_charging_status_, &value_ac_charging_status_, // NOLINT
466  &value_battery_voltage_offset_for_fans_on_, &value_eeprom_version_, &value_pv_charging_power_, // NOLINT
467  &value_charging_to_floating_mode_, &value_switch_on_, // NOLINT
468  &value_dustproof_installed_); // NOLINT
469  if (this->last_qpigs_) {
470  this->last_qpigs_->publish_state(tmp);
471  }
472  this->state_ = STATE_POLL_DECODED;
473  break;
474  case POLLING_QMOD:
475  ESP_LOGD(TAG, "Decode QMOD");
476  this->value_device_mode_ = char(this->read_buffer_[1]);
477  if (this->last_qmod_) {
478  this->last_qmod_->publish_state(tmp);
479  }
480  this->state_ = STATE_POLL_DECODED;
481  break;
482  case POLLING_QFLAG:
483  ESP_LOGD(TAG, "Decode QFLAG");
484  // result like:"(EbkuvxzDajy"
485  // get through all char: ignore first "(" Enable flag on 'E', Disable on 'D') else set the corresponding value
486  for (size_t i = 1; i < strlen(tmp); i++) {
487  switch (tmp[i]) {
488  case 'E':
489  enabled = true;
490  break;
491  case 'D':
492  enabled = false;
493  break;
494  case 'a':
495  this->value_silence_buzzer_open_buzzer_ = enabled;
496  break;
497  case 'b':
498  this->value_overload_bypass_function_ = enabled;
499  break;
500  case 'k':
501  this->value_lcd_escape_to_default_ = enabled;
502  break;
503  case 'u':
504  this->value_overload_restart_function_ = enabled;
505  break;
506  case 'v':
507  this->value_over_temperature_restart_function_ = enabled;
508  break;
509  case 'x':
510  this->value_backlight_on_ = enabled;
511  break;
512  case 'y':
513  this->value_alarm_on_when_primary_source_interrupt_ = enabled;
514  break;
515  case 'z':
516  this->value_fault_code_record_ = enabled;
517  break;
518  case 'j':
519  this->value_power_saving_ = enabled;
520  break;
521  }
522  }
523  if (this->last_qflag_) {
524  this->last_qflag_->publish_state(tmp);
525  }
526  this->state_ = STATE_POLL_DECODED;
527  break;
528  case POLLING_QPIWS:
529  ESP_LOGD(TAG, "Decode QPIWS");
530  // '(00000000000000000000000000000000'
531  // iterate over all available flag (as not all models have all flags, but at least in the same order)
532  this->value_warnings_present_ = false;
533  this->value_faults_present_ = true;
534 
535  for (size_t i = 1; i < strlen(tmp); i++) {
536  enabled = tmp[i] == '1';
537  switch (i) {
538  case 1:
539  this->value_warning_power_loss_ = enabled;
540  this->value_warnings_present_ += enabled;
541  break;
542  case 2:
543  this->value_fault_inverter_fault_ = enabled;
544  this->value_faults_present_ += enabled;
545  break;
546  case 3:
547  this->value_fault_bus_over_ = enabled;
548  this->value_faults_present_ += enabled;
549  break;
550  case 4:
551  this->value_fault_bus_under_ = enabled;
552  this->value_faults_present_ += enabled;
553  break;
554  case 5:
555  this->value_fault_bus_soft_fail_ = enabled;
556  this->value_faults_present_ += enabled;
557  break;
558  case 6:
559  this->value_warning_line_fail_ = enabled;
560  this->value_warnings_present_ += enabled;
561  break;
562  case 7:
563  this->value_fault_opvshort_ = enabled;
564  this->value_faults_present_ += enabled;
565  break;
566  case 8:
567  this->value_fault_inverter_voltage_too_low_ = enabled;
568  this->value_faults_present_ += enabled;
569  break;
570  case 9:
571  this->value_fault_inverter_voltage_too_high_ = enabled;
572  this->value_faults_present_ += enabled;
573  break;
574  case 10:
575  this->value_warning_over_temperature_ = enabled;
576  this->value_warnings_present_ += enabled;
577  break;
578  case 11:
579  this->value_warning_fan_lock_ = enabled;
580  this->value_warnings_present_ += enabled;
581  break;
582  case 12:
583  this->value_warning_battery_voltage_high_ = enabled;
584  this->value_warnings_present_ += enabled;
585  break;
586  case 13:
587  this->value_warning_battery_low_alarm_ = enabled;
588  this->value_warnings_present_ += enabled;
589  break;
590  case 15:
591  this->value_warning_battery_under_shutdown_ = enabled;
592  this->value_warnings_present_ += enabled;
593  break;
594  case 16:
595  this->value_warning_battery_derating_ = enabled;
596  this->value_warnings_present_ += enabled;
597  break;
598  case 17:
599  this->value_warning_over_load_ = enabled;
600  this->value_warnings_present_ += enabled;
601  break;
602  case 18:
603  this->value_warning_eeprom_failed_ = enabled;
604  this->value_warnings_present_ += enabled;
605  break;
606  case 19:
607  this->value_fault_inverter_over_current_ = enabled;
608  this->value_faults_present_ += enabled;
609  break;
610  case 20:
611  this->value_fault_inverter_soft_failed_ = enabled;
612  this->value_faults_present_ += enabled;
613  break;
614  case 21:
615  this->value_fault_self_test_failed_ = enabled;
616  this->value_faults_present_ += enabled;
617  break;
618  case 22:
619  this->value_fault_op_dc_voltage_over_ = enabled;
620  this->value_faults_present_ += enabled;
621  break;
622  case 23:
623  this->value_fault_battery_open_ = enabled;
624  this->value_faults_present_ += enabled;
625  break;
626  case 24:
627  this->value_fault_current_sensor_failed_ = enabled;
628  this->value_faults_present_ += enabled;
629  break;
630  case 25:
631  this->value_fault_battery_short_ = enabled;
632  this->value_faults_present_ += enabled;
633  break;
634  case 26:
635  this->value_warning_power_limit_ = enabled;
636  this->value_warnings_present_ += enabled;
637  break;
638  case 27:
639  this->value_warning_pv_voltage_high_ = enabled;
640  this->value_warnings_present_ += enabled;
641  break;
642  case 28:
643  this->value_fault_mppt_overload_ = enabled;
644  this->value_faults_present_ += enabled;
645  break;
646  case 29:
647  this->value_warning_mppt_overload_ = enabled;
648  this->value_warnings_present_ += enabled;
649  break;
650  case 30:
651  this->value_warning_battery_too_low_to_charge_ = enabled;
652  this->value_warnings_present_ += enabled;
653  break;
654  case 31:
655  this->value_fault_dc_dc_over_current_ = enabled;
656  this->value_faults_present_ += enabled;
657  break;
658  case 32:
659  fc = tmp[i];
660  fc += tmp[i + 1];
661  this->value_fault_code_ = parse_number<int>(fc).value_or(0);
662  break;
663  case 34:
664  this->value_warnung_low_pv_energy_ = enabled;
665  this->value_warnings_present_ += enabled;
666  break;
667  case 35:
668  this->value_warning_high_ac_input_during_bus_soft_start_ = enabled;
669  this->value_warnings_present_ += enabled;
670  break;
671  case 36:
672  this->value_warning_battery_equalization_ = enabled;
673  this->value_warnings_present_ += enabled;
674  break;
675  }
676  }
677  if (this->last_qpiws_) {
678  this->last_qpiws_->publish_state(tmp);
679  }
680  this->state_ = STATE_POLL_DECODED;
681  break;
682  case POLLING_QT:
683  ESP_LOGD(TAG, "Decode QT");
684  if (this->last_qt_) {
685  this->last_qt_->publish_state(tmp);
686  }
687  this->state_ = STATE_POLL_DECODED;
688  break;
689  case POLLING_QMN:
690  ESP_LOGD(TAG, "Decode QMN");
691  if (this->last_qmn_) {
692  this->last_qmn_->publish_state(tmp);
693  }
694  this->state_ = STATE_POLL_DECODED;
695  break;
696  default:
697  this->state_ = STATE_IDLE;
698  break;
699  }
700  return;
701  }
702 
703  if (this->state_ == STATE_POLL_COMPLETE) {
704  if (this->check_incoming_crc_()) {
705  if (this->read_buffer_[0] == '(' && this->read_buffer_[1] == 'N' && this->read_buffer_[2] == 'A' &&
706  this->read_buffer_[3] == 'K') {
707  this->state_ = STATE_IDLE;
708  return;
709  }
710  // crc ok
711  this->state_ = STATE_POLL_CHECKED;
712  return;
713  } else {
714  this->state_ = STATE_IDLE;
715  }
716  }
717 
718  if (this->state_ == STATE_COMMAND || this->state_ == STATE_POLL) {
719  while (this->available()) {
720  uint8_t byte;
721  this->read_byte(&byte);
722 
723  if (this->read_pos_ == PIPSOLAR_READ_BUFFER_LENGTH) {
724  this->read_pos_ = 0;
725  this->empty_uart_buffer_();
726  }
727  this->read_buffer_[this->read_pos_] = byte;
728  this->read_pos_++;
729 
730  // end of answer
731  if (byte == 0x0D) {
732  this->read_buffer_[this->read_pos_] = 0;
733  this->empty_uart_buffer_();
734  if (this->state_ == STATE_POLL) {
735  this->state_ = STATE_POLL_COMPLETE;
736  }
737  if (this->state_ == STATE_COMMAND) {
739  }
740  }
741  } // available
742  }
743  if (this->state_ == STATE_COMMAND) {
745  // command timeout
746  const char *command = this->command_queue_[this->command_queue_position_].c_str();
747  this->command_start_millis_ = millis();
748  ESP_LOGD(TAG, "timeout command from queue: %s", command);
749  this->command_queue_[this->command_queue_position_] = std::string("");
751  this->state_ = STATE_IDLE;
752  return;
753  } else {
754  }
755  }
756  if (this->state_ == STATE_POLL) {
758  // command timeout
759  ESP_LOGD(TAG, "timeout command to poll: %s", this->used_polling_commands_[this->last_polling_command_].command);
760  this->state_ = STATE_IDLE;
761  } else {
762  }
763  }
764 }
765 
767  if (this->read_pos_ - 3 == length) {
768  return 1;
769  }
770  return 0;
771 }
772 
774  uint16_t crc16;
775  crc16 = this->pipsolar_crc_(read_buffer_, read_pos_ - 3);
776  ESP_LOGD(TAG, "checking crc on incoming message");
777  if (((uint8_t) ((crc16) >> 8)) == read_buffer_[read_pos_ - 3] &&
778  ((uint8_t) ((crc16) &0xff)) == read_buffer_[read_pos_ - 2]) {
779  ESP_LOGD(TAG, "CRC OK");
780  read_buffer_[read_pos_ - 1] = 0;
781  read_buffer_[read_pos_ - 2] = 0;
782  read_buffer_[read_pos_ - 3] = 0;
783  return 1;
784  }
785  ESP_LOGD(TAG, "CRC NOK expected: %X %X but got: %X %X", ((uint8_t) ((crc16) >> 8)), ((uint8_t) ((crc16) &0xff)),
787  return 0;
788 }
789 
790 // send next command used
792  uint16_t crc16;
793  if (!this->command_queue_[this->command_queue_position_].empty()) {
794  const char *command = this->command_queue_[this->command_queue_position_].c_str();
795  uint8_t byte_command[16];
796  uint8_t length = this->command_queue_[this->command_queue_position_].length();
797  for (uint8_t i = 0; i < length; i++) {
798  byte_command[i] = (uint8_t) this->command_queue_[this->command_queue_position_].at(i);
799  }
800  this->state_ = STATE_COMMAND;
801  this->command_start_millis_ = millis();
802  this->empty_uart_buffer_();
803  this->read_pos_ = 0;
804  crc16 = this->pipsolar_crc_(byte_command, length);
805  this->write_str(command);
806  // checksum
807  this->write(((uint8_t) ((crc16) >> 8))); // highbyte
808  this->write(((uint8_t) ((crc16) &0xff))); // lowbyte
809  // end Byte
810  this->write(0x0D);
811  ESP_LOGD(TAG, "Sending command from queue: %s with length %d", command, length);
812  return 1;
813  }
814  return 0;
815 }
816 
818  uint16_t crc16;
819  this->last_polling_command_ = (this->last_polling_command_ + 1) % 15;
820  if (this->used_polling_commands_[this->last_polling_command_].length == 0) {
821  this->last_polling_command_ = 0;
822  }
823  if (this->used_polling_commands_[this->last_polling_command_].length == 0) {
824  // no command specified
825  return;
826  }
827  this->state_ = STATE_POLL;
828  this->command_start_millis_ = millis();
829  this->empty_uart_buffer_();
830  this->read_pos_ = 0;
831  crc16 = this->pipsolar_crc_(this->used_polling_commands_[this->last_polling_command_].command,
833  this->write_array(this->used_polling_commands_[this->last_polling_command_].command,
834  this->used_polling_commands_[this->last_polling_command_].length);
835  // checksum
836  this->write(((uint8_t) ((crc16) >> 8))); // highbyte
837  this->write(((uint8_t) ((crc16) &0xff))); // lowbyte
838  // end Byte
839  this->write(0x0D);
840  ESP_LOGD(TAG, "Sending polling command : %s with length %d",
841  this->used_polling_commands_[this->last_polling_command_].command,
842  this->used_polling_commands_[this->last_polling_command_].length);
843 }
844 
845 void Pipsolar::queue_command_(const char *command, uint8_t length) {
846  uint8_t next_position = command_queue_position_;
847  for (uint8_t i = 0; i < COMMAND_QUEUE_LENGTH; i++) {
848  uint8_t testposition = (next_position + i) % COMMAND_QUEUE_LENGTH;
849  if (command_queue_[testposition].empty()) {
850  command_queue_[testposition] = command;
851  ESP_LOGD(TAG, "Command queued successfully: %s with length %u at position %d", command,
852  command_queue_[testposition].length(), testposition);
853  return;
854  }
855  }
856  ESP_LOGD(TAG, "Command queue full dropping command: %s", command);
857 }
858 
859 void Pipsolar::switch_command(const std::string &command) {
860  ESP_LOGD(TAG, "got command: %s", command.c_str());
861  queue_command_(command.c_str(), command.length());
862 }
863 void Pipsolar::dump_config() {
864  ESP_LOGCONFIG(TAG, "Pipsolar:");
865  ESP_LOGCONFIG(TAG, "used commands:");
866  for (auto &used_polling_command : this->used_polling_commands_) {
867  if (used_polling_command.length != 0) {
868  ESP_LOGCONFIG(TAG, "%s", used_polling_command.command);
869  }
870  }
871 }
872 void Pipsolar::update() {}
873 
874 void Pipsolar::add_polling_command_(const char *command, ENUMPollingCommand polling_command) {
875  for (auto &used_polling_command : this->used_polling_commands_) {
876  if (used_polling_command.length == strlen(command)) {
877  uint8_t len = strlen(command);
878  if (memcmp(used_polling_command.command, command, len) == 0) {
879  return;
880  }
881  }
882  if (used_polling_command.length == 0) {
883  size_t length = strlen(command) + 1;
884  const char *beg = command;
885  const char *end = command + length;
886  used_polling_command.command = new uint8_t[length]; // NOLINT(cppcoreguidelines-owning-memory)
887  size_t i = 0;
888  for (; beg != end; ++beg, ++i) {
889  used_polling_command.command[i] = (uint8_t) (*beg);
890  }
891  used_polling_command.errors = 0;
892  used_polling_command.identifier = polling_command;
893  used_polling_command.length = length - 1;
894  return;
895  }
896  }
897 }
898 
899 uint16_t Pipsolar::pipsolar_crc_(uint8_t *msg, uint8_t len) {
900  uint16_t crc = crc16be(msg, len);
901  uint8_t crc_low = crc & 0xff;
902  uint8_t crc_high = crc >> 8;
903  if (crc_low == 0x28 || crc_low == 0x0d || crc_low == 0x0a)
904  crc_low++;
905  if (crc_high == 0x28 || crc_high == 0x0d || crc_high == 0x0a)
906  crc_high++;
907  crc = (crc_high << 8) | crc_low;
908  return crc;
909 }
910 
911 } // namespace pipsolar
912 } // namespace esphome
void write_str(const char *str)
Definition: uart.h:27
std::string command_queue_[COMMAND_QUEUE_LENGTH]
Definition: pipsolar.h:201
static const size_t COMMAND_QUEUE_LENGTH
Definition: pipsolar.h:190
uint16_t crc16be(const uint8_t *data, uint16_t len, uint16_t crc, uint16_t poly, bool refin, bool refout)
Definition: helpers.cpp:149
void write_array(const uint8_t *data, size_t len)
Definition: uart.h:21
uint8_t check_incoming_length_(uint8_t length)
Definition: pipsolar.cpp:766
static const size_t COMMAND_TIMEOUT
Definition: pipsolar.h:191
uint8_t read_buffer_[PIPSOLAR_READ_BUFFER_LENGTH]
Definition: pipsolar.h:203
void add_polling_command_(const char *command, ENUMPollingCommand polling_command)
Definition: pipsolar.cpp:874
uint16_t crc16(const uint8_t *data, uint16_t len, uint16_t crc, uint16_t reverse_poly, bool refin, bool refout)
Calculate a CRC-16 checksum of data with size len.
Definition: helpers.cpp:111
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:25
uint16_t pipsolar_crc_(uint8_t *msg, uint8_t len)
Definition: pipsolar.cpp:899
virtual void setup()
Where the component&#39;s initialization should happen.
Definition: component.cpp:48
bool read_byte(uint8_t *data)
Definition: uart.h:29
void queue_command_(const char *command, uint8_t length)
Definition: pipsolar.cpp:845
BedjetMode mode
BedJet operating mode.
Definition: bedjet_codec.h:183
static const size_t PIPSOLAR_READ_BUFFER_LENGTH
Definition: pipsolar.h:189
PollingCommand used_polling_commands_[15]
Definition: pipsolar.h:219
std::string size_t len
Definition: helpers.h:293
uint16_t length
Definition: tt21100.cpp:12
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
uint8_t end[39]
Definition: sun_gtil2.cpp:31
size_t write(uint8_t data)
Definition: uart.h:52