From dc5448ed3609b59a81d1f69572c86b3215254bde Mon Sep 17 00:00:00 2001 From: Patrick Amrhein Date: Thu, 28 Mar 2024 10:38:01 +0100 Subject: [PATCH 01/22] Bugfix: platformio.ini --- src/platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platformio.ini b/src/platformio.ini index 90b823f5..34a40c21 100644 --- a/src/platformio.ini +++ b/src/platformio.ini @@ -32,7 +32,7 @@ lib_deps = bblanchon/ArduinoJson @ ^6.21.3 https://github.com/JChristensen/Timezone @ ^1.2.4 olikraus/U8g2 @ ^2.35.9 - https://github.com/zinggjm/GxEPD2#1.5.3s + https://github.com/zinggjm/GxEPD2#1.5.3 build_flags = -std=c++17 -std=gnu++17 From 8049eb16c4228bf980627d03f5cf61b60349fafe Mon Sep 17 00:00:00 2001 From: DanielR92 Date: Thu, 28 Mar 2024 13:57:32 +0100 Subject: [PATCH 02/22] smal fixes, remove comment what clear is --- src/plugins/zeroExport/powermeter.h | 15 ++++++++--- src/plugins/zeroExport/zeroExport.h | 42 ++++++++++++++++++----------- 2 files changed, 38 insertions(+), 19 deletions(-) diff --git a/src/plugins/zeroExport/powermeter.h b/src/plugins/zeroExport/powermeter.h index 2611c825..b9817053 100644 --- a/src/plugins/zeroExport/powermeter.h +++ b/src/plugins/zeroExport/powermeter.h @@ -391,6 +391,7 @@ class powermeter { * @param group * @returns true/false * @TODO: Username & Passwort wird mittels base64 verschlüsselt. Dies wird für die Authentizierung benötigt. Wichtig diese im WebUI unkenntlich zu machen und base64 im eeprom zu speichern, statt klartext. + * @TODO: Abfrage Interval einbauen. Info: Datei-Size kann auch mal 0-bytes sein! */ sml_states_t currentState; @@ -448,7 +449,7 @@ class powermeter { http.begin(url); http.addHeader("Authorization", "Basic " + auth); - if (http.GET() == HTTP_CODE_OK) { + if (http.GET() == HTTP_CODE_OK && http.getSize() != 0) { String myString = http.getString(); char floatBuffer[20]; @@ -474,9 +475,7 @@ class powermeter { mCfg->groups[group].pmPowerL3 = _powerMeterTotal / 3; } -// TODO: Ein return an dieser Stelle verhindert das ordnungsgemäße http.end() result = true; -// return true; break; case SML_LISTEND: // check handlers on last received list @@ -487,9 +486,19 @@ class powermeter { } } break; + + default: + logObj["SML_DEFAULT"] = String(smlCurrentState); + break; } } } + else + { + logObj["result"] = String(result); + logObj["http_size"] = String(http.getSize()); + } + http.end(); logObj["P"] = mCfg->groups[group].pmPower; diff --git a/src/plugins/zeroExport/zeroExport.h b/src/plugins/zeroExport/zeroExport.h index ca268094..0ada3cbd 100644 --- a/src/plugins/zeroExport/zeroExport.h +++ b/src/plugins/zeroExport/zeroExport.h @@ -68,48 +68,50 @@ class ZeroExport { continue; } + bool flag = false; + switch (mCfg->groups[group].state) { case zeroExportState::INIT: - if (groupInit(group)) sendLog(); + flag = groupInit(group); break; case zeroExportState::WAIT: - if (groupWait(group)) sendLog(); + flag = groupWait(group); break; case zeroExportState::PUBLISH: - if (groupPublish(group)) sendLog(); + flag = groupPublish(group); break; case zeroExportState::WAITREFRESH: - if (groupWaitRefresh(group)) sendLog(); + flag = groupWaitRefresh(group); break; case zeroExportState::GETINVERTERACKS: - if (groupGetInverterAcks(group)) sendLog(); + flag = groupGetInverterAcks(group); break; case zeroExportState::GETINVERTERDATA: - if (groupGetInverterData(group)) sendLog(); + flag = groupGetInverterData(group); break; case zeroExportState::BATTERYPROTECTION: - if (groupBatteryprotection(group)) sendLog(); + flag = groupBatteryprotection(group); break; case zeroExportState::GETPOWERMETER: - if (groupGetPowermeter(group)) sendLog(); + flag = groupGetPowermeter(group); break; case zeroExportState::CONTROLLER: - if (groupController(group)) sendLog(); + flag = groupController(group); break; case zeroExportState::PROGNOSE: - if (groupPrognose(group)) sendLog(); + flag = groupPrognose(group); break; case zeroExportState::AUFTEILEN: - if (groupAufteilen(group)) sendLog(); + flag = groupAufteilen(group); break; case zeroExportState::SETLIMIT: - if (groupSetLimit(group)) sendLog(); + flag = groupSetLimit(group); break; case zeroExportState::SETPOWER: - if (groupSetPower(group)) sendLog(); + flag = groupSetPower(group); break; case zeroExportState::SETREBOOT: - if (groupSetReboot(group)) sendLog(); + flag = groupSetReboot(group); break; case zeroExportState::FINISH: case zeroExportState::ERROR: @@ -121,6 +123,8 @@ class ZeroExport { } break; } + + if (flag) sendLog(); } } @@ -292,13 +296,19 @@ class ZeroExport { } /** onMqttMessage - * + * Subscribe section */ void onMqttMessage(JsonObject obj) { if ((!mIsInitialized) || (!mCfg->enabled)) { return; } + // MQTT":{"val":0,"path":"zero","cmd":"set","id":0} + if (strcmp(obj["cmd"], "set") != 0 && strcmp(obj["path"], "zero") != 0) + { + mCfg->enabled = (bool)obj["val"]; + } + mLog["MQTT"] = obj; sendLog(); } @@ -460,7 +470,7 @@ class ZeroExport { // mCfg->groups[group].publishPower = false; obj["L1"] = mCfg->groups[group].pmPowerL1; obj["L2"] = mCfg->groups[group].pmPowerL2; - obj["L2"] = mCfg->groups[group].pmPowerL3; + obj["L3"] = mCfg->groups[group].pmPowerL3; obj["Sum"] = mCfg->groups[group].pmPower; mMqtt->publish("zero/state/powermeter/P", doc.as().c_str(), false); doc.clear(); From 5afc97f7f8d6d8046d55406cd7eeb7e76b99e2b7 Mon Sep 17 00:00:00 2001 From: Patrick Amrhein Date: Sat, 30 Mar 2024 10:04:14 +0100 Subject: [PATCH 03/22] Redesign Statemachine --- src/app.cpp | 6 +- src/config/settings.h | 28 +- src/plugins/zeroExport/zeroExport.h | 1077 ++++++++++++++------------- src/web/RestApi.h | 1 - 4 files changed, 589 insertions(+), 523 deletions(-) diff --git a/src/app.cpp b/src/app.cpp index 4c096e83..4e3595d1 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -235,7 +235,7 @@ void app::regularTickers(void) { // Plugin ZeroExport #if defined(PLUGIN_ZEROEXPORT) - everySec(std::bind(&ZeroExportType::tickerSecond, &mZeroExport), "ZeroExport"); + everySec(std::bind(&ZeroExportType::tickSecond, &mZeroExport), "ZeroExport"); #endif // Plugin ZeroExport - Ende @@ -423,6 +423,10 @@ void app::tickMinute(void) { //----------------------------------------------------------------------------- void app::tickMidnight(void) { + #if defined(PLUGIN_ZEROEXPORT) + mZeroExport.tickMidnight(); + #endif /*defined(PLUGIN_ZEROEXPORT)*/ + uint32_t localTime = gTimezone.toLocal(mTimestamp); uint32_t nxtTrig = gTimezone.toUTC(localTime - (localTime % 86400) + 86400); // next midnight local time onceAt(std::bind(&app::tickMidnight, this), nxtTrig, "mid2"); diff --git a/src/config/settings.h b/src/config/settings.h index 397bd627..0c350809 100644 --- a/src/config/settings.h +++ b/src/config/settings.h @@ -204,7 +204,6 @@ typedef struct { enum class zeroExportState : uint8_t { INIT, WAIT, - PUBLISH, WAITREFRESH, GETINVERTERACKS, GETINVERTERDATA, @@ -213,9 +212,11 @@ enum class zeroExportState : uint8_t { CONTROLLER, PROGNOSE, AUFTEILEN, - SETLIMIT, - SETPOWER, SETREBOOT, + SETPOWER, + SETLIMIT, + PUBLISH, + EMERGENCY, FINISH, ERROR }; @@ -271,6 +272,9 @@ typedef struct { unsigned long limitTsp; float dcVoltage; bool state; + // + bool doReboot; + int8_t doPower; } zeroExportGroupInverter_t; typedef struct { @@ -296,12 +300,11 @@ typedef struct { uint16_t powerMax; // - zeroExportState stateLast; zeroExportState state; zeroExportState stateNext; unsigned long lastRun; unsigned long lastRefresh; -// bool waitForAck; + uint16_t sleep; float eSum; float eSum1; @@ -315,12 +318,12 @@ typedef struct { float Ki; float Kd; -float pm_P[5]; -float pm_P1[5]; -float pm_P2[5]; -float pm_P3[5]; -uint8_t pm_iIn = 0; -uint8_t pm_iOut = 0; +//float pm_P[5]; +//float pm_P1[5]; +//float pm_P2[5]; +//float pm_P3[5]; +//uint8_t pm_iIn = 0; +//uint8_t pm_iOut = 0; float pmPower; float pmPowerL1; @@ -699,6 +702,8 @@ class settings { mCfg.plugin.zeroExport.groups[group].inverters[inv].waitLimitAck = false; mCfg.plugin.zeroExport.groups[group].inverters[inv].waitPowerAck = false; mCfg.plugin.zeroExport.groups[group].inverters[inv].waitRebootAck = false; + mCfg.plugin.zeroExport.groups[group].inverters[inv].doReboot = false; + mCfg.plugin.zeroExport.groups[group].inverters[inv].doPower = -1; } // Battery mCfg.plugin.zeroExport.groups[group].battEnabled = false; @@ -716,6 +721,7 @@ class settings { mCfg.plugin.zeroExport.groups[group].state = zeroExportState::INIT; mCfg.plugin.zeroExport.groups[group].lastRun = 0; mCfg.plugin.zeroExport.groups[group].lastRefresh = 0; + mCfg.plugin.zeroExport.groups[group].sleep = 0; mCfg.plugin.zeroExport.groups[group].pmPower = 0; mCfg.plugin.zeroExport.groups[group].pmPowerL1 = 0; mCfg.plugin.zeroExport.groups[group].pmPowerL2 = 0; diff --git a/src/plugins/zeroExport/zeroExport.h b/src/plugins/zeroExport/zeroExport.h index 0ada3cbd..a80e2e9b 100644 --- a/src/plugins/zeroExport/zeroExport.h +++ b/src/plugins/zeroExport/zeroExport.h @@ -17,10 +17,6 @@ template -// TODO: Anbindung an MQTT für Logausgabe. -// TODO: Powermeter erweitern -// TODO: Der Teil der noch in app.cpp steckt komplett hier in die Funktion verschieben. - class ZeroExport { public: /** ZeroExport @@ -55,6 +51,10 @@ class ZeroExport { /** loop * Arbeitsschleife + * @param + * @returns + * @todo publish + * @todo emergency */ void loop(void) { if ((!mIsInitialized) || (!mCfg->enabled)) { @@ -63,76 +63,242 @@ class ZeroExport { mPowermeter.loop(); + unsigned long Tsp = millis(); + bool DoLog = false; + for (uint8_t group = 0; group < ZEROEXPORT_MAX_GROUPS; group++) { if (!mCfg->groups[group].enabled) { continue; } - bool flag = false; + // sleep + if (Tsp <= (mCfg->groups[group].lastRun + mCfg->groups[group].sleep)) { + continue; + } + mCfg->groups[group].sleep = 0; + + mLog["g"] = group; + mLog["s"] = (uint8_t)mCfg->groups[group].state; switch (mCfg->groups[group].state) { case zeroExportState::INIT: - flag = groupInit(group); + if (groupInit(group, &Tsp, &DoLog)) { + mCfg->groups[group].lastRun = Tsp; + // OK -> Wait, WaitRefresh + mCfg->groups[group].state = zeroExportState::WAIT; + mCfg->groups[group].stateNext = zeroExportState::WAITREFRESH; + } else { + // Error -> Wait, Init + mCfg->groups[group].state = zeroExportState::WAIT; + mCfg->groups[group].stateNext = zeroExportState::INIT; + } break; case zeroExportState::WAIT: - flag = groupWait(group); - break; - case zeroExportState::PUBLISH: - flag = groupPublish(group); + if (groupWait(group, &Tsp, &DoLog)) { + mCfg->groups[group].lastRun = Tsp; + // OK + if (mCfg->groups[group].state != mCfg->groups[group].stateNext) { + // OK -> Next + mCfg->groups[group].state = mCfg->groups[group].stateNext; + } else { + // OK -> Init + mCfg->groups[group].state = zeroExportState::INIT; + mCfg->groups[group].stateNext = zeroExportState::INIT; + } + } break; case zeroExportState::WAITREFRESH: - flag = groupWaitRefresh(group); + if (groupWaitRefresh(group, &Tsp, &DoLog)) { + mCfg->groups[group].lastRun = Tsp; + // OK +#if defined(ZEROEXPORT_DEV_POWERMETER) + mCfg->groups[group].state = zeroExportState::GETPOWERMETER; + mCfg->groups[group].stateNext = zeroExportState::GETPOWERMETER; +#else + mCfg->groups[group].state = zeroExportState::GETINVERTERACKS; + mCfg->groups[group].stateNext = zeroExportState::GETINVERTERACKS; +#endif + } break; case zeroExportState::GETINVERTERACKS: - flag = groupGetInverterAcks(group); + if (groupGetInverterAcks(group, &Tsp, &DoLog)) { + mCfg->groups[group].lastRun = Tsp; + // OK + mCfg->groups[group].state = zeroExportState::GETINVERTERDATA; + mCfg->groups[group].stateNext = zeroExportState::GETINVERTERDATA; + } else { + // Error - Sleep + mCfg->groups[group].lastRun = Tsp; + mCfg->groups[group].sleep = 1000; + } break; case zeroExportState::GETINVERTERDATA: - flag = groupGetInverterData(group); + if (groupGetInverterData(group, &Tsp, &DoLog)) { + mCfg->groups[group].lastRun = Tsp; + // OK + mCfg->groups[group].state = zeroExportState::BATTERYPROTECTION; + mCfg->groups[group].stateNext = zeroExportState::BATTERYPROTECTION; + } else { + // Error - Sleep + mCfg->groups[group].lastRun = Tsp; + mCfg->groups[group].sleep = 500; + } break; case zeroExportState::BATTERYPROTECTION: - flag = groupBatteryprotection(group); + if (groupBatteryprotection(group, &Tsp, &DoLog)) { + mCfg->groups[group].lastRun = Tsp; + // OK + mCfg->groups[group].state = zeroExportState::GETPOWERMETER; + mCfg->groups[group].stateNext = zeroExportState::GETPOWERMETER; + } else { + // Error - Sleep + mCfg->groups[group].lastRun = Tsp; + mCfg->groups[group].sleep = 1000; + } break; case zeroExportState::GETPOWERMETER: - flag = groupGetPowermeter(group); + if (groupGetPowermeter(group, &Tsp, &DoLog)) { + mCfg->groups[group].lastRun = Tsp; + // OK +#if defined(ZEROEXPORT_DEV_POWERMETER) + mCfg->groups[group].lastRefresh = millis(); + mCfg->groups[group].state = zeroExportState::PUBLISH; + mCfg->groups[group].stateNext = zeroExportState::PUBLISH; +#else + mCfg->groups[group].state = zeroExportState::CONTROLLER; + mCfg->groups[group].stateNext = zeroExportState::CONTROLLER; +#endif + } else { + // Error - Sleep + mCfg->groups[group].lastRun = Tsp; + mCfg->groups[group].sleep = 3000; + } break; case zeroExportState::CONTROLLER: - flag = groupController(group); + if (groupController(group, &Tsp, &DoLog)) { + mCfg->groups[group].lastRun = Tsp; + mCfg->groups[group].lastRefresh = Tsp; + // OK + mCfg->groups[group].state = zeroExportState::PROGNOSE; + mCfg->groups[group].stateNext = zeroExportState::PROGNOSE; + } else { + // Error - WaitRefresh + mCfg->groups[group].state = zeroExportState::WAITREFRESH; + mCfg->groups[group].stateNext = zeroExportState::WAITREFRESH; + } break; case zeroExportState::PROGNOSE: - flag = groupPrognose(group); + if (groupPrognose(group, &Tsp, &DoLog)) { + mCfg->groups[group].lastRun = Tsp; + // OK + mCfg->groups[group].state = zeroExportState::AUFTEILEN; + mCfg->groups[group].stateNext = zeroExportState::AUFTEILEN; + } else { + // Error - Sleep + mCfg->groups[group].lastRun = Tsp; + mCfg->groups[group].sleep = 500; + } break; case zeroExportState::AUFTEILEN: - flag = groupAufteilen(group); + if (groupAufteilen(group, &Tsp, &DoLog)) { + mCfg->groups[group].lastRun = Tsp; + // OK + mCfg->groups[group].state = zeroExportState::SETREBOOT; + mCfg->groups[group].stateNext = zeroExportState::SETREBOOT; + } else { + // Error - Sleep + mCfg->groups[group].lastRun = Tsp; + mCfg->groups[group].sleep = 500; + } break; - case zeroExportState::SETLIMIT: - flag = groupSetLimit(group); + case zeroExportState::SETREBOOT: + if (groupSetReboot(group, &Tsp, &DoLog)) { + mCfg->groups[group].lastRun = Tsp; + mCfg->groups[group].lastRefresh = Tsp; + // OK + mCfg->groups[group].state = zeroExportState::SETPOWER; + mCfg->groups[group].stateNext = zeroExportState::SETPOWER; + } else { + // Error - Sleep + mCfg->groups[group].lastRun = Tsp; + mCfg->groups[group].sleep = 1000; + } break; case zeroExportState::SETPOWER: - flag = groupSetPower(group); + if (groupSetPower(group, &Tsp, &DoLog)) { + mCfg->groups[group].lastRun = Tsp; + mCfg->groups[group].lastRefresh = Tsp; + // OK + mCfg->groups[group].state = zeroExportState::SETLIMIT; + mCfg->groups[group].stateNext = zeroExportState::SETLIMIT; + } else { + // Error - Sleep + mCfg->groups[group].lastRun = Tsp; + mCfg->groups[group].sleep = 1000; + } break; - case zeroExportState::SETREBOOT: - flag = groupSetReboot(group); + case zeroExportState::SETLIMIT: + if (groupSetLimit(group, &Tsp, &DoLog)) { + mCfg->groups[group].lastRun = Tsp; + // OK + mCfg->groups[group].state = zeroExportState::PUBLISH; + mCfg->groups[group].stateNext = zeroExportState::PUBLISH; + } else { + // Error - Sleep + mCfg->groups[group].lastRun = Tsp; + mCfg->groups[group].sleep = 1000; + } + break; + case zeroExportState::PUBLISH: + if (groupPublish(group, &Tsp, &DoLog)) { + mCfg->groups[group].lastRun = Tsp; + // OK + mCfg->groups[group].state = zeroExportState::WAITREFRESH; + mCfg->groups[group].stateNext = zeroExportState::WAITREFRESH; + //} else { + // TODO: fehlt + } + break; + case zeroExportState::EMERGENCY: + if (groupEmergency(group, &Tsp, &DoLog)) { + mCfg->groups[group].lastRun = Tsp; + mCfg->groups[group].lastRefresh = Tsp; + // OK + mCfg->groups[group].state = zeroExportState::INIT; + mCfg->groups[group].stateNext = zeroExportState::INIT; + //} else { + // TODO: fehlt + } break; case zeroExportState::FINISH: case zeroExportState::ERROR: default: + mCfg->groups[group].lastRun = Tsp; + mCfg->groups[group].lastRefresh = Tsp; + mCfg->groups[group].sleep = 1000; mCfg->groups[group].state = zeroExportState::INIT; mCfg->groups[group].stateNext = zeroExportState::INIT; - if (millis() > (mCfg->groups[group].lastRefresh + (mCfg->groups[group].refresh * 1000UL))) { - mCfg->groups[group].lastRefresh = millis(); - } break; } - if (flag) sendLog(); + if (mCfg->debug) { + unsigned long eTsp = millis(); + mLog["B"] = Tsp; + mLog["E"] = eTsp; + mLog["D"] = eTsp - Tsp; + } + + if (DoLog) sendLog(); + clearLog(); + DoLog = false; } + return; } - /** tickerSecond + /** tickSecond * Zeitimpuls */ - void tickerSecond() { - // TODO: Warten ob benötigt, ggf ein bit setzen, das in der loop() abgearbeitet wird. + void tickSecond() { if ((!mIsInitialized) || (!mCfg->enabled)) { return; } @@ -156,6 +322,23 @@ class ZeroExport { } } + /** tickerMidnight + * Zeitimpuls + */ + void tickMidnight(void) { + if ((!mIsInitialized) || (!mCfg->enabled)) { + return; + } + + // Reboot + for (uint8_t group = 0; group < ZEROEXPORT_MAX_GROUPS; group++) { + for (uint8_t inv = 0; inv < ZEROEXPORT_GROUP_MAX_INVERTERS; inv++) { + // Reboot + mCfg->groups[group].inverters[inv].doReboot = true; + } + } + } + /** resetWaitLimitAck * * @param @@ -177,10 +360,12 @@ class ZeroExport { mCfg->groups[group].inverters[inv].waitLimitAck = 0; mLog["w"] = 0; - unsigned long eTsp = millis(); - mLog["B"] = bTsp; - mLog["E"] = eTsp; - mLog["D"] = eTsp - bTsp; + if (mCfg->debug) { + unsigned long eTsp = millis(); + mLog["B"] = bTsp; + mLog["E"] = eTsp; + mLog["D"] = eTsp - bTsp; + } sendLog(); return; } @@ -209,10 +394,12 @@ class ZeroExport { mCfg->groups[group].inverters[inv].waitPowerAck = 30; mLog["w"] = 30; - unsigned long eTsp = millis(); - mLog["B"] = bTsp; - mLog["E"] = eTsp; - mLog["D"] = eTsp - bTsp; + if (mCfg->debug) { + unsigned long eTsp = millis(); + mLog["B"] = bTsp; + mLog["E"] = eTsp; + mLog["D"] = eTsp - bTsp; + } sendLog(); return; } @@ -241,10 +428,12 @@ class ZeroExport { mCfg->groups[group].inverters[inv].waitRebootAck = 30; mLog["w"] = 30; - unsigned long eTsp = millis(); - mLog["B"] = bTsp; - mLog["E"] = eTsp; - mLog["D"] = eTsp - bTsp; + if (mCfg->debug) { + unsigned long eTsp = millis(); + mLog["B"] = bTsp; + mLog["E"] = eTsp; + mLog["D"] = eTsp - bTsp; + } sendLog(); return; } @@ -284,10 +473,13 @@ class ZeroExport { mLog["ivL"] = ivL; mLog["zeL"] = (uint16_t)mCfg->groups[group].inverters[inv].limit; mCfg->groups[group].inverters[inv].limit = ivL; - unsigned long eTsp = millis(); - mLog["B"] = bTsp; - mLog["E"] = eTsp; - mLog["D"] = eTsp - bTsp; + + if (mCfg->debug) { + unsigned long eTsp = millis(); + mLog["B"] = bTsp; + mLog["E"] = eTsp; + mLog["D"] = eTsp - bTsp; + } sendLog(); return; } @@ -302,10 +494,9 @@ class ZeroExport { if ((!mIsInitialized) || (!mCfg->enabled)) { return; } - + return; // MQTT":{"val":0,"path":"zero","cmd":"set","id":0} - if (strcmp(obj["cmd"], "set") != 0 && strcmp(obj["path"], "zero") != 0) - { + if (strcmp(obj["cmd"], "set") != 0 && strcmp(obj["path"], "zero") != 0) { mCfg->enabled = (bool)obj["val"]; } @@ -320,19 +511,18 @@ class ZeroExport { * @returns true/false * @todo getInverterById statt getInverterByPos, dann würde die Variable *iv und die Schleife nicht gebraucht. */ - bool groupInit(uint8_t group) { - bool doLog = false; - unsigned long bTsp = millis(); + bool groupInit(uint8_t group, unsigned long *tsp, bool *doLog) { + uint8_t result = false; - mLog["t"] = "groupInit"; - mLog["g"] = group; + if (mCfg->debug) mLog["t"] = "groupInit"; - mCfg->groups[group].stateLast = zeroExportState::INIT; + mCfg->groups[group].lastRun = *tsp; + + *doLog = true; // Init ivPointer for (uint8_t inv = 0; inv < ZEROEXPORT_GROUP_MAX_INVERTERS; inv++) { mIv[group][inv] = nullptr; - doLog = true; } // Search/Set ivPointer @@ -373,7 +563,7 @@ class ZeroExport { logObj["id"] = iv->id; mIv[group][inv] = mSys->getInverterByPos(i); - doLog = true; + result = true; } } @@ -384,188 +574,63 @@ class ZeroExport { mCfg->groups[group].inverters[inv].waitRebootAck = 0; } - // Next - mCfg->groups[group].state = zeroExportState::WAIT; - mCfg->groups[group].stateNext = zeroExportState::WAIT; - - unsigned long eTsp = millis(); - mLog["B"] = bTsp; - mLog["E"] = eTsp; - mLog["D"] = eTsp - bTsp; - - mCfg->groups[group].lastRun = eTsp; - mCfg->groups[group].lastRefresh = eTsp; + mCfg->groups[group].lastRefresh = *tsp; - return doLog; + return result; } /** groupWait - * pausiert die Gruppe + * pausiert die Gruppe bis die Wartezeit 60s seit dem lastRun abgelaufen ist. * @param group * @returns true/false */ - bool groupWait(uint8_t group) { - bool doLog = false; - unsigned long bTsp = millis(); - - mLog["t"] = "groupWait"; - mLog["g"] = group; + bool groupWait(uint8_t group, unsigned long *tsp, bool *doLog) { + if (mCfg->debug) mLog["t"] = "groupWait"; // Wait 60s - if (bTsp <= (mCfg->groups[group].lastRun + 60000UL)) { - return doLog; - } - - mCfg->groups[group].stateLast = zeroExportState::WAIT; - - doLog = true; - - // Next - if (mCfg->groups[group].state != mCfg->groups[group].stateNext) { - mCfg->groups[group].state = mCfg->groups[group].stateNext; - } else { - mCfg->groups[group].state = zeroExportState::WAITREFRESH; - mCfg->groups[group].stateNext = zeroExportState::WAITREFRESH; + if (*tsp <= (mCfg->groups[group].lastRun + 60000UL)) { + return false; } - unsigned long eTsp = millis(); - mLog["B"] = bTsp; - mLog["E"] = eTsp; - mLog["D"] = eTsp - bTsp; + mCfg->groups[group].lastRun = *tsp; - mCfg->groups[group].lastRun = eTsp; - mCfg->groups[group].lastRefresh = eTsp; + *doLog = true; - return doLog; - } - - /** groupPublish - * - */ - bool groupPublish(uint8_t group) { - bool doLog = false; - unsigned long bTsp = millis(); - - mLog["t"] = "groupPublish"; - mLog["g"] = group; - - mCfg->groups[group].stateLast = zeroExportState::PUBLISH; - - if (mMqtt->isConnected()) { - DynamicJsonDocument doc(512); - JsonObject obj = doc.to(); - - doLog = true; - - // Init - if (!mIsSubscribed) { - mIsSubscribed = true; - mMqtt->publish("zero/set/enabled", ((mCfg->enabled) ? dict[STR_TRUE] : dict[STR_FALSE]), false); - mMqtt->subscribe("zero/set/enabled", QOS_2); - } - - mMqtt->publish("zero/state/enabled", ((mCfg->enabled) ? dict[STR_TRUE] : dict[STR_FALSE]), false); - - // if (mCfg->groups[group].publishPower) { - // mCfg->groups[group].publishPower = false; - obj["L1"] = mCfg->groups[group].pmPowerL1; - obj["L2"] = mCfg->groups[group].pmPowerL2; - obj["L3"] = mCfg->groups[group].pmPowerL3; - obj["Sum"] = mCfg->groups[group].pmPower; - mMqtt->publish("zero/state/powermeter/P", doc.as().c_str(), false); - doc.clear(); - // } - - // if (mCfg->groups[group].pm_Publish_W) { - // mCfg->groups[group].pm_Publish_W = false; - // obj["todo"] = "true"; - // obj["L1"] = mCfg->groups[group].pm_P1; - // obj["L2"] = mCfg->groups[group].pm_P2; - // obj["L2"] = mCfg->groups[group].pm_P3; - // obj["Sum"] = mCfg->groups[group].pm_P; - // mMqtt->publish("zero/powermeter/W", doc.as().c_str(), false); - // doc.clear(); - // } - - for (uint8_t inv = 0; inv < ZEROEXPORT_GROUP_MAX_INVERTERS; inv++) { - } - } - - mCfg->groups[group].state = zeroExportState::WAITREFRESH; - mCfg->groups[group].stateNext = zeroExportState::WAITREFRESH; - - unsigned long eTsp = millis(); - mLog["B"] = bTsp; - mLog["E"] = eTsp; - mLog["D"] = eTsp - bTsp; - - mCfg->groups[group].lastRun = eTsp; - - return doLog; + return true; } /** groupWaitRefresh - * pausiert die Gruppe + * pausiert die Gruppe bis die Wartezeit refresh seit dem lastRefresh abgelaufen ist. * @param group * @returns true/false */ - bool groupWaitRefresh(uint8_t group) { - bool doLog = false; - unsigned long bTsp = millis(); - - mLog["t"] = "groupWaitRefresh"; - mLog["g"] = group; + bool groupWaitRefresh(uint8_t group, unsigned long *tsp, bool *doLog) { + if (mCfg->debug) mLog["t"] = "groupWaitRefresh"; // Wait Refreshtime - if (bTsp <= (mCfg->groups[group].lastRefresh + (mCfg->groups[group].refresh * 1000UL))) { - return doLog; + if (*tsp <= (mCfg->groups[group].lastRefresh + (mCfg->groups[group].refresh * 1000UL))) { + return false; } - mCfg->groups[group].stateLast = zeroExportState::WAITREFRESH; - - doLog = true; - - // Next -#if defined(ZEROEXPORT_DEV_POWERMETER) - mCfg->groups[group].state = zeroExportState::GETPOWERMETER; - mCfg->groups[group].stateNext = zeroExportState::GETPOWERMETER; -#else - mCfg->groups[group].state = zeroExportState::GETINVERTERACKS; - mCfg->groups[group].stateNext = zeroExportState::GETINVERTERACKS; -#endif - - unsigned long eTsp = millis(); - mLog["B"] = bTsp; - mLog["E"] = eTsp; - mLog["D"] = eTsp - bTsp; + mCfg->groups[group].lastRun = *tsp; - mCfg->groups[group].lastRun = eTsp; + *doLog = true; - return doLog; + return true; } /** groupGetInverterAcks * aktualisiert die Gruppe mit den ACKs der Inverter für neue Limits * @param group * @returns true/false + * @todo siehe code */ - bool groupGetInverterAcks(uint8_t group) { - bool doLog = false; - unsigned long bTsp = millis(); - - mLog["t"] = "groupGetInverterAcks"; - mLog["g"] = group; - - // Wait 250ms - if (mCfg->groups[group].stateLast == zeroExportState::GETINVERTERACKS) { - if (bTsp <= (mCfg->groups[group].lastRun + 250UL)) { - return doLog; - } - } + bool groupGetInverterAcks(uint8_t group, unsigned long *tsp, bool *doLog) { + if (mCfg->debug) mLog["t"] = "groupGetInverterAcks"; - mCfg->groups[group].stateLast = zeroExportState::GETINVERTERACKS; + mCfg->groups[group].lastRun = *tsp; - doLog = true; + *doLog = true; // Wait Acks JsonArray logArr = mLog.createNestedArray("ix"); @@ -592,34 +657,24 @@ class ZeroExport { logObj["wL"] = mCfg->groups[group].inverters[inv].waitLimitAck; wait = true; } - // waitPowerAck - if (mCfg->groups[group].inverters[inv].waitPowerAck > 0) { - logObj["wP"] = mCfg->groups[group].inverters[inv].waitPowerAck; - wait = true; - } - // waitRebootAck - if (mCfg->groups[group].inverters[inv].waitRebootAck > 0) { - logObj["wR"] = mCfg->groups[group].inverters[inv].waitRebootAck; - wait = true; - } + // TODO: Remove -> is moved to groupSetPower + // // waitPowerAck + // if (mCfg->groups[group].inverters[inv].waitPowerAck > 0) { + // logObj["wP"] = mCfg->groups[group].inverters[inv].waitPowerAck; + // wait = true; + // } + // TODO: Remove -> is moved to groupSetReboot + // // waitRebootAck + // if (mCfg->groups[group].inverters[inv].waitRebootAck > 0) { + // logObj["wR"] = mCfg->groups[group].inverters[inv].waitRebootAck; + // wait = true; + // } } mLog["w"] = wait; - // Next - if (!wait) { - mCfg->groups[group].state = zeroExportState::GETINVERTERDATA; - mCfg->groups[group].stateNext = zeroExportState::GETINVERTERDATA; - } - - unsigned long eTsp = millis(); - mLog["B"] = bTsp; - mLog["E"] = eTsp; - mLog["D"] = eTsp - bTsp; - - mCfg->groups[group].lastRun = eTsp; - - return doLog; + if (wait) return false; + return true; } /** groupGetInverterData @@ -627,16 +682,12 @@ class ZeroExport { * @param group * @returns true/false */ - bool groupGetInverterData(uint8_t group) { - bool doLog = false; - unsigned long bTsp = millis(); - - mLog["t"] = "groupGetInverterData"; - mLog["g"] = group; + bool groupGetInverterData(uint8_t group, unsigned long *tsp, bool *doLog) { + if (mCfg->debug) mLog["t"] = "groupGetInverterData"; - mCfg->groups[group].stateLast = zeroExportState::GETINVERTERDATA; + mCfg->groups[group].lastRun = *tsp; - doLog = true; + *doLog = true; // Get Data JsonArray logArr = mLog.createNestedArray("ix"); @@ -664,23 +715,9 @@ class ZeroExport { float p = mIv[group][inv]->getChannelFieldValue(CH0, FLD_PAC, rec); // TODO: Save Hole Power für die Webanzeige - - // Get Limit - // uint16_t Limit = mIv[group][inv]->actPowerLimit; } - // Next - mCfg->groups[group].state = zeroExportState::BATTERYPROTECTION; - mCfg->groups[group].stateNext = zeroExportState::BATTERYPROTECTION; - - unsigned long eTsp = millis(); - mLog["B"] = bTsp; - mLog["E"] = eTsp; - mLog["D"] = eTsp - bTsp; - - mCfg->groups[group].lastRun = eTsp; - - return doLog; + return true; } /** groupBatteryprotection @@ -688,18 +725,12 @@ class ZeroExport { * @param group * @returns true/false */ - bool groupBatteryprotection(uint8_t group) { - bool doLog = false; - unsigned long bTsp = millis(); - - mLog["t"] = "groupBatteryprotection"; - mLog["g"] = group; + bool groupBatteryprotection(uint8_t group, unsigned long *tsp, bool *doLog) { + if (mCfg->debug) mLog["t"] = "groupBatteryprotection"; - mCfg->groups[group].stateLast = zeroExportState::BATTERYPROTECTION; - mCfg->groups[group].state = zeroExportState::GETPOWERMETER; - mCfg->groups[group].stateNext = zeroExportState::GETPOWERMETER; + mCfg->groups[group].lastRun = *tsp; - doLog = true; + *doLog = true; // Protection if (mCfg->groups[group].battEnabled) { @@ -709,21 +740,21 @@ class ZeroExport { if (mCfg->groups[group].battVoltageOn <= mCfg->groups[group].battVoltageOff) { mCfg->groups[group].battSwitch = false; mLog["err"] = "Config - battVoltageOn(" + (String)mCfg->groups[group].battVoltageOn + ") <= battVoltageOff(" + (String)mCfg->groups[group].battVoltageOff + ")"; - return doLog; + return false; } // Config - parameter check if (mCfg->groups[group].battVoltageOn <= (mCfg->groups[group].battVoltageOff + 1)) { mCfg->groups[group].battSwitch = false; mLog["err"] = "Config - battVoltageOn(" + (String)mCfg->groups[group].battVoltageOn + ") <= battVoltageOff(" + (String)mCfg->groups[group].battVoltageOff + " + 1V)"; - return doLog; + return false; } // Config - parameter check if (mCfg->groups[group].battVoltageOn <= 22) { mCfg->groups[group].battSwitch = false; mLog["err"] = "Config - battVoltageOn(" + (String)mCfg->groups[group].battVoltageOn + ") <= 22V)"; - return doLog; + return false; } int8_t id = 0; @@ -841,31 +872,21 @@ class ZeroExport { mLog["sw"] = mCfg->groups[group].battSwitch; - unsigned long eTsp = millis(); - mLog["B"] = bTsp; - mLog["E"] = eTsp; - mLog["D"] = eTsp - bTsp; - - mCfg->groups[group].lastRun = eTsp; - - return doLog; + return true; } /** groupGetPowermeter * Holt die Daten vom Powermeter * @param group * @returns true/false + * @todo Eventuell muss am Ende geprüft werden ob die Daten vom Powermeter plausibel sind. */ - bool groupGetPowermeter(uint8_t group) { - bool doLog = false; - unsigned long bTsp = millis(); - - mLog["t"] = "groupGetPowermeter"; - mLog["g"] = group; + bool groupGetPowermeter(uint8_t group, unsigned long *tsp, bool *doLog) { + if (mCfg->debug) mLog["t"] = "groupGetPowermeter"; - mCfg->groups[group].stateLast = zeroExportState::GETPOWERMETER; + mCfg->groups[group].lastRun = *tsp; - doLog = true; + *doLog = true; bool result = false; result = mPowermeter.getData(mLog, group); @@ -874,31 +895,10 @@ class ZeroExport { (mCfg->groups[group].pmPower == 0) && (mCfg->groups[group].pmPower == 0) && (mCfg->groups[group].pmPower == 0)) { - return doLog; - } - - // TODO: eventuell muss hier geprüft werden ob die Daten vom Powermeter plausibel sind. - - // Next -#if defined(ZEROEXPORT_DEV_POWERMETER) - mCfg->groups[group].state = zeroExportState::PUBLISH; - mCfg->groups[group].stateNext = zeroExportState::PUBLISH; - mCfg->groups[group].lastRefresh = millis(); -#else - if (result) { - mCfg->groups[group].state = zeroExportState::CONTROLLER; - mCfg->groups[group].stateNext = zeroExportState::CONTROLLER; + return false; } -#endif - - unsigned long eTsp = millis(); - mLog["B"] = bTsp; - mLog["E"] = eTsp; - mLog["D"] = eTsp - bTsp; - mCfg->groups[group].lastRun = eTsp; - - return doLog; + return true; } /** groupController @@ -906,16 +906,12 @@ class ZeroExport { * @param group * @returns true/false */ - bool groupController(uint8_t group) { - bool doLog = false; - unsigned long bTsp = millis(); - - mLog["t"] = "groupController"; - mLog["g"] = group; + bool groupController(uint8_t group, unsigned long *tsp, bool *doLog) { + if (mCfg->debug) mLog["t"] = "groupController"; - mCfg->groups[group].stateLast = zeroExportState::GETPOWERMETER; + mCfg->groups[group].lastRun = *tsp; - doLog = true; + *doLog = true; // Führungsgröße w in Watt float w = mCfg->groups[group].setPoint; @@ -949,19 +945,15 @@ class ZeroExport { (e1 < mCfg->groups[group].powerTolerance) && (e1 > -mCfg->groups[group].powerTolerance) && (e2 < mCfg->groups[group].powerTolerance) && (e2 > -mCfg->groups[group].powerTolerance) && (e3 < mCfg->groups[group].powerTolerance) && (e3 > -mCfg->groups[group].powerTolerance)) { - mCfg->groups[group].state = zeroExportState::WAITREFRESH; - mCfg->groups[group].stateNext = zeroExportState::WAITREFRESH; - mCfg->groups[group].lastRun = bTsp; mLog["tol"] = mCfg->groups[group].powerTolerance; - return doLog; + return false; } // Regler - // TODO: Regelparameter unter Advanced konfigurierbar? Aber erst wenn Regler komplett ingegriert. float Kp = mCfg->groups[group].Kp; float Ki = mCfg->groups[group].Ki; float Kd = mCfg->groups[group].Kd; - unsigned long Ta = bTsp - mCfg->groups[group].lastRefresh; + unsigned long Ta = *tsp - mCfg->groups[group].lastRefresh; mLog["Kp"] = Kp; mLog["Ki"] = Ki; mLog["Kd"] = Kd; @@ -1028,19 +1020,7 @@ class ZeroExport { mCfg->groups[group].grpPowerL2 = y2; mCfg->groups[group].grpPowerL3 = y3; - // Next - mCfg->groups[group].state = zeroExportState::PROGNOSE; - mCfg->groups[group].stateNext = zeroExportState::PROGNOSE; - - unsigned long eTsp = millis(); - mLog["B"] = bTsp; - mLog["E"] = eTsp; - mLog["D"] = eTsp - bTsp; - - mCfg->groups[group].lastRun = eTsp; - mCfg->groups[group].lastRefresh = eTsp; - - return doLog; + return true; } /** groupPrognose @@ -1048,29 +1028,14 @@ class ZeroExport { * @param group * @returns true/false */ - bool groupPrognose(uint8_t group) { - bool result = false; - unsigned long bTsp = millis(); + bool groupPrognose(uint8_t group, unsigned long *tsp, bool *doLog) { + if (mCfg->debug) mLog["t"] = "groupPrognose"; - mLog["t"] = "groupPrognose"; - mLog["g"] = group; + mCfg->groups[group].lastRun = *tsp; - mCfg->groups[group].stateLast = zeroExportState::PROGNOSE; + *doLog = true; - result = true; - - // Next - mCfg->groups[group].state = zeroExportState::AUFTEILEN; - mCfg->groups[group].stateNext = zeroExportState::AUFTEILEN; - - unsigned long eTsp = millis(); - mLog["B"] = bTsp; - mLog["E"] = eTsp; - mLog["D"] = eTsp - bTsp; - - mCfg->groups[group].lastRun = eTsp; - - return result; + return true; } /** groupAufteilen @@ -1078,22 +1043,29 @@ class ZeroExport { * @param group * @returns true/false */ - bool groupAufteilen(uint8_t group) { - bool result = false; - unsigned long bTsp = millis(); + bool groupAufteilen(uint8_t group, unsigned long *tsp, bool *doLog) { + if (mCfg->debug) mLog["t"] = "groupAufteilen"; - mLog["t"] = "groupAufteilen"; - mLog["g"] = group; + mCfg->groups[group].lastRun = *tsp; - mCfg->groups[group].stateLast = zeroExportState::AUFTEILEN; - - result = true; + *doLog = true; float y = mCfg->groups[group].grpPower; float y1 = mCfg->groups[group].grpPowerL1; float y2 = mCfg->groups[group].grpPowerL2; float y3 = mCfg->groups[group].grpPowerL3; + // TDOD: nochmal durchdenken ... es muss für Sum und L1-3 sein + // uint16_t groupPmax = 0; + // for (uint8_t inv = 0; inv < ZEROEXPORT_GROUP_MAX_INVERTERS; inv++) { + // groupPmax = mCfg->groups[group].inverters[inv].limit; + // } + // int16_t groupPavail = mCfg->groups[group].powerMax - groupPmax; + // + // if ( y > groupPavail ) { + // y = groupPavail; + // } + mLog["y"] = y; mLog["y1"] = y1; mLog["y2"] = y2; @@ -1125,6 +1097,7 @@ class ZeroExport { // if (!cfgGroupInv->state) { // continue; // } + record_t<> *rec; rec = mIv[group][inv]->getRecordStruct(RealTimeRunData_Debug); cfgGroupInv->power = mIv[group][inv]->getChannelFieldValue(CH0, FLD_PAC, rec); @@ -1135,6 +1108,7 @@ class ZeroExport { ivId_Pmin[cfgGroupInv->target] = inv; // Hier kein return oder continue sonst dauerreboot } + if ((uint16_t)cfgGroupInv->power > ivPmax[cfgGroupInv->target]) { grpTarget[cfgGroupInv->target] = true; ivPmax[cfgGroupInv->target] = (uint16_t)cfgGroupInv->power; @@ -1218,37 +1192,25 @@ class ZeroExport { } } - // Next - mCfg->groups[group].state = zeroExportState::SETLIMIT; - mCfg->groups[group].stateNext = zeroExportState::SETLIMIT; - - unsigned long eTsp = millis(); - mLog["B"] = bTsp; - mLog["E"] = eTsp; - mLog["D"] = eTsp - bTsp; - - mCfg->groups[group].lastRun = eTsp; - - return result; + return true; } - /** groupSetLimit + /** groupSetReboot * * @param group * @returns true/false */ - bool groupSetLimit(uint8_t group) { - bool doLog = false; - unsigned long bTsp = millis(); + bool groupSetReboot(uint8_t group, unsigned long *tsp, bool *doLog) { + bool result = true; - mLog["t"] = "groupSetLimit"; - mLog["g"] = group; + if (mCfg->debug) mLog["t"] = "groupSetReboot"; - mCfg->groups[group].stateLast = zeroExportState::SETLIMIT; + mCfg->groups[group].lastRun = *tsp; - // Set limit + JsonArray logArr = mLog.createNestedArray("ix"); for (uint8_t inv = 0; inv < ZEROEXPORT_GROUP_MAX_INVERTERS; inv++) { - mLog["i"] = inv; + JsonObject logObj = logArr.createNestedObject(); + logObj["i"] = inv; // Inverter not enabled -> ignore if (!mCfg->groups[group].inverters[inv].enabled) { @@ -1258,100 +1220,53 @@ class ZeroExport { if (mCfg->groups[group].inverters[inv].id <= 0) { continue; } - // Nothing todo - if (mCfg->groups[group].inverters[inv].limit == mCfg->groups[group].inverters[inv].limitNew) { + *doLog = true; + // Reset + if ((mCfg->groups[group].inverters[inv].doReboot) && (mCfg->groups[group].inverters[inv].waitRebootAck == 0)) { + result = false; + mCfg->groups[group].inverters[inv].doReboot = false; continue; } - // Abbruch weil Inverter nicht verfügbar - if (!mIv[group][inv]->isAvailable()) { + // Wait + if (mCfg->groups[group].inverters[inv].waitRebootAck > 0) { + result = false; continue; } - // Abbruch weil Inverter produziert nicht - if (!mIv[group][inv]->isProducing()) { + // Inverter nothing to do -> ignore + if (!mCfg->groups[group].inverters[inv].doReboot) { continue; } - // do - - // Set Power on/off - // TODO: testen - /* - if (mCfg->groups[group].inverters[inv].limitNew < mCfg->groups[group].inverters[inv].powerMin) { - if (mIv[group][inv]->isProducing()) { - mCfg->groups[group].inverters[inv].switch = false; - mCfg->groups[group].stateNext = zeroExportState::SETPOWER; - } - } else { - if (!mIv[group][inv]->isProducing()) { - mCfg->groups[group].inverters[inv].switch = true; - mCfg->groups[group].stateNext = zeroExportState::SETPOWER; - } - } - */ - - // Restriction LimitNew >= Pmin - if (mCfg->groups[group].inverters[inv].limitNew < mCfg->groups[group].inverters[inv].powerMin) { - mCfg->groups[group].inverters[inv].limitNew = mCfg->groups[group].inverters[inv].powerMin; - } - // Restriction LimitNew >= 2% - uint16_t power2proz = mIv[group][inv]->getMaxPower() / 100 * 2; - if (mCfg->groups[group].inverters[inv].limitNew < power2proz) { - mCfg->groups[group].inverters[inv].limitNew = power2proz; - } - - // Restriction LimitNew <= Pmax - if (mCfg->groups[group].inverters[inv].limitNew > mCfg->groups[group].inverters[inv].powerMax) { - mCfg->groups[group].inverters[inv].limitNew = mCfg->groups[group].inverters[inv].powerMax; - } - - // Reject limit if difference < 5 W - if ( - (mCfg->groups[group].inverters[inv].limitNew > (mCfg->groups[group].inverters[inv].limit + ZEROEXPORT_GROUP_WR_LIMIT_MIN_DIFF)) && - (mCfg->groups[group].inverters[inv].limitNew < (mCfg->groups[group].inverters[inv].limit - ZEROEXPORT_GROUP_WR_LIMIT_MIN_DIFF))) { - mLog["err"] = String("Diff < ") + String(ZEROEXPORT_GROUP_WR_LIMIT_MIN_DIFF) + String("W"); - return false; - } - - // Nothing todo - if (mCfg->groups[group].inverters[inv].limit == mCfg->groups[group].inverters[inv].limitNew) { + // Inverter not available -> ignore + if (!mIv[group][inv]->isAvailable()) { + result = false; + logObj["err"] = "is not Available"; continue; } - doLog = true; + result = false; + *doLog = true; - mCfg->groups[group].inverters[inv].limit = mCfg->groups[group].inverters[inv].limitNew; - mLog["zeL"] = (uint16_t)mCfg->groups[group].inverters[inv].limit; + logObj["act"] = String("reboot"); // wait for Ack - mCfg->groups[group].inverters[inv].waitLimitAck = 60; - mLog["wL"] = mCfg->groups[group].inverters[inv].waitLimitAck; + mCfg->groups[group].inverters[inv].waitRebootAck = 120; + logObj["wR"] = mCfg->groups[group].inverters[inv].waitRebootAck; // send Command DynamicJsonDocument doc(512); JsonObject obj = doc.to(); - obj["val"] = (uint16_t)mCfg->groups[group].inverters[inv].limit; obj["id"] = mCfg->groups[group].inverters[inv].id; obj["path"] = "ctrl"; - obj["cmd"] = "limit_nonpersistent_absolute"; + obj["cmd"] = "restart"; mApi->ctrlRequest(obj); - mLog["d"] = obj; + logObj["d"] = obj; } - // Next - mCfg->groups[group].state = zeroExportState::PUBLISH; - mCfg->groups[group].stateNext = zeroExportState::PUBLISH; - - unsigned long eTsp = millis(); - mLog["B"] = bTsp; - mLog["E"] = eTsp; - mLog["D"] = eTsp - bTsp; - - mCfg->groups[group].lastRun = eTsp; - - return doLog; + return result; } /** groupSetPower @@ -1359,18 +1274,17 @@ class ZeroExport { * @param group * @returns true/false */ - bool groupSetPower(uint8_t group) { - bool doLog = false; - unsigned long bTsp = millis(); + bool groupSetPower(uint8_t group, unsigned long *tsp, bool *doLog) { + bool result = true; - mLog["t"] = "groupSetPower"; - mLog["g"] = group; + if (mCfg->debug) mLog["t"] = "groupSetPower"; - mCfg->groups[group].stateLast = zeroExportState::SETPOWER; + mCfg->groups[group].lastRun = *tsp; - // Set power + JsonArray logArr = mLog.createNestedArray("ix"); for (uint8_t inv = 0; inv < ZEROEXPORT_GROUP_MAX_INVERTERS; inv++) { - mLog["i"] = inv; + JsonObject logObj = logArr.createNestedObject(); + logObj["i"] = inv; // Inverter not enabled -> ignore if (!mCfg->groups[group].inverters[inv].enabled) { @@ -1381,123 +1295,261 @@ class ZeroExport { continue; } - // do - doLog = true; + if (mCfg->debug) *doLog = true; - // Abbruch weil Inverter nicht verfügbar + // Inverter not available -> ignore if (!mIv[group][inv]->isAvailable()) { - mLog["err"] = "is not Available"; + logObj["a"] = false; + result = false; continue; } - // Nothing todo - if (mCfg->groups[group].battSwitch == mIv[group][inv]->isProducing()) { - mLog["err"] = "battSwitch " + String(mCfg->groups[group].battSwitch) + " == isProducing()" + String(mIv[group][inv]->isProducing()); + // Reset + if ((mCfg->groups[group].inverters[inv].doPower != -1) && (mCfg->groups[group].inverters[inv].waitPowerAck == 0)) { + result = false; + mCfg->groups[group].inverters[inv].doPower = -1; + logObj["act"] = "done"; + continue; + } + + // Calculate + logObj["battSw"] = mCfg->groups[group].battSwitch; + logObj["ivL"] = mCfg->groups[group].inverters[inv].limitNew; + logObj["ivSw"] = mIv[group][inv]->isProducing(); + if ( + (mCfg->groups[group].battSwitch == true) && + (mCfg->groups[group].inverters[inv].limitNew > mCfg->groups[group].inverters[inv].powerMin) && + (mIv[group][inv]->isProducing() == false)) { + // On + mCfg->groups[group].inverters[inv].doPower = true; + logObj["act"] = "on"; + } + if ( + ( + (mCfg->groups[group].battSwitch == false) || + (mCfg->groups[group].inverters[inv].limitNew < mCfg->groups[group].inverters[inv].powerMin)) && + (mIv[group][inv]->isProducing() == true)) { + // Off + mCfg->groups[group].inverters[inv].doPower = false; + logObj["act"] = "off"; + } + + // Wait + if (mCfg->groups[group].inverters[inv].waitPowerAck > 0) { + logObj["w"] = mCfg->groups[group].inverters[inv].waitPowerAck; + result = false; + continue; + } + + // Nothing to do + if (mCfg->groups[group].inverters[inv].doPower == -1) { + logObj["act"] = "nothing to do"; continue; } - mLog["act"] = String("switch to: ") + String(mCfg->groups[group].battSwitch); + result = false; + *doLog = true; + + if (!mCfg->debug) logObj["act"] = mCfg->groups[group].inverters[inv].doPower; // wait for Ack mCfg->groups[group].inverters[inv].waitPowerAck = 120; - mLog["wP"] = mCfg->groups[group].inverters[inv].waitPowerAck; + logObj["wP"] = mCfg->groups[group].inverters[inv].waitPowerAck; // send Command DynamicJsonDocument doc(512); JsonObject obj = doc.to(); - obj["val"] = mCfg->groups[group].battSwitch; + obj["val"] = mCfg->groups[group].inverters[inv].doPower; obj["id"] = mCfg->groups[group].inverters[inv].id; obj["path"] = "ctrl"; obj["cmd"] = "power"; mApi->ctrlRequest(obj); - mLog["d"] = obj; + logObj["d"] = obj; } - // Next - mCfg->groups[group].state = zeroExportState::PUBLISH; - mCfg->groups[group].stateNext = zeroExportState::PUBLISH; - mCfg->groups[group].lastRefresh = millis(); - - unsigned long eTsp = millis(); - mLog["B"] = bTsp; - mLog["E"] = eTsp; - mLog["D"] = eTsp - bTsp; - - mCfg->groups[group].lastRun = eTsp; - - return doLog; + return result; } - /** groupSetReboot + /** groupSetLimit * * @param group * @returns true/false */ - bool groupSetReboot(uint8_t group) { - bool doLog = false; - unsigned long bTsp = millis(); + bool groupSetLimit(uint8_t group, unsigned long *tsp, bool *doLog) { + if (mCfg->debug) mLog["t"] = "groupSetLimit"; - mLog["t"] = "groupSetReboot"; - mLog["g"] = group; + mCfg->groups[group].lastRun = *tsp; - mCfg->groups[group].stateLast = zeroExportState::SETREBOOT; - - // Set reboot + // Set limit + JsonArray logArr = mLog.createNestedArray("ix"); for (uint8_t inv = 0; inv < ZEROEXPORT_GROUP_MAX_INVERTERS; inv++) { - mLog["i"] = inv; + JsonObject logObj = logArr.createNestedObject(); + logObj["i"] = inv; // Inverter not enabled -> ignore if (!mCfg->groups[group].inverters[inv].enabled) { continue; } + // Inverter not selected -> ignore if (mCfg->groups[group].inverters[inv].id <= 0) { continue; } + // if isOff -> Limit Pmin + if (!mIv[group][inv]->isProducing()) { + mCfg->groups[group].inverters[inv].limitNew = mCfg->groups[group].inverters[inv].powerMin; + } + + // Nothing todo + if (mCfg->groups[group].inverters[inv].limit == mCfg->groups[group].inverters[inv].limitNew) { + continue; + } + // Abbruch weil Inverter nicht verfügbar - // if (!mIv[group][inv]->isAvailable()) { - // continue; - // } + if (!mIv[group][inv]->isAvailable()) { + continue; + } + + // Abbruch weil Inverter produziert nicht + /// if (!mIv[group][inv]->isProducing()) { + /// continue; + /// } // do - doLog = true; - mLog["act"] = String("reboot"); + // Restriction LimitNew >= Pmin + if (mCfg->groups[group].inverters[inv].limitNew < mCfg->groups[group].inverters[inv].powerMin) { + mCfg->groups[group].inverters[inv].limitNew = mCfg->groups[group].inverters[inv].powerMin; + } + // Restriction LimitNew >= 2% + uint16_t power2proz = mIv[group][inv]->getMaxPower() / 100 * 2; + if (mCfg->groups[group].inverters[inv].limitNew < power2proz) { + mCfg->groups[group].inverters[inv].limitNew = power2proz; + } + + // Restriction LimitNew <= Pmax + if (mCfg->groups[group].inverters[inv].limitNew > mCfg->groups[group].inverters[inv].powerMax) { + mCfg->groups[group].inverters[inv].limitNew = mCfg->groups[group].inverters[inv].powerMax; + } + + // Reject limit if difference < 5 W + if ( + (mCfg->groups[group].inverters[inv].limitNew > (mCfg->groups[group].inverters[inv].powerMin + ZEROEXPORT_GROUP_WR_LIMIT_MIN_DIFF)) && + (mCfg->groups[group].inverters[inv].limitNew > (mCfg->groups[group].inverters[inv].limit + ZEROEXPORT_GROUP_WR_LIMIT_MIN_DIFF)) && + (mCfg->groups[group].inverters[inv].limitNew < (mCfg->groups[group].inverters[inv].limit - ZEROEXPORT_GROUP_WR_LIMIT_MIN_DIFF))) { + mLog["err"] = String("Diff < ") + String(ZEROEXPORT_GROUP_WR_LIMIT_MIN_DIFF) + String("W"); + return false; + } + + // Nothing todo + if (mCfg->groups[group].inverters[inv].limit == mCfg->groups[group].inverters[inv].limitNew) { + continue; + } + + *doLog = true; + + mCfg->groups[group].inverters[inv].limit = mCfg->groups[group].inverters[inv].limitNew; + mLog["zeL"] = (uint16_t)mCfg->groups[group].inverters[inv].limit; // wait for Ack - mCfg->groups[group].inverters[inv].waitRebootAck = 120; - mLog["wR"] = mCfg->groups[group].inverters[inv].waitRebootAck; + mCfg->groups[group].inverters[inv].waitLimitAck = 60; + mLog["wL"] = mCfg->groups[group].inverters[inv].waitLimitAck; // send Command DynamicJsonDocument doc(512); JsonObject obj = doc.to(); + obj["val"] = (uint16_t)mCfg->groups[group].inverters[inv].limit; obj["id"] = mCfg->groups[group].inverters[inv].id; obj["path"] = "ctrl"; - obj["cmd"] = "restart"; + obj["cmd"] = "limit_nonpersistent_absolute"; mApi->ctrlRequest(obj); mLog["d"] = obj; } - // Next - mCfg->groups[group].state = zeroExportState::PUBLISH; - mCfg->groups[group].stateNext = zeroExportState::PUBLISH; - mCfg->groups[group].lastRefresh = millis(); + return true; + } + + /** groupPublish + * + */ + bool groupPublish(uint8_t group, unsigned long *tsp, bool *doLog) { + if (mCfg->debug) mLog["t"] = "groupPublish"; + + mCfg->groups[group].lastRun = *tsp; + + *doLog = true; - unsigned long eTsp = millis(); - mLog["B"] = bTsp; - mLog["E"] = eTsp; - mLog["D"] = eTsp - bTsp; + return true; - mCfg->groups[group].lastRun = eTsp; + if (mMqtt->isConnected()) { + DynamicJsonDocument doc(512); + JsonObject obj = doc.to(); - return doLog; + *doLog = true; + + // Init + if (!mIsSubscribed) { + mIsSubscribed = true; + mMqtt->publish("zero/set/enabled", ((mCfg->enabled) ? dict[STR_TRUE] : dict[STR_FALSE]), false); + mMqtt->subscribe("zero/set/enabled", QOS_2); + } + + mMqtt->publish("zero/state/enabled", ((mCfg->enabled) ? dict[STR_TRUE] : dict[STR_FALSE]), false); + + // if (mCfg->groups[group].publishPower) { + // mCfg->groups[group].publishPower = false; + obj["L1"] = mCfg->groups[group].pmPowerL1; + obj["L2"] = mCfg->groups[group].pmPowerL2; + obj["L3"] = mCfg->groups[group].pmPowerL3; + obj["Sum"] = mCfg->groups[group].pmPower; + mMqtt->publish("zero/state/powermeter/P", doc.as().c_str(), false); + doc.clear(); + // } + + // if (mCfg->groups[group].pm_Publish_W) { + // mCfg->groups[group].pm_Publish_W = false; + // obj["todo"] = "true"; + // obj["L1"] = mCfg->groups[group].pm_P1; + // obj["L2"] = mCfg->groups[group].pm_P2; + // obj["L2"] = mCfg->groups[group].pm_P3; + // obj["Sum"] = mCfg->groups[group].pm_P; + // mMqtt->publish("zero/powermeter/W", doc.as().c_str(), false); + // doc.clear(); + // } + + for (uint8_t inv = 0; inv < ZEROEXPORT_GROUP_MAX_INVERTERS; inv++) { + } + } + + return true; + } + + /** groupEmergency + * + * @param group + * @returns true/false + * @todo Hier ist noch keine Funktion + */ + bool groupEmergency(uint8_t group, unsigned long *tsp, bool *doLog) { + if (mCfg->debug) mLog["t"] = "groupEmergency"; + + mCfg->groups[group].lastRun = *tsp; + + *doLog = true; + + // if (!korrect) { + // do + // return; + // } + + return true; } /** sendLog - * Sendet das Log über Webserial und/oder MQTT + * Sendet den LogSpeicher über Webserial und/oder MQTT */ void sendLog(void) { if (mCfg->log_over_webserial) { @@ -1509,7 +1561,12 @@ class ZeroExport { mMqtt->publish("ze", mDocLog.as().c_str(), false); } } + } + /** clearLog + * Löscht den LogSpeicher + */ + void clearLog(void) { mDocLog.clear(); } diff --git a/src/web/RestApi.h b/src/web/RestApi.h index 0db1e3ee..045eb2d8 100644 --- a/src/web/RestApi.h +++ b/src/web/RestApi.h @@ -1158,7 +1158,6 @@ class RestApi { mConfig->plugin.zeroExport.groups[group].Ki = jsonIn[F("Ki")]; mConfig->plugin.zeroExport.groups[group].Kd = jsonIn[F("Kd")]; // Global - mConfig->plugin.zeroExport.groups[group].stateLast = zeroExportState::INIT; mConfig->plugin.zeroExport.groups[group].state = zeroExportState::INIT; mConfig->plugin.zeroExport.groups[group].stateNext = zeroExportState::INIT; mApp->saveSettings(false); // without reboot From 6ab3c6fb5ed596d6816b12133eac082155e2ca03 Mon Sep 17 00:00:00 2001 From: DanielR92 Date: Sat, 30 Mar 2024 10:54:51 +0100 Subject: [PATCH 04/22] first steps MQTT --- patches/RF24_Hal.patch | 95 ++++++++++++----------------- src/config/settings.h | 2 - src/plugins/zeroExport/powermeter.h | 16 ++--- src/plugins/zeroExport/zeroExport.h | 32 +++++++--- src/publisher/pubMqtt.h | 1 + 5 files changed, 70 insertions(+), 76 deletions(-) diff --git a/patches/RF24_Hal.patch b/patches/RF24_Hal.patch index 88a53bf9..ed2c5bec 100644 --- a/patches/RF24_Hal.patch +++ b/patches/RF24_Hal.patch @@ -1,5 +1,5 @@ diff --git a/RF24.cpp b/RF24.cpp -index 9e5b4a8..af00758 100644 +index 2e500b6..af00758 100644 --- a/RF24.cpp +++ b/RF24.cpp @@ -12,228 +12,24 @@ @@ -605,8 +605,7 @@ index 9e5b4a8..af00758 100644 /****************************************************************************/ -bool RF24::begin(_SPI* spiBus, rf24_gpio_pin_t _cepin, rf24_gpio_pin_t _cspin) -+bool RF24::begin(RF24_hal* _hal) - { +-{ - ce_pin = _cepin; - csn_pin = _cspin; - return begin(spiBus); @@ -617,7 +616,8 @@ index 9e5b4a8..af00758 100644 -/****************************************************************************/ - -bool RF24::begin(rf24_gpio_pin_t _cepin, rf24_gpio_pin_t _cspin) --{ ++bool RF24::begin(RF24_hal* _hal) + { - ce_pin = _cepin; - csn_pin = _cspin; + hal = _hal; @@ -670,17 +670,12 @@ index 9e5b4a8..af00758 100644 bool RF24::_init_pins() { if (!isValid()) { -@@ -1028,46 +527,7 @@ bool RF24::_init_pins() +@@ -1028,41 +527,7 @@ bool RF24::_init_pins() return false; } -#if defined(RF24_LINUX) - -- #if defined(MRAA) -- GPIO(); -- gpio.begin(ce_pin, csn_pin); -- #endif -- - pinMode(ce_pin, OUTPUT); - ce(LOW); - delay(100); @@ -718,7 +713,7 @@ index 9e5b4a8..af00758 100644 } /****************************************************************************/ -@@ -1151,7 +611,7 @@ bool RF24::isChipConnected() +@@ -1146,7 +611,7 @@ bool RF24::isChipConnected() bool RF24::isValid() { @@ -727,7 +722,7 @@ index 9e5b4a8..af00758 100644 } /****************************************************************************/ -@@ -1675,15 +1135,8 @@ void RF24::closeReadingPipe(uint8_t pipe) +@@ -1670,15 +1135,8 @@ void RF24::closeReadingPipe(uint8_t pipe) void RF24::toggle_features(void) { @@ -745,7 +740,7 @@ index 9e5b4a8..af00758 100644 } /****************************************************************************/ -@@ -1871,6 +1324,11 @@ uint8_t RF24::getARC(void) +@@ -1866,6 +1324,11 @@ uint8_t RF24::getARC(void) return read_register(OBSERVE_TX) & 0x0F; } @@ -758,7 +753,7 @@ index 9e5b4a8..af00758 100644 bool RF24::setDataRate(rf24_datarate_e speed) diff --git a/RF24.h b/RF24.h -index dbd32ae..74ae35d 100644 +index c029c8e..c9d612a 100644 --- a/RF24.h +++ b/RF24.h @@ -16,12 +16,7 @@ @@ -775,7 +770,7 @@ index dbd32ae..74ae35d 100644 /** * @defgroup PALevel Power Amplifier level -@@ -115,29 +110,8 @@ typedef enum +@@ -115,26 +110,8 @@ typedef enum class RF24 { private: @@ -784,18 +779,15 @@ index dbd32ae..74ae35d 100644 -#elif defined(SPI_UART) - SPIUARTClass uspi; -#endif -- ++ RF24_hal *hal; + -#if defined(RF24_LINUX) || defined(XMEGA_D3) /* XMEGA can use SPI class */ - SPI spi; -#endif // defined (RF24_LINUX) || defined (XMEGA_D3) -#if defined(RF24_SPI_PTR) - _SPI* _spi; -#endif // defined (RF24_SPI_PTR) --#if defined(MRAA) -- GPIO gpio; --#endif -+ RF24_hal *hal; - +- - rf24_gpio_pin_t ce_pin; /* "Chip Enable" pin, activates the RX or TX role */ - rf24_gpio_pin_t csn_pin; /* SPI Chip select */ - uint32_t spi_speed; /* SPI Bus Speed */ @@ -806,7 +798,7 @@ index dbd32ae..74ae35d 100644 uint8_t status; /* The status byte returned from every SPI transaction */ uint8_t payload_size; /* Fixed size of payloads */ uint8_t pipe0_reading_address[5]; /* Last address set on pipe 0 for reading. */ -@@ -146,16 +120,6 @@ private: +@@ -143,16 +120,6 @@ private: bool _is_p0_rx; /* For keeping track of pipe 0's usage in user-triggered RX mode. */ protected: @@ -823,7 +815,7 @@ index dbd32ae..74ae35d 100644 /** Whether ack payloads are enabled. */ bool ack_payloads_enabled; /** The address width to use (3, 4 or 5 bytes). */ -@@ -198,30 +162,15 @@ public: +@@ -195,30 +162,15 @@ public: * * See [Related Pages](pages.html) for device specific information * @@ -858,7 +850,7 @@ index dbd32ae..74ae35d 100644 #if defined(RF24_LINUX) virtual ~RF24() {}; -@@ -243,58 +192,16 @@ public: +@@ -240,58 +192,16 @@ public: */ bool begin(void); @@ -869,15 +861,16 @@ index dbd32ae..74ae35d 100644 - * @note This function assumes the `SPI::begin()` method was called before to - * calling this function. - * -- * @warning This function is for the Arduino platforms only -- * + * @warning This function is for the Arduino platforms only + * - * @param spiBus A pointer or reference to an instantiated SPI bus object. - * The `_SPI` datatype is a "wrapped" definition that will represent - * various SPI implementations based on the specified platform. - * @see Review the [Arduino support page](md_docs_arduino.html). -- * -- * @return same result as begin() -- */ ++ * @param _hal A pointer to the device specific hardware abstraction layer + * + * @return same result as begin() + */ - bool begin(_SPI* spiBus); - - /** @@ -887,8 +880,8 @@ index dbd32ae..74ae35d 100644 - * @note This function assumes the `SPI::begin()` method was called before to - * calling this function. - * - * @warning This function is for the Arduino platforms only - * +- * @warning This function is for the Arduino platforms only +- * - * @param spiBus A pointer or reference to an instantiated SPI bus object. - * The `_SPI` datatype is a "wrapped" definition that will represent - * various SPI implementations based on the specified platform. @@ -896,8 +889,7 @@ index dbd32ae..74ae35d 100644 - * @param _cspin The pin attached to Chip Select (often labeled CSN) on the radio module. - * - For the Arduino Due board, the [Arduino Due extended SPI feature](https://www.arduino.cc/en/Reference/DueExtendedSPI) - * is not supported. This means that the Due's pins 4, 10, or 52 are not mandated options (can use any digital output pin) for the radio's CSN pin. -+ * @param _hal A pointer to the device specific hardware abstraction layer - * +- * - * @see Review the [Arduino support page](md_docs_arduino.html). - * - * @return same result as begin() @@ -912,14 +904,14 @@ index dbd32ae..74ae35d 100644 - * @param _cspin The pin attached to Chip Select (often labeled CSN) on the radio module. - * - For the Arduino Due board, the [Arduino Due extended SPI feature](https://www.arduino.cc/en/Reference/DueExtendedSPI) - * is not supported. This means that the Due's pins 4, 10, or 52 are not mandated options (can use any digital output pin) for the radio's CSN pin. - * @return same result as begin() - */ +- * @return same result as begin() +- */ - bool begin(rf24_gpio_pin_t _cepin, rf24_gpio_pin_t _cspin); + bool begin(RF24_hal* _hal); /** * Checks if the chip is connected to the SPI bus -@@ -667,12 +574,12 @@ public: +@@ -664,12 +574,12 @@ public: * This function uses much less ram than other `*print*Details()` methods. * * @code @@ -934,17 +926,7 @@ index dbd32ae..74ae35d 100644 * cause undefined behavior. * * Registers names and/or data corresponding to the index of the `encoded_details` array: -@@ -704,9 +611,6 @@ public: - * | 35 | FIFO_STATUS | - * | 36 | DYNPD | - * | 37 | FEATURE | -- * | 38-39 | ce_pin | -- * | 40-41 | csn_pin | -- * | 42 | SPI speed (in MHz) or'd with (isPlusVariant << 4) | - */ - void encodeRadioDetails(uint8_t* encoded_status); - -@@ -1644,6 +1548,7 @@ public: +@@ -1641,6 +1551,7 @@ public: * @return Returns values from 0 to 15. */ uint8_t getARC(void); @@ -952,7 +934,7 @@ index dbd32ae..74ae35d 100644 /** * Set the transmission @ref Datarate -@@ -1896,18 +1801,6 @@ private: +@@ -1893,17 +1804,6 @@ private: */ bool _init_pins(); @@ -967,17 +949,16 @@ index dbd32ae..74ae35d 100644 - * @param mode HIGH to take this unit off the SPI bus, LOW to put it on - */ - void csn(bool mode); -- + /** * Set chip enable - * -diff --git a/RF24_hal.cpp b/RF24_hal.cpp -new file mode 100644 -index 0000000..3cc78e4 ---- /dev/null -+++ b/RF24_hal.cpp -@@ -0,0 +1 @@ -+#include "RF24_hal.h" +@@ -2412,4 +2312,4 @@ private: + * Use `ctrl+c` to quit at any time. + */ + +-#endif // __RF24_H__ +\ No newline at end of file ++#endif // __RF24_H__ diff --git a/RF24_hal.h b/RF24_hal.h new file mode 100644 index 0000000..baceab3 diff --git a/src/config/settings.h b/src/config/settings.h index 397bd627..d898c4b4 100644 --- a/src/config/settings.h +++ b/src/config/settings.h @@ -350,8 +350,6 @@ typedef struct { bool debug; zeroExportGroup_t groups[ZEROEXPORT_MAX_GROUPS]; - - // uint8_t query_device; // 0 - Tibber, 1 - Shelly, 2 - other (rs232?) // char monitor_url[ZEXPORT_ADDR_LEN]; // char json_path[ZEXPORT_ADDR_LEN]; diff --git a/src/plugins/zeroExport/powermeter.h b/src/plugins/zeroExport/powermeter.h index b9817053..792404eb 100644 --- a/src/plugins/zeroExport/powermeter.h +++ b/src/plugins/zeroExport/powermeter.h @@ -417,6 +417,8 @@ class powermeter { 07 01 00 02 08 02 ff #objName: OBIS-Kennzahl für Wirkenergie Einspeisung Tarif2 */ + unsigned long previousMillis = 0; + const std::list smlHandlerList{ {{0x01, 0x00, 0x10, 0x07, 0x00, 0xff}, &smlOBISW, &_powerMeterTotal}, // total - OBIS-Kennzahl für momentane Gesamtwirkleistung @@ -430,6 +432,8 @@ class powermeter { bool getPowermeterWattsTibber(JsonObject logObj, uint8_t group) { bool result = false; + if(millis() - previousMillis <= 3000) return false; // skip when it is to fast + mCfg->groups[group].pmPower = 0; mCfg->groups[group].pmPowerL1 = 0; mCfg->groups[group].pmPowerL2 = 0; @@ -449,7 +453,7 @@ class powermeter { http.begin(url); http.addHeader("Authorization", "Basic " + auth); - if (http.GET() == HTTP_CODE_OK && http.getSize() != 0) { + if (http.GET() == HTTP_CODE_OK && http.getSize() > 0) { String myString = http.getString(); char floatBuffer[20]; @@ -486,18 +490,9 @@ class powermeter { } } break; - - default: - logObj["SML_DEFAULT"] = String(smlCurrentState); - break; } } } - else - { - logObj["result"] = String(result); - logObj["http_size"] = String(http.getSize()); - } http.end(); @@ -506,6 +501,7 @@ class powermeter { logObj["P2"] = mCfg->groups[group].pmPowerL2; logObj["P3"] = mCfg->groups[group].pmPowerL3; + previousMillis = millis(); return result; } diff --git a/src/plugins/zeroExport/zeroExport.h b/src/plugins/zeroExport/zeroExport.h index 0ada3cbd..01343aef 100644 --- a/src/plugins/zeroExport/zeroExport.h +++ b/src/plugins/zeroExport/zeroExport.h @@ -299,17 +299,26 @@ class ZeroExport { * Subscribe section */ void onMqttMessage(JsonObject obj) { - if ((!mIsInitialized) || (!mCfg->enabled)) { - return; - } + if ((!mIsInitialized) || (!mCfg->enabled)) return; + + String topic = String(obj["topic"]); + if(!topic.indexOf("/zero/set/")) return; + + mLog["t"] = "onMqttMessage"; - // MQTT":{"val":0,"path":"zero","cmd":"set","id":0} - if (strcmp(obj["cmd"], "set") != 0 && strcmp(obj["path"], "zero") != 0) + if (obj["path"] == "zero" && obj["cmd"] == "set") { + // "topic":"inverter/zero/set/groups/0/enabled" + if (topic.indexOf("groups") != -1) { + String i = topic.substring(topic.length() - 10, topic.length() - 8); + uint id = i.toInt(); + } + mCfg->enabled = (bool)obj["val"]; + mLog["zero_enable"] = mCfg->enabled; } - mLog["MQTT"] = obj; + mLog["Msg"] = obj; sendLog(); } @@ -457,14 +466,22 @@ class ZeroExport { doLog = true; + String gr; + // Init if (!mIsSubscribed) { mIsSubscribed = true; mMqtt->publish("zero/set/enabled", ((mCfg->enabled) ? dict[STR_TRUE] : dict[STR_FALSE]), false); mMqtt->subscribe("zero/set/enabled", QOS_2); + + gr = "zero/set/groups/" + String(group) + "/enabled"; + mMqtt->publish(gr.c_str(), ((mCfg->groups[group].enabled) ? dict[STR_TRUE] : dict[STR_FALSE]) , false); + mMqtt->subscribe(gr.c_str(), QOS_2); } mMqtt->publish("zero/state/enabled", ((mCfg->enabled) ? dict[STR_TRUE] : dict[STR_FALSE]), false); + gr = "zero/state/groups/" + String(group) + "/enabled"; + mMqtt->publish(gr.c_str(), ((mCfg->groups[group].enabled) ? dict[STR_TRUE] : dict[STR_FALSE]) , false); // if (mCfg->groups[group].publishPower) { // mCfg->groups[group].publishPower = false; @@ -472,6 +489,7 @@ class ZeroExport { obj["L2"] = mCfg->groups[group].pmPowerL2; obj["L3"] = mCfg->groups[group].pmPowerL3; obj["Sum"] = mCfg->groups[group].pmPower; + mMqtt->publish("zero/state/powermeter/P", doc.as().c_str(), false); doc.clear(); // } @@ -481,7 +499,7 @@ class ZeroExport { // obj["todo"] = "true"; // obj["L1"] = mCfg->groups[group].pm_P1; // obj["L2"] = mCfg->groups[group].pm_P2; - // obj["L2"] = mCfg->groups[group].pm_P3; + // obj["L3"] = mCfg->groups[group].pm_P3; // obj["Sum"] = mCfg->groups[group].pm_P; // mMqtt->publish("zero/powermeter/W", doc.as().c_str(), false); // doc.clear(); diff --git a/src/publisher/pubMqtt.h b/src/publisher/pubMqtt.h index 2dbf67ec..2734ff19 100644 --- a/src/publisher/pubMqtt.h +++ b/src/publisher/pubMqtt.h @@ -311,6 +311,7 @@ class PubMqtt { DynamicJsonDocument json(128); JsonObject root = json.to(); + root["topic"] = String(topic); bool limitAbs = false; if(len > 0) { From 73b3542c161aba000cb416bb41e868ed2d696e4a Mon Sep 17 00:00:00 2001 From: DanielR92 Date: Sat, 30 Mar 2024 12:12:00 +0100 Subject: [PATCH 05/22] Add MQTT parts (publish groups) --- src/plugins/zeroExport/zeroExport.h | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/plugins/zeroExport/zeroExport.h b/src/plugins/zeroExport/zeroExport.h index 054d897b..1c8a8429 100644 --- a/src/plugins/zeroExport/zeroExport.h +++ b/src/plugins/zeroExport/zeroExport.h @@ -504,10 +504,14 @@ class ZeroExport { if (topic.indexOf("groups") != -1) { String i = topic.substring(topic.length() - 10, topic.length() - 8); uint id = i.toInt(); - } - mCfg->enabled = (bool)obj["val"]; - mLog["zero_enable"] = mCfg->enabled; + mCfg->groups[id].enabled = (bool)obj["val"]; + } + else + { + mCfg->enabled = (bool)obj["val"]; + mLog["zero_enable"] = mCfg->enabled; + } } mLog["Msg"] = obj; @@ -1499,15 +1503,23 @@ class ZeroExport { JsonObject obj = doc.to(); *doLog = true; + String gr; // Init if (!mIsSubscribed) { mIsSubscribed = true; mMqtt->publish("zero/set/enabled", ((mCfg->enabled) ? dict[STR_TRUE] : dict[STR_FALSE]), false); mMqtt->subscribe("zero/set/enabled", QOS_2); + + gr = "zero/set/groups/" + String(group) + "/enabled"; + mMqtt->publish(gr.c_str(), ((mCfg->groups[group].enabled) ? dict[STR_TRUE] : dict[STR_FALSE]) , false); + mMqtt->subscribe(gr.c_str(), QOS_2); } mMqtt->publish("zero/state/enabled", ((mCfg->enabled) ? dict[STR_TRUE] : dict[STR_FALSE]), false); + gr = "zero/state/groups/" + String(group) + "/enabled"; + mMqtt->publish(gr.c_str(), ((mCfg->groups[group].enabled) ? dict[STR_TRUE] : dict[STR_FALSE]) , false); + // if (mCfg->groups[group].publishPower) { // mCfg->groups[group].publishPower = false; From 3736bff9abe65d4e648fd7c80c53f24a6fd332a4 Mon Sep 17 00:00:00 2001 From: Patrick Amrhein Date: Sat, 30 Mar 2024 18:23:19 +0100 Subject: [PATCH 06/22] Optimize --- src/plugins/zeroExport/zeroExport.h | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/plugins/zeroExport/zeroExport.h b/src/plugins/zeroExport/zeroExport.h index a80e2e9b..c7dfacf0 100644 --- a/src/plugins/zeroExport/zeroExport.h +++ b/src/plugins/zeroExport/zeroExport.h @@ -401,6 +401,7 @@ class ZeroExport { mLog["D"] = eTsp - bTsp; } sendLog(); + clearLog(); return; } } @@ -435,6 +436,7 @@ class ZeroExport { mLog["D"] = eTsp - bTsp; } sendLog(); + clearLog(); return; } } @@ -449,9 +451,13 @@ class ZeroExport { return; } - // if (!iv->isAvailable()) { - // return; - // } + if (!iv->isAvailable()) { + return; + } + + if (!iv->isProducing()) { + return; + } if (iv->actPowerLimit == 65535) { return; @@ -466,7 +472,8 @@ class ZeroExport { mLog["g"] = group; mLog["i"] = inv; mLog["id"] = iv->id; - mLog["a"] = iv->isAvailable(); + // mLog["a"] = iv->isAvailable(); + // mLog["p"] = iv->isProducing(); mLog["ivL%"] = iv->actPowerLimit; mLog["ivPm"] = iv->getMaxPower(); uint16_t ivL = (iv->getMaxPower() * iv->actPowerLimit) / 100; @@ -481,6 +488,7 @@ class ZeroExport { mLog["D"] = eTsp - bTsp; } sendLog(); + clearLog(); return; } } @@ -502,6 +510,7 @@ class ZeroExport { mLog["MQTT"] = obj; sendLog(); + clearLog(); } private: @@ -1480,10 +1489,6 @@ class ZeroExport { mCfg->groups[group].lastRun = *tsp; - *doLog = true; - - return true; - if (mMqtt->isConnected()) { DynamicJsonDocument doc(512); JsonObject obj = doc.to(); @@ -1553,7 +1558,8 @@ class ZeroExport { */ void sendLog(void) { if (mCfg->log_over_webserial) { - DBGPRINTLN(String("ze: ") + mDocLog.as()); +// DBGPRINTLN(String("ze: ") + mDocLog.as()); + DPRINTLN(DBG_INFO, String("ze: ") + mDocLog.as()); } if (mCfg->log_over_mqtt) { From 692dd6628c87810428b4e1286dc52cdf6f16749c Mon Sep 17 00:00:00 2001 From: DanielR92 Date: Sat, 30 Mar 2024 18:25:00 +0100 Subject: [PATCH 07/22] little changes --- src/config/settings.h | 2 +- src/plugins/zeroExport/powermeter.h | 12 ++++-------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/config/settings.h b/src/config/settings.h index 1b70161b..7458aa1f 100644 --- a/src/config/settings.h +++ b/src/config/settings.h @@ -188,7 +188,7 @@ typedef struct { // Plugin ZeroExport #if defined(PLUGIN_ZEROEXPORT) -#define ZEROEXPORT_DEV_POWERMETER +//#define ZEROEXPORT_DEV_POWERMETER #define ZEROEXPORT_MAX_GROUPS 6 #define ZEROEXPORT_GROUP_MAX_LEN_NAME 25 #define ZEROEXPORT_GROUP_MAX_LEN_PM_URL 100 diff --git a/src/plugins/zeroExport/powermeter.h b/src/plugins/zeroExport/powermeter.h index 792404eb..6ca1280a 100644 --- a/src/plugins/zeroExport/powermeter.h +++ b/src/plugins/zeroExport/powermeter.h @@ -433,6 +433,7 @@ class powermeter { bool result = false; if(millis() - previousMillis <= 3000) return false; // skip when it is to fast + logObj["mod"] = "getPowermeterWattsTibber"; mCfg->groups[group].pmPower = 0; mCfg->groups[group].pmPowerL1 = 0; @@ -455,11 +456,9 @@ class powermeter { if (http.GET() == HTTP_CODE_OK && http.getSize() > 0) { String myString = http.getString(); - - char floatBuffer[20]; double readVal = 0; - unsigned char c; + for (int i = 0; i < http.getSize(); ++i) { c = myString[i]; sml_states_t smlCurrentState = smlState(c); @@ -472,11 +471,8 @@ class powermeter { mCfg->groups[group].pmPowerL2 = _powerMeter2Power; mCfg->groups[group].pmPowerL3 = _powerMeter3Power; - if(! (_powerMeter1Power && _powerMeter2Power && _powerMeter3Power)) - { - mCfg->groups[group].pmPowerL1 = _powerMeterTotal / 3; - mCfg->groups[group].pmPowerL2 = _powerMeterTotal / 3; - mCfg->groups[group].pmPowerL3 = _powerMeterTotal / 3; + if(! (_powerMeter1Power && _powerMeter2Power && _powerMeter3Power)) { + mCfg->groups[group].pmPowerL1 = mCfg->groups[group].pmPowerL2 = mCfg->groups[group].pmPowerL3 = _powerMeterTotal / 3; } result = true; From 9314e699431a84644bc633d81139b1c5dbdf68f7 Mon Sep 17 00:00:00 2001 From: DanielR92 Date: Sat, 30 Mar 2024 18:57:15 +0100 Subject: [PATCH 08/22] fix issue zeroexport PowerL123 --- src/plugins/zeroExport/zeroExport.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/zeroExport/zeroExport.h b/src/plugins/zeroExport/zeroExport.h index e94da80b..d2f99831 100644 --- a/src/plugins/zeroExport/zeroExport.h +++ b/src/plugins/zeroExport/zeroExport.h @@ -915,9 +915,9 @@ class ZeroExport { result = mPowermeter.getData(mLog, group); if ( (mCfg->groups[group].pmPower == 0) && - (mCfg->groups[group].pmPower == 0) && - (mCfg->groups[group].pmPower == 0) && - (mCfg->groups[group].pmPower == 0)) { + (mCfg->groups[group].pmPowerL1 == 0) && + (mCfg->groups[group].pmPowerL2 == 0) && + (mCfg->groups[group].pmPowerL3 == 0)) { return false; } From 0856a67d7d4785bfc704edc1df32bed7828af98e Mon Sep 17 00:00:00 2001 From: DanielR92 Date: Sat, 30 Mar 2024 22:22:29 +0100 Subject: [PATCH 09/22] fix selected inverter continue --- src/plugins/zeroExport/zeroExport.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/zeroExport/zeroExport.h b/src/plugins/zeroExport/zeroExport.h index d2f99831..5f83b270 100644 --- a/src/plugins/zeroExport/zeroExport.h +++ b/src/plugins/zeroExport/zeroExport.h @@ -562,7 +562,7 @@ class ZeroExport { } // Inverter not selected -> ignore - if (mCfg->groups[group].inverters[inv].id <= 0) { + if (mCfg->groups[group].inverters[inv].id < 0) { continue; } From 4554eea03a12f0ea0c78c4a680b58eead0fb8f46 Mon Sep 17 00:00:00 2001 From: DanielR92 Date: Sat, 30 Mar 2024 22:24:42 +0100 Subject: [PATCH 10/22] Update zeroExport.h - fix fix needed: dont continue inverter id 0 --- src/plugins/zeroExport/zeroExport.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/zeroExport/zeroExport.h b/src/plugins/zeroExport/zeroExport.h index c7dfacf0..052aa476 100644 --- a/src/plugins/zeroExport/zeroExport.h +++ b/src/plugins/zeroExport/zeroExport.h @@ -548,7 +548,7 @@ class ZeroExport { } // Inverter not selected -> ignore - if (mCfg->groups[group].inverters[inv].id <= 0) { + if (mCfg->groups[group].inverters[inv].id < 0) { continue; } @@ -1593,4 +1593,4 @@ class ZeroExport { #endif /*__ZEROEXPORT__*/ -#endif /* #if defined(PLUGIN_ZEROEXPORT) */ \ No newline at end of file +#endif /* #if defined(PLUGIN_ZEROEXPORT) */ From be0553c3c567dc89c17763e3f1f05fec85b91018 Mon Sep 17 00:00:00 2001 From: DanielR92 Date: Sun, 31 Mar 2024 09:37:08 +0200 Subject: [PATCH 11/22] create func NotEnabledOrNotSelected --- src/plugins/zeroExport/zeroExport.h | 102 ++++++++++------------------ 1 file changed, 34 insertions(+), 68 deletions(-) diff --git a/src/plugins/zeroExport/zeroExport.h b/src/plugins/zeroExport/zeroExport.h index 5f83b270..c2bacfc1 100644 --- a/src/plugins/zeroExport/zeroExport.h +++ b/src/plugins/zeroExport/zeroExport.h @@ -46,7 +46,7 @@ class ZeroExport { mApi = api; mMqtt = mqtt; - mIsInitialized = mPowermeter.setup(mCfg); + mIsInitialized = mPowermeter.setup(mCfg, &mLog); } /** loop @@ -79,6 +79,7 @@ class ZeroExport { mLog["g"] = group; mLog["s"] = (uint8_t)mCfg->groups[group].state; + mLog["s2"] = (uint8_t)mCfg->groups[group].stateNext; switch (mCfg->groups[group].state) { case zeroExportState::INIT: @@ -509,6 +510,7 @@ class ZeroExport { if (obj["path"] == "zero" && obj["cmd"] == "set") { // "topic":"inverter/zero/set/groups/0/enabled" + // @TODO: state machine init if (topic.indexOf("groups") != -1) { String i = topic.substring(topic.length() - 10, topic.length() - 8); uint id = i.toInt(); @@ -534,6 +536,14 @@ class ZeroExport { * @returns true/false * @todo getInverterById statt getInverterByPos, dann würde die Variable *iv und die Schleife nicht gebraucht. */ + + /* EnabledSelected + * Inverter not enabled -> ignore || Inverter not selected -> ignore + */ + bool NotEnabledOrNotSelected(uint8_t group, uint8_t inv) { + return !mCfg->groups[group].inverters[inv].enabled || mCfg->groups[group].inverters[inv].id < 0; + } + bool groupInit(uint8_t group, unsigned long *tsp, bool *doLog) { uint8_t result = false; @@ -550,6 +560,7 @@ class ZeroExport { // Search/Set ivPointer JsonArray logArr = mLog.createNestedArray("ix"); + for (uint8_t inv = 0; inv < ZEROEXPORT_GROUP_MAX_INVERTERS; inv++) { JsonObject logObj = logArr.createNestedObject(); logObj["i"] = inv; @@ -557,14 +568,7 @@ class ZeroExport { mIv[group][inv] = nullptr; // Inverter not enabled -> ignore - if (!mCfg->groups[group].inverters[inv].enabled) { - continue; - } - - // Inverter not selected -> ignore - if (mCfg->groups[group].inverters[inv].id < 0) { - continue; - } + if (NotEnabledOrNotSelected(group, inv)) continue; // Load Config Inverter<> *iv; @@ -572,14 +576,10 @@ class ZeroExport { iv = mSys->getInverterByPos(i); // Inverter not configured -> ignore - if (iv == NULL) { - continue; - } + if (iv == NULL) continue; // Inverter not matching -> ignore - if (iv->id != (uint8_t)mCfg->groups[group].inverters[inv].id) { - continue; - } + if (iv->id != (uint8_t)mCfg->groups[group].inverters[inv].id) continue; // Save Inverter logObj["pos"] = i; @@ -663,13 +663,8 @@ class ZeroExport { logObj["i"] = inv; // Inverter not enabled -> ignore - if (!mCfg->groups[group].inverters[inv].enabled) { - continue; - } - // Inverter not selected -> ignore - if (mCfg->groups[group].inverters[inv].id <= 0) { - continue; - } + if (NotEnabledOrNotSelected(group, inv)) continue; + // Inverter is not available -> wait if (!mIv[group][inv]->isAvailable()) { logObj["a"] = false; @@ -720,13 +715,7 @@ class ZeroExport { logObj["i"] = inv; // Inverter not enabled -> ignore - if (!mCfg->groups[group].inverters[inv].enabled) { - continue; - } - // Inverter not selected -> ignore - if (mCfg->groups[group].inverters[inv].id <= 0) { - continue; - } + if (NotEnabledOrNotSelected(group, inv)) continue; if (!mIv[group][inv]->isAvailable()) { continue; @@ -829,13 +818,7 @@ class ZeroExport { // zeroExportGroupInverter_t *cfgGroupInv = &mCfg->groups[group].inverters[inv]; // Inverter not enabled -> ignore - if (!mCfg->groups[group].inverters[inv].enabled) { - continue; - } - // Inverter not selected -> ignore - if (mCfg->groups[group].inverters[inv].id <= 0) { - continue; - } + if (NotEnabledOrNotSelected(group, inv)) continue; // Abbruch weil Inverter nicht verfügbar if (!mIv[group][inv]->isAvailable()) { @@ -863,13 +846,7 @@ class ZeroExport { // zeroExportGroupInverter_t *cfgGroupInv = &mCfg->groups[group].inverters[inv]; // Inverter not enabled -> ignore - if (!mCfg->groups[group].inverters[inv].enabled) { - continue; - } - // Inverter not selected -> ignore - if (mCfg->groups[group].inverters[inv].id <= 0) { - continue; - } + if (NotEnabledOrNotSelected(group, inv)) continue; // Abbruch weil Inverter nicht verfügbar if (!mIv[group][inv]->isAvailable()) { @@ -906,13 +883,15 @@ class ZeroExport { */ bool groupGetPowermeter(uint8_t group, unsigned long *tsp, bool *doLog) { if (mCfg->debug) mLog["t"] = "groupGetPowermeter"; - mCfg->groups[group].lastRun = *tsp; *doLog = true; - bool result = false; - result = mPowermeter.getData(mLog, group); + mCfg->groups[group].pmPower = mPowermeter.getDataAVG(group).P; + mCfg->groups[group].pmPowerL1 = mPowermeter.getDataAVG(group).P1; + mCfg->groups[group].pmPowerL2 = mPowermeter.getDataAVG(group).P2; + mCfg->groups[group].pmPowerL3 = mPowermeter.getDataAVG(group).P3; + if ( (mCfg->groups[group].pmPower == 0) && (mCfg->groups[group].pmPowerL1 == 0) && @@ -921,6 +900,11 @@ class ZeroExport { return false; } + mLog["P"] = mCfg->groups[group].pmPower; + mLog["P1"] = mCfg->groups[group].pmPowerL1; + mLog["P2"] = mCfg->groups[group].pmPowerL2; + mLog["P3"] = mCfg->groups[group].pmPowerL3; + return true; } @@ -1236,13 +1220,8 @@ class ZeroExport { logObj["i"] = inv; // Inverter not enabled -> ignore - if (!mCfg->groups[group].inverters[inv].enabled) { - continue; - } - // Inverter not selected -> ignore - if (mCfg->groups[group].inverters[inv].id <= 0) { - continue; - } + if (NotEnabledOrNotSelected(group, inv)) continue; + *doLog = true; // Reset if ((mCfg->groups[group].inverters[inv].doReboot) && (mCfg->groups[group].inverters[inv].waitRebootAck == 0)) { @@ -1310,13 +1289,7 @@ class ZeroExport { logObj["i"] = inv; // Inverter not enabled -> ignore - if (!mCfg->groups[group].inverters[inv].enabled) { - continue; - } - // Inverter not selected -> ignore - if (mCfg->groups[group].inverters[inv].id <= 0) { - continue; - } + if (NotEnabledOrNotSelected(group, inv)) continue; if (mCfg->debug) *doLog = true; @@ -1411,14 +1384,7 @@ class ZeroExport { logObj["i"] = inv; // Inverter not enabled -> ignore - if (!mCfg->groups[group].inverters[inv].enabled) { - continue; - } - - // Inverter not selected -> ignore - if (mCfg->groups[group].inverters[inv].id <= 0) { - continue; - } + if (NotEnabledOrNotSelected(group, inv)) continue; // if isOff -> Limit Pmin if (!mIv[group][inv]->isProducing()) { From d15a3dd2d0121cebc2792c7444cbfda31748336f Mon Sep 17 00:00:00 2001 From: Patrick Amrhein Date: Sun, 31 Mar 2024 09:40:33 +0200 Subject: [PATCH 12/22] Redesign --- src/config/settings.h | 3 +- src/plugins/zeroExport/zeroExport.h | 466 +++++++++------------------- src/web/RestApi.h | 1 - 3 files changed, 148 insertions(+), 322 deletions(-) diff --git a/src/config/settings.h b/src/config/settings.h index 0c350809..ce008d0c 100644 --- a/src/config/settings.h +++ b/src/config/settings.h @@ -203,7 +203,6 @@ typedef struct { enum class zeroExportState : uint8_t { INIT, - WAIT, WAITREFRESH, GETINVERTERACKS, GETINVERTERDATA, @@ -301,7 +300,7 @@ typedef struct { // zeroExportState state; - zeroExportState stateNext; +// zeroExportState stateNext; unsigned long lastRun; unsigned long lastRefresh; uint16_t sleep; diff --git a/src/plugins/zeroExport/zeroExport.h b/src/plugins/zeroExport/zeroExport.h index 052aa476..46a85960 100644 --- a/src/plugins/zeroExport/zeroExport.h +++ b/src/plugins/zeroExport/zeroExport.h @@ -57,9 +57,7 @@ class ZeroExport { * @todo emergency */ void loop(void) { - if ((!mIsInitialized) || (!mCfg->enabled)) { - return; - } + if ((!mIsInitialized) || (!mCfg->enabled)) return; mPowermeter.loop(); @@ -67,205 +65,126 @@ class ZeroExport { bool DoLog = false; for (uint8_t group = 0; group < ZEROEXPORT_MAX_GROUPS; group++) { - if (!mCfg->groups[group].enabled) { - continue; - } + zeroExportGroup_t *cfgGroup = &mCfg->groups[group]; + + if (!cfgGroup->enabled) continue; // sleep - if (Tsp <= (mCfg->groups[group].lastRun + mCfg->groups[group].sleep)) { - continue; - } - mCfg->groups[group].sleep = 0; + if (Tsp <= (cfgGroup->lastRun + cfgGroup->sleep)) continue; + cfgGroup->sleep = 0; mLog["g"] = group; - mLog["s"] = (uint8_t)mCfg->groups[group].state; + mLog["s"] = (uint8_t)cfgGroup->state; - switch (mCfg->groups[group].state) { + switch (cfgGroup->state) { case zeroExportState::INIT: if (groupInit(group, &Tsp, &DoLog)) { - mCfg->groups[group].lastRun = Tsp; - // OK -> Wait, WaitRefresh - mCfg->groups[group].state = zeroExportState::WAIT; - mCfg->groups[group].stateNext = zeroExportState::WAITREFRESH; + cfgGroup->state = zeroExportState::WAITREFRESH; + cfgGroup->sleep = 60000; } else { - // Error -> Wait, Init - mCfg->groups[group].state = zeroExportState::WAIT; - mCfg->groups[group].stateNext = zeroExportState::INIT; - } - break; - case zeroExportState::WAIT: - if (groupWait(group, &Tsp, &DoLog)) { - mCfg->groups[group].lastRun = Tsp; - // OK - if (mCfg->groups[group].state != mCfg->groups[group].stateNext) { - // OK -> Next - mCfg->groups[group].state = mCfg->groups[group].stateNext; - } else { - // OK -> Init - mCfg->groups[group].state = zeroExportState::INIT; - mCfg->groups[group].stateNext = zeroExportState::INIT; - } + cfgGroup->state = zeroExportState::INIT; + cfgGroup->sleep = 60000; +#if defined(ZEROEXPORT_DEV_POWERMETER) + cfgGroup->state = zeroExportState::WAITREFRESH; + cfgGroup->sleep = 10000; +#endif } break; case zeroExportState::WAITREFRESH: if (groupWaitRefresh(group, &Tsp, &DoLog)) { - mCfg->groups[group].lastRun = Tsp; - // OK + cfgGroup->state = zeroExportState::GETINVERTERACKS; #if defined(ZEROEXPORT_DEV_POWERMETER) - mCfg->groups[group].state = zeroExportState::GETPOWERMETER; - mCfg->groups[group].stateNext = zeroExportState::GETPOWERMETER; -#else - mCfg->groups[group].state = zeroExportState::GETINVERTERACKS; - mCfg->groups[group].stateNext = zeroExportState::GETINVERTERACKS; + cfgGroup->state = zeroExportState::GETPOWERMETER; #endif } break; case zeroExportState::GETINVERTERACKS: if (groupGetInverterAcks(group, &Tsp, &DoLog)) { - mCfg->groups[group].lastRun = Tsp; - // OK - mCfg->groups[group].state = zeroExportState::GETINVERTERDATA; - mCfg->groups[group].stateNext = zeroExportState::GETINVERTERDATA; + cfgGroup->state = zeroExportState::GETINVERTERDATA; } else { - // Error - Sleep - mCfg->groups[group].lastRun = Tsp; - mCfg->groups[group].sleep = 1000; + cfgGroup->sleep = 1000; } break; case zeroExportState::GETINVERTERDATA: if (groupGetInverterData(group, &Tsp, &DoLog)) { - mCfg->groups[group].lastRun = Tsp; - // OK - mCfg->groups[group].state = zeroExportState::BATTERYPROTECTION; - mCfg->groups[group].stateNext = zeroExportState::BATTERYPROTECTION; + cfgGroup->state = zeroExportState::BATTERYPROTECTION; } else { - // Error - Sleep - mCfg->groups[group].lastRun = Tsp; - mCfg->groups[group].sleep = 500; + cfgGroup->sleep = 500; } break; case zeroExportState::BATTERYPROTECTION: if (groupBatteryprotection(group, &Tsp, &DoLog)) { - mCfg->groups[group].lastRun = Tsp; - // OK - mCfg->groups[group].state = zeroExportState::GETPOWERMETER; - mCfg->groups[group].stateNext = zeroExportState::GETPOWERMETER; + cfgGroup->state = zeroExportState::GETPOWERMETER; } else { - // Error - Sleep - mCfg->groups[group].lastRun = Tsp; - mCfg->groups[group].sleep = 1000; + cfgGroup->sleep = 1000; } break; case zeroExportState::GETPOWERMETER: if (groupGetPowermeter(group, &Tsp, &DoLog)) { - mCfg->groups[group].lastRun = Tsp; - // OK + cfgGroup->state = zeroExportState::CONTROLLER; #if defined(ZEROEXPORT_DEV_POWERMETER) - mCfg->groups[group].lastRefresh = millis(); - mCfg->groups[group].state = zeroExportState::PUBLISH; - mCfg->groups[group].stateNext = zeroExportState::PUBLISH; -#else - mCfg->groups[group].state = zeroExportState::CONTROLLER; - mCfg->groups[group].stateNext = zeroExportState::CONTROLLER; + cfgGroup->lastRefresh = millis(); + cfgGroup->state = zeroExportState::PUBLISH; #endif } else { - // Error - Sleep - mCfg->groups[group].lastRun = Tsp; - mCfg->groups[group].sleep = 3000; + cfgGroup->sleep = 3000; } break; case zeroExportState::CONTROLLER: if (groupController(group, &Tsp, &DoLog)) { - mCfg->groups[group].lastRun = Tsp; - mCfg->groups[group].lastRefresh = Tsp; - // OK - mCfg->groups[group].state = zeroExportState::PROGNOSE; - mCfg->groups[group].stateNext = zeroExportState::PROGNOSE; + cfgGroup->lastRefresh = Tsp; + cfgGroup->state = zeroExportState::PROGNOSE; } else { - // Error - WaitRefresh - mCfg->groups[group].state = zeroExportState::WAITREFRESH; - mCfg->groups[group].stateNext = zeroExportState::WAITREFRESH; + cfgGroup->state = zeroExportState::WAITREFRESH; } break; case zeroExportState::PROGNOSE: if (groupPrognose(group, &Tsp, &DoLog)) { - mCfg->groups[group].lastRun = Tsp; - // OK - mCfg->groups[group].state = zeroExportState::AUFTEILEN; - mCfg->groups[group].stateNext = zeroExportState::AUFTEILEN; + cfgGroup->state = zeroExportState::AUFTEILEN; } else { - // Error - Sleep - mCfg->groups[group].lastRun = Tsp; - mCfg->groups[group].sleep = 500; + cfgGroup->sleep = 500; } break; case zeroExportState::AUFTEILEN: if (groupAufteilen(group, &Tsp, &DoLog)) { - mCfg->groups[group].lastRun = Tsp; - // OK - mCfg->groups[group].state = zeroExportState::SETREBOOT; - mCfg->groups[group].stateNext = zeroExportState::SETREBOOT; + cfgGroup->state = zeroExportState::SETREBOOT; } else { - // Error - Sleep - mCfg->groups[group].lastRun = Tsp; - mCfg->groups[group].sleep = 500; + cfgGroup->sleep = 500; } break; case zeroExportState::SETREBOOT: if (groupSetReboot(group, &Tsp, &DoLog)) { - mCfg->groups[group].lastRun = Tsp; - mCfg->groups[group].lastRefresh = Tsp; - // OK - mCfg->groups[group].state = zeroExportState::SETPOWER; - mCfg->groups[group].stateNext = zeroExportState::SETPOWER; + cfgGroup->state = zeroExportState::SETPOWER; } else { - // Error - Sleep - mCfg->groups[group].lastRun = Tsp; - mCfg->groups[group].sleep = 1000; + cfgGroup->sleep = 1000; } break; case zeroExportState::SETPOWER: if (groupSetPower(group, &Tsp, &DoLog)) { - mCfg->groups[group].lastRun = Tsp; - mCfg->groups[group].lastRefresh = Tsp; - // OK - mCfg->groups[group].state = zeroExportState::SETLIMIT; - mCfg->groups[group].stateNext = zeroExportState::SETLIMIT; + cfgGroup->lastRefresh = Tsp; + cfgGroup->state = zeroExportState::SETLIMIT; } else { - // Error - Sleep - mCfg->groups[group].lastRun = Tsp; - mCfg->groups[group].sleep = 1000; + cfgGroup->sleep = 1000; } break; case zeroExportState::SETLIMIT: if (groupSetLimit(group, &Tsp, &DoLog)) { - mCfg->groups[group].lastRun = Tsp; - // OK - mCfg->groups[group].state = zeroExportState::PUBLISH; - mCfg->groups[group].stateNext = zeroExportState::PUBLISH; + cfgGroup->state = zeroExportState::PUBLISH; } else { - // Error - Sleep - mCfg->groups[group].lastRun = Tsp; - mCfg->groups[group].sleep = 1000; + cfgGroup->sleep = 1000; } break; case zeroExportState::PUBLISH: if (groupPublish(group, &Tsp, &DoLog)) { - mCfg->groups[group].lastRun = Tsp; - // OK - mCfg->groups[group].state = zeroExportState::WAITREFRESH; - mCfg->groups[group].stateNext = zeroExportState::WAITREFRESH; + cfgGroup->state = zeroExportState::WAITREFRESH; //} else { // TODO: fehlt } break; case zeroExportState::EMERGENCY: - if (groupEmergency(group, &Tsp, &DoLog)) { - mCfg->groups[group].lastRun = Tsp; - mCfg->groups[group].lastRefresh = Tsp; - // OK - mCfg->groups[group].state = zeroExportState::INIT; - mCfg->groups[group].stateNext = zeroExportState::INIT; + if (groupEmergency(cfgGroup, &Tsp, &DoLog)) { + cfgGroup->lastRefresh = Tsp; + cfgGroup->state = zeroExportState::INIT; //} else { // TODO: fehlt } @@ -273,11 +192,10 @@ class ZeroExport { case zeroExportState::FINISH: case zeroExportState::ERROR: default: - mCfg->groups[group].lastRun = Tsp; - mCfg->groups[group].lastRefresh = Tsp; - mCfg->groups[group].sleep = 1000; - mCfg->groups[group].state = zeroExportState::INIT; - mCfg->groups[group].stateNext = zeroExportState::INIT; + cfgGroup->lastRun = Tsp; + cfgGroup->lastRefresh = Tsp; + cfgGroup->sleep = 1000; + cfgGroup->state = zeroExportState::INIT; break; } @@ -296,12 +214,12 @@ class ZeroExport { } /** tickSecond - * Zeitimpuls + * Time pulse every second + * @param void + * @returns void */ void tickSecond() { - if ((!mIsInitialized) || (!mCfg->enabled)) { - return; - } + if ((!mIsInitialized) || (!mCfg->enabled)) return; // Wait for ACK for (uint8_t group = 0; group < ZEROEXPORT_MAX_GROUPS; group++) { @@ -323,30 +241,28 @@ class ZeroExport { } /** tickerMidnight - * Zeitimpuls + * Time pulse Midnicht + * @param void + * @returns void */ void tickMidnight(void) { - if ((!mIsInitialized) || (!mCfg->enabled)) { - return; - } + if ((!mIsInitialized) || (!mCfg->enabled)) return; - // Reboot + // Select all Inverter to reboot for (uint8_t group = 0; group < ZEROEXPORT_MAX_GROUPS; group++) { for (uint8_t inv = 0; inv < ZEROEXPORT_GROUP_MAX_INVERTERS; inv++) { - // Reboot mCfg->groups[group].inverters[inv].doReboot = true; } } } /** resetWaitLimitAck - * - * @param + * Reset waiting time limit + * @param iv + * @returns void */ void resetWaitLimitAck(Inverter<> *iv) { - if ((!mIsInitialized) || (!mCfg->enabled)) { - return; - } + if ((!mIsInitialized) || (!mCfg->enabled)) return; for (uint8_t group = 0; group < ZEROEXPORT_MAX_GROUPS; group++) { for (uint8_t inv = 0; inv < ZEROEXPORT_GROUP_MAX_INVERTERS; inv++) { @@ -367,20 +283,20 @@ class ZeroExport { mLog["D"] = eTsp - bTsp; } sendLog(); + clearLog(); return; } } } } - /** setPowerAck - * - * @param + /** resetPowerAck + * Reset waiting time power + * @param iv + * @returns void */ void resetWaitPowerAck(Inverter<> *iv) { - if ((!mIsInitialized) || (!mCfg->enabled)) { - return; - } + if ((!mIsInitialized) || (!mCfg->enabled)) return; for (uint8_t group = 0; group < ZEROEXPORT_MAX_GROUPS; group++) { for (uint8_t inv = 0; inv < ZEROEXPORT_GROUP_MAX_INVERTERS; inv++) { @@ -409,13 +325,12 @@ class ZeroExport { } /** resetWaitRebootAck - * - * @param + * Reset waiting time reboot + * @param iv + * @returns void */ void resetWaitRebootAck(Inverter<> *iv) { - if ((!mIsInitialized) || (!mCfg->enabled)) { - return; - } + if ((!mIsInitialized) || (!mCfg->enabled)) return; for (uint8_t group = 0; group < ZEROEXPORT_MAX_GROUPS; group++) { for (uint8_t inv = 0; inv < ZEROEXPORT_GROUP_MAX_INVERTERS; inv++) { @@ -445,52 +360,46 @@ class ZeroExport { /** newDataAvailable * + * @param iv + * @returns void */ void newDataAvailable(Inverter<> *iv) { - if ((!mIsInitialized) || (!mCfg->enabled)) { - return; - } + if ((!mIsInitialized) || (!mCfg->enabled)) return; - if (!iv->isAvailable()) { - return; - } + if (!iv->isAvailable()) return; - if (!iv->isProducing()) { - return; - } + if (!iv->isProducing()) return; - if (iv->actPowerLimit == 65535) { - return; - } + if (iv->actPowerLimit == 65535) return; for (uint8_t group = 0; group < ZEROEXPORT_MAX_GROUPS; group++) { for (uint8_t inv = 0; inv < ZEROEXPORT_GROUP_MAX_INVERTERS; inv++) { - if (iv->id == mCfg->groups[group].inverters[inv].id) { - unsigned long bTsp = millis(); - - mLog["t"] = "newDataAvailable"; - mLog["g"] = group; - mLog["i"] = inv; - mLog["id"] = iv->id; - // mLog["a"] = iv->isAvailable(); - // mLog["p"] = iv->isProducing(); - mLog["ivL%"] = iv->actPowerLimit; - mLog["ivPm"] = iv->getMaxPower(); - uint16_t ivL = (iv->getMaxPower() * iv->actPowerLimit) / 100; - mLog["ivL"] = ivL; - mLog["zeL"] = (uint16_t)mCfg->groups[group].inverters[inv].limit; - mCfg->groups[group].inverters[inv].limit = ivL; - - if (mCfg->debug) { - unsigned long eTsp = millis(); - mLog["B"] = bTsp; - mLog["E"] = eTsp; - mLog["D"] = eTsp - bTsp; - } - sendLog(); - clearLog(); - return; + if (!mCfg->groups[group].inverters[inv].enabled) continue; + + if (iv->id != mCfg->groups[group].inverters[inv].id) continue; + + unsigned long bTsp = millis(); + + mLog["t"] = "newDataAvailable"; + mLog["g"] = group; + mLog["i"] = inv; + mLog["id"] = iv->id; + mLog["ivL%"] = iv->actPowerLimit; + mLog["ivPm"] = iv->getMaxPower(); + uint16_t ivL = (iv->getMaxPower() * iv->actPowerLimit) / 100; + mLog["ivL"] = ivL; + mLog["zeL"] = (uint16_t)mCfg->groups[group].inverters[inv].limit; + mCfg->groups[group].inverters[inv].limit = ivL; + + if (mCfg->debug) { + unsigned long eTsp = millis(); + mLog["B"] = bTsp; + mLog["E"] = eTsp; + mLog["D"] = eTsp - bTsp; } + sendLog(); + clearLog(); + return; } } } @@ -499,9 +408,8 @@ class ZeroExport { * Subscribe section */ void onMqttMessage(JsonObject obj) { - if ((!mIsInitialized) || (!mCfg->enabled)) { - return; - } + if ((!mIsInitialized) || (!mCfg->enabled)) return; + return; // MQTT":{"val":0,"path":"zero","cmd":"set","id":0} if (strcmp(obj["cmd"], "set") != 0 && strcmp(obj["path"], "zero") != 0) { @@ -515,7 +423,7 @@ class ZeroExport { private: /** groupInit - * initialisiert die Gruppe und sucht die ivPointer + * Initialize the group and search the InverterPointer * @param group * @returns true/false * @todo getInverterById statt getInverterByPos, dann würde die Variable *iv und die Schleife nicht gebraucht. @@ -543,14 +451,10 @@ class ZeroExport { mIv[group][inv] = nullptr; // Inverter not enabled -> ignore - if (!mCfg->groups[group].inverters[inv].enabled) { - continue; - } + if (!mCfg->groups[group].inverters[inv].enabled) continue; // Inverter not selected -> ignore - if (mCfg->groups[group].inverters[inv].id < 0) { - continue; - } + if (mCfg->groups[group].inverters[inv].id < 0) continue; // Load Config Inverter<> *iv; @@ -558,14 +462,10 @@ class ZeroExport { iv = mSys->getInverterByPos(i); // Inverter not configured -> ignore - if (iv == NULL) { - continue; - } + if (iv == NULL) continue; // Inverter not matching -> ignore - if (iv->id != (uint8_t)mCfg->groups[group].inverters[inv].id) { - continue; - } + if (iv->id != (uint8_t)mCfg->groups[group].inverters[inv].id) continue; // Save Inverter logObj["pos"] = i; @@ -588,28 +488,8 @@ class ZeroExport { return result; } - /** groupWait - * pausiert die Gruppe bis die Wartezeit 60s seit dem lastRun abgelaufen ist. - * @param group - * @returns true/false - */ - bool groupWait(uint8_t group, unsigned long *tsp, bool *doLog) { - if (mCfg->debug) mLog["t"] = "groupWait"; - - // Wait 60s - if (*tsp <= (mCfg->groups[group].lastRun + 60000UL)) { - return false; - } - - mCfg->groups[group].lastRun = *tsp; - - *doLog = true; - - return true; - } - /** groupWaitRefresh - * pausiert die Gruppe bis die Wartezeit refresh seit dem lastRefresh abgelaufen ist. + * Pauses the group until the wait time since the lastRefresh has expired. * @param group * @returns true/false */ @@ -617,9 +497,7 @@ class ZeroExport { if (mCfg->debug) mLog["t"] = "groupWaitRefresh"; // Wait Refreshtime - if (*tsp <= (mCfg->groups[group].lastRefresh + (mCfg->groups[group].refresh * 1000UL))) { - return false; - } + if (*tsp <= (mCfg->groups[group].lastRefresh + (mCfg->groups[group].refresh * 1000UL))) return false; mCfg->groups[group].lastRun = *tsp; @@ -649,13 +527,11 @@ class ZeroExport { logObj["i"] = inv; // Inverter not enabled -> ignore - if (!mCfg->groups[group].inverters[inv].enabled) { - continue; - } + if (!mCfg->groups[group].inverters[inv].enabled) continue; + // Inverter not selected -> ignore - if (mCfg->groups[group].inverters[inv].id <= 0) { - continue; - } + if (mCfg->groups[group].inverters[inv].id < 0) continue; + // Inverter is not available -> wait if (!mIv[group][inv]->isAvailable()) { logObj["a"] = false; @@ -666,18 +542,6 @@ class ZeroExport { logObj["wL"] = mCfg->groups[group].inverters[inv].waitLimitAck; wait = true; } - // TODO: Remove -> is moved to groupSetPower - // // waitPowerAck - // if (mCfg->groups[group].inverters[inv].waitPowerAck > 0) { - // logObj["wP"] = mCfg->groups[group].inverters[inv].waitPowerAck; - // wait = true; - // } - // TODO: Remove -> is moved to groupSetReboot - // // waitRebootAck - // if (mCfg->groups[group].inverters[inv].waitRebootAck > 0) { - // logObj["wR"] = mCfg->groups[group].inverters[inv].waitRebootAck; - // wait = true; - // } } mLog["w"] = wait; @@ -706,17 +570,12 @@ class ZeroExport { logObj["i"] = inv; // Inverter not enabled -> ignore - if (!mCfg->groups[group].inverters[inv].enabled) { - continue; - } + if (!mCfg->groups[group].inverters[inv].enabled) continue; + // Inverter not selected -> ignore - if (mCfg->groups[group].inverters[inv].id <= 0) { - continue; - } + if (mCfg->groups[group].inverters[inv].id < 0) continue; - if (!mIv[group][inv]->isAvailable()) { - continue; - } + if (!mIv[group][inv]->isAvailable()) continue; // Get Pac record_t<> *rec; @@ -773,17 +632,12 @@ class ZeroExport { zeroExportGroupInverter_t *cfgGroupInv = &mCfg->groups[group].inverters[inv]; // Ignore disabled Inverter - if (!cfgGroupInv->enabled) { - continue; - } - if (cfgGroupInv->id <= 0) { - continue; - } + if (!cfgGroupInv->enabled) continue; + + if (cfgGroupInv->id < 0) continue; if (!mIv[group][inv]->isAvailable()) { - if (U > 0) { - continue; - } + if (U > 0) continue; U = 0; id = cfgGroupInv->id; continue; @@ -815,13 +669,10 @@ class ZeroExport { // zeroExportGroupInverter_t *cfgGroupInv = &mCfg->groups[group].inverters[inv]; // Inverter not enabled -> ignore - if (!mCfg->groups[group].inverters[inv].enabled) { - continue; - } + if (!mCfg->groups[group].inverters[inv].enabled) continue; + // Inverter not selected -> ignore - if (mCfg->groups[group].inverters[inv].id <= 0) { - continue; - } + if (mCfg->groups[group].inverters[inv].id < 0) continue; // Abbruch weil Inverter nicht verfügbar if (!mIv[group][inv]->isAvailable()) { @@ -836,7 +687,6 @@ class ZeroExport { } mCfg->groups[group].state = zeroExportState::SETPOWER; - mCfg->groups[group].stateNext = zeroExportState::SETPOWER; } } @@ -849,13 +699,10 @@ class ZeroExport { // zeroExportGroupInverter_t *cfgGroupInv = &mCfg->groups[group].inverters[inv]; // Inverter not enabled -> ignore - if (!mCfg->groups[group].inverters[inv].enabled) { - continue; - } + if (!mCfg->groups[group].inverters[inv].enabled) continue; + // Inverter not selected -> ignore - if (mCfg->groups[group].inverters[inv].id <= 0) { - continue; - } + if (mCfg->groups[group].inverters[inv].id < 0) continue; // Abbruch weil Inverter nicht verfügbar if (!mIv[group][inv]->isAvailable()) { @@ -870,7 +717,6 @@ class ZeroExport { } mCfg->groups[group].state = zeroExportState::SETPOWER; - mCfg->groups[group].stateNext = zeroExportState::SETPOWER; } } } else { @@ -901,9 +747,9 @@ class ZeroExport { result = mPowermeter.getData(mLog, group); if ( (mCfg->groups[group].pmPower == 0) && - (mCfg->groups[group].pmPower == 0) && - (mCfg->groups[group].pmPower == 0) && - (mCfg->groups[group].pmPower == 0)) { + (mCfg->groups[group].pmPowerL1 == 0) && + (mCfg->groups[group].pmPowerL2 == 0) && + (mCfg->groups[group].pmPowerL3 == 0)) { return false; } @@ -924,7 +770,6 @@ class ZeroExport { // Führungsgröße w in Watt float w = mCfg->groups[group].setPoint; - mLog["w"] = w; // Regelgröße x in Watt @@ -932,7 +777,6 @@ class ZeroExport { float x1 = mCfg->groups[group].pmPowerL1; float x2 = mCfg->groups[group].pmPowerL2; float x3 = mCfg->groups[group].pmPowerL3; - mLog["x"] = x; mLog["x1"] = x1; mLog["x2"] = x2; @@ -943,12 +787,10 @@ class ZeroExport { float e1 = w - x1; float e2 = w - x2; float e3 = w - x3; - mLog["e"] = e; mLog["e1"] = e1; mLog["e2"] = e2; mLog["e3"] = e3; - if ( (e < mCfg->groups[group].powerTolerance) && (e > -mCfg->groups[group].powerTolerance) && (e1 < mCfg->groups[group].powerTolerance) && (e1 > -mCfg->groups[group].powerTolerance) && @@ -1023,7 +865,6 @@ class ZeroExport { mLog["y1"] = y1; mLog["y2"] = y2; mLog["y3"] = y3; - mCfg->groups[group].grpPower = y; mCfg->groups[group].grpPowerL1 = y1; mCfg->groups[group].grpPowerL2 = y2; @@ -1226,7 +1067,7 @@ class ZeroExport { continue; } // Inverter not selected -> ignore - if (mCfg->groups[group].inverters[inv].id <= 0) { + if (mCfg->groups[group].inverters[inv].id < 0) { continue; } *doLog = true; @@ -1296,13 +1137,10 @@ class ZeroExport { logObj["i"] = inv; // Inverter not enabled -> ignore - if (!mCfg->groups[group].inverters[inv].enabled) { - continue; - } + if (!mCfg->groups[group].inverters[inv].enabled) continue; + // Inverter not selected -> ignore - if (mCfg->groups[group].inverters[inv].id <= 0) { - continue; - } + if (mCfg->groups[group].inverters[inv].id < 0) continue; if (mCfg->debug) *doLog = true; @@ -1336,7 +1174,7 @@ class ZeroExport { if ( ( (mCfg->groups[group].battSwitch == false) || - (mCfg->groups[group].inverters[inv].limitNew < mCfg->groups[group].inverters[inv].powerMin)) && + (mCfg->groups[group].inverters[inv].limitNew < (mCfg->groups[group].inverters[inv].powerMin - 50))) && (mIv[group][inv]->isProducing() == true)) { // Off mCfg->groups[group].inverters[inv].doPower = false; @@ -1397,14 +1235,10 @@ class ZeroExport { logObj["i"] = inv; // Inverter not enabled -> ignore - if (!mCfg->groups[group].inverters[inv].enabled) { - continue; - } + if (!mCfg->groups[group].inverters[inv].enabled) continue; // Inverter not selected -> ignore - if (mCfg->groups[group].inverters[inv].id <= 0) { - continue; - } + if (mCfg->groups[group].inverters[inv].id < 0) continue; // if isOff -> Limit Pmin if (!mIv[group][inv]->isProducing()) { @@ -1412,14 +1246,10 @@ class ZeroExport { } // Nothing todo - if (mCfg->groups[group].inverters[inv].limit == mCfg->groups[group].inverters[inv].limitNew) { - continue; - } + if (mCfg->groups[group].inverters[inv].limit == mCfg->groups[group].inverters[inv].limitNew) continue; // Abbruch weil Inverter nicht verfügbar - if (!mIv[group][inv]->isAvailable()) { - continue; - } + if (!mIv[group][inv]->isAvailable()) continue; // Abbruch weil Inverter produziert nicht /// if (!mIv[group][inv]->isProducing()) { @@ -1453,9 +1283,7 @@ class ZeroExport { } // Nothing todo - if (mCfg->groups[group].inverters[inv].limit == mCfg->groups[group].inverters[inv].limitNew) { - continue; - } + if (mCfg->groups[group].inverters[inv].limit == mCfg->groups[group].inverters[inv].limitNew) continue; *doLog = true; @@ -1538,10 +1366,10 @@ class ZeroExport { * @returns true/false * @todo Hier ist noch keine Funktion */ - bool groupEmergency(uint8_t group, unsigned long *tsp, bool *doLog) { + bool groupEmergency(zeroExportGroup_t *cfgGroup, unsigned long *tsp, bool *doLog) { if (mCfg->debug) mLog["t"] = "groupEmergency"; - mCfg->groups[group].lastRun = *tsp; + cfgGroup->lastRun = *tsp; *doLog = true; @@ -1558,7 +1386,7 @@ class ZeroExport { */ void sendLog(void) { if (mCfg->log_over_webserial) { -// DBGPRINTLN(String("ze: ") + mDocLog.as()); + // DBGPRINTLN(String("ze: ") + mDocLog.as()); DPRINTLN(DBG_INFO, String("ze: ") + mDocLog.as()); } diff --git a/src/web/RestApi.h b/src/web/RestApi.h index 045eb2d8..70a30a48 100644 --- a/src/web/RestApi.h +++ b/src/web/RestApi.h @@ -1159,7 +1159,6 @@ class RestApi { mConfig->plugin.zeroExport.groups[group].Kd = jsonIn[F("Kd")]; // Global mConfig->plugin.zeroExport.groups[group].state = zeroExportState::INIT; - mConfig->plugin.zeroExport.groups[group].stateNext = zeroExportState::INIT; mApp->saveSettings(false); // without reboot } #endif From 5a1a7bc22257ce2b3dd71cc74990588d20ef8534 Mon Sep 17 00:00:00 2001 From: DanielR92 Date: Sun, 31 Mar 2024 09:41:06 +0200 Subject: [PATCH 13/22] add struct array for power --- src/config/settings.h | 2 +- src/plugins/zeroExport/powermeter.h | 240 ++++++++++++---------------- 2 files changed, 106 insertions(+), 136 deletions(-) diff --git a/src/config/settings.h b/src/config/settings.h index 7458aa1f..1b70161b 100644 --- a/src/config/settings.h +++ b/src/config/settings.h @@ -188,7 +188,7 @@ typedef struct { // Plugin ZeroExport #if defined(PLUGIN_ZEROEXPORT) -//#define ZEROEXPORT_DEV_POWERMETER +#define ZEROEXPORT_DEV_POWERMETER #define ZEROEXPORT_MAX_GROUPS 6 #define ZEROEXPORT_GROUP_MAX_LEN_NAME 25 #define ZEROEXPORT_GROUP_MAX_LEN_PM_URL 100 diff --git a/src/plugins/zeroExport/powermeter.h b/src/plugins/zeroExport/powermeter.h index 6ca1280a..29a1ffc9 100644 --- a/src/plugins/zeroExport/powermeter.h +++ b/src/plugins/zeroExport/powermeter.h @@ -21,6 +21,13 @@ typedef struct { float *Arg; } OBISHandler; +typedef struct { + float P; + float P1; + float P2; + float P3; +} PowerMeterBuffer_t; + class powermeter { public: powermeter() { @@ -29,44 +36,67 @@ class powermeter { ~powermeter() { } - bool setup(zeroExport_t *cfg /*Hier muss noch geklärt werden was gebraucht wird*/) { + bool setup(zeroExport_t *cfg, JsonObject *log /*Hier muss noch geklärt werden was gebraucht wird*/) { mCfg = cfg; - + mLog = log; return true; } - void loop(void) { - } - - /** groupGetPowermeter - * Holt die Daten vom Powermeter - * @param group - * @returns true/false + /** loop + * abfrage der gruppen um die aktuellen Werte (Zähler) zu ermitteln. */ - bool getData(JsonObject logObj, uint8_t group) { - bool result = false; - switch (mCfg->groups[group].pm_type) { - case 1: - result = getPowermeterWattsShelly(logObj, group); + void loop(void) + { + PowerMeterBuffer_t power; + + if(millis() - previousMillis <= 3000) return; // skip when it is to fast + for (u_short group = 0; group < ZEROEXPORT_MAX_GROUPS; group++) + { + switch (mCfg->groups[group].pm_type) { + case zeroExportPowermeterType_t::Shelly: + getPowermeterWattsShelly(*mLog, group); break; - case 2: - result = getPowermeterWattsTasmota(logObj, group); + case zeroExportPowermeterType_t::Tasmota: + getPowermeterWattsTasmota(*mLog, group); break; - case 3: - result = getPowermeterWattsMqtt(logObj, group); + case zeroExportPowermeterType_t::Mqtt: + getPowermeterWattsMqtt(*mLog, group); break; - case 4: - result = getPowermeterWattsHichi(logObj, group); + case zeroExportPowermeterType_t::Hichi: + getPowermeterWattsHichi(*mLog, group); break; - case 5: - result = getPowermeterWattsTibber(logObj, group); + case zeroExportPowermeterType_t::Tibber: + power = getPowermeterWattsTibber(*mLog, group); break; + } + + bufferWrite(power, group); } - if (!result) { - logObj["err"] = "type: " + String(mCfg->groups[group].pm_type); + previousMillis = millis(); + } + + /** groupGetPowermeter + * Holt die Daten vom Powermeter + * @param group + * @returns true/false + */ + PowerMeterBuffer_t getDataAVG(uint8_t group) { + PowerMeterBuffer_t avg; + avg.P = avg.P1 = avg.P2 = avg.P2 = avg.P3 = 0; + + for (int i = 0; i < 5; i++) + { + avg.P += powermeterbuffer[group][i].P; + avg.P1 += powermeterbuffer[group][i].P1; + avg.P2 += powermeterbuffer[group][i].P2; + avg.P3 += powermeterbuffer[group][i].P3; } + avg.P = avg.P / 5; + avg.P1 = avg.P1 / 5; + avg.P2 = avg.P2 / 5; + avg.P3 = avg.P3 / 5; - return result; + return avg; } private: @@ -76,16 +106,11 @@ class powermeter { * @param group * @returns true/false */ - bool getPowermeterWattsShelly(JsonObject logObj, uint8_t group) { - bool result = false; + PowerMeterBuffer_t getPowermeterWattsShelly(JsonObject logObj, uint8_t group) { + PowerMeterBuffer_t result; logObj["mod"] = "getPowermeterWattsShelly"; - mCfg->groups[group].pmPower = 0; - mCfg->groups[group].pmPowerL1 = 0; - mCfg->groups[group].pmPowerL2 = 0; - mCfg->groups[group].pmPowerL3 = 0; - HTTPClient http; http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); http.setUserAgent("Ahoy-Agent"); @@ -107,57 +132,48 @@ class powermeter { DeserializationError error = deserializeJson(doc, http.getString()); if (error) { logObj["err"] = "deserializeJson: " + String(error.c_str()); - return false; + return result; } else { if (doc.containsKey(F("total_power"))) { // Shelly 3EM - mCfg->groups[group].pmPower = doc["total_power"]; - result = true; + result.P = doc["total_power"]; } else if (doc.containsKey(F("em:0"))) { // Shelly pro 3EM - mCfg->groups[group].pmPower = doc["em:0"]["total_act_power"]; - result = true; + result.P = doc["em:0"]["total_act_power"]; } else { // Keine Daten - mCfg->groups[group].pmPower = 0; + result.P = 0; } if (doc.containsKey(F("emeters"))) { // Shelly 3EM - mCfg->groups[group].pmPowerL1 = doc["emeters"][0]["power"]; - result = true; + result.P1 = doc["emeters"][0]["power"]; } else if (doc.containsKey(F("em:0"))) { // Shelly pro 3EM - mCfg->groups[group].pmPowerL1 = doc["em:0"]["a_act_power"]; - result = true; + result.P1 = doc["em:0"]["a_act_power"]; } else if (doc.containsKey(F("switch:0"))) { // Shelly plus1pm plus2pm - mCfg->groups[group].pmPowerL1 = doc["switch:0"]["apower"]; - mCfg->groups[group].pmPower += mCfg->groups[group].pmPowerL1; - result = true; + result.P1 = doc["switch:0"]["apower"]; + result.P += result.P1; } else if (doc.containsKey(F("apower"))) { // Shelly Alternative - mCfg->groups[group].pmPowerL1 = doc["apower"]; - mCfg->groups[group].pmPower += mCfg->groups[group].pmPowerL1; - result = true; + result.P1 = doc["apower"]; + result.P += result.P1; } else { // Keine Daten - mCfg->groups[group].pmPowerL1 = 0; + result.P1 = 0; } if (doc.containsKey(F("emeters"))) { // Shelly 3EM - mCfg->groups[group].pmPowerL2 = doc["emeters"][1]["power"]; - result = true; + result.P2 = doc["emeters"][1]["power"]; } else if (doc.containsKey(F("em:0"))) { // Shelly pro 3EM - mCfg->groups[group].pmPowerL2 = doc["em:0"]["b_act_power"]; - result = true; + result.P2 = doc["em:0"]["b_act_power"]; } else if (doc.containsKey(F("switch:1"))) { // Shelly plus1pm plus2pm - mCfg->groups[group].pmPowerL2 = doc["switch.1"]["apower"]; - mCfg->groups[group].pmPower += mCfg->groups[group].pmPowerL2; - result = true; + result.P2 = doc["switch.1"]["apower"]; + result.P += result.P2; //} else if (doc.containsKey(F("apower"))) { // Shelly Alternative // mCfg->groups[group].pmPowerL2 = doc["apower"]; @@ -165,22 +181,19 @@ class powermeter { // ret = true; } else { // Keine Daten - mCfg->groups[group].pmPowerL2 = 0; + result.P2 = 0; } if (doc.containsKey(F("emeters"))) { // Shelly 3EM - mCfg->groups[group].pmPowerL3 = doc["emeters"][2]["power"]; - result = true; + result.P3 = doc["emeters"][2]["power"]; } else if (doc.containsKey(F("em:0"))) { // Shelly pro 3EM - mCfg->groups[group].pmPowerL3 = doc["em:0"]["c_act_power"]; - result = true; + result.P3 = doc["em:0"]["c_act_power"]; } else if (doc.containsKey(F("switch:2"))) { // Shelly plus1pm plus2pm - mCfg->groups[group].pmPowerL3 = doc["switch:2"]["apower"]; - mCfg->groups[group].pmPower += mCfg->groups[group].pmPowerL3; - result = true; + result.P3 = doc["switch:2"]["apower"]; + result.P += result.P3; //} else if (doc.containsKey(F("apower"))) { // Shelly Alternative // mCfg->groups[group].pmPowerL3 = doc["apower"]; @@ -188,17 +201,11 @@ class powermeter { // result = true; } else { // Keine Daten - mCfg->groups[group].pmPowerL3 = 0; + result.P3 = 0; } } } http.end(); - - logObj["P"] = mCfg->groups[group].pmPower; - logObj["P1"] = mCfg->groups[group].pmPowerL1; - logObj["P2"] = mCfg->groups[group].pmPowerL2; - logObj["P3"] = mCfg->groups[group].pmPowerL3; - return result; } @@ -250,17 +257,10 @@ class powermeter { * } * } */ - bool getPowermeterWattsTasmota(JsonObject logObj, uint8_t group) { - bool result = false; + PowerMeterBuffer_t getPowermeterWattsTasmota(JsonObject logObj, uint8_t group) { + PowerMeterBuffer_t result; logObj["mod"] = "getPowermeterWattsTasmota"; - - mCfg->groups[group].pmPower = 0; - mCfg->groups[group].pmPowerL1 = 0; - mCfg->groups[group].pmPowerL2 = 0; - mCfg->groups[group].pmPowerL3 = 0; - - result = true; /* // TODO: nicht komplett @@ -329,28 +329,16 @@ class powermeter { * @param group * @returns true/false */ - bool getPowermeterWattsMqtt(JsonObject logObj, uint8_t group) { - bool result = false; + PowerMeterBuffer_t getPowermeterWattsMqtt(JsonObject logObj, uint8_t group) { + PowerMeterBuffer_t result; logObj["mod"] = "getPowermeterWattsMqtt"; - mCfg->groups[group].pmPower = 0; - mCfg->groups[group].pmPowerL1 = 0; - mCfg->groups[group].pmPowerL2 = 0; - mCfg->groups[group].pmPowerL3 = 0; - // Hier neuer Code - Anfang // TODO: Noch nicht komplett - result = true; - // Hier neuer Code - Ende - logObj["P"] = mCfg->groups[group].pmPower; - logObj["P1"] = mCfg->groups[group].pmPowerL1; - logObj["P2"] = mCfg->groups[group].pmPowerL2; - logObj["P3"] = mCfg->groups[group].pmPowerL3; - return result; } @@ -360,28 +348,16 @@ class powermeter { * @param group * @returns true/false */ - bool getPowermeterWattsHichi(JsonObject logObj, uint8_t group) { - bool result = false; + PowerMeterBuffer_t getPowermeterWattsHichi(JsonObject logObj, uint8_t group) { + PowerMeterBuffer_t result; logObj["mod"] = "getPowermeterWattsHichi"; - mCfg->groups[group].pmPower = 0; - mCfg->groups[group].pmPowerL1 = 0; - mCfg->groups[group].pmPowerL2 = 0; - mCfg->groups[group].pmPowerL3 = 0; // Hier neuer Code - Anfang // TODO: Noch nicht komplett - result = true; - // Hier neuer Code - Ende - - logObj["P"] = mCfg->groups[group].pmPower; - logObj["P1"] = mCfg->groups[group].pmPowerL1; - logObj["P2"] = mCfg->groups[group].pmPowerL2; - logObj["P3"] = mCfg->groups[group].pmPowerL3; - return result; } @@ -416,9 +392,6 @@ class powermeter { 07 01 00 02 08 01 ff #objName: OBIS-Kennzahl für Wirkenergie Einspeisung Tarif1 07 01 00 02 08 02 ff #objName: OBIS-Kennzahl für Wirkenergie Einspeisung Tarif2 */ - - unsigned long previousMillis = 0; - const std::list smlHandlerList{ {{0x01, 0x00, 0x10, 0x07, 0x00, 0xff}, &smlOBISW, &_powerMeterTotal}, // total - OBIS-Kennzahl für momentane Gesamtwirkleistung @@ -429,17 +402,11 @@ class powermeter { {{0x01, 0x00, 0x01, 0x08, 0x00, 0xff}, &smlOBISWh, &_powerMeterImport}, {{0x01, 0x00, 0x02, 0x08, 0x00, 0xff}, &smlOBISWh, &_powerMeterExport}}; - bool getPowermeterWattsTibber(JsonObject logObj, uint8_t group) { - bool result = false; + PowerMeterBuffer_t getPowermeterWattsTibber(JsonObject logObj, uint8_t group) { + PowerMeterBuffer_t result; - if(millis() - previousMillis <= 3000) return false; // skip when it is to fast logObj["mod"] = "getPowermeterWattsTibber"; - mCfg->groups[group].pmPower = 0; - mCfg->groups[group].pmPowerL1 = 0; - mCfg->groups[group].pmPowerL2 = 0; - mCfg->groups[group].pmPowerL3 = 0; - HTTPClient http; http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); http.setUserAgent("Ahoy-Agent"); @@ -465,17 +432,14 @@ class powermeter { switch (smlCurrentState) { case SML_FINAL: - mCfg->groups[group].pmPower = _powerMeterTotal; - - mCfg->groups[group].pmPowerL1 = _powerMeter1Power; - mCfg->groups[group].pmPowerL2 = _powerMeter2Power; - mCfg->groups[group].pmPowerL3 = _powerMeter3Power; + result.P = _powerMeterTotal; + result.P1 = _powerMeter1Power; + result.P2 = _powerMeter2Power; + result.P3 = _powerMeter3Power; if(! (_powerMeter1Power && _powerMeter2Power && _powerMeter3Power)) { - mCfg->groups[group].pmPowerL1 = mCfg->groups[group].pmPowerL2 = mCfg->groups[group].pmPowerL3 = _powerMeterTotal / 3; + result.P1 = result.P2 = result.P3 = _powerMeterTotal / 3; } - - result = true; break; case SML_LISTEND: // check handlers on last received list @@ -491,18 +455,24 @@ class powermeter { } http.end(); - - logObj["P"] = mCfg->groups[group].pmPower; - logObj["P1"] = mCfg->groups[group].pmPowerL1; - logObj["P2"] = mCfg->groups[group].pmPowerL2; - logObj["P3"] = mCfg->groups[group].pmPowerL3; - - previousMillis = millis(); return result; } - private: + void bufferWrite(PowerMeterBuffer_t raw, short group) + { + powermeterbuffer[group][powerbufferpos[group]] = raw; + powerbufferpos[group]++; + if(powerbufferpos[group] >= 5) powerbufferpos[group] = 0; + } + + + zeroExport_t *mCfg; + JsonObject *mLog; + unsigned long previousMillis = 0; + + PowerMeterBuffer_t powermeterbuffer[ZEROEXPORT_MAX_GROUPS][5] = { 0 }; + short powerbufferpos[ZEROEXPORT_MAX_GROUPS] = { 0 }; }; // TODO: Vorlagen für Powermeter-Analyse From 55767dca7c3293ed45fb88c78f3eacbb50403a83 Mon Sep 17 00:00:00 2001 From: Patrick Amrhein Date: Sun, 31 Mar 2024 12:38:10 +0200 Subject: [PATCH 14/22] Redesign, Bugfix onMqttMessage enable/disable -> init groups --- src/plugins/zeroExport/zeroExport.h | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/plugins/zeroExport/zeroExport.h b/src/plugins/zeroExport/zeroExport.h index 60373a20..6e29c13d 100644 --- a/src/plugins/zeroExport/zeroExport.h +++ b/src/plugins/zeroExport/zeroExport.h @@ -418,17 +418,26 @@ class ZeroExport { if (obj["path"] == "zero" && obj["cmd"] == "set") { // "topic":"inverter/zero/set/groups/0/enabled" - // @TODO: state machine init if (topic.indexOf("groups") != -1) { String i = topic.substring(topic.length() - 10, topic.length() - 8); - uint id = i.toInt(); + uint8_t group = i.toInt(); - mCfg->groups[id].enabled = (bool)obj["val"]; + mCfg->groups[group].enabled = (bool)obj["val"]; + + // Initialize group + mCfg->groups[group].state = zeroExportState::INIT; + mCfg->groups[group].sleep = 0; } else { mCfg->enabled = (bool)obj["val"]; mLog["zero_enable"] = mCfg->enabled; + + // Initialize groups + for (uint8_t group = 0; group < ZEROEXPORT_MAX_GROUPS; group++) { + mCfg->groups[group].state = zeroExportState::INIT; + mCfg->groups[group].sleep = 0; + } } return; } @@ -441,6 +450,9 @@ class ZeroExport { private: /** NotEnabledOrNotSelected * Inverter not enabled -> ignore || Inverter not selected -> ignore + * @param group + * @param inv + * @returns true/false */ bool NotEnabledOrNotSelected(uint8_t group, uint8_t inv) { return ((!mCfg->groups[group].inverters[inv].enabled) || (mCfg->groups[group].inverters[inv].id < 0)); @@ -1083,7 +1095,6 @@ class ZeroExport { // Inverter not enabled or not selected -> ignore if (NotEnabledOrNotSelected(group, inv)) continue; - *doLog = true; // Reset if ((mCfg->groups[group].inverters[inv].doReboot) && (mCfg->groups[group].inverters[inv].waitRebootAck == 0)) { result = false; @@ -1110,6 +1121,7 @@ class ZeroExport { } result = false; + *doLog = true; logObj["act"] = String("reboot"); @@ -1205,6 +1217,7 @@ class ZeroExport { } result = false; + *doLog = true; if (!mCfg->debug) logObj["act"] = mCfg->groups[group].inverters[inv].doPower; From 0b2b72fac741bdcb669d01b572dd68f95f707371 Mon Sep 17 00:00:00 2001 From: Patrick Amrhein Date: Sun, 31 Mar 2024 13:28:05 +0200 Subject: [PATCH 15/22] Bugfix: powermeter no data --- src/plugins/zeroExport/powermeter.h | 74 ++++++++++++++++------------- 1 file changed, 40 insertions(+), 34 deletions(-) diff --git a/src/plugins/zeroExport/powermeter.h b/src/plugins/zeroExport/powermeter.h index 29a1ffc9..65e44cc7 100644 --- a/src/plugins/zeroExport/powermeter.h +++ b/src/plugins/zeroExport/powermeter.h @@ -26,7 +26,7 @@ typedef struct { float P1; float P2; float P3; -} PowerMeterBuffer_t; +} PowermeterBuffer_t; class powermeter { public: @@ -47,23 +47,23 @@ class powermeter { */ void loop(void) { - PowerMeterBuffer_t power; + PowermeterBuffer_t power; if(millis() - previousMillis <= 3000) return; // skip when it is to fast for (u_short group = 0; group < ZEROEXPORT_MAX_GROUPS; group++) { switch (mCfg->groups[group].pm_type) { case zeroExportPowermeterType_t::Shelly: - getPowermeterWattsShelly(*mLog, group); + power = getPowermeterWattsShelly(*mLog, group); break; case zeroExportPowermeterType_t::Tasmota: - getPowermeterWattsTasmota(*mLog, group); + power = getPowermeterWattsTasmota(*mLog, group); break; case zeroExportPowermeterType_t::Mqtt: - getPowermeterWattsMqtt(*mLog, group); + power = getPowermeterWattsMqtt(*mLog, group); break; case zeroExportPowermeterType_t::Hichi: - getPowermeterWattsHichi(*mLog, group); + power = getPowermeterWattsHichi(*mLog, group); break; case zeroExportPowermeterType_t::Tibber: power = getPowermeterWattsTibber(*mLog, group); @@ -80,16 +80,16 @@ class powermeter { * @param group * @returns true/false */ - PowerMeterBuffer_t getDataAVG(uint8_t group) { - PowerMeterBuffer_t avg; + PowermeterBuffer_t getDataAVG(uint8_t group) { + PowermeterBuffer_t avg; avg.P = avg.P1 = avg.P2 = avg.P2 = avg.P3 = 0; for (int i = 0; i < 5; i++) { - avg.P += powermeterbuffer[group][i].P; - avg.P1 += powermeterbuffer[group][i].P1; - avg.P2 += powermeterbuffer[group][i].P2; - avg.P3 += powermeterbuffer[group][i].P3; + avg.P += mPowermeterBuffer[group][i].P; + avg.P1 += mPowermeterBuffer[group][i].P1; + avg.P2 += mPowermeterBuffer[group][i].P2; + avg.P3 += mPowermeterBuffer[group][i].P3; } avg.P = avg.P / 5; avg.P1 = avg.P1 / 5; @@ -106,8 +106,9 @@ class powermeter { * @param group * @returns true/false */ - PowerMeterBuffer_t getPowermeterWattsShelly(JsonObject logObj, uint8_t group) { - PowerMeterBuffer_t result; + PowermeterBuffer_t getPowermeterWattsShelly(JsonObject logObj, uint8_t group) { + PowermeterBuffer_t result; + result.P = result.P1 = result.P2 = result.P3 = 0; logObj["mod"] = "getPowermeterWattsShelly"; @@ -115,9 +116,8 @@ class powermeter { http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); http.setUserAgent("Ahoy-Agent"); // TODO: Ahoy-0.8.850024-zero - http.setConnectTimeout(1000); + http.setConnectTimeout(500); http.setTimeout(1000); - // TODO: Timeout von 1000 reduzieren? http.addHeader("Content-Type", "application/json"); http.addHeader("Accept", "application/json"); @@ -257,8 +257,9 @@ class powermeter { * } * } */ - PowerMeterBuffer_t getPowermeterWattsTasmota(JsonObject logObj, uint8_t group) { - PowerMeterBuffer_t result; + PowermeterBuffer_t getPowermeterWattsTasmota(JsonObject logObj, uint8_t group) { + PowermeterBuffer_t result; + result.P = result.P1 = result.P2 = result.P3 = 0; logObj["mod"] = "getPowermeterWattsTasmota"; /* @@ -329,12 +330,14 @@ class powermeter { * @param group * @returns true/false */ - PowerMeterBuffer_t getPowermeterWattsMqtt(JsonObject logObj, uint8_t group) { - PowerMeterBuffer_t result; + PowermeterBuffer_t getPowermeterWattsMqtt(JsonObject logObj, uint8_t group) { + PowermeterBuffer_t result; + result.P = result.P1 = result.P2 = result.P3 = 0; logObj["mod"] = "getPowermeterWattsMqtt"; // Hier neuer Code - Anfang + // TODO: Noch nicht komplett // Hier neuer Code - Ende @@ -348,16 +351,18 @@ class powermeter { * @param group * @returns true/false */ - PowerMeterBuffer_t getPowermeterWattsHichi(JsonObject logObj, uint8_t group) { - PowerMeterBuffer_t result; + PowermeterBuffer_t getPowermeterWattsHichi(JsonObject logObj, uint8_t group) { + PowermeterBuffer_t result; + result.P = result.P1 = result.P2 = result.P3 = 0; logObj["mod"] = "getPowermeterWattsHichi"; - // Hier neuer Code - Anfang + // TODO: Noch nicht komplett // Hier neuer Code - Ende + return result; } @@ -402,15 +407,17 @@ class powermeter { {{0x01, 0x00, 0x01, 0x08, 0x00, 0xff}, &smlOBISWh, &_powerMeterImport}, {{0x01, 0x00, 0x02, 0x08, 0x00, 0xff}, &smlOBISWh, &_powerMeterExport}}; - PowerMeterBuffer_t getPowermeterWattsTibber(JsonObject logObj, uint8_t group) { - PowerMeterBuffer_t result; + PowermeterBuffer_t getPowermeterWattsTibber(JsonObject logObj, uint8_t group) { + PowermeterBuffer_t result; + result.P = result.P1 = result.P2 = result.P3 = 0; logObj["mod"] = "getPowermeterWattsTibber"; HTTPClient http; http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); http.setUserAgent("Ahoy-Agent"); - http.setConnectTimeout(1000); + // TODO: Ahoy-0.8.850024-zero + http.setConnectTimeout(500); http.setTimeout(1000); http.addHeader("Content-Type", "application/json"); http.addHeader("Accept", "application/json"); @@ -458,21 +465,20 @@ class powermeter { return result; } - void bufferWrite(PowerMeterBuffer_t raw, short group) + void bufferWrite(PowermeterBuffer_t raw, short group) { - powermeterbuffer[group][powerbufferpos[group]] = raw; - powerbufferpos[group]++; - if(powerbufferpos[group] >= 5) powerbufferpos[group] = 0; + mPowermeterBuffer[group][mPowermeterBufferPos[group]] = raw; + mPowermeterBufferPos[group]++; + if(mPowermeterBufferPos[group] >= 5) mPowermeterBufferPos[group] = 0; } - - zeroExport_t *mCfg; JsonObject *mLog; + unsigned long previousMillis = 0; - PowerMeterBuffer_t powermeterbuffer[ZEROEXPORT_MAX_GROUPS][5] = { 0 }; - short powerbufferpos[ZEROEXPORT_MAX_GROUPS] = { 0 }; + PowermeterBuffer_t mPowermeterBuffer[ZEROEXPORT_MAX_GROUPS][5] = { 0 }; + short mPowermeterBufferPos[ZEROEXPORT_MAX_GROUPS] = { 0 }; }; // TODO: Vorlagen für Powermeter-Analyse From e938392ffecff431d2b126e2e0ec7b03acd6b603 Mon Sep 17 00:00:00 2001 From: Patrick Amrhein Date: Sun, 31 Mar 2024 13:44:06 +0200 Subject: [PATCH 16/22] Timing --- src/plugins/zeroExport/powermeter.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/plugins/zeroExport/powermeter.h b/src/plugins/zeroExport/powermeter.h index 65e44cc7..70bd046b 100644 --- a/src/plugins/zeroExport/powermeter.h +++ b/src/plugins/zeroExport/powermeter.h @@ -47,9 +47,12 @@ class powermeter { */ void loop(void) { + unsigned long Tsp = millis(); + if(Tsp - mPreviousTsp <= 1000) return; // skip when it is to fast + mPreviousTsp = Tsp; + PowermeterBuffer_t power; - if(millis() - previousMillis <= 3000) return; // skip when it is to fast for (u_short group = 0; group < ZEROEXPORT_MAX_GROUPS; group++) { switch (mCfg->groups[group].pm_type) { @@ -72,7 +75,6 @@ class powermeter { bufferWrite(power, group); } - previousMillis = millis(); } /** groupGetPowermeter @@ -408,6 +410,8 @@ class powermeter { {{0x01, 0x00, 0x02, 0x08, 0x00, 0xff}, &smlOBISWh, &_powerMeterExport}}; PowermeterBuffer_t getPowermeterWattsTibber(JsonObject logObj, uint8_t group) { + mPreviousTsp = mPreviousTsp + 2000; // Zusätzliche Pause + PowermeterBuffer_t result; result.P = result.P1 = result.P2 = result.P3 = 0; @@ -475,7 +479,7 @@ class powermeter { zeroExport_t *mCfg; JsonObject *mLog; - unsigned long previousMillis = 0; + unsigned long mPreviousTsp = 0; PowermeterBuffer_t mPowermeterBuffer[ZEROEXPORT_MAX_GROUPS][5] = { 0 }; short mPowermeterBufferPos[ZEROEXPORT_MAX_GROUPS] = { 0 }; From e5f5584ef289a60c946a497503c3dc073e534f20 Mon Sep 17 00:00:00 2001 From: Patrick Amrhein Date: Sun, 31 Mar 2024 14:35:48 +0200 Subject: [PATCH 17/22] Redesign, Bugfix --- src/plugins/zeroExport/zeroExport.h | 39 ++++++++++++++--------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/src/plugins/zeroExport/zeroExport.h b/src/plugins/zeroExport/zeroExport.h index 6e29c13d..bff250e7 100644 --- a/src/plugins/zeroExport/zeroExport.h +++ b/src/plugins/zeroExport/zeroExport.h @@ -411,27 +411,28 @@ class ZeroExport { if (!mIsInitialized) return; String topic = String(obj["topic"]); - if(!topic.indexOf("/zero/set/")) return; + if (!topic.indexOf("/zero/set/")) return; mLog["t"] = "onMqttMessage"; - if (obj["path"] == "zero" && obj["cmd"] == "set") - { + if (obj["path"] == "zero" && obj["cmd"] == "set") { // "topic":"inverter/zero/set/groups/0/enabled" if (topic.indexOf("groups") != -1) { +// TODO: Topicprüfung +// TODO: Topicprüfung ist 10 und 8 korrekt? Wäre es nicht besser das anhand der / rauszufiltern wenn es 2-stellige Gruppen gibt? String i = topic.substring(topic.length() - 10, topic.length() - 8); uint8_t group = i.toInt(); + mLog["g"] = group; mCfg->groups[group].enabled = (bool)obj["val"]; // Initialize group mCfg->groups[group].state = zeroExportState::INIT; mCfg->groups[group].sleep = 0; - } - else - { + } else { +// TODO: Topicprüfung mCfg->enabled = (bool)obj["val"]; - mLog["zero_enable"] = mCfg->enabled; + mLog["mCfg->enabled"] = mCfg->enabled; // Initialize groups for (uint8_t group = 0; group < ZEROEXPORT_MAX_GROUPS; group++) { @@ -439,12 +440,12 @@ class ZeroExport { mCfg->groups[group].sleep = 0; } } - return; } mLog["Msg"] = obj; sendLog(); clearLog(); + return; } private: @@ -765,10 +766,10 @@ class ZeroExport { *doLog = true; - mCfg->groups[group].pmPower = mPowermeter.getDataAVG(group).P; - mCfg->groups[group].pmPowerL1 = mPowermeter.getDataAVG(group).P1; - mCfg->groups[group].pmPowerL2 = mPowermeter.getDataAVG(group).P2; - mCfg->groups[group].pmPowerL3 = mPowermeter.getDataAVG(group).P3; + mCfg->groups[group].pmPower = mPowermeter.getDataAVG(group).P; + mCfg->groups[group].pmPowerL1 = mPowermeter.getDataAVG(group).P1; + mCfg->groups[group].pmPowerL2 = mPowermeter.getDataAVG(group).P2; + mCfg->groups[group].pmPowerL3 = mPowermeter.getDataAVG(group).P3; if ( (mCfg->groups[group].pmPower == 0) && @@ -913,7 +914,7 @@ class ZeroExport { mCfg->groups[group].lastRun = *tsp; - *doLog = true; + // *doLog = true; return true; } @@ -1164,8 +1165,6 @@ class ZeroExport { // Inverter not enabled or not selected -> ignore if (NotEnabledOrNotSelected(group, inv)) continue; - if (mCfg->debug) *doLog = true; - // Inverter not available -> ignore if (!mIv[group][inv]->isAvailable()) { logObj["a"] = false; @@ -1341,7 +1340,7 @@ class ZeroExport { DynamicJsonDocument doc(512); JsonObject obj = doc.to(); - *doLog = true; + // *doLog = true; String gr; // Init @@ -1351,14 +1350,14 @@ class ZeroExport { mMqtt->subscribe("zero/set/enabled", QOS_2); gr = "zero/set/groups/" + String(group) + "/enabled"; - mMqtt->publish(gr.c_str(), ((mCfg->groups[group].enabled) ? dict[STR_TRUE] : dict[STR_FALSE]) , false); + mMqtt->publish(gr.c_str(), ((mCfg->groups[group].enabled) ? dict[STR_TRUE] : dict[STR_FALSE]), false); mMqtt->subscribe(gr.c_str(), QOS_2); } mMqtt->publish("zero/state/enabled", ((mCfg->enabled) ? dict[STR_TRUE] : dict[STR_FALSE]), false); - gr = "zero/state/groups/" + String(group) + "/enabled"; - mMqtt->publish(gr.c_str(), ((mCfg->groups[group].enabled) ? dict[STR_TRUE] : dict[STR_FALSE]) , false); + gr = "zero/state/groups/" + String(group) + "/enabled"; + mMqtt->publish(gr.c_str(), ((mCfg->groups[group].enabled) ? dict[STR_TRUE] : dict[STR_FALSE]), false); // if (mCfg->groups[group].publishPower) { // mCfg->groups[group].publishPower = false; @@ -1399,7 +1398,7 @@ class ZeroExport { cfgGroup->lastRun = *tsp; - *doLog = true; + // *doLog = true; // if (!korrect) { // do From db5c48076c8749c1914b944c394c2bb404a2e9db Mon Sep 17 00:00:00 2001 From: Patrick Amrhein Date: Sun, 31 Mar 2024 23:14:20 +0200 Subject: [PATCH 18/22] Redesign, MQTT --- src/config/settings.h | 3 +- src/plugins/zeroExport/zeroExport.h | 281 +++++++++++++++++++--------- 2 files changed, 193 insertions(+), 91 deletions(-) diff --git a/src/config/settings.h b/src/config/settings.h index 64375ec3..891a6555 100644 --- a/src/config/settings.h +++ b/src/config/settings.h @@ -204,7 +204,6 @@ typedef struct { enum class zeroExportState : uint8_t { INIT, WAITREFRESH, - GETINVERTERACKS, GETINVERTERDATA, BATTERYPROTECTION, GETPOWERMETER, @@ -274,6 +273,7 @@ typedef struct { // bool doReboot; int8_t doPower; + bool doLimit; } zeroExportGroupInverter_t; typedef struct { @@ -701,6 +701,7 @@ class settings { mCfg.plugin.zeroExport.groups[group].inverters[inv].waitRebootAck = false; mCfg.plugin.zeroExport.groups[group].inverters[inv].doReboot = false; mCfg.plugin.zeroExport.groups[group].inverters[inv].doPower = -1; + mCfg.plugin.zeroExport.groups[group].inverters[inv].doLimit = false; } // Battery mCfg.plugin.zeroExport.groups[group].battEnabled = false; diff --git a/src/plugins/zeroExport/zeroExport.h b/src/plugins/zeroExport/zeroExport.h index bff250e7..55ea4551 100644 --- a/src/plugins/zeroExport/zeroExport.h +++ b/src/plugins/zeroExport/zeroExport.h @@ -92,19 +92,20 @@ class ZeroExport { break; case zeroExportState::WAITREFRESH: if (groupWaitRefresh(group, &Tsp, &DoLog)) { - cfgGroup->state = zeroExportState::GETINVERTERACKS; +/// cfgGroup->state = zeroExportState::GETINVERTERACKS; + cfgGroup->state = zeroExportState::GETINVERTERDATA; #if defined(ZEROEXPORT_DEV_POWERMETER) cfgGroup->state = zeroExportState::GETPOWERMETER; #endif } break; - case zeroExportState::GETINVERTERACKS: - if (groupGetInverterAcks(group, &Tsp, &DoLog)) { - cfgGroup->state = zeroExportState::GETINVERTERDATA; - } else { - cfgGroup->sleep = 1000; - } - break; +/// case zeroExportState::GETINVERTERACKS: +/// if (groupGetInverterAcks(group, &Tsp, &DoLog)) { +/// cfgGroup->state = zeroExportState::GETINVERTERDATA; +/// } else { +/// cfgGroup->sleep = 1000; +/// } +/// break; case zeroExportState::GETINVERTERDATA: if (groupGetInverterData(group, &Tsp, &DoLog)) { cfgGroup->state = zeroExportState::BATTERYPROTECTION; @@ -182,7 +183,7 @@ class ZeroExport { } break; case zeroExportState::EMERGENCY: - if (groupEmergency(cfgGroup, &Tsp, &DoLog)) { + if (groupEmergency(group, &Tsp, &DoLog)) { cfgGroup->lastRefresh = Tsp; cfgGroup->state = zeroExportState::INIT; //} else { @@ -418,11 +419,11 @@ class ZeroExport { if (obj["path"] == "zero" && obj["cmd"] == "set") { // "topic":"inverter/zero/set/groups/0/enabled" if (topic.indexOf("groups") != -1) { -// TODO: Topicprüfung -// TODO: Topicprüfung ist 10 und 8 korrekt? Wäre es nicht besser das anhand der / rauszufiltern wenn es 2-stellige Gruppen gibt? + // TODO: Topicprüfung + // TODO: Topicprüfung ist 10 und 8 korrekt? Wäre es nicht besser das anhand der / rauszufiltern wenn es 2-stellige Gruppen gibt? String i = topic.substring(topic.length() - 10, topic.length() - 8); uint8_t group = i.toInt(); - mLog["g"] = group; + mLog["g"] = group; mCfg->groups[group].enabled = (bool)obj["val"]; @@ -430,7 +431,7 @@ class ZeroExport { mCfg->groups[group].state = zeroExportState::INIT; mCfg->groups[group].sleep = 0; } else { -// TODO: Topicprüfung + // TODO: Topicprüfung mCfg->enabled = (bool)obj["val"]; mLog["mCfg->enabled"] = mCfg->enabled; @@ -548,6 +549,7 @@ class ZeroExport { * @returns true/false * @todo siehe code */ +/* bool groupGetInverterAcks(uint8_t group, unsigned long *tsp, bool *doLog) { if (mCfg->debug) mLog["t"] = "groupGetInverterAcks"; @@ -582,6 +584,7 @@ class ZeroExport { if (wait) return false; return true; } +*/ /** groupGetInverterData * @@ -1079,6 +1082,8 @@ class ZeroExport { /** groupSetReboot * * @param group + * @param tsp + * @param doLog * @returns true/false */ bool groupSetReboot(uint8_t group, unsigned long *tsp, bool *doLog) { @@ -1148,20 +1153,25 @@ class ZeroExport { /** groupSetPower * * @param group + * @param tsp + * @param doLog * @returns true/false */ bool groupSetPower(uint8_t group, unsigned long *tsp, bool *doLog) { + zeroExportGroup_t *cfgGroup = &mCfg->groups[group]; bool result = true; if (mCfg->debug) mLog["t"] = "groupSetPower"; - mCfg->groups[group].lastRun = *tsp; + cfgGroup->lastRun = *tsp; JsonArray logArr = mLog.createNestedArray("ix"); for (uint8_t inv = 0; inv < ZEROEXPORT_GROUP_MAX_INVERTERS; inv++) { JsonObject logObj = logArr.createNestedObject(); logObj["i"] = inv; + zeroExportGroupInverter_t *cfgGroupInv = &cfgGroup->inverters[inv]; + // Inverter not enabled or not selected -> ignore if (NotEnabledOrNotSelected(group, inv)) continue; @@ -1173,44 +1183,44 @@ class ZeroExport { } // Reset - if ((mCfg->groups[group].inverters[inv].doPower != -1) && (mCfg->groups[group].inverters[inv].waitPowerAck == 0)) { + if ((cfgGroupInv->doPower != -1) && (cfgGroupInv->waitPowerAck == 0)) { result = false; - mCfg->groups[group].inverters[inv].doPower = -1; + cfgGroupInv->doPower = -1; logObj["act"] = "done"; continue; } // Calculate logObj["battSw"] = mCfg->groups[group].battSwitch; - logObj["ivL"] = mCfg->groups[group].inverters[inv].limitNew; + logObj["ivL"] = cfgGroupInv->limitNew; logObj["ivSw"] = mIv[group][inv]->isProducing(); if ( (mCfg->groups[group].battSwitch == true) && - (mCfg->groups[group].inverters[inv].limitNew > mCfg->groups[group].inverters[inv].powerMin) && + (cfgGroupInv->limitNew > cfgGroupInv->powerMin) && (mIv[group][inv]->isProducing() == false)) { // On - mCfg->groups[group].inverters[inv].doPower = true; + cfgGroupInv->doPower = true; logObj["act"] = "on"; } if ( ( (mCfg->groups[group].battSwitch == false) || - (mCfg->groups[group].inverters[inv].limitNew < (mCfg->groups[group].inverters[inv].powerMin - 50))) && + (cfgGroupInv->limitNew < (cfgGroupInv->powerMin - 50))) && (mIv[group][inv]->isProducing() == true)) { // Off - mCfg->groups[group].inverters[inv].doPower = false; + cfgGroupInv->doPower = false; logObj["act"] = "off"; } // Wait - if (mCfg->groups[group].inverters[inv].waitPowerAck > 0) { - logObj["w"] = mCfg->groups[group].inverters[inv].waitPowerAck; + if (cfgGroupInv->waitPowerAck > 0) { + logObj["w"] = cfgGroupInv->waitPowerAck; result = false; continue; } - // Nothing to do - if (mCfg->groups[group].inverters[inv].doPower == -1) { + // Nothing todo + if (cfgGroupInv->doPower == -1) { logObj["act"] = "nothing to do"; continue; } @@ -1219,21 +1229,20 @@ class ZeroExport { *doLog = true; - if (!mCfg->debug) logObj["act"] = mCfg->groups[group].inverters[inv].doPower; + if (!mCfg->debug) logObj["act"] = cfgGroupInv->doPower; // wait for Ack - mCfg->groups[group].inverters[inv].waitPowerAck = 120; - logObj["wP"] = mCfg->groups[group].inverters[inv].waitPowerAck; + cfgGroupInv->waitPowerAck = 120; + logObj["wP"] = cfgGroupInv->waitPowerAck; // send Command DynamicJsonDocument doc(512); JsonObject obj = doc.to(); - obj["val"] = mCfg->groups[group].inverters[inv].doPower; - obj["id"] = mCfg->groups[group].inverters[inv].id; + obj["val"] = cfgGroupInv->doPower; + obj["id"] = cfgGroupInv->id; obj["path"] = "ctrl"; obj["cmd"] = "power"; mApi->ctrlRequest(obj); - logObj["d"] = obj; } @@ -1241,100 +1250,135 @@ class ZeroExport { } /** groupSetLimit - * + * Sets the calculated Limit to the Inverter and waits for ACK. * @param group + * @param tsp + * @param doLog * @returns true/false */ bool groupSetLimit(uint8_t group, unsigned long *tsp, bool *doLog) { + zeroExportGroup_t *cfgGroup = &mCfg->groups[group]; + bool result = true; + if (mCfg->debug) mLog["t"] = "groupSetLimit"; - mCfg->groups[group].lastRun = *tsp; + cfgGroup->lastRun = *tsp; - // Set limit JsonArray logArr = mLog.createNestedArray("ix"); for (uint8_t inv = 0; inv < ZEROEXPORT_GROUP_MAX_INVERTERS; inv++) { JsonObject logObj = logArr.createNestedObject(); logObj["i"] = inv; + zeroExportGroupInverter_t *cfgGroupInv = &cfgGroup->inverters[inv]; + // Inverter not enabled or not selected -> ignore if (NotEnabledOrNotSelected(group, inv)) continue; - // if isOff -> Limit Pmin - if (!mIv[group][inv]->isProducing()) { - mCfg->groups[group].inverters[inv].limitNew = mCfg->groups[group].inverters[inv].powerMin; + // Inverter not available -> ignore + if (!mIv[group][inv]->isAvailable()) { + logObj["a"] = false; + result = false; + continue; } - // Nothing todo - if (mCfg->groups[group].inverters[inv].limit == mCfg->groups[group].inverters[inv].limitNew) continue; - - // Abbruch weil Inverter nicht verfügbar - if (!mIv[group][inv]->isAvailable()) continue; + // Reset + if ((cfgGroupInv->doLimit) && (cfgGroupInv->waitLimitAck == 0)) { + result = false; + cfgGroupInv->doLimit = false; + logObj["act"] = "done"; + continue; + } - // Abbruch weil Inverter produziert nicht - /// if (!mIv[group][inv]->isProducing()) { - /// continue; - /// } + // Calculate - // do + // if isOff -> Limit Pmin + if (!mIv[group][inv]->isProducing()) { + cfgGroupInv->limitNew = cfgGroupInv->powerMin; + } // Restriction LimitNew >= Pmin - if (mCfg->groups[group].inverters[inv].limitNew < mCfg->groups[group].inverters[inv].powerMin) { - mCfg->groups[group].inverters[inv].limitNew = mCfg->groups[group].inverters[inv].powerMin; + if (cfgGroupInv->limitNew < cfgGroupInv->powerMin) { + cfgGroupInv->limitNew = cfgGroupInv->powerMin; } // Restriction LimitNew >= 2% uint16_t power2proz = mIv[group][inv]->getMaxPower() / 100 * 2; - if (mCfg->groups[group].inverters[inv].limitNew < power2proz) { - mCfg->groups[group].inverters[inv].limitNew = power2proz; + if (cfgGroupInv->limitNew < power2proz) { + cfgGroupInv->limitNew = power2proz; } // Restriction LimitNew <= Pmax - if (mCfg->groups[group].inverters[inv].limitNew > mCfg->groups[group].inverters[inv].powerMax) { - mCfg->groups[group].inverters[inv].limitNew = mCfg->groups[group].inverters[inv].powerMax; + if (cfgGroupInv->limitNew > cfgGroupInv->powerMax) { + cfgGroupInv->limitNew = cfgGroupInv->powerMax; } // Reject limit if difference < 5 W - if ( - (mCfg->groups[group].inverters[inv].limitNew > (mCfg->groups[group].inverters[inv].powerMin + ZEROEXPORT_GROUP_WR_LIMIT_MIN_DIFF)) && - (mCfg->groups[group].inverters[inv].limitNew > (mCfg->groups[group].inverters[inv].limit + ZEROEXPORT_GROUP_WR_LIMIT_MIN_DIFF)) && - (mCfg->groups[group].inverters[inv].limitNew < (mCfg->groups[group].inverters[inv].limit - ZEROEXPORT_GROUP_WR_LIMIT_MIN_DIFF))) { - mLog["err"] = String("Diff < ") + String(ZEROEXPORT_GROUP_WR_LIMIT_MIN_DIFF) + String("W"); - return false; + /* + if ( + (cfgGroupInv->limitNew > (cfgGroupInv->powerMin + ZEROEXPORT_GROUP_WR_LIMIT_MIN_DIFF)) && + (cfgGroupInv->limitNew > (cfgGroupInv->limit + ZEROEXPORT_GROUP_WR_LIMIT_MIN_DIFF)) && + (cfgGroupInv->limitNew < (cfgGroupInv->limit - ZEROEXPORT_GROUP_WR_LIMIT_MIN_DIFF))) { + logObj["err"] = String("Diff < ") + String(ZEROEXPORT_GROUP_WR_LIMIT_MIN_DIFF) + String("W"); + + *doLog = true; + + return false; + } + */ + // Wait + if (cfgGroupInv->waitLimitAck > 0) { + logObj["w"] = cfgGroupInv->waitLimitAck; + result = false; + continue; } // Nothing todo - if (mCfg->groups[group].inverters[inv].limit == mCfg->groups[group].inverters[inv].limitNew) continue; +// if (cfgGroupInv->doLimit == false) { +// logObj["act"] = "nothing to do"; +// continue; +// } + + if (cfgGroupInv->limit == cfgGroupInv->limitNew) { + logObj["act"] = "nothing to do"; + continue; + } + + result = false; *doLog = true; - mCfg->groups[group].inverters[inv].limit = mCfg->groups[group].inverters[inv].limitNew; - mLog["zeL"] = (uint16_t)mCfg->groups[group].inverters[inv].limit; + cfgGroupInv->doLimit = true; + if (!mCfg->debug) logObj["act"] = cfgGroupInv->doLimit; + + cfgGroupInv->limit = cfgGroupInv->limitNew; + logObj["zeL"] = (uint16_t)cfgGroupInv->limit; // wait for Ack - mCfg->groups[group].inverters[inv].waitLimitAck = 60; - mLog["wL"] = mCfg->groups[group].inverters[inv].waitLimitAck; + cfgGroupInv->waitLimitAck = 60; + logObj["wL"] = cfgGroupInv->waitLimitAck; // send Command DynamicJsonDocument doc(512); JsonObject obj = doc.to(); - obj["val"] = (uint16_t)mCfg->groups[group].inverters[inv].limit; - obj["id"] = mCfg->groups[group].inverters[inv].id; + obj["val"] = (uint16_t)cfgGroupInv->limit; + obj["id"] = cfgGroupInv->id; obj["path"] = "ctrl"; obj["cmd"] = "limit_nonpersistent_absolute"; mApi->ctrlRequest(obj); - - mLog["d"] = obj; + logObj["d"] = obj; } - return true; + return result; } /** groupPublish * */ bool groupPublish(uint8_t group, unsigned long *tsp, bool *doLog) { + zeroExportGroup_t *cfgGroup = &mCfg->groups[group]; + if (mCfg->debug) mLog["t"] = "groupPublish"; - mCfg->groups[group].lastRun = *tsp; + cfgGroup->lastRun = *tsp; if (mMqtt->isConnected()) { DynamicJsonDocument doc(512); @@ -1344,44 +1388,100 @@ class ZeroExport { String gr; // Init +// TODO: Init wird fälschlicherweise hier nur ausgeführt wenn zeroExport 1x aktiviert war. +// BUG: Wenn zeroExport deaktiviert wurde und dann rebootet, lässt sich zeroExport nicht mehr einschalten. if (!mIsSubscribed) { mIsSubscribed = true; + + // Global (zeroExport) +// TODO: Global wird fälschlicherweise hier je nach anzahl der aktivierten Gruppen bis zu 6x ausgeführt. mMqtt->publish("zero/set/enabled", ((mCfg->enabled) ? dict[STR_TRUE] : dict[STR_FALSE]), false); mMqtt->subscribe("zero/set/enabled", QOS_2); + // General gr = "zero/set/groups/" + String(group) + "/enabled"; - mMqtt->publish(gr.c_str(), ((mCfg->groups[group].enabled) ? dict[STR_TRUE] : dict[STR_FALSE]), false); + mMqtt->publish(gr.c_str(), ((cfgGroup->enabled) ? dict[STR_TRUE] : dict[STR_FALSE]), false); mMqtt->subscribe(gr.c_str(), QOS_2); + + // Powermeter +// TODO: fehlt + + // Inverters +// TODO: fehlt + + // Battery +// TODO: fehlt + + // Advanced +// TODO: fehlt } + // Global (zeroExport) +// TODO: Global wird fälschlicherweise hier je nach anzahl der aktivierten Gruppen bis zu 6x ausgeführt. mMqtt->publish("zero/state/enabled", ((mCfg->enabled) ? dict[STR_TRUE] : dict[STR_FALSE]), false); + // General gr = "zero/state/groups/" + String(group) + "/enabled"; - mMqtt->publish(gr.c_str(), ((mCfg->groups[group].enabled) ? dict[STR_TRUE] : dict[STR_FALSE]), false); - - // if (mCfg->groups[group].publishPower) { - // mCfg->groups[group].publishPower = false; - obj["L1"] = mCfg->groups[group].pmPowerL1; - obj["L2"] = mCfg->groups[group].pmPowerL2; - obj["L3"] = mCfg->groups[group].pmPowerL3; - obj["Sum"] = mCfg->groups[group].pmPower; - mMqtt->publish("zero/state/powermeter/P", doc.as().c_str(), false); - doc.clear(); - // } - - // if (mCfg->groups[group].pm_Publish_W) { - // mCfg->groups[group].pm_Publish_W = false; + mMqtt->publish(gr.c_str(), ((cfgGroup->enabled) ? dict[STR_TRUE] : dict[STR_FALSE]), false); + + gr = "zero/state/groups/" + String(group) + "/name"; + mMqtt->publish(gr.c_str(), cfgGroup->name, false); + + // Powermeter +// if (cfgGroup->publishPower) { +// cfgGroup->publishPower = false; + obj["L1"] = cfgGroup->pmPowerL1; + obj["L2"] = cfgGroup->pmPowerL2; + obj["L3"] = cfgGroup->pmPowerL3; + obj["Sum"] = cfgGroup->pmPower; + mMqtt->publish("zero/state/powermeter/P", doc.as().c_str(), false); + doc.clear(); +// } + + // if (cfgGroup->pm_Publish_W) { + // cfgGroup->pm_Publish_W = false; // obj["todo"] = "true"; - // obj["L1"] = mCfg->groups[group].pm_P1; - // obj["L2"] = mCfg->groups[group].pm_P2; - // obj["L2"] = mCfg->groups[group].pm_P3; - // obj["Sum"] = mCfg->groups[group].pm_P; + // obj["L1"] = cfgGroup->pm_P1; + // obj["L2"] = cfgGroup->pm_P2; + // obj["L2"] = cfgGroup->pm_P3; + // obj["Sum"] = cfgGroup->pm_P; // mMqtt->publish("zero/powermeter/W", doc.as().c_str(), false); // doc.clear(); // } + // Inverters for (uint8_t inv = 0; inv < ZEROEXPORT_GROUP_MAX_INVERTERS; inv++) { + zeroExportGroupInverter_t *cfgGroupInv = &cfgGroup->inverters[inv]; + gr = "zero/state/groups/" + String(group) + "/inverters/" + String(inv); + obj["enabled"] = cfgGroupInv->enabled; + obj["id"] = cfgGroupInv->id; + obj["powerMin"] = cfgGroupInv->powerMin; + obj["powerMax"] = cfgGroupInv->powerMax; + mMqtt->publish(gr.c_str(), doc.as().c_str(), false); + doc.clear(); } + + // Battery + gr = "zero/state/groups/" + String(group) + "/battery"; + obj["enabled"] = cfgGroup->battEnabled; + obj["voltageOn"] = cfgGroup->battVoltageOn; + obj["voltageOff"] = cfgGroup->battVoltageOff; + obj["switch"] = cfgGroup->battSwitch; + mMqtt->publish(gr.c_str(), doc.as().c_str(), false); + doc.clear(); + + // Advanced + gr = "zero/state/groups/" + String(group) + "/advanced"; + obj["setPoint"] = cfgGroup->setPoint; + obj["refresh"] = cfgGroup->refresh; + obj["powerTolerance"] = cfgGroup->powerTolerance; + obj["powerMax"] = cfgGroup->powerMax; + obj["Kp"] = cfgGroup->Kp; + obj["Ki"] = cfgGroup->Ki; + obj["Kd"] = cfgGroup->Kd; + mMqtt->publish(gr.c_str(), doc.as().c_str(), false); + doc.clear(); + } return true; @@ -1393,7 +1493,8 @@ class ZeroExport { * @returns true/false * @todo Hier ist noch keine Funktion */ - bool groupEmergency(zeroExportGroup_t *cfgGroup, unsigned long *tsp, bool *doLog) { + bool groupEmergency(uint8_t group, unsigned long *tsp, bool *doLog) { + zeroExportGroup_t *cfgGroup = &mCfg->groups[group]; if (mCfg->debug) mLog["t"] = "groupEmergency"; cfgGroup->lastRun = *tsp; From 37e261ddafb12596cb3be9fe87f3331fe52efa4e Mon Sep 17 00:00:00 2001 From: Patrick Amrhein Date: Sun, 31 Mar 2024 23:41:23 +0200 Subject: [PATCH 19/22] Redesign, MQTT --- src/plugins/zeroExport/zeroExport.h | 84 +++++++++++------------------ 1 file changed, 32 insertions(+), 52 deletions(-) diff --git a/src/plugins/zeroExport/zeroExport.h b/src/plugins/zeroExport/zeroExport.h index 55ea4551..41bb8b2a 100644 --- a/src/plugins/zeroExport/zeroExport.h +++ b/src/plugins/zeroExport/zeroExport.h @@ -92,20 +92,12 @@ class ZeroExport { break; case zeroExportState::WAITREFRESH: if (groupWaitRefresh(group, &Tsp, &DoLog)) { -/// cfgGroup->state = zeroExportState::GETINVERTERACKS; cfgGroup->state = zeroExportState::GETINVERTERDATA; #if defined(ZEROEXPORT_DEV_POWERMETER) cfgGroup->state = zeroExportState::GETPOWERMETER; #endif } break; -/// case zeroExportState::GETINVERTERACKS: -/// if (groupGetInverterAcks(group, &Tsp, &DoLog)) { -/// cfgGroup->state = zeroExportState::GETINVERTERDATA; -/// } else { -/// cfgGroup->sleep = 1000; -/// } -/// break; case zeroExportState::GETINVERTERDATA: if (groupGetInverterData(group, &Tsp, &DoLog)) { cfgGroup->state = zeroExportState::BATTERYPROTECTION; @@ -420,7 +412,8 @@ class ZeroExport { // "topic":"inverter/zero/set/groups/0/enabled" if (topic.indexOf("groups") != -1) { // TODO: Topicprüfung - // TODO: Topicprüfung ist 10 und 8 korrekt? Wäre es nicht besser das anhand der / rauszufiltern wenn es 2-stellige Gruppen gibt? + // TODO: Topicprüfung ist 10 und 8 korrekt? + // TODO: Wäre es nicht besser das anhand der / rauszufiltern wenn es 2-stellige Gruppen gibt? String i = topic.substring(topic.length() - 10, topic.length() - 8); uint8_t group = i.toInt(); mLog["g"] = group; @@ -443,6 +436,35 @@ class ZeroExport { } } +// Global +// - enabled + +// General +// - enabled + +// Powermeter +/// ? + +// Inverter +// - enabled +// - powerMin +// - powerMax + +// Battery +/// - enabled +/// - voltageOn +/// - voltageOff +// - switch + +// Advanced +// - setpoint +/// - refresh +// - powerTolerance +// - powerMax +/// - Kp +/// - Ki +/// - Kd + mLog["Msg"] = obj; sendLog(); clearLog(); @@ -543,49 +565,6 @@ class ZeroExport { return true; } - /** groupGetInverterAcks - * aktualisiert die Gruppe mit den ACKs der Inverter für neue Limits - * @param group - * @returns true/false - * @todo siehe code - */ -/* - bool groupGetInverterAcks(uint8_t group, unsigned long *tsp, bool *doLog) { - if (mCfg->debug) mLog["t"] = "groupGetInverterAcks"; - - mCfg->groups[group].lastRun = *tsp; - - *doLog = true; - - // Wait Acks - JsonArray logArr = mLog.createNestedArray("ix"); - bool wait = false; - for (uint8_t inv = 0; inv < ZEROEXPORT_GROUP_MAX_INVERTERS; inv++) { - JsonObject logObj = logArr.createNestedObject(); - logObj["i"] = inv; - - // Inverter not enabled or not selected -> ignore - if (NotEnabledOrNotSelected(group, inv)) continue; - - // Inverter is not available -> wait - if (!mIv[group][inv]->isAvailable()) { - logObj["a"] = false; - wait = true; - } - // waitLimitAck - if (mCfg->groups[group].inverters[inv].waitLimitAck > 0) { - logObj["wL"] = mCfg->groups[group].inverters[inv].waitLimitAck; - wait = true; - } - } - - mLog["w"] = wait; - - if (wait) return false; - return true; - } -*/ - /** groupGetInverterData * * @param group @@ -1455,6 +1434,7 @@ class ZeroExport { gr = "zero/state/groups/" + String(group) + "/inverters/" + String(inv); obj["enabled"] = cfgGroupInv->enabled; obj["id"] = cfgGroupInv->id; + obj["target"] = cfgGroupInv->target; obj["powerMin"] = cfgGroupInv->powerMin; obj["powerMax"] = cfgGroupInv->powerMax; mMqtt->publish(gr.c_str(), doc.as().c_str(), false); From 5c2e151797c255231dc1dcce5e906f0ad5f3c75d Mon Sep 17 00:00:00 2001 From: Patrick Amrhein Date: Mon, 1 Apr 2024 04:42:51 +0200 Subject: [PATCH 20/22] Bugfix: groupSetReboot --- src/config/settings.h | 2 +- src/plugins/zeroExport/zeroExport.h | 48 +++++++++++++++++------------ 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/config/settings.h b/src/config/settings.h index 891a6555..70779662 100644 --- a/src/config/settings.h +++ b/src/config/settings.h @@ -271,7 +271,7 @@ typedef struct { float dcVoltage; bool state; // - bool doReboot; + int8_t doReboot; int8_t doPower; bool doLimit; } zeroExportGroupInverter_t; diff --git a/src/plugins/zeroExport/zeroExport.h b/src/plugins/zeroExport/zeroExport.h index 41bb8b2a..b3941edd 100644 --- a/src/plugins/zeroExport/zeroExport.h +++ b/src/plugins/zeroExport/zeroExport.h @@ -20,7 +20,7 @@ template class ZeroExport { public: /** ZeroExport - * Konstruktor + * constructor */ ZeroExport() { mIsInitialized = false; @@ -32,7 +32,7 @@ class ZeroExport { } /** ~ZeroExport - * Destruktor + * destructor */ ~ZeroExport() {} @@ -1066,42 +1066,53 @@ class ZeroExport { * @returns true/false */ bool groupSetReboot(uint8_t group, unsigned long *tsp, bool *doLog) { + zeroExportGroup_t *cfgGroup = &mCfg->groups[group]; bool result = true; if (mCfg->debug) mLog["t"] = "groupSetReboot"; - mCfg->groups[group].lastRun = *tsp; + cfgGroup->lastRun = *tsp; JsonArray logArr = mLog.createNestedArray("ix"); for (uint8_t inv = 0; inv < ZEROEXPORT_GROUP_MAX_INVERTERS; inv++) { JsonObject logObj = logArr.createNestedObject(); logObj["i"] = inv; + zeroExportGroupInverter_t *cfgGroupInv = &cfgGroup->inverters[inv]; + // Inverter not enabled or not selected -> ignore if (NotEnabledOrNotSelected(group, inv)) continue; - // Reset - if ((mCfg->groups[group].inverters[inv].doReboot) && (mCfg->groups[group].inverters[inv].waitRebootAck == 0)) { + // Inverter not available -> ignore + if (!mIv[group][inv]->isAvailable()) { + logObj["a"] = false; result = false; - mCfg->groups[group].inverters[inv].doReboot = false; continue; } - // Wait - if (mCfg->groups[group].inverters[inv].waitRebootAck > 0) { + // Reset + if ((cfgGroupInv->doReboot == 2) && (cfgGroupInv->waitRebootAck == 0)) { result = false; + cfgGroupInv->doReboot = 0; + logObj["act"] = "done"; continue; } - // Inverter nothing to do -> ignore - if (!mCfg->groups[group].inverters[inv].doReboot) { + // Calculate + if (cfgGroupInv->doReboot == 1) { + cfgGroupInv->doReboot = 2; + } + + // Wait + if (cfgGroupInv->waitRebootAck > 0) { + logObj["w"] = cfgGroupInv->waitRebootAck; + result = false; continue; } - // Inverter not available -> ignore - if (!mIv[group][inv]->isAvailable()) { - result = false; - logObj["err"] = "is not Available"; + // Inverter nothing to do -> ignore + if (cfgGroupInv->doReboot == 0) { + logObj["act"] = "nothing to do"; continue; } @@ -1109,20 +1120,19 @@ class ZeroExport { *doLog = true; - logObj["act"] = String("reboot"); + if (!mCfg->debug) logObj["act"] = cfgGroupInv->doReboot; // wait for Ack - mCfg->groups[group].inverters[inv].waitRebootAck = 120; - logObj["wR"] = mCfg->groups[group].inverters[inv].waitRebootAck; + cfgGroupInv->waitRebootAck = 120; + logObj["wR"] = cfgGroupInv->waitRebootAck; // send Command DynamicJsonDocument doc(512); JsonObject obj = doc.to(); - obj["id"] = mCfg->groups[group].inverters[inv].id; + obj["id"] = cfgGroupInv->id; obj["path"] = "ctrl"; obj["cmd"] = "restart"; mApi->ctrlRequest(obj); - logObj["d"] = obj; } From bf2bd773f656279f977d481b98d3e36b49340e7f Mon Sep 17 00:00:00 2001 From: Patrick Amrhein Date: Mon, 1 Apr 2024 13:42:07 +0200 Subject: [PATCH 21/22] =?UTF-8?q?Bugfix:=20Fallschirm=20(Keine=20Daten?= =?UTF-8?q?=C3=BCbernahme=20solange=20waitForAck=20l=C3=A4uft)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/zeroExport/zeroExport.h | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/plugins/zeroExport/zeroExport.h b/src/plugins/zeroExport/zeroExport.h index b3941edd..8b8c5475 100644 --- a/src/plugins/zeroExport/zeroExport.h +++ b/src/plugins/zeroExport/zeroExport.h @@ -367,9 +367,28 @@ class ZeroExport { for (uint8_t group = 0; group < ZEROEXPORT_MAX_GROUPS; group++) { for (uint8_t inv = 0; inv < ZEROEXPORT_GROUP_MAX_INVERTERS; inv++) { + // Keine Datenübernahme wenn falscher Inverter + if (iv->id != mCfg->groups[group].inverters[inv].id) continue; + + // Keine Datenübernahme wenn nicht enabled if (!mCfg->groups[group].inverters[inv].enabled) continue; - if (iv->id != mCfg->groups[group].inverters[inv].id) continue; + // Keine Datenübernahme wenn setReboot läuft + if (mCfg->groups[group].inverters[inv].waitRebootAck > 0) continue; + + // Keine Datenübernahme wenn setPower läuft + if (mCfg->groups[group].inverters[inv].waitPowerAck > 0) continue; + + // Keine Datenübernahme wenn setLimit läuft + if (mCfg->groups[group].inverters[inv].waitLimitAck > 0) continue; + + int32_t ivLp = iv->actPowerLimit; + int32_t ivPm = iv->getMaxPower();; + int32_t ivL = (ivPm * ivLp) / 100; + int32_t zeL = mCfg->groups[group].inverters[inv].limit; + + // Keine Datenübernahme wenn zeL gleich ivL + if (zeL == ivL) continue; unsigned long bTsp = millis(); @@ -377,11 +396,10 @@ class ZeroExport { mLog["g"] = group; mLog["i"] = inv; mLog["id"] = iv->id; - mLog["ivL%"] = iv->actPowerLimit; - mLog["ivPm"] = iv->getMaxPower(); - uint16_t ivL = (iv->getMaxPower() * iv->actPowerLimit) / 100; + mLog["ivL%"] = ivLp; + mLog["ivPm"] = ivPm; mLog["ivL"] = ivL; - mLog["zeL"] = (uint16_t)mCfg->groups[group].inverters[inv].limit; + mLog["zeL"] = zeL; mCfg->groups[group].inverters[inv].limit = ivL; if (mCfg->debug) { From d66d975d4fec2d05e090dbdb67e47dda69dc7a51 Mon Sep 17 00:00:00 2001 From: Patrick Amrhein Date: Mon, 1 Apr 2024 18:20:03 +0200 Subject: [PATCH 22/22] 0.8.970010 --- src/config/settings.h | 97 ++++------- src/defines.h | 2 +- src/plugins/zeroExport/zeroExport.h | 258 +++++++++++++++------------- 3 files changed, 177 insertions(+), 180 deletions(-) diff --git a/src/config/settings.h b/src/config/settings.h index 70779662..a888d5ac 100644 --- a/src/config/settings.h +++ b/src/config/settings.h @@ -188,7 +188,7 @@ typedef struct { // Plugin ZeroExport #if defined(PLUGIN_ZEROEXPORT) -#define ZEROEXPORT_DEV_POWERMETER +//#define ZEROEXPORT_DEV_POWERMETER #define ZEROEXPORT_MAX_GROUPS 6 #define ZEROEXPORT_GROUP_MAX_LEN_NAME 25 #define ZEROEXPORT_GROUP_MAX_LEN_PM_URL 100 @@ -227,22 +227,7 @@ typedef enum { Hichi = 4, Tibber = 5, } zeroExportPowermeterType_t; -/* -typedef struct { - uint8_t type; - uint8_t ip; - uint8_t url; - bool login; - uint8_t username; - uint8_t password; - uint8_t group; - uint8_t phase; - uint16_t nextRun; - uint16_t interval; - uint8_t error; - uint16_t power; -} zeroExportPowermeter_t; -*/ + typedef enum { Sum = 0, L1 = 1, @@ -261,9 +246,9 @@ typedef struct { uint16_t powerMax; // - float power; - float limit; - float limitNew; + int32_t power; + int32_t limit; + int32_t limitNew; uint8_t waitLimitAck; uint8_t waitPowerAck; uint8_t waitRebootAck; @@ -279,6 +264,7 @@ typedef struct { typedef struct { // General bool enabled; + bool sleep; char name[ZEROEXPORT_GROUP_MAX_LEN_NAME]; // Powermeter uint8_t pm_type; @@ -303,50 +289,37 @@ typedef struct { // zeroExportState stateNext; unsigned long lastRun; unsigned long lastRefresh; - uint16_t sleep; - - float eSum; - float eSum1; - float eSum2; - float eSum3; - float eOld; - float eOld1; - float eOld2; - float eOld3; - float Kp; - float Ki; - float Kd; + uint16_t wait; -//float pm_P[5]; -//float pm_P1[5]; -//float pm_P2[5]; -//float pm_P3[5]; -//uint8_t pm_iIn = 0; -//uint8_t pm_iOut = 0; - - float pmPower; - float pmPowerL1; - float pmPowerL2; - float pmPowerL3; + int32_t pm_P; + int32_t pm_P1; + int32_t pm_P2; + int32_t pm_P3; bool publishPower = false; bool battSwitch; - float grpPower; - float grpPowerL1; - float grpPowerL2; - float grpPowerL3; -// float grpLimit; -// float grpLimitL1; -// float grpLimitL2; -// float grpLimitL3; - -// uint16_t power; // Aktueller Verbrauch -// uint16_t powerLimitAkt; // Aktuelles Limit -// uint16_t powerHyst; // Hysterese + + // PID controller + int32_t eSum; + int32_t eSum1; + int32_t eSum2; + int32_t eSum3; + int32_t eOld; + int32_t eOld1; + int32_t eOld2; + int32_t eOld3; + float Kp; + float Ki; + float Kd; + int32_t y; + int32_t y1; + int32_t y2; + int32_t y3; } zeroExportGroup_t; typedef struct { bool enabled; + bool sleep; bool log_over_webserial; bool log_over_mqtt; bool debug; @@ -675,12 +648,14 @@ class settings { // Plugin ZeroExport #if defined(PLUGIN_ZEROEXPORT) mCfg.plugin.zeroExport.enabled = false; + mCfg.plugin.zeroExport.sleep = false; mCfg.plugin.zeroExport.log_over_webserial = false; mCfg.plugin.zeroExport.log_over_mqtt = false; mCfg.plugin.zeroExport.debug = false; for(uint8_t group = 0; group < ZEROEXPORT_MAX_GROUPS; group++) { // General mCfg.plugin.zeroExport.groups[group].enabled = false; + mCfg.plugin.zeroExport.groups[group].sleep = false; snprintf(mCfg.plugin.zeroExport.groups[group].name, ZEROEXPORT_GROUP_MAX_LEN_NAME, "%s", DEF_ZEXPORT); // Powermeter mCfg.plugin.zeroExport.groups[group].pm_type = zeroExportPowermeterType_t::None; @@ -719,11 +694,11 @@ class settings { mCfg.plugin.zeroExport.groups[group].state = zeroExportState::INIT; mCfg.plugin.zeroExport.groups[group].lastRun = 0; mCfg.plugin.zeroExport.groups[group].lastRefresh = 0; - mCfg.plugin.zeroExport.groups[group].sleep = 0; - mCfg.plugin.zeroExport.groups[group].pmPower = 0; - mCfg.plugin.zeroExport.groups[group].pmPowerL1 = 0; - mCfg.plugin.zeroExport.groups[group].pmPowerL2 = 0; - mCfg.plugin.zeroExport.groups[group].pmPowerL3 = 0; + mCfg.plugin.zeroExport.groups[group].wait = 0; + mCfg.plugin.zeroExport.groups[group].pm_P = 0; + mCfg.plugin.zeroExport.groups[group].pm_P1 = 0; + mCfg.plugin.zeroExport.groups[group].pm_P2 = 0; + mCfg.plugin.zeroExport.groups[group].pm_P3 = 0; mCfg.plugin.zeroExport.groups[group].battSwitch = false; } diff --git a/src/defines.h b/src/defines.h index 4b3e7d43..6a92fbe9 100644 --- a/src/defines.h +++ b/src/defines.h @@ -13,7 +13,7 @@ //------------------------------------- #define VERSION_MAJOR 0 #define VERSION_MINOR 8 -#define VERSION_PATCH 970009 +#define VERSION_PATCH 970010 //------------------------------------- typedef struct { diff --git a/src/plugins/zeroExport/zeroExport.h b/src/plugins/zeroExport/zeroExport.h index 8b8c5475..10ffa31f 100644 --- a/src/plugins/zeroExport/zeroExport.h +++ b/src/plugins/zeroExport/zeroExport.h @@ -69,9 +69,9 @@ class ZeroExport { if (!cfgGroup->enabled) continue; - // sleep - if (Tsp <= (cfgGroup->lastRun + cfgGroup->sleep)) continue; - cfgGroup->sleep = 0; + // wait + if (Tsp <= (cfgGroup->lastRun + cfgGroup->wait)) continue; + cfgGroup->wait = 0; mLog["g"] = group; mLog["s"] = (uint8_t)cfgGroup->state; @@ -80,13 +80,13 @@ class ZeroExport { case zeroExportState::INIT: if (groupInit(group, &Tsp, &DoLog)) { cfgGroup->state = zeroExportState::WAITREFRESH; - cfgGroup->sleep = 60000; + cfgGroup->wait = 60000; } else { cfgGroup->state = zeroExportState::INIT; - cfgGroup->sleep = 60000; + cfgGroup->wait = 60000; #if defined(ZEROEXPORT_DEV_POWERMETER) cfgGroup->state = zeroExportState::WAITREFRESH; - cfgGroup->sleep = 10000; + cfgGroup->wait = 10000; #endif } break; @@ -102,14 +102,14 @@ class ZeroExport { if (groupGetInverterData(group, &Tsp, &DoLog)) { cfgGroup->state = zeroExportState::BATTERYPROTECTION; } else { - cfgGroup->sleep = 500; + cfgGroup->wait = 500; } break; case zeroExportState::BATTERYPROTECTION: if (groupBatteryprotection(group, &Tsp, &DoLog)) { cfgGroup->state = zeroExportState::GETPOWERMETER; } else { - cfgGroup->sleep = 1000; + cfgGroup->wait = 1000; } break; case zeroExportState::GETPOWERMETER: @@ -120,7 +120,7 @@ class ZeroExport { cfgGroup->state = zeroExportState::PUBLISH; #endif } else { - cfgGroup->sleep = 3000; + cfgGroup->wait = 3000; } break; case zeroExportState::CONTROLLER: @@ -135,21 +135,21 @@ class ZeroExport { if (groupPrognose(group, &Tsp, &DoLog)) { cfgGroup->state = zeroExportState::AUFTEILEN; } else { - cfgGroup->sleep = 500; + cfgGroup->wait = 500; } break; case zeroExportState::AUFTEILEN: if (groupAufteilen(group, &Tsp, &DoLog)) { cfgGroup->state = zeroExportState::SETREBOOT; } else { - cfgGroup->sleep = 500; + cfgGroup->wait = 500; } break; case zeroExportState::SETREBOOT: if (groupSetReboot(group, &Tsp, &DoLog)) { cfgGroup->state = zeroExportState::SETPOWER; } else { - cfgGroup->sleep = 1000; + cfgGroup->wait = 1000; } break; case zeroExportState::SETPOWER: @@ -157,14 +157,14 @@ class ZeroExport { cfgGroup->lastRefresh = Tsp; cfgGroup->state = zeroExportState::SETLIMIT; } else { - cfgGroup->sleep = 1000; + cfgGroup->wait = 1000; } break; case zeroExportState::SETLIMIT: if (groupSetLimit(group, &Tsp, &DoLog)) { cfgGroup->state = zeroExportState::PUBLISH; } else { - cfgGroup->sleep = 1000; + cfgGroup->wait = 1000; } break; case zeroExportState::PUBLISH: @@ -187,7 +187,7 @@ class ZeroExport { default: cfgGroup->lastRun = Tsp; cfgGroup->lastRefresh = Tsp; - cfgGroup->sleep = 1000; + cfgGroup->wait = 1000; cfgGroup->state = zeroExportState::INIT; break; } @@ -367,7 +367,7 @@ class ZeroExport { for (uint8_t group = 0; group < ZEROEXPORT_MAX_GROUPS; group++) { for (uint8_t inv = 0; inv < ZEROEXPORT_GROUP_MAX_INVERTERS; inv++) { - // Keine Datenübernahme wenn falscher Inverter + // Wrong Inverter -> ignore if (iv->id != mCfg->groups[group].inverters[inv].id) continue; // Keine Datenübernahme wenn nicht enabled @@ -382,6 +382,7 @@ class ZeroExport { // Keine Datenübernahme wenn setLimit läuft if (mCfg->groups[group].inverters[inv].waitLimitAck > 0) continue; + // Calculate int32_t ivLp = iv->actPowerLimit; int32_t ivPm = iv->getMaxPower();; int32_t ivL = (ivPm * ivLp) / 100; @@ -427,6 +428,28 @@ class ZeroExport { mLog["t"] = "onMqttMessage"; if (obj["path"] == "zero" && obj["cmd"] == "set") { +/* + // "topic":"???/zero/set/enabled" + if (topic.indexOf("zero/set/enabled") != -1) { + + } + else + // "topic":"???/zero/set/sleep" + if (topic.indexOf("zero/set/sleep") != -1) { + + } + else + // "topic":"???/zero/set/groups/0/enabled" + if (topic.indexOf("zero/set/groups") != -1) { + String i = topic.substring(topic.length() - 10, topic.length() - 8); + uint8_t group = i.toInt(); + mLog["g"] = group; + if (topic.indexOf("enabled") != -1) { + } + if (topic.indexOf("zero/set/groups") != -1) { + } + } +*/ // "topic":"inverter/zero/set/groups/0/enabled" if (topic.indexOf("groups") != -1) { // TODO: Topicprüfung @@ -766,23 +789,23 @@ class ZeroExport { *doLog = true; - mCfg->groups[group].pmPower = mPowermeter.getDataAVG(group).P; - mCfg->groups[group].pmPowerL1 = mPowermeter.getDataAVG(group).P1; - mCfg->groups[group].pmPowerL2 = mPowermeter.getDataAVG(group).P2; - mCfg->groups[group].pmPowerL3 = mPowermeter.getDataAVG(group).P3; + mCfg->groups[group].pm_P = mPowermeter.getDataAVG(group).P; + mCfg->groups[group].pm_P1 = mPowermeter.getDataAVG(group).P1; + mCfg->groups[group].pm_P2 = mPowermeter.getDataAVG(group).P2; + mCfg->groups[group].pm_P3 = mPowermeter.getDataAVG(group).P3; if ( - (mCfg->groups[group].pmPower == 0) && - (mCfg->groups[group].pmPowerL1 == 0) && - (mCfg->groups[group].pmPowerL2 == 0) && - (mCfg->groups[group].pmPowerL3 == 0)) { + (mCfg->groups[group].pm_P == 0) && + (mCfg->groups[group].pm_P1 == 0) && + (mCfg->groups[group].pm_P2 == 0) && + (mCfg->groups[group].pm_P3 == 0)) { return false; } - mLog["P"] = mCfg->groups[group].pmPower; - mLog["P1"] = mCfg->groups[group].pmPowerL1; - mLog["P2"] = mCfg->groups[group].pmPowerL2; - mLog["P3"] = mCfg->groups[group].pmPowerL3; + mLog["P"] = mCfg->groups[group].pm_P; + mLog["P1"] = mCfg->groups[group].pm_P1; + mLog["P2"] = mCfg->groups[group].pm_P2; + mLog["P3"] = mCfg->groups[group].pm_P3; return true; } @@ -793,113 +816,115 @@ class ZeroExport { * @returns true/false */ bool groupController(uint8_t group, unsigned long *tsp, bool *doLog) { + zeroExportGroup_t *cfgGroup = &mCfg->groups[group]; + if (mCfg->debug) mLog["t"] = "groupController"; - mCfg->groups[group].lastRun = *tsp; + cfgGroup->lastRun = *tsp; *doLog = true; // Führungsgröße w in Watt - float w = mCfg->groups[group].setPoint; + int32_t w = cfgGroup->setPoint; mLog["w"] = w; // Regelgröße x in Watt - float x = mCfg->groups[group].pmPower; - float x1 = mCfg->groups[group].pmPowerL1; - float x2 = mCfg->groups[group].pmPowerL2; - float x3 = mCfg->groups[group].pmPowerL3; + int32_t x = cfgGroup->pm_P; + int32_t x1 = cfgGroup->pm_P1; + int32_t x2 = cfgGroup->pm_P2; + int32_t x3 = cfgGroup->pm_P3; mLog["x"] = x; mLog["x1"] = x1; mLog["x2"] = x2; mLog["x3"] = x3; // Regelabweichung e in Watt - float e = w - x; - float e1 = w - x1; - float e2 = w - x2; - float e3 = w - x3; + int32_t e = w - x; + int32_t e1 = w - x1; + int32_t e2 = w - x2; + int32_t e3 = w - x3; mLog["e"] = e; mLog["e1"] = e1; mLog["e2"] = e2; mLog["e3"] = e3; if ( - (e < mCfg->groups[group].powerTolerance) && (e > -mCfg->groups[group].powerTolerance) && - (e1 < mCfg->groups[group].powerTolerance) && (e1 > -mCfg->groups[group].powerTolerance) && - (e2 < mCfg->groups[group].powerTolerance) && (e2 > -mCfg->groups[group].powerTolerance) && - (e3 < mCfg->groups[group].powerTolerance) && (e3 > -mCfg->groups[group].powerTolerance)) { - mLog["tol"] = mCfg->groups[group].powerTolerance; + (e < cfgGroup->powerTolerance) && (e > -cfgGroup->powerTolerance) && + (e1 < cfgGroup->powerTolerance) && (e1 > -cfgGroup->powerTolerance) && + (e2 < cfgGroup->powerTolerance) && (e2 > -cfgGroup->powerTolerance) && + (e3 < cfgGroup->powerTolerance) && (e3 > -cfgGroup->powerTolerance)) { + mLog["tol"] = cfgGroup->powerTolerance; return false; } // Regler - float Kp = mCfg->groups[group].Kp; - float Ki = mCfg->groups[group].Ki; - float Kd = mCfg->groups[group].Kd; + float Kp = cfgGroup->Kp; + float Ki = cfgGroup->Ki; + float Kd = cfgGroup->Kd; unsigned long Ta = *tsp - mCfg->groups[group].lastRefresh; mLog["Kp"] = Kp; mLog["Ki"] = Ki; mLog["Kd"] = Kd; mLog["Ta"] = Ta; // - P-Anteil - float yP = Kp * e; - float yP1 = Kp * e1; - float yP2 = Kp * e2; - float yP3 = Kp * e3; + int32_t yP = Kp * e; + int32_t yP1 = Kp * e1; + int32_t yP2 = Kp * e2; + int32_t yP3 = Kp * e3; mLog["yP"] = yP; mLog["yP1"] = yP1; mLog["yP2"] = yP2; mLog["yP3"] = yP3; // - I-Anteil - mCfg->groups[group].eSum += e; - mCfg->groups[group].eSum1 += e1; - mCfg->groups[group].eSum2 += e2; - mCfg->groups[group].eSum3 += e3; - mLog["esum"] = mCfg->groups[group].eSum; - mLog["esum1"] = mCfg->groups[group].eSum1; - mLog["esum2"] = mCfg->groups[group].eSum2; - mLog["esum3"] = mCfg->groups[group].eSum3; - float yI = Ki * Ta * mCfg->groups[group].eSum; - float yI1 = Ki * Ta * mCfg->groups[group].eSum1; - float yI2 = Ki * Ta * mCfg->groups[group].eSum2; - float yI3 = Ki * Ta * mCfg->groups[group].eSum3; + cfgGroup->eSum += e; + cfgGroup->eSum1 += e1; + cfgGroup->eSum2 += e2; + cfgGroup->eSum3 += e3; + mLog["esum"] = cfgGroup->eSum; + mLog["esum1"] = cfgGroup->eSum1; + mLog["esum2"] = cfgGroup->eSum2; + mLog["esum3"] = cfgGroup->eSum3; + int32_t yI = Ki * Ta * cfgGroup->eSum; + int32_t yI1 = Ki * Ta * cfgGroup->eSum1; + int32_t yI2 = Ki * Ta * cfgGroup->eSum2; + int32_t yI3 = Ki * Ta * cfgGroup->eSum3; mLog["yI"] = yI; mLog["yI1"] = yI1; mLog["yI2"] = yI2; mLog["yI3"] = yI3; // - D-Anteil - mLog["ealt"] = mCfg->groups[group].eOld; - mLog["ealt1"] = mCfg->groups[group].eOld1; - mLog["ealt2"] = mCfg->groups[group].eOld2; - mLog["ealt3"] = mCfg->groups[group].eOld3; - float yD = Kd * (e - mCfg->groups[group].eOld) / Ta; - float yD1 = Kd * (e1 - mCfg->groups[group].eOld1) / Ta; - float yD2 = Kd * (e2 - mCfg->groups[group].eOld2) / Ta; - float yD3 = Kd * (e3 - mCfg->groups[group].eOld3) / Ta; + mLog["ealt"] = cfgGroup->eOld; + mLog["ealt1"] = cfgGroup->eOld1; + mLog["ealt2"] = cfgGroup->eOld2; + mLog["ealt3"] = cfgGroup->eOld3; + int32_t yD = Kd * (e - cfgGroup->eOld) / Ta; + int32_t yD1 = Kd * (e1 - cfgGroup->eOld1) / Ta; + int32_t yD2 = Kd * (e2 - cfgGroup->eOld2) / Ta; + int32_t yD3 = Kd * (e3 - cfgGroup->eOld3) / Ta; mLog["yD"] = yD; mLog["yD1"] = yD1; mLog["yD2"] = yD2; mLog["yD3"] = yD3; - mCfg->groups[group].eOld = e; - mCfg->groups[group].eOld1 = e1; - mCfg->groups[group].eOld2 = e2; - mCfg->groups[group].eOld3 = e3; + cfgGroup->eOld = e; + cfgGroup->eOld1 = e1; + cfgGroup->eOld2 = e2; + cfgGroup->eOld3 = e3; // - PID-Anteil - float y = yP + yI + yD; - float y1 = yP1 + yI1 + yD1; - float y2 = yP2 + yI2 + yD2; - float y3 = yP3 + yI3 + yD3; + int32_t y = yP + yI + yD; + int32_t y1 = yP1 + yI1 + yD1; + int32_t y2 = yP2 + yI2 + yD2; + int32_t y3 = yP3 + yI3 + yD3; // Regelbegrenzung // TODO: Hier könnte man den maximalen Sprung begrenzen + cfgGroup->y = y; + cfgGroup->y1 = y1; + cfgGroup->y2 = y2; + cfgGroup->y3 = y3; mLog["y"] = y; mLog["y1"] = y1; mLog["y2"] = y2; mLog["y3"] = y3; - mCfg->groups[group].grpPower = y; - mCfg->groups[group].grpPowerL1 = y1; - mCfg->groups[group].grpPowerL2 = y2; - mCfg->groups[group].grpPowerL3 = y3; return true; } @@ -931,10 +956,10 @@ class ZeroExport { *doLog = true; - float y = mCfg->groups[group].grpPower; - float y1 = mCfg->groups[group].grpPowerL1; - float y2 = mCfg->groups[group].grpPowerL2; - float y3 = mCfg->groups[group].grpPowerL3; + float y = mCfg->groups[group].y; + float y1 = mCfg->groups[group].y1; + float y2 = mCfg->groups[group].y2; + float y3 = mCfg->groups[group].y3; // TDOD: nochmal durchdenken ... es muss für Sum und L1-3 sein // uint16_t groupPmax = 0; @@ -965,34 +990,20 @@ class ZeroExport { continue; } - // if ((cfgGroup->battSwitch) && (!cfgGroupInv->state)) { - // setPower(&logObj, group, inv, true); - // continue; - // } - - // if ((!cfgGroup->battSwitch) && (cfgGroupInv->state)) { - // setPower(&logObj, group, inv, false); - // continue; - // } - - // if (!cfgGroupInv->state) { - // continue; - // } - record_t<> *rec; rec = mIv[group][inv]->getRecordStruct(RealTimeRunData_Debug); cfgGroupInv->power = mIv[group][inv]->getChannelFieldValue(CH0, FLD_PAC, rec); - if ((uint16_t)cfgGroupInv->power < ivPmin[cfgGroupInv->target]) { + if (cfgGroupInv->power < ivPmin[cfgGroupInv->target]) { grpTarget[cfgGroupInv->target] = true; - ivPmin[cfgGroupInv->target] = (uint16_t)cfgGroupInv->power; + ivPmin[cfgGroupInv->target] = cfgGroupInv->power; ivId_Pmin[cfgGroupInv->target] = inv; // Hier kein return oder continue sonst dauerreboot } - if ((uint16_t)cfgGroupInv->power > ivPmax[cfgGroupInv->target]) { + if (cfgGroupInv->power > ivPmax[cfgGroupInv->target]) { grpTarget[cfgGroupInv->target] = true; - ivPmax[cfgGroupInv->target] = (uint16_t)cfgGroupInv->power; + ivPmax[cfgGroupInv->target] = cfgGroupInv->power; ivId_Pmax[cfgGroupInv->target] = inv; // Hier kein return oder continue sonst dauerreboot } @@ -1007,22 +1018,22 @@ class ZeroExport { } mLog[String(String("10") + String(i))] = String(i); - float *deltaP; + int32_t *deltaP; switch (i) { case 6: case 3: - deltaP = &mCfg->groups[group].grpPowerL3; + deltaP = &mCfg->groups[group].y3; break; case 5: case 2: - deltaP = &mCfg->groups[group].grpPowerL2; + deltaP = &mCfg->groups[group].y2; break; case 4: case 1: - deltaP = &mCfg->groups[group].grpPowerL1; + deltaP = &mCfg->groups[group].y1; break; case 0: - deltaP = &mCfg->groups[group].grpPower; + deltaP = &mCfg->groups[group].y; break; } @@ -1110,7 +1121,7 @@ class ZeroExport { // Reset if ((cfgGroupInv->doReboot == 2) && (cfgGroupInv->waitRebootAck == 0)) { - result = false; +/// result = false; cfgGroupInv->doReboot = 0; logObj["act"] = "done"; continue; @@ -1191,7 +1202,7 @@ class ZeroExport { // Reset if ((cfgGroupInv->doPower != -1) && (cfgGroupInv->waitPowerAck == 0)) { - result = false; +/// result = false; cfgGroupInv->doPower = -1; logObj["act"] = "done"; continue; @@ -1290,7 +1301,7 @@ class ZeroExport { // Reset if ((cfgGroupInv->doLimit) && (cfgGroupInv->waitLimitAck == 0)) { - result = false; +/// result = false; cfgGroupInv->doLimit = false; logObj["act"] = "done"; continue; @@ -1345,7 +1356,7 @@ class ZeroExport { // } if (cfgGroupInv->limit == cfgGroupInv->limitNew) { - logObj["act"] = "nothing to do"; +/// logObj["act"] = "nothing to do"; continue; } @@ -1404,11 +1415,17 @@ class ZeroExport { // TODO: Global wird fälschlicherweise hier je nach anzahl der aktivierten Gruppen bis zu 6x ausgeführt. mMqtt->publish("zero/set/enabled", ((mCfg->enabled) ? dict[STR_TRUE] : dict[STR_FALSE]), false); mMqtt->subscribe("zero/set/enabled", QOS_2); +// TODO: Global wird fälschlicherweise hier je nach anzahl der aktivierten Gruppen bis zu 6x ausgeführt. + mMqtt->publish("zero/set/sleep", ((mCfg->sleep) ? dict[STR_TRUE] : dict[STR_FALSE]), false); + mMqtt->subscribe("zero/set/sleep", QOS_2); // General gr = "zero/set/groups/" + String(group) + "/enabled"; mMqtt->publish(gr.c_str(), ((cfgGroup->enabled) ? dict[STR_TRUE] : dict[STR_FALSE]), false); mMqtt->subscribe(gr.c_str(), QOS_2); + gr = "zero/set/groups/" + String(group) + "/sleep"; + mMqtt->publish(gr.c_str(), ((cfgGroup->sleep) ? dict[STR_TRUE] : dict[STR_FALSE]), false); + mMqtt->subscribe(gr.c_str(), QOS_2); // Powermeter // TODO: fehlt @@ -1426,21 +1443,26 @@ class ZeroExport { // Global (zeroExport) // TODO: Global wird fälschlicherweise hier je nach anzahl der aktivierten Gruppen bis zu 6x ausgeführt. mMqtt->publish("zero/state/enabled", ((mCfg->enabled) ? dict[STR_TRUE] : dict[STR_FALSE]), false); +// TODO: Global wird fälschlicherweise hier je nach anzahl der aktivierten Gruppen bis zu 6x ausgeführt. + mMqtt->publish("zero/state/sleep", ((mCfg->sleep) ? dict[STR_TRUE] : dict[STR_FALSE]), false); // General gr = "zero/state/groups/" + String(group) + "/enabled"; mMqtt->publish(gr.c_str(), ((cfgGroup->enabled) ? dict[STR_TRUE] : dict[STR_FALSE]), false); + gr = "zero/state/groups/" + String(group) + "/sleep"; + mMqtt->publish(gr.c_str(), ((cfgGroup->sleep) ? dict[STR_TRUE] : dict[STR_FALSE]), false); + gr = "zero/state/groups/" + String(group) + "/name"; mMqtt->publish(gr.c_str(), cfgGroup->name, false); // Powermeter // if (cfgGroup->publishPower) { // cfgGroup->publishPower = false; - obj["L1"] = cfgGroup->pmPowerL1; - obj["L2"] = cfgGroup->pmPowerL2; - obj["L3"] = cfgGroup->pmPowerL3; - obj["Sum"] = cfgGroup->pmPower; + obj["Sum"] = cfgGroup->pm_P; + obj["L1"] = cfgGroup->pm_P1; + obj["L2"] = cfgGroup->pm_P2; + obj["L3"] = cfgGroup->pm_P3; mMqtt->publish("zero/state/powermeter/P", doc.as().c_str(), false); doc.clear(); // } @@ -1448,10 +1470,10 @@ class ZeroExport { // if (cfgGroup->pm_Publish_W) { // cfgGroup->pm_Publish_W = false; // obj["todo"] = "true"; + // obj["Sum"] = cfgGroup->pm_P; // obj["L1"] = cfgGroup->pm_P1; // obj["L2"] = cfgGroup->pm_P2; // obj["L2"] = cfgGroup->pm_P3; - // obj["Sum"] = cfgGroup->pm_P; // mMqtt->publish("zero/powermeter/W", doc.as().c_str(), false); // doc.clear(); // }