diff --git a/src/CHANGES.md b/src/CHANGES.md index 1ec8d9f1..ff7fd956 100644 --- a/src/CHANGES.md +++ b/src/CHANGES.md @@ -1,10 +1,11 @@ Changelog for ahoy-all-in-one compared to 0.6.9 of the main project -- read SML/OBIS from UART (stream parser with min resources needed); Connections 9600,8,n,1, GND-GND, VCC-3V3, TX-TX, RX-RX +- configurable read SML/OBIS from UART (stream parser with min resources needed); Connections 9600,8,n,1, GND-GND, VCC-3V3, TX-TX, RX-RX - prepared to show chart of grid power and total solar ac power for current days daylight period (6 a.m. to 8 p.m.) - show current grid power - show max solar ac/dc power -- improved radio retransmit (complete retransmit if nothing was received, but only when inverter ought to be active) for the hm series +- improved radio retransmit (complete retransmit if nothing was received, but only when inverter ought to be active) for the hm series) +- Heuristic for choosing the best send channel (of 5 possible) helps reducing retransmits - shortcut radio traces a little bit DRAWBACKS: diff --git a/src/app.cpp b/src/app.cpp index 226e9ecc..6b197dbd 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -121,15 +121,16 @@ void app::loopStandard(void) { packet_t *p = &mSys.Radio.mBufCtrl.front(); if (mConfig->serial.debug) { +#ifdef undef DPRINT(DBG_INFO, F("RX ")); DBGPRINT(String(p->len)); -#ifdef undef DBGPRINT(F("B Ch")); DBGPRINT(String(p->ch)); DBGPRINT(F(" | ")); mSys.Radio.dumpBuf(p->packet, p->len); #else - DBGPRINTLN(" Bytes"); + DPRINTLN(DBG_INFO, "RX (Ch " + String (p->ch) + "), " + + String (p->len) + " Bytes"); #endif } @@ -366,7 +367,8 @@ void app::tickMidnight(void) { mMqtt.tickerMidnight(); #endif } - mSys.cleanup_history (); + mSys.Radio.resetSendChannelQuality(); + mSys.cleanup_history(); #ifdef AHOY_SML_OBIS_SUPPORT // design: allways try to clean up sml_cleanup_history(); diff --git a/src/hm/hmPayload.h b/src/hm/hmPayload.h index dfb8464c..c9c91159 100644 --- a/src/hm/hmPayload.h +++ b/src/hm/hmPayload.h @@ -25,6 +25,7 @@ typedef struct { bool requested; bool gotFragment; bool rxTmo; + uint8_t lastFragments; // for send quality measurement } invPayload_t; @@ -246,7 +247,13 @@ class HmPayload { if (!mPayload[iv->id].complete) { bool crcPass, pyldComplete; - crcPass = build(iv->id, &pyldComplete); + uint8 Fragments; + crcPass = build(iv->id, &pyldComplete, &Fragments); + + // evaluate quality of send channel with rcv params + mSys->Radio.evalSendChannelQuality (crcPass, mPayload[iv->id].retransmits, + Fragments, mPayload[iv->id].lastFragments); + mPayload[iv->id].lastFragments = Fragments; if (!crcPass && !pyldComplete) { // payload not complete if ((mPayload[iv->id].requested) && (retransmit)) { if (mPayload[iv->id].retransmits < mMaxRetrans) { @@ -351,6 +358,7 @@ class HmPayload { } iv->doCalculations(); uint8_t pos = iv->getPosByChFld(CH0, FLD_PAC, rec); + if (pos != 0xff) { float ac_power = iv->getValue(pos, rec); mSys->handle_pac (iv, (uint16_t)(ac_power+0.5f)); @@ -396,7 +404,7 @@ class HmPayload { (mCbAlarm)(code, start, endTime); } - bool build(uint8_t id, bool *complete) { + bool build(uint8_t id, bool *complete, uint8_t *fragments) { DPRINTLN(DBG_VERBOSE, F("build")); uint16_t crc = 0xffff, crcRcv = 0x0000; if (mPayload[id].maxPackId > MAX_PAYLOAD_ENTRIES) @@ -404,9 +412,13 @@ class HmPayload { // check if all fragments are there *complete = true; + *fragments = 0; for (uint8_t i = 0; i < mPayload[id].maxPackId; i++) { - if(mPayload[id].len[i] == 0) + if(mPayload[id].len[i] == 0) { *complete = false; + } else { + (*fragments)++; + } } if(!*complete) return false; @@ -440,6 +452,7 @@ class HmPayload { mPayload[id].requested = false; mPayload[id].ts = *mTimestamp; mPayload[id].rxTmo = true; // design: dont start with complete retransmit + mPayload[id].lastFragments = 0; // for send channel quality measurement } IApp *mApp; diff --git a/src/hm/hmRadio.h b/src/hm/hmRadio.h index 29d8ae0d..2a46d5bc 100644 --- a/src/hm/hmRadio.h +++ b/src/hm/hmRadio.h @@ -21,6 +21,15 @@ #define ALL_FRAMES 0x80 #define SINGLE_FRAME 0x81 +#define SEND_CHANNEL_QUALITY_INTEGRATOR_SIZE 4 +#define SEND_CHANNEL_MAX_QUALITY 4 +#define SEND_CHANNEL_MIN_QUALITY -6 +#define SEND_CHANNEL_QUALITY_GOOD 2 +#define SEND_CHANNEL_QUALITY_OK 1 +#define SEND_CHANNEL_QUALITY_NEUTRAL 0 +#define SEND_CHANNEL_QUALITY_LOW -1 +#define SEND_CHANNEL_QUALITY_BAD -2 + const char* const rf24AmpPowerNames[] = {"MIN", "LOW", "HIGH", "MAX"}; @@ -269,6 +278,82 @@ class HmRadio { return mNrf24.isPVariant(); } + bool isNewSendChannel () + { + return mTxChIdx != mTxLastChIdx; + } + + uint8_t getNextSendChannelIndex (void) + { + // start with the next index: round robbin in case of same max bad quality for all channels + uint8_t bestIndex = (mTxChIdx + 1) % RF_CHANNELS; + uint8_t curIndex = (bestIndex + 1) % RF_CHANNELS; + uint16_t i; + + for (i=1; i mChQuality[bestIndex]) { + bestIndex = curIndex; + } + curIndex = (curIndex + 1) % RF_CHANNELS; + } + return bestIndex; + } + + void addSendChannelQuality (int8_t quality) + { + // continous averaging + // assume: mTxChIdx is still the last send channel index used + quality = mChQuality[mTxChIdx] + quality; + if (quality < SEND_CHANNEL_MIN_QUALITY) { + quality = SEND_CHANNEL_MIN_QUALITY; + } else if (quality > SEND_CHANNEL_MAX_QUALITY) { + quality = SEND_CHANNEL_MAX_QUALITY; + } + mChQuality[mTxChIdx] = quality; + } + + void evalSendChannelQuality (bool crcPass, uint8_t Retransmits, uint8_t rxFragments, + uint8_t lastRxFragments) + { + if (lastRxFragments == rxFragments) { + // nothing received: send probably lost + if (!Retransmits || isNewSendChannel()) { + // dont overestimate burst distortion + addSendChannelQuality (SEND_CHANNEL_QUALITY_BAD); + } + } else if (!lastRxFragments && crcPass) { + if (!Retransmits || isNewSendChannel()) { + // every fragment received successfull immediately + addSendChannelQuality (SEND_CHANNEL_QUALITY_GOOD); + } else { + // every fragment received successfully + addSendChannelQuality (SEND_CHANNEL_QUALITY_OK); + } + } else if (crcPass) { + if (isNewSendChannel ()) { + // last Fragment successfully received on new send channel + addSendChannelQuality (SEND_CHANNEL_QUALITY_OK); + } + } else if (!Retransmits || isNewSendChannel()) { + // no complete receive for this send channel + addSendChannelQuality (SEND_CHANNEL_QUALITY_LOW); + } + } + + void resetSendChannelQuality () + { + for(uint8_t i = 0; i < RF_CHANNELS; i++) { + mChQuality[mTxChIdx] = 0; + } + } + + void dumpSendQuality() + { + for(uint8_t i = 0; i < RF_CHANNELS; i++) { + DBGPRINT(" " + String (mChQuality[i])); + } + } + std::queue mBufCtrl; uint32_t mSendCnt; @@ -337,22 +422,26 @@ class HmRadio { len++; // set TX and RX channels - mTxChIdx = (mTxChIdx + 1) % RF_CHANNELS; + + mTxLastChIdx = mTxChIdx; + mTxChIdx = getNextSendChannelIndex (); mRxChIdx = (mTxChIdx + 2) % RF_CHANNELS; if(mSerialDebug) { +#ifdef undef DPRINT(DBG_INFO, F("TX ")); DBGPRINT(String(len)); -#ifdef undef DBGPRINT("B Ch"); DBGPRINT(String(mRfChLst[mTxChIdx])); DBGPRINT(F(" | ")); dumpBuf(mTxBuf, len); #else - DBGPRINTLN (" Bytes"); + DPRINT(DBG_INFO, F("TX (Ch ") + String (mRfChLst[mTxChIdx]) + "), " + + String (len) + " Bytes, Quality:"); + dumpSendQuality(); + DBGPRINTLN(""); #endif } - mNrf24.stopListening(); mNrf24.setChannel(mRfChLst[mTxChIdx]); mNrf24.openWritingPipe(reinterpret_cast(&invId)); @@ -368,7 +457,9 @@ class HmRadio { uint64_t DTU_RADIO_ID; uint8_t mRfChLst[RF_CHANNELS]; + int8_t mChQuality[RF_CHANNELS]; uint8_t mTxChIdx; + uint8_t mTxLastChIdx; uint8_t mRxChIdx; SPIClass* mSpi; diff --git a/src/plugins/SML_OBIS_Parser.cpp b/src/plugins/SML_OBIS_Parser.cpp index 93d11265..bbcb3eeb 100644 --- a/src/plugins/SML_OBIS_Parser.cpp +++ b/src/plugins/SML_OBIS_Parser.cpp @@ -19,6 +19,7 @@ #define min(a,b) (((a) < (b)) ? (a) : (b)) #endif + #define SML_ESCAPE_CHAR 0x1b #define SML_VERSION1_CHAR 0x01 #define SML_MAX_LIST_LAYER 8 @@ -377,7 +378,7 @@ int sml_find_hist_power (File file, uint16_t index) return sml_get_obis_pac_average (); } DPRINTLN (DBG_DEBUG, "sml_find_hist_power(1), cant find " + String (index)); - return -1; + return INT32_MIN; } if (cmp_index == index) { return (int16_t)(data[2] + (data[3] << 8)); @@ -387,7 +388,7 @@ int sml_find_hist_power (File file, uint16_t index) } else if ((index == obis_cur_pac_index) && obis_cur_pac_cnt) { return sml_get_obis_pac_average (); } - return -1; + return INT32_MIN; } //----------------------------------------------------------------------------- diff --git a/src/web/RestApi.h b/src/web/RestApi.h index fb9c33e0..9305f54d 100644 --- a/src/web/RestApi.h +++ b/src/web/RestApi.h @@ -296,7 +296,7 @@ class RestApi { #ifdef AHOY_SML_OBIS_SUPPORT if (mConfig->sml_obis.ir_connected) { - if ((sml_power = sml_find_hist_power(sml_hist, cur_interval)) == -1) { + if ((sml_power = sml_find_hist_power(sml_hist, cur_interval)) == INT32_MIN) { snprintf (&content[index], length - index, "\n%u:%02u,%u,", minutes / 60, minutes % 60, ac_power); } else { @@ -319,7 +319,7 @@ class RestApi { #ifdef AHOY_SML_OBIS_SUPPORT if (mConfig->sml_obis.ir_connected) { - if ((sml_power = sml_find_hist_power(sml_hist, cur_interval)) == -1) { + if ((sml_power = sml_find_hist_power(sml_hist, cur_interval)) == INT32_MIN) { snprintf (&content[index], length - index, "\n%u:%02u,%u,", minutes / 60, minutes % 60, ac_power); } else {