diff --git a/src/hm/CommQueue.h b/src/hm/CommQueue.h index 0ca9279f..acbe9a35 100644 --- a/src/hm/CommQueue.h +++ b/src/hm/CommQueue.h @@ -12,8 +12,8 @@ #include "../utils/dbg.h" #define DEFAULT_ATTEMPS 5 -#define MORE_ATTEMPS_ALARMDATA 8 -#define MORE_ATTEMPS_GRIDONPROFILEPARA 5 +#define MORE_ATTEMPS_ALARMDATA 3 // 8 +#define MORE_ATTEMPS_GRIDONPROFILEPARA 0 // 5 template class CommQueue { diff --git a/src/hm/Communication.h b/src/hm/Communication.h index b74f689d..5725f2ec 100644 --- a/src/hm/Communication.h +++ b/src/hm/Communication.h @@ -83,10 +83,11 @@ class Communication : public CommQueue<> { q->iv->mGotFragment = false; q->iv->mGotLastMsg = false; q->iv->curFrmCnt = 0; + q->iv->radioStatistics.txCnt++; mIsRetransmit = false; if(NULL == q->iv->radio) cmdDone(false); // can't communicate while radio is not defined! - mFirstTry = q->iv->isAvailable(); + mFirstTry = INV_RADIO_TYPE_NRF == q->iv->ivRadioType && q->iv->isAvailable(); q->iv->mCmd = q->cmd; q->iv->mIsSingleframeReq = false; mFramesExpected = getFramesExpected(q); // function to get expected frame count. @@ -112,13 +113,13 @@ class Communication : public CommQueue<> { } else q->iv->radio->prepareDevInformCmd(q->iv, q->cmd, q->ts, q->iv->alarmLastId, false); - q->iv->radioStatistics.txCnt++; + //q->iv->radioStatistics.txCnt++; q->iv->radio->mRadioWaitTime.startTimeMonitor(mTimeout); + if(!mIsRetransmit && (q->cmd == AlarmData) || (q->cmd == GridOnProFilePara)) + incrAttempt(q->cmd == AlarmData? MORE_ATTEMPS_ALARMDATA : MORE_ATTEMPS_GRIDONPROFILEPARA); mIsRetransmit = false; setAttempt(); - if((q->cmd == AlarmData) || (q->cmd == GridOnProFilePara)) - incrAttempt(q->cmd == AlarmData? MORE_ATTEMPS_ALARMDATA : MORE_ATTEMPS_GRIDONPROFILEPARA); mState = States::WAIT; break; @@ -129,13 +130,14 @@ class Communication : public CommQueue<> { break; case States::CHECK_FRAMES: { - if((q->iv->radio->mBufCtrl.empty() && !mIsRetransmit) || (0 == q->attempts)) { // radio buffer empty or no more answers + if((q->iv->radio->mBufCtrl.empty() && !mIsRetransmit) ) { // || (0 == q->attempts)) { // radio buffer empty. No more answers will be checked later if(*mSerialDebug) { DPRINT_IVID(DBG_INFO, q->iv->id); DBGPRINT(F("request timeout: ")); DBGPRINT(String(q->iv->radio->mRadioWaitTime.getRunTime())); DBGPRINTLN(F("ms")); } + if(!q->iv->mGotFragment) { if(INV_RADIO_TYPE_CMT == q->iv->ivRadioType) { #if defined(ESP32) @@ -146,18 +148,20 @@ class Communication : public CommQueue<> { mWaitTime.startTimeMonitor(1000); #endif } else { + mHeu.setIvRetriesBad(q->iv); if(IV_MI == q->iv->ivGen) q->iv->mIvTxCnt++; if(mFirstTry) { - mFirstTry = false; - setAttempt(); + if(q->attempts < 3 || !q->iv->isProducing()) + mFirstTry = false; mHeu.evalTxChQuality(q->iv, false, 0, 0); - q->iv->radioStatistics.rxFailNoAnser++; + mHeu.getTxCh(q->iv); + //q->iv->radioStatistics.rxFailNoAnser++; // should only be one of fail or retransmit. + //q->iv->radioStatistics.txCnt--; q->iv->radioStatistics.retransmits++; q->iv->radio->mRadioWaitTime.stopTimeMonitor(); mState = States::START; - return; } } @@ -178,8 +182,11 @@ class Communication : public CommQueue<> { q->iv->mDtuRxCnt++; if (p->packet[0] == (TX_REQ_INFO + ALL_FRAMES)) { // response from get information command - if(parseFrame(p)) + if(parseFrame(p)) { q->iv->curFrmCnt++; + if(!mIsRetransmit && (p->packet[9] == 0x02 || p->packet[9] == 0x82) && p->millis < LIMIT_FAST_IV) + mHeu.setIvRetriesGood(q->iv,p->millis < LIMIT_VERYFAST_IV); + } } else if (p->packet[0] == (TX_REQ_DEVCONTROL + ALL_FRAMES)) { // response from dev control command if(parseDevCtrl(p, q)) closeRequest(q, true); @@ -200,7 +207,6 @@ class Communication : public CommQueue<> { if(q->iv->ivGen != IV_MI) { mState = States::CHECK_PACKAGE; } else { - bool fastNext = true; if(q->iv->miMultiParts < 6) { mState = States::WAIT; if((q->iv->radio->mRadioWaitTime.isTimeout() && mIsRetransmit) || !mIsRetransmit) { @@ -213,12 +219,8 @@ class Communication : public CommQueue<> { || ((q->cmd == MI_REQ_CH2) && (q->iv->type == INV_TYPE_2CH)) || ((q->cmd == MI_REQ_CH1) && (q->iv->type == INV_TYPE_1CH))) { miComplete(q->iv); - fastNext = false; } - if(fastNext) - miNextRequest(q->iv->type == INV_TYPE_4CH ? MI_REQ_4CH : MI_REQ_CH1, q); - else - closeRequest(q, true); + closeRequest(q, true); } } @@ -250,10 +252,33 @@ class Communication : public CommQueue<> { if(framnr) { if(0 == q->attempts) { DPRINT_IVID(DBG_INFO, q->iv->id); - DBGPRINT(F("no attempts left")); + DBGPRINTLN(F("timeout, no attempts left")); closeRequest(q, false); return; } + //count missing frames + if(!q->iv->mIsSingleframeReq && q->iv->ivRadioType == INV_RADIO_TYPE_NRF) { // already checked? + uint8_t missedFrames = 0; + for(uint8_t i = 0; i < q->iv->radio->mFramesExpected; i++) { + if(mLocalBuf[i].len == 0) + missedFrames++; + } + if(missedFrames > 3 || (q->cmd == RealTimeRunData_Debug && missedFrames > 1) || (missedFrames > 1 && missedFrames + 2 > q->attempts)) { + if(*mSerialDebug) { + DPRINT_IVID(DBG_INFO, q->iv->id); + DBGPRINT(String(missedFrames)); + DBGPRINT(F(" frames missing ")); + DBGPRINTLN(F("-> complete retransmit")); + } + mHeu.evalTxChQuality(q->iv, false, (q->attemptsMax - 1 - q->attempts), q->iv->curFrmCnt); + q->iv->radioStatistics.txCnt--; + q->iv->radioStatistics.retransmits++; + mCompleteRetry = true; + mState = States::RESET; + return; + } + } + setAttempt(); if(*mSerialDebug) { @@ -410,6 +435,8 @@ class Communication : public CommQueue<> { } inline bool parseMiFrame(packet_t *p, const queue_s *q) { + if(!mIsRetransmit && p->packet[9] == 0x00 && p->millis < LIMIT_FAST_IV_MI) //first frame is fast? + mHeu.setIvRetriesGood(q->iv,p->millis < LIMIT_VERYFAST_IV_MI); if ((p->packet[0] == MI_REQ_CH1 + ALL_FRAMES) || (p->packet[0] == MI_REQ_CH2 + ALL_FRAMES) || ((p->packet[0] >= (MI_REQ_4CH + ALL_FRAMES)) @@ -429,7 +456,6 @@ class Communication : public CommQueue<> { record_t<> *rec = q->iv->getRecordStruct(RealTimeRunData_Debug); // choose the record structure rec->ts = q->ts; miStsConsolidate(q, ((p->packet[0] == 0x88) ? 1 : 2), rec, p->packet[10], p->packet[12], p->packet[9], p->packet[11]); - //mHeu.setGotFragment(q->iv); only do this when we are through the cycle? } return true; @@ -493,6 +519,7 @@ class Communication : public CommQueue<> { } else DBGPRINTLN(F("-> complete retransmit")); + mCompleteRetry = true; mState = States::RESET; return; } @@ -592,7 +619,7 @@ class Communication : public CommQueue<> { mHeu.evalTxChQuality(q->iv, crcPass, (q->attemptsMax - 1 - q->attempts), q->iv->curFrmCnt); if(crcPass) q->iv->radioStatistics.rxSuccess++; - else if(q->iv->mGotFragment) + else if(q->iv->mGotFragment || mCompleteRetry) q->iv->radioStatistics.rxFail++; // got no complete payload else q->iv->radioStatistics.rxFailNoAnser++; // got nothing @@ -607,6 +634,7 @@ class Communication : public CommQueue<> { q->iv->mGotLastMsg = false; q->iv->miMultiParts = 0; mIsRetransmit = false; + mCompleteRetry = false; mState = States::RESET; DBGPRINTLN(F("-----")); } @@ -820,8 +848,9 @@ class Communication : public CommQueue<> { DBGHEXLN(cmd); } - if(q->iv->miMultiParts == 7) - q->iv->radioStatistics.rxSuccess++; + /*if(q->iv->miMultiParts > 5) //if(q->iv->miMultiParts == 7) + q->iv->radioStatistics.rxSuccess++;*/ + q->iv->radioStatistics.ivSent++; mFramesExpected = getFramesExpected(q); q->iv->radio->setExpectedFrames(mFramesExpected); @@ -846,6 +875,7 @@ class Communication : public CommQueue<> { } q->iv->radio->sendCmdPacket(q->iv, q->cmd, 0x00, true); + q->iv->radioStatistics.retransmits++; q->iv->radio->mRadioWaitTime.startTimeMonitor(DURATION_TXFRAME + DURATION_ONEFRAME + duration_reserve[q->iv->ivRadioType]); mIsRetransmit = false; @@ -999,8 +1029,9 @@ class Communication : public CommQueue<> { uint16_t *mInverterGap; TimeMonitor mWaitTime = TimeMonitor(0, true); // start as expired (due to code in RESET state) std::array mLocalBuf; - bool mFirstTry = false; // see, if we should do a second try - bool mIsRetransmit = false; // we already had waited one complete cycle + bool mFirstTry = false; // see, if we should do a second try + bool mCompleteRetry = false; // remember if we did request a complete retransmission + bool mIsRetransmit = false; // we already had waited one complete cycle uint8_t mMaxFrameId; uint8_t mFramesExpected = 12; // 0x8c was highest last frame for alarm data uint16_t mTimeout = 0; // calculating that once should be ok diff --git a/src/hm/Heuristic.h b/src/hm/Heuristic.h index 64c78cc8..3fb86614 100644 --- a/src/hm/Heuristic.h +++ b/src/hm/Heuristic.h @@ -23,8 +23,8 @@ class Heuristic { public: uint8_t getTxCh(Inverter<> *iv) { - if((IV_HMS == iv->ivGen) || (IV_HMT == iv->ivGen)) - return 0; // not used for these inverter types + if(iv->ivRadioType != INV_RADIO_TYPE_NRF) + return 0; // not used for other than nRF inverter types HeuristicInv *ih = &iv->heuristics; @@ -66,6 +66,8 @@ class Heuristic { ih->testPeriodFailCnt = 0; } + iv->radio->mTxRetriesNext = getIvRetries(iv); + return id2Ch(ih->txRfChId); } @@ -155,6 +157,48 @@ class Heuristic { DBGPRINTLN(String(iv->config->powerLevel)); } + uint8_t getIvRetries(Inverter<> *iv) { + if(iv->heuristics.rxSpeeds[0]) + return RETRIES_VERYFAST_IV; + if(iv->heuristics.rxSpeeds[1]) + return RETRIES_FAST_IV; + return 15; + } + + void setIvRetriesGood(Inverter<> *iv, bool veryGood) { + if(iv->ivRadioType != INV_RADIO_TYPE_NRF) + return; // not used for other than nRF inverter types + + if(iv->heuristics.rxSpeedCnt[veryGood] > 9) + return; + iv->heuristics.rxSpeedCnt[veryGood]++; + iv->heuristics.rxSpeeds[veryGood] = true; + } + + void setIvRetriesBad(Inverter<> *iv) { + if(iv->ivRadioType != INV_RADIO_TYPE_NRF) + return; // not used for other than nRF inverter types + + if(iv->heuristics.rxSpeedCnt[0]) { + iv->heuristics.rxSpeedCnt[0]--; + return; + } + if(iv->heuristics.rxSpeeds[0]) { + iv->heuristics.rxSpeeds[0] = false; + return; + } + + if(iv->heuristics.rxSpeedCnt[1]) { + iv->heuristics.rxSpeedCnt[1]--; + return; + } + if(iv->heuristics.rxSpeeds[1]) { + iv->heuristics.rxSpeeds[1] = false; + return; + } + return; + } + private: bool isNewTxCh(HeuristicInv *ih) { return ih->txRfChId != ih->lastBestTxChId; diff --git a/src/hm/HeuristicInv.h b/src/hm/HeuristicInv.h index 0391e758..89099ca8 100644 --- a/src/hm/HeuristicInv.h +++ b/src/hm/HeuristicInv.h @@ -42,6 +42,9 @@ class HeuristicInv { uint8_t testChId = 0; int8_t saveOldTestQuality = -6; uint8_t lastRxFragments = 0; + bool rxSpeeds[2] = {false,false}; // is inverter responding very fast respective fast? + uint8_t rxSpeedCnt[2] = {0,0}; // count how many messages had been received very fast respective fast (10 max) + }; #endif /*__HEURISTIC_INV_H__*/ diff --git a/src/hm/hmDefines.h b/src/hm/hmDefines.h index 8df06dc1..f06c06fe 100644 --- a/src/hm/hmDefines.h +++ b/src/hm/hmDefines.h @@ -84,7 +84,15 @@ enum {INV_RADIO_TYPE_NRF = 0, INV_RADIO_TYPE_CMT}; #define DURATION_TXFRAME 85 // timeout parameter for first transmission and first expected frame (time to first channel switch from tx start!) (ms) #define DURATION_LISTEN_MIN 5 // time to stay at least on a listening channel (ms) #define DURATION_PAUSE_LASTFR 45 // how long to pause after last frame (ms) -const uint8_t duration_reserve[2] = {115,115}; +const uint8_t duration_reserve[2] = {65,115}; + +#define LIMIT_FAST_IV 85 // time limit to qualify an inverter as very fast answering inverter +#define LIMIT_VERYFAST_IV 70 // time limit to qualify an inverter as very fast answering inverter +#define LIMIT_FAST_IV_MI 35 // time limit to qualify a MI type inverter as fast answering inverter +#define LIMIT_VERYFAST_IV_MI 22 // time limit to qualify a MI type inverter as very fast answering inverter +#define RETRIES_FAST_IV 12 // how often shall a message be automatically retransmitted by the nRF (fast answering inverter) +#define RETRIES_VERYFAST_IV 9 // how often shall a message be automatically retransmitted by the nRF (very fast answering inverter) + typedef struct { uint8_t fieldId; // field id diff --git a/src/hm/hmInverter.h b/src/hm/hmInverter.h index f63fa688..e6e22fb7 100644 --- a/src/hm/hmInverter.h +++ b/src/hm/hmInverter.h @@ -133,6 +133,7 @@ class Inverter { bool isConnected; // shows if inverter was successfully identified (fw version and hardware info) InverterStatus status; // indicates the current inverter status std::array lastAlarm; // holds last 10 alarms + uint8_t rxOffset; // holds the default channel offset between tx and rx channel (nRF only) int8_t rssi; // RSSI uint16_t alarmCnt; // counts the total number of occurred alarms uint16_t alarmLastId; // lastId which was received @@ -193,27 +194,33 @@ class Inverter { cb(RealTimeRunData_Debug, false); // get live data else { if(INV_RADIO_TYPE_NRF == ivRadioType) { - // get live data until quility reaches maximum + // get live data until quality reaches maximum if(!heuristics.isTxAtMax()) { cb(RealTimeRunData_Debug, false); // get live data return; } } - if(actPowerLimit == 0xffff) + if(actPowerLimit == 0xffff) { cb(SystemConfigPara, false); // power limit info - else if(InitDataState != devControlCmd) { + cb(RealTimeRunData_Debug, false); + } else if(InitDataState != devControlCmd) { cb(devControlCmd, false); // custom command which was received by API devControlCmd = InitDataState; mGetLossInterval = 1; - } else if(0 == getFwVersion()) + } else if(0 == getFwVersion()) { cb(InverterDevInform_All, false); // get firmware version - else if(0 == getHwVersion()) + cb(RealTimeRunData_Debug, false); // get live data + } + else if(0 == getHwVersion()) { cb(InverterDevInform_Simple, false); // get hardware version - else if((alarmLastId != alarmMesIndex) && (alarmMesIndex != 0)) + cb(RealTimeRunData_Debug, false); // get live data + } else if((alarmLastId != alarmMesIndex) && (alarmMesIndex != 0)) { cb(AlarmData, false); // get last alarms - else if((0 == mGridLen) && generalConfig->readGrid) { // read grid profile + cb(RealTimeRunData_Debug, false); // get live data + } else if((0 == mGridLen) && generalConfig->readGrid) { // read grid profile cb(GridOnProFilePara, false); + cb(RealTimeRunData_Debug, false); // get live data } else if (mGetLossInterval > AHOY_GET_LOSS_INTERVAL) { // get loss rate mGetLossInterval = 1; cb(RealTimeRunData_Debug, false); // get live data @@ -222,21 +229,21 @@ class Inverter { cb(RealTimeRunData_Debug, false); // get live data } } else { // MI - if(0 == getFwVersion()) { - mIvRxCnt +=2; - cb(0x0f, false); // get firmware version; for MI, this makes part of polling the device software and hardware version number - } else { - record_t<> *rec = getRecordStruct(InverterDevInform_Simple); - if (getChannelFieldValue(CH0, FLD_PART_NUM, rec) == 0) { - cb(0x0f, false); // hard- and firmware version for missing HW part nr, delivered by frame 1 + cb(((type == INV_TYPE_4CH) ? MI_REQ_4CH : MI_REQ_CH1), false); + mGetLossInterval++; + if (type != INV_TYPE_4CH) + mIvRxCnt++; // statistics workaround... + if(isAvailable()) { + if(0 == getFwVersion()) { mIvRxCnt +=2; - } else if((getChannelFieldValue(CH0, FLD_GRID_PROFILE_CODE, rec) == 0) && generalConfig->readGrid) // read grid profile - cb(0x10, false); // legacy GPF command - else { - cb(((type == INV_TYPE_4CH) ? MI_REQ_4CH : MI_REQ_CH1), false); - mGetLossInterval++; - if (type != INV_TYPE_4CH) - mIvRxCnt++; // statistics workaround... + cb(0x0f, false); // get firmware version; for MI, this makes part of polling the device software and hardware version number + } else { + record_t<> *rec = getRecordStruct(InverterDevInform_Simple); + if (getChannelFieldValue(CH0, FLD_PART_NUM, rec) == 0) { + cb(0x0f, false); // hard- and firmware version for missing HW part nr, delivered by frame 1 + mIvRxCnt +=2; + } else if((getChannelFieldValue(CH0, FLD_GRID_PROFILE_CODE, rec) == 0) && generalConfig->readGrid) // read grid profile + cb(0x10, false); // legacy GPF command } } } @@ -457,7 +464,12 @@ class Inverter { status = InverterStatus::OFF; actPowerLimit = 0xffff; // power limit will be read once inverter becomes available alarmMesIndex = 0; - heuristics.clear(); + if(ivRadioType == INV_RADIO_TYPE_NRF) { + heuristics.clear(); + #ifdef DYNAMIC_OFFSET + rxOffset = ivGen == IV_HM ? 13 : 12; // effective 3 (or 2), but can easily be recognized as default setting + #endif + } } else status = InverterStatus::WAS_ON; diff --git a/src/hm/hmRadio.h b/src/hm/hmRadio.h index 5730c492..d1dd42d5 100644 --- a/src/hm/hmRadio.h +++ b/src/hm/hmRadio.h @@ -112,16 +112,16 @@ class HmRadio : public Radio { if (mRadioWaitTime.isTimeout()) { // timeout reached! mNRFisInRX = false; + rx_ready = false; return false; } // otherwise switch to next RX channel mTimeslotStart = millis(); - if(!mNRFloopChannels && ((mTimeslotStart - mLastIrqTime) > (DURATION_TXFRAME+DURATION_ONEFRAME))) + if(!mNRFloopChannels && ((mTimeslotStart - mLastIrqTime) > (DURATION_TXFRAME))) //(DURATION_TXFRAME+DURATION_ONEFRAME))) mNRFloopChannels = true; mRxPendular = !mRxPendular; - //innerLoopTimeout = (mRxPendular ? 1 : 2)*DURATION_LISTEN_MIN; innerLoopTimeout = DURATION_LISTEN_MIN; if(mNRFloopChannels) @@ -152,20 +152,28 @@ class HmRadio : public Radio { if(tx_ok) mLastIv->mAckCount++; + //#ifdef DYNAMIC_OFFSET + mRxChIdx = (mTxChIdx + mLastIv->rxOffset) % RF_CHANNELS; + /*#else mRxChIdx = (mTxChIdx + 2) % RF_CHANNELS; + #endif*/ mNrf24->setChannel(mRfChLst[mRxChIdx]); mNrf24->startListening(); mTimeslotStart = millis(); - tempRxChIdx = mRxChIdx; + tempRxChIdx = mRxChIdx; // might be better to start off with one channel less? mRxPendular = false; mNRFloopChannels = (mLastIv->ivGen == IV_MI); - innerLoopTimeout = DURATION_TXFRAME; + //innerLoopTimeout = mLastIv->ivGen != IV_MI ? DURATION_TXFRAME : DURATION_ONEFRAME; + //innerLoopTimeout = mLastIv->ivGen != IV_MI ? DURATION_LISTEN_MIN : 4; + //innerLoopTimeout = (mLastIv->mIsSingleframeReq || mLastIv->ivGen == IV_MI) ? DURATION_LISTEN_MIN : DURATION_TXFRAME; + innerLoopTimeout = DURATION_LISTEN_MIN; } if(rx_ready) { - if (getReceived()) { // check what we got, returns true for last package + if (getReceived()) { // check what we got, returns true for last package or success for single frame request mNRFisInRX = false; + rx_ready = false; mRadioWaitTime.startTimeMonitor(DURATION_PAUSE_LASTFR); // let the inverter first end his transmissions mNrf24->stopListening(); return false; @@ -173,7 +181,6 @@ class HmRadio : public Radio { innerLoopTimeout = DURATION_LISTEN_MIN; mTimeslotStart = millis(); if (!mNRFloopChannels) { - //mRxPendular = true; // stay longer on the next rx channel if (isRxInit) { isRxInit = false; tempRxChIdx = (mRxChIdx + 4) % RF_CHANNELS; @@ -183,6 +190,8 @@ class HmRadio : public Radio { } return true; } + rx_ready = false; // reset + return mNRFisInRX; } } @@ -292,6 +301,7 @@ class HmRadio : public Radio { private: inline bool getReceived(void) { bool isLastPackage = false; + bool isRetransmitAnswer = false; rx_ready = false; // reset for ACK case while(mNrf24->available()) { @@ -314,14 +324,31 @@ class HmRadio : public Radio { mLastIv->mGotFragment = true; mBufCtrl.push(p); - if (p.packet[0] == (TX_REQ_INFO + ALL_FRAMES)) // response from get information command + if (p.packet[0] == (TX_REQ_INFO + ALL_FRAMES)) { // response from get information command isLastPackage = (p.packet[9] > ALL_FRAMES); // > ALL_FRAMES indicates last packet received + if(mLastIv->mIsSingleframeReq) // we only expect one frame here... + isRetransmitAnswer = true; + if(isLastPackage) + setExpectedFrames(p.packet[9] - ALL_FRAMES); + #ifdef DYNAMIC_OFFSET + if(p.packet[9] == 1 && p.millis < DURATION_ONEFRAME) + mLastIv->rxOffset = (RF_CHANNELS + mTxChIdx - tempRxChIdx + 1) % RF_CHANNELS; + else if(mNRFloopChannels && mLastIv->rxOffset > RF_CHANNELS) { // unsure setting? + mLastIv->rxOffset = (RF_CHANNELS + mTxChIdx - tempRxChIdx + (isLastPackage ? mFramesExpected : p.packet[9])); // make clear it's not sure, start with one more offset + mNRFloopChannels = false; + } + #endif + } if(IV_MI == mLastIv->ivGen) { if (p.packet[0] == (0x0f + ALL_FRAMES)) // response from MI get information command isLastPackage = (p.packet[9] > 0x10); // > 0x10 indicates last packet received else if ((p.packet[0] != 0x88) && (p.packet[0] != 0x92)) // ignore MI status messages //#0 was p.packet[0] != 0x00 && isLastPackage = true; // response from dev control command + #ifdef DYNAMIC_OFFSET + if(p.packet[9] == 0x00 && p.millis < DURATION_ONEFRAME) + mLastIv->rxOffset = (RF_CHANNELS + mTxChIdx - tempRxChIdx - 1) % RF_CHANNELS; + #endif } rx_ready = true; //reset in case we first read messages from other inverter or ACK zero payloads } @@ -331,7 +358,7 @@ class HmRadio : public Radio { } if(isLastPackage) mLastIv->mGotLastMsg = true; - return isLastPackage; + return isLastPackage || isRetransmitAnswer; } void sendPacket(Inverter<> *iv, uint8_t len, bool isRetransmit, bool appendCrc16=true) { @@ -352,8 +379,19 @@ class HmRadio : public Radio { DBGPRINT(F("TX ")); DBGPRINT(String(len)); DBGPRINT(" CH"); + if(mTxChIdx == 0) + DBGPRINT("0"); DBGPRINT(String(mRfChLst[mTxChIdx])); + DBGPRINT(F(", ")); + DBGPRINT(String(mTxRetriesNext)); + //DBGPRINT(F(" retries | ")); + //#ifdef DYNAMIC_OFFSET + DBGPRINT(F(" ret., rx offset: ")); + DBGPRINT(String(iv->rxOffset)); DBGPRINT(F(" | ")); + /*#else + DBGPRINT(F(" ret. | ")); + #endif*/ if(*mPrintWholeTrace) { if(*mPrivacyMode) ah::dumpBuf(mTxBuf, len, 1, 4); @@ -370,6 +408,10 @@ class HmRadio : public Radio { mNrf24->stopListening(); mNrf24->flush_rx(); + if(!isRetransmit && mTxRetries != mTxRetriesNext) { + mNrf24->setRetries(3, mTxRetriesNext); + mTxRetries = mTxRetriesNext; + } mNrf24->setChannel(mRfChLst[mTxChIdx]); mNrf24->openWritingPipe(reinterpret_cast(&iv->radioId.u64)); mNrf24->startWrite(mTxBuf, len, false); // false = request ACK response @@ -412,6 +454,7 @@ class HmRadio : public Radio { bool mRxPendular = false; uint32_t innerLoopTimeout = DURATION_LISTEN_MIN; //uint8_t mTxSetupTime = 0; + uint8_t mTxRetries = 15; // memorize last setting for mNrf24->setRetries(3, 15); std::unique_ptr mSpi; std::unique_ptr mNrf24; diff --git a/src/hm/hmSystem.h b/src/hm/hmSystem.h index 8229378e..aa5d7492 100644 --- a/src/hm/hmSystem.h +++ b/src/hm/hmSystem.h @@ -95,6 +95,13 @@ class HmSystem { if((iv->config->serial.b[5] == 0x10) && ((iv->config->serial.b[4] & 0x03) == 0x01)) DPRINTLN(DBG_WARN, F("MI Inverter, has some restrictions!")); + #ifdef DYNAMIC_OFFSET + iv->rxOffset = iv->ivGen == IV_HM ? 13 : 12; // effective 3 (or 2), but can easily be recognized as default setting + #else + iv->rxOffset = (iv->ivGen == IV_HM && iv->type == INV_TYPE_4CH) ? 3 : 2; + iv->rxOffset = iv->ivGen == IV_HM ? 3 : 2; + #endif + cb(iv); } diff --git a/src/hm/radio.h b/src/hm/radio.h index 907a2023..7bbb42bc 100644 --- a/src/hm/radio.h +++ b/src/hm/radio.h @@ -79,6 +79,8 @@ class Radio { std::queue mBufCtrl; uint8_t mIrqOk = IRQ_UNKNOWN; TimeMonitor mRadioWaitTime = TimeMonitor(0, true); // start as expired (due to code in RESET state) + uint8_t mTxRetriesNext = 15; // let heuristics tell us the next reties count (for nRF type radios only) + uint8_t mFramesExpected = 0x0c; protected: virtual void sendPacket(Inverter<> *iv, uint8_t len, bool isRetransmit, bool appendCrc16=true) = 0; @@ -128,11 +130,11 @@ class Radio { } + uint32_t mDtuSn; volatile bool mIrqRcvd; bool *mSerialDebug, *mPrivacyMode, *mPrintWholeTrace; uint8_t mTxBuf[MAX_RF_PAYLOAD_SIZE]; - uint8_t mFramesExpected = 0x0c; }; #endif /*__RADIO_H__*/ diff --git a/src/hms/hmsRadio.h b/src/hms/hmsRadio.h index beea5829..ae9fd369 100644 --- a/src/hms/hmsRadio.h +++ b/src/hms/hmsRadio.h @@ -176,10 +176,10 @@ class CmtRadio : public Radio { if(CmtStatus::SUCCESS == status) mBufCtrl.push(p); - // this code completly stops communication! - //if(p.packet[9] > ALL_FRAMES) // indicates last frame + if(p.packet[9] > ALL_FRAMES) // indicates last frame + mRadioWaitTime.startTimeMonitor(DURATION_PAUSE_LASTFR); // let the inverter first get back to rx mode? + // optionally instead: // mRadioWaitTime.stopTimeMonitor(); // we got everything we expected and can exit rx mode... - //optionally instead: mRadioWaitTime.startTimeMonitor(DURATION_PAUSE_LASTFR); // let the inverter first get back to rx mode? } CmtType mCmt;