diff --git a/include/PowerLimiterInverter.h b/include/PowerLimiterInverter.h index 97030a995..adff68091 100644 --- a/include/PowerLimiterInverter.h +++ b/include/PowerLimiterInverter.h @@ -93,6 +93,8 @@ class PowerLimiterInverter { void setTargetPowerState(bool enable) { _oTargetPowerState = enable; } void setExpectedOutputAcWatts(uint16_t power) { _expectedOutputAcWatts = power; } + static char mpptName(MpptNum_t mppt); + // copied to avoid races with web UI PowerLimiterInverterConfig _config; diff --git a/include/PowerLimiterSolarInverter.h b/include/PowerLimiterSolarInverter.h index 392615b36..72023211c 100644 --- a/include/PowerLimiterSolarInverter.h +++ b/include/PowerLimiterSolarInverter.h @@ -17,5 +17,4 @@ class PowerLimiterSolarInverter : public PowerLimiterInverter { private: uint16_t scaleLimit(uint16_t expectedOutputWatts); void setAcOutput(uint16_t expectedOutputWatts) final; - static char mpptName(MpptNum_t mppt); }; diff --git a/src/PowerLimiterInverter.cpp b/src/PowerLimiterInverter.cpp index 09c23b545..947b18205 100644 --- a/src/PowerLimiterInverter.cpp +++ b/src/PowerLimiterInverter.cpp @@ -349,4 +349,44 @@ void PowerLimiterInverter::debug() const (_oTargetPowerState.has_value()?(*_oTargetPowerState?"production":"standby"):"unchanged"), getUpdateTimeouts() ); + + MessageOutput.printf(" MPPTs AC power:"); + + auto pStats = _spInverter->Statistics(); + float inverterEfficiencyFactor = pStats->getChannelFieldValue(TYPE_INV, CH0, FLD_EFF) / 100; + std::vector dcMppts = _spInverter->getMppts(); + + for (auto& m : dcMppts) { + float mpptPowerAC = 0.0; + std::vector mpptChnls = _spInverter->getChannelsDCByMppt(m); + + for (auto& c : mpptChnls) { + mpptPowerAC += pStats->getChannelFieldValue(TYPE_DC, c, FLD_PDC) * inverterEfficiencyFactor; + } + + MessageOutput.printf(" %c: %.0f W", + mpptName(m), mpptPowerAC); + } + + MessageOutput.printf("\r\n"); +} + +char PowerLimiterInverter::mpptName(MpptNum_t mppt) +{ + switch (mppt) { + case MpptNum_t::MPPT_A: + return 'a'; + + case MpptNum_t::MPPT_B: + return 'b'; + + case MpptNum_t::MPPT_C: + return 'c'; + + case MpptNum_t::MPPT_D: + return 'd'; + + default: + return '?'; + } } diff --git a/src/PowerLimiterSolarInverter.cpp b/src/PowerLimiterSolarInverter.cpp index 32584e995..c8972b0f9 100644 --- a/src/PowerLimiterSolarInverter.cpp +++ b/src/PowerLimiterSolarInverter.cpp @@ -27,14 +27,31 @@ uint16_t PowerLimiterSolarInverter::getMaxIncreaseWatts() const return maxTotalIncrease; } + // when the current limit is less than 15% of the max power of the inverter + // the output will not match the limit as the inverters are not able to work + // with those low limits. In this case we assume that the inverter is able to + // provide more power and we return the maximum possible increase. + // thanks spcqike for creating a table that can be found here: + // https://github.com/hoylabs/OpenDTU-OnBattery/issues/1087#issuecomment-2216787552 + if (getCurrentLimitWatts() < getInverterMaxPowerWatts() * 0.15) { return maxTotalIncrease; } + auto pStats = _spInverter->Statistics(); std::vector dcMppts = _spInverter->getMppts(); size_t dcTotalMppts = dcMppts.size(); float inverterEfficiencyFactor = pStats->getChannelFieldValue(TYPE_INV, CH0, FLD_EFF) / 100; - // 98% of the expected power is good enough - auto expectedAcPowerPerMppt = (getCurrentLimitWatts() / dcTotalMppts) * 0.98; + // with 97% we are a bit less strict than when we scale the limit + auto expectedPowerPercentage = 0.97; + + // use the scaling threshold as the expected power percentage if lower, + // but only when overscaling is enabled and the inverter does not support PDL + if (_config.UseOverscaling && !_spInverter->supportsPowerDistributionLogic()) { + expectedPowerPercentage = std::min(expectedPowerPercentage, static_cast(_config.ScalingThreshold) / 100.0); + } + + // x% of the expected power is good enough + auto expectedAcPowerPerMppt = (getCurrentLimitWatts() / dcTotalMppts) * expectedPowerPercentage; size_t dcNonShadedMppts = 0; auto nonShadedMpptACPowerSum = 0.0; @@ -227,23 +244,3 @@ void PowerLimiterSolarInverter::setAcOutput(uint16_t expectedOutputWatts) setTargetPowerLimitWatts(scaleLimit(expectedOutputWatts)); setTargetPowerState(true); } - -char PowerLimiterSolarInverter::mpptName(MpptNum_t mppt) -{ - switch (mppt) { - case MpptNum_t::MPPT_A: - return 'a'; - - case MpptNum_t::MPPT_B: - return 'b'; - - case MpptNum_t::MPPT_C: - return 'c'; - - case MpptNum_t::MPPT_D: - return 'd'; - - default: - return '?'; - } -}