Browse Source

Merge branch 'development03' into Zero-Export

pull/1155/head
DanielR92 2 years ago
parent
commit
06bb525e6e
  1. 5
      src/CHANGES.md
  2. 2
      src/app.cpp
  3. 3
      src/config/config.h
  4. 4
      src/config/settings.h
  5. 2
      src/defines.h
  6. 4
      src/hm/CommQueue.h
  7. 128
      src/hm/Communication.h
  8. 6
      src/hm/Heuristic.h
  9. 89
      src/hm/hmInverter.h
  10. 21
      src/hm/hmRadio.h
  11. 4
      src/hm/radio.h
  12. 18
      src/platformio.ini
  13. 5
      src/utils/helper.cpp
  14. 2
      src/utils/helper.h
  15. 1
      src/web/RestApi.h
  16. 5
      src/web/html/setup.html
  17. 6
      src/web/web.h

5
src/CHANGES.md

@ -1,5 +1,10 @@
# Development Changes # Development Changes
## 0.8.9 - 2023-11-19
* merged PR #1234
* added new alarm codes
* removed serial interval, was not in use anymore
## 0.8.8 - 2023-11-16 ## 0.8.8 - 2023-11-16
* fix ESP8266 save inverter #1232 * fix ESP8266 save inverter #1232

2
src/app.cpp

