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