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