@ -154,7 +154,7 @@ void app::regularTickers(void) {
everySec(std::bind(&ZeroExportType::tickerSecond, &mzExport), "zExport"); everySec(std::bind(&ZeroExportType::tickerSecond, &mzExport), "zExport");
#endif #endif
every(std::bind(&PubSerialType::tick, &mPubSerial), mConfig->serial.interval, "uart"); every(std::bind(&PubSerialType::tick, &mPubSerial), 5, "uart");
#if !defined(ETHERNET) #if !defined(ETHERNET)
//everySec([this]() { mImprov.tickSerial(); }, "impro"); //everySec([this]() { mImprov.tickSerial(); }, "impro");
#endif #endif

3
src/config/config.h

@ -142,9 +142,6 @@
#define MAX_NUM_INVERTERS 4 #define MAX_NUM_INVERTERS 4
#endif #endif
// default serial interval
#define SERIAL_INTERVAL 5
// default send interval // default send interval
#define SEND_INTERVAL 15 #define SEND_INTERVAL 15

4
src/config/settings.h

@ -114,7 +114,6 @@ typedef struct {
} cfgSun_t; } cfgSun_t;
typedef struct { typedef struct {
uint16_t interval;
bool showIv; bool showIv;
bool debug; bool debug;
} cfgSerial_t; } cfgSerial_t;
@ -446,7 +445,6 @@ class settings {
mCfg.sun.lon = 0.0; mCfg.sun.lon = 0.0;
mCfg.sun.offsetSec = 0; mCfg.sun.offsetSec = 0;
mCfg.serial.interval = SERIAL_INTERVAL;
mCfg.serial.showIv = false; mCfg.serial.showIv = false;
mCfg.serial.debug = false; mCfg.serial.debug = false;
@ -656,11 +654,9 @@ class settings {
void jsonSerial(JsonObject obj, bool set = false) { void jsonSerial(JsonObject obj, bool set = false) {
if(set) { if(set) {
obj[F("intvl")] = mCfg.serial.interval;
obj[F("show")] = mCfg.serial.showIv; obj[F("show")] = mCfg.serial.showIv;
obj[F("debug")] = mCfg.serial.debug; obj[F("debug")] = mCfg.serial.debug;
} else { } else {
getVal<uint16_t>(obj, F("intvl"), &mCfg.serial.interval);
getVal<bool>(obj, F("show"), &mCfg.serial.showIv); getVal<bool>(obj, F("show"), &mCfg.serial.showIv);
getVal<bool>(obj, F("debug"), &mCfg.serial.debug); getVal<bool>(obj, F("debug"), &mCfg.serial.debug);
} }

2
src/defines.h

@ -13,7 +13,7 @@
//------------------------------------- //-------------------------------------
#define VERSION_MAJOR 0 #define VERSION_MAJOR 0
#define VERSION_MINOR 8 #define VERSION_MINOR 8
#define VERSION_PATCH 8 #define VERSION_PATCH 9
//------------------------------------- //-------------------------------------
typedef struct { typedef struct {

4
src/hm/CommQueue.h

@ -85,6 +85,10 @@ class CommQueue {
mQueue[mRdPtr].attempts--; mQueue[mRdPtr].attempts--;
} }
void incrAttempt(void) {
mQueue[mRdPtr].attempts++;
}
void inc(uint8_t *ptr) { void inc(uint8_t *ptr) {
if(++(*ptr) >= N) if(++(*ptr) >= N)
*ptr = 0; *ptr = 0;

128
src/hm/Communication.h

@ -11,9 +11,10 @@
#include "../utils/crc.h" #include "../utils/crc.h"
#include "Heuristic.h" #include "Heuristic.h"
#define MI_TIMEOUT 250 #define MI_TIMEOUT 250 // timeout for MI type requests
#define DEFAULT_TIMEOUT 500 #define FRSTMSG_TIMEOUT 150 // how long to wait for first msg to be received
#define SINGLEFR_TIMEOUT 60 #define DEFAULT_TIMEOUT 500 // timeout for regular requests
#define SINGLEFR_TIMEOUT 65 // timeout for single frame requests
#define MAX_BUFFER 250 #define MAX_BUFFER 250
typedef std::function<void(uint8_t, Inverter<> *)> payloadListenerType; typedef std::function<void(uint8_t, Inverter<> *)> payloadListenerType;
@ -43,7 +44,8 @@ class Communication : public CommQueue<> {
if(!valid) if(!valid)
return; // empty return; // empty
uint16_t timeout = q->iv->ivGen != IV_MI ? DEFAULT_TIMEOUT : MI_TIMEOUT; uint16_t timeout = q->iv->ivGen != IV_MI ? ((q->iv->mGotFragment && q->iv->mGotLastMsg) ? SINGLEFR_TIMEOUT : DEFAULT_TIMEOUT) : MI_TIMEOUT;
uint16_t timeout_min = q->iv->ivGen != IV_MI ? ((q->iv->mGotFragment) ? SINGLEFR_TIMEOUT : FRSTMSG_TIMEOUT) : MI_TIMEOUT;
bool testMode = false; bool testMode = false;
switch(mState) { switch(mState) {
@ -55,11 +57,13 @@ class Communication : public CommQueue<> {
mLocalBuf[i].len = 0; mLocalBuf[i].len = 0;
} }
if(q->iv->radio->isSerialDebug())
mHeu.printStatus(q->iv); mHeu.printStatus(q->iv);
mHeu.getTxCh(q->iv); mHeu.getTxCh(q->iv);
testMode = mHeu.getTestModeEnabled(); testMode = mHeu.getTestModeEnabled();
mGotFragment = false; q->iv->mGotFragment = false;
mFirstTry = mFirstTry ? false : ( (IV_HM == q->iv->ivGen) || (IV_MI == q->iv->ivGen) ) && (q->iv->isAvailable()); //) || (millis() < 120000));} q->iv->mGotLastMsg = false;
mFirstTry = mFirstTry ? false : (((IV_HM == q->iv->ivGen) || (IV_MI == q->iv->ivGen) ) && (q->iv->isAvailable()) || (millis() < 120000));
if(NULL == q->iv->radio) if(NULL == q->iv->radio)
cmdDone(true); // can't communicate while radio is not defined! cmdDone(true); // can't communicate while radio is not defined!
mState = States::START; mState = States::START;
@ -84,11 +88,29 @@ class Communication : public CommQueue<> {
if(!testMode) if(!testMode)
q->iv->radioStatistics.txCnt++; q->iv->radioStatistics.txCnt++;
mWaitTimeout = millis() + timeout; mWaitTimeout = millis() + timeout;
mWaitTimeout_min = millis() + timeout_min;
setAttempt(); setAttempt();
mState = States::WAIT; mState = States::WAIT;
break; break;
case States::WAIT: case States::WAIT:
if(millis() > mWaitTimeout_min) {
if(!q->iv->mGotFragment) { // nothing received yet?
if(q->iv->mGotLastMsg) {
//mState = States::CHECK_FRAMES;
mWaitTimeout = mWaitTimeout_min;
}
} else if(mFirstTry) {
DPRINT_IVID(DBG_INFO, q->iv->id);
DBGPRINTLN(F("second try"));
mFirstTry = false;
if(!testMode)
q->iv->radioStatistics.retransmits++; // got nothing
mState = States::START;
break;
}
}
if(millis() < mWaitTimeout) if(millis() < mWaitTimeout)
return; return;
mState = States::CHECK_FRAMES; mState = States::CHECK_FRAMES;
@ -102,7 +124,7 @@ class Communication : public CommQueue<> {
DBGPRINT(String(millis() - mWaitTimeout + timeout)); DBGPRINT(String(millis() - mWaitTimeout + timeout));
DBGPRINTLN(F("ms")); DBGPRINTLN(F("ms"));
if(!mGotFragment && !mFirstTry) { if(!q->iv->mGotFragment) {
if(!testMode) if(!testMode)
q->iv->radioStatistics.rxFailNoAnser++; // got nothing q->iv->radioStatistics.rxFailNoAnser++; // got nothing
mHeu.setGotNothing(q->iv); mHeu.setGotNothing(q->iv);
@ -118,7 +140,6 @@ class Communication : public CommQueue<> {
break; break;
} }
mGotFragment = true;
mFirstTry = false; // for correct reset mFirstTry = false; // for correct reset
States nextState = States::RESET; States nextState = States::RESET;
while(!q->iv->radio->mBufCtrl.empty()) { while(!q->iv->radio->mBufCtrl.empty()) {
@ -140,7 +161,7 @@ class Communication : public CommQueue<> {
DBGPRINT(F(", ")); DBGPRINT(F(", "));
DBGPRINT(String(p->rssi)); DBGPRINT(String(p->rssi));
DBGPRINT(F("dBm | ")); DBGPRINT(F("dBm | "));
ah::dumpBuf(p->packet, p->len); ah::dumpBuf(p->packet, p->len, 1, 8, "#"+String(q->iv->id));
if(checkIvSerial(&p->packet[1], q->iv)) { if(checkIvSerial(&p->packet[1], q->iv)) {
if(!testMode) if(!testMode)
@ -178,40 +199,39 @@ class Communication : public CommQueue<> {
break; break;
case States::CHECK_PACKAGE: case States::CHECK_PACKAGE:
uint8_t framnr = 0;
if(0 == mMaxFrameId) { if(0 == mMaxFrameId) {
setAttempt();
DPRINT_IVID(DBG_WARN, q->iv->id);
DBGPRINT(F("frame missing: request retransmit ("));
DBGPRINT(String(q->attempts));
DBGPRINTLN(F(" attempts left)"));
uint8_t i = 0; uint8_t i = 0;
while(i < MAX_PAYLOAD_ENTRIES) { while(i < MAX_PAYLOAD_ENTRIES) {
if(mLocalBuf[i].len == 0) if(mLocalBuf[i].len == 0) {
framnr = i+1;
break; break;
}
i++; i++;
} }
sendRetransmit(q, i);
return;
} }
if(!framnr) {
for(uint8_t i = 0; i < mMaxFrameId; i++) { for(uint8_t i = 0; i < mMaxFrameId; i++) {
if(mLocalBuf[i].len == 0) { if(mLocalBuf[i].len == 0) {
framnr = i+1;
break;
}
}
}
if(framnr) {
setAttempt(); setAttempt();
DPRINT_IVID(DBG_WARN, q->iv->id); DPRINT_IVID(DBG_WARN, q->iv->id);
DBGPRINT(F("frame ")); DBGPRINT(F("frame "));
DBGPRINT(String(i + 1)); DBGPRINT(String(framnr));
DBGPRINT(F(" missing: request retransmit (")); DBGPRINT(F(" missing: request retransmit ("));
DBGPRINT(String(q->attempts)); DBGPRINT(String(q->attempts));
DBGPRINTLN(F(" attempts left)")); DBGPRINTLN(F(" attempts left)"));
sendRetransmit(q, framnr-1);
sendRetransmit(q, i);
return; return;
} }
}
mHeu.setGotAll(q->iv); mHeu.setGotAll(q->iv);
compilePayload(q, testMode); compilePayload(q, testMode);
@ -413,9 +433,6 @@ class Communication : public CommQueue<> {
inline void miHwDecode(packet_t *p, const queue_s *q) { inline void miHwDecode(packet_t *p, const queue_s *q) {
record_t<> *rec = q->iv->getRecordStruct(InverterDevInform_All); // choose the record structure record_t<> *rec = q->iv->getRecordStruct(InverterDevInform_All); // choose the record structure
rec->ts = q->ts; rec->ts = q->ts;
//mPayload[iv->id].gotFragment = true;
uint8_t multi_parts = 0;
/* /*
Polling the device software and hardware version number command Polling the device software and hardware version number command
start byte Command word routing address target address User data check end byte start byte Command word routing address target address User data check end byte
@ -458,14 +475,13 @@ class Communication : public CommQueue<> {
q->iv->isConnected = true; q->iv->isConnected = true;
//if(mSerialDebug) { //if(mSerialDebug) {
DPRINT_IVID(DBG_INFO, q->iv->id); DPRINT_IVID(DBG_INFO, q->iv->id);
DPRINT(DBG_INFO,F("HW_VER is ")); DBGPRINT(F("HW_VER is "));
DBGPRINTLN(String((p->packet[24] << 8) + p->packet[25])); DBGPRINTLN(String((p->packet[24] << 8) + p->packet[25]));
//} //}
record_t<> *rec = q->iv->getRecordStruct(InverterDevInform_Simple); // choose the record structure record_t<> *rec = q->iv->getRecordStruct(InverterDevInform_Simple); // choose the record structure
rec->ts = q->ts; rec->ts = q->ts;
q->iv->setValue(1, rec, (uint32_t) ((p->packet[24] << 8) + p->packet[25])/1); q->iv->setValue(1, rec, (uint32_t) ((p->packet[24] << 8) + p->packet[25])/1);
//mPayload[iv->id].multi_parts +=4; q->iv->miMultiParts +=4;
multi_parts +=4;
} else if ( p->packet[9] == 0x01 || p->packet[9] == 0x10 ) {//second frame for MI, 3rd gen. answers in 0x10 } else if ( p->packet[9] == 0x01 || p->packet[9] == 0x10 ) {//second frame for MI, 3rd gen. answers in 0x10
DPRINT_IVID(DBG_INFO, q->iv->id); DPRINT_IVID(DBG_INFO, q->iv->id);
if ( p->packet[9] == 0x01 ) { if ( p->packet[9] == 0x01 ) {
@ -486,19 +502,19 @@ class Communication : public CommQueue<> {
//if(mSerialDebug) { //if(mSerialDebug) {
DPRINT(DBG_INFO,F("HW_FB_TLmValue ")); DPRINT(DBG_INFO,F("HW_FB_TLmValue "));
DBGPRINTLN(String((p->packet[14] << 8) + p->packet[15])); DBGPRINTLN(String((p->packet[14] << 8) + p->packet[15]));
DPRINT(DBG_INFO,F("HW_FB_ReSPRT ")); DBGPRINT(F("HW_FB_ReSPRT "));
DBGPRINTLN(String((p->packet[16] << 8) + p->packet[17])); DBGPRINTLN(String((p->packet[16] << 8) + p->packet[17]));
DPRINT(DBG_INFO,F("HW_GridSamp_ResValule ")); DBGPRINT(F("HW_GridSamp_ResValule "));
DBGPRINTLN(String((p->packet[18] << 8) + p->packet[19])); DBGPRINTLN(String((p->packet[18] << 8) + p->packet[19]));
DPRINT(DBG_INFO,F("HW_ECapValue ")); DBGPRINT(F("HW_ECapValue "));
DBGPRINTLN(String((p->packet[20] << 8) + p->packet[21])); DBGPRINTLN(String((p->packet[20] << 8) + p->packet[21]));
DPRINT(DBG_INFO,F("Matching_APPFW_PN ")); DBGPRINT(F("Matching_APPFW_PN "));
DBGPRINTLN(String((uint32_t) (((p->packet[22] << 8) | p->packet[23]) << 8 | p->packet[24]) << 8 | p->packet[25])); DBGPRINTLN(String((uint32_t) (((p->packet[22] << 8) | p->packet[23]) << 8 | p->packet[24]) << 8 | p->packet[25]));
//} //}
//notify(InverterDevInform_Simple, iv); if(NULL != mCbPayload)
//mPayload[iv->id].multi_parts +=2; (mCbPayload)(InverterDevInform_All, q->iv);
multi_parts +=2; q->iv->miMultiParts +=2;
//notify(InverterDevInform_All, iv);
} else { } else {
DBGPRINTLN(F("3rd gen. inverter!")); DBGPRINTLN(F("3rd gen. inverter!"));
} }
@ -515,21 +531,24 @@ class Communication : public CommQueue<> {
//if(mSerialDebug) { //if(mSerialDebug) {
DPRINT(DBG_INFO,F("APPFW_MINVER ")); DPRINT(DBG_INFO,F("APPFW_MINVER "));
DBGPRINTLN(String((p->packet[10] << 8) + p->packet[11])); DBGPRINTLN(String((p->packet[10] << 8) + p->packet[11]));
DPRINT(DBG_INFO,F("HWInfoAddr ")); DBGPRINT(F("HWInfoAddr "));
DBGPRINTLN(String((p->packet[12] << 8) + p->packet[13])); DBGPRINTLN(String((p->packet[12] << 8) + p->packet[13]));
DPRINT(DBG_INFO,F("PNInfoCRC_gusv ")); DBGPRINT(F("PNInfoCRC_gusv "));
DBGPRINTLN(String((p->packet[14] << 8) + p->packet[15])); DBGPRINTLN(String((p->packet[14] << 8) + p->packet[15]));
//} //}
//mPayload[iv->id].multi_parts++; if(NULL != mCbPayload)
multi_parts++; (mCbPayload)(InverterDevInform_Simple, q->iv);
q->iv->miMultiParts++;
} }
if(multi_parts > 5) { if(q->iv->miMultiParts > 5) {
cmdDone(true); cmdDone(true);
mState = States::RESET; mState = States::RESET;
q->iv->radioStatistics.rxSuccess++; q->iv->radioStatistics.rxSuccess++;
mHeu.setGotAll(q->iv); mHeu.setGotAll(q->iv);
q->iv->miMultiParts = 0;
} else { } else {
mHeu.setGotFragment(q->iv); mHeu.setGotFragment(q->iv);
mState = States::WAIT;
} }
/*if (mPayload[iv->id].multi_parts > 5) { /*if (mPayload[iv->id].multi_parts > 5) {
@ -568,9 +587,8 @@ class Communication : public CommQueue<> {
q->iv->setValue(q->iv->getPosByChFld(0, FLD_T, rec), rec, (float) ((int16_t)(p->packet[21] << 8) + p->packet[22])/10); q->iv->setValue(q->iv->getPosByChFld(0, FLD_T, rec), rec, (float) ((int16_t)(p->packet[21] << 8) + p->packet[22])/10);
q->iv->setValue(q->iv->getPosByChFld(0, FLD_IRR, rec), rec, (float) (calcIrradiation(q->iv, datachan))); q->iv->setValue(q->iv->getPosByChFld(0, FLD_IRR, rec), rec, (float) (calcIrradiation(q->iv, datachan)));
//mPayload[q->iv->id].rssi[(datachan-1)] = p->rssi;
if (datachan == 1) //mPayload[q->iv->id].rssi[(datachan-1)] = p->rssi; if (datachan == 1)
q->iv->rssi = p->rssi; q->iv->rssi = p->rssi;
else if(q->iv->rssi > p->rssi) else if(q->iv->rssi > p->rssi)
q->iv->rssi = p->rssi; q->iv->rssi = p->rssi;
@ -597,6 +615,7 @@ class Communication : public CommQueue<> {
} else if((p->packet[0] == (MI_REQ_CH1 + ALL_FRAMES)) && (q->iv->type == INV_TYPE_2CH)) { } else if((p->packet[0] == (MI_REQ_CH1 + ALL_FRAMES)) && (q->iv->type == INV_TYPE_2CH)) {
//addImportant(q->iv, MI_REQ_CH2); //addImportant(q->iv, MI_REQ_CH2);
miNextRequest(MI_REQ_CH2, q); miNextRequest(MI_REQ_CH2, q);
//use also miMultiParts here for better statistics?
mHeu.setGotFragment(q->iv); mHeu.setGotFragment(q->iv);
} else { // first data msg for 1ch, 2nd for 2ch } else { // first data msg for 1ch, 2nd for 2ch
miComplete(q->iv); miComplete(q->iv);
@ -604,25 +623,24 @@ class Communication : public CommQueue<> {
} }
inline void miNextRequest(uint8_t cmd, const queue_s *q) { inline void miNextRequest(uint8_t cmd, const queue_s *q) {
//setAttempt(); incrAttempt(); // if function is called, we got something, and we necessarily need more transmissions for MI types...
DPRINT_IVID(DBG_WARN, q->iv->id); DPRINT_IVID(DBG_WARN, q->iv->id);
DBGPRINT(F("next request (")); DBGPRINT(F("next request ("));
DBGPRINT(String(q->attempts)); DBGPRINT(String(q->attempts));
DBGPRINT(F(" attempts left): 0x")); DBGPRINT(F(" attempts left): 0x"));
DBGHEXLN(cmd); DBGHEXLN(cmd);
if(q->attempts) { //if(q->attempts) {
q->iv->radio->sendCmdPacket(q->iv, cmd, 0x00, true); q->iv->radio->sendCmdPacket(q->iv, cmd, 0x00, true);
q->iv->radioStatistics.retransmits++; q->iv->radioStatistics.retransmits++;
mWaitTimeout = millis() + MI_TIMEOUT; mWaitTimeout = millis() + MI_TIMEOUT;
//chgCmd(Inverter<> *iv, uint8_t cmd, bool delOnPop = true)
chgCmd(cmd); chgCmd(cmd);
mState = States::WAIT; mState = States::WAIT;
} else { /*} else {
add(q, true); add(q, true);
cmdDone(); cmdDone();
mState = States::RESET; mState = States::RESET;
} }*/
} }
inline void miStsConsolidate(const queue_s *q, uint8_t stschan, record_t<> *rec, uint8_t uState, uint8_t uEnum, uint8_t lState = 0, uint8_t lEnum = 0) { inline void miStsConsolidate(const queue_s *q, uint8_t stschan, record_t<> *rec, uint8_t uState, uint8_t uEnum, uint8_t lState = 0, uint8_t lEnum = 0) {
@ -699,13 +717,10 @@ class Communication : public CommQueue<> {
inline void miComplete(Inverter<> *iv) { inline void miComplete(Inverter<> *iv) {
//if ( mPayload[iv->id].complete ) if (iv->radio->isSerialDebug()) {
// return; //if we got second message as well in repreated attempt
//mPayload[iv->id].complete = true;
//if (mSerialDebug) {
DPRINT_IVID(DBG_INFO, iv->id); DPRINT_IVID(DBG_INFO, iv->id);
DBGPRINTLN(F("got all data msgs")); DBGPRINTLN(F("got all data msgs"));
//} }
record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug);
iv->setValue(iv->getPosByChFld(0, FLD_YD, rec), rec, calcYieldDayCh0(iv,0)); iv->setValue(iv->getPosByChFld(0, FLD_YD, rec), rec, calcYieldDayCh0(iv,0));
@ -752,8 +767,9 @@ class Communication : public CommQueue<> {
States mState = States::RESET; States mState = States::RESET;
uint32_t *mTimestamp; uint32_t *mTimestamp;
uint32_t mWaitTimeout = 0; uint32_t mWaitTimeout = 0;
uint32_t mWaitTimeout_min = 0;
std::array<frame_t, MAX_PAYLOAD_ENTRIES> mLocalBuf; std::array<frame_t, MAX_PAYLOAD_ENTRIES> mLocalBuf;
bool mGotFragment = false; //bool mGotFragment = false;
bool mFirstTry = false; bool mFirstTry = false;
uint8_t mMaxFrameId; uint8_t mMaxFrameId;
uint8_t mPayload[MAX_BUFFER]; uint8_t mPayload[MAX_BUFFER];

6
src/hm/Heuristic.h

@ -62,7 +62,7 @@ class Heuristic {
void printStatus(Inverter<> *iv) { void printStatus(Inverter<> *iv) {
DPRINT_IVID(DBG_INFO, iv->id); DPRINT_IVID(DBG_INFO, iv->id);
DBGPRINT(F("CH qualities:")); DBGPRINT(F("Radio infos:"));
for(uint8_t i = 0; i < RF_MAX_CHANNEL_ID; i++) { for(uint8_t i = 0; i < RF_MAX_CHANNEL_ID; i++) {
DBGPRINT(F(" ")); DBGPRINT(F(" "));
DBGPRINT(String(iv->txRfQuality[i])); DBGPRINT(String(iv->txRfQuality[i]));
@ -74,7 +74,9 @@ class Heuristic {
DBGPRINT(F(", f: ")); DBGPRINT(F(", f: "));
DBGPRINT(String(iv->radioStatistics.rxFail)); DBGPRINT(String(iv->radioStatistics.rxFail));
DBGPRINT(F(", n: ")); DBGPRINT(F(", n: "));
DBGPRINTLN(String(iv->radioStatistics.rxFailNoAnser)); DBGPRINT(String(iv->radioStatistics.rxFailNoAnser));
DBGPRINT(F(" | p: ")); // better debugging for helpers...
DBGPRINTLN(String(iv->config->powerLevel));
} }
bool getTestModeEnabled(void) { bool getTestModeEnabled(void) {

89
src/hm/hmInverter.h

@ -125,6 +125,10 @@ class Inverter {
uint16_t alarmCnt; // counts the total number of occurred alarms uint16_t alarmCnt; // counts the total number of occurred alarms
uint16_t alarmLastId; // lastId which was received uint16_t alarmLastId; // lastId which was received
int8_t rssi; // RSSI int8_t rssi; // RSSI
uint8_t miMultiParts; // helper info for MI multiframe msgs
uint8_t outstandingFrames; // helper info to count difference between expected and received frames
bool mGotFragment; // shows if inverter has sent at least one fragment
bool mGotLastMsg; // shows if inverter has already finished transmission cycle
Radio *radio; // pointer to associated radio class Radio *radio; // pointer to associated radio class
statistics_t radioStatistics; // information about transmitted, failed, ... packets statistics_t radioStatistics; // information about transmitted, failed, ... packets
int8_t txRfQuality[5]; // heuristics tx quality (check 'Heuristics.h') int8_t txRfQuality[5]; // heuristics tx quality (check 'Heuristics.h')
@ -150,8 +154,11 @@ class Inverter {
alarmCnt = 0; alarmCnt = 0;
alarmLastId = 0; alarmLastId = 0;
rssi = -127; rssi = -127;
miMultiParts = 0;
mGotLastMsg = false;
radio = NULL; radio = NULL;
commEnabled = true; commEnabled = true;
memset(&radioStatistics, 0, sizeof(statistics_t)); memset(&radioStatistics, 0, sizeof(statistics_t));
memset(txRfQuality, -6, 5); memset(txRfQuality, -6, 5);
@ -587,14 +594,40 @@ class Inverter {
static String getAlarmStr(uint16_t alarmCode) { static String getAlarmStr(uint16_t alarmCode) {
switch (alarmCode) { // breaks are intentionally missing! switch (alarmCode) { // breaks are intentionally missing!
case 1: return String(F("Inverter start")); case 1: return String(F("Inverter start"));
case 2: return String(F("DTU command failed")); case 2: return String(F("Time calibration"));
case 73: return String(F("Temperature >80°C")); // https://github.com/tbnobody/OpenDTU/discussions/590#discussioncomment-6049750 case 3: return String(F("EEPROM reading and writing error during operation"));
case 4: return String(F("Offline"));
case 11: return String(F("Grid voltage surge"));
case 12: return String(F("Grid voltage sharp drop"));
case 13: return String(F("Grid frequency mutation"));
case 14: return String(F("Grid phase mutation"));
case 15: return String(F("Grid transient fluctuation"));
case 36: return String(F("INV overvoltage or overcurrent"));
case 46: return String(F("FB overvoltage"));
case 47: return String(F("FB overcurrent"));
case 48: return String(F("FB clamp overvoltage"));
case 49: return String(F("FB clamp overvoltage"));
case 61: return String(F("Calibration parameter error"));
case 62: return String(F("System configuration parameter error"));
case 63: return String(F("Abnormal power generation data"));
case 71: return String(F("Grid overvoltage load reduction (VW) function enable"));
case 72: return String(F("Power grid over-frequency load reduction (FW) function enable"));
case 73: return String(F("Over-temperature load reduction (TW) function enable"));
case 95: return String(F("PV-1: Module in suspected shadow"));
case 96: return String(F("PV-2: Module in suspected shadow"));
case 97: return String(F("PV-3: Module in suspected shadow"));
case 98: return String(F("PV-4: Module in suspected shadow"));
case 121: return String(F("Over temperature protection")); case 121: return String(F("Over temperature protection"));
case 122: return String(F("Microinverter is suspected of being stolen"));
case 123: return String(F("Locked by remote control"));
case 124: return String(F("Shut down by remote control")); case 124: return String(F("Shut down by remote control"));
case 125: return String(F("Grid configuration parameter error")); case 125: return String(F("Grid configuration parameter error"));
case 126: return String(F("Software error code 126")); case 126: return String(F("EEPROM reading and writing error"));
case 127: return String(F("Firmware error")); case 127: return String(F("Firmware error"));
case 128: return String(F("Software error code 128")); case 128: return String(F("Hardware configuration error"));
case 129: return String(F("Abnormal bias")); case 129: return String(F("Abnormal bias"));
case 130: return String(F("Offline")); case 130: return String(F("Offline"));
case 141: return String(F("Grid: Grid overvoltage")); case 141: return String(F("Grid: Grid overvoltage"));
@ -606,7 +639,12 @@ class Inverter {
case 147: return String(F("Grid: Power grid outage")); case 147: return String(F("Grid: Power grid outage"));
case 148: return String(F("Grid: Grid disconnection")); case 148: return String(F("Grid: Grid disconnection"));
case 149: return String(F("Grid: Island detected")); case 149: return String(F("Grid: Island detected"));
case 150: return String(F("DCI exceeded"));
case 171: return String(F("Grid: Abnormal phase difference between phase to phase")); case 171: return String(F("Grid: Abnormal phase difference between phase to phase"));
case 181: return String(F("Abnormal insulation impedance"));
case 182: return String(F("Abnormal grounding"));
case 205: return String(F("MPPT-A: Input overvoltage")); case 205: return String(F("MPPT-A: Input overvoltage"));
case 206: return String(F("MPPT-B: Input overvoltage")); case 206: return String(F("MPPT-B: Input overvoltage"));
case 207: return String(F("MPPT-A: Input undervoltage")); case 207: return String(F("MPPT-A: Input undervoltage"));
@ -625,24 +663,33 @@ class Inverter {
case 220: return String(F("PV-3: Input undervoltage")); case 220: return String(F("PV-3: Input undervoltage"));
case 221: return String(F("PV-4: Input overvoltage")); case 221: return String(F("PV-4: Input overvoltage"));
case 222: return String(F("PV-4: Input undervoltage")); case 222: return String(F("PV-4: Input undervoltage"));
case 301: return String(F("Hardware error code 301"));
case 302: return String(F("Hardware error code 302")); case 301: return String(F("FB short circuit failure"));
case 303: return String(F("Hardware error code 303")); case 302: return String(F("FB short circuit failure"));
case 304: return String(F("Hardware error code 304")); case 303: return String(F("FB overcurrent protection failure"));
case 305: return String(F("Hardware error code 305")); case 304: return String(F("FB overcurrent protection failure"));
case 306: return String(F("Hardware error code 306")); case 305: return String(F("FB clamp circuit failure"));
case 307: return String(F("Hardware error code 307")); case 306: return String(F("FB clamp circuit failure"));
case 308: return String(F("Hardware error code 308")); case 307: return String(F("INV power device failure"));
case 308: return String(F("INV overcurrent or overvoltage protection failure"));
case 309: return String(F("Hardware error code 309")); case 309: return String(F("Hardware error code 309"));
case 310: return String(F("Hardware error code 310")); case 310: return String(F("Hardware error code 310"));
case 311: return String(F("Hardware error code 311")); case 311: return String(F("Hardware error code 311"));
case 312: return String(F("Hardware error code 312")); case 312: return String(F("Hardware error code 312"));
case 313: return String(F("Hardware error code 313")); case 313: return String(F("Hardware error code 313"));
case 314: return String(F("Hardware error code 314")); case 314: return String(F("Hardware error code 314"));
case 5041: return String(F("Error code-04 Port 1"));
case 5042: return String(F("Error code-04 Port 2")); case 5011: return String(F("PV-1: MOSFET overcurrent (II)"));
case 5043: return String(F("Error code-04 Port 3")); case 5012: return String(F("PV-2: MOSFET overcurrent (II)"));
case 5044: return String(F("Error code-04 Port 4")); case 5013: return String(F("PV-3: MOSFET overcurrent (II)"));
case 5014: return String(F("PV-4: MOSFET overcurrent (II)"));
case 5020: return String(F("H-bridge MOSFET overcurrent or H-bridge overvoltage"));
case 5041: return String(F("PV-1: current overcurrent (II)"));
case 5042: return String(F("PV-2: current overcurrent (II)"));
case 5043: return String(F("PV-3: current overcurrent (II)"));
case 5044: return String(F("PV-4: current overcurrent (II)"));
case 5051: return String(F("PV Input 1 Overvoltage/Undervoltage")); case 5051: return String(F("PV Input 1 Overvoltage/Undervoltage"));
case 5052: return String(F("PV Input 2 Overvoltage/Undervoltage")); case 5052: return String(F("PV Input 2 Overvoltage/Undervoltage"));
case 5053: return String(F("PV Input 3 Overvoltage/Undervoltage")); case 5053: return String(F("PV Input 3 Overvoltage/Undervoltage"));
@ -652,10 +699,18 @@ class Inverter {
case 5080: return String(F("Grid Overvoltage/Undervoltage")); case 5080: return String(F("Grid Overvoltage/Undervoltage"));
case 5090: return String(F("Grid Overfrequency/Underfrequency")); case 5090: return String(F("Grid Overfrequency/Underfrequency"));
case 5100: return String(F("Island detected")); case 5100: return String(F("Island detected"));
case 5110: return String(F("GFDI"));
case 5120: return String(F("EEPROM reading and writing error")); case 5120: return String(F("EEPROM reading and writing error"));
case 5141:
case 5142:
case 5143:
case 5144:
return String(F("FB clamp overvoltage"));
case 5150: return String(F("10 min value grid overvoltage")); case 5150: return String(F("10 min value grid overvoltage"));
case 5160: return String(F("Grid transient fluctuation"));
case 5200: return String(F("Firmware error")); case 5200: return String(F("Firmware error"));
case 8310: return String(F("Shut down")); case 8310: return String(F("Shut down by remote control"));
case 8320: return String(F("Locked by remote control"));
case 9000: return String(F("Microinverter is suspected of being stolen")); case 9000: return String(F("Microinverter is suspected of being stolen"));
default: return String(F("Unknown")); default: return String(F("Unknown"));
} }

21
src/hm/hmRadio.h

@ -252,17 +252,25 @@ class HmRadio : public Radio {
p.millis = millis() - mMillis; p.millis = millis() - mMillis;
mNrf24.read(p.packet, p.len); mNrf24.read(p.packet, p.len);
if (p.packet[0] != 0x00) { if (p.packet[0] != 0x00) {
if(!checkIvSerial(&p.packet[1], mLastIv)) {
DPRINT(DBG_WARN, "RX other inverter: ");
ah::dumpBuf(p.packet, p.len);
return false;
}
mLastIv->mGotFragment = true;
mBufCtrl.push(p); 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 isLastPackage = (p.packet[9] > ALL_FRAMES); // > ALL_FRAMES indicates last packet received
else if (p.packet[0] == ( 0x0f + ALL_FRAMES) ) // response from MI get information command else if (p.packet[0] == ( 0x0f + ALL_FRAMES) ) // response from MI get information command
isLastPackage = (p.packet[9] > 0x10); // > 0x10 indicates last packet received isLastPackage = (p.packet[9] > 0x10); // > 0x10 indicates last packet received
else if ((p.packet[0] != 0x88) && (p.packet[0] != 0x92)) // ignore fragment number zero and MI status messages //#0 was p.packet[0] != 0x00 && 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 isLastPackage = true; // response from dev control command
} }
} }
yield(); yield();
} }
if(isLastPackage)
mLastIv->mGotLastMsg = true;
return isLastPackage; return isLastPackage;
} }
@ -280,7 +288,7 @@ class HmRadio : public Radio {
DBGPRINT(" CH"); DBGPRINT(" CH");
DBGPRINT(String(mTxChIdx)); DBGPRINT(String(mTxChIdx));
DBGPRINT(F(" | ")); DBGPRINT(F(" | "));
ah::dumpBuf(mTxBuf, len); ah::dumpBuf(mTxBuf, len, 1, 4, "#"+String(iv->id));
} }
mNrf24.stopListening(); mNrf24.stopListening();
@ -300,10 +308,19 @@ class HmRadio : public Radio {
return iv->ivGen; return iv->ivGen;
} }
inline bool checkIvSerial(uint8_t buf[], Inverter<> *iv) {
for(uint8_t i = 0; i < 4; i++) {
if(buf[3-i] != iv->radioId.b[i])
return false;
}
return true;
}
uint64_t DTU_RADIO_ID; uint64_t DTU_RADIO_ID;
uint8_t mRfChLst[RF_CHANNELS] = {03, 23, 40, 61, 75}; // channel List:2403, 2423, 2440, 2461, 2475MHz uint8_t mRfChLst[RF_CHANNELS] = {03, 23, 40, 61, 75}; // channel List:2403, 2423, 2440, 2461, 2475MHz
uint8_t mTxChIdx = 0; uint8_t mTxChIdx = 0;
uint8_t mRxChIdx = 0; uint8_t mRxChIdx = 0;
bool mGotLastMsg = false;
uint32_t mMillis; uint32_t mMillis;
SPIClass* mSpi; SPIClass* mSpi;

4
src/hm/radio.h

@ -38,6 +38,10 @@ class Radio {
mSerialDebug = true; mSerialDebug = true;
} }
bool isSerialDebug() {
return mSerialDebug;
}
void sendCmdPacket(Inverter<> *iv, uint8_t mid, uint8_t pid, bool isRetransmit, bool appendCrc16=true) { void sendCmdPacket(Inverter<> *iv, uint8_t mid, uint8_t pid, bool isRetransmit, bool appendCrc16=true) {
initPacket(getIvId(iv), mid, pid); initPacket(getIvId(iv), mid, pid);
sendPacket(iv, 10, isRetransmit, appendCrc16); sendPacket(iv, 10, isRetransmit, appendCrc16);

18
src/platformio.ini

@ -25,9 +25,9 @@ extra_scripts =
lib_deps = lib_deps =
https://github.com/yubox-node-org/ESPAsyncWebServer https://github.com/yubox-node-org/ESPAsyncWebServer
nrf24/RF24 @ 1.4.7 nrf24/RF24 @ 1.4.8
paulstoffregen/Time @ ^1.6.1 paulstoffregen/Time @ ^1.6.1
https://github.com/bertmelis/espMqttClient#v1.4.5 https://github.com/bertmelis/espMqttClient#v1.5.0
bblanchon/ArduinoJson @ ^6.21.3 bblanchon/ArduinoJson @ ^6.21.3
https://github.com/JChristensen/Timezone @ ^1.2.4 https://github.com/JChristensen/Timezone @ ^1.2.4
olikraus/U8g2 @ ^2.35.7 olikraus/U8g2 @ ^2.35.7
@ -71,7 +71,7 @@ monitor_filters =
esp8266_exception_decoder esp8266_exception_decoder
[env:esp32-wroom32] [env:esp32-wroom32]
platform = espressif32@6.3.2 platform = espressif32@6.4.0
board = lolin_d32 board = lolin_d32
build_flags = ${env.build_flags} build_flags = ${env.build_flags}
-DUSE_HSPI_FOR_EPD -DUSE_HSPI_FOR_EPD
@ -79,7 +79,7 @@ monitor_filters =
esp32_exception_decoder esp32_exception_decoder
[env:esp32-wroom32-prometheus] [env:esp32-wroom32-prometheus]
platform = espressif32@6.3.2 platform = espressif32@6.4.0
board = lolin_d32 board = lolin_d32
build_flags = ${env.build_flags} build_flags = ${env.build_flags}
-DUSE_HSPI_FOR_EPD -DUSE_HSPI_FOR_EPD
@ -93,9 +93,9 @@ board = esp32dev
lib_deps = lib_deps =
khoih-prog/AsyncWebServer_ESP32_W5500 khoih-prog/AsyncWebServer_ESP32_W5500
khoih-prog/AsyncUDP_ESP32_W5500 khoih-prog/AsyncUDP_ESP32_W5500
nrf24/RF24 @ ^1.4.7 nrf24/RF24 @ ^1.4.8
paulstoffregen/Time @ ^1.6.1 paulstoffregen/Time @ ^1.6.1
https://github.com/bertmelis/espMqttClient#v1.4.4 https://github.com/bertmelis/espMqttClient#v1.5.0
bblanchon/ArduinoJson @ ^6.21.3 bblanchon/ArduinoJson @ ^6.21.3
https://github.com/JChristensen/Timezone @ ^1.2.4 https://github.com/JChristensen/Timezone @ ^1.2.4
olikraus/U8g2 @ ^2.35.7 olikraus/U8g2 @ ^2.35.7
@ -110,7 +110,7 @@ monitor_filters =
esp32_exception_decoder esp32_exception_decoder
[env:esp32-s2-mini] [env:esp32-s2-mini]
platform = espressif32@6.3.2 platform = espressif32@6.4.0
board = lolin_s2_mini board = lolin_s2_mini
build_flags = ${env.build_flags} build_flags = ${env.build_flags}
-DUSE_HSPI_FOR_EPD -DUSE_HSPI_FOR_EPD
@ -124,7 +124,7 @@ monitor_filters =
esp32_exception_decoder esp32_exception_decoder
[env:opendtufusion] [env:opendtufusion]
platform = espressif32@6.3.2 platform = espressif32@6.4.0
board = esp32-s3-devkitc-1 board = esp32-s3-devkitc-1
upload_protocol = esp-builtin upload_protocol = esp-builtin
build_flags = ${env.build_flags} build_flags = ${env.build_flags}
@ -147,7 +147,7 @@ monitor_filters =
esp32_exception_decoder, colorize esp32_exception_decoder, colorize
[env:opendtufusion-dev] [env:opendtufusion-dev]
platform = espressif32@6.3.2 platform = espressif32@6.4.0
board = esp32-s3-devkitc-1 board = esp32-s3-devkitc-1
upload_protocol = esp-builtin upload_protocol = esp-builtin
build_flags = ${env.build_flags} build_flags = ${env.build_flags}

5
src/utils/helper.cpp

@ -86,9 +86,12 @@ namespace ah {
return ret; return ret;
} }
void dumpBuf(uint8_t buf[], uint8_t len) { void dumpBuf(uint8_t buf[], uint8_t len, uint8_t firstRepl, uint8_t lastRepl, String repl) {
for(uint8_t i = 0; i < len; i++) { for(uint8_t i = 0; i < len; i++) {
if(i < firstRepl || i > lastRepl)
DHEX(buf[i]); DHEX(buf[i]);
else
DBGPRINT(repl);
DBGPRINT(" "); DBGPRINT(" ");
} }
DBGPRINTLN(""); DBGPRINTLN("");

2
src/utils/helper.h

@ -45,7 +45,7 @@ namespace ah {
String getDateTimeStrFile(time_t t); String getDateTimeStrFile(time_t t);
String getTimeStr(time_t t); String getTimeStr(time_t t);
uint64_t Serial2u64(const char *val); uint64_t Serial2u64(const char *val);
void dumpBuf(uint8_t buf[], uint8_t len); void dumpBuf(uint8_t buf[], uint8_t len, uint8_t firstRepl = 0, uint8_t lastRepl = 0, String repl = "");
} }
#endif /*__HELPER_H__*/ #endif /*__HELPER_H__*/

1
src/web/RestApi.h

@ -574,7 +574,6 @@ class RestApi {
} }
void getSerial(JsonObject obj) { void getSerial(JsonObject obj) {
obj[F("interval")] = (uint16_t)mConfig->serial.interval;
obj[F("show_live_data")] = mConfig->serial.showIv; obj[F("show_live_data")] = mConfig->serial.showIv;
obj[F("debug")] = mConfig->serial.debug; obj[F("debug")] = mConfig->serial.debug;
} }

5
src/web/html/setup.html

@ -49,10 +49,6 @@
<div class="col-8 col-sm-3">Serial Debug</div> <div class="col-8 col-sm-3">Serial Debug</div>
<div class="col-4 col-sm-9"><input type="checkbox" name="serDbg"/></div> <div class="col-4 col-sm-9"><input type="checkbox" name="serDbg"/></div>
</div> </div>
<div class="row mb-3">
<div class="col-12 col-sm-3 my-2">Interval [s]</div>
<div class="col-12 col-sm-9"><input type="number" name="serIntvl" title="Invalid input"/></div>
</div>
</fieldset> </fieldset>
</div> </div>
@ -984,7 +980,6 @@
function parseSerial(obj) { function parseSerial(obj) {
for(var i of [["serEn", "show_live_data"], ["serDbg", "debug"]]) for(var i of [["serEn", "show_live_data"], ["serDbg", "debug"]])
document.getElementsByName(i[0])[0].checked = obj[i[1]]; document.getElementsByName(i[0])[0].checked = obj[i[1]];
document.getElementsByName("serIntvl")[0].value = obj["interval"];
} }
function parseDisplay(obj, type, system) { function parseDisplay(obj, type, system) {

6
src/web/web.h

@ -582,14 +582,8 @@ class Web {
#endif #endif
// serial console // serial console
if (request->arg("serIntvl") != "") {
mConfig->serial.interval = request->arg("serIntvl").toInt() & 0xffff;
mConfig->serial.debug = (request->arg("serDbg") == "on"); mConfig->serial.debug = (request->arg("serDbg") == "on");
mConfig->serial.showIv = (request->arg("serEn") == "on"); mConfig->serial.showIv = (request->arg("serEn") == "on");
// Needed to log TX buffers to serial console
// mSys->Radio.mSerialDebug = mConfig->serial.debug;
}
// display // display
mConfig->plugin.display.pwrSaveAtIvOffline = (request->arg("disp_pwr") == "on"); mConfig->plugin.display.pwrSaveAtIvOffline = (request->arg("disp_pwr") == "on");

Loading…
Cancel
Save