|
@ -15,6 +15,8 @@ |
|
|
#include "AsyncJson.h" |
|
|
#include "AsyncJson.h" |
|
|
#include "powermeter.h" |
|
|
#include "powermeter.h" |
|
|
|
|
|
|
|
|
|
|
|
#include "utils/DynamicJsonHandler.h" |
|
|
|
|
|
|
|
|
template <class HMSYSTEM> |
|
|
template <class HMSYSTEM> |
|
|
|
|
|
|
|
|
class ZeroExport { |
|
|
class ZeroExport { |
|
@ -47,9 +49,16 @@ class ZeroExport { |
|
|
mApi = api; |
|
|
mApi = api; |
|
|
mMqtt = mqtt; |
|
|
mMqtt = mqtt; |
|
|
|
|
|
|
|
|
mIsInitialized = mPowermeter.setup(mApp, mCfg, mqtt, &mLog); |
|
|
mIsInitialized = mPowermeter.setup(mApp, mCfg, mqtt, &_log); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*void printJson() {
|
|
|
|
|
|
serializeJson(doc, Serial); |
|
|
|
|
|
Serial.println(); |
|
|
|
|
|
serializeJsonPretty(doc, Serial); |
|
|
|
|
|
}*/ |
|
|
|
|
|
|
|
|
/** loop
|
|
|
/** loop
|
|
|
* Arbeitsschleife |
|
|
* Arbeitsschleife |
|
|
* @param void |
|
|
* @param void |
|
@ -83,26 +92,24 @@ class ZeroExport { |
|
|
zeroExportGroupInverter_t *CfgGroupInv = &CfgGroup->inverters[inv]; |
|
|
zeroExportGroupInverter_t *CfgGroupInv = &CfgGroup->inverters[inv]; |
|
|
Inverter<> *iv = mSys->getInverterByPos(Queue.id); |
|
|
Inverter<> *iv = mSys->getInverterByPos(Queue.id); |
|
|
|
|
|
|
|
|
mLog["g"] = group; |
|
|
_log.addProperty("g", group); |
|
|
mLog["i"] = inv; |
|
|
_log.addProperty("i", inv); |
|
|
|
|
|
|
|
|
// Check Data->iv
|
|
|
// Check Data->iv
|
|
|
if (!iv->isAvailable()) { |
|
|
if (!iv->isAvailable()) { |
|
|
if (mCfg->debug) { |
|
|
if (mCfg->debug) { |
|
|
mLog["nA"] = "!isAvailable"; |
|
|
_log.addProperty("nA", "!isAvailable"); |
|
|
sendLog(); |
|
|
sendLog(); |
|
|
} |
|
|
} |
|
|
clearLog(); |
|
|
|
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Check Data->waitAck
|
|
|
// Check Data->waitAck
|
|
|
if (CfgGroupInv->waitAck > 0) { |
|
|
if (CfgGroupInv->waitAck > 0) { |
|
|
if (mCfg->debug) { |
|
|
if (mCfg->debug) { |
|
|
mLog["wA"] = CfgGroupInv->waitAck; |
|
|
_log.addProperty("wA", CfgGroupInv->waitAck); |
|
|
sendLog(); |
|
|
sendLog(); |
|
|
} |
|
|
} |
|
|
clearLog(); |
|
|
|
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -113,16 +120,19 @@ class ZeroExport { |
|
|
groupPower += mCfg->groups[group].inverters[inv].power; // Calc Data->groupPower
|
|
|
groupPower += mCfg->groups[group].inverters[inv].power; // Calc Data->groupPower
|
|
|
groupLimit += mCfg->groups[group].inverters[inv].limit; // Calc Data->groupLimit
|
|
|
groupLimit += mCfg->groups[group].inverters[inv].limit; // Calc Data->groupLimit
|
|
|
} |
|
|
} |
|
|
mLog["gP"] = groupPower; |
|
|
|
|
|
mLog["gL"] = groupLimit; |
|
|
_log.addProperty("gP", groupPower); |
|
|
|
|
|
_log.addProperty("gL", groupLimit); |
|
|
|
|
|
|
|
|
// Batteryprotection
|
|
|
// Batteryprotection
|
|
|
mLog["bEn"] = (uint8_t)CfgGroup->battCfg; |
|
|
_log.addProperty("bEn", (uint8_t)CfgGroup->battCfg); |
|
|
|
|
|
|
|
|
switch (CfgGroup->battCfg) { |
|
|
switch (CfgGroup->battCfg) { |
|
|
case zeroExportBatteryCfg::none: |
|
|
case zeroExportBatteryCfg::none: |
|
|
if (CfgGroup->battSwitch != true) { |
|
|
if (CfgGroup->battSwitch != true) { |
|
|
CfgGroup->battSwitch = true; |
|
|
CfgGroup->battSwitch = true; |
|
|
mLog["bA"] = "turn on"; |
|
|
|
|
|
|
|
|
_log.addProperty("bA", "turn on"); |
|
|
} |
|
|
} |
|
|
break; |
|
|
break; |
|
|
case zeroExportBatteryCfg::invUdc: |
|
|
case zeroExportBatteryCfg::invUdc: |
|
@ -131,34 +141,34 @@ class ZeroExport { |
|
|
if (CfgGroup->battSwitch != true) { |
|
|
if (CfgGroup->battSwitch != true) { |
|
|
if (CfgGroup->battValue > CfgGroup->battLimitOn) { |
|
|
if (CfgGroup->battValue > CfgGroup->battLimitOn) { |
|
|
CfgGroup->battSwitch = true; |
|
|
CfgGroup->battSwitch = true; |
|
|
mLog["bA"] = "turn on"; |
|
|
_log.addProperty("bA", "turn on"); |
|
|
} |
|
|
} |
|
|
if ((CfgGroup->battValue > CfgGroup->battLimitOff) && (CfgGroupInv->power > 0)) { |
|
|
if ((CfgGroup->battValue > CfgGroup->battLimitOff) && (CfgGroupInv->power > 0)) { |
|
|
CfgGroup->battSwitch = true; |
|
|
CfgGroup->battSwitch = true; |
|
|
mLog["bA"] = "turn on"; |
|
|
_log.addProperty("bA", "turn on"); |
|
|
} |
|
|
} |
|
|
} else { |
|
|
} else { |
|
|
if (CfgGroup->battValue < CfgGroup->battLimitOff) { |
|
|
if (CfgGroup->battValue < CfgGroup->battLimitOff) { |
|
|
CfgGroup->battSwitch = false; |
|
|
CfgGroup->battSwitch = false; |
|
|
mLog["bA"] = "turn off"; |
|
|
_log.addProperty("bA", "turn off"); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
mLog["bU"] = ah::round1(CfgGroup->battValue); |
|
|
_log.addProperty("bU", ah::round1(CfgGroup->battValue)); |
|
|
break; |
|
|
break; |
|
|
default: |
|
|
default: |
|
|
if (CfgGroup->battSwitch == true) { |
|
|
if (CfgGroup->battSwitch == true) { |
|
|
CfgGroup->battSwitch = false; |
|
|
CfgGroup->battSwitch = false; |
|
|
mLog["bA"] = "turn off"; |
|
|
_log.addProperty("bA", "turn off"); |
|
|
} |
|
|
} |
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
|
mLog["bSw"] = CfgGroup->battSwitch; |
|
|
_log.addProperty("bSw", CfgGroup->battSwitch); |
|
|
|
|
|
|
|
|
// Controller
|
|
|
// Controller
|
|
|
|
|
|
|
|
|
// Führungsgröße w in Watt
|
|
|
// Führungsgröße w in Watt
|
|
|
int16_t w = CfgGroup->setPoint; |
|
|
int16_t w = CfgGroup->setPoint; |
|
|
mLog["w"] = w; |
|
|
_log.addProperty("w", w); |
|
|
|
|
|
|
|
|
// Regelgröße x in Watt
|
|
|
// Regelgröße x in Watt
|
|
|
int16_t x = 0.0; |
|
|
int16_t x = 0.0; |
|
@ -167,18 +177,17 @@ class ZeroExport { |
|
|
} else { |
|
|
} else { |
|
|
x = mPowermeter.getDataAVG(group); |
|
|
x = mPowermeter.getDataAVG(group); |
|
|
} |
|
|
} |
|
|
mLog["x"] = x; |
|
|
_log.addProperty("x", x); |
|
|
|
|
|
|
|
|
// Regelabweichung e in Watt
|
|
|
// Regelabweichung e in Watt
|
|
|
int16_t e = w - x; |
|
|
int16_t e = w - x; |
|
|
mLog["e"] = e; |
|
|
_log.addProperty("e", e); |
|
|
|
|
|
|
|
|
// Keine Regelung innerhalb der Toleranzgrenzen
|
|
|
// Keine Regelung innerhalb der Toleranzgrenzen
|
|
|
if ((e < CfgGroup->powerTolerance) && (e > -CfgGroup->powerTolerance)) { |
|
|
if ((e < CfgGroup->powerTolerance) && (e > -CfgGroup->powerTolerance)) { |
|
|
e = 0; |
|
|
e = 0; |
|
|
mLog["eK"] = e; |
|
|
_log.addProperty("eK", e); |
|
|
sendLog(); |
|
|
sendLog(); |
|
|
clearLog(); |
|
|
|
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -192,29 +201,28 @@ class ZeroExport { |
|
|
CfgGroup->eSum += e; |
|
|
CfgGroup->eSum += e; |
|
|
int16_t yI = Ki * Ta * CfgGroup->eSum; |
|
|
int16_t yI = Ki * Ta * CfgGroup->eSum; |
|
|
if (Ta == 0) { |
|
|
if (Ta == 0) { |
|
|
mLog["Error"] = "Ta = 0"; |
|
|
_log.addProperty("Error", "Ta = 0"); |
|
|
sendLog(); |
|
|
sendLog(); |
|
|
clearLog(); |
|
|
|
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
int16_t yD = Kd * (e - CfgGroup->eOld) / Ta; |
|
|
int16_t yD = Kd * (e - CfgGroup->eOld) / Ta; |
|
|
|
|
|
|
|
|
if (mCfg->debug) { |
|
|
if (mCfg->debug) { |
|
|
mLog["Kp"] = Kp; |
|
|
_log.addProperty("Kp", Kp); |
|
|
mLog["Ki"] = Ki; |
|
|
_log.addProperty("Ki", Ki); |
|
|
mLog["Kd"] = Kd; |
|
|
_log.addProperty("Kd", Kd); |
|
|
mLog["Ta"] = Ta; |
|
|
_log.addProperty("Ta", Ta); |
|
|
mLog["yP"] = yP; |
|
|
_log.addProperty("yP", yP); |
|
|
mLog["yI"] = yI; |
|
|
_log.addProperty("yI", yI); |
|
|
mLog["eSum"] = CfgGroup->eSum; |
|
|
_log.addProperty("eSum", CfgGroup->eSum); |
|
|
mLog["yD"] = yD; |
|
|
_log.addProperty("yD", yD); |
|
|
mLog["eOld"] = CfgGroup->eOld; |
|
|
_log.addProperty("eOld", CfgGroup->eOld); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
CfgGroup->eOld = e; |
|
|
CfgGroup->eOld = e; |
|
|
int16_t y = yP + yI + yD; |
|
|
int16_t y = yP + yI + yD; |
|
|
|
|
|
|
|
|
mLog["y"] = y; |
|
|
_log.addProperty("y", y); |
|
|
|
|
|
|
|
|
// Regelbegrenzung
|
|
|
// Regelbegrenzung
|
|
|
// TODO: Hier könnte man den maximalen Sprung begrenzen
|
|
|
// TODO: Hier könnte man den maximalen Sprung begrenzen
|
|
@ -230,7 +238,7 @@ class ZeroExport { |
|
|
if (CfgGroupInv->actionTimer == 0) CfgGroupInv->actionTimer = 1; |
|
|
if (CfgGroupInv->actionTimer == 0) CfgGroupInv->actionTimer = 1; |
|
|
if (CfgGroupInv->actionTimer > 10) { |
|
|
if (CfgGroupInv->actionTimer > 10) { |
|
|
CfgGroupInv->action = zeroExportAction_t::doTurnOn; |
|
|
CfgGroupInv->action = zeroExportAction_t::doTurnOn; |
|
|
mLog["do"] = "doTurnOn"; |
|
|
_log.addProperty("do", "doTurnOn"); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
if ((CfgGroupInv->turnOff) && (CfgGroupInv->limitNew <= 0) && (CfgGroupInv->power > 0)) { |
|
|
if ((CfgGroupInv->turnOff) && (CfgGroupInv->limitNew <= 0) && (CfgGroupInv->power > 0)) { |
|
@ -238,26 +246,28 @@ class ZeroExport { |
|
|
if (CfgGroupInv->actionTimer == 0) CfgGroupInv->actionTimer = -1; |
|
|
if (CfgGroupInv->actionTimer == 0) CfgGroupInv->actionTimer = -1; |
|
|
if (CfgGroupInv->actionTimer < 30) { |
|
|
if (CfgGroupInv->actionTimer < 30) { |
|
|
CfgGroupInv->action = zeroExportAction_t::doTurnOff; |
|
|
CfgGroupInv->action = zeroExportAction_t::doTurnOff; |
|
|
mLog["do"] = "doTurnOff"; |
|
|
_log.addProperty("do", "doTurnOff"); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
if (((CfgGroup->battSwitch == false) || (mCfg->sleep == true) || (CfgGroup->sleep == true)) && (CfgGroupInv->power > 0)) { |
|
|
if (((CfgGroup->battSwitch == false) || (mCfg->sleep == true) || (CfgGroup->sleep == true)) && (CfgGroupInv->power > 0)) { |
|
|
CfgGroupInv->action = zeroExportAction_t::doTurnOff; |
|
|
CfgGroupInv->action = zeroExportAction_t::doTurnOff; |
|
|
mLog["do"] = "sleep"; |
|
|
_log.addProperty("do", "sleep"); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
mLog["doT"] = CfgGroupInv->action; |
|
|
_log.addProperty("doT", CfgGroupInv->action); |
|
|
|
|
|
|
|
|
if (CfgGroupInv->action == zeroExportAction_t::doNone) { |
|
|
if (CfgGroupInv->action == zeroExportAction_t::doNone) { |
|
|
mLog["l"] = CfgGroupInv->limit; |
|
|
_log.addProperty("l", CfgGroupInv->limit); |
|
|
mLog["ln"] = CfgGroupInv->limitNew; |
|
|
_log.addProperty("ln", CfgGroupInv->limitNew); |
|
|
|
|
|
|
|
|
// groupMax
|
|
|
// groupMax
|
|
|
uint16_t otherIvLimit = groupLimit - CfgGroupInv->limit; |
|
|
uint16_t otherIvLimit = groupLimit - CfgGroupInv->limit; |
|
|
if ((otherIvLimit + CfgGroupInv->limitNew) > CfgGroup->powerMax) { |
|
|
if ((otherIvLimit + CfgGroupInv->limitNew) > CfgGroup->powerMax) { |
|
|
CfgGroupInv->limitNew = CfgGroup->powerMax - otherIvLimit; |
|
|
CfgGroupInv->limitNew = CfgGroup->powerMax - otherIvLimit; |
|
|
} |
|
|
} |
|
|
if (mCfg->debug) mLog["gPM"] = CfgGroup->powerMax; |
|
|
if (mCfg->debug) { |
|
|
|
|
|
_log.addProperty("gPM", CfgGroup->powerMax); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// PowerMax
|
|
|
// PowerMax
|
|
|
uint16_t powerMax = 100; |
|
|
uint16_t powerMax = 100; |
|
@ -291,13 +301,13 @@ class ZeroExport { |
|
|
|
|
|
|
|
|
// CfgGroupInv->actionTimer = 0;
|
|
|
// CfgGroupInv->actionTimer = 0;
|
|
|
// TODO: Timer stoppen wenn Limit gesetzt wird.
|
|
|
// TODO: Timer stoppen wenn Limit gesetzt wird.
|
|
|
mLog["lN"] = CfgGroupInv->limitNew; |
|
|
_log.addProperty("lN", CfgGroupInv->limitNew); |
|
|
|
|
|
|
|
|
CfgGroupInv->limit = CfgGroupInv->limitNew; |
|
|
CfgGroupInv->limit = CfgGroupInv->limitNew; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// doAction
|
|
|
// doAction
|
|
|
mLog["a"] = CfgGroupInv->action; |
|
|
_log.addProperty("a", CfgGroupInv->action); |
|
|
|
|
|
|
|
|
switch (CfgGroupInv->action) { |
|
|
switch (CfgGroupInv->action) { |
|
|
case zeroExportAction_t::doRestart: |
|
|
case zeroExportAction_t::doRestart: |
|
@ -350,9 +360,6 @@ class ZeroExport { |
|
|
// mqttPublish(String("zero/state/groups/" + String(group) + "/inverter/" + String(inv)).c_str(), mDocLog.as<std::string>().c_str());
|
|
|
// mqttPublish(String("zero/state/groups/" + String(group) + "/inverter/" + String(inv)).c_str(), mDocLog.as<std::string>().c_str());
|
|
|
// }
|
|
|
// }
|
|
|
/// BUG: 003 Ende
|
|
|
/// BUG: 003 Ende
|
|
|
|
|
|
|
|
|
clearLog(); |
|
|
|
|
|
|
|
|
|
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -422,17 +429,22 @@ class ZeroExport { |
|
|
if (!mCfg->groups[group].inverters[inv].enabled) continue; |
|
|
if (!mCfg->groups[group].inverters[inv].enabled) continue; |
|
|
|
|
|
|
|
|
if (iv->id == (uint8_t)mCfg->groups[group].inverters[inv].id) { |
|
|
if (iv->id == (uint8_t)mCfg->groups[group].inverters[inv].id) { |
|
|
mLog["g"] = group; |
|
|
_log.addProperty("g", group); |
|
|
mLog["i"] = inv; |
|
|
_log.addProperty("i", inv); |
|
|
|
|
|
|
|
|
mCfg->groups[group].inverters[inv].waitAck = 0; |
|
|
mCfg->groups[group].inverters[inv].waitAck = 0; |
|
|
mLog["wA"] = mCfg->groups[group].inverters[inv].waitAck; |
|
|
|
|
|
|
|
|
_log.addProperty("wA", mCfg->groups[group].inverters[inv].waitAck); |
|
|
|
|
|
|
|
|
|
|
|
// Wenn ein neuer LimitWert da ist. Soll es in group schreiben.
|
|
|
if (iv->actPowerLimit != 0xffff) { |
|
|
if (iv->actPowerLimit != 0xffff) { |
|
|
mLog["l"] = mCfg->groups[group].inverters[inv].limit; |
|
|
_log.addProperty("l", mCfg->groups[group].inverters[inv].limit); |
|
|
|
|
|
|
|
|
mCfg->groups[group].inverters[inv].limit = iv->actPowerLimit; |
|
|
mCfg->groups[group].inverters[inv].limit = iv->actPowerLimit; |
|
|
mLog["lF"] = mCfg->groups[group].inverters[inv].limit; |
|
|
|
|
|
|
|
|
_log.addProperty("lF", mCfg->groups[group].inverters[inv].limit); |
|
|
} |
|
|
} |
|
|
sendLog(); |
|
|
sendLog(); |
|
|
clearLog(); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
@ -453,12 +465,13 @@ class ZeroExport { |
|
|
if (!mCfg->groups[group].inverters[inv].enabled) continue; |
|
|
if (!mCfg->groups[group].inverters[inv].enabled) continue; |
|
|
|
|
|
|
|
|
if (iv->id == mCfg->groups[group].inverters[inv].id) { |
|
|
if (iv->id == mCfg->groups[group].inverters[inv].id) { |
|
|
mLog["g"] = group; |
|
|
_log.addProperty("g", group); |
|
|
mLog["i"] = inv; |
|
|
_log.addProperty("i", inv); |
|
|
|
|
|
|
|
|
mCfg->groups[group].inverters[inv].waitAck = 0; |
|
|
mCfg->groups[group].inverters[inv].waitAck = 0; |
|
|
mLog["wA"] = mCfg->groups[group].inverters[inv].waitAck; |
|
|
|
|
|
|
|
|
_log.addProperty("wA", mCfg->groups[group].inverters[inv].waitAck); |
|
|
sendLog(); |
|
|
sendLog(); |
|
|
clearLog(); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
@ -479,10 +492,12 @@ class ZeroExport { |
|
|
if (!mCfg->groups[group].inverters[inv].enabled) continue; |
|
|
if (!mCfg->groups[group].inverters[inv].enabled) continue; |
|
|
|
|
|
|
|
|
if (iv->id == mCfg->groups[group].inverters[inv].id) { |
|
|
if (iv->id == mCfg->groups[group].inverters[inv].id) { |
|
|
mLog["g"] = group; |
|
|
_log.addProperty("g", group); |
|
|
mLog["i"] = inv; |
|
|
_log.addProperty("i", inv); |
|
|
|
|
|
|
|
|
mCfg->groups[group].inverters[inv].waitAck = 0; |
|
|
mCfg->groups[group].inverters[inv].waitAck = 0; |
|
|
mLog["wA"] = mCfg->groups[group].inverters[inv].waitAck; |
|
|
|
|
|
|
|
|
_log.addProperty("wA", mCfg->groups[group].inverters[inv].waitAck); |
|
|
|
|
|
|
|
|
mCfg->groups[group].inverters[inv].limit = mCfg->groups[group].inverters[inv].powerMin; |
|
|
mCfg->groups[group].inverters[inv].limit = mCfg->groups[group].inverters[inv].powerMin; |
|
|
iv->powerLimit[0] = static_cast<uint16_t>(mCfg->groups[group].inverters[inv].limit * 10.0); |
|
|
iv->powerLimit[0] = static_cast<uint16_t>(mCfg->groups[group].inverters[inv].limit * 10.0); |
|
@ -495,7 +510,6 @@ class ZeroExport { |
|
|
mCfg->groups[group].inverters[inv].actionTimestamp = millis(); |
|
|
mCfg->groups[group].inverters[inv].actionTimestamp = millis(); |
|
|
} |
|
|
} |
|
|
sendLog(); |
|
|
sendLog(); |
|
|
clearLog(); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
@ -518,8 +532,8 @@ class ZeroExport { |
|
|
if (!CfgGroupInv->enabled) continue; |
|
|
if (!CfgGroupInv->enabled) continue; |
|
|
if (CfgGroupInv->id != iv->id) continue; |
|
|
if (CfgGroupInv->id != iv->id) continue; |
|
|
|
|
|
|
|
|
mLog["g"] = group; |
|
|
_log.addProperty("g", group); |
|
|
mLog["i"] = inv; |
|
|
_log.addProperty("i", inv); |
|
|
|
|
|
|
|
|
// TODO: Ist nach eventAckSetLimit verschoben
|
|
|
// TODO: Ist nach eventAckSetLimit verschoben
|
|
|
// if (iv->actPowerLimit != 0xffff) {
|
|
|
// if (iv->actPowerLimit != 0xffff) {
|
|
@ -531,17 +545,19 @@ class ZeroExport { |
|
|
// TODO: Es dauert bis getMaxPower übertragen wird.
|
|
|
// TODO: Es dauert bis getMaxPower übertragen wird.
|
|
|
if (iv->getMaxPower() > 0) { |
|
|
if (iv->getMaxPower() > 0) { |
|
|
CfgGroupInv->MaxPower = iv->getMaxPower(); |
|
|
CfgGroupInv->MaxPower = iv->getMaxPower(); |
|
|
mLog["pM"] = CfgGroupInv->MaxPower; |
|
|
|
|
|
|
|
|
_log.addProperty("pM", CfgGroupInv->MaxPower); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
record_t<> *rec; |
|
|
record_t<> *rec; |
|
|
rec = iv->getRecordStruct(RealTimeRunData_Debug); |
|
|
rec = iv->getRecordStruct(RealTimeRunData_Debug); |
|
|
if (iv->getLastTs(rec) > (millis() - 15000)) { |
|
|
if (iv->getLastTs(rec) > (millis() - 15000)) { |
|
|
CfgGroupInv->power = iv->getChannelFieldValue(CH0, FLD_PAC, rec); |
|
|
CfgGroupInv->power = iv->getChannelFieldValue(CH0, FLD_PAC, rec); |
|
|
mLog["p"] = CfgGroupInv->power; |
|
|
|
|
|
|
|
|
_log.addProperty("pM", CfgGroupInv->MaxPower); |
|
|
|
|
|
|
|
|
CfgGroupInv->dcVoltage = iv->getChannelFieldValue(CH1, FLD_UDC, rec); |
|
|
CfgGroupInv->dcVoltage = iv->getChannelFieldValue(CH1, FLD_UDC, rec); |
|
|
mLog["bU"] = ah::round1(CfgGroupInv->dcVoltage); |
|
|
_log.addProperty("bU", ah::round1(CfgGroupInv->dcVoltage)); |
|
|
|
|
|
|
|
|
// Batterieüberwachung - Überwachung über die DC-Spannung am PV-Eingang 1 des Inverters
|
|
|
// Batterieüberwachung - Überwachung über die DC-Spannung am PV-Eingang 1 des Inverters
|
|
|
if (CfgGroup->battCfg == zeroExportBatteryCfg::invUdc) { |
|
|
if (CfgGroup->battCfg == zeroExportBatteryCfg::invUdc) { |
|
@ -561,16 +577,16 @@ class ZeroExport { |
|
|
uint16_t powerPercent = 100 / CfgGroupInv->MaxPower * CfgGroupInv->power; |
|
|
uint16_t powerPercent = 100 / CfgGroupInv->MaxPower * CfgGroupInv->power; |
|
|
uint16_t delta = abs(limitPercent - powerPercent); |
|
|
uint16_t delta = abs(limitPercent - powerPercent); |
|
|
if ((delta > 10) && (CfgGroupInv->power > 0)) { |
|
|
if ((delta > 10) && (CfgGroupInv->power > 0)) { |
|
|
mLog["delta"] = delta; |
|
|
_log.addProperty("delta", delta); |
|
|
unsigned long delay = iv->getLastTs(rec) - CfgGroupInv->actionTimestamp; |
|
|
unsigned long delay = iv->getLastTs(rec) - CfgGroupInv->actionTimestamp; |
|
|
mLog["delay"] = delay; |
|
|
_log.addProperty("delay", delay); |
|
|
if (delay > 30000) { |
|
|
if (delay > 30000) { |
|
|
CfgGroupInv->action = zeroExportAction_t::doActivePowerContr; |
|
|
CfgGroupInv->action = zeroExportAction_t::doActivePowerContr; |
|
|
mLog["do"] = "doActivePowerContr"; |
|
|
_log.addProperty("do", "doActivePowerContr"); |
|
|
} |
|
|
} |
|
|
if (delay > 60000) { |
|
|
if (delay > 60000) { |
|
|
CfgGroupInv->action = zeroExportAction_t::doRestart; |
|
|
CfgGroupInv->action = zeroExportAction_t::doRestart; |
|
|
mLog["do"] = "doRestart"; |
|
|
_log.addProperty("do", "doRestart"); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
@ -583,8 +599,6 @@ class ZeroExport { |
|
|
putQueue(Entry); |
|
|
putQueue(Entry); |
|
|
|
|
|
|
|
|
sendLog(); |
|
|
sendLog(); |
|
|
clearLog(); |
|
|
|
|
|
|
|
|
|
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
@ -634,40 +648,59 @@ class ZeroExport { |
|
|
|
|
|
|
|
|
if (strcmp(mCfg->groups[group].battTopic, String(topic).c_str())) { |
|
|
if (strcmp(mCfg->groups[group].battTopic, String(topic).c_str())) { |
|
|
mCfg->groups[group].battValue = (bool)obj["val"]; |
|
|
mCfg->groups[group].battValue = (bool)obj["val"]; |
|
|
mLog["k"] = mCfg->groups[group].battTopic; |
|
|
|
|
|
mLog["v"] = mCfg->groups[group].battValue; |
|
|
|
|
|
|
|
|
_log.addProperty("k", mCfg->groups[group].battTopic); |
|
|
|
|
|
_log.addProperty("v", mCfg->groups[group].battValue); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// "topic":"ctrl/zero"
|
|
|
// "topic":"ctrl/zero"
|
|
|
if (topic.indexOf("ctrl/zero") == -1) return; |
|
|
if (topic.indexOf("ctrl/zero") == -1) return; |
|
|
|
|
|
|
|
|
if (mCfg->debug) mLog["d"] = obj; |
|
|
|
|
|
|
|
|
_log.addProperty("d", obj); |
|
|
|
|
|
|
|
|
if (obj["path"] == "ctrl" && obj["cmd"] == "zero") { |
|
|
if (obj["path"] == "ctrl" && obj["cmd"] == "zero") { |
|
|
int8_t topicGroup = getGroupFromTopic(topic.c_str()); |
|
|
int8_t topicGroup = getGroupFromTopic(topic.c_str()); |
|
|
int8_t topicInverter = getInverterFromTopic(topic.c_str()); |
|
|
int8_t topicInverter = getInverterFromTopic(topic.c_str()); |
|
|
|
|
|
|
|
|
if (topicGroup != -1) mLog["g"] = topicGroup; |
|
|
if (topicGroup != -1) { |
|
|
if (topicInverter == -1) mLog["i"] = topicInverter; |
|
|
_log.addProperty("g", topicGroup); |
|
|
|
|
|
} |
|
|
|
|
|
if (topicInverter == -1) { |
|
|
|
|
|
_log.addProperty("i", topicInverter); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
mLog["k"] = topic; |
|
|
_log.addProperty("k", topic); |
|
|
|
|
|
|
|
|
// "topic":"ctrl/zero/enabled"
|
|
|
// "topic":"ctrl/zero/enabled"
|
|
|
if (topic.indexOf("ctrl/zero/enabled") != -1) mCfg->enabled = mLog["v"] = (bool)obj["val"]; |
|
|
if (topic.indexOf("ctrl/zero/enabled") != -1) { |
|
|
|
|
|
_log.addProperty("v", (bool)obj["val"]); |
|
|
|
|
|
mCfg->enabled = (bool)obj["val"]; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// "topic":"ctrl/zero/sleep"
|
|
|
// "topic":"ctrl/zero/sleep"
|
|
|
else if (topic.indexOf("ctrl/zero/sleep") != -1) mCfg->sleep = mLog["v"] = (bool)obj["val"]; |
|
|
else if (topic.indexOf("ctrl/zero/sleep") != -1) { |
|
|
|
|
|
_log.addProperty("v", (bool)obj["val"]); |
|
|
|
|
|
mCfg->sleep = (bool)obj["val"]; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
else if ((topicGroup >= 0) && (topicGroup < ZEROEXPORT_MAX_GROUPS)) |
|
|
else if ((topicGroup >= 0) && (topicGroup < ZEROEXPORT_MAX_GROUPS)) |
|
|
{ |
|
|
{ |
|
|
String stopicGroup = String(topicGroup); |
|
|
String stopicGroup = String(topicGroup); |
|
|
|
|
|
|
|
|
// "topic":"ctrl/zero/groups/+/enabled"
|
|
|
// "topic":"ctrl/zero/groups/+/enabled"
|
|
|
if (topic.endsWith("/enabled")) mCfg->groups[topicGroup].enabled = mLog["v"] = (bool)obj["val"]; |
|
|
if (topic.endsWith("/enabled")) { |
|
|
|
|
|
_log.addProperty("v", (bool)obj["val"]); |
|
|
|
|
|
mCfg->groups[topicGroup].enabled = (bool)obj["val"]; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// "topic":"ctrl/zero/groups/+/sleep"
|
|
|
// "topic":"ctrl/zero/groups/+/sleep"
|
|
|
else if (topic.endsWith("/sleep")) mCfg->groups[topicGroup].sleep = mLog["v"] = (bool)obj["val"]; |
|
|
else if (topic.endsWith("/sleep")) { |
|
|
|
|
|
_log.addProperty("v", (bool)obj["val"]); |
|
|
|
|
|
mCfg->groups[topicGroup].sleep = (bool)obj["val"]; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// Auf Eis gelegt, dafür 2 Gruppen mehr
|
|
|
// Auf Eis gelegt, dafür 2 Gruppen mehr
|
|
|
// 0.8.103008.2
|
|
|
// 0.8.103008.2
|
|
@ -692,42 +725,64 @@ class ZeroExport { |
|
|
// }
|
|
|
// }
|
|
|
|
|
|
|
|
|
// "topic":"ctrl/zero/groups/+/battery/switch"
|
|
|
// "topic":"ctrl/zero/groups/+/battery/switch"
|
|
|
else if (topic.endsWith("/battery/switch")) mCfg->groups[topicGroup].battSwitch = mLog["v"] = (bool)obj["val"]; |
|
|
else if (topic.endsWith("/battery/switch")) { |
|
|
|
|
|
_log.addProperty("v", (bool)obj["val"]); |
|
|
|
|
|
mCfg->groups[topicGroup].battSwitch = (bool)obj["val"]; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
else if (topic.indexOf("/advanced/") != -1) |
|
|
else if (topic.indexOf("/advanced/") != -1) |
|
|
{ |
|
|
{ |
|
|
// "topic":"ctrl/zero/groups/+/advanced/setPoint"
|
|
|
// "topic":"ctrl/zero/groups/+/advanced/setPoint"
|
|
|
if (topic.endsWith("/setPoint")) mCfg->groups[topicGroup].setPoint = mLog["v"] = (int16_t)obj["val"]; |
|
|
if (topic.endsWith("/setPoint")) { |
|
|
|
|
|
_log.addProperty("v", (int16_t)obj["val"]); |
|
|
|
|
|
mCfg->groups[topicGroup].setPoint = (int16_t)obj["val"]; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// "topic":"ctrl/zero/groups/+/advanced/powerTolerance"
|
|
|
// "topic":"ctrl/zero/groups/+/advanced/powerTolerance"
|
|
|
else if (topic.endsWith("/powerTolerance")) mCfg->groups[topicGroup].powerTolerance = mLog["v"] = (uint8_t)obj["val"]; |
|
|
else if (topic.endsWith("/powerTolerance")) { |
|
|
|
|
|
_log.addProperty("v", (uint8_t)obj["val"]); |
|
|
|
|
|
mCfg->groups[topicGroup].powerTolerance = (uint8_t)obj["val"]; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// "topic":"ctrl/zero/groups/+/advanced/powerMax"
|
|
|
// "topic":"ctrl/zero/groups/+/advanced/powerMax"
|
|
|
else if (topic.endsWith("/powerMax")) mCfg->groups[topicGroup].powerMax = mLog["v"] = (uint16_t)obj["val"]; |
|
|
else if (topic.endsWith("/powerMax")) { |
|
|
|
|
|
_log.addProperty("v", (uint16_t)obj["val"]); |
|
|
|
|
|
mCfg->groups[topicGroup].powerMax = (uint16_t)obj["val"]; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
else if (topic.indexOf("/inverter/") != -1) |
|
|
else if (topic.indexOf("/inverter/") != -1) |
|
|
{ |
|
|
{ |
|
|
if ((topicInverter >= 0) && (topicInverter < ZEROEXPORT_GROUP_MAX_INVERTERS)) |
|
|
if ((topicInverter >= 0) && (topicInverter < ZEROEXPORT_GROUP_MAX_INVERTERS)) |
|
|
{ |
|
|
{ |
|
|
// "topic":"ctrl/zero/groups/+/inverter/+/enabled"
|
|
|
// "topic":"ctrl/zero/groups/+/inverter/+/enabled"
|
|
|
if (topic.endsWith("/enabled")) mCfg->groups[topicGroup].inverters[topicInverter].enabled = mLog["v"] = (bool)obj["val"]; |
|
|
if (topic.endsWith("/enabled")) { |
|
|
|
|
|
_log.addProperty("v", (bool)obj["val"]); |
|
|
|
|
|
mCfg->groups[topicGroup].inverters[topicInverter].enabled = (bool)obj["val"]; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// "topic":"ctrl/zero/groups/+/inverter/+/powerMin"
|
|
|
// "topic":"ctrl/zero/groups/+/inverter/+/powerMin"
|
|
|
else if (topic.endsWith("/powerMin")) mCfg->groups[topicGroup].inverters[topicInverter].powerMin = mLog["v"] = (uint16_t)obj["val"]; |
|
|
else if (topic.endsWith("/powerMin")) { |
|
|
|
|
|
_log.addProperty("v", (uint16_t)obj["val"]); |
|
|
|
|
|
mCfg->groups[topicGroup].inverters[topicInverter].powerMin = (uint16_t)obj["val"]; |
|
|
|
|
|
} |
|
|
// "topic":"ctrl/zero/groups/+/inverter/+/powerMax"
|
|
|
// "topic":"ctrl/zero/groups/+/inverter/+/powerMax"
|
|
|
else if (topic.endsWith("/powerMax")) mCfg->groups[topicGroup].inverters[topicInverter].powerMax = mLog["v"] = (uint16_t)obj["val"]; |
|
|
else if (topic.endsWith("/powerMax")) { |
|
|
else mLog["k"] = "error"; |
|
|
_log.addProperty("v", (uint16_t)obj["val"]); |
|
|
|
|
|
mCfg->groups[topicGroup].inverters[topicInverter].powerMax = (uint16_t)obj["val"]; |
|
|
|
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
{ |
|
|
|
|
|
_log.addProperty("k", "error"); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
else { |
|
|
else { |
|
|
mLog["k"] = "error"; |
|
|
_log.addProperty("k", "error"); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
sendLog(); |
|
|
sendLog(); |
|
|
clearLog(); |
|
|
|
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -770,7 +825,8 @@ class ZeroExport { |
|
|
while (*pGroupSection != '/' && digitsCopied < 2) strGroup[digitsCopied++] = *pGroupSection++; |
|
|
while (*pGroupSection != '/' && digitsCopied < 2) strGroup[digitsCopied++] = *pGroupSection++; |
|
|
strGroup[digitsCopied] = '\0'; |
|
|
strGroup[digitsCopied] = '\0'; |
|
|
int8_t group = atoi(strGroup); |
|
|
int8_t group = atoi(strGroup); |
|
|
mLog["getGroupFromTopic"] = group; |
|
|
|
|
|
|
|
|
_log.addProperty("getGroupFromTopic", "group"); |
|
|
return group; |
|
|
return group; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
@ -819,7 +875,7 @@ class ZeroExport { |
|
|
void sendLog(void) { |
|
|
void sendLog(void) { |
|
|
// Log over Webserial
|
|
|
// Log over Webserial
|
|
|
if (mCfg->log_over_webserial) { |
|
|
if (mCfg->log_over_webserial) { |
|
|
DPRINTLN(DBG_INFO, String("ze: ") + mDocLog.as<String>()); |
|
|
DPRINTLN(DBG_INFO, String("ze: ") + _log.toString()); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Log over MQTT
|
|
|
// Log over MQTT
|
|
@ -836,12 +892,15 @@ class ZeroExport { |
|
|
* Löscht den LogSpeicher |
|
|
* Löscht den LogSpeicher |
|
|
*/ |
|
|
*/ |
|
|
void clearLog(void) { |
|
|
void clearLog(void) { |
|
|
mDocLog.clear(); |
|
|
_log.clear(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// private member variables
|
|
|
// private member variables
|
|
|
bool mIsInitialized = false; |
|
|
bool mIsInitialized = false; |
|
|
|
|
|
|
|
|
|
|
|
// Maximale Größe des JSON-Dokuments
|
|
|
|
|
|
static const size_t max_size = 5000; |
|
|
|
|
|
|
|
|
IApp *mApp = nullptr; |
|
|
IApp *mApp = nullptr; |
|
|
uint32_t *mTimestamp = nullptr; |
|
|
uint32_t *mTimestamp = nullptr; |
|
|
zeroExport_t *mCfg = nullptr; |
|
|
zeroExport_t *mCfg = nullptr; |
|
@ -855,15 +914,12 @@ class ZeroExport { |
|
|
|
|
|
|
|
|
unsigned long mLastRun = 0; |
|
|
unsigned long mLastRun = 0; |
|
|
|
|
|
|
|
|
StaticJsonDocument<5000> mDocLog; |
|
|
|
|
|
JsonObject mLog = mDocLog.to<JsonObject>(); |
|
|
|
|
|
|
|
|
|
|
|
powermeter mPowermeter; |
|
|
powermeter mPowermeter; |
|
|
|
|
|
|
|
|
PubMqttType *mMqtt = nullptr; |
|
|
PubMqttType *mMqtt = nullptr; |
|
|
bool mIsSubscribed = false; |
|
|
bool mIsSubscribed = false; |
|
|
StaticJsonDocument<512> mqttDoc; // DynamicJsonDocument mqttDoc(512);
|
|
|
|
|
|
JsonObject mqttObj = mqttDoc.to<JsonObject>(); |
|
|
DynamicJsonHandler _log; |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
#endif /*__ZEROEXPORT__*/ |
|
|
#endif /*__ZEROEXPORT__*/ |
|
|