From 0b509f60b439d1ae0b438124e9bf197879aacf2a Mon Sep 17 00:00:00 2001 From: chefpro Date: Wed, 10 Jan 2024 09:21:02 +0100 Subject: [PATCH] Add support for pipsolar inverter (#20408) * Add support for pipsolar inverter * Fix CI-Warnings * Remove unneeded define * Pipsolar: Fix dat result * Add support for the rules engine --------- Co-authored-by: Peter Rustler --- CODE_OWNERS.md | 1 + tasmota/berry/include/be_gpio_defines.h | 2 + tasmota/include/i18n.h | 13 + tasmota/include/tasmota_template.h | 7 + tasmota/language/af_AF.h | 4 + tasmota/language/bg_BG.h | 4 + tasmota/language/ca_AD.h | 4 + tasmota/language/cs_CZ.h | 4 + tasmota/language/de_DE.h | 4 + tasmota/language/el_GR.h | 4 + tasmota/language/en_GB.h | 4 + tasmota/language/es_ES.h | 4 + tasmota/language/fr_FR.h | 4 + tasmota/language/fy_NL.h | 4 + tasmota/language/he_HE.h | 4 + tasmota/language/hu_HU.h | 4 + tasmota/language/it_IT.h | 4 + tasmota/language/ko_KO.h | 4 + tasmota/language/nl_NL.h | 4 + tasmota/language/pl_PL.h | 4 + tasmota/language/pt_BR.h | 4 + tasmota/language/pt_PT.h | 4 + tasmota/language/ro_RO.h | 4 + tasmota/language/ru_RU.h | 4 + tasmota/language/sk_SK.h | 4 + tasmota/language/sv_SE.h | 4 + tasmota/language/tr_TR.h | 4 + tasmota/language/uk_UA.h | 4 + tasmota/language/vi_VN.h | 4 + tasmota/language/zh_CN.h | 4 + tasmota/language/zh_TW.h | 4 + .../tasmota_xdrv_driver/xdrv_92_pipsolar.ino | 1050 +++++++++++++++++ tools/lv_gpio/lv_gpio_enum.h | 2 + 33 files changed, 1183 insertions(+) create mode 100644 tasmota/tasmota_xdrv_driver/xdrv_92_pipsolar.ino diff --git a/CODE_OWNERS.md b/CODE_OWNERS.md index b0d05100f..56a053548 100644 --- a/CODE_OWNERS.md +++ b/CODE_OWNERS.md @@ -100,6 +100,7 @@ In addition to @arendst the following code is mainly owned by: | xdrv_88_esp32_shelly_pro | @arendst | xdrv_89_esp32_dali | @eeak | xdrv_90_esp32_dingtian_relay | @barbudor +| xdrv_92_pipsolar | @chefpro | | | xdrv_122_file_settings_demo | @arendst | xdrv_127_debug | @arendst diff --git a/tasmota/berry/include/be_gpio_defines.h b/tasmota/berry/include/be_gpio_defines.h index 4f6ccde55..452a5414b 100644 --- a/tasmota/berry/include/be_gpio_defines.h +++ b/tasmota/berry/include/be_gpio_defines.h @@ -334,6 +334,8 @@ const be_const_member_t lv_gpio_constants[] = { { "ZIGBEE_RST", (int32_t) GPIO_ZIGBEE_RST }, { "ZIGBEE_RX", (int32_t) GPIO_ZIGBEE_RX }, { "ZIGBEE_TX", (int32_t) GPIO_ZIGBEE_TX }, + { "PIPSOLAR_RX", (int32_t) GPIO_PIPSOLAR_RX }, + { "PIPSOLAR_TX", (int32_t) GPIO_PIPSOLAR_TX }, }; diff --git a/tasmota/include/i18n.h b/tasmota/include/i18n.h index e5cdb2f61..481456201 100644 --- a/tasmota/include/i18n.h +++ b/tasmota/include/i18n.h @@ -809,6 +809,19 @@ // xsns_71_veml7700.ino #define D_JSON_WHITE_CONTENT "WhiteContent" +// xdrv_92_pipsolar.ino +#define D_CMND_PIP_PREFIX "PipSolar" +#define D_CMND_PIP_QT "QT" +#define D_CMND_PIP_QET "QET" +#define D_CMND_PIP_QEY "QEY" +#define D_CMND_PIP_QEM "QEM" +#define D_CMND_PIP_QED "QED" +#define D_CMND_PIP_QEH "QEH" +#define D_CMND_PIP_DAT "DAT" +#define D_CMND_PIP_POLLVALUES "PollValues" +#define D_CMND_PIP_BAUDRATE "BaudRate" +#define D_CMND_PIP_SERIALCONFIG "SerialConfig" + /********************************************************************************************/ // Log message prefix diff --git a/tasmota/include/tasmota_template.h b/tasmota/include/tasmota_template.h index 1d09cb47b..340d1ab21 100644 --- a/tasmota/include/tasmota_template.h +++ b/tasmota/include/tasmota_template.h @@ -215,6 +215,7 @@ enum UserSelectablePins { GPIO_HC8_RXD, // HC8 Serial interface GPIO_I2S_DAC, // Audio DAC support for ESP32 and ESP32S2 GPIO_MAGIC_SWITCH, // MagicSwitch as in Sonoff BasicR4 + GPIO_PIPSOLAR_TX, GPIO_PIPSOLAR_RX, // pipsolar inverter GPIO_SENSOR_END }; // Error as warning to rethink GPIO usage with max 2045 @@ -477,6 +478,7 @@ const char kSensorNames[] PROGMEM = D_SENSOR_HC8_RX "|" D_SENSOR_I2S_DAC "|" D_GPIO_MAGIC_SWITCH "|" + D_SENSOR_PIPSOLAR_TX "|" D_SENSOR_PIPSOLAR_RX "|" ; const char kSensorNamesFixed[] PROGMEM = @@ -1150,6 +1152,11 @@ const uint16_t kGpioNiceList[] PROGMEM = { AGPIO(GPIO_MAGIC_SWITCH) + MAX_MAGIC_SWITCH_MODES, #endif +#ifdef USE_PIPSOLAR // xdrv_92_pipsolar.ino + AGPIO(GPIO_PIPSOLAR_TX), // pipsolar inverter Serial interface + AGPIO(GPIO_PIPSOLAR_RX), // pipsolar inverter Serial interface +#endif + /*-------------------------------------------------------------------------------------------*\ * ESP32 specifics \*-------------------------------------------------------------------------------------------*/ diff --git a/tasmota/language/af_AF.h b/tasmota/language/af_AF.h index 406c96c45..7224bd8db 100644 --- a/tasmota/language/af_AF.h +++ b/tasmota/language/af_AF.h @@ -1223,4 +1223,8 @@ #define D_AVG_RAD_DOSE "Gemiddelde Stralingsdosis" #define D_UNIT_US_H "µSv/h" +// ixrv92_pipsolar.ino +#define D_SENSOR_PIPSOLAR_TX "Pipsolar TX" +#define D_SENSOR_PIPSOLAR_RX "Pipsolar RX" + #endif // _LANGUAGE_AF_AF_H_ diff --git a/tasmota/language/bg_BG.h b/tasmota/language/bg_BG.h index 8d02d6967..54e5b9e52 100644 --- a/tasmota/language/bg_BG.h +++ b/tasmota/language/bg_BG.h @@ -1223,4 +1223,8 @@ #define D_AVG_RAD_DOSE "средна доза радиация" #define D_UNIT_US_H "µSv/h" +// ixrv92_pipsolar.ino +#define D_SENSOR_PIPSOLAR_TX "Pipsolar TX" +#define D_SENSOR_PIPSOLAR_RX "Pipsolar RX" + #endif // _LANGUAGE_BG_BG_H_ diff --git a/tasmota/language/ca_AD.h b/tasmota/language/ca_AD.h index 55fe18f99..086e9c732 100644 --- a/tasmota/language/ca_AD.h +++ b/tasmota/language/ca_AD.h @@ -1223,4 +1223,8 @@ #define D_AVG_RAD_DOSE "dosi mitjana de radiació" #define D_UNIT_US_H "µSv/h" +// ixrv92_pipsolar.ino +#define D_SENSOR_PIPSOLAR_TX "Pipsolar TX" +#define D_SENSOR_PIPSOLAR_RX "Pipsolar RX" + #endif // _LANGUAGE_CA_AD_H_ diff --git a/tasmota/language/cs_CZ.h b/tasmota/language/cs_CZ.h index 71453c987..1de0dea2a 100644 --- a/tasmota/language/cs_CZ.h +++ b/tasmota/language/cs_CZ.h @@ -1223,4 +1223,8 @@ #define D_AVG_RAD_DOSE "průměrná dávka záření" #define D_UNIT_US_H "µSv/h" +// ixrv92_pipsolar.ino +#define D_SENSOR_PIPSOLAR_TX "Pipsolar TX" +#define D_SENSOR_PIPSOLAR_RX "Pipsolar RX" + #endif // _LANGUAGE_CS_CZ_H_ diff --git a/tasmota/language/de_DE.h b/tasmota/language/de_DE.h index c4a3ce265..8c5c5e0f6 100644 --- a/tasmota/language/de_DE.h +++ b/tasmota/language/de_DE.h @@ -1223,4 +1223,8 @@ #define D_AVG_RAD_DOSE "durchschnittliche Strahlendosis" #define D_UNIT_US_H "µSv/h" +// ixrv92_pipsolar.ino +#define D_SENSOR_PIPSOLAR_TX "Pipsolar TX" +#define D_SENSOR_PIPSOLAR_RX "Pipsolar RX" + #endif // _LANGUAGE_DE_DE_H_ diff --git a/tasmota/language/el_GR.h b/tasmota/language/el_GR.h index 7efd379ca..ca6c28fd9 100644 --- a/tasmota/language/el_GR.h +++ b/tasmota/language/el_GR.h @@ -1223,4 +1223,8 @@ #define D_AVG_RAD_DOSE "μέση δόση ακτινοβολίας" #define D_UNIT_US_H "µSv/h" +// ixrv92_pipsolar.ino +#define D_SENSOR_PIPSOLAR_TX "Pipsolar TX" +#define D_SENSOR_PIPSOLAR_RX "Pipsolar RX" + #endif // _LANGUAGE_EL_GR_H_ diff --git a/tasmota/language/en_GB.h b/tasmota/language/en_GB.h index 8d82a9408..720f1f169 100644 --- a/tasmota/language/en_GB.h +++ b/tasmota/language/en_GB.h @@ -1224,4 +1224,8 @@ #define D_AVG_RAD_DOSE "Radiation" #define D_UNIT_US_H "µSv/h" +// ixrv92_pipsolar.ino +#define D_SENSOR_PIPSOLAR_TX "Pipsolar TX" +#define D_SENSOR_PIPSOLAR_RX "Pipsolar RX" + #endif // _LANGUAGE_EN_GB_H_ diff --git a/tasmota/language/es_ES.h b/tasmota/language/es_ES.h index 9e6c0ed8d..bc8e5728f 100644 --- a/tasmota/language/es_ES.h +++ b/tasmota/language/es_ES.h @@ -1223,4 +1223,8 @@ #define D_AVG_RAD_DOSE "dosis media de radiación" #define D_UNIT_US_H "µSv/h" +// ixrv92_pipsolar.ino +#define D_SENSOR_PIPSOLAR_TX "Pipsolar TX" +#define D_SENSOR_PIPSOLAR_RX "Pipsolar RX" + #endif // _LANGUAGE_ES_ES_H_ diff --git a/tasmota/language/fr_FR.h b/tasmota/language/fr_FR.h index c1063146d..1c3fe23fe 100644 --- a/tasmota/language/fr_FR.h +++ b/tasmota/language/fr_FR.h @@ -1224,4 +1224,8 @@ #define D_AVG_RAD_DOSE "dose moyenne de rayonnement" #define D_UNIT_US_H "µSv/h" +// ixrv92_pipsolar.ino +#define D_SENSOR_PIPSOLAR_TX "Pipsolar TX" +#define D_SENSOR_PIPSOLAR_RX "Pipsolar RX" + #endif // _LANGUAGE_FR_FR_H_ diff --git a/tasmota/language/fy_NL.h b/tasmota/language/fy_NL.h index fcf0973a2..917fa9704 100644 --- a/tasmota/language/fy_NL.h +++ b/tasmota/language/fy_NL.h @@ -1223,4 +1223,8 @@ #define D_AVG_RAD_DOSE "gemiddelde stralingsdosis" #define D_UNIT_US_H "µSv/h" +// ixrv92_pipsolar.ino +#define D_SENSOR_PIPSOLAR_TX "Pipsolar TX" +#define D_SENSOR_PIPSOLAR_RX "Pipsolar RX" + #endif // _LANGUAGE_FY_NL_H_ diff --git a/tasmota/language/he_HE.h b/tasmota/language/he_HE.h index fc74df7ff..978149adc 100644 --- a/tasmota/language/he_HE.h +++ b/tasmota/language/he_HE.h @@ -1223,4 +1223,8 @@ #define D_AVG_RAD_DOSE "מינון קרינה ממוצע" #define D_UNIT_US_H "µSv/h" +// ixrv92_pipsolar.ino +#define D_SENSOR_PIPSOLAR_TX "Pipsolar TX" +#define D_SENSOR_PIPSOLAR_RX "Pipsolar RX" + #endif // _LANGUAGE_HE_HE_H_ diff --git a/tasmota/language/hu_HU.h b/tasmota/language/hu_HU.h index 8592a911e..58fd37d0d 100644 --- a/tasmota/language/hu_HU.h +++ b/tasmota/language/hu_HU.h @@ -1226,4 +1226,8 @@ #define D_AVG_RAD_DOSE "átlagos sugárdózis" #define D_UNIT_US_H "µSv/h" +// ixrv92_pipsolar.ino +#define D_SENSOR_PIPSOLAR_TX "Pipsolar TX" +#define D_SENSOR_PIPSOLAR_RX "Pipsolar RX" + #endif // _LANGUAGE_HU_HU_H_ diff --git a/tasmota/language/it_IT.h b/tasmota/language/it_IT.h index b35a353af..75a216e10 100644 --- a/tasmota/language/it_IT.h +++ b/tasmota/language/it_IT.h @@ -1223,4 +1223,8 @@ #define D_AVG_RAD_DOSE "Radiazioni" #define D_UNIT_US_H "µSv/h" +// ixrv92_pipsolar.ino +#define D_SENSOR_PIPSOLAR_TX "Pipsolar TX" +#define D_SENSOR_PIPSOLAR_RX "Pipsolar RX" + #endif // _LANGUAGE_IT_IT_H_ diff --git a/tasmota/language/ko_KO.h b/tasmota/language/ko_KO.h index e0b6871f6..c02ce1fc1 100644 --- a/tasmota/language/ko_KO.h +++ b/tasmota/language/ko_KO.h @@ -1223,4 +1223,8 @@ #define D_AVG_RAD_DOSE "average radiation dose" #define D_UNIT_US_H "µSv/h" +// ixrv92_pipsolar.ino +#define D_SENSOR_PIPSOLAR_TX "Pipsolar TX" +#define D_SENSOR_PIPSOLAR_RX "Pipsolar RX" + #endif // _LANGUAGE_KO_KO_H_ diff --git a/tasmota/language/nl_NL.h b/tasmota/language/nl_NL.h index 6cd4b2c2b..a5dbe04aa 100644 --- a/tasmota/language/nl_NL.h +++ b/tasmota/language/nl_NL.h @@ -1223,4 +1223,8 @@ #define D_AVG_RAD_DOSE "gemiddelde stralingsdosis" #define D_UNIT_US_H "µSv/h" +// ixrv92_pipsolar.ino +#define D_SENSOR_PIPSOLAR_TX "Pipsolar TX" +#define D_SENSOR_PIPSOLAR_RX "Pipsolar RX" + #endif // _LANGUAGE_NL_NL_H_ diff --git a/tasmota/language/pl_PL.h b/tasmota/language/pl_PL.h index b6d6f9fe7..d890ab6ed 100644 --- a/tasmota/language/pl_PL.h +++ b/tasmota/language/pl_PL.h @@ -1223,4 +1223,8 @@ #define D_AVG_RAD_DOSE "Średnia Dawka Promieniowania" #define D_UNIT_US_H "µSv/h" +// ixrv92_pipsolar.ino +#define D_SENSOR_PIPSOLAR_TX "Pipsolar TX" +#define D_SENSOR_PIPSOLAR_RX "Pipsolar RX" + #endif // _LANGUAGE_PL_PL_D_H_ diff --git a/tasmota/language/pt_BR.h b/tasmota/language/pt_BR.h index f73294280..75b176504 100644 --- a/tasmota/language/pt_BR.h +++ b/tasmota/language/pt_BR.h @@ -1223,4 +1223,8 @@ #define D_AVG_RAD_DOSE "dose média de radiação" #define D_UNIT_US_H "µSv/h" +// ixrv92_pipsolar.ino +#define D_SENSOR_PIPSOLAR_TX "Pipsolar TX" +#define D_SENSOR_PIPSOLAR_RX "Pipsolar RX" + #endif // _LANGUAGE_PT_BR_H_ diff --git a/tasmota/language/pt_PT.h b/tasmota/language/pt_PT.h index d29fd64cf..6f06e7222 100644 --- a/tasmota/language/pt_PT.h +++ b/tasmota/language/pt_PT.h @@ -1223,4 +1223,8 @@ #define D_AVG_RAD_DOSE "dose média de radiação" #define D_UNIT_US_H "µSv/h" +// ixrv92_pipsolar.ino +#define D_SENSOR_PIPSOLAR_TX "Pipsolar TX" +#define D_SENSOR_PIPSOLAR_RX "Pipsolar RX" + #endif // _LANGUAGE_PT_PT_H_ diff --git a/tasmota/language/ro_RO.h b/tasmota/language/ro_RO.h index 754f0021c..344fe3efe 100644 --- a/tasmota/language/ro_RO.h +++ b/tasmota/language/ro_RO.h @@ -1223,4 +1223,8 @@ #define D_AVG_RAD_DOSE "doza medie de radiație" #define D_UNIT_US_H "µSv/h" +// ixrv92_pipsolar.ino +#define D_SENSOR_PIPSOLAR_TX "Pipsolar TX" +#define D_SENSOR_PIPSOLAR_RX "Pipsolar RX" + #endif // _LANGUAGE_RO_RO_H_ diff --git a/tasmota/language/ru_RU.h b/tasmota/language/ru_RU.h index 4e4a729e7..5291fe748 100644 --- a/tasmota/language/ru_RU.h +++ b/tasmota/language/ru_RU.h @@ -1224,4 +1224,8 @@ #define D_AVG_RAD_DOSE "средняя доза облучения" #define D_UNIT_US_H "µSv/h" +// ixrv92_pipsolar.ino +#define D_SENSOR_PIPSOLAR_TX "Pipsolar TX" +#define D_SENSOR_PIPSOLAR_RX "Pipsolar RX" + #endif // _LANGUAGE_RU_RU_H_ diff --git a/tasmota/language/sk_SK.h b/tasmota/language/sk_SK.h index 0bdbc978e..9bd615000 100644 --- a/tasmota/language/sk_SK.h +++ b/tasmota/language/sk_SK.h @@ -1223,4 +1223,8 @@ #define D_AVG_RAD_DOSE "priemerná dávka žiarenia" #define D_UNIT_US_H "µSv/h" +// ixrv92_pipsolar.ino +#define D_SENSOR_PIPSOLAR_TX "Pipsolar TX" +#define D_SENSOR_PIPSOLAR_RX "Pipsolar RX" + #endif // _LANGUAGE_SK_SK_H_ diff --git a/tasmota/language/sv_SE.h b/tasmota/language/sv_SE.h index b702a0054..9f64ab048 100644 --- a/tasmota/language/sv_SE.h +++ b/tasmota/language/sv_SE.h @@ -1223,4 +1223,8 @@ #define D_AVG_RAD_DOSE "genomsnittlig stråldos" #define D_UNIT_US_H "µSv/h" +// ixrv92_pipsolar.ino +#define D_SENSOR_PIPSOLAR_TX "Pipsolar TX" +#define D_SENSOR_PIPSOLAR_RX "Pipsolar RX" + #endif // _LANGUAGE_SV_SE_H_ diff --git a/tasmota/language/tr_TR.h b/tasmota/language/tr_TR.h index 25fdddaa0..3fc97e797 100644 --- a/tasmota/language/tr_TR.h +++ b/tasmota/language/tr_TR.h @@ -1223,4 +1223,8 @@ #define D_AVG_RAD_DOSE "ortalama radyasyon dozu" #define D_UNIT_US_H "µSv/h" +// ixrv92_pipsolar.ino +#define D_SENSOR_PIPSOLAR_TX "Pipsolar TX" +#define D_SENSOR_PIPSOLAR_RX "Pipsolar RX" + #endif // _LANGUAGE_TR_TR_H_ diff --git a/tasmota/language/uk_UA.h b/tasmota/language/uk_UA.h index 42aa19f92..e5bcf184a 100644 --- a/tasmota/language/uk_UA.h +++ b/tasmota/language/uk_UA.h @@ -1223,4 +1223,8 @@ #define D_AVG_RAD_DOSE "середня доза радіації" #define D_UNIT_US_H "µSv/h" +// ixrv92_pipsolar.ino +#define D_SENSOR_PIPSOLAR_TX "Pipsolar TX" +#define D_SENSOR_PIPSOLAR_RX "Pipsolar RX" + #endif // _LANGUAGE_UK_UA_H_ diff --git a/tasmota/language/vi_VN.h b/tasmota/language/vi_VN.h index 175178106..ce42b275c 100644 --- a/tasmota/language/vi_VN.h +++ b/tasmota/language/vi_VN.h @@ -1223,4 +1223,8 @@ #define D_AVG_RAD_DOSE "liều bức xạ trung bình" #define D_UNIT_US_H "µSv/h" +// ixrv92_pipsolar.ino +#define D_SENSOR_PIPSOLAR_TX "Pipsolar TX" +#define D_SENSOR_PIPSOLAR_RX "Pipsolar RX" + #endif // _LANGUAGE_VI_VN_H_ diff --git a/tasmota/language/zh_CN.h b/tasmota/language/zh_CN.h index af408878a..54ebf9e0e 100644 --- a/tasmota/language/zh_CN.h +++ b/tasmota/language/zh_CN.h @@ -1223,4 +1223,8 @@ #define D_AVG_RAD_DOSE "平均辐射剂量" #define D_UNIT_US_H "µSv/h" +// ixrv92_pipsolar.ino +#define D_SENSOR_PIPSOLAR_TX "Pipsolar TX" +#define D_SENSOR_PIPSOLAR_RX "Pipsolar RX" + #endif // _LANGUAGE_ZH_CN_H_ diff --git a/tasmota/language/zh_TW.h b/tasmota/language/zh_TW.h index 46be35384..ee54c65a4 100644 --- a/tasmota/language/zh_TW.h +++ b/tasmota/language/zh_TW.h @@ -1223,4 +1223,8 @@ #define D_AVG_RAD_DOSE "平均輻射劑量" #define D_UNIT_US_H "µSv/h" +// ixrv92_pipsolar.ino +#define D_SENSOR_PIPSOLAR_TX "Pipsolar TX" +#define D_SENSOR_PIPSOLAR_RX "Pipsolar RX" + #endif // _LANGUAGE_ZH_TW_H_ diff --git a/tasmota/tasmota_xdrv_driver/xdrv_92_pipsolar.ino b/tasmota/tasmota_xdrv_driver/xdrv_92_pipsolar.ino new file mode 100644 index 000000000..747704f43 --- /dev/null +++ b/tasmota/tasmota_xdrv_driver/xdrv_92_pipsolar.ino @@ -0,0 +1,1050 @@ +/* + xdrv_92_pipsolar.ino - modbus bridge support for Tasmota + + Copyright (C) 2023 Peter Rustler + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#if defined(USE_PIPSOLAR) + +#define XDRV_92 92 + +#define PIPSOLAR_RECEIVEBUFFER_SIZE TM_SERIAL_BUFFER_SIZE * 2 // 128 +#define PIPSOLAR_SENDBUFFER_SIZE TM_SERIAL_BUFFER_SIZE // 64 + +#include +#include + +#include + +struct PipSolar +{ + TasmotaSerial *serial; + uint8_t *receiveBuffer; + uint8_t *sendBuffer; + + uint8_t receiveBufferPosition; + enum class CommandType + { + NONE, + QPIGS, // get analog values + //QPIRI, // Device Rating Information (settings) + QMOD, // get mode + QPIWS, // get status/error information + QT, // get inverter time + QET, // energy total + QEY, // energy year + QEM, // energy month + QED, // energy day + QEH, // energy hour + DAT, // set date time + } commandType; + + enum class CommandState { + None, + WaitForResponse, + GotResponse + } commandState; + + uint32_t secondIntervalCounter; + uint16_t commandTimeoutCounter; + bool pollValues; + + uint32_t successPoll; + uint32_t errorPoll; + uint32_t currentErrorPoll; + uint32_t maxCurrentErrorPoll; + + union Value + { + int intValue; + float floatValue; + bool boolValue; + char charValue; + }; + + struct ValueSetting + { + Value value; + enum class Type + { + unknownType, + intType, + intTypeCopy, + floatType, + floatTypeCopy, + boolType, + charType, + stringType, + } const type; + const uint8_t position; + const uint8_t count; + const char name[30]; + const char unit[4]; + const bool publish; + }; + struct QueryCommand { + PipSolar::CommandType commandType; + bool send; + char parameter[20]; + } queryCommand; +} PIPSOLAR; + +struct PIPSOLARPollingValue { + const uint32_t interval; + const PipSolar::CommandType command; + const uint16_t timeout; // *2ms + const uint16_t waitAfterResponse; // *2ms + bool poll; +}; +struct PIPSOLARPollingValue PIPSOLARpollingList[] = { + {3, PipSolar::CommandType::QPIWS, 600, 200, false}, + {3, PipSolar::CommandType::QPIGS, 600, 200, false}, + {3, PipSolar::CommandType::QMOD, 200, 200, false}, + {3, PipSolar::CommandType::QT, 200, 200, false}, + {3, PipSolar::CommandType::QET, 200, 200, false}, + {0, PipSolar::CommandType::QEY, 200, 200, false}, + {0, PipSolar::CommandType::QEM, 200, 200, false}, + {0, PipSolar::CommandType::QED, 200, 200, false}, + {0, PipSolar::CommandType::QEH, 200, 200, false}, + {0, PipSolar::CommandType::DAT, 200, 200, false}, +}; +constexpr uint8_t PIPSOLARpollingListCount = sizeof(PIPSOLARpollingList) / sizeof(PIPSOLARPollingValue); +uint8_t PIPSOLARpollingValuePosition = PIPSOLARpollingListCount; + +// (233.6 50.0 230.2 49.9 0299 0236 005 363 51.80 000 036 0039 00.1 000.0 00.00 00005 00010000 00 00 00000 010 +struct PipSolar::ValueSetting PIPSOLARqpigsValueSettings[] = { + {{}, PipSolar::ValueSetting::Type::floatType, 1, 5, "Grid_voltage", "V", false}, + {{}, PipSolar::ValueSetting::Type::floatType, 7, 4, "Grid_frequency", "Hz", false}, + {{}, PipSolar::ValueSetting::Type::floatType, 12, 5, "AC_output_voltage", "V", false}, + {{}, PipSolar::ValueSetting::Type::floatType, 18, 4, "AC_output_frequency", "Hz", false}, + {{}, PipSolar::ValueSetting::Type::intType, 23, 4, "AC_output_apparent_power", "W", false}, + {{}, PipSolar::ValueSetting::Type::intType, 28, 4, "AC_output_active_power", "W", true}, + {{}, PipSolar::ValueSetting::Type::intType, 33, 3, "Output_load_percent", "%", true}, + {{}, PipSolar::ValueSetting::Type::intType, 37, 3, "BUS_voltage", "V", false}, + {{}, PipSolar::ValueSetting::Type::floatType, 41, 5, "Battery_voltage", "V", true}, + {{}, PipSolar::ValueSetting::Type::intType, 47, 3, "Battery_charging_current", "A", false}, + {{}, PipSolar::ValueSetting::Type::intType, 51, 3, "Battery_capacity", "Ah", false}, + {{}, PipSolar::ValueSetting::Type::intType, 55, 4, "Heat_sink_temperature", "°C", true}, + {{}, PipSolar::ValueSetting::Type::floatType, 60, 4, "PV_Input_current", "A", false}, + {{}, PipSolar::ValueSetting::Type::floatType, 65, 5, "PV_Input_voltage", "V", false}, + {{}, PipSolar::ValueSetting::Type::floatType, 71, 5, "Battery_voltage_from_SCC", "V", false}, + {{}, PipSolar::ValueSetting::Type::intType, 77, 5, "Battery_discharge_current", "A", false}, + // deviceStatus 8x + {{}, PipSolar::ValueSetting::Type::stringType, 83, 8, "Device status", "", false}, + //{{}, PipSolar::ValueSetting::Type::boolType, 83, 1, "a7", "", false}, + //{{}, PipSolar::ValueSetting::Type::boolType, 84, 1, "a6", "", false}, + //{{}, PipSolar::ValueSetting::Type::boolType, 85, 1, "a5", "", false}, + //{{}, PipSolar::ValueSetting::Type::boolType, 86, 1, "a4", "", false}, + //{{}, PipSolar::ValueSetting::Type::boolType, 87, 1, "a3", "", false}, + //{{}, PipSolar::ValueSetting::Type::boolType, 88, 1, "a2", "", false}, + //{{}, PipSolar::ValueSetting::Type::boolType, 89, 1, "a1", "", false}, + //{{}, PipSolar::ValueSetting::Type::boolType, 90, 1, "a0", "", false}, + + {{}, PipSolar::ValueSetting::Type::intType, 92, 2, "Battery_voltage_offset_ffo", "V", false}, + {{}, PipSolar::ValueSetting::Type::intType, 95, 2, "EEPROM_version", "", false}, + {{}, PipSolar::ValueSetting::Type::intType, 98, 5, "PV_Charging_power", "W", true}, + // deviceStatus 3x + {{}, PipSolar::ValueSetting::Type::stringType, 100, 3, "Device status", "", false}, + //{{}, PipSolar::ValueSetting::Type::boolType, 100, 1, "Device_status[8]", ""}, + //{{}, PipSolar::ValueSetting::Type::boolType, 101, 1, "Device_status[9]", ""}, + //{{}, PipSolar::ValueSetting::Type::boolType, 102, 1, "Device_status[10]", ""}, +}; +constexpr uint8_t PIPSOLARqpigsValueSettingsCount = sizeof(PIPSOLARqpigsValueSettings) / sizeof(PipSolar::ValueSetting); + +// (B +struct PipSolar::ValueSetting PIPSOLARqmodValueSettings[] = { + {{}, PipSolar::ValueSetting::Type::charType, 1, 1, "Mode", "", true}, +}; +constexpr uint8_t PIPSOLARqmodValueSettingsCount = sizeof(PIPSOLARqmodValueSettings) / sizeof(PipSolar::ValueSetting); + +// (YYYYMMDDHHMMSS +struct PipSolar::ValueSetting PIPSOLARqtValueSettings[] = { + {{}, PipSolar::ValueSetting::Type::intTypeCopy, 1, 4, "Year", "", false}, + {{}, PipSolar::ValueSetting::Type::intTypeCopy, 5, 2, "Month", "", false}, + {{}, PipSolar::ValueSetting::Type::intTypeCopy, 7, 2, "Day", "", false}, + {{}, PipSolar::ValueSetting::Type::intTypeCopy, 9, 2, "Hour", "", false}, + {{}, PipSolar::ValueSetting::Type::intTypeCopy, 11, 2, "Minute", "", false}, + {{}, PipSolar::ValueSetting::Type::intTypeCopy, 13, 2, "Second", "", false}, +}; +constexpr uint8_t PIPSOLARqtValueSettingsCount = sizeof(PIPSOLARqtValueSettings) / sizeof(PipSolar::ValueSetting); + +struct PipSolar::ValueSetting PIPSOLARqexValueSettings[] = { + {{}, PipSolar::ValueSetting::Type::intType, 1, 8, "Total_Energy", "Wh", true}, +}; +constexpr uint8_t PIPSOLARqexValueSettingsCount = sizeof(PIPSOLARqexValueSettings) / sizeof(PipSolar::ValueSetting); + +/********************************************************************************************/ +bool PIPSOLARSendCommand(PipSolar::CommandType cmd, const char *parameter = nullptr, bool checksum = false); + +bool SetPipSolarSerialBegin(void) { + return PIPSOLAR.serial->begin(Settings->sbaudrate * 300, ConvertSerialConfig(Settings->sserial_config)); // Reinitialize serial port with new baud rate +} + +bool checkNumbersFormat(const char *buffer, int count) +{ + if (count == 0) + return false; + for (int i = 0; i < count; ++i) + if (buffer[i] > '9' || buffer[i] < '0') + return false; + return true; +} + +void CmndPipSolarNoParameter(PipSolar::CommandType commandType) { + if (PIPSOLAR.commandType != PipSolar::CommandType::NONE + && PIPSOLAR.queryCommand.commandType != PipSolar::CommandType::NONE) { + ResponseCmndChar_P(PSTR("Error\", \"Error\": \"Busy")); + } else if (PIPSOLAR.commandType == PipSolar::CommandType::NONE) { + ResponseCmndChar_P(PSTR("Send")); + PIPSOLAR.queryCommand.send = true; + PIPSOLAR.queryCommand.commandType = commandType; + PIPSOLARSendCommand(commandType); + } else { + PIPSOLAR.queryCommand.commandType = commandType; + PIPSOLAR.queryCommand.send = false; + ResponseCmndChar_P(PSTR("Queue")); + } +} +void CmndPipSolarParameter(PipSolar::CommandType commandType) { + if (PIPSOLAR.commandType != PipSolar::CommandType::NONE + && PIPSOLAR.queryCommand.commandType != PipSolar::CommandType::NONE) { + ResponseCmndChar_P(PSTR("Error\", \"Error\": \"Busy")); + } else if (PIPSOLAR.commandType == PipSolar::CommandType::NONE) { + PIPSOLAR.queryCommand.send = true; + strncpy(PIPSOLAR.queryCommand.parameter, XdrvMailbox.data, sizeof(PIPSOLAR.queryCommand.parameter)); + PIPSOLAR.queryCommand.commandType = commandType; + PIPSOLARSendCommand(commandType, PIPSOLAR.queryCommand.parameter); + ResponseCmndChar_P(PSTR("Send")); + } else { + PIPSOLAR.queryCommand.send = false; + strncpy(PIPSOLAR.queryCommand.parameter, XdrvMailbox.data, sizeof(PIPSOLAR.queryCommand.parameter)); + PIPSOLAR.queryCommand.commandType = commandType; + ResponseCmndChar_P(PSTR("Queue")); + } +} + +void CmndPipSolarQT(void) { + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: command QT")); + CmndPipSolarNoParameter(PipSolar::CommandType::QT); +} + +void CmndPipSolarQET(void) { + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: command QET")); + CmndPipSolarNoParameter(PipSolar::CommandType::QET); +} + +void CmndPipSolarQEY(void) { + if (!checkNumbersFormat(XdrvMailbox.data, XdrvMailbox.data_len)) { + ResponseCmndChar_P(PSTR("Error\", \"Error\": \"No numeric parameter")); + return; + } + if (XdrvMailbox.data_len != 4) { + ResponseCmndChar_P(PSTR("Error\", \"Error\": \"Parameter count")); + return; + } + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: command QEY")); + CmndPipSolarParameter(PipSolar::CommandType::QEY); +} + +void CmndPipSolarQEM(void) { + if (!checkNumbersFormat(XdrvMailbox.data, XdrvMailbox.data_len)) { + ResponseCmndChar_P(PSTR("Error\", \"Error\": \"No numeric parameter")); + return; + } + if (XdrvMailbox.data_len != 6) { + ResponseCmndChar_P(PSTR("Error\", \"Error\": \"Parameter count")); + return; + } + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: command QEM")); + CmndPipSolarParameter(PipSolar::CommandType::QEM); +} + +void CmndPipSolarQED(void) { + if (!checkNumbersFormat(XdrvMailbox.data, XdrvMailbox.data_len)) { + ResponseCmndChar_P(PSTR("Error\", \"Error\": \"No numeric parameter")); + return; + } + if (XdrvMailbox.data_len != 8) { + ResponseCmndChar_P(PSTR("Error\", \"Error\": \"Parameter count")); + return; + } + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: command QED")); + CmndPipSolarParameter(PipSolar::CommandType::QED); +} + +void CmndPipSolarQEH(void) { + if (!checkNumbersFormat(XdrvMailbox.data, XdrvMailbox.data_len)) { + ResponseCmndChar_P(PSTR("Error\", \"Error\": \"No numeric parameter")); + return; + } + if (XdrvMailbox.data_len != 10) { + ResponseCmndChar_P(PSTR("Error\", \"Error\": \"Parameter count")); + return; + } + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: command QEH")); + CmndPipSolarParameter(PipSolar::CommandType::QEH); +} + +void CmndPipSolarDAT(void) { + if (!checkNumbersFormat(XdrvMailbox.data, XdrvMailbox.data_len)) { + ResponseCmndChar_P(PSTR("Error\", \"Error\": \"No numeric parameter")); + return; + } + if (XdrvMailbox.data_len != 12) { + ResponseCmndChar_P(PSTR("Error\", \"Error\": \"Parameter count")); + return; + } + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: command DAT")); + CmndPipSolarParameter(PipSolar::CommandType::DAT); +} + +void CmndPipSolarPollValues(void) { + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: command PollValues")); + char argument[XdrvMailbox.data_len]; + + if (ArgC()==1) { + PIPSOLAR.pollValues = atoi(ArgV(argument, 1)) == 0 ? false : true; + } + Response_P(PSTR("{\"" D_CMND_PIP_PREFIX D_CMND_PIP_POLLVALUES "\": %d}"), PIPSOLAR.pollValues); +} + +void CmndPipSolarBaudRate(void) { + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: command BaudRate")); + if (XdrvMailbox.payload >= 300) { + XdrvMailbox.payload /= 300; // Make it a valid baudrate + Settings->sbaudrate = XdrvMailbox.payload; + if (SetPipSolarSerialBegin()) + { + if (PIPSOLAR.serial->hardwareSerial()) + { + ClaimSerial(); + } + } + } + ResponseCmndNumber(Settings->sbaudrate * 300); +} + +void SetPipSolarSerialConfig(uint32_t serial_config) { + if (serial_config > TS_SERIAL_8O2) { + serial_config = TS_SERIAL_8N1; + } + if (serial_config != Settings->sserial_config) { + Settings->sserial_config = serial_config; + SetPipSolarSerialBegin(); + } +} + +void CmndPipSolarSerialConfig(void) { + + if (XdrvMailbox.data_len > 0) { + if (XdrvMailbox.data_len < 3) { // Use 0..23 as serial config option + if ((XdrvMailbox.payload >= TS_SERIAL_5N1) && (XdrvMailbox.payload <= TS_SERIAL_8O2)) { + SetPipSolarSerialConfig(XdrvMailbox.payload); + } + } + else if ((XdrvMailbox.payload >= 5) && (XdrvMailbox.payload <= 8)) { + int8_t serial_config = ParseSerialConfig(XdrvMailbox.data); + if (serial_config >= 0) { + SetPipSolarSerialConfig(serial_config); + } + } + } + ResponseCmndChar(GetSerialConfig(Settings->sserial_config).c_str()); +} + +const char kPipSolarCommands[] PROGMEM = D_CMND_PIP_PREFIX "|" + D_CMND_PIP_QT "|" + D_CMND_PIP_QET "|" + D_CMND_PIP_QEY "|" + D_CMND_PIP_QEM "|" + D_CMND_PIP_QED "|" + D_CMND_PIP_QEH "|" + D_CMND_PIP_DAT "|" + D_CMND_PIP_POLLVALUES "|" + D_CMND_PIP_BAUDRATE "|" + D_CMND_PIP_SERIALCONFIG; + +void (* const PipSolarCommand[])(void) PROGMEM = { + &CmndPipSolarQT, + &CmndPipSolarQET, + &CmndPipSolarQEY, + &CmndPipSolarQEM, + &CmndPipSolarQED, + &CmndPipSolarQEH, + &CmndPipSolarDAT, + &CmndPipSolarPollValues, + &CmndPipSolarBaudRate, + &CmndPipSolarSerialConfig + }; + + +void PIPSOLARPublishResult(const char *subtopic, const char *payload, const char *parameter) +{ + char buffer[150]; + snprintf_P(buffer, sizeof(buffer), PSTR("{\"%s\": {\"value\": \"%s\", \"parameter\": \"%s\"}}"), subtopic, payload, parameter); + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: RESULT %s"), (const char*)buffer); + MqttPublishPayloadPrefixTopic_P(RESULT_OR_STAT, subtopic, (const char*)buffer); + XdrvRulesProcess(0, buffer); +} + +void PIPSOLARPublishResult(const char *subtopic, int payload, const char *parameter) +{ + char buffer[150]; + snprintf_P(buffer, sizeof(buffer), PSTR("{\"%s\": {\"value\": %d, \"parameter\": \"%s\"}}"), subtopic, payload, parameter); + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: RESULT %s"), (const char*)buffer); + MqttPublishPayloadPrefixTopic_P(RESULT_OR_STAT, subtopic, (const char*)buffer); + XdrvRulesProcess(0, buffer); +} + +void PIPSOLARPublishResult(const char *subtopic, const char* payload) +{ + char buffer[150]; + snprintf_P(buffer, sizeof(buffer), PSTR("{\"%s\": {\"value\": \"%s\"}}"), subtopic, payload); + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: RESULT %s"), (const char*)buffer); + MqttPublishPayloadPrefixTopic_P(RESULT_OR_STAT, subtopic, (const char*)buffer); + XdrvRulesProcess(0, buffer); +} + +void PIPSOLARPublishResult(const char *subtopic, int payload) +{ + char buffer[150]; + snprintf_P(buffer, sizeof(buffer), PSTR("{\"%s\": {\"value\": %d}}"), subtopic, payload); + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: RESULT %s"), (const char*)buffer); + MqttPublishPayloadPrefixTopic_P(RESULT_OR_STAT, subtopic, (const char*)buffer); + XdrvRulesProcess(0, buffer); +} + +void PIPSOLARPublish(const char *subtopic, const char *payload) +{ + MqttPublishPayloadPrefixTopic_P(STAT, subtopic, payload); + char buffer[150]; + snprintf_P(buffer, sizeof(buffer), PSTR("{\"%s\": \"%s\"}"), subtopic, payload); + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: PUBLISH %s"), (const char*)buffer); + XdrvRulesProcess(0, buffer); +} + +void PIPSOLARPublishRaw(const char *subtopic, const char *payload) +{ + MqttPublishPayloadPrefixTopic_P(STAT, subtopic, payload); + char buffer[150]; + snprintf_P(buffer, sizeof(buffer), PSTR("{\"%s\": %s}"), subtopic, payload); + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: PUBLISH %s"), (const char*)buffer); + XdrvRulesProcess(0, buffer); +} + +void PIPSOLARPublish(const char *subtopic, int value) +{ + char buffer[15]; + snprintf(buffer, sizeof(buffer), "%d", value); + PIPSOLARPublishRaw(subtopic, buffer); +} + +void PIPSOLARPublish(const char *subtopic, float value) +{ + char buffer[15]; + snprintf(buffer, sizeof(buffer), "%f", value); + PIPSOLARPublishRaw(subtopic, buffer); +} + +void PIPSOLARPublish(const char *subtopic, char value) +{ + char buffer[2]; + snprintf(buffer, sizeof(buffer), "%c", value); + PIPSOLARPublish(subtopic, buffer); +} + +void PIPSOLARPublish(const char *subtopic, bool value) +{ + char buffer[2]; + snprintf(buffer, sizeof(buffer), "%d", value); + PIPSOLARPublishRaw(subtopic, buffer); +} + +// crc function is from here: https://forum.arduino.cc/t/rs232-read-data-from-mpp-solar-inverter/600960/6 +uint16_t PIPSOLARCalcCrc(const uint8_t *pin, uint8_t len) +{ + uint16_t crc; + uint8_t da; + const uint8_t *ptr; + uint8_t bCRCHign; + uint8_t bCRCLow; + + const unsigned short crc_ta[16] = + { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef}; + + ptr = pin; + crc = 0; + while (len-- != 0) + { + da = ((uint8_t)(crc >> 8)) >> 4; + crc <<= 4; + crc ^= crc_ta[da ^ (*ptr >> 4)]; + da = ((uint8_t)(crc >> 8)) >> 4; + crc <<= 4; + crc ^= crc_ta[da ^ (*ptr & 0x0f)]; + ptr++; + } + bCRCLow = crc; + bCRCHign = (uint8_t)(crc >> 8); + if (bCRCLow == 0x28 || bCRCLow == 0x0d || bCRCLow == 0x0a) + { + bCRCLow++; + } + if (bCRCHign == 0x28 || bCRCHign == 0x0d || bCRCHign == 0x0a) + { + bCRCHign++; + } + crc = ((unsigned short)bCRCHign) << 8; + crc += bCRCLow; + return (crc); +} + +bool PIPSOLARSendCommand(PipSolar::CommandType cmd, const char *parameter, bool checksum) +{ + const char *command; + switch (cmd) + { + case PipSolar::CommandType::NONE: + return false; + case PipSolar::CommandType::QPIGS: + command = PSTR("QPIGS"); + break; + case PipSolar::CommandType::QMOD: + command = PSTR("QMOD"); + break; + case PipSolar::CommandType::QPIWS: + command = PSTR("QPIWS"); + break; + case PipSolar::CommandType::QT: + command = PSTR("QT"); + break; + case PipSolar::CommandType::QET: + command = PSTR("QET"); + break; + case PipSolar::CommandType::QEY: + command = PSTR("QEY"); + break; + case PipSolar::CommandType::QEM: + command = PSTR("QEM"); + break; + case PipSolar::CommandType::QED: + command = PSTR("QED"); + break; + case PipSolar::CommandType::QEH: + command = PSTR("QEH"); + break; + case PipSolar::CommandType::DAT: + command = PSTR("DAT"); + break; + } + PIPSOLAR.commandType = cmd; + PIPSOLAR.commandState = PipSolar::CommandState::WaitForResponse; + PIPSOLAR.commandTimeoutCounter = 0; + + uint8_t len = strlen(command); + memcpy(PIPSOLAR.sendBuffer, command, len); + if (parameter) + { + uint8_t parameterLen = strlen(parameter); + memcpy(PIPSOLAR.sendBuffer + len, parameter, parameterLen); + len += parameterLen; + if (checksum) { + uint8_t checksumValue = 0; + for (uint8_t i = 0; i < len; ++i) + checksumValue += PIPSOLAR.sendBuffer[i]; + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: Checksum %d len %d"), checksumValue, len+3); + sprintf(reinterpret_cast(PIPSOLAR.sendBuffer + len), "%03u", checksumValue); + len += 3; + } + } + + PIPSOLAR.sendBuffer[len] = 0; + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: Send %s"), PIPSOLAR.sendBuffer); + + uint16_t crc16 = PIPSOLARCalcCrc(PIPSOLAR.sendBuffer, len); + PIPSOLAR.sendBuffer[len++] = ((uint8_t)((crc16) >> 8)); + PIPSOLAR.sendBuffer[len++] = ((uint8_t)((crc16)&0xff)); + PIPSOLAR.sendBuffer[len++] = '\r'; + + + // PIPSOLAR.serial->setReadChunkMode(1); // Enable chunk mode introducing possible Hardware Watchdogs + PIPSOLAR.serial->flush(); + PIPSOLAR.serial->write(PIPSOLAR.sendBuffer, len); + PIPSOLAR.commandTimeoutCounter = 0; + return false; +} + +const char* PIPSOLARGetValueString(char *data, int8_t position, uint8_t count) +{ + data[position+count] = 0; + return &data[position]; +} + +static char PIPSOLARGetValueStringCopyString[10]; +const char* PIPSOLARGetValueStringCopy(char *data, int8_t position, uint8_t count) +{ + PIPSOLARGetValueStringCopyString[count] = 0; + memcpy((void*)PIPSOLARGetValueStringCopyString, &data[position], count); + + return PIPSOLARGetValueStringCopyString; +} + +void PIPSOLARInterpret(struct PipSolar::ValueSetting *settings, uint8_t count) +{ + //(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: line %d"), __LINE__); + for (uint8_t i = 0; i < count; ++i) + { + auto & setting = settings[i]; + + switch (setting.type) + { + case PipSolar::ValueSetting::Type::intType: { + const char* temp = PIPSOLARGetValueString((char*)PIPSOLAR.receiveBuffer, setting.position, setting.count); + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: line %d %s %s"), __LINE__, setting.name, temp); + setting.value.intValue = atoi(temp); + if (setting.publish) + PIPSOLARPublish(setting.name, setting.value.intValue); + break; + } + case PipSolar::ValueSetting::Type::intTypeCopy: { + const char* temp = PIPSOLARGetValueStringCopy((char*)PIPSOLAR.receiveBuffer, setting.position, setting.count); + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: line %d %s %s"), __LINE__, setting.name, temp); + setting.value.intValue = atoi(temp); + if (setting.publish) + PIPSOLARPublish(setting.name, setting.value.intValue); + break; + } + case PipSolar::ValueSetting::Type::floatType: { + const char* temp = PIPSOLARGetValueString((char*)PIPSOLAR.receiveBuffer, setting.position, setting.count); + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: line %d %s %s"), __LINE__, setting.name, temp); + setting.value.floatValue = atof(temp); + if (setting.publish) + PIPSOLARPublish(setting.name, setting.value.floatValue); + break; + } + case PipSolar::ValueSetting::Type::floatTypeCopy: { + const char* temp = PIPSOLARGetValueString((char*)PIPSOLAR.receiveBuffer, setting.position, setting.count); + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: line %d %s %s"), __LINE__, setting.name, temp); + setting.value.floatValue = atof(temp); + if (setting.publish) + PIPSOLARPublish(setting.name, setting.value.floatValue); + break; + } + case PipSolar::ValueSetting::Type::boolType: + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: line %d"), __LINE__); + setting.value.boolValue = PIPSOLAR.receiveBuffer[setting.position] == '1'; + if (setting.publish) + PIPSOLARPublish(setting.name, setting.value.boolValue); + break; + case PipSolar::ValueSetting::Type::charType: + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: line %d"), __LINE__); + setting.value.charValue = PIPSOLAR.receiveBuffer[setting.position]; + if (setting.publish) + PIPSOLARPublish(setting.name, setting.value.charValue); + break; + case PipSolar::ValueSetting::Type::stringType: + const char* temp = PIPSOLARGetValueString((char*)PIPSOLAR.receiveBuffer, setting.position, setting.count); + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: line %d"), __LINE__); + if (setting.publish) + PIPSOLARPublish(setting.name, temp); + break; + } + } +} + +void PIPSOLARInterpret(void) +{ + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: line %d %d"), __LINE__, PIPSOLAR.commandType); + if (PIPSOLAR.receiveBuffer[0] != '(') + return; + bool wasQuery = (PIPSOLAR.commandType == PIPSOLAR.queryCommand.commandType && PIPSOLAR.queryCommand.send); + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: line %d"), __LINE__); + switch (PIPSOLAR.commandType) + { + case PipSolar::CommandType::NONE: + break; + case PipSolar::CommandType::QPIGS: { + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: %s %d %d"), __FUNCTION__, __LINE__, PIPSOLAR.receiveBufferPosition); + if (PIPSOLAR.receiveBufferPosition != 107) + return; + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: %s %d"), __FUNCTION__, __LINE__); + PIPSOLARInterpret(PIPSOLARqpigsValueSettings, PIPSOLARqpigsValueSettingsCount); + int batteryCurrent = PIPSOLARqpigsValueSettings[9].value.intValue - PIPSOLARqpigsValueSettings[15].value.intValue; + PIPSOLARPublish(PSTR("Battery_current"), batteryCurrent); + PIPSOLARPublish(PSTR("Battery_power"), PIPSOLARqpigsValueSettings[8].value.floatValue * batteryCurrent); + char buffer[8+3+1]; + auto &deviceState1 = PIPSOLARqpigsValueSettings[16]; + auto &deviceState2 = PIPSOLARqpigsValueSettings[20]; + snprintf(buffer, sizeof(buffer), "%3s%8s" + , PIPSOLARGetValueString((char*)PIPSOLAR.receiveBuffer, deviceState2.position, deviceState2.count) + , PIPSOLARGetValueString((char*)PIPSOLAR.receiveBuffer, deviceState1.position, deviceState1.count)); + PIPSOLARPublish(PSTR("Device_state"), buffer); + } + break; + case PipSolar::CommandType::QMOD: + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: line %d"), __LINE__); + if (PIPSOLAR.receiveBufferPosition != 2) + return; + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: line %d"), __LINE__); + PIPSOLARInterpret(PIPSOLARqmodValueSettings, PIPSOLARqmodValueSettingsCount); + break; + case PipSolar::CommandType::QPIWS: + PIPSOLARPublish(PSTR("Device_warning_state"), PIPSOLARGetValueString((char*)PIPSOLAR.receiveBuffer, 1, 35)); + break; + case PipSolar::CommandType::QT: + if (wasQuery) + PIPSOLARPublishResult(PSTR("QT"), PIPSOLARGetValueString((char*)PIPSOLAR.receiveBuffer, 1, 14)); + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: line %d"), __LINE__); + PIPSOLARInterpret(PIPSOLARqtValueSettings, PIPSOLARqtValueSettingsCount); + PIPSOLARPublish(PSTR("Inverter_date_time"), PIPSOLARGetValueString((char*)PIPSOLAR.receiveBuffer, 1, 14)); + break; + case PipSolar::CommandType::QET: + if (wasQuery) + PIPSOLARPublishResult(PSTR("QET"), atoi(PIPSOLARGetValueString((char*)PIPSOLAR.receiveBuffer, 1, 8))); + PIPSOLARInterpret(PIPSOLARqexValueSettings, PIPSOLARqexValueSettingsCount); + break; + case PipSolar::CommandType::QEY: + if (wasQuery) + PIPSOLARPublishResult(PSTR("QEY"), atoi(PIPSOLARGetValueString((char*)PIPSOLAR.receiveBuffer, 1, 8)), PIPSOLAR.queryCommand.parameter); + else + PIPSOLARInterpret(PIPSOLARqexValueSettings, PIPSOLARqexValueSettingsCount); + break; + case PipSolar::CommandType::QEM: + if (wasQuery) + PIPSOLARPublishResult(PSTR("QEM"), atoi(PIPSOLARGetValueString((char*)PIPSOLAR.receiveBuffer, 1, 8)), PIPSOLAR.queryCommand.parameter); + else + PIPSOLARInterpret(PIPSOLARqexValueSettings, PIPSOLARqexValueSettingsCount); + break; + case PipSolar::CommandType::QED: + if (wasQuery) + PIPSOLARPublishResult(PSTR("QED"), atoi(PIPSOLARGetValueString((char*)PIPSOLAR.receiveBuffer, 1, 8)), PIPSOLAR.queryCommand.parameter); + else + PIPSOLARInterpret(PIPSOLARqexValueSettings, PIPSOLARqexValueSettingsCount); + break; + case PipSolar::CommandType::QEH: + if (wasQuery) + PIPSOLARPublishResult(PSTR("QEH"), atoi(PIPSOLARGetValueString((char*)PIPSOLAR.receiveBuffer, 1, 8)), PIPSOLAR.queryCommand.parameter); + else + PIPSOLARInterpret(PIPSOLARqexValueSettings, PIPSOLARqexValueSettingsCount); + break; + case PipSolar::CommandType::DAT: + if (wasQuery) + PIPSOLARPublishResult(PSTR("DAT"), PIPSOLARGetValueString((char*)PIPSOLAR.receiveBuffer, 1, 3), PIPSOLAR.queryCommand.parameter); + break; + } + + if (wasQuery) { + PIPSOLAR.queryCommand.commandType = PipSolar::CommandType::NONE; + PIPSOLAR.queryCommand.parameter[0] = 0; + PIPSOLAR.queryCommand.send = false; + } + + PIPSOLAR.receiveBufferPosition = 0; + if (!PIPSOLAR.pollValues) { + PIPSOLAR.commandState = PipSolar::CommandState::None; + PIPSOLAR.commandType = PipSolar::CommandType::NONE; + } +} + +void PIPSOLARInput(void) +{ + while (PIPSOLAR.serial->available()) + { + uint8_t bytesAvailable = PIPSOLAR.serial->read(PIPSOLAR.receiveBuffer + PIPSOLAR.receiveBufferPosition, PIPSOLAR_RECEIVEBUFFER_SIZE - PIPSOLAR.receiveBufferPosition); + PIPSOLAR.receiveBufferPosition += bytesAvailable; + if (PIPSOLAR.receiveBufferPosition == PIPSOLAR_RECEIVEBUFFER_SIZE) { // buffer overflow + ++PIPSOLAR.currentErrorPoll; + PIPSOLAR.maxCurrentErrorPoll = std::max(PIPSOLAR.maxCurrentErrorPoll, PIPSOLAR.currentErrorPoll); + ++PIPSOLAR.errorPoll; + PIPSOLAR.receiveBufferPosition = 0; // we are in trouble, but keep it going + } + } + if (PIPSOLAR.receiveBufferPosition > 2 && PIPSOLAR.receiveBuffer[PIPSOLAR.receiveBufferPosition - 1] == '\r') + { // we hope that we always have a complete reply + bool valid = true; + uint16_t crc16 = PIPSOLARCalcCrc(PIPSOLAR.receiveBuffer, PIPSOLAR.receiveBufferPosition - 3); + uint8_t crcLow = PIPSOLAR.receiveBuffer[PIPSOLAR.receiveBufferPosition - 2]; + uint8_t crcHigh = PIPSOLAR.receiveBuffer[PIPSOLAR.receiveBufferPosition - 3]; + if (((uint8_t)((crc16)&0xff)) != crcLow || ((uint8_t)((crc16) >> 8)) != crcHigh) + valid = false; + + PIPSOLAR.receiveBufferPosition -= 3; // remove crc and \r + PIPSOLAR.receiveBuffer[PIPSOLAR.receiveBufferPosition] = 0; // terminate string + if (valid) + { + PIPSOLAR.currentErrorPoll = 0; + ++PIPSOLAR.successPoll; + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: Received valid data \"%s\" timeout %d"), PIPSOLAR.receiveBuffer, PIPSOLAR.commandTimeoutCounter); + PIPSOLARInterpret(); + PIPSOLAR.commandState = PipSolar::CommandState::GotResponse; + } else { + ++PIPSOLAR.currentErrorPoll; + PIPSOLAR.maxCurrentErrorPoll = std::max(PIPSOLAR.maxCurrentErrorPoll, PIPSOLAR.currentErrorPoll); + ++PIPSOLAR.errorPoll; + AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: Received invalid data \"%s\" crc %02x %02x expected %2x %2x timeout %d"), PIPSOLAR.receiveBuffer, crcLow, crcHigh, ((uint8_t)((crc16)&0xff)), ((uint8_t)((crc16) >> 8)), PIPSOLAR.commandTimeoutCounter); + PIPSOLAR.receiveBufferPosition = 0; + PIPSOLAR.commandState = PipSolar::CommandState::None; + PIPSOLAR.commandType = PipSolar::CommandType::NONE; + } + PIPSOLAR.commandTimeoutCounter = -1; + } + if (PIPSOLAR.pollValues && PIPSOLAR.commandType != PipSolar::CommandType::NONE) { + + switch (PIPSOLAR.commandState) { + case PipSolar::CommandState::None: + break; + case PipSolar::CommandState::WaitForResponse: + if (PIPSOLAR.commandTimeoutCounter > PIPSOLARpollingList[PIPSOLARpollingValuePosition].timeout) { + AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: timeout WaitForResponse %d"), PIPSOLARpollingValuePosition, PIPSOLAR.commandTimeoutCounter, PIPSOLARpollingList[PIPSOLARpollingValuePosition].timeout); + PIPSOLAR.commandTimeoutCounter = 0; + PIPSOLAR.commandState = PipSolar::CommandState::None; + PIPSOLAR.commandType = PipSolar::CommandType::NONE; + PIPSOLAR.receiveBufferPosition = 0; + ++PIPSOLAR.currentErrorPoll; + PIPSOLAR.maxCurrentErrorPoll = std::max(PIPSOLAR.maxCurrentErrorPoll, PIPSOLAR.currentErrorPoll); + ++PIPSOLAR.errorPoll; + PIPSOLARNextPolling(); + } + break; + case PipSolar::CommandState::GotResponse: + if (PIPSOLAR.commandTimeoutCounter > PIPSOLARpollingList[PIPSOLARpollingValuePosition].waitAfterResponse) { + PIPSOLAR.commandTimeoutCounter = 0; + PIPSOLAR.commandState = PipSolar::CommandState::None; + PIPSOLAR.commandType = PipSolar::CommandType::NONE; + PIPSOLARNextPolling(); + } + break; + } + ++PIPSOLAR.commandTimeoutCounter; + } +} + +/********************************************************************************************/ + +void PIPSOLARNextPolling() { + if (PIPSOLAR.queryCommand.commandType != PipSolar::CommandType::NONE + && !PIPSOLAR.queryCommand.send) { + switch(PIPSOLAR.queryCommand.commandType) { + case PipSolar::CommandType::QT: + case PipSolar::CommandType::QET: + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: query without parameter")); + PIPSOLARSendCommand(PIPSOLAR.queryCommand.commandType); + PIPSOLAR.queryCommand.send = true; + return; + case PipSolar::CommandType::QEY: + case PipSolar::CommandType::QEM: + case PipSolar::CommandType::QED: + case PipSolar::CommandType::QEH: + case PipSolar::CommandType::DAT: + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: query with parameter")); + PIPSOLARSendCommand(PIPSOLAR.queryCommand.commandType, PIPSOLAR.queryCommand.parameter); + PIPSOLAR.queryCommand.send = true; + return; + default: // unsupported query + PIPSOLAR.queryCommand.send = false; + PIPSOLAR.queryCommand.commandType = PipSolar::CommandType::NONE; + PIPSOLAR.queryCommand.parameter[0] = 0; + break; + } + } + while(PIPSOLARpollingValuePosition < PIPSOLARpollingListCount) { + if (!PIPSOLARpollingList[PIPSOLARpollingValuePosition].poll) { + ++PIPSOLARpollingValuePosition; + continue; + } + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: %d"), __LINE__); + auto &command = PIPSOLARpollingList[PIPSOLARpollingValuePosition].command; + switch(command) { // TODO add the others with parameter + case PipSolar::CommandType::QPIWS: + case PipSolar::CommandType::QPIGS: + case PipSolar::CommandType::QMOD: + case PipSolar::CommandType::QT: + case PipSolar::CommandType::QET: + PIPSOLARSendCommand(command); + PIPSOLARpollingList[PIPSOLARpollingValuePosition].poll = false; + break; + case PipSolar::CommandType::NONE: + break; + } + return; + } + PIPSOLARpollingValuePosition = 0; +} + +void PIPSOLAREverySecond(void) +{ + if (PIPSOLAR.pollValues) { + for(int i = 0; i < PIPSOLARpollingListCount; ++i) { + if (PIPSOLARpollingList[i].interval != 0 && (PIPSOLAR.secondIntervalCounter % PIPSOLARpollingList[i].interval) == 0) { + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: schedule %d"), i); + PIPSOLARpollingList[i].poll = true; + } + } + if (PIPSOLAR.commandType == PipSolar::CommandType::NONE) { + PIPSOLARpollingValuePosition = 0; + PIPSOLARNextPolling(); + } + ++PIPSOLAR.secondIntervalCounter; + } +} + +void PIPSOLARInit(void) +{ + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: %s %d"), __FUNCTION__, __LINE__); + if (PinUsed(GPIO_PIPSOLAR_RX) && PinUsed(GPIO_PIPSOLAR_TX)) + { + PIPSOLAR.pollValues = true; + PIPSOLAR.receiveBuffer = (uint8_t *)malloc(PIPSOLAR_RECEIVEBUFFER_SIZE); + PIPSOLAR.sendBuffer = (uint8_t *)malloc(PIPSOLAR_SENDBUFFER_SIZE); + PIPSOLAR.receiveBufferPosition = 0; + PIPSOLAR.secondIntervalCounter = 0; + PIPSOLAR.commandType = PipSolar::CommandType::NONE; + PIPSOLAR.errorPoll = 0; + PIPSOLAR.successPoll = 0; + PIPSOLAR.currentErrorPoll = 0; + PIPSOLAR.queryCommand.commandType = PipSolar::CommandType::NONE; + PIPSOLAR.queryCommand.send = false; + + if (!PIPSOLAR.receiveBuffer || !PIPSOLAR.sendBuffer) + { + return; + } + + PIPSOLAR.receiveBuffer[0] = 0u; + PIPSOLAR.sendBuffer[0] = 0u; + + PIPSOLAR.serial = new TasmotaSerial(Pin(GPIO_PIPSOLAR_RX), Pin(GPIO_PIPSOLAR_TX), 2); + if (SetPipSolarSerialBegin()) + { + if (PIPSOLAR.serial->hardwareSerial()) + { + ClaimSerial(); + } + } + } +} + +/*********************************************************************************************\ + * Presentation +\*********************************************************************************************/ + +void PIPSOLARShowWeb(const PipSolar::ValueSetting &setting) +{ + switch (setting.type) + { + case PipSolar::ValueSetting::Type::intTypeCopy: + case PipSolar::ValueSetting::Type::intType: + WSContentSend_PD(PSTR("{s}%s{m}%d %s{e}"), setting.name, setting.value.intValue, setting.unit); + break; + case PipSolar::ValueSetting::Type::floatTypeCopy: + case PipSolar::ValueSetting::Type::floatType: + WSContentSend_PD(PSTR("{s}%s{m}%f %s{e}"), setting.name, setting.value.floatValue, setting.unit); + break; + case PipSolar::ValueSetting::Type::boolType: + WSContentSend_PD(PSTR("{s}%s{m}%s %s{e}"), setting.name, (setting.value.boolValue ? "true" : "false"), setting.unit); + break; + case PipSolar::ValueSetting::Type::charType: + WSContentSend_PD(PSTR("{s}%s{m}%c %s{e}"), setting.name, setting.value.charValue, setting.unit); + break; + } +} +void PIPSOLARShow(bool json) +{ + //AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("PIPSOLAR: %s %d"), __FUNCTION__, __LINE__); + if (json) + { // // cm cm cm % % + ResponseAppend_P(PSTR(",\"PIPSOLAR\":1")); +#ifdef USE_WEBSERVER + } + else + { + WSContentSend_PD(PSTR("{s}

QT

{e}")); + WSContentSend_PD(PSTR("{s}InverterTime{m}%02d:%02d:%02d %02d.%02d.%04d{e}"), + PIPSOLARqtValueSettings[3].value.intValue, + PIPSOLARqtValueSettings[4].value.intValue, + PIPSOLARqtValueSettings[5].value.intValue, + PIPSOLARqtValueSettings[2].value.intValue, + PIPSOLARqtValueSettings[1].value.intValue, + PIPSOLARqtValueSettings[0].value.intValue + ); + WSContentSend_PD(PSTR("{s}

QEx

{e}")); + for (int i = 0; i < PIPSOLARqexValueSettingsCount; ++i) + { + auto &setting = PIPSOLARqexValueSettings[i]; + PIPSOLARShowWeb(setting); + } + WSContentSend_PD(PSTR("{s}

QMOD

{e}")); + for (int i = 0; i < PIPSOLARqmodValueSettingsCount; ++i) + { + auto &setting = PIPSOLARqmodValueSettings[i]; + PIPSOLARShowWeb(setting); + } + WSContentSend_PD(PSTR("{s}

QPIGS

{e}")); + for (int i = 0; i < PIPSOLARqpigsValueSettingsCount; ++i) + { + auto &setting = PIPSOLARqpigsValueSettings[i]; + PIPSOLARShowWeb(setting); + } + float percentError = 100.0 * PIPSOLAR.errorPoll / (PIPSOLAR.errorPoll + PIPSOLAR.successPoll); + WSContentSend_PD(PSTR("{s}

ERROR

{m}Success: %u
Error: %u
CurrentError: %u
Max CurrentError: %u
ErrorRate %.2f %%{e}"), PIPSOLAR.successPoll, PIPSOLAR.errorPoll, PIPSOLAR.currentErrorPoll, PIPSOLAR.maxCurrentErrorPoll, percentError); +#endif // USE_WEBSERVER + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xdrv92(uint32_t function) +{ + bool result = true; + + if (PIPSOLAR.serial || function == FUNC_INIT) + { + switch (function) + { + case FUNC_COMMAND: + result = DecodeCommand(kPipSolarCommands, PipSolarCommand); + break; + + case FUNC_INIT: + PIPSOLARInit(); + break; + case FUNC_LOOP: + case FUNC_SLEEP_LOOP: + PIPSOLARInput(); + break; + case FUNC_EVERY_50_MSECOND: + break; + case FUNC_EVERY_SECOND: + PIPSOLAREverySecond(); + break; + case FUNC_JSON_APPEND: + PIPSOLARShow(true); + break; +#ifdef USE_WEBSERVER + case FUNC_WEB_SENSOR: + PIPSOLARShow(false); + break; +#endif + } + } + return result; +} + +#endif // USE_PIPSOLAR diff --git a/tools/lv_gpio/lv_gpio_enum.h b/tools/lv_gpio/lv_gpio_enum.h index 7aa4f3f94..23649947a 100644 --- a/tools/lv_gpio/lv_gpio_enum.h +++ b/tools/lv_gpio/lv_gpio_enum.h @@ -341,5 +341,7 @@ HDMI_CEC = GPIO_HDMI_CEC HC8_RXD = GPIO_HC8_RXD I2S_DAC = GPIO_I2S_DAC MAGIC_SWITCH = GPIO_MAGIC_SWITCH +PIPSOLAR_TX = GPIO_PIPSOLAR_TX +PIPSOLAR_RX = GPIO_PIPSOLAR_RX SENSOR_END = GPIO_SENSOR_END