diff --git a/src/hm/CommQueue.h b/src/hm/CommQueue.h index 5f27db60..94cc63bd 100644 --- a/src/hm/CommQueue.h +++ b/src/hm/CommQueue.h @@ -22,10 +22,6 @@ class CommQueue { void add(Inverter<> *iv, uint8_t cmd, bool delOnPop = true) { mQueue[mWrPtr] = queue_s(iv, cmd, delOnPop, false); - /*mQueue[mRdPtr].firstTry = false; - if((IV_HM == mQueue[mRdPtr].iv->ivGen) || (IV_MI == mQueue[mRdPtr].iv->ivGen)) { - mQueue[mRdPtr].firstTry = ((mQueue[mRdPtr].iv->isAvailable()) || (millis() < 120000)); - }*/ inc(&mWrPtr); } @@ -36,7 +32,6 @@ class CommQueue { uint8_t attempts; bool delOnPop; bool isDevControl; - bool firstTry; uint32_t ts; queue_s() {} queue_s(Inverter<> *i, uint8_t c, bool d, bool dev) : @@ -46,10 +41,13 @@ class CommQueue { protected: void add(queue_s q) { mQueue[mWrPtr] = q; - /*mQueue[mRdPtr].firstTry = false; - if((IV_HM == mQueue[mRdPtr].iv->ivGen) || (IV_MI == mQueue[mRdPtr].iv->ivGen)) { - mQueue[mRdPtr].firstTry = ((mQueue[mRdPtr].iv->isAvailable()) || (millis() < 120000)); - }*/ + inc(&mWrPtr); + } + + void add(const queue_s *q, bool rstAttempts = false) { + mQueue[mWrPtr] = *q; + if(rstAttempts) + mQueue[mWrPtr].attempts = 5; inc(&mWrPtr); } @@ -69,13 +67,6 @@ class CommQueue { inc(&mRdPtr); } - bool isFirstTry(void) { - if(!mQueue[mRdPtr].firstTry) - return false; - mQueue[mRdPtr].firstTry = false; - return true; - } - void setTs(uint32_t *ts) { mQueue[mRdPtr].ts = *ts; } diff --git a/src/hm/Communication.h b/src/hm/Communication.h index 2dbf4593..d8859477 100644 --- a/src/hm/Communication.h +++ b/src/hm/Communication.h @@ -10,6 +10,9 @@ #include #include "../utils/crc.h" +#define MI_TIMEOUT 500 +#define DEFAULT_TIMEOUT 500 + typedef std::function *)> payloadListenerType; typedef std::function *)> alarmListenerType; @@ -37,6 +40,8 @@ class Communication : public CommQueue<> { if(!valid) return; // empty + uint16_t lcl_tmo = q->iv->ivGen != IV_MI ? DEFAULT_TIMEOUT : MI_TIMEOUT; + switch(mState) { case States::RESET: if(millis() < mWaitTimeout) @@ -50,34 +55,26 @@ class Communication : public CommQueue<> { case States::START: setTs(mTimestamp); - if (q->iv->ivGen != IV_MI) { - if(q->isDevControl) { - if(ActivePowerContr == q->cmd) - q->iv->powerLimitAck = false; - q->iv->radio->sendControlPacket(q->iv, q->cmd, q->iv->powerLimit, false); - } else - q->iv->radio->prepareDevInformCmd(q->iv, q->cmd, q->ts, q->iv->alarmLastId, false); - } else { // IV_MI - if(q->isDevControl) { - if(ActivePowerContr == q->cmd) - q->iv->powerLimitAck = false; - q->iv->radio->sendControlPacket(q->iv, q->cmd, q->iv->powerLimit, false, false, (q->iv->powerLimit[1] == RelativNonPersistent) ? 0 : q->iv->getMaxPower()); - } else { - //uint8_t cmd = q->iv->type == INV_TYPE_4CH ? MI_REQ_4CH : MI_REQ_CH1; - //q->iv->radio->sendCmdPacket(q->iv, cmd, cmd, false, false); - q->iv->radio->sendCmdPacket(q->iv, q->cmd, q->cmd, false, false); - //if (q->iv->radio->mSerialDebug) { - DPRINT_IVID(DBG_INFO, q->iv->id); - DBGPRINT(F("legacy cmd 0x")); - DBGHEXLN(q->cmd); - //} - //mPayload[q->iv->id]. = cmd; - } - + if(q->isDevControl) { + if(ActivePowerContr == q->cmd) + q->iv->powerLimitAck = false; + q->iv->radio->sendControlPacket(q->iv, q->cmd, q->iv->powerLimit, false); + } + else if (q->iv->ivGen != IV_MI) + q->iv->radio->prepareDevInformCmd(q->iv, q->cmd, q->ts, q->iv->alarmLastId, false); + else { // IV_MI + //uint8_t cmd = q->iv->type == INV_TYPE_4CH ? MI_REQ_4CH : MI_REQ_CH1; + //q->iv->radio->sendCmdPacket(q->iv, cmd, cmd, false, false); + q->iv->radio->sendCmdPacket(q->iv, q->cmd, q->cmd, false, false); + //if (q->iv->radio->mSerialDebug) { + DPRINT_IVID(DBG_INFO, q->iv->id); + DBGPRINT(F("legacy cmd 0x")); + DBGHEXLN(q->cmd); + //} + //mPayload[q->iv->id]. = cmd; } - q->iv->radioStatistics.txCnt++; - mWaitTimeout = millis() + 500; + mWaitTimeout = millis() + lcl_tmo; setAttempt(); mState = States::WAIT; break; @@ -90,17 +87,10 @@ class Communication : public CommQueue<> { case States::CHECK_FRAMES: { if(!q->iv->radio->get()) { // radio buffer empty - // second try for nRF type inverters if available - /*if(isFirstTry()) { - DPRINT_IVID(DBG_INFO, q->iv->id); - DBGPRINTLN(F("enqueue second try")); - mState = States::START; - break; - }*/ cmdDone(); DPRINT_IVID(DBG_INFO, q->iv->id); DBGPRINT(F("request timeout: ")); - DBGPRINT(String(millis() - mWaitTimeout + 500)); + DBGPRINT(String(millis() - mWaitTimeout + lcl_tmo)); DBGPRINTLN(F("ms")); q->iv->radioStatistics.rxFailNoAnser++; // got nothing @@ -118,15 +108,15 @@ class Communication : public CommQueue<> { DPRINT_IVID(DBG_INFO, q->iv->id); DBGPRINT(F("RX ")); - if(p->millis<100) - DBGPRINT("0"); + if(p->millis < 100) + DBGPRINT(F("0")); DBGPRINT(String(p->millis)); DBGPRINT(F("ms ")); DBGPRINT(String(p->len)); if((IV_HM == q->iv->ivGen) || (IV_MI == q->iv->ivGen)) { DBGPRINT(F(" CH")); - if(p->ch == 3) - DBGPRINT("03"); + if(3 == p->ch) + DBGPRINT(F("03")); else DBGPRINT(String(p->ch)); } @@ -144,25 +134,11 @@ class Communication : public CommQueue<> { } else if (p->packet[0] == (TX_REQ_DEVCONTROL + ALL_FRAMES)) { // response from dev control command parseDevCtrl(p, q); cmdDone(true); // remove done request - } else if (q->iv->ivGen == IV_MI) { - /*if (p->packet[0] == (0x88)) { // 0x88 is MI status response to 0x09 - miStsDecode(p, q); - } - - else if (p->packet[0] == (MI_REQ_CH2 + SINGLE_FRAME)) { // 0x92; MI status response to 0x11 - miStsDecode(p, q, CH2); - - } else*/ 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) && p->packet[0] < (0x39 + SINGLE_FRAME) - && q->cmd != 0x0f) ) { // small MI or MI 1500 data responses to 0x09, 0x11, 0x36, 0x37, 0x38 and 0x39 - //mPayload[iv->id].txId = p->packet[0]; - miDataDecode(p, q); - - } - //parseMiFrame(p, q); + } else if(IV_MI == q->iv->ivGen) { + parseMiFrame(p, q); } - } + } else + DPRINTLN(DBG_WARN, F("Inverter serial does not match")); q->iv->radio->mBufCtrl.pop(); yield(); @@ -176,7 +152,7 @@ class Communication : public CommQueue<> { setAttempt(); DPRINT_IVID(DBG_WARN, q->iv->id); - DBGPRINT(F("last frame missing: request retransmit (")); + DBGPRINT(F("frame missing: request retransmit (")); DBGPRINT(String(q->attempts)); DBGPRINTLN(F(" attempts left)")); @@ -186,11 +162,17 @@ class Communication : public CommQueue<> { break; } - q->iv->radio->sendCmdPacket(q->iv, TX_REQ_INFO, (ALL_FRAMES + i), true); - q->iv->radioStatistics.retransmits++; - mWaitTimeout = millis() + 500; - mState = States::WAIT; - break; + if(q->attempts) { + q->iv->radio->sendCmdPacket(q->iv, TX_REQ_INFO, (ALL_FRAMES + i), true); + q->iv->radioStatistics.retransmits++; + mWaitTimeout = millis() + lcl_tmo; + mState = States::WAIT; + } else { + add(q, true); + cmdDone(q); + mState = States::RESET; + } + return; } for(uint8_t i = 0; i < mMaxFrameId; i++) { @@ -204,10 +186,16 @@ class Communication : public CommQueue<> { DBGPRINT(String(q->attempts)); DBGPRINTLN(F(" attempts left)")); - q->iv->radio->sendCmdPacket(q->iv, TX_REQ_INFO, (ALL_FRAMES + i), true); - q->iv->radioStatistics.retransmits++; - mWaitTimeout = millis() + 500; - mState = States::WAIT; + if(q->attempts) { + q->iv->radio->sendCmdPacket(q->iv, TX_REQ_INFO, (ALL_FRAMES + i), true); + q->iv->radioStatistics.retransmits++; + mWaitTimeout = millis() + lcl_tmo; + mState = States::WAIT; + } else { + add(q, true); + cmdDone(q); + mState = States::RESET; + } return; } } @@ -264,47 +252,167 @@ class Communication : public CommQueue<> { f->rssi = p->rssi; } - inline void parseDevCtrl(packet_t *p, const queue_s *q) { - if((p->packet[12] != ActivePowerContr) || (p->packet[13] != 0x00)) - return; - bool accepted = true; - if((p->packet[10] == 0x00) && (p->packet[11] == 0x00)) - q->iv->powerLimitAck = true; - else - accepted = false; + inline void parseMiFrame(packet_t *p, const queue_s *q) { + 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)) + && (p->packet[0] < (0x39 + SINGLE_FRAME)) + && (q->cmd != 0x0f))) { + // small MI or MI 1500 data responses to 0x09, 0x11, 0x36, 0x37, 0x38 and 0x39 + //mPayload[iv->id].txId = p->packet[0]; + miDataDecode(p, q); + + } + else if (p->packet[0] == 0x0f + ALL_FRAMES) + miHwDecode(p, q); + + else if ((p->packet[0] == 0x88) || (p->packet[0] == 0x92)) { + 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]); + } + - DPRINT_IVID(DBG_INFO, q->iv->id); - DBGPRINT(F(" has ")); - if(!accepted) DBGPRINT(F("not ")); - DBGPRINT(F("accepted power limit set point ")); - DBGPRINT(String(q->iv->powerLimit[0])); - DBGPRINT(F(" with PowerLimitControl ")); - DBGPRINTLN(String(q->iv->powerLimit[1])); - q->iv->actPowerLimit = 0xffff; // unknown, readback current value } - inline void parseMiFrame(packet_t *p, const queue_s *q) { - if((p->packet[12] != ActivePowerContr) || (p->packet[13] != 0x00)) - return; - bool accepted = true; - if((p->packet[10] == 0x00) && (p->packet[11] == 0x00)) - q->iv->powerLimitAck = true; - else - accepted = false; + inline void miHwDecode(packet_t *p, const queue_s *q) { + record_t<> *rec = q->iv->getRecordStruct(InverterDevInform_All); // choose the record structure + rec->ts = q->ts; + //mPayload[iv->id].gotFragment = true; + uint8_t multi_parts = 0; + +/* +Polling the device software and hardware version number command +start byte Command word routing address target address User data check end byte +byte[0] byte[1] byte[2] byte[3] byte[4] byte[5] byte[6] byte[7] byte[8] byte[9] byte[10] byte[11] byte[12] +0x7e 0x0f xx xx xx xx YY YY YY YY 0x00 CRC 0x7f +Command Receipt - First Frame +start byte Command word target address routing address Multi-frame marking User data User data User data User data User data User data User data User data User data User data User data User data User data User data User data User data check end byte +byte[0] byte[1] byte[2] byte[3] byte[4] byte[5] byte[6] byte[7] byte[8] byte[9] byte[10] byte[11] byte[12] byte[13] byte[14] byte[15] byte[16] byte[17] byte[18] byte[19] byte[20] byte[21] byte[22] byte[23] byte[24] byte[25] byte[26] byte[27] byte[28] +0x7e 0x8f YY YY YY YY xx xx xx xx 0x00 USFWBuild_VER APPFWBuild_VER APPFWBuild_YYYY APPFWBuild_MMDD APPFWBuild_HHMM APPFW_PN HW_VER CRC 0x7f +Command Receipt - Second Frame +start byte Command word target address routing address Multi-frame marking User data User data User data User data User data User data User data User data User data User data User data User data User data User data User data User data check end byte +byte[0] byte[1] byte[2] byte[3] byte[4] byte[5] byte[6] byte[7] byte[8] byte[9] byte[10] byte[11] byte[12] byte[13] byte[14] byte[15] byte[16] byte[17] byte[18] byte[19] byte[20] byte[21] byte[22] byte[23] byte[24] byte[25] byte[26] byte[27] byte[28] +0x7e 0x8f YY YY YY YY xx xx xx xx 0x01 HW_PN HW_FB_TLmValue HW_FB_ReSPRT HW_GridSamp_ResValule HW_ECapValue Matching_APPFW_PN CRC 0x7f +Command receipt - third frame +start byte Command word target address routing address Multi-frame marking User data User data User data User data User data User data User data User data check end byte +byte[0] byte[1] byte[2] byte[3] byte[4] byte[5] byte[6] byte[7] byte[8] byte[9] byte[10] byte[11] byte[12] byte[13] byte[14] byte[15] byte[16] byte[15] byte[16] byte[17] byte[18] +0x7e 0x8f YY YY YY YY xx xx xx xx 0x12 APPFW_MINVER HWInfoAddr PNInfoCRC_gusv PNInfoCRC_gusv CRC 0x7f +*/ + +/* +case InverterDevInform_All: + rec->length = (uint8_t)(HMINFO_LIST_LEN); + rec->assign = (byteAssign_t *)InfoAssignment; + rec->pyldLen = HMINFO_PAYLOAD_LEN; + break; + +const byteAssign_t InfoAssignment[] = { +{ FLD_FW_VERSION, UNIT_NONE, CH0, 0, 2, 1 }, +{ FLD_FW_BUILD_YEAR, UNIT_NONE, CH0, 2, 2, 1 }, +{ FLD_FW_BUILD_MONTH_DAY, UNIT_NONE, CH0, 4, 2, 1 }, +{ FLD_FW_BUILD_HOUR_MINUTE, UNIT_NONE, CH0, 6, 2, 1 }, +{ FLD_BOOTLOADER_VER, UNIT_NONE, CH0, 8, 2, 1 } +}; +*/ - DPRINT_IVID(DBG_INFO, q->iv->id); - DBGPRINT(F("has ")); - if(!accepted) DBGPRINT(F("not ")); - DBGPRINT(F("accepted power limit set point ")); - DBGPRINT(String(q->iv->powerLimit[0])); - DBGPRINT(F(" with PowerLimitControl ")); - DBGPRINTLN(String(q->iv->powerLimit[1])); - q->iv->actPowerLimit = 0xffff; // unknown, readback current value + if ( p->packet[9] == 0x00 ) {//first frame + //FLD_FW_VERSION + for (uint8_t i = 0; i < 5; i++) { + q->iv->setValue(i, rec, (float) ((p->packet[(12+2*i)] << 8) + p->packet[(13+2*i)])/1); + } + q->iv->isConnected = true; + //if(mSerialDebug) { + DPRINT_IVID(DBG_INFO, q->iv->id); + DPRINT(DBG_INFO,F("HW_VER is ")); + DBGPRINTLN(String((p->packet[24] << 8) + p->packet[25])); + //} + record_t<> *rec = q->iv->getRecordStruct(InverterDevInform_Simple); // choose the record structure + rec->ts = q->ts; + q->iv->setValue(1, rec, (uint32_t) ((p->packet[24] << 8) + p->packet[25])/1); + //mPayload[iv->id].multi_parts +=4; + multi_parts +=4; + } 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); + if ( p->packet[9] == 0x01 ) { + DBGPRINTLN(F("got 2nd frame (hw info)")); + /* according to xlsx (different start byte -1!) + byte[11] to byte[14] HW_PN + byte[15] byte[16] HW_FB_TLmValue + byte[17] byte[18] HW_FB_ReSPRT + byte[19] byte[20] HW_GridSamp_ResValule + byte[21] byte[22] HW_ECapValue + byte[23] to byte[26] Matching_APPFW_PN*/ + DPRINT(DBG_INFO,F("HW_PartNo ")); + DBGPRINTLN(String((uint32_t) (((p->packet[10] << 8) | p->packet[11]) << 8 | p->packet[12]) << 8 | p->packet[13])); + record_t<> *rec = q->iv->getRecordStruct(InverterDevInform_Simple); // choose the record structure + rec->ts = q->ts; + q->iv->setValue(0, rec, (uint32_t) ((((p->packet[10] << 8) | p->packet[11]) << 8 | p->packet[12]) << 8 | p->packet[13])/1); + + //if(mSerialDebug) { + DPRINT(DBG_INFO,F("HW_FB_TLmValue ")); + DBGPRINTLN(String((p->packet[14] << 8) + p->packet[15])); + DPRINT(DBG_INFO,F("HW_FB_ReSPRT ")); + DBGPRINTLN(String((p->packet[16] << 8) + p->packet[17])); + DPRINT(DBG_INFO,F("HW_GridSamp_ResValule ")); + DBGPRINTLN(String((p->packet[18] << 8) + p->packet[19])); + DPRINT(DBG_INFO,F("HW_ECapValue ")); + DBGPRINTLN(String((p->packet[20] << 8) + p->packet[21])); + DPRINT(DBG_INFO,F("Matching_APPFW_PN ")); + DBGPRINTLN(String((uint32_t) (((p->packet[22] << 8) | p->packet[23]) << 8 | p->packet[24]) << 8 | p->packet[25])); + //} + //notify(InverterDevInform_Simple, iv); + //mPayload[iv->id].multi_parts +=2; + multi_parts +=2; + //notify(InverterDevInform_All, iv); + } else { + DBGPRINTLN(F("3rd gen. inverter!")); + } + + } else if ( p->packet[9] == 0x12 ) {//3rd frame + DPRINT_IVID(DBG_INFO, q->iv->id); + DBGPRINTLN(F("got 3rd frame (hw info)")); + /* according to xlsx (different start byte -1!) + byte[11] byte[12] APPFW_MINVER + byte[13] byte[14] HWInfoAddr + byte[15] byte[16] PNInfoCRC_gusv + byte[15] byte[16] PNInfoCRC_gusv (this really is double mentionned in xlsx...) + */ + //if(mSerialDebug) { + DPRINT(DBG_INFO,F("APPFW_MINVER ")); + DBGPRINTLN(String((p->packet[10] << 8) + p->packet[11])); + DPRINT(DBG_INFO,F("HWInfoAddr ")); + DBGPRINTLN(String((p->packet[12] << 8) + p->packet[13])); + DPRINT(DBG_INFO,F("PNInfoCRC_gusv ")); + DBGPRINTLN(String((p->packet[14] << 8) + p->packet[15])); + //} + //mPayload[iv->id].multi_parts++; + multi_parts++; + } + if(multi_parts > 5) { + cmdDone(q); + mState = States::RESET; + q->iv->radioStatistics.rxSuccess++; + } + + /*if (mPayload[iv->id].multi_parts > 5) { + iv->setQueuedCmdFinished(); + mPayload[iv->id].complete = true; + mPayload[iv->id].rxTmo = true; + mPayload[iv->id].requested= false; + iv->radioStatistics.rxSuccess++; + } + if (mHighPrioIv == NULL) + mHighPrioIv = iv; + */ } inline void miDataDecode(packet_t *p, const queue_s *q) { record_t<> *rec = q->iv->getRecordStruct(RealTimeRunData_Debug); // choose the parser - //rec->ts = mPayload[iv->id].ts; + rec->ts = q->ts; + q->iv->radioStatistics.rxSuccess++; + mState = States::RESET; + //mPayload[iv->id].gotFragment = true; //mPayload[iv->id].multi_parts += 4; @@ -314,19 +422,22 @@ class Communication : public CommQueue<> { CH4; // count in RF_communication_protocol.xlsx is with offset = -1 q->iv->setValue(q->iv->getPosByChFld(datachan, FLD_UDC, rec), rec, (float)((p->packet[9] << 8) + p->packet[10])/10); - yield(); + q->iv->setValue(q->iv->getPosByChFld(datachan, FLD_IDC, rec), rec, (float)((p->packet[11] << 8) + p->packet[12])/10); - yield(); + q->iv->setValue(q->iv->getPosByChFld(0, FLD_UAC, rec), rec, (float)((p->packet[13] << 8) + p->packet[14])/10); - yield(); + q->iv->setValue(q->iv->getPosByChFld(0, FLD_F, rec), rec, (float) ((p->packet[15] << 8) + p->packet[16])/100); q->iv->setValue(q->iv->getPosByChFld(datachan, FLD_PDC, rec), rec, (float)((p->packet[17] << 8) + p->packet[18])/10); - yield(); + q->iv->setValue(q->iv->getPosByChFld(datachan, FLD_YD, rec), rec, (float)((p->packet[19] << 8) + p->packet[20])/1); - yield(); + 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))); - //mPayload[q->iv->id].rssi[(datachan-1)] = p->rssi; + if (datachan == 1) //mPayload[q->iv->id].rssi[(datachan-1)] = p->rssi; + q->iv->rssi = p->rssi; + else if(q->iv->rssi > p->rssi) + q->iv->rssi = p->rssi; /*if ( datachan < 3 ) { mPayload[q->iv->id].dataAB[datachan] = true; @@ -342,19 +453,164 @@ class Communication : public CommQueue<> { FCNT = (uint8_t)(p->packet[26]); FCODE = (uint8_t)(p->packet[27]); }*/ - //miStsConsolidate(iv, datachan, rec, p->packet[23], p->packet[24]); + miStsConsolidate(q, datachan, rec, p->packet[23], p->packet[24]); if (p->packet[0] < (0x39 + ALL_FRAMES) ) { - addImportant(q->iv, (q->cmd + 1)); - //mPayload[iv->id].txCmd++; - //mPayload[iv->id].retransmits = 0; // reserve retransmissions for each response - //mPayload[iv->id].complete = false; + miNextRequest((p->packet[0] - ALL_FRAMES + 1), q); } else { - //miComplete(iv); + miComplete(q->iv); } } else if((p->packet[0] == (MI_REQ_CH1 + ALL_FRAMES)) && q->iv->type == INV_TYPE_2CH ) { - addImportant(q->iv, MI_REQ_CH2); + miNextRequest(MI_REQ_CH2, q); + } else { // first data msg for 1ch, 2nd for 2ch + miComplete(q->iv); + } + cmdDone(q); + } + + inline void miNextRequest(uint8_t cmd, const queue_s *q) { + //setAttempt(); + DPRINT_IVID(DBG_WARN, q->iv->id); + DBGPRINT(F("next request (")); + DBGPRINT(String(q->attempts)); + DBGPRINT(F(" attempts left): 0x")); + DBGHEXLN(cmd); + + if(q->attempts) { + q->iv->radio->sendCmdPacket(q->iv, cmd, 0x00, true); + q->iv->radioStatistics.retransmits++; + mWaitTimeout = millis() + MI_TIMEOUT; + mState = States::WAIT; + } else { + add(q, true); + cmdDone(q); + mState = States::RESET; + } + } + +/* inline void miStsDecode(Inverter<> *iv, packet_t *p, uint8_t stschan = CH1) { + record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); // choose the record structure + rec->ts = mPayload[iv->id].ts; + mPayload[iv->id].gotFragment = true; + mPayload[iv->id].multi_parts += 3; + mPayload[iv->id].txId = p->packet[0]; + miStsConsolidate(iv, stschan, rec, p->packet[10], p->packet[12], p->packet[9], p->packet[11]); + mPayload[iv->id].stsAB[stschan] = true; + if (mPayload[iv->id].stsAB[CH1] && mPayload[iv->id].stsAB[CH2]) + */ + + 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) { + //uint8_t status = (p->packet[11] << 8) + p->packet[12]; + uint16_t statusMi = 3; // regular status for MI, change to 1 later? + if ( uState == 2 ) { + statusMi = 5050 + stschan; //first approach, needs review! + if (lState) + statusMi += lState*10; + } else if ( uState > 3 ) { + statusMi = uState*1000 + uEnum*10; + if (lState) + statusMi += lState*100; //needs review, esp. for 4ch-8310 state! + //if (lEnum) + statusMi += lEnum; + if (uEnum < 6) { + statusMi += stschan; + } + if (statusMi == 8000) + statusMi = 8310; //trick? + } + + uint16_t prntsts = statusMi == 3 ? 1 : statusMi; + bool stsok = true; + if ( prntsts != rec->record[q->iv->getPosByChFld(0, FLD_EVT, rec)] ) { //sth.'s changed? + q->iv->alarmCnt = 1; // minimum... + //sth is or was wrong? + if ( (q->iv->type != INV_TYPE_1CH) && ( (statusMi != 3) + || ((q->iv->lastAlarm[stschan].code) && (statusMi == 3) && (q->iv->lastAlarm[stschan].code != 1))) + ) { + q->iv->lastAlarm[stschan] = alarm_t(prntsts, q->ts,0); + q->iv->alarmCnt = q->iv->type == INV_TYPE_2CH ? 3 : 5; + } + + q->iv->alarmLastId = prntsts; //iv->alarmMesIndex; + + stsok = false; + if (q->iv->alarmCnt > 1) { //more than one channel + for (uint8_t ch = 0; ch < (q->iv->alarmCnt); ++ch) { //start with 1 + if (q->iv->lastAlarm[ch].code == 1) { + stsok = true; + break; + } + } + } + //if (mSerialDebug) { + DPRINT(DBG_WARN, F("New state on CH")); + DBGPRINT(String(stschan)); DBGPRINT(F(" (")); + DBGPRINT(String(prntsts)); DBGPRINT(F("): ")); + DBGPRINTLN(q->iv->getAlarmStr(prntsts)); + //} + } + + if (!stsok) { + q->iv->setValue(q->iv->getPosByChFld(0, FLD_EVT, rec), rec, prntsts); + q->iv->lastAlarm[0] = alarm_t(prntsts, q->ts, 0); + } + + if (q->iv->alarmMesIndex < rec->record[q->iv->getPosByChFld(0, FLD_EVT, rec)]) { + q->iv->alarmMesIndex = rec->record[q->iv->getPosByChFld(0, FLD_EVT, rec)]; // seems there's no status per channel in 3rd gen. models?!? + //if (mSerialDebug) { + DPRINT_IVID(DBG_INFO, q->iv->id); + DBGPRINT(F("alarm ID incremented to ")); + DBGPRINTLN(String(q->iv->alarmMesIndex)); + //} + } + } + + + inline void miComplete(Inverter<> *iv) { + //if ( mPayload[iv->id].complete ) + // return; //if we got second message as well in repreated attempt + //mPayload[iv->id].complete = true; + //if (mSerialDebug) { + DPRINT_IVID(DBG_INFO, iv->id); + DBGPRINTLN(F("got all data msgs")); + //} + record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); + iv->setValue(iv->getPosByChFld(0, FLD_YD, rec), rec, calcYieldDayCh0(iv,0)); + + //preliminary AC calculation... + float ac_pow = 0; + for(uint8_t i = 1; i <= iv->channels; i++) { + if ((!iv->lastAlarm[i].code) || (iv->lastAlarm[i].code == 1)) { + uint8_t pos = iv->getPosByChFld(i, FLD_PDC, rec); + ac_pow += iv->getValue(pos, rec); + } } + ac_pow = (int) (ac_pow*9.5); + iv->setValue(iv->getPosByChFld(0, FLD_PAC, rec), rec, (float) ac_pow/10); + + iv->doCalculations(); + // update status state-machine, + iv->isProducing(); + } + + + inline void parseDevCtrl(packet_t *p, const queue_s *q) { + if((p->packet[12] != ActivePowerContr) || (p->packet[13] != 0x00)) + return; + bool accepted = true; + if((p->packet[10] == 0x00) && (p->packet[11] == 0x00)) + q->iv->powerLimitAck = true; + else + accepted = false; + + DPRINT_IVID(DBG_INFO, q->iv->id); + DBGPRINT(F("has ")); + if(!accepted) DBGPRINT(F("not ")); + DBGPRINT(F("accepted power limit set point ")); + DBGPRINT(String(q->iv->powerLimit[0])); + DBGPRINT(F(" with PowerLimitControl ")); + DBGPRINTLN(String(q->iv->powerLimit[1])); + q->iv->actPowerLimit = 0xffff; // unknown, readback current value } inline void compilePayload(const queue_s *q) { @@ -396,7 +652,7 @@ class Communication : public CommQueue<> { } memcpy(&mPayload[len], mLocalBuf[i].buf, mLocalBuf[i].len); len += mLocalBuf[i].len; - // get worst RSSI + // get best RSSI if(mLocalBuf[i].rssi > rssi) rssi = mLocalBuf[i].rssi; } diff --git a/src/hm/hmInverter.h b/src/hm/hmInverter.h index e90acac3..bbf9167a 100644 --- a/src/hm/hmInverter.h +++ b/src/hm/hmInverter.h @@ -182,7 +182,7 @@ class Inverter { } void tickSend(std::function cb) { - if (ivGen != IV_MI) { + if (IV_MI != ivGen) { if(mDevControlRequest) { cb(devControlCmd, true); mDevControlRequest = false; @@ -197,17 +197,23 @@ class Inverter { else cb(RealTimeRunData_Debug, false); // get live data } else { + /*if (cmd == 0x01) { //0x1 for HM-types + cmd2 = 0x00; + cmd = 0x0f; // for MI, these seem to make part of polling the device software and hardware version number command + } + if (cmd == SystemConfigPara ) { // 0x05 for HM-types + cmd2 = 0x00; + cmd = 0x10; // legacy GPF request + */ if(mDevControlRequest) { cb(devControlCmd, true); mDevControlRequest = false; - } - else if(0 == getFwVersion()) - cb(MI_REQ_CH1, false); // get firmware version - //cb(InverterDevInform_All, false); // get firmware version + } else if(0 == getFwVersion()) + 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(InverterDevInform_All, false); // hard- and firmware version for missing HW part nr, delivered by frame 1 + cb(0x0f, false); // hard- and firmware version for missing HW part nr, delivered by frame 1 else cb(type == INV_TYPE_4CH ? MI_REQ_4CH : MI_REQ_CH1, false); } @@ -634,9 +640,9 @@ class Inverter { uint32_t startTimeOffset = 0, endTimeOffset = 0; uint32_t start, endTime; - if (((wCode >> 13) & 0x01) == 1) // check if is AM or PM - startTimeOffset = 12 * 60 * 60; - if (((wCode >> 12) & 0x01) == 1) // check if is AM or PM + // check if is AM or PM + startTimeOffset = ((wCode >> 13) & 0x01) * 12 * 60 * 60; + if (((wCode >> 12) & 0x03) != 0) endTimeOffset = 12 * 60 * 60; start = (((uint16_t)pyld[startOff + 4] << 8) | ((uint16_t)pyld[startOff + 5])) + startTimeOffset; diff --git a/src/hm/hmRadio.h b/src/hm/hmRadio.h index e8a95990..3d74222d 100644 --- a/src/hm/hmRadio.h +++ b/src/hm/hmRadio.h @@ -117,24 +117,24 @@ class HmRadio : public Radio { mNrf24.setChannel(mRfChLst[mRxChIdx]); mNrf24.startListening(); - uint32_t startMicros = micros(); + uint32_t startMicros = micros() + 5110; uint32_t loopMillis = millis() + 400; while (millis() < loopMillis) { - while (micros()-startMicros < 5110) { // listen (4088us or?) 5110us to each channel + while (micros() < startMicros) { // listen (4088us or?) 5110us to each channel if (mIrqRcvd) { mIrqRcvd = false; - if (getReceived()) { // everything received + + if (getReceived()) // everything received return; - } + } yield(); } // switch to next RX channel - startMicros = micros(); if(++mRxChIdx >= RF_CHANNELS) mRxChIdx = 0; mNrf24.setChannel(mRfChLst[mRxChIdx]); - yield(); + startMicros = micros() + 5000; } // not finished but time is over return; @@ -145,13 +145,13 @@ class HmRadio : public Radio { return mNrf24.isChipConnected(); } - void sendControlPacket(Inverter<> *iv, uint8_t cmd, uint16_t *data, bool isRetransmit, bool isNoMI = true, uint16_t powerMax = 0) { - DPRINT_IVID(DBG_INFO,iv->id); + void sendControlPacket(Inverter<> *iv, uint8_t cmd, uint16_t *data, bool isRetransmit) { + DPRINT_IVID(DBG_INFO, iv->id); DBGPRINT(F("sendControlPacket cmd: 0x")); DBGHEXLN(cmd); initPacket(iv->radioId.u64, TX_REQ_DEVCONTROL, SINGLE_FRAME); uint8_t cnt = 10; - if (isNoMI) { + if (IV_MI != iv->ivGen) { mTxBuf[cnt++] = cmd; // cmd -> 0 on, 1 off, 2 restart, 11 active power, 12 reactive power, 13 power factor mTxBuf[cnt++] = 0x00; if(cmd >= ActivePowerContr && cmd <= PFSet) { // ActivePowerContr, ReactivePowerContr, PFSet @@ -161,6 +161,7 @@ class HmRadio : public Radio { mTxBuf[cnt++] = ((data[1] ) ) & 0xff; // setting for persistens handling } } else { //MI 2nd gen. specific + uint16_t powerMax = ((iv->powerLimit[1] == RelativNonPersistent) ? 0 : iv->getMaxPower()); switch (cmd) { case Restart: case TurnOn: @@ -226,7 +227,7 @@ class HmRadio : public Radio { } cnt++; } - sendPacket(iv, cnt, isRetransmit, isNoMI); + sendPacket(iv, cnt, isRetransmit, (IV_MI != iv->ivGen)); } uint8_t getDataRate(void) { @@ -298,6 +299,10 @@ class HmRadio : public Radio { return iv->radioId.u64; } + uint8_t getIvGen(Inverter<> *iv) { + return iv->ivGen; + } + uint64_t DTU_RADIO_ID; uint8_t mRfChLst[RF_CHANNELS]; uint8_t mTxChIdx; diff --git a/src/hm/miPayload.h b/src/hm/miPayload.h index 892dc0a8..94f7a9b3 100644 --- a/src/hm/miPayload.h +++ b/src/hm/miPayload.h @@ -36,7 +36,7 @@ typedef struct { } miPayload_t; -//typedef std::function *)> miPayloadListenerType; +typedef std::function *)> miPayloadListenerType; template diff --git a/src/hm/radio.h b/src/hm/radio.h index 46c05d99..75db5436 100644 --- a/src/hm/radio.h +++ b/src/hm/radio.h @@ -21,7 +21,7 @@ class Inverter; // abstract radio interface class Radio { public: - virtual void sendControlPacket(Inverter<> *iv, uint8_t cmd, uint16_t *data, bool isRetransmit, bool isNoMI = true, uint16_t powerMax = 0) = 0; + virtual void sendControlPacket(Inverter<> *iv, uint8_t cmd, uint16_t *data, bool isRetransmit) = 0; virtual bool switchFrequency(Inverter<> *iv, uint32_t fromkHz, uint32_t tokHz) { return true; } virtual void loop(void) {}; @@ -42,7 +42,15 @@ class Radio { sendPacket(iv, 10, isRetransmit, appendCrc16); } - void prepareDevInformCmd(Inverter<> *iv, uint8_t cmd, uint32_t ts, uint16_t alarmMesId, bool isRetransmit, uint8_t reqfld=TX_REQ_INFO) { + void prepareDevInformCmd(Inverter<> *iv, uint8_t cmd, uint32_t ts, uint16_t alarmMesId, bool isRetransmit, uint8_t reqfld=TX_REQ_INFO) { // might not be necessary to add additional arg. + if(IV_MI == getIvGen(iv)) { + DPRINT_IVID(DBG_INFO, getIvId(iv)); + DBGPRINT(F("legacy cmd 0x")); + DBGHEXLN(cmd); + sendCmdPacket(iv, cmd, cmd, false, false); + return; + } + if(mSerialDebug) { DPRINT(DBG_DEBUG, F("prepareDevInformCmd 0x")); DPRINTLN(DBG_DEBUG,String(cmd, HEX)); @@ -63,6 +71,7 @@ class Radio { protected: virtual void sendPacket(Inverter<> *iv, uint8_t len, bool isRetransmit, bool appendCrc16=true) = 0; virtual uint64_t getIvId(Inverter<> *iv) = 0; + virtual uint8_t getIvGen(Inverter<> *iv) = 0; void initPacket(uint64_t ivId, uint8_t mid, uint8_t pid) { mTxBuf[0] = mid;