From cf4a59c74004850e62729bcab7194b39eb8adfb1 Mon Sep 17 00:00:00 2001 From: vaterlangen Date: Tue, 22 Oct 2024 20:00:44 +0200 Subject: [PATCH] Feature: DPL: support overscaling on all inverters (#1286) 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 49531d99..17e0a618 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 8220272e..fc6f1e38 100644 --- a/lib/Hoymiles/src/inverters/HERF_1CH.h +++ b/lib/Hoymiles/src/inverters/HERF_1CH.h @@ -10,4 +10,6 @@ public: 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 f0216a64..14f4644b 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 048ccb61..e606a843 100644 --- a/lib/Hoymiles/src/inverters/HERF_2CH.h +++ b/lib/Hoymiles/src/inverters/HERF_2CH.h @@ -10,4 +10,6 @@ public: 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 2c7e3857..a04b0b7d 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 a5a64c17..de53d501 100644 --- a/lib/Hoymiles/src/inverters/HMS_1CH.h +++ b/lib/Hoymiles/src/inverters/HMS_1CH.h @@ -11,4 +11,6 @@ public: 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 d79d2c1d..3716cd98 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 c831d120..2274ae53 100644 --- a/lib/Hoymiles/src/inverters/HMS_1CHv2.h +++ b/lib/Hoymiles/src/inverters/HMS_1CHv2.h @@ -11,4 +11,6 @@ public: 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 4cbc686c..c22fcf0d 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 9f1ed91f..75d3f9b0 100644 --- a/lib/Hoymiles/src/inverters/HMS_2CH.h +++ b/lib/Hoymiles/src/inverters/HMS_2CH.h @@ -11,4 +11,6 @@ public: 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 b3cf1f38..1616d013 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 9d49de07..cb757339 100644 --- a/lib/Hoymiles/src/inverters/HMS_4CH.h +++ b/lib/Hoymiles/src/inverters/HMS_4CH.h @@ -10,4 +10,6 @@ public: 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 c84eff47..48caf8ce 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 01d32893..8f26c14b 100644 --- a/lib/Hoymiles/src/inverters/HMT_4CH.h +++ b/lib/Hoymiles/src/inverters/HMT_4CH.h @@ -10,4 +10,6 @@ public: 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 2c3dd5f3..9e835671 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 6b728006..ab849174 100644 --- a/lib/Hoymiles/src/inverters/HMT_6CH.h +++ b/lib/Hoymiles/src/inverters/HMT_6CH.h @@ -10,4 +10,6 @@ public: 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 eef82c5c..37761942 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 a35b4e56..60724aab 100644 --- a/lib/Hoymiles/src/inverters/HM_1CH.h +++ b/lib/Hoymiles/src/inverters/HM_1CH.h @@ -11,4 +11,6 @@ public: 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 91228ff3..28778463 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 1fd54496..34e9dbc0 100644 --- a/lib/Hoymiles/src/inverters/HM_2CH.h +++ b/lib/Hoymiles/src/inverters/HM_2CH.h @@ -10,4 +10,6 @@ public: 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 45ebab40..4d90e4ad 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 e54f3323..cce04578 100644 --- a/lib/Hoymiles/src/inverters/HM_4CH.h +++ b/lib/Hoymiles/src/inverters/HM_4CH.h @@ -10,4 +10,6 @@ public: 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 26a89c13..033fbb05 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 29fba12f..db2ed255 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 @@ public: 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 @@ public: 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 101be843..c4fc0e9f 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 2804ec9d..7658412b 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;