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:
Bernhard Kirchen 2023-08-04 12:35:37 +02:00 committed by GitHub
parent 5335ec1bde
commit aff7924411
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 105 additions and 72 deletions

View File

@ -6,6 +6,7 @@
#include <Arduino.h> #include <Arduino.h>
#include <Hoymiles.h> #include <Hoymiles.h>
#include <memory> #include <memory>
#include <functional>
#define PL_UI_STATE_INACTIVE 0 #define PL_UI_STATE_INACTIVE 0
#define PL_UI_STATE_CHARGING 1 #define PL_UI_STATE_CHARGING 1
@ -81,9 +82,12 @@ private:
void commitPowerLimit(std::shared_ptr<InverterAbstract> inverter, int32_t limit, bool enablePowerProduction); void commitPowerLimit(std::shared_ptr<InverterAbstract> inverter, int32_t limit, bool enablePowerProduction);
bool setNewPowerLimit(std::shared_ptr<InverterAbstract> inverter, int32_t newPowerLimit); bool setNewPowerLimit(std::shared_ptr<InverterAbstract> inverter, int32_t newPowerLimit);
int32_t getSolarChargePower(); int32_t getSolarChargePower();
float getLoadCorrectedVoltage(std::shared_ptr<InverterAbstract> inverter); float getLoadCorrectedVoltage();
bool isStartThresholdReached(std::shared_ptr<InverterAbstract> inverter); bool testThreshold(float socThreshold, float voltThreshold,
bool isStopThresholdReached(std::shared_ptr<InverterAbstract> inverter); std::function<bool(float, float)> compare);
bool isStartThresholdReached();
bool isStopThresholdReached();
bool isBelowStopThreshold();
bool useFullSolarPassthrough(std::shared_ptr<InverterAbstract> inverter); bool useFullSolarPassthrough(std::shared_ptr<InverterAbstract> inverter);
}; };

View File

