Skip to content

Commit

Permalink
Update overscaling for better mixed setup management
Browse files Browse the repository at this point in the history
  • Loading branch information
leoai-81 authored and AndreasBoehm committed Oct 10, 2024
1 parent 28c0c9d commit 66e7af2
Show file tree
Hide file tree
Showing 10 changed files with 140 additions and 309 deletions.
1 change: 1 addition & 0 deletions include/Configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ struct CONFIG_T {
int32_t TargetPowerConsumptionHysteresis;
int32_t LowerPowerLimit;
int32_t BaseLoadLimit;
int32_t ShadedFactor;
int32_t UpperPowerLimit;
bool IgnoreSoc;
uint32_t BatterySocStartThreshold;
Expand Down
1 change: 1 addition & 0 deletions include/defaults.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@
#define POWERLIMITER_LOWER_POWER_LIMIT 10
#define POWERLIMITER_BASE_LOAD_LIMIT 100
#define POWERLIMITER_UPPER_POWER_LIMIT 800
#define POWERLIMITER_SHADED_FACTOR 60
#define POWERLIMITER_IGNORE_SOC false
#define POWERLIMITER_BATTERY_SOC_START_THRESHOLD 80
#define POWERLIMITER_BATTERY_SOC_STOP_THRESHOLD 20
Expand Down
2 changes: 2 additions & 0 deletions src/Configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ bool ConfigurationClass::write()
powerlimiter["lower_power_limit"] = config.PowerLimiter.LowerPowerLimit;
powerlimiter["base_load_limit"] = config.PowerLimiter.BaseLoadLimit;
powerlimiter["upper_power_limit"] = config.PowerLimiter.UpperPowerLimit;
powerlimiter["shaded_factor"] = config.PowerLimiter.ShadedFactor;
powerlimiter["ignore_soc"] = config.PowerLimiter.IgnoreSoc;
powerlimiter["battery_soc_start_threshold"] = config.PowerLimiter.BatterySocStartThreshold;
powerlimiter["battery_soc_stop_threshold"] = config.PowerLimiter.BatterySocStopThreshold;
Expand Down Expand Up @@ -630,6 +631,7 @@ bool ConfigurationClass::read()
config.PowerLimiter.LowerPowerLimit = powerlimiter["lower_power_limit"] | POWERLIMITER_LOWER_POWER_LIMIT;
config.PowerLimiter.BaseLoadLimit = powerlimiter["base_load_limit"] | POWERLIMITER_BASE_LOAD_LIMIT;
config.PowerLimiter.UpperPowerLimit = powerlimiter["upper_power_limit"] | POWERLIMITER_UPPER_POWER_LIMIT;
config.PowerLimiter.ShadedFactor = powerlimiter["shaded_factor"] | POWERLIMITER_SHADED_FACTOR;
config.PowerLimiter.IgnoreSoc = powerlimiter["ignore_soc"] | POWERLIMITER_IGNORE_SOC;
config.PowerLimiter.BatterySocStartThreshold = powerlimiter["battery_soc_start_threshold"] | POWERLIMITER_BATTERY_SOC_START_THRESHOLD;
config.PowerLimiter.BatterySocStopThreshold = powerlimiter["battery_soc_stop_threshold"] | POWERLIMITER_BATTERY_SOC_STOP_THRESHOLD;
Expand Down
49 changes: 22 additions & 27 deletions src/PowerLimiter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,10 @@ void PowerLimiterClass::loop()
calcNextInverterRestart();
}


