diff --git a/src/CHANGES.md b/src/CHANGES.md index 75ab7826..441d0a29 100644 --- a/src/CHANGES.md +++ b/src/CHANGES.md @@ -5,6 +5,8 @@ ## 0.5.84 * fix blue LED lights up all the time #672 * added an instant start communication (once NTP is synced) +* add MI 3rd generation inverters (10x2 serial numbers) +* first decodings of messages from MI 2nd generation inverters ## 0.5.83 * fix MQTT publishing, `callback` was set but reset by following `setup()` diff --git a/src/hm/miPayload.h b/src/hm/miPayload.h index 592f5552..9d294498 100644 --- a/src/hm/miPayload.h +++ b/src/hm/miPayload.h @@ -6,6 +6,7 @@ #ifndef __MI_PAYLOAD_H__ #define __MI_PAYLOAD_H__ +//#include "hmInverter.h" #include "../utils/dbg.h" #include "../utils/crc.h" #include "../config/config.h" @@ -21,12 +22,12 @@ typedef struct { bool stsb; uint8_t txId; uint8_t invId; + uint8_t retransmits; /* uint8_t data[MAX_PAYLOAD_ENTRIES][MAX_RF_PAYLOAD_SIZE]; uint8_t maxPackId; bool lastFound; - uint8_t retransmits; bool gotFragment;*/ } miPayload_t; @@ -49,6 +50,7 @@ class MiPayload { reset(i); } mSerialDebug = false; + mHighPrioIv = NULL; mCbMiPayload = NULL; } @@ -60,8 +62,19 @@ class MiPayload { mCbMiPayload = cb; } - void loop() {} + void addAlarmListener(alarmListenerType cb) { + mCbMiAlarm = cb; + } + void loop() { + /*if(NULL != mHighPrioIv) { + iv->ivSend(mHighPrioIv, true); // should request firmware version etc.? + mHighPrioIv = NULL; + }*/ + } + void ivSendHighPrio(Inverter<> *iv) { + mHighPrioIv = iv; + } void ivSend(Inverter<> *iv) { reset(iv->id); @@ -80,88 +93,46 @@ class MiPayload { void add(Inverter<> *iv, packet_t *p) { DPRINTLN(DBG_INFO, F("MI got data [0]=") + String(p->packet[0], HEX)); - if (p->packet[0] == (0x08 + ALL_FRAMES)) { // MI status response to 0x09 + if (p->packet[0] == (0x08 + ALL_FRAMES)) { // 0x88; MI status response to 0x09 mPayload[iv->id].stsa = true; - /*decode here or memcopy payload for later decoding? - for decoding see - void MI600StsMsg (NRF24_packet_t *p){ - STAT = (int)((p->packet[11] << 8) + p->packet[12]); - FCNT = (int)((p->packet[13] << 8) + p->packet[14]); - FCODE = (int)((p->packet[15] << 8) + p->packet[16]); - #ifdef ESP8266 - VALUES[PV][5]=STAT; - VALUES[PV][6]=FCNT; - VALUES[PV][7]=FCODE; - #endif - } - */ - DPRINTLN(DBG_INFO, F("Inverter ") + String(iv->id) + F(": status msg ") + p->packet[0]); - } else if (p->packet[0] == (0x12 + ALL_FRAMES)) { // MI status response to 0x11 + miStsDecode(iv, p); + } else if (p->packet[0] == (0x11 + SINGLE_FRAME)) { // 0x92; MI status response to 0x11 mPayload[iv->id].stsb = true; - DPRINTLN(DBG_INFO, F("Inverter ") + String(iv->id) + F(": status msg ") + p->packet[0]); + miStsDecode(iv, p, 2); } else if (p->packet[0] == (0x09 + ALL_FRAMES)) { // MI data response to 0x09 mPayload[iv->id].txId = p->packet[0]; + miDataDecode(iv,p); + iv->setQueuedCmdFinished(); if (INV_TYPE_2CH == iv->type) { - mSys->Radio.prepareDevInformCmd(iv->radioId.u64, iv->getQueuedCmd(), mPayload[iv->id].ts, iv->alarmMesIndex, false, 0x11); + //mSys->Radio.prepareDevInformCmd(iv->radioId.u64, iv->getQueuedCmd(), mPayload[iv->id].ts, iv->alarmMesIndex, false, 0x11); + mSys->Radio.prepareDevInformCmd(iv->radioId.u64, 0x11, mPayload[iv->id].ts, iv->alarmMesIndex, false, 0x11); } else { // additional check for mPayload[iv->id].stsa == true might be a good idea (request retransmit?) mPayload[iv->id].complete = true; + //iv->setQueuedCmdFinished(); } - /*decode here or memcopy payload for later decoding? - void MI600DataMsg(NRF24_packet_t *p){ - U_DC = (float) ((p->packet[11] << 8) + p->packet[12])/10; - I_DC = (float) ((p->packet[13] << 8) + p->packet[14])/10; - U_AC = (float) ((p->packet[15] << 8) + p->packet[16])/10; - F_AC = (float) ((p->packet[17] << 8) + p->packet[18])/100; - P_DC = (float)((p->packet[19] << 8) + p->packet[20])/10; - Q_DC = (float)((p->packet[21] << 8) + p->packet[22])/1; - TEMP = (float) ((p->packet[23] << 8) + p->packet[24])/10; - - if ((30packet[2] == 0x89) {PV= 0; TotalP[1]=P_DC; pvCnt[0]=1;}//port 1 - if (p->packet[2] == 0x91) {PV= 1; TotalP[2]=P_DC; pvCnt[1]=1;}//port 2 - - TotalP[0]=TotalP[1]+TotalP[2]+TotalP[3]+TotalP[4];//in TotalP[0] is the totalPV power - if((P_DC>400) || (P_DC<0) || (TotalP[0]>MAXPOWER)){// cant be!! - TotalP[0]=0; - return; - } - #ifdef ESP8266 - VALUES[PV][0]=PV; - VALUES[PV][1]=P_DC; - VALUES[PV][2]=U_DC; - VALUES[PV][3]=I_DC; - VALUES[PV][4]=Q_DC; - #endif - PMI=TotalP[0]; - LIM=(uint16_t)Limit; - PrintOutValues(); - }*/ - DPRINTLN(DBG_INFO, F("Inverter ") + String(iv->id) + F(": data msg ") + p->packet[0]); - } else if (p->packet[0] == (0x11 + ALL_FRAMES)) { // MI data response to 0x11 mPayload[iv->id].txId = p->packet[0]; mPayload[iv->id].complete = true; - //decode here or memcopy payload for later decoding? - DPRINTLN(DBG_INFO, F("Inverter ") + String(iv->id) + F(": data msg ") + p->packet[0]); - + miDataDecode(iv,p); + iv->setQueuedCmdFinished(); - } else if (p->packet[0] >= (0x36 + ALL_FRAMES) && p->packet[0] <= (0x39 + ALL_FRAMES)) { // MI 1500 data response to 0x11 + } else if (p->packet[0] >= (0x36 + ALL_FRAMES) && p->packet[0] < (0x39 + SINGLE_FRAME)) { // MI 1500 data response to 0x36, 0x37, 0x38 and 0x39 mPayload[iv->id].txId = p->packet[0]; + miDataDecode(iv,p); + iv->setQueuedCmdFinished(); if (p->packet[0] < (0x39 + ALL_FRAMES)) { - mSys->Radio.prepareDevInformCmd(iv->radioId.u64, iv->getQueuedCmd(), mPayload[iv->id].ts, iv->alarmMesIndex, false, p->packet[0] + 1 - ALL_FRAMES); + //mSys->Radio.prepareDevInformCmd(iv->radioId.u64, iv->getQueuedCmd(), mPayload[iv->id].ts, iv->alarmMesIndex, false, p->packet[0] + 1 - ALL_FRAMES); + mSys->Radio.prepareDevInformCmd(iv->radioId.u64, p->packet[0] + 1 - ALL_FRAMES, mPayload[iv->id].ts, iv->alarmMesIndex, false, p->packet[0] + 1 - ALL_FRAMES); } else { mPayload[iv->id].complete = true; + //iv->setValue(iv->getPosByChFld(0, FLD_YD, rec), rec, CALC_YD_CH0); + //iv->setQueuedCmdFinished(); } - //decode here or memcopy payload for later decoding? - DPRINTLN(DBG_INFO, F("Inverter MI1500 ") + String(iv->id) + F(": data msg ") + p->packet[0]); - } + /*} - /*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 mPayload[iv->id].txId = p->packet[0]; DPRINTLN(DBG_DEBUG, F("Response from info request received")); uint8_t *pid = &p->packet[9]; @@ -184,6 +155,7 @@ class MiPayload { } } } + } */ } else if (p->packet[0] == (TX_REQ_DEVCONTROL + ALL_FRAMES)) { // response from dev control command DPRINTLN(DBG_DEBUG, F("Response from devcontrol request received")); @@ -201,7 +173,64 @@ class MiPayload { iv->enqueCommand(SystemConfigPara); // read back power limit } iv->devControlCmd = Init; - }*/ + } else { // some other response; copied from hmPayload:process; might not be correct to do that here!!! + DPRINTLN(DBG_INFO, F("procPyld: cmd: 0x") + String(mPayload[iv->id].txCmd, HEX)); + DPRINTLN(DBG_INFO, F("procPyld: txid: 0x") + String(mPayload[iv->id].txId, HEX)); + //DPRINTLN(DBG_DEBUG, F("procPyld: max: ") + String(mPayload[iv->id].maxPackId)); + record_t<> *rec = iv->getRecordStruct(mPayload[iv->id].txCmd); // choose the parser + mPayload[iv->id].complete = true; + + uint8_t payload[128]; + uint8_t payloadLen = 0; + + memset(payload, 0, 128); + + /*for (uint8_t i = 0; i < (mPayload[iv->id].maxPackId); i++) { + memcpy(&payload[payloadLen], mPayload[iv->id].data[i], (mPayload[iv->id].len[i])); + payloadLen += (mPayload[iv->id].len[i]); + yield(); + }*/ + payloadLen -= 2; + + if (mSerialDebug) { + DPRINT(DBG_INFO, F("Payload (") + String(payloadLen) + "): "); + mSys->Radio.dumpBuf(payload, payloadLen); + } + + if (NULL == rec) { + DPRINTLN(DBG_ERROR, F("record is NULL!")); + } else if ((rec->pyldLen == payloadLen) || (0 == rec->pyldLen)) { + if (mPayload[iv->id].txId == (TX_REQ_INFO + ALL_FRAMES)) + mStat->rxSuccess++; + + rec->ts = mPayload[iv->id].ts; + for (uint8_t i = 0; i < rec->length; i++) { + iv->addValue(i, payload, rec); + yield(); + } + iv->doCalculations(); + notify(mPayload[iv->id].txCmd); + + if(AlarmData == mPayload[iv->id].txCmd) { + uint8_t i = 0; + uint16_t code; + uint32_t start, end; + while(1) { + code = iv->parseAlarmLog(i++, payload, payloadLen, &start, &end); + if(0 == code) + break; + if (NULL != mCbMiAlarm) + (mCbMiAlarm)(code, start, end); + yield(); + } + } + } else { + DPRINTLN(DBG_ERROR, F("plausibility check failed, expected ") + String(rec->pyldLen) + F(" bytes")); + mStat->rxFail++; + } + + iv->setQueuedCmdFinished(); + } } void process(bool retransmit) { @@ -217,7 +246,7 @@ class MiPayload { // no processing needed if txId is not 0x95 mPayload[iv->id].complete = true; continue; // skip to next inverter - } + }*/ if (!mPayload[iv->id].complete) { bool crcPass, pyldComplete; @@ -234,7 +263,9 @@ class MiPayload { } else { if (mPayload[iv->id].retransmits < mMaxRetrans) { mPayload[iv->id].retransmits++; - if(false == mPayload[iv->id].gotFragment) { + //mSys->Radio.prepareDevInformCmd(iv->radioId.u64, iv->getQueuedCmd(), mPayload[iv->id].ts, iv->alarmMesIndex, false, 0x11); + mSys->Radio.sendCmdPacket(iv->radioId.u64, iv->getQueuedCmd(), 24, true); + /*if(false == mPayload[iv->id].gotFragment) { DPRINTLN(DBG_WARN, F("(#") + String(iv->id) + F(") nothing received")); mPayload[iv->id].retransmits = mMaxRetrans; } else { @@ -246,7 +277,7 @@ class MiPayload { } yield(); } - } + }*/ } } } @@ -258,7 +289,7 @@ class MiPayload { DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") prepareDevInformCmd 0x") + String(mPayload[iv->id].txCmd, HEX)); mSys->Radio.prepareDevInformCmd(iv->radioId.u64, mPayload[iv->id].txCmd, mPayload[iv->id].ts, iv->alarmMesIndex, true); } - } else { // payload complete + } /*else { // payload complete DPRINTLN(DBG_INFO, F("procPyld: cmd: 0x") + String(mPayload[iv->id].txCmd, HEX)); DPRINTLN(DBG_INFO, F("procPyld: txid: 0x") + String(mPayload[iv->id].txId, HEX)); DPRINTLN(DBG_DEBUG, F("procPyld: max: ") + String(mPayload[iv->id].maxPackId)); @@ -315,8 +346,8 @@ class MiPayload { } iv->setQueuedCmdFinished(); - } - }*/ + }*/ + } yield(); } } @@ -327,6 +358,152 @@ class MiPayload { (mCbMiPayload)(val); } + void miStsDecode(Inverter<> *iv, packet_t *p, uint8_t chan = 1) { + record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); // choose the record structure + rec->ts = mPayload[iv->id].ts; + + int8_t offset = -2; + + //iv->setValue(iv->getPosByChFld(chan, FLD_YD, rec), rec, (int)((p->packet[11+6] << 8) + p->packet[12+6])); // was 11/12, might be wrong! + + //if (INV_TYPE_1CH == iv->type) + //iv->setValue(iv->getPosByChFld(0, FLD_YD, rec), rec, (int)((p->packet[11+6] << 8) + p->packet[12+6])); + + //iv->setValue(iv->getPosByChFld(chan, FLD_EVT, rec), rec, (int)((p->packet[13] << 8) + p->packet[14])); + + iv->setValue(iv->getPosByChFld(0, FLD_EVT, rec), rec, (int)((p->packet[11+offset] << 8) + p->packet[12+offset])); + if (iv->alarmMesIndex < rec->record[iv->getPosByChFld(0, FLD_EVT, rec)]){ + iv->alarmMesIndex = rec->record[iv->getPosByChFld(0, FLD_EVT, rec)]; + + DPRINTLN(DBG_INFO, "alarm ID incremented to " + String(iv->alarmMesIndex)); + iv->enqueCommand(AlarmData); + } + + + /* for decoding see + void MI600StsMsg (NRF24_packet_t *p){ + STAT = (int)((p->packet[11] << 8) + p->packet[12]); + FCNT = (int)((p->packet[13] << 8) + p->packet[14]); + FCODE = (int)((p->packet[15] << 8) + p->packet[16]); + #ifdef ESP8266 + VALUES[PV][5]=STAT; + VALUES[PV][6]=FCNT; + VALUES[PV][7]=FCODE; + #endif + } + */ + DPRINTLN(DBG_INFO, F("Inverter ") + String(iv->id) + F(": status msg ") + p->packet[0]); + } + + void miDataDecode(Inverter<> *iv, packet_t *p) { + record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); // choose the parser + rec->ts = mPayload[iv->id].ts; + + uint8_t chan = ( p->packet[2] == 0x89 || p->packet[2] == (0x36 + ALL_FRAMES) ) ? 1 : + ( p->packet[2] == 0x91 || p->packet[2] == (0x37 + ALL_FRAMES) ) ? 2 : + p->packet[2] == (0x38 + ALL_FRAMES) ? 3 : + 4; + int8_t offset = -2; + // U_DC = (float) ((p->packet[11] << 8) + p->packet[12])/10; + iv->setValue(iv->getPosByChFld(chan, FLD_UDC, rec), rec, (float)((p->packet[11+offset] << 8) + p->packet[12+offset])/10); + yield(); + // I_DC = (float) ((p->packet[13] << 8) + p->packet[14])/10; + iv->setValue(iv->getPosByChFld(chan, FLD_IDC, rec), rec, (float)((p->packet[13+offset] << 8) + p->packet[14+offset])/10); + yield(); + // U_AC = (float) ((p->packet[15] << 8) + p->packet[16])/10; + iv->setValue(iv->getPosByChFld(0, FLD_UAC, rec), rec, (float)((p->packet[15+offset] << 8) + p->packet[16+offset])/10); + yield(); + // F_AC = (float) ((p->packet[17] << 8) + p->packet[18])/100; + //iv->setValue(iv->getPosByChFld(0, FLD_IAC, rec), rec, (float)((p->packet[17+offset] << 8) + p->packet[18+offset])/100); + //yield(); + // P_DC = (float)((p->packet[19] << 8) + p->packet[20])/10; + iv->setValue(iv->getPosByChFld(chan, FLD_PDC, rec), rec, (float)((p->packet[19+offset] << 8) + p->packet[20+offset])/10); + yield(); + // Q_DC = (float)((p->packet[21] << 8) + p->packet[22])/1; + iv->setValue(iv->getPosByChFld(chan, FLD_Q, rec), rec, (float)((p->packet[21+offset] << 8) + p->packet[22+offset])/1); + yield(); + iv->setValue(iv->getPosByChFld(0, FLD_T, rec), rec, (float) ((int16_t)(p->packet[23+offset] << 8) + p->packet[24+offset])/10); + iv->setValue(iv->getPosByChFld(0, FLD_F, rec), rec, (float) ((p->packet[17+offset] << 8) + p->packet[18+offset])/100); //23 is freq or IAC? + yield(); + //FLD_YD + + if (p->packet[2] >= (0x36 + ALL_FRAMES) ) { + /*STAT = (uint8_t)(p->packet[25] ); + FCNT = (uint8_t)(p->packet[26]); + FCODE = (uint8_t)(p->packet[27]); // MI300: (int)((p->packet[15] << 8) + p->packet[16]); */ + //iv->setValue(iv->getPosByChFld(chan, FLD_YD, rec), rec, (uint8_t)(p->packet[25])); + //iv->setValue(iv->getPosByChFld(chan, FLD_EVT, rec), rec, (uint8_t)(p->packet[27])); + iv->setValue(iv->getPosByChFld(0, FLD_EVT, rec), rec, (uint8_t)(p->packet[21+offset])); + yield(); + if (iv->alarmMesIndex < rec->record[iv->getPosByChFld(0, FLD_EVT, rec)]){ + iv->alarmMesIndex = rec->record[iv->getPosByChFld(0, FLD_EVT, rec)]; + + DPRINTLN(DBG_INFO, "alarm ID incremented to " + String(iv->alarmMesIndex)); + iv->enqueCommand(AlarmData); + } + } + iv->setValue(iv->getPosByChFld(0, FLD_YD, rec), rec, CALC_YD_CH0); // (getValue(iv->getPosByChFld(1, FLD_YD, rec), rec) + getValue(iv->getPosByChFld(2, FLD_YD, rec), rec))); + + iv->doCalculations(); + notify(mPayload[iv->id].txCmd); +/* + if(AlarmData == mPayload[iv->id].txCmd) { + uint8_t i = 0; + uint16_t code; + uint32_t start, end; + while(1) { + code = iv->parseAlarmLog(i++, payload, payloadLen, &start, &end); + if(0 == code) + break; + if (NULL != mCbMiAlarm) + (mCbAlarm)(code, start, end); + yield(); + } + }*/ +/*decode here or memcopy payload for later decoding? + void MI600DataMsg(NRF24_packet_t *p){ + U_DC = (float) ((p->packet[11] << 8) + p->packet[12])/10; + I_DC = (float) ((p->packet[13] << 8) + p->packet[14])/10; + U_AC = (float) ((p->packet[15] << 8) + p->packet[16])/10; + F_AC = (float) ((p->packet[17] << 8) + p->packet[18])/100; + P_DC = (float)((p->packet[19] << 8) + p->packet[20])/10; + Q_DC = (float)((p->packet[21] << 8) + p->packet[22])/1; + TEMP = (float) ((p->packet[23] << 8) + p->packet[24])/10; //(int16_t) + + if ((30packet[2] == 0x89) {PV= 0; TotalP[1]=P_DC; pvCnt[0]=1;}//port 1 + if (p->packet[2] == 0x91) {PV= 1; TotalP[2]=P_DC; pvCnt[1]=1;}//port 2 + + TotalP[0]=TotalP[1]+TotalP[2]+TotalP[3]+TotalP[4];//in TotalP[0] is the totalPV power + if((P_DC>400) || (P_DC<0) || (TotalP[0]>MAXPOWER)){// cant be!! + TotalP[0]=0; + return; + } + #ifdef ESP8266 + VALUES[PV][0]=PV; + VALUES[PV][1]=P_DC; + VALUES[PV][2]=U_DC; + VALUES[PV][3]=I_DC; + VALUES[PV][4]=Q_DC; + #endif + PMI=TotalP[0]; + LIM=(uint16_t)Limit; + PrintOutValues(); + }*/ + + /*For MI1500: + if (MI1500) { + STAT = (uint8_t)(p->packet[25] ); + FCNT = (uint8_t)(p->packet[26]); + FCODE = (uint8_t)(p->packet[27]); + } + */ + DPRINTLN(DBG_INFO, F("Inverter ") + String(iv->id) + F(": data msg ") + p->packet[0]); + } + bool build(uint8_t id, bool *complete) { /*DPRINTLN(DBG_VERBOSE, F("build")); uint16_t crc = 0xffff, crcRcv = 0x0000; @@ -362,9 +539,9 @@ class MiPayload { memset(mPayload[id].len, 0, MAX_PAYLOAD_ENTRIES); /* mPayload[id].gotFragment = false; - mPayload[id].retransmits = 0; mPayload[id].maxPackId = MAX_PAYLOAD_ENTRIES; mPayload[id].lastFound = false;*/ + mPayload[id].retransmits = 0; mPayload[id].complete = false; mPayload[id].txCmd = 0; mPayload[id].requested = false; @@ -381,6 +558,8 @@ class MiPayload { miPayload_t mPayload[MAX_NUM_INVERTERS]; bool mSerialDebug; + Inverter<> *mHighPrioIv; + alarmListenerType mCbMiAlarm; payloadListenerType mCbMiPayload; };