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