From e9d72204d95a23960f8bc34959753c053a7b980f Mon Sep 17 00:00:00 2001 From: wusel Date: Thu, 8 May 2025 23:33:25 +0200 Subject: [PATCH] =?UTF-8?q?BatteryPrioritizeCharge=20v0.1=20=E2=80=94=20ha?= =?UTF-8?q?ve=20DPL=20stop=20discharging=20the=20battery=20at=20sunrise,?= =?UTF-8?q?=20similar=20to=20BatteryDischargeAtNight?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/Configuration.h | 1 + include/PowerLimiter.h | 1 + include/defaults.h | 1 + src/Configuration.cpp | 2 ++ src/PowerLimiter.cpp | 38 +++++++++++++++++++--- webapp/src/locales/de.json | 2 ++ webapp/src/locales/en.json | 2 ++ webapp/src/locales/fr.json | 3 ++ webapp/src/types/PowerLimiterConfig.ts | 1 + webapp/src/views/PowerLimiterAdminView.vue | 8 +++++ 10 files changed, 55 insertions(+), 4 deletions(-) diff --git a/include/Configuration.h b/include/Configuration.h index 6a294f124..972fc8da8 100644 --- a/include/Configuration.h +++ b/include/Configuration.h @@ -159,6 +159,7 @@ struct POWERLIMITER_CONFIG_T { bool SolarPassThroughEnabled; uint8_t ConductionLosses; bool BatteryAlwaysUseAtNight; + bool BatteryPrioritizeCharge; int16_t TargetPowerConsumption; uint16_t TargetPowerConsumptionHysteresis; uint16_t BaseLoadLimit; diff --git a/include/PowerLimiter.h b/include/PowerLimiter.h index 6ba305f40..7f74aa4d5 100644 --- a/include/PowerLimiter.h +++ b/include/PowerLimiter.h @@ -75,6 +75,7 @@ class PowerLimiterClass { std::deque> _retirees; bool _batteryDischargeEnabled = false; bool _nighttimeDischarging = false; + bool _daytimeDischargingLimited = false; std::pair _nextInverterRestart = { false, 0 }; bool _fullSolarPassThroughActive = false; float _loadCorrectedVoltage = 0.0f; diff --git a/include/defaults.h b/include/defaults.h index 2d53ecc66..d90842a8b 100644 --- a/include/defaults.h +++ b/include/defaults.h @@ -130,6 +130,7 @@ #define POWERLIMITER_SOLAR_PASSTHROUGH_ENABLED false #define POWERLIMITER_CONDUCTION_LOSSES 3 #define POWERLIMITER_BATTERY_ALWAYS_USE_AT_NIGHT false +#define POWERLIMITER_BATTERY_PRIORITIZE_CHARGE false #define POWERLIMITER_IS_INVERTER_BEHIND_POWER_METER true #define POWERLIMITER_USE_OVERSCALING false #define POWERLIMITER_INVERTER_CHANNEL_ID 0 diff --git a/src/Configuration.cpp b/src/Configuration.cpp index 946ab0764..bf1ae6366 100644 --- a/src/Configuration.cpp +++ b/src/Configuration.cpp @@ -194,6 +194,7 @@ void ConfigurationClass::serializePowerLimiterConfig(PowerLimiterConfig const& s target["solar_passthrough_enabled"] = source.SolarPassThroughEnabled; target["conduction_losses"] = source.ConductionLosses; target["battery_always_use_at_night"] = source.BatteryAlwaysUseAtNight; + target["battery_prioritize_charge"] = source.BatteryPrioritizeCharge; target["target_power_consumption"] = source.TargetPowerConsumption; target["target_power_consumption_hysteresis"] = source.TargetPowerConsumptionHysteresis; target["base_load_limit"] = source.BaseLoadLimit; @@ -590,6 +591,7 @@ void ConfigurationClass::deserializePowerLimiterConfig(JsonObject const& source, target.SolarPassThroughEnabled = source["solar_passthrough_enabled"] | POWERLIMITER_SOLAR_PASSTHROUGH_ENABLED; target.ConductionLosses = source["conduction_losses"] | POWERLIMITER_CONDUCTION_LOSSES; target.BatteryAlwaysUseAtNight = source["battery_always_use_at_night"] | POWERLIMITER_BATTERY_ALWAYS_USE_AT_NIGHT; + target.BatteryPrioritizeCharge = source["battery_prioritize_charge"] | POWERLIMITER_BATTERY_PRIORITIZE_CHARGE; target.TargetPowerConsumption = source["target_power_consumption"] | POWERLIMITER_TARGET_POWER_CONSUMPTION; target.TargetPowerConsumptionHysteresis = source["target_power_consumption_hysteresis"] | POWERLIMITER_TARGET_POWER_CONSUMPTION_HYSTERESIS; target.BaseLoadLimit = source["base_load_limit"] | POWERLIMITER_BASE_LOAD_LIMIT; diff --git a/src/PowerLimiter.cpp b/src/PowerLimiter.cpp index 89b842496..e8f7e0354 100644 --- a/src/PowerLimiter.cpp +++ b/src/PowerLimiter.cpp @@ -242,9 +242,36 @@ void PowerLimiterClass::loop() auto isDayPeriod = SunPosition.isDayPeriod(); - if (_nighttimeDischarging && isDayPeriod) { - _nighttimeDischarging = false; - return isStartThresholdReached(); + // Unify _nighttimeDischarging & _daytimeDischargingLimited: + // + // if it's day + // if _nighttimeDischarging, turn that off & return isStartThresholdReached() + // if !_daytimeDischargingLimited, turn that on, maybe log and return isStartThresholdReached(), + // else + // turn daytimeDischargingLimited off + if (isDayPeriod) { + if (_nighttimeDischarging) { + _nighttimeDischarging = false; + if (_verboseLogging) { + MessageOutput.printf("[DPL] it is now daytime, %sstopping nightly discharge because %s\r\n", + isStartThresholdReached()?"not ":"", + isStartThresholdReached()?"battery is charged enough":"charge is prioritized"); + } + return isStartThresholdReached(); + } + if (config.PowerLimiter.BatteryPrioritizeCharge) { + if (!_daytimeDischargingLimited) { + _daytimeDischargingLimited = true; + if (_verboseLogging) { + MessageOutput.printf("[DPL] it is now daytime, %sstopping any discharge because %s\r\n", + isStartThresholdReached()?"not ":"", + isStartThresholdReached()?"battery is charged enough":"charge is prioritized"); + } + return isStartThresholdReached(); + } + } + } else { + _daytimeDischargingLimited = false; } if (isStopThresholdReached()) { return false; } @@ -343,10 +370,13 @@ void PowerLimiterClass::loop() config.PowerLimiter.FullSolarPassThroughStopVoltage); } - MessageOutput.printf("[DPL] start %sreached, stop %sreached, solar-passthrough %sabled, use at night %sabled and %s\r\n", + MessageOutput.printf("[DPL] start %sreached, stop %sreached, solar-passthrough %sabled, " \ + "prioritize battery charge %sabled and %s, use at night %sabled and %s\r\n", (isStartThresholdReached()?"":"NOT "), (isStopThresholdReached()?"":"NOT "), (isSolarPassThroughEnabled()?"en":"dis"), + (config.PowerLimiter.BatteryPrioritizeCharge?"en":"dis"), + (_daytimeDischargingLimited?"active":"dormant"), (config.PowerLimiter.BatteryAlwaysUseAtNight?"en":"dis"), (_nighttimeDischarging?"active":"dormant")); diff --git a/webapp/src/locales/de.json b/webapp/src/locales/de.json index a7f3cd7fe..26308c7c2 100644 --- a/webapp/src/locales/de.json +++ b/webapp/src/locales/de.json @@ -698,6 +698,8 @@ "ConductionLossesInfo": "Bei der Übertragung von Energie vom Solarladeregler oder der Batterie zum Inverter sind Leitungsverluste zu erwarten. Diese Verluste werden berücksichtigt, um besser geeignete Wechselrichterlimits zu errechnen.", "BatteryDischargeAtNight": "Batterie nachts sogar teilweise geladen nutzen", "BatteryDischargeAtNightHint": "Ermöglicht das Entladen der Batterie in der Nacht, auch wenn der Start-Schwellwert nicht erreicht wurde. Das Entladen stoppt bei Sonnenaufgang, oder sobald der Stop-Schwellwert erreicht wurde.", + "BatteryPrioritizeCharge": "Bei Sonnenaufgang ggf. Entladezyklus beenden", + "BatteryPrioritizeChargeHint": "Sollte der Start-Schwellwert bei Sonnenaufgang unterschritten sein, wird ein Entladezyklus beendet. Das Entladen wird wieder aufgenommen, solabd der Start-Schwellwert erreicht wurde.", "SolarPassthroughInfo": "Solar-Passthrough ermöglicht den unmittelbaren Verbrauch der verfügbaren Solarleistung. Dazu wird die aktuell vom Laderegler gemeldete Solarleistung als Sollwert für die Wechselrichtergesamtleistung angenommen, selbst wenn sich die Batterie in einem Ladezyklus befindet. Somit wird eine unnötige verlustbehaftete Speicherung der Energie umgangen.", "DcPowerBusSettings": "Einstellungen DC-Stromschiene", "SelectInverter": "Inverter auswählen...", diff --git a/webapp/src/locales/en.json b/webapp/src/locales/en.json index 417974e36..35bf8f094 100644 --- a/webapp/src/locales/en.json +++ b/webapp/src/locales/en.json @@ -700,6 +700,8 @@ "ConductionLossesInfo": "Conduction losses are to be expected when transferring energy from the solar charge controller or from the battery to the inverter. These losses are taken into account to calculate better suited inverter limits.", "BatteryDischargeAtNight": "Use battery at night even if only partially charged", "BatteryDischargeAtNightHint": "Allows the battery to be discharged at night even if it hasn't reached the Start Threshold. Discharging continues until sunrise or until the Stop Threshold is reached.", + "BatteryPrioritizeCharge": "Prioritize charging of the battery at day", + "BatteryPrioritizeChargeHint": "Stops a discharge cycle if the Start Threshold isn't reached at sunrise. Discharging will start again when the Start Threshold is reached.", "SolarPassthroughInfo": "Solar-Passthrough enables the immediate consumption of the available solar power. For this purpose, the solar power currently reported by the charge controller is assumed as the inverters' output power target, even if the battery is in a charge cycle. This avoids unnecessary lossy energy storage.", "DcPowerBusSettings": "DC Power Bus Settings", "SelectInverter": "Select an inverter...", diff --git a/webapp/src/locales/fr.json b/webapp/src/locales/fr.json index 049b7d2e7..f8b17bb17 100644 --- a/webapp/src/locales/fr.json +++ b/webapp/src/locales/fr.json @@ -737,6 +737,9 @@ "ConductionLosses": "Conduction Losses", "ConductionLossesInfo": "Conduction losses are to be expected when transferring energy from the solar charge controller or from the battery to the inverter. These losses are taken into account to calculate better suited inverter limits.", "BatteryDischargeAtNight": "Use battery at night even if only partially charged", + "BatteryDischargeAtNightHint": "Allows the battery to be discharged at night even if it hasn't reached the Start Threshold. Discharging continues until sunrise or until the Stop Threshold is reached.", + "BatteryPrioritizeCharge": "Prioritize charging of the battery at day", + "BatteryPrioritizeChargeHint": "Stops a discharge cycle if the Start Threshold isn't reached at sunrise. Discharging will start again when the Start Threshold is reached.", "SolarPassthroughInfo": "Solar-Passthrough enables the immediate consumption of the available solar power. For this purpose, the solar power currently reported by the charge controller is assumed as the inverters' output power target, even if the battery is in a charge cycle. This avoids unnecessary lossy energy storage.", "DcPowerBusSettings": "DC Power Bus Settings", "SelectInverter": "Select an inverter...", diff --git a/webapp/src/types/PowerLimiterConfig.ts b/webapp/src/types/PowerLimiterConfig.ts index 2040b27c3..a77681822 100644 --- a/webapp/src/types/PowerLimiterConfig.ts +++ b/webapp/src/types/PowerLimiterConfig.ts @@ -38,6 +38,7 @@ export interface PowerLimiterConfig { solar_passthrough_enabled: boolean; conduction_losses: number; battery_always_use_at_night: boolean; + battery_prioritize_charge: boolean; target_power_consumption: number; target_power_consumption_hysteresis: number; base_load_limit: number; diff --git a/webapp/src/views/PowerLimiterAdminView.vue b/webapp/src/views/PowerLimiterAdminView.vue index 687174c61..a13efaf70 100644 --- a/webapp/src/views/PowerLimiterAdminView.vue +++ b/webapp/src/views/PowerLimiterAdminView.vue @@ -234,6 +234,14 @@ type="checkbox" wide /> + +