Inhibit solar passthrough while battery below stop threshold (#354)
* DPL: improve verbose logging * shorten DPL log prefix * canUseDirectSolarPower() was printed two times * _batteryDischargeEnabled was printed two times * convert boolean values to human-readable strings * add units where possible * split messages into block "before calculating new limit" and "after calculating new limit", as the latter cannot rely on _inverter being available. * order messages such that variables whose value is derived from other variables are printed later than their dependencies. * merge output into blocks (one instance near "Printout some stats") * remove more redundant info (produced in functions outside loop()) * print target grid consumption * DPL: inhibit solar passthrough while stop threshold reached * DPL: implement and use isBelowStopThreshold() we only want to inhibit solar passthrough if the SoC is *below* the stop threshold, not if it is equal to the stop threshold. otherwise, when discharging, we would discharge until the battery reached the stop threshold, then we would also inhibit solar passthrough, until the battery is charged to the SoC stop threshold plus one percent.
This commit is contained in:
parent
5335ec1bde
commit
aff7924411
@ -6,6 +6,7 @@
|
||||
#include <Arduino.h>
|
||||
#include <Hoymiles.h>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
#define PL_UI_STATE_INACTIVE 0
|
||||
#define PL_UI_STATE_CHARGING 1
|
||||
@ -81,9 +82,12 @@ private:
|
||||
void commitPowerLimit(std::shared_ptr<InverterAbstract> inverter, int32_t limit, bool enablePowerProduction);
|
||||
bool setNewPowerLimit(std::shared_ptr<InverterAbstract> inverter, int32_t newPowerLimit);
|
||||
int32_t getSolarChargePower();
|
||||
float getLoadCorrectedVoltage(std::shared_ptr<InverterAbstract> inverter);
|
||||
bool isStartThresholdReached(std::shared_ptr<InverterAbstract> inverter);
|
||||
bool isStopThresholdReached(std::shared_ptr<InverterAbstract> inverter);
|
||||
float getLoadCorrectedVoltage();
|
||||
bool testThreshold(float socThreshold, float voltThreshold,
|
||||
std::function<bool(float, float)> compare);
|
||||
bool isStartThresholdReached();
|
||||
bool isStopThresholdReached();
|
||||
bool isBelowStopThreshold();
|
||||
bool useFullSolarPassthrough(std::shared_ptr<InverterAbstract> inverter);
|
||||
};
|
||||
|
||||
|
||||
@ -228,12 +228,12 @@ void PowerLimiterClass::loop()
|
||||
}
|
||||
|
||||
if (_verboseLogging) {
|
||||
MessageOutput.println("[PowerLimiterClass::loop] ******************* ENTER **********************");
|
||||
MessageOutput.println("[DPL::loop] ******************* ENTER **********************");
|
||||
}
|
||||
|
||||
// Check if next inverter restart time is reached
|
||||
if ((_nextInverterRestart > 1) && (_nextInverterRestart <= millis())) {
|
||||
MessageOutput.println("[PowerLimiterClass::loop] send inverter restart");
|
||||
MessageOutput.println("[DPL::loop] send inverter restart");
|
||||
_inverter->sendRestartControlRequest();
|
||||
calcNextInverterRestart();
|
||||
}
|
||||
@ -246,34 +246,27 @@ void PowerLimiterClass::loop()
|
||||
if (getLocalTime(&timeinfo, 5)) {
|
||||
calcNextInverterRestart();
|
||||
} else {
|
||||
MessageOutput.println("[PowerLimiterClass::loop] inverter restart calculation: NTP not ready");
|
||||
MessageOutput.println("[DPL::loop] inverter restart calculation: NTP not ready");
|
||||
_nextCalculateCheck += 5000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Printout some stats
|
||||
if (_verboseLogging && millis() - PowerMeter.getLastPowerMeterUpdate() < (30 * 1000)) {
|
||||
float dcVoltage = _inverter->Statistics()->getChannelFieldValue(TYPE_DC, (ChannelNum_t) config.PowerLimiter_InverterChannelId, FLD_UDC);
|
||||
MessageOutput.printf("[PowerLimiterClass::loop] dcVoltage: %.2f Voltage Start Threshold: %.2f Voltage Stop Threshold: %.2f inverter->isProducing(): %d\r\n",
|
||||
dcVoltage, config.PowerLimiter_VoltageStartThreshold, config.PowerLimiter_VoltageStopThreshold, _inverter->isProducing());
|
||||
}
|
||||
|
||||
// Battery charging cycle conditions
|
||||
// First we always disable discharge if the battery is empty
|
||||
if (isStopThresholdReached(_inverter)) {
|
||||
if (isStopThresholdReached()) {
|
||||
// Disable battery discharge when empty
|
||||
_batteryDischargeEnabled = false;
|
||||
} else {
|
||||
// UI: Solar Passthrough Enabled -> false
|
||||
// Battery discharge can be enabled when start threshold is reached
|
||||
if (!config.PowerLimiter_SolarPassThroughEnabled && isStartThresholdReached(_inverter)) {
|
||||
if (!config.PowerLimiter_SolarPassThroughEnabled && isStartThresholdReached()) {
|
||||
_batteryDischargeEnabled = true;
|
||||
}
|
||||
|
||||
// UI: Solar Passthrough Enabled -> true && EMPTY_AT_NIGHT
|
||||
if (config.PowerLimiter_SolarPassThroughEnabled && config.PowerLimiter_BatteryDrainStategy == EMPTY_AT_NIGHT) {
|
||||
if(isStartThresholdReached(_inverter)) {
|
||||
if(isStartThresholdReached()) {
|
||||
// In this case we should only discharge the battery as long it is above startThreshold
|
||||
_batteryDischargeEnabled = true;
|
||||
}
|
||||
@ -285,24 +278,46 @@ void PowerLimiterClass::loop()
|
||||
|
||||
// UI: Solar Passthrough Enabled -> true && EMPTY_WHEN_FULL
|
||||
// Battery discharge can be enabled when start threshold is reached
|
||||
if (config.PowerLimiter_SolarPassThroughEnabled && isStartThresholdReached(_inverter) && config.PowerLimiter_BatteryDrainStategy == EMPTY_WHEN_FULL) {
|
||||
if (config.PowerLimiter_SolarPassThroughEnabled && isStartThresholdReached() && config.PowerLimiter_BatteryDrainStategy == EMPTY_WHEN_FULL) {
|
||||
_batteryDischargeEnabled = true;
|
||||
}
|
||||
}
|
||||
// Calculate and set Power Limit
|
||||
|
||||
if (_verboseLogging) {
|
||||
MessageOutput.printf("[DPL::loop] battery interface %s, SoC: %d %%, StartTH: %d %%, StopTH: %d %%, SoC age: %li ms\r\n",
|
||||
(config.Battery_Enabled?"enabled":"disabled"), Battery.stateOfCharge,
|
||||
config.PowerLimiter_BatterySocStartThreshold, config.PowerLimiter_BatterySocStopThreshold,
|
||||
millis() - Battery.stateOfChargeLastUpdate);
|
||||
|
||||
float dcVoltage = _inverter->Statistics()->getChannelFieldValue(TYPE_DC, (ChannelNum_t)config.PowerLimiter_InverterChannelId, FLD_UDC);
|
||||
MessageOutput.printf("[DPL::loop] dcVoltage: %.2f V, loadCorrectedVoltage: %.2f V, StartTH: %.2f V, StopTH: %.2f V\r\n",
|
||||
dcVoltage, getLoadCorrectedVoltage(),
|
||||
config.PowerLimiter_VoltageStartThreshold,
|
||||
config.PowerLimiter_VoltageStopThreshold);
|
||||
|
||||
MessageOutput.printf("[DPL::loop] StartTH reached: %s, StopTH reached: %s, inverter %s producing\r\n",
|
||||
(isStartThresholdReached()?"yes":"no"),
|
||||
(isStopThresholdReached()?"yes":"no"),
|
||||
(_inverter->isProducing()?"is":"is NOT"));
|
||||
|
||||
MessageOutput.printf("[DPL::loop] SolarPT %s, Drain Strategy: %i, canUseDirectSolarPower: %s\r\n",
|
||||
(config.PowerLimiter_SolarPassThroughEnabled?"enabled":"disabled"),
|
||||
config.PowerLimiter_BatteryDrainStategy, (canUseDirectSolarPower()?"yes":"no"));
|
||||
|
||||
MessageOutput.printf("[DPL::loop] battery discharging %s, PowerMeter: %d W, target consumption: %d W\r\n",
|
||||
(_batteryDischargeEnabled?"allowed":"prevented"),
|
||||
static_cast<int32_t>(round(PowerMeter.getPowerTotal())),
|
||||
config.PowerLimiter_TargetPowerConsumption);
|
||||
}
|
||||
|
||||
// Calculate and set Power Limit (NOTE: might reset _inverter to nullptr!)
|
||||
int32_t newPowerLimit = calcPowerLimit(_inverter, canUseDirectSolarPower(), _batteryDischargeEnabled);
|
||||
bool limitUpdated = setNewPowerLimit(_inverter, newPowerLimit);
|
||||
|
||||
if (_verboseLogging) {
|
||||
MessageOutput.printf("[PowerLimiterClass::loop] Status: SolarPT enabled %i, Drain Strategy: %i, canUseDirectSolarPower: %i, Batt discharge: %i\r\n",
|
||||
config.PowerLimiter_SolarPassThroughEnabled, config.PowerLimiter_BatteryDrainStategy, canUseDirectSolarPower(), _batteryDischargeEnabled);
|
||||
// the inverter might have been reset to nullptr due to a shutdown
|
||||
if (_inverter != nullptr) {
|
||||
MessageOutput.printf("[PowerLimiterClass::loop] Status: StartTH %i, StopTH: %i, loadCorrectedV %f\r\n",
|
||||
isStartThresholdReached(_inverter), isStopThresholdReached(_inverter), getLoadCorrectedVoltage(_inverter));
|
||||
}
|
||||
MessageOutput.printf("[PowerLimiterClass::loop] Status Batt: Ena: %i, SOC: %i, StartTH: %i, StopTH: %i, LastUpdate: %li\r\n",
|
||||
config.Battery_Enabled, Battery.stateOfCharge, config.PowerLimiter_BatterySocStartThreshold, config.PowerLimiter_BatterySocStopThreshold, millis() - Battery.stateOfChargeLastUpdate);
|
||||
MessageOutput.printf("[PowerLimiterClass::loop] ******************* Leaving PL, PL set to: %i, SP: %i, Batt: %i, PM: %f\r\n", newPowerLimit, canUseDirectSolarPower(), _batteryDischargeEnabled, round(PowerMeter.getPowerTotal()));
|
||||
MessageOutput.printf("[DPL::loop] ******************* Leaving PL, calculated limit: %d W, requested limit: %d W (%s)\r\n",
|
||||
newPowerLimit, _lastRequestedPowerLimit,
|
||||
(limitUpdated?"updated from calculated":"kept last requested"));
|
||||
}
|
||||
|
||||
_lastCalculation = millis();
|
||||
@ -396,6 +411,7 @@ bool PowerLimiterClass::canUseDirectSolarPower()
|
||||
CONFIG_T& config = Configuration.get();
|
||||
|
||||
if (!config.PowerLimiter_SolarPassThroughEnabled
|
||||
|| isBelowStopThreshold()
|
||||
|| !config.Vedirect_Enabled
|
||||
|| !VeDirect.isDataValid()) {
|
||||
return false;
|
||||
@ -463,17 +479,13 @@ int32_t PowerLimiterClass::calcPowerLimit(std::shared_ptr<InverterAbstract> inve
|
||||
if (solarPowerEnabled && !batteryDischargeEnabled) {
|
||||
// Case 2 - Limit power to solar power only
|
||||
if (_verboseLogging) {
|
||||
MessageOutput.printf("[PowerLimiterClass::loop] Consuming Solar Power Only -> adjustedVictronChargePower: %d, powerConsumption: %d \r\n",
|
||||
MessageOutput.printf("[DPL::loop] Consuming Solar Power Only -> adjustedVictronChargePower: %d W, newPowerLimit: %d W\r\n",
|
||||
adjustedVictronChargePower, newPowerLimit);
|
||||
}
|
||||
|
||||
newPowerLimit = std::min(newPowerLimit, adjustedVictronChargePower);
|
||||
}
|
||||
|
||||
if (_verboseLogging) {
|
||||
MessageOutput.printf("[PowerLimiterClass::loop] newPowerLimit: %d\r\n", newPowerLimit);
|
||||
}
|
||||
|
||||
return newPowerLimit;
|
||||
}
|
||||
|
||||
@ -482,7 +494,7 @@ void PowerLimiterClass::commitPowerLimit(std::shared_ptr<InverterAbstract> inver
|
||||
// disable power production as soon as possible.
|
||||
// setting the power limit is less important.
|
||||
if (!enablePowerProduction && inverter->isProducing()) {
|
||||
MessageOutput.println("[PowerLimiterClass::commitPowerLimit] Stopping inverter...");
|
||||
MessageOutput.println("[DPL::commitPowerLimit] Stopping inverter...");
|
||||
inverter->sendPowerControlRequest(false);
|
||||
}
|
||||
|
||||
@ -494,7 +506,7 @@ void PowerLimiterClass::commitPowerLimit(std::shared_ptr<InverterAbstract> inver
|
||||
// enable power production only after setting the desired limit,
|
||||
// such that an older, greater limit will not cause power spikes.
|
||||
if (enablePowerProduction && !inverter->isProducing()) {
|
||||
MessageOutput.println("[PowerLimiterClass::commitPowerLimit] Starting up inverter...");
|
||||
MessageOutput.println("[DPL::commitPowerLimit] Starting up inverter...");
|
||||
inverter->sendPowerControlRequest(true);
|
||||
}
|
||||
}
|
||||
@ -531,7 +543,7 @@ bool PowerLimiterClass::setNewPowerLimit(std::shared_ptr<InverterAbstract> inver
|
||||
}
|
||||
}
|
||||
if ((dcProdChnls > 0) && (dcProdChnls != dcTotalChnls)) {
|
||||
MessageOutput.printf("[PowerLimiterClass::setNewPowerLimit] %d channels total, %d producing channels, scaling power limit\r\n",
|
||||
MessageOutput.printf("[DPL::setNewPowerLimit] %d channels total, %d producing channels, scaling power limit\r\n",
|
||||
dcTotalChnls, dcProdChnls);
|
||||
effPowerLimit = round(effPowerLimit * static_cast<float>(dcTotalChnls) / dcProdChnls);
|
||||
}
|
||||
@ -542,14 +554,14 @@ bool PowerLimiterClass::setNewPowerLimit(std::shared_ptr<InverterAbstract> inver
|
||||
auto diff = std::abs(effPowerLimit - _lastRequestedPowerLimit);
|
||||
if ( diff < config.PowerLimiter_TargetPowerConsumptionHysteresis) {
|
||||
if (_verboseLogging) {
|
||||
MessageOutput.printf("[PowerLimiterClass::setNewPowerLimit] reusing old limit: %d W, diff: %d, hysteresis: %d\r\n",
|
||||
MessageOutput.printf("[DPL::setNewPowerLimit] reusing old limit: %d W, diff: %d W, hysteresis: %d W\r\n",
|
||||
_lastRequestedPowerLimit, diff, config.PowerLimiter_TargetPowerConsumptionHysteresis);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_verboseLogging) {
|
||||
MessageOutput.printf("[PowerLimiterClass::setNewPowerLimit] using new limit: %d W, requested power limit: %d\r\n",
|
||||
MessageOutput.printf("[DPL::setNewPowerLimit] using new limit: %d W, requested power limit: %d W\r\n",
|
||||
effPowerLimit, newPowerLimit);
|
||||
}
|
||||
|
||||
@ -566,12 +578,19 @@ int32_t PowerLimiterClass::getSolarChargePower()
|
||||
return VeDirect.veFrame.V * VeDirect.veFrame.I;
|
||||
}
|
||||
|
||||
float PowerLimiterClass::getLoadCorrectedVoltage(std::shared_ptr<InverterAbstract> inverter)
|
||||
float PowerLimiterClass::getLoadCorrectedVoltage()
|
||||
{
|
||||
if (!_inverter) {
|
||||
// there should be no need to call this method if no target inverter is known
|
||||
MessageOutput.println(F("DPL getLoadCorrectedVoltage: no inverter (programmer error)"));
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
CONFIG_T& config = Configuration.get();
|
||||
|
||||
float acPower = inverter->Statistics()->getChannelFieldValue(TYPE_AC, (ChannelNum_t) config.PowerLimiter_InverterChannelId, FLD_PAC);
|
||||
float dcVoltage = inverter->Statistics()->getChannelFieldValue(TYPE_DC, (ChannelNum_t) config.PowerLimiter_InverterChannelId, FLD_UDC);
|
||||
auto channel = static_cast<ChannelNum_t>(config.PowerLimiter_InverterChannelId);
|
||||
float acPower = _inverter->Statistics()->getChannelFieldValue(TYPE_AC, channel, FLD_PAC);
|
||||
float dcVoltage = _inverter->Statistics()->getChannelFieldValue(TYPE_DC, channel, FLD_UDC);
|
||||
|
||||
if (dcVoltage <= 0.0) {
|
||||
return 0.0;
|
||||
@ -580,44 +599,54 @@ float PowerLimiterClass::getLoadCorrectedVoltage(std::shared_ptr<InverterAbstrac
|
||||
return dcVoltage + (acPower * config.PowerLimiter_VoltageLoadCorrectionFactor);
|
||||
}
|
||||
|
||||
bool PowerLimiterClass::isStartThresholdReached(std::shared_ptr<InverterAbstract> inverter)
|
||||
bool PowerLimiterClass::testThreshold(float socThreshold, float voltThreshold,
|
||||
std::function<bool(float, float)> compare)
|
||||
{
|
||||
CONFIG_T& config = Configuration.get();
|
||||
|
||||
// Check if the Battery interface is enabled and the SOC start threshold is reached
|
||||
if (config.Battery_Enabled
|
||||
&& config.PowerLimiter_BatterySocStartThreshold > 0.0
|
||||
// prefer SoC provided through battery interface
|
||||
if (config.Battery_Enabled && socThreshold > 0.0
|
||||
&& (millis() - Battery.stateOfChargeLastUpdate) < 60000) {
|
||||
return Battery.stateOfCharge >= config.PowerLimiter_BatterySocStartThreshold;
|
||||
return compare(Battery.stateOfCharge, socThreshold);
|
||||
}
|
||||
|
||||
// Otherwise we use the voltage threshold
|
||||
if (config.PowerLimiter_VoltageStartThreshold <= 0.0) {
|
||||
return false;
|
||||
}
|
||||
// use voltage threshold as fallback
|
||||
if (voltThreshold <= 0.0) { return false; }
|
||||
|
||||
float correctedDcVoltage = getLoadCorrectedVoltage(inverter);
|
||||
return correctedDcVoltage >= config.PowerLimiter_VoltageStartThreshold;
|
||||
return compare(getLoadCorrectedVoltage(), voltThreshold);
|
||||
}
|
||||
|
||||
bool PowerLimiterClass::isStopThresholdReached(std::shared_ptr<InverterAbstract> inverter)
|
||||
bool PowerLimiterClass::isStartThresholdReached()
|
||||
{
|
||||
CONFIG_T& config = Configuration.get();
|
||||
|
||||
// Check if the Battery interface is enabled and the SOC stop threshold is reached
|
||||
if (config.Battery_Enabled
|
||||
&& config.PowerLimiter_BatterySocStopThreshold > 0.0
|
||||
&& (millis() - Battery.stateOfChargeLastUpdate) < 60000) {
|
||||
return Battery.stateOfCharge <= config.PowerLimiter_BatterySocStopThreshold;
|
||||
}
|
||||
return testThreshold(
|
||||
config.PowerLimiter_BatterySocStartThreshold,
|
||||
config.PowerLimiter_VoltageStartThreshold,
|
||||
[](float a, float b) -> bool { return a >= b; }
|
||||
);
|
||||
}
|
||||
|
||||
// Otherwise we use the voltage threshold
|
||||
if (config.PowerLimiter_VoltageStopThreshold <= 0.0) {
|
||||
return false;
|
||||
}
|
||||
bool PowerLimiterClass::isStopThresholdReached()
|
||||
{
|
||||
CONFIG_T& config = Configuration.get();
|
||||
|
||||
float correctedDcVoltage = getLoadCorrectedVoltage(inverter);
|
||||
return correctedDcVoltage <= config.PowerLimiter_VoltageStopThreshold;
|
||||
return testThreshold(
|
||||
config.PowerLimiter_BatterySocStopThreshold,
|
||||
config.PowerLimiter_VoltageStopThreshold,
|
||||
[](float a, float b) -> bool { return a <= b; }
|
||||
);
|
||||
}
|
||||
|
||||
bool PowerLimiterClass::isBelowStopThreshold()
|
||||
{
|
||||
CONFIG_T& config = Configuration.get();
|
||||
|
||||
return testThreshold(
|
||||
config.PowerLimiter_BatterySocStopThreshold,
|
||||
config.PowerLimiter_VoltageStopThreshold,
|
||||
[](float a, float b) -> bool { return a < b; }
|
||||
);
|
||||
}
|
||||
|
||||
/// @brief calculate next inverter restart in millis
|
||||
@ -628,7 +657,7 @@ void PowerLimiterClass::calcNextInverterRestart()
|
||||
// first check if restart is configured at all
|
||||
if (config.PowerLimiter_RestartHour < 0) {
|
||||
_nextInverterRestart = 1;
|
||||
MessageOutput.println("[PowerLimiterClass::calcNextInverterRestart] _nextInverterRestart disabled");
|
||||
MessageOutput.println("[DPL::calcNextInverterRestart] _nextInverterRestart disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -646,18 +675,18 @@ void PowerLimiterClass::calcNextInverterRestart()
|
||||
_nextInverterRestart = 1440 - dayMinutes + targetMinutes;
|
||||
}
|
||||
if (_verboseLogging) {
|
||||
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);
|
||||
MessageOutput.printf("[DPL::calcNextInverterRestart] Localtime read %d %d / configured RestartHour %d\r\n", timeinfo.tm_hour, timeinfo.tm_min, config.PowerLimiter_RestartHour);
|
||||
MessageOutput.printf("[DPL::calcNextInverterRestart] dayMinutes %d / targetMinutes %d\r\n", dayMinutes, targetMinutes);
|
||||
MessageOutput.printf("[DPL::calcNextInverterRestart] next inverter restart in %d minutes\r\n", _nextInverterRestart);
|
||||
}
|
||||
// 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");
|
||||
MessageOutput.println("[DPL::calcNextInverterRestart] getLocalTime not successful, no calculation");
|
||||
_nextInverterRestart = 0;
|
||||
}
|
||||
MessageOutput.printf("[PowerLimiterClass::calcNextInverterRestart] _nextInverterRestart @ %d millis\r\n", _nextInverterRestart);
|
||||
MessageOutput.printf("[DPL::calcNextInverterRestart] _nextInverterRestart @ %d millis\r\n", _nextInverterRestart);
|
||||
}
|
||||
|
||||
bool PowerLimiterClass::useFullSolarPassthrough(std::shared_ptr<InverterAbstract> inverter)
|
||||
@ -684,7 +713,7 @@ bool PowerLimiterClass::useFullSolarPassthrough(std::shared_ptr<InverterAbstract
|
||||
float dcVoltage = inverter->Statistics()->getChannelFieldValue(TYPE_DC, (ChannelNum_t) config.PowerLimiter_InverterChannelId, FLD_UDC);
|
||||
|
||||
if (_verboseLogging) {
|
||||
MessageOutput.printf("[PowerLimiterClass::loop] useFullSolarPassthrough: FullSolarPT Start %f, FullSolarPT Stop: %f, dcVoltage: %f\r\n",
|
||||
MessageOutput.printf("[DPL::loop] useFullSolarPassthrough: FullSolarPT Start %.2f V, FullSolarPT Stop: %.2f V, dcVoltage: %.2f V\r\n",
|
||||
config.PowerLimiter_FullSolarPassThroughStartVoltage, config.PowerLimiter_FullSolarPassThroughStopVoltage, dcVoltage);
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user