@ -228,12 +228,12 @@ void PowerLimiterClass::loop()
} }
if (_verboseLogging) { if (_verboseLogging) {
MessageOutput.println("[PowerLimiterClass::loop] ******************* ENTER **********************"); MessageOutput.println("[DPL::loop] ******************* ENTER **********************");
} }
// Check if next inverter restart time is reached // Check if next inverter restart time is reached
if ((_nextInverterRestart > 1) && (_nextInverterRestart <= millis())) { if ((_nextInverterRestart > 1) && (_nextInverterRestart <= millis())) {
MessageOutput.println("[PowerLimiterClass::loop] send inverter restart"); MessageOutput.println("[DPL::loop] send inverter restart");
_inverter->sendRestartControlRequest(); _inverter->sendRestartControlRequest();
calcNextInverterRestart(); calcNextInverterRestart();
} }
@ -246,34 +246,27 @@ void PowerLimiterClass::loop()
if (getLocalTime(&timeinfo, 5)) { if (getLocalTime(&timeinfo, 5)) {
calcNextInverterRestart(); calcNextInverterRestart();
} else { } else {
MessageOutput.println("[PowerLimiterClass::loop] inverter restart calculation: NTP not ready"); MessageOutput.println("[DPL::loop] inverter restart calculation: NTP not ready");
_nextCalculateCheck += 5000; _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 // Battery charging cycle conditions
// First we always disable discharge if the battery is empty // First we always disable discharge if the battery is empty
if (isStopThresholdReached(_inverter)) { if (isStopThresholdReached()) {
// Disable battery discharge when empty // Disable battery discharge when empty
_batteryDischargeEnabled = false; _batteryDischargeEnabled = false;
} else { } else {
// UI: Solar Passthrough Enabled -> false // UI: Solar Passthrough Enabled -> false
// Battery discharge can be enabled when start threshold is reached // Battery discharge can be enabled when start threshold is reached
if (!config.PowerLimiter_SolarPassThroughEnabled && isStartThresholdReached(_inverter)) { if (!config.PowerLimiter_SolarPassThroughEnabled && isStartThresholdReached()) {
_batteryDischargeEnabled = true; _batteryDischargeEnabled = true;
} }
// UI: Solar Passthrough Enabled -> true && EMPTY_AT_NIGHT // UI: Solar Passthrough Enabled -> true && EMPTY_AT_NIGHT
if (config.PowerLimiter_SolarPassThroughEnabled && config.PowerLimiter_BatteryDrainStategy == 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 // In this case we should only discharge the battery as long it is above startThreshold
_batteryDischargeEnabled = true; _batteryDischargeEnabled = true;
} }
@ -285,24 +278,46 @@ void PowerLimiterClass::loop()
// UI: Solar Passthrough Enabled -> true && EMPTY_WHEN_FULL // UI: Solar Passthrough Enabled -> true && EMPTY_WHEN_FULL
// Battery discharge can be enabled when start threshold is reached // 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; _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); int32_t newPowerLimit = calcPowerLimit(_inverter, canUseDirectSolarPower(), _batteryDischargeEnabled);
bool limitUpdated = setNewPowerLimit(_inverter, newPowerLimit); bool limitUpdated = setNewPowerLimit(_inverter, newPowerLimit);
if (_verboseLogging) { if (_verboseLogging) {
MessageOutput.printf("[PowerLimiterClass::loop] Status: SolarPT enabled %i, Drain Strategy: %i, canUseDirectSolarPower: %i, Batt discharge: %i\r\n", MessageOutput.printf("[DPL::loop] ******************* Leaving PL, calculated limit: %d W, requested limit: %d W (%s)\r\n",
config.PowerLimiter_SolarPassThroughEnabled, config.PowerLimiter_BatteryDrainStategy, canUseDirectSolarPower(), _batteryDischargeEnabled); newPowerLimit, _lastRequestedPowerLimit,
// the inverter might have been reset to nullptr due to a shutdown (limitUpdated?"updated from calculated":"kept last requested"));
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()));
} }
_lastCalculation = millis(); _lastCalculation = millis();
@ -396,6 +411,7 @@ bool PowerLimiterClass::canUseDirectSolarPower()
CONFIG_T& config = Configuration.get(); CONFIG_T& config = Configuration.get();
if (!config.PowerLimiter_SolarPassThroughEnabled if (!config.PowerLimiter_SolarPassThroughEnabled
|| isBelowStopThreshold()
|| !config.Vedirect_Enabled || !config.Vedirect_Enabled
|| !VeDirect.isDataValid()) { || !VeDirect.isDataValid()) {
return false; return false;
@ -463,17 +479,13 @@ int32_t PowerLimiterClass::calcPowerLimit(std::shared_ptr<InverterAbstract> inve
if (solarPowerEnabled && !batteryDischargeEnabled) { if (solarPowerEnabled && !batteryDischargeEnabled) {
// Case 2 - Limit power to solar power only // Case 2 - Limit power to solar power only
if (_verboseLogging) { 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); adjustedVictronChargePower, newPowerLimit);
} }
newPowerLimit = std::min(newPowerLimit, adjustedVictronChargePower); newPowerLimit = std::min(newPowerLimit, adjustedVictronChargePower);
} }
if (_verboseLogging) {
MessageOutput.printf("[PowerLimiterClass::loop] newPowerLimit: %d\r\n", newPowerLimit);
}
return newPowerLimit; return newPowerLimit;
} }
@ -482,7 +494,7 @@ void PowerLimiterClass::commitPowerLimit(std::shared_ptr<InverterAbstract> inver
// disable power production as soon as possible. // disable power production as soon as possible.
// setting the power limit is less important. // setting the power limit is less important.
if (!enablePowerProduction && inverter->isProducing()) { if (!enablePowerProduction && inverter->isProducing()) {
MessageOutput.println("[PowerLimiterClass::commitPowerLimit] Stopping inverter..."); MessageOutput.println("[DPL::commitPowerLimit] Stopping inverter...");
inverter->sendPowerControlRequest(false); inverter->sendPowerControlRequest(false);
} }
@ -494,7 +506,7 @@ void PowerLimiterClass::commitPowerLimit(std::shared_ptr<InverterAbstract> inver
// enable power production only after setting the desired limit, // enable power production only after setting the desired limit,
// such that an older, greater limit will not cause power spikes. // such that an older, greater limit will not cause power spikes.
if (enablePowerProduction && !inverter->isProducing()) { if (enablePowerProduction && !inverter->isProducing()) {
MessageOutput.println("[PowerLimiterClass::commitPowerLimit] Starting up inverter..."); MessageOutput.println("[DPL::commitPowerLimit] Starting up inverter...");
inverter->sendPowerControlRequest(true); inverter->sendPowerControlRequest(true);
} }
} }
@ -531,7 +543,7 @@ bool PowerLimiterClass::setNewPowerLimit(std::shared_ptr<InverterAbstract> inver
} }
} }
if ((dcProdChnls > 0) && (dcProdChnls != dcTotalChnls)) { 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); dcTotalChnls, dcProdChnls);
effPowerLimit = round(effPowerLimit * static_cast<float>(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); auto diff = std::abs(effPowerLimit - _lastRequestedPowerLimit);
if ( diff < config.PowerLimiter_TargetPowerConsumptionHysteresis) { if ( diff < config.PowerLimiter_TargetPowerConsumptionHysteresis) {
if (_verboseLogging) { 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); _lastRequestedPowerLimit, diff, config.PowerLimiter_TargetPowerConsumptionHysteresis);
} }
return false; return false;
} }
if (_verboseLogging) { 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); effPowerLimit, newPowerLimit);
} }
@ -566,12 +578,19 @@ int32_t PowerLimiterClass::getSolarChargePower()
return VeDirect.veFrame.V * VeDirect.veFrame.I; 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(); CONFIG_T& config = Configuration.get();
float acPower = inverter->Statistics()->getChannelFieldValue(TYPE_AC, (ChannelNum_t) config.PowerLimiter_InverterChannelId, FLD_PAC); auto channel = static_cast<ChannelNum_t>(config.PowerLimiter_InverterChannelId);
float dcVoltage = inverter->Statistics()->getChannelFieldValue(TYPE_DC, (ChannelNum_t) config.PowerLimiter_InverterChannelId, FLD_UDC); float acPower = _inverter->Statistics()->getChannelFieldValue(TYPE_AC, channel, FLD_PAC);
float dcVoltage = _inverter->Statistics()->getChannelFieldValue(TYPE_DC, channel, FLD_UDC);
if (dcVoltage <= 0.0) { if (dcVoltage <= 0.0) {
return 0.0; return 0.0;
@ -580,44 +599,54 @@ float PowerLimiterClass::getLoadCorrectedVoltage(std::shared_ptr<InverterAbstrac
return dcVoltage + (acPower * config.PowerLimiter_VoltageLoadCorrectionFactor); 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(); CONFIG_T& config = Configuration.get();
// Check if the Battery interface is enabled and the SOC start threshold is reached // prefer SoC provided through battery interface
if (config.Battery_Enabled if (config.Battery_Enabled && socThreshold > 0.0
&& config.PowerLimiter_BatterySocStartThreshold > 0.0
&& (millis() - Battery.stateOfChargeLastUpdate) < 60000) { && (millis() - Battery.stateOfChargeLastUpdate) < 60000) {
return Battery.stateOfCharge >= config.PowerLimiter_BatterySocStartThreshold; return compare(Battery.stateOfCharge, socThreshold);
} }
// Otherwise we use the voltage threshold // use voltage threshold as fallback
if (config.PowerLimiter_VoltageStartThreshold <= 0.0) { if (voltThreshold <= 0.0) { return false; }
return false;
}
float correctedDcVoltage = getLoadCorrectedVoltage(inverter); return compare(getLoadCorrectedVoltage(), voltThreshold);
return correctedDcVoltage >= config.PowerLimiter_VoltageStartThreshold;
} }
bool PowerLimiterClass::isStopThresholdReached(std::shared_ptr<InverterAbstract> inverter) bool PowerLimiterClass::isStartThresholdReached()
{ {
CONFIG_T& config = Configuration.get(); CONFIG_T& config = Configuration.get();
// Check if the Battery interface is enabled and the SOC stop threshold is reached return testThreshold(
if (config.Battery_Enabled config.PowerLimiter_BatterySocStartThreshold,
&& config.PowerLimiter_BatterySocStopThreshold > 0.0 config.PowerLimiter_VoltageStartThreshold,
&& (millis() - Battery.stateOfChargeLastUpdate) < 60000) { [](float a, float b) -> bool { return a >= b; }
return Battery.stateOfCharge <= config.PowerLimiter_BatterySocStopThreshold; );
} }
// Otherwise we use the voltage threshold bool PowerLimiterClass::isStopThresholdReached()
if (config.PowerLimiter_VoltageStopThreshold <= 0.0) { {
return false; CONFIG_T& config = Configuration.get();
}
float correctedDcVoltage = getLoadCorrectedVoltage(inverter); return testThreshold(
return correctedDcVoltage <= config.PowerLimiter_VoltageStopThreshold; 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 /// @brief calculate next inverter restart in millis
@ -628,7 +657,7 @@ void PowerLimiterClass::calcNextInverterRestart()
// first check if restart is configured at all // first check if restart is configured at all
if (config.PowerLimiter_RestartHour < 0) { if (config.PowerLimiter_RestartHour < 0) {
_nextInverterRestart = 1; _nextInverterRestart = 1;
MessageOutput.println("[PowerLimiterClass::calcNextInverterRestart] _nextInverterRestart disabled"); MessageOutput.println("[DPL::calcNextInverterRestart] _nextInverterRestart disabled");
return; return;
} }
@ -646,18 +675,18 @@ void PowerLimiterClass::calcNextInverterRestart()
_nextInverterRestart = 1440 - dayMinutes + targetMinutes; _nextInverterRestart = 1440 - dayMinutes + targetMinutes;
} }
if (_verboseLogging) { 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("[DPL::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("[DPL::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] next inverter restart in %d minutes\r\n", _nextInverterRestart);
} }
// then convert unit for next restart to milliseconds and add current uptime millis() // then convert unit for next restart to milliseconds and add current uptime millis()
_nextInverterRestart *= 60000; _nextInverterRestart *= 60000;
_nextInverterRestart += millis(); _nextInverterRestart += millis();
} else { } else {
MessageOutput.println("[PowerLimiterClass::calcNextInverterRestart] getLocalTime not successful, no calculation"); MessageOutput.println("[DPL::calcNextInverterRestart] getLocalTime not successful, no calculation");
_nextInverterRestart = 0; _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) 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); float dcVoltage = inverter->Statistics()->getChannelFieldValue(TYPE_DC, (ChannelNum_t) config.PowerLimiter_InverterChannelId, FLD_UDC);
if (_verboseLogging) { 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); config.PowerLimiter_FullSolarPassThroughStartVoltage, config.PowerLimiter_FullSolarPassThroughStopVoltage, dcVoltage);
} }