Skip to content

Commit f7274b0

Browse files
Introduce FrequencyManager for CMT radio
Fixes HMS inverters losing connection sometimes. Fixes #2277, #2278, #2137. HMS inverters sometimes decide to change their own frequency. They no longer listen to our inverterTargetFrequency and not on inverterBootFrequency. FrequencyManager deals with this by slowly scanning the band once isReachable()==false
1 parent a07a027 commit f7274b0

27 files changed

+327
-24
lines changed

lang/el.lang.json

+1
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@
170170
"RxFailNothing": "Αποτυχημένα RX: Δεν λαμβάνετε τίποτα",
171171
"RxFailPartial": "Αποτυχημένα RX: Μερική λήψη",
172172
"RxFailCorrupt": "Αποτυχημένα RX: Λήψη κατεστραμμένου",
173+
"RxLastFrequency": "RX frequency of the latest package received",
173174
"TxReRequest": "TX Επαναίτηση Fragment",
174175
"StatsReset": "Επαναφορά Στατιστικών",
175176
"StatsResetting": "Επαναφορά...",

lang/es.lang.json

+1
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@
170170
"RxFailNothing": "RX Fail: Receive Nothing",
171171
"RxFailPartial": "RX Fail: Receive Partial",
172172
"RxFailCorrupt": "RX Fail: Receive Corrupt",
173+
"RxLastFrequency": "RX frequency of the latest package received",
173174
"TxReRequest": "TX Re-Request Fragment",
174175
"StatsReset": "Reset Statistics",
175176
"StatsResetting": "Resetting...",

lang/it.lang.json

+1
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@
170170
"RxFailNothing": "RX Fail: Receive Nothing",
171171
"RxFailPartial": "RX Fail: Receive Partial",
172172
"RxFailCorrupt": "RX Fail: Receive Corrupt",
173+
"RxLastFrequency": "RX frequency of the latest package received",
173174
"TxReRequest": "TX Re-Request Fragment",
174175
"StatsReset": "Reset Statistics",
175176
"StatsResetting": "Resetting...",

lib/Hoymiles/src/Hoymiles.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,9 @@ void HoymilesClass::loop()
6767
_messageOutput->print("Fetch inverter: ");
6868
_messageOutput->println(iv->serial(), HEX);
6969

