From 95c5a41df073141d4c76d33f2bf42dfb04aa582e Mon Sep 17 00:00:00 2001 From: ohAnd <15704728+ohAnd@users.noreply.github.com> Date: Wed, 11 Sep 2024 22:29:17 +0200 Subject: [PATCH] fixing dtu conn handling at dtu shutdown and tft nightmode showing no power at 0 --- include/base/platformData.h | 1 + include/dtuInterface.h | 1 + include/version.h | 6 ++--- include/version.json | 8 +++---- platformio.ini | 7 +++--- readme.md | 31 ++++++++++++++++++-------- src/displayTFT.cpp | 2 ++ src/dtuGateway.ino | 44 ++++++++++++++----------------------- src/dtuInterface.cpp | 40 ++++++++++++++++++++++++++++----- 9 files changed, 87 insertions(+), 53 deletions(-) diff --git a/include/base/platformData.h b/include/base/platformData.h index 700b6c7..480b6f4 100644 --- a/include/base/platformData.h +++ b/include/base/platformData.h @@ -13,6 +13,7 @@ struct baseDataStruct #elif defined(ESP32) uint64_t chipID = ESP.getEfuseMac(); #endif + boolean esp32 = false; String espUniqueName = String(AP_NAME_START) + "_" + chipID; const char *fwVersion = VERSION; diff --git a/include/dtuInterface.h b/include/dtuInterface.h index 9937421..c520ce4 100644 --- a/include/dtuInterface.h +++ b/include/dtuInterface.h @@ -145,6 +145,7 @@ class DTUInterface { void checkingDataUpdate(); + void checkingForLastDataReceived(); boolean cloudPauseActiveControl(); // Protobuf functions diff --git a/include/version.h b/include/version.h index 628376c..0aa5069 100644 --- a/include/version.h +++ b/include/version.h @@ -1,3 +1,3 @@ -#define VERSION "1.9.857_localDev" -#define BUILDTIME "09.09.2024 - 12:39:02" -#define BUILDTIMESTAMP "1725878342" \ No newline at end of file +#define VERSION "2.0.55_localDev" +#define BUILDTIME "11.09.2024 - 21:58:24" +#define BUILDTIMESTAMP "1726084704" \ No newline at end of file diff --git a/include/version.json b/include/version.json index 3d58b92..db02380 100644 --- a/include/version.json +++ b/include/version.json @@ -1,6 +1,6 @@ { - "version": "1.9.857_localDev", - "versiondate": "09.09.2024 - 12:39:02", - "linksnapshot": "https://github.com/ohAnd/dtuGateway/releases/download/snapshot/dtuGateway_snapshot_1.9.857_localDev.bin", - "link": "https://github.com/ohAnd/dtuGateway/releases/latest/download/dtuGateway_release_1.9.857_localDev.bin" + "version": "2.0.55_localDev", + "versiondate": "11.09.2024 - 21:58:24", + "linksnapshot": "https://github.com/ohAnd/dtuGateway/releases/download/snapshot/dtuGateway_snapshot_2.0.55_localDev.bin", + "link": "https://github.com/ohAnd/dtuGateway/releases/latest/download/dtuGateway_release_2.0.55_localDev.bin" } \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 6b2b909..e3adb05 100644 --- a/platformio.ini +++ b/platformio.ini @@ -13,8 +13,8 @@ platform = espressif32 board = esp32dev framework = arduino monitor_speed = 115200 -monitor_port = COM8 -upload_port = COM8 +monitor_port = COM6 +upload_port = COM6 upload_speed = 921600 lib_deps = arduino-libraries/NTPClient @ ^3.2.1 @@ -23,7 +23,6 @@ lib_deps = gyverlibs/UnixTime @ ^1.1 bblanchon/ArduinoJson @ ^7.0.0 knolleary/PubSubClient @ ^2.8 - khoih-prog/ESP32TimerInterrupt @ ^2.3.0 olikraus/U8g2 @ ^2.35.19 bodmer/TFT_eSPI @ ^2.5.43 me-no-dev/AsyncTCP @ ^1.1.1 @@ -36,6 +35,7 @@ custom_nanopb_protos = extra_scripts = pre:version_inc.py board_build.partitions = min_spiffs.csv monitor_filters = + esp32_exception_decoder default time build_flags = @@ -73,7 +73,6 @@ lib_deps = nanopb/Nanopb @ ^0.4.8 gyverlibs/UnixTime @ ^1.1 bblanchon/ArduinoJson @ ^7.0.0 - khoih-prog/ESP8266TimerInterrupt @ ^1.6.0 knolleary/PubSubClient @ ^2.8 me-no-dev/ESPAsyncTCP @ ^1.2.2 me-no-dev/ESP Async WebServer @ ^1.2.3 diff --git a/readme.md b/readme.md index 264f79a..18d0753 100644 --- a/readme.md +++ b/readme.md @@ -39,7 +39,7 @@ ## problem -The new series of Hoymiles inverter with internal wireless access point and wireless client have no direct API to include this endpoint in smarthome installations/ IFTT environments. +The newer series of Hoymiles inverter with internal wireless access point and wireless client have no direct API to include this endpoint in smarthome installations/ IFTT environments. Usually there should be no need for an extra device to "translate" the connection to common APIs or bindings. Unfortunately the interface on the dtu is unlikely unstable/ or not really stable. @@ -77,10 +77,10 @@ So I decided to put this abstraction in an **ESP8266** to have a stable abstract - automatic reboot of DTU, if there is an error detected (e.g. inplausible not changed values) #### connections to the environment -- serving the readed data per /api/data +- serving the read data per /api/data.json - configuration of bindings with seperate activation and login data setting -- binding: updating openHab instance with readed data and pulling set data from the instance -- binding: updating to a MQTT broker with readed data incl. set PowerLimit over MQTT +- binding: updating openHab instance with read data and pulling power set data from the instance +- binding: updating to a MQTT broker with read data incl. subscribing to the Set PowerLimit over MQTT - 2 ways to configure - simple mqtt publishing with base topic or together with HA MQTT AutoDiscovery based - for all publishing retain flag is set (keeping last seen data in broker) - TLS connection to mqtt broker e.g. for hivemq.cloud - ! only possible for ESP32 setup @@ -101,18 +101,30 @@ So I decided to put this abstraction in an **ESP8266** to have a stable abstract - setting the orientation of the display via advanced web config[^2] - OLED - 0 and 180 degrees are supported - TFT - 0, 90, 180, 270 degrees are supported - - brightness and night mode (brightness only for OLED and TFT with connected backlight control) + - brightness and night mode (brightness only for TFT with connected backlight control and OLED) - with night mode enabled and during the active time frame the display will be - - off/ blank (without backlight control) or + - off (with backlight control)/ blank (without backlight control) or - show the current time and power (if greater than 0) in a reduced scope - adjustable via web config[^2] - brightness day [0...255] - will also be used without night mode enabled for standard brightness (falling back to this after power value changed) - - brightness night [0...255] + - brightness night [0...255] - note: 0 = backlight off - (to disable PWM control for TFT without backlight control set both brightness values to zero) - night mode enabled on/ off - night mode start in minutes to start of the day - e.g. 1320 for 22:00 - night mode stop in minutes to start of the day - e.g. 360 for 6:00 - - night clock enabled on/ of - on = clock will be displayed instead of dark screen + - night clock enabled on/ off - on = clock will be displayed instead of dark screen + - example settings: + + | setting | value | comment | + |-----------------|-------|--------- + | brightnessDay | 150 | note: 255 - ~150 only difficult to perceive + | brightnessNight | 30 | + | nightClock | true | show the clock instead of black screen during night time + | nightMode | true | night mode is enabled + | nightmodeStart | 1320 | night time will start at 22 o'clock + | nightmodeEnd | 390 | night time will end at 6:30 + + - display hardware types - display SSH1106 OLED 1,3" 128x64 (other sizes with same driver (SSH1106) and resolution should also directly work) @@ -486,12 +498,13 @@ With the manual login to dtu access point and forcing the storing of local wifi - lot of single updates for power setting within few seconds (< 2-3) without any reading of values (e.g. realdata) -> it seems this creating no problems - therefore current setup -> no time limit for power setting, but reading data only every 31 seconds is running fine - sometimes hanging or full shutdown/ break of DTU will be prevented by sending an active reboot request to dtu (hanging detection at this time over grid voltage, should be changing at least within 10 consecutive incoming data) -- with this setup: now the device is running for days without any stops (overall system point of view: target settings will be performed everytime, readed data will be available, no manual steps needed to recover the dtu connection) +- with this setup: now the device is running for days without any stops (overall system point of view: target settings will be performed everytime, read data will be available, no manual steps needed to recover the dtu connection) ### hoymiles cloud update - everey 15 min (0,15,30,45) -> timestamp update - after 7 min 40 s update of graph data (if wifi not reachable, also reset of wifi AP) +- if there is at these points an active connection to the dtu and current data will be requested, the update to the cloud will be interrupted and no current data for this point in time will be stored in the cloud ### sources diff --git a/src/displayTFT.cpp b/src/displayTFT.cpp index c9d6d7f..b93c278 100644 --- a/src/displayTFT.cpp +++ b/src/displayTFT.cpp @@ -294,6 +294,8 @@ void DisplayTFT::drawFooter(String time) { tft.setTextColor(SPECIAL_BLUE, TFT_BLACK); tft.drawCentreString(" " + String(lastDisplayData.totalPower) + " W ", 120, 174, 4); + } else { + tft.fillRect(60, 170, 120, 37, TFT_BLACK); // clear power display } // // debug brightness diff --git a/src/dtuGateway.ino b/src/dtuGateway.ino index 4f095ad..de0bdfb 100644 --- a/src/dtuGateway.ino +++ b/src/dtuGateway.ino @@ -573,13 +573,13 @@ boolean getPowerSetDataFromOpenHab() } else { - Serial.print("got wrong data for SetLimit: " + openhabMessage); + Serial.println("OPENHAB:\t\t got wrong data for SetLimit: " + openhabMessage); return false; } if (lastOpenhabLimit != newLimit && lastOpenhabLimit != 255) { dtuGlobalData.powerLimitSet = newLimit; - Serial.println("OPENHAB: got new OH Limit: " + String(dtuGlobalData.powerLimitSet) + " - last OH limit: " + String(lastOpenhabLimit) + " %"); + Serial.println("OPENHAB:\t\t got new OH Limit: " + String(dtuGlobalData.powerLimitSet) + " - last OH limit: " + String(lastOpenhabLimit) + " %"); } lastOpenhabLimit = newLimit; return true; @@ -668,9 +668,9 @@ void updateValuesToMqtt(boolean haAutoDiscovery = false) // update all apis according to current states and settings void updateDataToApis() { - if (!dtuConnection.dtuActiveOffToCloudUpdate) + if (!dtuConnection.dtuActiveOffToCloudUpdate) // normal update { - if ((globalControls.getDataAuto || globalControls.getDataOnce) && dtuGlobalData.uptodate) + if (((globalControls.getDataAuto || globalControls.getDataOnce) && dtuGlobalData.uptodate) || dtuConnection.dtuErrorState == DTU_ERROR_LAST_SEND) { if (userConfig.openhabActive) updateValueToOpenhab(); @@ -688,27 +688,6 @@ void updateDataToApis() if (globalControls.getDataOnce) globalControls.getDataOnce = false; } - else if ((dtuGlobalData.currentTimestamp - dtuGlobalData.lastRespTimestamp) > (5 * 60) && dtuGlobalData.grid.voltage > 0) // dtuGlobalData.grid.voltage > 0 indicates dtu/ inverter working - { - dtuGlobalData.grid.power = 0; - dtuGlobalData.grid.current = 0; - dtuGlobalData.grid.voltage = 0; - - dtuGlobalData.pv0.power = 0; - dtuGlobalData.pv0.current = 0; - dtuGlobalData.pv0.voltage = 0; - - dtuGlobalData.pv1.power = 0; - dtuGlobalData.pv1.current = 0; - dtuGlobalData.pv1.voltage = 0; - - if (userConfig.openhabActive) - updateValueToOpenhab(); - if (userConfig.mqttActive) - updateValuesToMqtt(userConfig.mqttHAautoDiscoveryON); - dtuConnection.dtuErrorState = DTU_ERROR_LAST_SEND; - Serial.print(F("\n>>>>> TIMEOUT 5 min for DTU -> NIGHT - send zero values\n")); - } } } @@ -728,6 +707,7 @@ void setup() platformData.chipID |= ((ESP.getEfuseMac() >> (40 - i)) & 0xff) << i; } platformData.espUniqueName = String(AP_NAME_START) + "_" + platformData.chipID; + platformData.esp32 = true; #endif // initialize digital pin LED_BLINK as an output. @@ -1176,7 +1156,16 @@ void loop() // --------> // led blink code only 5 min after startup if ((platformData.currentNTPtime - platformData.dtuGWstarttime) < 300) + { blinkCodeTask(); + } + // turn the LED off only if OLED or TFT with ESP8266 is connected + // ESP32 has an overlapping LED with the SCK pin of the SPI interface for the TFT + else if (userConfig.displayConnected == 0 || (userConfig.displayConnected == 1 && !platformData.esp32)) + { + digitalWrite(LED_BLINK, LED_BLINK_OFF); + } + serialInputTask(); if (userConfig.mqttActive) @@ -1281,9 +1270,8 @@ void loop() Serial.println("----- ----- set new power limit from " + String(dtuGlobalData.powerLimit) + " % to " + String(dtuGlobalData.powerLimitSet) + " % ----- ----- "); dtuInterface.setPowerLimit(dtuGlobalData.powerLimitSet); // set next normal request in 5 seconds from now on, only if last data updated within last 2 times of user setted update rate - // if (currentMillis - dtuGlobalData.lastRespTimestamp < (userConfig.dtuUpdateTime * 2)) - // platformData.dtuNextUpdateCounterSeconds = currentMillis - (userConfig.dtuUpdateTime - 5); - platformData.dtuNextUpdateCounterSeconds = dtuGlobalData.currentTimestamp - userConfig.dtuUpdateTime + 5; + if (dtuGlobalData.currentTimestamp - dtuGlobalData.lastRespTimestamp < (userConfig.dtuUpdateTime * 2)) + platformData.dtuNextUpdateCounterSeconds = dtuGlobalData.currentTimestamp - userConfig.dtuUpdateTime + 5; } } diff --git a/src/dtuInterface.cpp b/src/dtuInterface.cpp index 6f3dcd6..ebf2699 100644 --- a/src/dtuInterface.cpp +++ b/src/dtuInterface.cpp @@ -101,7 +101,7 @@ void DTUInterface::setPowerLimit(int limit) dtuGlobalData.powerLimitSet = limit; if (client->connected()) { - Serial.println("DTUinterface:\t setPowerLimit: " + String(limit) + " - send command to DTU ..."); + Serial.println("DTUinterface:\t try to set setPowerLimit: " + String(limit) + " %"); writeReqCommand(limit); } else @@ -129,11 +129,16 @@ void DTUInterface::dtuLoop() { txrxStateObserver(); + // check if cloud pause is active to prevent cloud errors if (dtuConnection.preventCloudErrors) cloudPauseActiveControl(); else dtuConnection.dtuActiveOffToCloudUpdate = false; + // check for last data received + checkingForLastDataReceived(); + + // check if we are in a cloud pause period if (dtuConnection.dtuActiveOffToCloudUpdate) { if (client->connected()) @@ -298,7 +303,7 @@ void DTUInterface::handleError(uint8_t errorState) { dtuConnection.dtuErrorState = errorState; dtuConnection.dtuConnectState = DTU_STATE_DTU_REBOOT; - Serial.print(F("+++ DTU Connection --- ERROR - try with reboot of DTU - error state: ")); + Serial.print(F("DTUinterface:\t DTU Connection --- ERROR - try with reboot of DTU - error state: ")); Serial.println(errorState); writeCommandRestartDevice(); dtuGlobalData.dtuResetRequested = dtuGlobalData.dtuResetRequested + 1; @@ -618,7 +623,7 @@ void DTUInterface::checkingDataUpdate() // Serial.println("DTUinterface:\t GridV check result: " + String(gridVoltValueHanging)); if (gridVoltValueHanging) { - Serial.println(F("DTUinterface:\t grid voltage observer found hanging value - try to reboot DTU")); + Serial.println(F("DTUinterface:\t checkingDataUpdate -> grid voltage observer found hanging value - try to reboot DTU")); handleError(DTU_ERROR_DATA_NO_CHANGE); dtuGlobalData.uptodate = false; } @@ -632,18 +637,43 @@ void DTUInterface::checkingDataUpdate() if (abs((int(dtuGlobalData.respTimestamp) - int(dtuGlobalData.currentTimestamp))) > 3) { dtuGlobalData.currentTimestamp = dtuGlobalData.respTimestamp; - Serial.print(F("\n>--> synced local time with DTU time <--<\n")); + Serial.print(F("DTUinterface:\t checkingDataUpdate ---> synced local time with DTU time\n")); } } else { dtuGlobalData.uptodate = false; + Serial.print(F("DTUinterface:\t checkingDataUpdate -> DTU_ERROR_NO_TIME\n")); // stopping connection to DTU when response time error - try with reconnec handleError(DTU_ERROR_NO_TIME); } dtuGlobalData.lastRespTimestamp = dtuGlobalData.respTimestamp; } +void DTUInterface::checkingForLastDataReceived() +{ + // check if last data received - currentTimestamp + 5 sec (to debounce async current timestamp) - lastRespTimestamp > 3 min + if (((dtuGlobalData.currentTimestamp + 5) - dtuGlobalData.lastRespTimestamp) > (3 * 60) && dtuGlobalData.grid.voltage > 0 && dtuConnection.dtuErrorState != DTU_ERROR_LAST_SEND) // dtuGlobalData.grid.voltage > 0 indicates dtu/ inverter was working + { + dtuGlobalData.grid.power = 0; + dtuGlobalData.grid.current = 0; + dtuGlobalData.grid.voltage = 0; + + dtuGlobalData.pv0.power = 0; + dtuGlobalData.pv0.current = 0; + dtuGlobalData.pv0.voltage = 0; + + dtuGlobalData.pv1.power = 0; + dtuGlobalData.pv1.current = 0; + dtuGlobalData.pv1.voltage = 0; + + + dtuConnection.dtuErrorState = DTU_ERROR_LAST_SEND; + dtuGlobalData.updateReceived = true; + Serial.println("DTUinterface:\t checkingForLastDataReceived >>>>> TIMEOUT 5 min for DTU -> NIGHT - send zero values +++ currentTimestamp: " + String(dtuGlobalData.currentTimestamp) + " - lastRespTimestamp: " + String(dtuGlobalData.lastRespTimestamp)); + } +} + void DTUInterface::writeReqAppGetHistPower() { uint8_t buffer[200]; @@ -1013,7 +1043,7 @@ boolean DTUInterface::readRespCommandRestartDevice(pb_istream_t istream) dtuConnection.dtuTxRxState = DTU_TXRX_STATE_IDLE; CommandReqDTO commandreqdto = CommandReqDTO_init_default; - Serial.print(" --> respCommand Restart - got remote: " + getTimeStringByTimestamp(commandreqdto.time)); + Serial.print("DTUinterface:\t -readRespCommandRestartDevice - got remote: " + getTimeStringByTimestamp(commandreqdto.time)); pb_decode(&istream, &GetConfigReqDTO_msg, &commandreqdto); Serial.printf("\ncommand req action: %i", commandreqdto.action);