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