diff --git a/src/app.cpp b/src/app.cpp index 4fb5d66e..f110398d 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -126,7 +126,7 @@ void app::setup() { #if defined(PLUGIN_ZEROEXPORT) // TODO: aufräumen // if (mConfig->plugin.zeroExport.enabled) { - mZeroExport.setup(&mConfig->plugin.zeroExport, &mSys, mConfig, &mApi); + mZeroExport.setup(&mConfig->plugin.zeroExport, &mSys, mConfig, &mApi, &mMqtt); // } #endif // Plugin ZeroExport - Ende diff --git a/src/config/settings.h b/src/config/settings.h index 89972dc5..214f42f2 100644 --- a/src/config/settings.h +++ b/src/config/settings.h @@ -241,15 +241,14 @@ typedef struct { int8_t target; uint16_t powerMin; uint16_t powerMax; - + // float power; uint16_t limit; uint16_t limitNew; unsigned long limitTsp; -// bool limitAck; float dcVoltage; -// uint16_t waitingTime; + bool state; } zeroExportGroupInverter_t; typedef struct { @@ -273,7 +272,7 @@ typedef struct { uint8_t refresh; uint8_t powerTolerance; uint16_t powerMax; - + // zeroExportState state; zeroExportState stateNext; @@ -287,10 +286,10 @@ typedef struct { float grpPowerL1; float grpPowerL2; float grpPowerL3; - float grpLimit; - float grpLimitL1; - float grpLimitL2; - float grpLimitL3; +// float grpLimit; +// float grpLimitL1; +// float grpLimitL2; +// float grpLimitL3; // uint16_t power; // Aktueller Verbrauch // uint16_t powerLimitAkt; // Aktuelles Limit diff --git a/src/defines.h b/src/defines.h index f0e4e20e..2a2d5d74 100644 --- a/src/defines.h +++ b/src/defines.h @@ -13,7 +13,7 @@ //------------------------------------- #define VERSION_MAJOR 0 #define VERSION_MINOR 8 -#define VERSION_PATCH 910004 +#define VERSION_PATCH 910006 //------------------------------------- typedef struct { diff --git a/src/plugins/zeroExport/zeroExport.h b/src/plugins/zeroExport/zeroExport.h index b77a36c4..8e7c3538 100644 --- a/src/plugins/zeroExport/zeroExport.h +++ b/src/plugins/zeroExport/zeroExport.h @@ -11,7 +11,7 @@ template -// TODO: Anbindung an MQTT für Logausgabe zuzüglich DBG-Ausgabe in json. Deshalb alle Debugausgaben ersetzten durch json, dazu sollte ein jsonObject an die Funktion übergeben werden, zu dem die Funktion dann ihren Teil hinzufügt. +// TODO: Anbindung an MQTT für Logausgabe. // TODO: Powermeter erweitern // TODO: Der Teil der noch in app.pp steckt komplett hier in die Funktion verschieben. @@ -19,20 +19,35 @@ class ZeroExport { public: + /** + * ZeroExport + */ ZeroExport() { mIsInitialized = false; } - void setup(zeroExport_t *cfg, HMSYSTEM *sys, settings_t *config, RestApiType *api) { + /** + * ~ZeroExport + */ + ~ZeroExport() {} + + /** + * setup + */ + void setup(zeroExport_t *cfg, HMSYSTEM *sys, settings_t *config, RestApiType *api, PubMqttType *mqtt) { mCfg = cfg; mSys = sys; mConfig = config; mApi = api; + mMqtt = mqtt; // TODO: Sicherheitsreturn weil noch Sicherheitsfunktionen fehlen. // mIsInitialized = true; } + /** + * loop + */ void loop(void) { if ((!mIsInitialized) || (!mCfg->enabled)) { return; @@ -115,8 +130,12 @@ return; } break; default: - DBGPRINT(String("ze: ")); - DBGPRINTLN(mDocLog.as()); +// TODO: Debug Webserial. Deaktiviert um CPU-Last zu verringern. + DBGPRINTLN(String("ze: ")); +// DBGPRINTLN(mDocLog.as()); + if (mMqtt->isConnected()) { + mMqtt->publish("ze", mDocLog.as().c_str(), false); + } mDocLog.clear(); if (mCfg->groups[group].stateNext != mCfg->groups[group].state) { mCfg->groups[group].state = mCfg->groups[group].stateNext; @@ -129,6 +148,9 @@ return; } } + /** + * tickerSecond + */ void tickerSecond() { // TODO: Warten ob benötigt, ggf ein bit setzen, das in der loop() abgearbeitet wird. if ((!mIsInitialized) || (!mCfg->enabled)) { @@ -136,57 +158,6 @@ return; } } - - - - -/* -// TODO: Inverter sortieren nach Leistung -// -> Aufsteigend bei Leistungserhöhung -// -> Absteigend bei Leistungsreduzierung - for (uint8_t inv = 0; inv < ZEROEXPORT_GROUP_MAX_INVERTERS; inv++) { - if (!mCfg->groups[group].inverters[inv].enabled) { - continue; - } - if (mCfg->groups[group].inverters[inv].waitingTime) { - mCfg->groups[group].inverters[inv].waitingTime--; - continue; - } - // Leistung erhöhen - if (mCfg->groups[group].power < mCfg->groups[group].powerLimitAkt - mCfg->groups[group].powerHyst) { -// mCfg->groups[group].powerLimitAkt = mCfg->groups[group].power - - - mCfg->groups[group].inverters[inv].waitingTime = ZEROEXPORT_DEF_INV_WAITINGTIME_MS; - return; - } - // Leistung reduzieren - if (mCfg->groups[group].power > mCfg->groups[group].powerLimitAkt + mCfg->groups[group].powerHyst) { - - - - mCfg->groups[group].inverters[inv].waitingTime = ZEROEXPORT_DEF_INV_WAITINGTIME_MS; - return; - } - - - - if ((Power < PowerLimit - Hyst) || (Power > PowerLimit + Hyst)) { - if (Limit < 2%) { - setPower(Off); - setPowerLimit(100%) - } else { - setPower(On); - setPowerLimit(Limit); - mCfg->Inv[inv].waitingTime = ZEROEXPORT_DEF_INV_WAITINGTIME_MS; - } - } - - - - } -*/ - private: /* // TODO: Vorlage für nachfolgende Funktion getPowermeterWatts. Funktionen erst zusammenführen, wenn keine weiteren Powermeter mehr kommen. @@ -297,7 +268,10 @@ return; return result; } - int getPowermeterWattsShelly(JsonObject logObj, uint8_t group) { + /** + * getPowermeterWattsShelly + */ + bool getPowermeterWattsShelly(JsonObject logObj, uint8_t group) { bool result = false; mCfg->groups[group].pmPower = 0; @@ -427,7 +401,10 @@ return; return result; } - int getPowermeterWattsTasmota(JsonObject logObj, uint8_t group) { + /** + * getPowermeterWattsTasmota + */ + bool getPowermeterWattsTasmota(JsonObject logObj, uint8_t group) { // TODO: nicht komplett bool ret = false; @@ -484,7 +461,10 @@ return; return ret; } - int getPowermeterWattsMqtt(JsonObject logObj, uint8_t group) { + /** + * getPowermeterWattsMqtt + */ + bool getPowermeterWattsMqtt(JsonObject logObj, uint8_t group) { // TODO: nicht komplett bool ret = false; @@ -500,7 +480,10 @@ return; return ret; } - int getPowermeterWattsHichi(JsonObject logObj, uint8_t group) { + /** + * getPowermeterWattsHichi + */ + bool getPowermeterWattsHichi(JsonObject logObj, uint8_t group) { // TODO: nicht komplett bool ret = false; @@ -516,7 +499,10 @@ return; return ret; } - int getPowermeterWattsTibber(JsonObject logObj, uint8_t group) { + /** + * getPowermeterWattsTibber + */ + bool getPowermeterWattsTibber(JsonObject logObj, uint8_t group) { // TODO: nicht komplett bool ret = false; @@ -534,6 +520,9 @@ return; // Inverter + /** + * getInverterData + */ bool getInverterData(uint8_t group) { zeroExportGroup_t *cfgGroup = &mCfg->groups[group]; @@ -612,16 +601,15 @@ return; // TODO: Eingang muss konfigurierbar sein // ACK - if (cfgGroupInv->limitTsp != 0) { - if (iv->powerLimitAck) { - iv->powerLimitAck = false; - cfgGroupInv->limitTsp = 0; - } + if (iv->powerLimitAck) { + iv->powerLimitAck = false; + cfgGroupInv->limitTsp = 0; + } + if (cfgGroupInv->limitTsp > 0) { if ((millis() + 10000) > cfgGroupInv->limitTsp) { cfgGroupInv->limitTsp = 0; } } - ret = true; } } @@ -644,6 +632,7 @@ return; bool batteryProtection(uint8_t group) { zeroExportGroup_t *cfgGroup = &mCfg->groups[group]; // TODO: Wenn kein WR gefunden wird, wird nicht abgeschaltet!!! +// TODO: Es fehlt die Möglichkeit manuell einzuschalten JsonObject logObj = mLog.createNestedObject("bp"); logObj["grp"] = group; @@ -737,15 +726,9 @@ return; long int bTsp = millis(); // Führungsgröße w in Watt - float w_Sum = cfgGroup->setPoint; - float w_L1 = cfgGroup->setPoint / 3; - float w_L2 = cfgGroup->setPoint / 3; - float w_L3 = cfgGroup->setPoint / 3; + float w = cfgGroup->setPoint; - logObj["w_P"] = w_Sum; - logObj["w_P1"] = w_L1; - logObj["w_P2"] = w_L2; - logObj["w_P3"] = w_L3; + logObj["w"] = w; // Regelgröße x in Watt float x_Sum = cfgGroup->pmPower; @@ -759,10 +742,10 @@ return; logObj["x_P3"] = x_L3; // Regelabweichung e in Watt - float e_Sum = 0 - (w_Sum - x_Sum); - float e_L1 = 0 - (w_L1 - x_L1); - float e_L2 = 0 - (w_L2 - x_L2); - float e_L3 = 0 - (w_L3 - x_L3); + float e_Sum = w - x_Sum; + float e_L1 = w - x_L1; + float e_L2 = w - x_L2; + float e_L3 = w - x_L3; logObj["e_P"] = e_Sum; logObj["e_P1"] = e_L1; @@ -771,11 +754,9 @@ return; // Regler // TODO: Regelparameter unter Advanced konfigurierbar? Aber erst wenn Regler komplett ingegriert. - const float Kp = 1; - const float Ki = 1; - const float Kd = 1; - -// unsigned long tsp = millis(); + const float Kp = -1; + const float Ki = -1; + const float Kd = -1; // - P-Anteil float yP_Sum = Kp * e_Sum; @@ -810,10 +791,10 @@ return; logObj["yPID_P2"] = yPID_L2; logObj["yPID_P3"] = yPID_L3; - cfgGroup->grpPower += yPID_Sum; - cfgGroup->grpPowerL1 += yPID_L1; - cfgGroup->grpPowerL2 += yPID_L2; - cfgGroup->grpPowerL3 += yPID_L3; + cfgGroup->grpPower = yPID_Sum; + cfgGroup->grpPowerL1 = yPID_L1; + cfgGroup->grpPowerL2 = yPID_L2; + cfgGroup->grpPowerL3 = yPID_L3; long int eTsp = millis(); logObj["b"] = bTsp; @@ -836,13 +817,8 @@ return; JsonObject logObj = mLog.createNestedObject("sl"); logObj["grp"] = group; -// bool ret = true; - long int bTsp = millis(); -// JsonArray logArrInv = logObj.createNestedArray("iv"); -// unsigned long tsp = millis(); - float deltaY_Sum = cfgGroup->grpPower; float deltaY_L1 = cfgGroup->grpPowerL1; float deltaY_L2 = cfgGroup->grpPowerL2; @@ -871,6 +847,16 @@ return; continue; } + if ((cfgGroup->battSwitch) && (!cfgGroupInv->state)) { + setPower(&logObj, group, inv, 1); + return false; + } + + if ((!cfgGroup->battSwitch) && (cfgGroupInv->state)) { + setPower(&logObj, group, inv, 0); + return false; + } + if (cfgGroupInv->power < ivPmin[cfgGroupInv->target]) { grpTarget[cfgGroupInv->target] = true; ivPmin[cfgGroupInv->target] = cfgGroupInv->power; @@ -915,73 +901,70 @@ return; // Leistung erhöhen if (*deltaP > 0) { + // Toleranz + if (*deltaP < cfgGroup->powerTolerance) { + continue; + } logObj["+deltaP"] = *deltaP; zeroExportGroupInverter_t *cfgGroupInv = &cfgGroup->inverters[ivId_Pmin[i]]; - cfgGroupInv->limitNew = cfgGroupInv->limit + *deltaP; - if (i != 0) { - cfgGroup->grpPower - *deltaP; - } + cfgGroupInv->limitNew = (uint16_t)((float)cfgGroupInv->limit + *deltaP); +// if (i != 0) { +// cfgGroup->grpPower - *deltaP; +// } *deltaP = 0; - if (cfgGroupInv->limitNew > cfgGroupInv->powerMax) { - *deltaP = cfgGroupInv->limitNew - cfgGroupInv->powerMax; - cfgGroupInv->limitNew = cfgGroupInv->powerMax; - if (i != 0) { - cfgGroup->grpPower + *deltaP; - } - } +// if (cfgGroupInv->limitNew > cfgGroupInv->powerMax) { +// *deltaP = cfgGroupInv->limitNew - cfgGroupInv->powerMax; +// cfgGroupInv->limitNew = cfgGroupInv->powerMax; +// if (i != 0) { +// cfgGroup->grpPower + *deltaP; +// } +// } setLimit(&logObj, group, ivId_Pmin[i]); continue; } // Leistung reduzieren if (*deltaP < 0) { + // Toleranz + if (*deltaP > -cfgGroup->powerTolerance) { + continue; + } logObj["-deltaP"] = *deltaP; zeroExportGroupInverter_t *cfgGroupInv = &cfgGroup->inverters[ivId_Pmax[i]]; - cfgGroupInv->limitNew = cfgGroupInv->limit - *deltaP; - if (i != 0) { - cfgGroup->grpPower - *deltaP; - } + cfgGroupInv->limitNew = (uint16_t)((float)cfgGroupInv->limit + *deltaP); +// if (i != 0) { +// cfgGroup->grpPower - *deltaP; +// } *deltaP = 0; - if (cfgGroupInv->limitNew < cfgGroupInv->powerMin) { - *deltaP = cfgGroupInv->limitNew - cfgGroupInv->powerMin; - cfgGroupInv->limitNew = cfgGroupInv->powerMin; - if (i != 0) { - cfgGroup->grpPower + *deltaP; - } - } +// if (cfgGroupInv->limitNew < cfgGroupInv->powerMin) { +// *deltaP = cfgGroupInv->limitNew - cfgGroupInv->powerMin; +// cfgGroupInv->limitNew = cfgGroupInv->powerMin; +// if (i != 0) { +// cfgGroup->grpPower + *deltaP; +// } +// } setLimit(&logObj, group, ivId_Pmax[i]); continue; } - } -// if (ret) { -// logObj["todo"] = "- nothing todo - "; -// } - long int eTsp = millis(); logObj["b"] = bTsp; logObj["e"] = eTsp; logObj["d"] = eTsp - bTsp; -// return ret; return true; } + // Funktionen - - - - - - - - -// TODO: Hier folgen Unterfunktionen für SetControl die Erweitert werden müssen -// setLimit, checkLimit -// setPower, checkPower -// setReboot, checkReboot - + /** + * setLimit + * @param objLog + * @param group + * @param inv + * @returns true/false + */ bool setLimit(JsonObject *objlog, uint8_t group, uint8_t inv) { zeroExportGroupInverter_t *cfgGroupInv = &mCfg->groups[group].inverters[inv]; @@ -990,11 +973,20 @@ return; objLog["iv"] = inv; // Reject limit if difference < 5 W -// if ((cfgGroupInv->limitNew > cfgGroupInv->limit - 5) && (cfgGroupInv->limitNew < cfgGroupInv->limit + 5)) { -// TODO: 5W Toleranz konfigurierbar? -// objLog["error"] = "Diff < 5W"; -// return true; -// } + if ((cfgGroupInv->limitNew > cfgGroupInv->limit + 5) && (cfgGroupInv->limitNew < cfgGroupInv->limit - 5)) { + objLog["err"] = "Diff < 5W"; + return false; + } + + // Restriction LimitNew >= Pmin + if (cfgGroupInv->limitNew < cfgGroupInv->powerMin) { + cfgGroupInv->limitNew = cfgGroupInv->powerMin; + } + + // Restriction LimitNew <= Pmax + if (cfgGroupInv->limitNew > cfgGroupInv->powerMax) { + cfgGroupInv->limitNew = cfgGroupInv->powerMax; + } cfgGroupInv->limit = cfgGroupInv->limitNew; cfgGroupInv->limitTsp = millis(); @@ -1016,7 +1008,74 @@ return; return true; } + /** + * setPower + * @param objLog + * @param group + * @param inv + * @param state + * @returns true/false + */ + bool setPower(JsonObject *objlog, uint8_t group, uint8_t inv, bool state) { + zeroExportGroupInverter_t *cfgGroupInv = &mCfg->groups[group].inverters[inv]; + + JsonObject objLog = objlog->createNestedObject("setPower"); + objLog["grp"] = group; + objLog["iv"] = inv; + +// cfgGroupInv->limit = cfgGroupInv->limitNew; + cfgGroupInv->limitTsp = millis(); + +// objLog["P"] = cfgGroupInv->limit; + objLog["tsp"] = cfgGroupInv->limitTsp; + + // State übergeben + DynamicJsonDocument doc(512); + JsonObject obj = doc.to(); + obj["val"] = state; + obj["id"] = cfgGroupInv->id; + obj["path"] = "ctrl"; + obj["cmd"] = "power"; + mApi->ctrlRequest(obj); + + objLog["data"] = obj; + + return false; + } + + /** + * setReboot + * @param objLog + * @param group + * @param inv + * @returns true/false + */ + bool setReboot(JsonObject *objlog, uint8_t group, uint8_t inv) { + zeroExportGroupInverter_t *cfgGroupInv = &mCfg->groups[group].inverters[inv]; + + JsonObject objLog = objlog->createNestedObject("setReboot"); + objLog["grp"] = group; + objLog["iv"] = inv; + +// cfgGroupInv->limit = cfgGroupInv->limitNew; + cfgGroupInv->limitTsp = millis(); + +// objLog["P"] = cfgGroupInv->limit; + objLog["tsp"] = cfgGroupInv->limitTsp; + // Reboot übergeben + DynamicJsonDocument doc(512); + JsonObject obj = doc.to(); +// obj["val"] = cfgGroupInv->limit; + obj["id"] = cfgGroupInv->id; + obj["path"] = "ctrl"; + obj["cmd"] = "restart"; + mApi->ctrlRequest(obj); + + objLog["data"] = obj; + + return false; + } /* // TODO: Vorlage für Berechnung @@ -1060,6 +1119,7 @@ return; RestApiType *mApi; StaticJsonDocument<5000> mDocLog; JsonObject mLog = mDocLog.to(); + PubMqttType *mMqtt; };