From d69763cf25e717d6d96eb49415b86aae91c56bb0 Mon Sep 17 00:00:00 2001 From: vaterlangen Date: Tue, 22 Oct 2024 20:00:44 +0200 Subject: [PATCH] Feature: Support DPL overscaling on all inverters this change allows to support overscaling for all inverters, as the configuration of inputs (which one is part of a particular MPPT) is now provided from withing the code. this information is used to implement overscaling for any of the inverters (which are generally compatible with OpenDTU(-OnBattery)). --- lib/Hoymiles/src/inverters/HERF_1CH.cpp | 14 ++++ lib/Hoymiles/src/inverters/HERF_1CH.h | 2 + lib/Hoymiles/src/inverters/HERF_2CH.cpp | 15 ++++ lib/Hoymiles/src/inverters/HERF_2CH.h | 2 + lib/Hoymiles/src/inverters/HMS_1CH.cpp | 14 ++++ lib/Hoymiles/src/inverters/HMS_1CH.h | 4 +- lib/Hoymiles/src/inverters/HMS_1CHv2.cpp | 14 ++++ lib/Hoymiles/src/inverters/HMS_1CHv2.h | 4 +- lib/Hoymiles/src/inverters/HMS_2CH.cpp | 15 ++++ lib/Hoymiles/src/inverters/HMS_2CH.h | 4 +- lib/Hoymiles/src/inverters/HMS_4CH.cpp | 17 ++++ lib/Hoymiles/src/inverters/HMS_4CH.h | 4 +- lib/Hoymiles/src/inverters/HMT_4CH.cpp | 17 ++++ lib/Hoymiles/src/inverters/HMT_4CH.h | 4 +- lib/Hoymiles/src/inverters/HMT_6CH.cpp | 19 +++++ lib/Hoymiles/src/inverters/HMT_6CH.h | 4 +- lib/Hoymiles/src/inverters/HM_1CH.cpp | 14 ++++ lib/Hoymiles/src/inverters/HM_1CH.h | 4 +- lib/Hoymiles/src/inverters/HM_2CH.cpp | 15 ++++ lib/Hoymiles/src/inverters/HM_2CH.h | 4 +- lib/Hoymiles/src/inverters/HM_4CH.cpp | 17 ++++ lib/Hoymiles/src/inverters/HM_4CH.h | 4 +- .../src/inverters/InverterAbstract.cpp | 32 ++++++++ lib/Hoymiles/src/inverters/InverterAbstract.h | 21 +++++ src/PowerLimiterInverter.cpp | 1 - src/PowerLimiterSolarInverter.cpp | 78 ++++++++++--------- 26 files changed, 298 insertions(+), 45 deletions(-) diff --git a/lib/Hoymiles/src/inverters/HERF_1CH.cpp b/lib/Hoymiles/src/inverters/HERF_1CH.cpp index 49531d99c..17e0a6183 100644 --- a/lib/Hoymiles/src/inverters/HERF_1CH.cpp +++ b/lib/Hoymiles/src/inverters/HERF_1CH.cpp @@ -29,6 +29,10 @@ static const byteAssign_t byteAssignment[] = { { TYPE_INV, CH0, FLD_EFF, UNIT_PCT, CALC_TOTAL_EFF, 0, CMD_CALC, false, 3 } }; +static const channelMetaData_t channelMetaData[] = { + { CH0, MPPT_A } +}; + HERF_1CH::HERF_1CH(HoymilesRadio* radio, const uint64_t serial) : HM_Abstract(radio, serial) {}; @@ -53,3 +57,13 @@ uint8_t HERF_1CH::getByteAssignmentSize() const { return sizeof(byteAssignment) / sizeof(byteAssignment[0]); } + +const channelMetaData_t* HERF_1CH::getChannelMetaData() const +{ + return channelMetaData; +} + +uint8_t HERF_1CH::getChannelMetaDataSize() const +{ + return sizeof(channelMetaData) / sizeof(channelMetaData[0]); +} diff --git a/lib/Hoymiles/src/inverters/HERF_1CH.h b/lib/Hoymiles/src/inverters/HERF_1CH.h index 8220272e3..fc6f1e38a 100644 --- a/lib/Hoymiles/src/inverters/HERF_1CH.h +++ b/lib/Hoymiles/src/inverters/HERF_1CH.h @@ -10,4 +10,6 @@ class HERF_1CH : public HM_Abstract { String typeName() const; const byteAssign_t* getByteAssignment() const; uint8_t getByteAssignmentSize() const; + const channelMetaData_t* getChannelMetaData() const; + uint8_t getChannelMetaDataSize() const; }; diff --git a/lib/Hoymiles/src/inverters/HERF_2CH.cpp b/lib/Hoymiles/src/inverters/HERF_2CH.cpp index f0216a643..14f4644bc 100644 --- a/lib/Hoymiles/src/inverters/HERF_2CH.cpp +++ b/lib/Hoymiles/src/inverters/HERF_2CH.cpp @@ -36,6 +36,11 @@ static const byteAssign_t byteAssignment[] = { { TYPE_INV, CH0, FLD_EFF, UNIT_PCT, CALC_TOTAL_EFF, 0, CMD_CALC, false, 3 } }; +static const channelMetaData_t channelMetaData[] = { + { CH0, MPPT_A }, + { CH1, MPPT_B } +}; + HERF_2CH::HERF_2CH(HoymilesRadio* radio, const uint64_t serial) : HM_Abstract(radio, serial) {}; @@ -60,3 +65,13 @@ uint8_t HERF_2CH::getByteAssignmentSize() const { return sizeof(byteAssignment) / sizeof(byteAssignment[0]); } + +const channelMetaData_t* HERF_2CH::getChannelMetaData() const +{ + return channelMetaData; +} + +uint8_t HERF_2CH::getChannelMetaDataSize() const +{ + return sizeof(channelMetaData) / sizeof(channelMetaData[0]); +} diff --git a/lib/Hoymiles/src/inverters/HERF_2CH.h b/lib/Hoymiles/src/inverters/HERF_2CH.h index 048ccb618..e606a8437 100644 --- a/lib/Hoymiles/src/inverters/HERF_2CH.h +++ b/lib/Hoymiles/src/inverters/HERF_2CH.h @@ -10,4 +10,6 @@ class HERF_2CH : public HM_Abstract { String typeName() const; const byteAssign_t* getByteAssignment() const; uint8_t getByteAssignmentSize() const; + const channelMetaData_t* getChannelMetaData() const; + uint8_t getChannelMetaDataSize() const; }; diff --git a/lib/Hoymiles/src/inverters/HMS_1CH.cpp b/lib/Hoymiles/src/inverters/HMS_1CH.cpp index 2c7e3857b..a04b0b7d2 100644 --- a/lib/Hoymiles/src/inverters/HMS_1CH.cpp +++ b/lib/Hoymiles/src/inverters/HMS_1CH.cpp @@ -28,6 +28,10 @@ static const byteAssign_t byteAssignment[] = { { TYPE_INV, CH0, FLD_EFF, UNIT_PCT, CALC_TOTAL_EFF, 0, CMD_CALC, false, 3 } }; +static const channelMetaData_t channelMetaData[] = { + { CH0, MPPT_A } +}; + HMS_1CH::HMS_1CH(HoymilesRadio* radio, const uint64_t serial) : HMS_Abstract(radio, serial) {}; @@ -52,3 +56,13 @@ uint8_t HMS_1CH::getByteAssignmentSize() const { return sizeof(byteAssignment) / sizeof(byteAssignment[0]); } + +const channelMetaData_t* HMS_1CH::getChannelMetaData() const +{ + return channelMetaData; +} + +uint8_t HMS_1CH::getChannelMetaDataSize() const +{ + return sizeof(channelMetaData) / sizeof(channelMetaData[0]); +} diff --git a/lib/Hoymiles/src/inverters/HMS_1CH.h b/lib/Hoymiles/src/inverters/HMS_1CH.h index a5a64c177..de53d501d 100644 --- a/lib/Hoymiles/src/inverters/HMS_1CH.h +++ b/lib/Hoymiles/src/inverters/HMS_1CH.h @@ -11,4 +11,6 @@ class HMS_1CH : public HMS_Abstract { String typeName() const; const byteAssign_t* getByteAssignment() const; uint8_t getByteAssignmentSize() const; -}; \ No newline at end of file + const channelMetaData_t* getChannelMetaData() const; + uint8_t getChannelMetaDataSize() const; +}; diff --git a/lib/Hoymiles/src/inverters/HMS_1CHv2.cpp b/lib/Hoymiles/src/inverters/HMS_1CHv2.cpp index d79d2c1d2..3716cd98f 100644 --- a/lib/Hoymiles/src/inverters/HMS_1CHv2.cpp +++ b/lib/Hoymiles/src/inverters/HMS_1CHv2.cpp @@ -28,6 +28,10 @@ static const byteAssign_t byteAssignment[] = { { TYPE_INV, CH0, FLD_EFF, UNIT_PCT, CALC_TOTAL_EFF, 0, CMD_CALC, false, 3 } }; +static const channelMetaData_t channelMetaData[] = { + { CH0, MPPT_A } +}; + HMS_1CHv2::HMS_1CHv2(HoymilesRadio* radio, const uint64_t serial) : HMS_Abstract(radio, serial) {}; @@ -52,3 +56,13 @@ uint8_t HMS_1CHv2::getByteAssignmentSize() const { return sizeof(byteAssignment) / sizeof(byteAssignment[0]); } + +const channelMetaData_t* HMS_1CHv2::getChannelMetaData() const +{ + return channelMetaData; +} + +uint8_t HMS_1CHv2::getChannelMetaDataSize() const +{ + return sizeof(channelMetaData) / sizeof(channelMetaData[0]); +} diff --git a/lib/Hoymiles/src/inverters/HMS_1CHv2.h b/lib/Hoymiles/src/inverters/HMS_1CHv2.h index c831d1204..2274ae53c 100644 --- a/lib/Hoymiles/src/inverters/HMS_1CHv2.h +++ b/lib/Hoymiles/src/inverters/HMS_1CHv2.h @@ -11,4 +11,6 @@ class HMS_1CHv2 : public HMS_Abstract { String typeName() const; const byteAssign_t* getByteAssignment() const; uint8_t getByteAssignmentSize() const; -}; \ No newline at end of file + const channelMetaData_t* getChannelMetaData() const; + uint8_t getChannelMetaDataSize() const; +}; diff --git a/lib/Hoymiles/src/inverters/HMS_2CH.cpp b/lib/Hoymiles/src/inverters/HMS_2CH.cpp index 4cbc686cd..c22fcf0d4 100644 --- a/lib/Hoymiles/src/inverters/HMS_2CH.cpp +++ b/lib/Hoymiles/src/inverters/HMS_2CH.cpp @@ -35,6 +35,11 @@ static const byteAssign_t byteAssignment[] = { { TYPE_INV, CH0, FLD_EFF, UNIT_PCT, CALC_TOTAL_EFF, 0, CMD_CALC, false, 3 } }; +static const channelMetaData_t channelMetaData[] = { + { CH0, MPPT_A }, + { CH1, MPPT_B } +}; + HMS_2CH::HMS_2CH(HoymilesRadio* radio, const uint64_t serial) : HMS_Abstract(radio, serial) {}; @@ -59,3 +64,13 @@ uint8_t HMS_2CH::getByteAssignmentSize() const { return sizeof(byteAssignment) / sizeof(byteAssignment[0]); } + +const channelMetaData_t* HMS_2CH::getChannelMetaData() const +{ + return channelMetaData; +} + +uint8_t HMS_2CH::getChannelMetaDataSize() const +{ + return sizeof(channelMetaData) / sizeof(channelMetaData[0]); +} diff --git a/lib/Hoymiles/src/inverters/HMS_2CH.h b/lib/Hoymiles/src/inverters/HMS_2CH.h index 9f1ed91f6..75d3f9b0b 100644 --- a/lib/Hoymiles/src/inverters/HMS_2CH.h +++ b/lib/Hoymiles/src/inverters/HMS_2CH.h @@ -11,4 +11,6 @@ class HMS_2CH : public HMS_Abstract { String typeName() const; const byteAssign_t* getByteAssignment() const; uint8_t getByteAssignmentSize() const; -}; \ No newline at end of file + const channelMetaData_t* getChannelMetaData() const; + uint8_t getChannelMetaDataSize() const; +}; diff --git a/lib/Hoymiles/src/inverters/HMS_4CH.cpp b/lib/Hoymiles/src/inverters/HMS_4CH.cpp index b3cf1f380..1616d013e 100644 --- a/lib/Hoymiles/src/inverters/HMS_4CH.cpp +++ b/lib/Hoymiles/src/inverters/HMS_4CH.cpp @@ -49,6 +49,13 @@ static const byteAssign_t byteAssignment[] = { { TYPE_INV, CH0, FLD_EFF, UNIT_PCT, CALC_TOTAL_EFF, 0, CMD_CALC, false, 3 } }; +static const channelMetaData_t channelMetaData[] = { + { CH0, MPPT_A }, + { CH1, MPPT_B }, + { CH2, MPPT_C }, + { CH3, MPPT_D } +}; + HMS_4CH::HMS_4CH(HoymilesRadio* radio, const uint64_t serial) : HMS_Abstract(radio, serial) {}; @@ -73,3 +80,13 @@ uint8_t HMS_4CH::getByteAssignmentSize() const { return sizeof(byteAssignment) / sizeof(byteAssignment[0]); } + +const channelMetaData_t* HMS_4CH::getChannelMetaData() const +{ + return channelMetaData; +} + +uint8_t HMS_4CH::getChannelMetaDataSize() const +{ + return sizeof(channelMetaData) / sizeof(channelMetaData[0]); +} diff --git a/lib/Hoymiles/src/inverters/HMS_4CH.h b/lib/Hoymiles/src/inverters/HMS_4CH.h index 9d49de07a..cb7573390 100644 --- a/lib/Hoymiles/src/inverters/HMS_4CH.h +++ b/lib/Hoymiles/src/inverters/HMS_4CH.h @@ -10,4 +10,6 @@ class HMS_4CH : public HMS_Abstract { String typeName() const; const byteAssign_t* getByteAssignment() const; uint8_t getByteAssignmentSize() const; -}; \ No newline at end of file + const channelMetaData_t* getChannelMetaData() const; + uint8_t getChannelMetaDataSize() const; +}; diff --git a/lib/Hoymiles/src/inverters/HMT_4CH.cpp b/lib/Hoymiles/src/inverters/HMT_4CH.cpp index c84eff478..48caf8ce8 100644 --- a/lib/Hoymiles/src/inverters/HMT_4CH.cpp +++ b/lib/Hoymiles/src/inverters/HMT_4CH.cpp @@ -58,6 +58,13 @@ static const byteAssign_t byteAssignment[] = { { TYPE_INV, CH0, FLD_EFF, UNIT_PCT, CALC_TOTAL_EFF, 0, CMD_CALC, false, 3 } }; +static const channelMetaData_t channelMetaData[] = { + { CH0, MPPT_A }, + { CH1, MPPT_A }, + { CH2, MPPT_B }, + { CH3, MPPT_B } +}; + HMT_4CH::HMT_4CH(HoymilesRadio* radio, const uint64_t serial) : HMT_Abstract(radio, serial) {}; @@ -82,3 +89,13 @@ uint8_t HMT_4CH::getByteAssignmentSize() const { return sizeof(byteAssignment) / sizeof(byteAssignment[0]); } + +const channelMetaData_t* HMT_4CH::getChannelMetaData() const +{ + return channelMetaData; +} + +uint8_t HMT_4CH::getChannelMetaDataSize() const +{ + return sizeof(channelMetaData) / sizeof(channelMetaData[0]); +} diff --git a/lib/Hoymiles/src/inverters/HMT_4CH.h b/lib/Hoymiles/src/inverters/HMT_4CH.h index 01d328938..8f26c14b4 100644 --- a/lib/Hoymiles/src/inverters/HMT_4CH.h +++ b/lib/Hoymiles/src/inverters/HMT_4CH.h @@ -10,4 +10,6 @@ class HMT_4CH : public HMT_Abstract { String typeName() const; const byteAssign_t* getByteAssignment() const; uint8_t getByteAssignmentSize() const; -}; \ No newline at end of file + const channelMetaData_t* getChannelMetaData() const; + uint8_t getChannelMetaDataSize() const; +}; diff --git a/lib/Hoymiles/src/inverters/HMT_6CH.cpp b/lib/Hoymiles/src/inverters/HMT_6CH.cpp index 2c3dd5f3a..9e835671d 100644 --- a/lib/Hoymiles/src/inverters/HMT_6CH.cpp +++ b/lib/Hoymiles/src/inverters/HMT_6CH.cpp @@ -72,6 +72,15 @@ static const byteAssign_t byteAssignment[] = { { TYPE_INV, CH0, FLD_EFF, UNIT_PCT, CALC_TOTAL_EFF, 0, CMD_CALC, false, 3 } }; +static const channelMetaData_t channelMetaData[] = { + { CH0, MPPT_A }, + { CH1, MPPT_A }, + { CH2, MPPT_B }, + { CH3, MPPT_B }, + { CH4, MPPT_C }, + { CH5, MPPT_C } +}; + HMT_6CH::HMT_6CH(HoymilesRadio* radio, const uint64_t serial) : HMT_Abstract(radio, serial) {}; @@ -96,3 +105,13 @@ uint8_t HMT_6CH::getByteAssignmentSize() const { return sizeof(byteAssignment) / sizeof(byteAssignment[0]); } + +const channelMetaData_t* HMT_6CH::getChannelMetaData() const +{ + return channelMetaData; +} + +uint8_t HMT_6CH::getChannelMetaDataSize() const +{ + return sizeof(channelMetaData) / sizeof(channelMetaData[0]); +} diff --git a/lib/Hoymiles/src/inverters/HMT_6CH.h b/lib/Hoymiles/src/inverters/HMT_6CH.h index 6b7280068..ab8491743 100644 --- a/lib/Hoymiles/src/inverters/HMT_6CH.h +++ b/lib/Hoymiles/src/inverters/HMT_6CH.h @@ -10,4 +10,6 @@ class HMT_6CH : public HMT_Abstract { String typeName() const; const byteAssign_t* getByteAssignment() const; uint8_t getByteAssignmentSize() const; -}; \ No newline at end of file + const channelMetaData_t* getChannelMetaData() const; + uint8_t getChannelMetaDataSize() const; +}; diff --git a/lib/Hoymiles/src/inverters/HM_1CH.cpp b/lib/Hoymiles/src/inverters/HM_1CH.cpp index 0f0c64c23..cbf31569f 100644 --- a/lib/Hoymiles/src/inverters/HM_1CH.cpp +++ b/lib/Hoymiles/src/inverters/HM_1CH.cpp @@ -28,6 +28,10 @@ static const byteAssign_t byteAssignment[] = { { TYPE_INV, CH0, FLD_EFF, UNIT_PCT, CALC_TOTAL_EFF, 0, CMD_CALC, false, 3 } }; +static const channelMetaData_t channelMetaData[] = { + { CH0, MPPT_A } +}; + HM_1CH::HM_1CH(HoymilesRadio* radio, const uint64_t serial) : HM_Abstract(radio, serial) {}; @@ -65,3 +69,13 @@ uint8_t HM_1CH::getByteAssignmentSize() const { return sizeof(byteAssignment) / sizeof(byteAssignment[0]); } + +const channelMetaData_t* HM_1CH::getChannelMetaData() const +{ + return channelMetaData; +} + +uint8_t HM_1CH::getChannelMetaDataSize() const +{ + return sizeof(channelMetaData) / sizeof(channelMetaData[0]); +} diff --git a/lib/Hoymiles/src/inverters/HM_1CH.h b/lib/Hoymiles/src/inverters/HM_1CH.h index a35b4e568..60724aab0 100644 --- a/lib/Hoymiles/src/inverters/HM_1CH.h +++ b/lib/Hoymiles/src/inverters/HM_1CH.h @@ -11,4 +11,6 @@ class HM_1CH : public HM_Abstract { String typeName() const; const byteAssign_t* getByteAssignment() const; uint8_t getByteAssignmentSize() const; -}; \ No newline at end of file + const channelMetaData_t* getChannelMetaData() const; + uint8_t getChannelMetaDataSize() const; +}; diff --git a/lib/Hoymiles/src/inverters/HM_2CH.cpp b/lib/Hoymiles/src/inverters/HM_2CH.cpp index 02dd8ae4f..644106928 100644 --- a/lib/Hoymiles/src/inverters/HM_2CH.cpp +++ b/lib/Hoymiles/src/inverters/HM_2CH.cpp @@ -36,6 +36,11 @@ static const byteAssign_t byteAssignment[] = { { TYPE_INV, CH0, FLD_EFF, UNIT_PCT, CALC_TOTAL_EFF, 0, CMD_CALC, false, 3 } }; +static const channelMetaData_t channelMetaData[] = { + { CH0, MPPT_A }, + { CH1, MPPT_B } +}; + HM_2CH::HM_2CH(HoymilesRadio* radio, const uint64_t serial) : HM_Abstract(radio, serial) {}; @@ -73,3 +78,13 @@ uint8_t HM_2CH::getByteAssignmentSize() const { return sizeof(byteAssignment) / sizeof(byteAssignment[0]); } + +const channelMetaData_t* HM_2CH::getChannelMetaData() const +{ + return channelMetaData; +} + +uint8_t HM_2CH::getChannelMetaDataSize() const +{ + return sizeof(channelMetaData) / sizeof(channelMetaData[0]); +} diff --git a/lib/Hoymiles/src/inverters/HM_2CH.h b/lib/Hoymiles/src/inverters/HM_2CH.h index 1fd54496a..34e9dbc05 100644 --- a/lib/Hoymiles/src/inverters/HM_2CH.h +++ b/lib/Hoymiles/src/inverters/HM_2CH.h @@ -10,4 +10,6 @@ class HM_2CH : public HM_Abstract { String typeName() const; const byteAssign_t* getByteAssignment() const; uint8_t getByteAssignmentSize() const; -}; \ No newline at end of file + const channelMetaData_t* getChannelMetaData() const; + uint8_t getChannelMetaDataSize() const; +}; diff --git a/lib/Hoymiles/src/inverters/HM_4CH.cpp b/lib/Hoymiles/src/inverters/HM_4CH.cpp index 586248b59..2e86b1f7d 100644 --- a/lib/Hoymiles/src/inverters/HM_4CH.cpp +++ b/lib/Hoymiles/src/inverters/HM_4CH.cpp @@ -49,6 +49,13 @@ static const byteAssign_t byteAssignment[] = { { TYPE_INV, CH0, FLD_EFF, UNIT_PCT, CALC_TOTAL_EFF, 0, CMD_CALC, false, 3 } }; +static const channelMetaData_t channelMetaData[] = { + { CH0, MPPT_A }, + { CH1, MPPT_A }, + { CH2, MPPT_B }, + { CH3, MPPT_B } +}; + HM_4CH::HM_4CH(HoymilesRadio* radio, const uint64_t serial) : HM_Abstract(radio, serial) {}; @@ -86,3 +93,13 @@ uint8_t HM_4CH::getByteAssignmentSize() const { return sizeof(byteAssignment) / sizeof(byteAssignment[0]); } + +const channelMetaData_t* HM_4CH::getChannelMetaData() const +{ + return channelMetaData; +} + +uint8_t HM_4CH::getChannelMetaDataSize() const +{ + return sizeof(channelMetaData) / sizeof(channelMetaData[0]); +} diff --git a/lib/Hoymiles/src/inverters/HM_4CH.h b/lib/Hoymiles/src/inverters/HM_4CH.h index e54f33234..cce045789 100644 --- a/lib/Hoymiles/src/inverters/HM_4CH.h +++ b/lib/Hoymiles/src/inverters/HM_4CH.h @@ -10,4 +10,6 @@ class HM_4CH : public HM_Abstract { String typeName() const; const byteAssign_t* getByteAssignment() const; uint8_t getByteAssignmentSize() const; -}; \ No newline at end of file + const channelMetaData_t* getChannelMetaData() const; + uint8_t getChannelMetaDataSize() const; +}; diff --git a/lib/Hoymiles/src/inverters/InverterAbstract.cpp b/lib/Hoymiles/src/inverters/InverterAbstract.cpp index 5d52a380c..8b2002548 100644 --- a/lib/Hoymiles/src/inverters/InverterAbstract.cpp +++ b/lib/Hoymiles/src/inverters/InverterAbstract.cpp @@ -298,3 +298,35 @@ void InverterAbstract::resetRadioStats() { RadioStats = {}; } + +std::vector InverterAbstract::getChannelsDC() const +{ + std::vector l; + for (uint8_t i = 0; i < getChannelMetaDataSize(); i++) { + l.push_back(getChannelMetaData()[i].ch); + } + return l; +} + +std::vector InverterAbstract::getMppts() const +{ + std::vector l; + for (uint8_t i = 0; i < getChannelMetaDataSize(); i++) { + auto m = getChannelMetaData()[i].mppt; + if (l.end() == std::find(l.begin(), l.end(), m)){ + l.push_back(m); + } + } + return l; +} + +std::vector InverterAbstract::getChannelsDCByMppt(const MpptNum_t mppt) const +{ + std::vector l; + for (uint8_t i = 0; i < getChannelMetaDataSize(); i++) { + if (getChannelMetaData()[i].mppt == mppt) { + l.push_back(getChannelMetaData()[i].ch); + } + } + return l; +} diff --git a/lib/Hoymiles/src/inverters/InverterAbstract.h b/lib/Hoymiles/src/inverters/InverterAbstract.h index 29fba12fa..db2ed2556 100644 --- a/lib/Hoymiles/src/inverters/InverterAbstract.h +++ b/lib/Hoymiles/src/inverters/InverterAbstract.h @@ -24,6 +24,20 @@ enum { FRAGMENT_OK = 0 }; +enum MpptNum_t { + MPPT_A = 0, + MPPT_B, + MPPT_C, + MPPT_D, + MPPT_CNT +}; + +// additional meta data per input channel +typedef struct { + ChannelNum_t ch; // channel 0 - 5 + MpptNum_t mppt; // mppt a - d (0 - 3) +} channelMetaData_t; + #define MAX_RF_FRAGMENT_COUNT 13 class CommandAbstract; @@ -40,6 +54,9 @@ class InverterAbstract { virtual const byteAssign_t* getByteAssignment() const = 0; virtual uint8_t getByteAssignmentSize() const = 0; + virtual const channelMetaData_t* getChannelMetaData() const = 0; + virtual uint8_t getChannelMetaDataSize() const = 0; + bool isProducing(); bool isReachable(); @@ -112,6 +129,10 @@ class InverterAbstract { StatisticsParser* Statistics(); SystemConfigParaParser* SystemConfigPara(); + std::vector getMppts() const; + std::vector getChannelsDC() const; + std::vector getChannelsDCByMppt(const MpptNum_t mppt) const; + protected: HoymilesRadio* _radio; diff --git a/src/PowerLimiterInverter.cpp b/src/PowerLimiterInverter.cpp index 1ae0ecbe0..d91a826c2 100644 --- a/src/PowerLimiterInverter.cpp +++ b/src/PowerLimiterInverter.cpp @@ -3,7 +3,6 @@ #include "PowerLimiterInverter.h" #include "PowerLimiterBatteryInverter.h" #include "PowerLimiterSolarInverter.h" -#include "inverters/HMS_4CH.h" std::unique_ptr PowerLimiterInverter::create( bool verboseLogging, PowerLimiterInverterConfig const& config) diff --git a/src/PowerLimiterSolarInverter.cpp b/src/PowerLimiterSolarInverter.cpp index 2804ec9d3..7658412b3 100644 --- a/src/PowerLimiterSolarInverter.cpp +++ b/src/PowerLimiterSolarInverter.cpp @@ -1,6 +1,5 @@ #include "MessageOutput.h" #include "PowerLimiterSolarInverter.h" -#include "inverters/HMS_4CH.h" PowerLimiterSolarInverter::PowerLimiterSolarInverter(bool verboseLogging, PowerLimiterInverterConfig const& config) : PowerLimiterInverter(verboseLogging, config) { } @@ -73,16 +72,13 @@ uint16_t PowerLimiterSolarInverter::scaleLimit(uint16_t expectedOutputWatts) if (!isProducing()) { return expectedOutputWatts; } auto pStats = _spInverter->Statistics(); - std::list dcChnls = pStats->getChannelsByType(TYPE_DC); + std::vector dcChnls = _spInverter->getChannelsDC(); + std::vector dcMppts = _spInverter->getMppts(); size_t dcTotalChnls = dcChnls.size(); + size_t dcTotalMppts = dcMppts.size(); - // according to the upstream projects README (table with supported devs), - // every 2 channel inverter has 2 MPPTs. then there are the HM*S* 4 channel - // models which have 4 MPPTs. all others have a different number of MPPTs - // than inputs. those are not supported by the current scaling mechanism. - bool supported = dcTotalChnls == 2; - supported |= dcTotalChnls == 4 && HMS_4CH::isValidSerial(getSerial()); - if (!supported) { return expectedOutputWatts; } + // if there is only one MPPT available, there is nothing we can do + if (dcTotalMppts <= 1) { return expectedOutputWatts; } // test for a reasonable power limit that allows us to assume that an input // channel with little energy is actually not producing, rather than @@ -101,37 +97,42 @@ uint16_t PowerLimiterSolarInverter::scaleLimit(uint16_t expectedOutputWatts) inverterEfficiencyFactor = (inverterEfficiencyFactor > 0) ? inverterEfficiencyFactor/100 : 0.967; // 98% of the expected power is good enough - auto expectedAcPowerPerChannel = (getCurrentLimitWatts() / dcTotalChnls) * 0.98; + auto expectedAcPowerPerMppt = (getCurrentLimitWatts() / dcTotalMppts) * 0.98; if (_verboseLogging) { - MessageOutput.printf("%s expected AC power per channel %f W\r\n", - _logPrefix, expectedAcPowerPerChannel); + MessageOutput.printf("%s expected AC power per mppt %f W\r\n", + _logPrefix, expectedAcPowerPerMppt); } - size_t dcShadedChnls = 0; + size_t dcShadedMppts = 0; auto shadedChannelACPowerSum = 0.0; - for (auto& c : dcChnls) { - auto channelPowerAC = pStats->getChannelFieldValue(TYPE_DC, c, FLD_PDC) * inverterEfficiencyFactor; + for (auto& m : dcMppts) { + float mpptPowerAC = 0.0; + std::vector mpptChnls = _spInverter->getChannelsDCByMppt(m); - if (channelPowerAC < expectedAcPowerPerChannel) { - dcShadedChnls++; - shadedChannelACPowerSum += channelPowerAC; + for (auto& c : mpptChnls) { + mpptPowerAC += pStats->getChannelFieldValue(TYPE_DC, c, FLD_PDC) * inverterEfficiencyFactor; + } + + if (mpptPowerAC < expectedAcPowerPerMppt) { + dcShadedMppts++; + shadedChannelACPowerSum += mpptPowerAC; } if (_verboseLogging) { - MessageOutput.printf("%s ch %d AC power %f W\r\n", - _logPrefix, c, channelPowerAC); + MessageOutput.printf("%s mppt-%c AC power %f W\r\n", + _logPrefix, m + 'a', mpptPowerAC); } } // no shading or the shaded channels provide more power than what // we currently need. - if (dcShadedChnls == 0 || shadedChannelACPowerSum >= expectedOutputWatts) { + if (dcShadedMppts == 0 || shadedChannelACPowerSum >= expectedOutputWatts) { return expectedOutputWatts; } - if (dcShadedChnls == dcTotalChnls) { + if (dcShadedMppts == dcTotalMppts) { // keep the currentLimit when: // - all channels are shaded // - currentLimit >= expectedOutputWatts @@ -139,7 +140,7 @@ uint16_t PowerLimiterSolarInverter::scaleLimit(uint16_t expectedOutputWatts) if (getCurrentLimitWatts() >= expectedOutputWatts && inverterOutputAC <= expectedOutputWatts) { if (_verboseLogging) { - MessageOutput.printf("%s all channels are shaded, " + MessageOutput.printf("%s all mppts are shaded, " "keeping the current limit of %d W\r\n", _logPrefix, getCurrentLimitWatts()); } @@ -151,31 +152,38 @@ uint16_t PowerLimiterSolarInverter::scaleLimit(uint16_t expectedOutputWatts) } } - size_t dcNonShadedChnls = dcTotalChnls - dcShadedChnls; - uint16_t overScaledLimit = (expectedOutputWatts - shadedChannelACPowerSum) / dcNonShadedChnls * dcTotalChnls; + size_t dcNonShadedMppts = dcTotalMppts - dcShadedMppts; + uint16_t overScaledLimit = (expectedOutputWatts - shadedChannelACPowerSum) / dcNonShadedMppts * dcTotalMppts; if (overScaledLimit <= expectedOutputWatts) { return expectedOutputWatts; } if (_verboseLogging) { - MessageOutput.printf("%s %d/%d channels are shaded, scaling %d W\r\n", - _logPrefix, dcShadedChnls, dcTotalChnls, overScaledLimit); + MessageOutput.printf("%s %d/%d mppts are shaded, scaling %d W\r\n", + _logPrefix, dcShadedMppts, dcTotalMppts, overScaledLimit); } return overScaledLimit; } - size_t dcProdChnls = 0; - for (auto& c : dcChnls) { - if (pStats->getChannelFieldValue(TYPE_DC, c, FLD_PDC) > 2.0) { - dcProdChnls++; + size_t dcProdMppts = 0; + for (auto& m : dcMppts) { + float dcPowerMppt = 0.0; + std::vector mpptChnls = _spInverter->getChannelsDCByMppt(m); + + for (auto& c : mpptChnls) { + dcPowerMppt += pStats->getChannelFieldValue(TYPE_DC, c, FLD_PDC); + } + + if (dcPowerMppt > 2.0 * mpptChnls.size()) { + dcProdMppts++; } } - if (dcProdChnls == 0 || dcProdChnls == dcTotalChnls) { return expectedOutputWatts; } + if (dcProdMppts == 0 || dcProdMppts == dcTotalMppts) { return expectedOutputWatts; } - uint16_t scaled = expectedOutputWatts / dcProdChnls * dcTotalChnls; - MessageOutput.printf("%s %d/%d channels are producing, scaling from %d to " - "%d W\r\n", _logPrefix, dcProdChnls, dcTotalChnls, + uint16_t scaled = expectedOutputWatts / dcProdMppts * dcTotalMppts; + MessageOutput.printf("%s %d/%d mppts are producing, scaling from %d to " + "%d W\r\n", _logPrefix, dcProdMppts, dcTotalMppts, expectedOutputWatts, scaled); return scaled;