70-
if (!iv->isReachable()) {
70+
iv->getFrequencyManager()->startNextFetch();
71+
72+
if (!iv->isReachable() || iv->getFrequencyManager()->shouldSendChangeChannelCommand()) {
7173
iv->sendChangeChannelRequest();
7274
}
7375

lib/Hoymiles/src/HoymilesRadio.cpp

+9-7
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "HoymilesRadio.h"
66
#include "Hoymiles.h"
77
#include "crc.h"
8+
#include "frequencymanagers/FrequencyManagerAbstract.h"
89

910
serial_u HoymilesRadio::DtuSerial() const
1011
{
@@ -34,21 +35,21 @@ bool HoymilesRadio::checkFragmentCrc(const fragment_t& fragment) const
3435
return (crc == fragment.fragment[fragment.len - 1]);
3536
}
3637

37-
void HoymilesRadio::sendRetransmitPacket(const uint8_t fragment_id)
38+
void HoymilesRadio::sendRetransmitPacket(const uint8_t fragment_id, FrequencyManagerAbstract& freq_mgr)
3839
{
3940
CommandAbstract* cmd = _commandQueue.front().get();
4041

4142
CommandAbstract* requestCmd = cmd->getRequestFrameCommand(fragment_id);
4243

4344
if (requestCmd != nullptr) {
44-
sendEsbPacket(*requestCmd);
45+
sendEsbPacket(*requestCmd, freq_mgr);
4546
}
4647
}
4748

48-
void HoymilesRadio::sendLastPacketAgain()
49+
void HoymilesRadio::sendLastPacketAgain(FrequencyManagerAbstract &freq_mgr)
4950
{
5051
CommandAbstract* cmd = _commandQueue.front().get();
51-
sendEsbPacket(*cmd);
52+
sendEsbPacket(*cmd, freq_mgr);
5253
}
5354

5455
void HoymilesRadio::handleReceivedPackage()
@@ -60,9 +61,10 @@ void HoymilesRadio::handleReceivedPackage()
6061
if (nullptr != inv) {
6162
CommandAbstract* cmd = _commandQueue.front().get();
6263
uint8_t verifyResult = inv->verifyAllFragments(*cmd);
64+
inv->getFrequencyManager()->processRXResult(cmd, verifyResult);
6365
if (verifyResult == FRAGMENT_ALL_MISSING_RESEND) {
6466
Hoymiles.getMessageOutput()->println("Nothing received, resend whole request");
65-
sendLastPacketAgain();
67+
sendLastPacketAgain(*inv->getFrequencyManager());
6668

6769
} else if (verifyResult == FRAGMENT_ALL_MISSING_TIMEOUT) {
6870
Hoymiles.getMessageOutput()->println("Nothing received, resend count exeeded");
@@ -101,7 +103,7 @@ void HoymilesRadio::handleReceivedPackage()
101103
// Statistics: Count TX Re-Request Fragment
102104
inv->RadioStats.TxReRequestFragment++;
103105

104-
sendRetransmitPacket(verifyResult);
106+
sendRetransmitPacket(verifyResult, *inv->getFrequencyManager());
105107

106108
} else {
107109
// Successful received all packages
@@ -132,7 +134,7 @@ void HoymilesRadio::handleReceivedPackage()
132134
// Statistics: TX Requests
133135
inv->RadioStats.TxRequestData++;
134136

135-
sendEsbPacket(*cmd);
137+
sendEsbPacket(*cmd, *inv->getFrequencyManager());
136138
} else {
137139
Hoymiles.getMessageOutput()->println("TX: Invalid inverter found");
138140
_commandQueue.pop();

lib/Hoymiles/src/HoymilesRadio.h

+4-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include "Arduino.h"
55
#include "commands/CommandAbstract.h"
6+
#include "frequencymanagers/FrequencyManagerAbstract.h"
67
#include "queue/CommandQueue.h"
78
#include "types.h"
89
#include <TimeoutHelper.h>
@@ -75,9 +76,9 @@ class HoymilesRadio {
7576
static void dumpBuf(const uint8_t buf[], const uint8_t len, const bool appendNewline = true);
7677

7778
bool checkFragmentCrc(const fragment_t& fragment) const;
78-
virtual void sendEsbPacket(CommandAbstract& cmd) = 0;
79-
void sendRetransmitPacket(const uint8_t fragment_id);
80-
void sendLastPacketAgain();
79+
virtual void sendEsbPacket(CommandAbstract& cmd, FrequencyManagerAbstract& freq_mgr) = 0;
80+
void sendRetransmitPacket(const uint8_t fragment_id, FrequencyManagerAbstract& freq_mgr);
81+
void sendLastPacketAgain(FrequencyManagerAbstract& freq_mgr);
8182
void handleReceivedPackage();
8283

8384
serial_u _dtuSerial;

lib/Hoymiles/src/HoymilesRadio_CMT.cpp

+42-6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "crc.h"
88
#include <FunctionalInterrupt.h>
99
#include <frozen/map.h>
10+
#include "frequencymanagers/FrequencyManagerAbstract.h"
1011

1112
constexpr CountryFrequencyDefinition_t make_value(FrequencyBand_t Band, uint32_t Freq_Legal_Min, uint32_t Freq_Legal_Max, uint32_t Freq_Default, uint32_t Freq_StartUp)
1213
{
@@ -192,6 +193,7 @@ void HoymilesRadio_CMT::setPALevel(const int8_t paLevel)
192193
if (!_isInitialized) {
193194
return;
194195
}
196+
this->_pa_level = paLevel;
195197

196198
if (_radio->setPALevel(paLevel)) {
197199
Hoymiles.getMessageOutput()->printf("CMT TX power set to %" PRId8 " dBm\r\n", paLevel);
@@ -232,6 +234,16 @@ uint32_t HoymilesRadio_CMT::getMaxFrequency() const
232234
return countryDefinition.at(_countryMode).Freq_Max;
233235
}
234236

237+
uint32_t HoymilesRadio_CMT::getLegalMinFrequency() const
238+
{
239+
return countryDefinition.at(_countryMode).Freq_Legal_Min;
240+
}
241+
242+
uint32_t HoymilesRadio_CMT::getLegalMaxFrequency() const
243+
{
244+
return countryDefinition.at(_countryMode).Freq_Legal_Max;
245+
}
246+
235247
CountryModeId_t HoymilesRadio_CMT::getCountryMode() const
236248
{
237249
return _countryMode;
@@ -262,26 +274,50 @@ void ARDUINO_ISR_ATTR HoymilesRadio_CMT::handleInt2()
262274
_packetReceived = true;
263275
}
264276

265-
void HoymilesRadio_CMT::sendEsbPacket(CommandAbstract& cmd)
277+
void HoymilesRadio_CMT::handleTxError(bool is_error) {
278+
if(!is_error) {
279+
this->_tx_error_counter = 0;
280+
return;
281+
}
282+
this->_tx_error_counter++;
283+
if(_tx_error_counter==5 || _tx_error_counter == 10 || _tx_error_counter == 15) {
284+
Hoymiles.getMessageOutput()->println("TX recovery: Re-applying PA level");
285+
this->setPALevel(this->_pa_level);
286+
return;
287+
}
288+
if(_tx_error_counter==20 || _tx_error_counter == 25) {
289+
Hoymiles.getMessageOutput()->println("TX recovery: Re-initializing radio");
290+
this->_radio->begin();
291+
}
292+
if(_tx_error_counter >= 30) {
293+
Hoymiles.getMessageOutput()->println("TX recovery: Giving up");
294+
this->_isInitialized = false;
295+
}
296+
297+
}
298+
299+
void HoymilesRadio_CMT::sendEsbPacket(CommandAbstract& cmd, FrequencyManagerAbstract &freq_mgr)
266300
{
267301
cmd.incrementSendCount();
268302

269303
cmd.setRouterAddress(DtuSerial().u64);
270304

271305
_radio->stopListening();
272306

273-
if (cmd.getDataPayload()[0] == 0x56) { // @todo(tbnobody) Bad hack to identify ChannelChange Command
274-
cmtSwitchDtuFreq(getInvBootFrequency());
275-
}
307+
cmtSwitchDtuFreq(freq_mgr.getTXFrequency(cmd));
276308

277309
Hoymiles.getMessageOutput()->printf("TX %s %.2f MHz --> ",
278310
cmd.getCommandName().c_str(), getFrequencyFromChannel(_radio->getChannel()) / 1000000.0);
279311
cmd.dumpDataPayload(Hoymiles.getMessageOutput());
280312

281-
if (!_radio->write(cmd.getDataPayload(), cmd.getDataSize())) {
313+
bool tx_worked = _radio->write(cmd.getDataPayload(), cmd.getDataSize());
314+
if (!tx_worked) {
282315
Hoymiles.getMessageOutput()->println("TX SPI Timeout");
283316
}
284-
cmtSwitchDtuFreq(_inverterTargetFrequency);
317+
this->handleTxError(!tx_worked);
318+
319+
320+
cmtSwitchDtuFreq(freq_mgr.getRXFrequency(cmd));
285321
_radio->startListening();
286322
_busyFlag = true;
287323
_rxTimeout.set(cmd.getTimeout());

lib/Hoymiles/src/HoymilesRadio_CMT.h

+7-1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ class HoymilesRadio_CMT : public HoymilesRadio {
5151

5252
uint32_t getMinFrequency() const;
5353
uint32_t getMaxFrequency() const;
54+
uint32_t getLegalMinFrequency() const;
55+
uint32_t getLegalMaxFrequency() const;
5456
static constexpr uint32_t getChannelWidth()
5557
{
5658
return FH_OFFSET * CMT2300A_ONE_STEP_SIZE;
@@ -70,7 +72,7 @@ class HoymilesRadio_CMT : public HoymilesRadio {
7072
void ARDUINO_ISR_ATTR handleInt1();
7173
void ARDUINO_ISR_ATTR handleInt2();
7274

73-
void sendEsbPacket(CommandAbstract& cmd);
75+
void sendEsbPacket(CommandAbstract& cmd, FrequencyManagerAbstract &freq_mgr);
7476

7577
std::unique_ptr<CMT2300A> _radio;
7678

@@ -79,6 +81,7 @@ class HoymilesRadio_CMT : public HoymilesRadio {
7981

8082
bool _gpio2_configured = false;
8183
bool _gpio3_configured = false;
84+
int8_t _pa_level = 0;
8285

8386
std::queue<fragment_t> _rxBuffer;
8487
TimeoutHelper _txTimeout;
@@ -88,4 +91,7 @@ class HoymilesRadio_CMT : public HoymilesRadio {
8891
bool cmtSwitchDtuFreq(const uint32_t to_frequency);
8992

9093
CountryModeId_t _countryMode;
94+
95+
int _tx_error_counter = 0;
96+
void handleTxError(bool is_error);
9197
};

lib/Hoymiles/src/HoymilesRadio_NRF.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,10 @@ void HoymilesRadio_NRF::switchRxCh()
169169
_radio->startListening();
170170
}
171171

172-
void HoymilesRadio_NRF::sendEsbPacket(CommandAbstract& cmd)
172+
void HoymilesRadio_NRF::sendEsbPacket(CommandAbstract& cmd, FrequencyManagerAbstract& freq_mgr)
173173
{
174+
(void) freq_mgr;
175+
174176
cmd.incrementSendCount();
175177

176178
cmd.setRouterAddress(DtuSerial().u64);

lib/Hoymiles/src/HoymilesRadio_NRF.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class HoymilesRadio_NRF : public HoymilesRadio {
3030
void openReadingPipe();
3131
void openWritingPipe(const serial_u serial);
3232

33-
void sendEsbPacket(CommandAbstract& cmd);
33+
void sendEsbPacket(CommandAbstract& cmd, FrequencyManagerAbstract &freq_mgr);
3434

3535
std::unique_ptr<SPIClass> _spiPtr;
3636
std::unique_ptr<RF24> _radio;

lib/Hoymiles/src/commands/ChannelChangeCommand.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@ bool ChannelChangeCommand::handleResponse(const fragment_t fragment[], const uin
7272
return true;
7373
}
7474

75-
uint8_t ChannelChangeCommand::getMaxResendCount()
75+
uint8_t ChannelChangeCommand::getMaxResendCount() const
7676
{
77-
// This command will never retrieve an answer. Therefor it's not required to repeat it
78-
return 0;
77+
// This command will never retrieve an answer. Repeat anyway to allow FrequencyManager to send it on a few different frequencies.
78+
return MAX_RESEND_COUNT;
7979
}

lib/Hoymiles/src/commands/ChannelChangeCommand.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,5 @@ class ChannelChangeCommand : public CommandAbstract {
1717

1818
virtual bool handleResponse(const fragment_t fragment[], const uint8_t max_fragment_id);
1919

20-
virtual uint8_t getMaxResendCount();
20+
virtual uint8_t getMaxResendCount() const;
2121
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
3+
#include "FrequencyManagerAbstract.h"
4+
#include "inverters/InverterAbstract.h"
5+
#include "commands/CommandAbstract.h"
6+
7+
FrequencyManagerAbstract::FrequencyManagerAbstract(InverterAbstract* inv) {
8+
this->_inv = inv;
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
#pragma once
3+
4+
#include "Arduino.h"
5+
#include "types.h"
6+
7+
class InverterAbstract;
8+
class CommandAbstract;
9+
10+
class FrequencyManagerAbstract {
11+
public:
12+
explicit FrequencyManagerAbstract(InverterAbstract* inv);
13+
virtual ~FrequencyManagerAbstract() {};
14+
15+
virtual uint32_t getTXFrequency(CommandAbstract& cmd) = 0;
16+
virtual uint32_t getRXFrequency(CommandAbstract& cmd) = 0;
17+
18+
virtual void processRXResult(CommandAbstract *cmd, uint8_t verify_fragments_result) = 0;
19+
virtual bool shouldSendChangeChannelCommand() = 0;
20+
virtual void startNextFetch() = 0;
21+
protected:
22+
23+
InverterAbstract* _inv;
24+
};

0 commit comments

Comments
 (0)