// Check if NTP time is set and next inverter restart not calculated yet
if ((config.PowerLimiter.RestartHour >= 0) && (_nextInverterRestart == 0) ) {
MessageOutput.printf("[DPL::loop] Setting next restart \r\n");
// check every 5 seconds
if (_nextCalculateCheck < millis()) {
struct tm timeinfo;
Expand Down Expand Up @@ -739,13 +741,14 @@ static int32_t scalePowerLimit(std::shared_ptr<InverterAbstract> inverter, int32
// overscalling allows us to compensate for shaded panels by increasing the
// total power limit, if the inverter is solar powered.
if (allowOverscaling && isInverterSolarPowered) {
auto inverterOutputAC = inverter->Statistics()->getChannelFieldValue(TYPE_AC, CH0, FLD_PAC);
float inverterEfficiencyFactor = getInverterEfficiency(inverter);

// 98% of the expected power is good enough
auto expectedAcPowerPerChannel = (currentLimitWatts / dcTotalChnls) * 0.98;
// defined % of the expected power is good enough
auto factor_shaded=(float)config.PowerLimiter.ShadedFactor/(float)100;
auto expectedAcPowerPerChannel = (currentLimitWatts / dcTotalChnls) * (float)factor_shaded;

if (log) {
MessageOutput.printf("[DPL::scalePowerLimit] shaded factor raw %d, adapted %f \r\n",config.PowerLimiter.ShadedFactor,factor_shaded);
MessageOutput.printf("[DPL::scalePowerLimit] expected AC power per channel %f W\r\n",
expectedAcPowerPerChannel);
}
Expand All @@ -771,32 +774,30 @@ static int32_t scalePowerLimit(std::shared_ptr<InverterAbstract> inverter, int32
// we currently need.
if (dcShadedChnls == 0 || shadedChannelACPowerSum >= newLimit) { return newLimit; }

if (dcShadedChnls == dcTotalChnls) {
// keep the currentLimit when:
// - all channels are shaded
// - currentLimit >= newLimit
// - we get the expected AC power or less and
if (currentLimitWatts >= newLimit && inverterOutputAC <= newLimit) {
if (log) {
MessageOutput.printf("[DPL::scalePowerLimit] all channels are shaded, "
"keeping the current limit of %d W\r\n", currentLimitWatts);
}

return currentLimitWatts;
size_t dcNonShadedChnls = dcTotalChnls - dcShadedChnls;

} else {
return newLimit;
}
if (log) {
MessageOutput.printf("[DPL::scalePowerLimit] newLimit %d W\r\n", newLimit);
MessageOutput.printf("[DPL::scalePowerLimit] shadedChannelACPowerSum %d W\r\n", (int)shadedChannelACPowerSum);
MessageOutput.printf("[DPL::scalePowerLimit] dcNonShadedChnls %d ch\r\n", dcNonShadedChnls);
MessageOutput.printf("[DPL::scalePowerLimit] dcTotalChnls %d ch\r\n", dcTotalChnls);
}

size_t dcNonShadedChnls = dcTotalChnls - dcShadedChnls;
auto overScaledLimit = static_cast<int32_t>((newLimit - shadedChannelACPowerSum) / dcNonShadedChnls * dcTotalChnls);
// if all channels are shaded, hopefully 1 can patch-up
if (dcNonShadedChnls == 0) {
dcNonShadedChnls=1;
if (log) {
MessageOutput.printf("[DPL::scalePowerLimit] all channels are shaded, hopefully 1 can patch-up\r\n");
}
}

auto overScaledLimit = (float)(newLimit - (int)shadedChannelACPowerSum) / ((float)dcNonShadedChnls/(float)dcTotalChnls);

if (overScaledLimit <= newLimit) { return newLimit; }

if (log) {
MessageOutput.printf("[DPL::scalePowerLimit] %d/%d channels are shaded, "
"scaling %d W\r\n", dcShadedChnls, dcTotalChnls, overScaledLimit);
"scaling %f W\r\n", dcShadedChnls, dcTotalChnls, overScaledLimit);
}

return overScaledLimit;
Expand Down Expand Up @@ -1005,12 +1006,6 @@ void PowerLimiterClass::calcNextInverterRestart()
return;
}

if (config.PowerLimiter.IsInverterSolarPowered) {
_nextInverterRestart = 1;
MessageOutput.println("[DPL::calcNextInverterRestart] not restarting solar-powered inverters");
return;
}

// read time from timeserver, if time is not synced then return
struct tm timeinfo;
if (getLocalTime(&timeinfo, 5)) {
Expand Down
4 changes: 3 additions & 1 deletion src/WebApi_powerlimiter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ void WebApiPowerLimiterClass::onStatus(AsyncWebServerRequest* request)
root["lower_power_limit"] = config.PowerLimiter.LowerPowerLimit;
root["base_load_limit"] = config.PowerLimiter.BaseLoadLimit;
root["upper_power_limit"] = config.PowerLimiter.UpperPowerLimit;
root["shaded_factor"] = config.PowerLimiter.ShadedFactor;
root["ignore_soc"] = config.PowerLimiter.IgnoreSoc;
root["battery_soc_start_threshold"] = config.PowerLimiter.BatterySocStartThreshold;
root["battery_soc_stop_threshold"] = config.PowerLimiter.BatterySocStopThreshold;
Expand Down Expand Up @@ -167,7 +168,8 @@ void WebApiPowerLimiterClass::onAdminPost(AsyncWebServerRequest* request)
config.PowerLimiter.TargetPowerConsumptionHysteresis = root["target_power_consumption_hysteresis"].as<int32_t>();
config.PowerLimiter.LowerPowerLimit = root["lower_power_limit"].as<int32_t>();
config.PowerLimiter.BaseLoadLimit = root["base_load_limit"].as<int32_t>();
config.PowerLimiter.UpperPowerLimit = root["upper_power_limit"].as<int32_t>();
config.PowerLimiter.UpperPowerLimit = root["upper_power_limit"].as<int32_t>();
config.PowerLimiter.ShadedFactor = root["shaded_factor"].as<int32_t>();

if (config.Battery.Enabled) {
config.PowerLimiter.IgnoreSoc = root["ignore_soc"].as<bool>();
Expand Down
1 change: 1 addition & 0 deletions webapp/src/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,7 @@
"VoltageLoadCorrectionFactor": "Lastkorrekturfaktor",
"BatterySocInfo": "<b>Hinweis:</b> Die Akku SoC (State of Charge) Werte werden nur benutzt, wenn die Batterie-Kommunikationsschnittstelle innerhalb der letzten Minute gültige Werte geschickt hat. Andernfalls werden als Fallback-Option die Spannungseinstellungen verwendet.",
"InverterIsBehindPowerMeter": "Stromzählermessung beinhaltet Wechselrichter",
"ShadedFactor": "Factor to consider if panels are shaded 0-100, in % vs expected channel power",
"InverterIsBehindPowerMeterHint": "Aktivieren falls sich der Stromzähler-Messwert um die Ausgangsleistung des Wechselrichters verringert, wenn dieser Strom produziert. Normalerweise ist das zutreffend.",
"InverterIsSolarPowered": "Wechselrichter wird von Solarmodulen gespeist",
"UseOverscalingToCompensateShading": "Verschattung durch Überskalierung ausgleichen",
Expand Down
1 change: 1 addition & 0 deletions webapp/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,7 @@
"VoltageLoadCorrectionFactor": "Load correction factor",
"BatterySocInfo": "<b>Hint:</b> The battery SoC (State of Charge) values are only used if the battery communication interface reported SoC updates in the last minute. Otherwise the voltage thresholds will be used as fallback.",
"InverterIsBehindPowerMeter": "PowerMeter reading includes inverter output",
"ShadedFactor": "Factor to consider if panels are shaded 0-100, in % vs expected channel power",
"InverterIsBehindPowerMeterHint": "Enable this option if the power meter reading is reduced by the inverter's output when it produces power. This is typically true.",
"InverterIsSolarPowered": "Inverter is powered by solar modules",
"UseOverscalingToCompensateShading": "Compensate for shading",
Expand Down
1 change: 1 addition & 0 deletions webapp/src/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,7 @@
"VoltageLoadCorrectionFactor": "Load correction factor",
"BatterySocInfo": "<b>Hint:</b> The battery SoC (State of Charge) values are only used if the battery communication interface reported SoC updates in the last minute. Otherwise the voltage thresholds will be used as fallback.",
"InverterIsBehindPowerMeter": "PowerMeter reading includes inverter output",
"ShadedFactor": "Factor to consider if panels are shaded 0-100, in % vs expected channel power",
"InverterIsBehindPowerMeterHint": "Enable this option if the power meter reading is reduced by the inverter's output when it produces power. This is typically true.",
"InverterIsSolarPowered": "Inverter is powered by solar modules",
"VoltageThresholds": "Battery Voltage Thresholds",
Expand Down
1 change: 1 addition & 0 deletions webapp/src/types/PowerLimiterConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export interface PowerLimiterConfig {
lower_power_limit: number;
base_load_limit: number;
upper_power_limit: number;
shaded_factor: number;
ignore_soc: boolean;
battery_soc_start_threshold: number;
battery_soc_stop_threshold: number;
Expand Down
Loading

0 comments on commit 66e7af2

Please sign in to comment.