Merge pull request #255 from madmartin:inverter_restart

Feature: add daily restart for the inverter
This commit is contained in:
helgeerbe 2023-06-05 10:10:14 +02:00 committed by GitHub
commit 8298a0c36f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 125 additions and 1 deletions

View File

@ -150,6 +150,7 @@ struct CONFIG_T {
float PowerLimiter_VoltageStartThreshold; float PowerLimiter_VoltageStartThreshold;
float PowerLimiter_VoltageStopThreshold; float PowerLimiter_VoltageStopThreshold;
float PowerLimiter_VoltageLoadCorrectionFactor; float PowerLimiter_VoltageLoadCorrectionFactor;
int8_t PowerLimiter_RestartHour;
bool Battery_Enabled; bool Battery_Enabled;
bool Huawei_Enabled; bool Huawei_Enabled;

View File

@ -31,6 +31,7 @@ public:
int32_t getLastRequestedPowewrLimit(); int32_t getLastRequestedPowewrLimit();
void setDisable(bool disable); void setDisable(bool disable);
bool getDisable(); bool getDisable();
void calcNextInverterRestart();
private: private:
uint32_t _lastLoop = 0; uint32_t _lastLoop = 0;
@ -39,6 +40,8 @@ private:
plStates _plState; plStates _plState;
bool _disabled = false; bool _disabled = false;
bool _batteryDischargeEnabled = false; bool _batteryDischargeEnabled = false;
uint32_t _nextInverterRestart = 0; // Values: 0->not calculated / 1->no restart configured / >1->time of next inverter restart in millis()
uint32_t _nextCalculateCheck = 5000; // time in millis for next NTP check to calulate restart
float _powerMeter1Power; float _powerMeter1Power;
float _powerMeter2Power; float _powerMeter2Power;

View File

@ -122,6 +122,7 @@
#define POWERLIMITER_VOLTAGE_START_THRESHOLD 50.0 #define POWERLIMITER_VOLTAGE_START_THRESHOLD 50.0
#define POWERLIMITER_VOLTAGE_STOP_THRESHOLD 49.0 #define POWERLIMITER_VOLTAGE_STOP_THRESHOLD 49.0
#define POWERLIMITER_VOLTAGE_LOAD_CORRECTION_FACTOR 0.001 #define POWERLIMITER_VOLTAGE_LOAD_CORRECTION_FACTOR 0.001
#define POWERLIMITER_RESTART_HOUR -1
#define BATTERY_ENABLED false #define BATTERY_ENABLED false

View File

@ -163,6 +163,7 @@ bool ConfigurationClass::write()
powerlimiter["voltage_start_threshold"] = config.PowerLimiter_VoltageStartThreshold; powerlimiter["voltage_start_threshold"] = config.PowerLimiter_VoltageStartThreshold;
powerlimiter["voltage_stop_threshold"] = config.PowerLimiter_VoltageStopThreshold; powerlimiter["voltage_stop_threshold"] = config.PowerLimiter_VoltageStopThreshold;
powerlimiter["voltage_load_correction_factor"] = config.PowerLimiter_VoltageLoadCorrectionFactor; powerlimiter["voltage_load_correction_factor"] = config.PowerLimiter_VoltageLoadCorrectionFactor;
powerlimiter["inverter_restart_hour"] = config.PowerLimiter_RestartHour;
JsonObject battery = doc.createNestedObject("battery"); JsonObject battery = doc.createNestedObject("battery");
battery["enabled"] = config.Battery_Enabled; battery["enabled"] = config.Battery_Enabled;
@ -360,6 +361,7 @@ bool ConfigurationClass::read()
config.PowerLimiter_VoltageStartThreshold = powerlimiter["voltage_start_threshold"] | POWERLIMITER_VOLTAGE_START_THRESHOLD; config.PowerLimiter_VoltageStartThreshold = powerlimiter["voltage_start_threshold"] | POWERLIMITER_VOLTAGE_START_THRESHOLD;
config.PowerLimiter_VoltageStopThreshold = powerlimiter["voltage_stop_threshold"] | POWERLIMITER_VOLTAGE_STOP_THRESHOLD; config.PowerLimiter_VoltageStopThreshold = powerlimiter["voltage_stop_threshold"] | POWERLIMITER_VOLTAGE_STOP_THRESHOLD;
config.PowerLimiter_VoltageLoadCorrectionFactor = powerlimiter["voltage_load_correction_factor"] | POWERLIMITER_VOLTAGE_LOAD_CORRECTION_FACTOR; config.PowerLimiter_VoltageLoadCorrectionFactor = powerlimiter["voltage_load_correction_factor"] | POWERLIMITER_VOLTAGE_LOAD_CORRECTION_FACTOR;
config.PowerLimiter_RestartHour = powerlimiter["inverter_restart_hour"] | POWERLIMITER_RESTART_HOUR;
JsonObject battery = doc["battery"]; JsonObject battery = doc["battery"];
config.Battery_Enabled = battery["enabled"] | BATTERY_ENABLED; config.Battery_Enabled = battery["enabled"] | BATTERY_ENABLED;

View File

@ -53,6 +53,27 @@ void PowerLimiterClass::loop()
return; return;
} }
// Check if next inverter restart time is reached
if ((_nextInverterRestart > 1) && (_nextInverterRestart <= millis())) {
MessageOutput.println("[PowerLimiterClass::loop] send inverter restart");
inverter->sendRestartControlRequest();
calcNextInverterRestart();
}
// Check if NTP time is set and next inverter restart not calculated yet
if ((config.PowerLimiter_RestartHour >= 0) && (_nextInverterRestart == 0) ) {
// check every 5 seconds
if (_nextCalculateCheck < millis()) {
struct tm timeinfo;
if (getLocalTime(&timeinfo, 5)) {
calcNextInverterRestart();
} else {
MessageOutput.println("[PowerLimiterClass::loop] inverter restart calculation: NTP not ready");
_nextCalculateCheck += 5000;
}
}
}
// Make sure inverter is turned off if PL is disabled by user/MQTT // Make sure inverter is turned off if PL is disabled by user/MQTT
if (((!config.PowerLimiter_Enabled || _disabled) && _plState != SHUTDOWN)) { if (((!config.PowerLimiter_Enabled || _disabled) && _plState != SHUTDOWN)) {
if (inverter->isProducing()) { if (inverter->isProducing()) {
@ -385,3 +406,43 @@ bool PowerLimiterClass::isStopThresholdReached(std::shared_ptr<InverterAbstract>
float correctedDcVoltage = getLoadCorrectedVoltage(inverter); float correctedDcVoltage = getLoadCorrectedVoltage(inverter);
return correctedDcVoltage <= config.PowerLimiter_VoltageStopThreshold; return correctedDcVoltage <= config.PowerLimiter_VoltageStopThreshold;
} }
/// @brief calculate next inverter restart in millis
void PowerLimiterClass::calcNextInverterRestart()
{
CONFIG_T& config = Configuration.get();
// first check if restart is configured at all
if (config.PowerLimiter_RestartHour < 0) {
_nextInverterRestart = 1;
MessageOutput.println("[PowerLimiterClass::calcNextInverterRestart] _nextInverterRestart disabled");
return;
}
// read time from timeserver, if time is not synced then return
struct tm timeinfo;
if (getLocalTime(&timeinfo, 5)) {
// calculation first step is offset to next restart in minutes
uint16_t dayMinutes = timeinfo.tm_hour * 60 + timeinfo.tm_min;
uint16_t targetMinutes = config.PowerLimiter_RestartHour * 60;
if (config.PowerLimiter_RestartHour > timeinfo.tm_hour) {
// next restart is on the same day
_nextInverterRestart = targetMinutes - dayMinutes;
} else {
// next restart is on next day
_nextInverterRestart = 1440 - dayMinutes + targetMinutes;
}
#ifdef POWER_LIMITER_DEBUG
MessageOutput.printf("[PowerLimiterClass::calcNextInverterRestart] Localtime read %d %d / configured RestartHour %d\r\n", timeinfo.tm_hour, timeinfo.tm_min, config.PowerLimiter_RestartHour);
MessageOutput.printf("[PowerLimiterClass::calcNextInverterRestart] dayMinutes %d / targetMinutes %d\r\n", dayMinutes, targetMinutes);
MessageOutput.printf("[PowerLimiterClass::calcNextInverterRestart] next inverter restart in %d minutes\r\n", _nextInverterRestart);
#endif
// then convert unit for next restart to milliseconds and add current uptime millis()
_nextInverterRestart *= 60000;
_nextInverterRestart += millis();
} else {
MessageOutput.println("[PowerLimiterClass::calcNextInverterRestart] getLocalTime not successful, no calculation");
_nextInverterRestart = 0;
}
MessageOutput.printf("[PowerLimiterClass::calcNextInverterRestart] _nextInverterRestart @ %d millis\r\n", _nextInverterRestart);
}

View File

@ -52,6 +52,7 @@ void WebApiPowerLimiterClass::onStatus(AsyncWebServerRequest* request)
root[F("voltage_start_threshold")] = static_cast<int>(config.PowerLimiter_VoltageStartThreshold * 100 +0.5) / 100.0; root[F("voltage_start_threshold")] = static_cast<int>(config.PowerLimiter_VoltageStartThreshold * 100 +0.5) / 100.0;
root[F("voltage_stop_threshold")] = static_cast<int>(config.PowerLimiter_VoltageStopThreshold * 100 +0.5) / 100.0;; root[F("voltage_stop_threshold")] = static_cast<int>(config.PowerLimiter_VoltageStopThreshold * 100 +0.5) / 100.0;;
root[F("voltage_load_correction_factor")] = config.PowerLimiter_VoltageLoadCorrectionFactor; root[F("voltage_load_correction_factor")] = config.PowerLimiter_VoltageLoadCorrectionFactor;
root[F("inverter_restart_hour")] = config.PowerLimiter_RestartHour;
response->setLength(); response->setLength();
request->send(response); request->send(response);
@ -136,8 +137,11 @@ void WebApiPowerLimiterClass::onAdminPost(AsyncWebServerRequest* request)
config.PowerLimiter_VoltageStopThreshold = root[F("voltage_stop_threshold")].as<float>(); config.PowerLimiter_VoltageStopThreshold = root[F("voltage_stop_threshold")].as<float>();
config.PowerLimiter_VoltageStopThreshold = static_cast<int>(config.PowerLimiter_VoltageStopThreshold * 100) / 100.0; config.PowerLimiter_VoltageStopThreshold = static_cast<int>(config.PowerLimiter_VoltageStopThreshold * 100) / 100.0;
config.PowerLimiter_VoltageLoadCorrectionFactor = root[F("voltage_load_correction_factor")].as<float>(); config.PowerLimiter_VoltageLoadCorrectionFactor = root[F("voltage_load_correction_factor")].as<float>();
config.PowerLimiter_RestartHour = root[F("inverter_restart_hour")].as<int8_t>();
Configuration.write(); Configuration.write();
PowerLimiter.calcNextInverterRestart();
retMsg[F("type")] = F("success"); retMsg[F("type")] = F("success");
retMsg[F("message")] = F("Settings saved!"); retMsg[F("message")] = F("Settings saved!");

View File

@ -549,6 +549,9 @@
"InverterIsBehindPowerMeter": "Welchselrichter ist hinter Leistungsmesser", "InverterIsBehindPowerMeter": "Welchselrichter ist hinter Leistungsmesser",
"Battery": "DC / Akku", "Battery": "DC / Akku",
"VoltageLoadCorrectionInfo": "<b>Hinweis:</b> Wenn Leistung von der Batterie abgegeben wird, bricht normalerweise die Spannung etwas ein. Damit nicht vorzeitig der Wechelrichter ausgeschaltet wird sobald der \"Stop\"-Schwellenwert erreicht wird, wird der hier angegebene Korrekturfaktor mit einberechnet. Korrigierte Spannung = DC Spannung + (Aktuelle Leistung (W) + Korrekturfaktor).", "VoltageLoadCorrectionInfo": "<b>Hinweis:</b> Wenn Leistung von der Batterie abgegeben wird, bricht normalerweise die Spannung etwas ein. Damit nicht vorzeitig der Wechelrichter ausgeschaltet wird sobald der \"Stop\"-Schwellenwert erreicht wird, wird der hier angegebene Korrekturfaktor mit einberechnet. Korrigierte Spannung = DC Spannung + (Aktuelle Leistung (W) + Korrekturfaktor).",
"InverterRestart": "Wechselrichter Neustart",
"InverterRestartHour": "Stunde für Neustart",
"InverterRestartHint": "Neustart des Wechselrichter einmal täglich um die \"Tagesertrag\" Werte wieder auf Null zu setzen.",
"Save": "@:dtuadmin.Save" "Save": "@:dtuadmin.Save"
}, },
"batteryadmin": { "batteryadmin": {

View File

@ -553,6 +553,9 @@
"InverterIsBehindPowerMeter": "Inverter is behind Power meter", "InverterIsBehindPowerMeter": "Inverter is behind Power meter",
"Battery": "DC / Battery", "Battery": "DC / Battery",
"VoltageLoadCorrectionInfo": "<b>Hint:</b> When the power output is higher, the voltage is usually decreasing. In order to not stop the inverter too early (Stop treshold), a power factor can be specified here to correct this. Corrected voltage = DC Voltage + (Current power * correction factor).", "VoltageLoadCorrectionInfo": "<b>Hint:</b> When the power output is higher, the voltage is usually decreasing. In order to not stop the inverter too early (Stop treshold), a power factor can be specified here to correct this. Corrected voltage = DC Voltage + (Current power * correction factor).",
"InverterRestart": "Inverter Restart",
"InverterRestartHour": "Restart Hour",
"InverterRestartHint": "Restart the Inverter once a day to reset the \"YieldDay\" values.",
"Save": "@:dtuadmin.Save" "Save": "@:dtuadmin.Save"
}, },
"batteryadmin": { "batteryadmin": {

View File

@ -14,4 +14,5 @@ export interface PowerLimiterConfig {
voltage_start_threshold: number; voltage_start_threshold: number;
voltage_stop_threshold: number; voltage_stop_threshold: number;
voltage_load_correction_factor: number; voltage_load_correction_factor: number;
inverter_restart_hour: number;
} }

View File

@ -193,6 +193,24 @@
<div class="alert alert-secondary" role="alert" v-html="$t('powerlimiteradmin.VoltageLoadCorrectionInfo')"></div> <div class="alert alert-secondary" role="alert" v-html="$t('powerlimiteradmin.VoltageLoadCorrectionInfo')"></div>
</CardElement> </CardElement>
<CardElement :text="$t('powerlimiteradmin.InverterRestart')" textVariant="text-bg-primary" add-space
v-show="powerLimiterConfigList.enabled"
>
<div class="row mb-3" v-show="powerLimiterConfigList.enabled">
<label for="inputTimezone" class="col-sm-2 col-form-label">
{{ $t('powerlimiteradmin.InverterRestartHour') }}:
<BIconInfoCircle v-tooltip :title="$t('powerlimiteradmin.InverterRestartHint')" />
</label>
<div class="col-sm-10">
<select class="form-select" v-model="powerLimiterConfigList.inverter_restart_hour">
<option v-for="hour in restartHourList" :key="hour.key" :value="hour.key">
{{ hour.value }}
</option>
</select>
</div>
</div>
</CardElement>
<button type="submit" class="btn btn-primary mb-3">{{ $t('powerlimiteradmin.Save') }}</button> <button type="submit" class="btn btn-primary mb-3">{{ $t('powerlimiteradmin.Save') }}</button>
</form> </form>
</BasePage> </BasePage>
@ -243,6 +261,33 @@ export default defineComponent({
{ key: 0, value: "powerlimiteradmin.BatteryDrainWhenFull"}, { key: 0, value: "powerlimiteradmin.BatteryDrainWhenFull"},
{ key: 1, value: "powerlimiteradmin.BatteryDrainAtNight" }, { key: 1, value: "powerlimiteradmin.BatteryDrainAtNight" },
], ],
restartHourList: [
{ key: -1, value: "- - - -" },
{ key: 0, value: "0:00" },
{ key: 1, value: "1:00" },
{ key: 2, value: "2:00" },
{ key: 3, value: "3:00" },
{ key: 4, value: "4:00" },
{ key: 5, value: "5:00" },
{ key: 6, value: "6:00" },
{ key: 7, value: "7:00" },
{ key: 8, value: "8:00" },
{ key: 9, value: "9:00" },
{ key: 10, value: "10:00" },
{ key: 11, value: "11:00" },
{ key: 12, value: "12:00" },
{ key: 13, value: "13:00" },
{ key: 14, value: "14:00" },
{ key: 15, value: "15:00" },
{ key: 16, value: "16:00" },
{ key: 17, value: "17:00" },
{ key: 18, value: "18:00" },
{ key: 19, value: "19:00" },
{ key: 20, value: "20:00" },
{ key: 21, value: "21:00" },
{ key: 22, value: "22:00" },
{ key: 23, value: "23:00" },
],
alertMessage: "", alertMessage: "",
alertType: "info", alertType: "info",
showAlert: false, showAlert: false,