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 committed Oct 10, 2024
1 parent 28c0c9d commit 5d39c9b
Show file tree
Hide file tree
Showing 11 changed files with 46 additions and 30 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 98
#define POWERLIMITER_IGNORE_SOC false
#define POWERLIMITER_BATTERY_SOC_START_THRESHOLD 80
#define POWERLIMITER_BATTERY_SOC_STOP_THRESHOLD 20
Expand Down
2 changes: 1 addition & 1 deletion src/Battery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ float BatteryClass::getDischargeCurrentLimit()
}

if (dischargeCurrentValid) {
return dischargeCurrentLimit;
return dischargeCurrentValid;
}

return FLT_MAX;
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
48 changes: 21 additions & 27 deletions src/PowerLimiter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ void PowerLimiterClass::loop()

// 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 +740,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=static_cast<float>(config.PowerLimiter.ShadedFactor)/static_cast<float>(100);
auto expectedAcPowerPerChannel = (currentLimitWatts / dcTotalChnls) * static_cast<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 +773,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", static_cast<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 = static_cast<float>((newLimit - static_cast<int>(shadedChannelACPowerSum)) / (static_cast<float>(dcNonShadedChnls)/static_cast<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 +1005,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
14 changes: 13 additions & 1 deletion webapp/src/views/PowerLimiterAdminView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,19 @@
wide
/>

<div class="row mb-3" v-if="!powerLimiterConfigList.is_inverter_solar_powered">
<InputElement
:label="$t('powerlimiteradmin.ShadedFactor')"
v-model="powerLimiterConfigList.shaded_factor"
:tooltip="$t('powerlimiteradmin.ShadedFactor')"
placeholder="60"
:min="(0).toString()"
:max="(100).toString()"
postfix="%"
type="number"
wide
/>

<div class="row mb-3">
<label for="inverter_restart" class="col-sm-4 col-form-label">
{{ $t('powerlimiteradmin.InverterRestartHour') }}
<BIconInfoCircle v-tooltip :title="$t('powerlimiteradmin.InverterRestartHint')" />
Expand Down

0 comments on commit 5d39c9b

Please sign in to comment.