Browse Source

updated to latest changes from @rejoe2 for MI inverters

pull/1219/head
lumapu 12 months ago
parent
commit
70b2ae6246
  1. 2
      src/hm/CommQueue.h
  2. 315
      src/hm/Communication.h
  3. 7
      src/hm/hmInverter.h

2
src/hm/CommQueue.h

@ -30,9 +30,9 @@ class CommQueue {
Inverter<> *iv; Inverter<> *iv;
uint8_t cmd; uint8_t cmd;
uint8_t attempts; uint8_t attempts;
uint32_t ts;
bool delOnPop; bool delOnPop;
bool isDevControl; bool isDevControl;
uint32_t ts;
queue_s() {} queue_s() {}
queue_s(Inverter<> *i, uint8_t c, bool d, bool dev) : queue_s(Inverter<> *i, uint8_t c, bool d, bool dev) :
iv(i), cmd(c), attempts(5), ts(0), delOnPop(d), isDevControl(dev) {} iv(i), cmd(c), attempts(5), ts(0), delOnPop(d), isDevControl(dev) {}

315
src/hm/Communication.h

@ -10,6 +10,9 @@
#include <Arduino.h> #include <Arduino.h>
#include "../utils/crc.h" #include "../utils/crc.h"
#define MI_TIMEOUT 500
#define DEFAULT_TIMEOUT 500
typedef std::function<void(uint8_t, Inverter<> *)> payloadListenerType; typedef std::function<void(uint8_t, Inverter<> *)> payloadListenerType;
typedef std::function<void(Inverter<> *)> alarmListenerType; typedef std::function<void(Inverter<> *)> alarmListenerType;
@ -37,6 +40,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;
switch(mState) { switch(mState) {
case States::RESET: case States::RESET:
if(millis() < mWaitTimeout) if(millis() < mWaitTimeout)
@ -57,7 +62,7 @@ class Communication : public CommQueue<> {
} else } else
q->iv->radio->prepareDevInformCmd(q->iv, q->cmd, q->ts, q->iv->alarmLastId, false); q->iv->radio->prepareDevInformCmd(q->iv, q->cmd, q->ts, q->iv->alarmLastId, false);
q->iv->radioStatistics.txCnt++; q->iv->radioStatistics.txCnt++;
mWaitTimeout = millis() + 500; mWaitTimeout = millis() + timeout;
setAttempt(); setAttempt();
mState = States::WAIT; mState = States::WAIT;
break; break;
@ -73,7 +78,7 @@ class Communication : public CommQueue<> {
cmdDone(); cmdDone();
DPRINT_IVID(DBG_INFO, q->iv->id); DPRINT_IVID(DBG_INFO, q->iv->id);
DBGPRINT(F("request timeout: ")); DBGPRINT(F("request timeout: "));
DBGPRINT(String(millis() - mWaitTimeout + 500)); DBGPRINT(String(millis() - mWaitTimeout + timeout));
DBGPRINTLN(F("ms")); DBGPRINTLN(F("ms"));
q->iv->radioStatistics.rxFailNoAnser++; // got nothing q->iv->radioStatistics.rxFailNoAnser++; // got nothing
@ -92,7 +97,7 @@ class Communication : public CommQueue<> {
DPRINT_IVID(DBG_INFO, q->iv->id); DPRINT_IVID(DBG_INFO, q->iv->id);
DBGPRINT(F("RX ")); DBGPRINT(F("RX "));
if(p->millis < 100) if(p->millis < 100)
DBGPRINT(F("0")); DBGPRINT(F(" "));
DBGPRINT(String(p->millis)); DBGPRINT(String(p->millis));
DBGPRINT(F("ms ")); DBGPRINT(F("ms "));
DBGPRINT(String(p->len)); DBGPRINT(String(p->len));
@ -119,13 +124,20 @@ class Communication : public CommQueue<> {
} else if(IV_MI == q->iv->ivGen) { } else if(IV_MI == q->iv->ivGen) {
parseMiFrame(p, q); parseMiFrame(p, q);
} }
} else } else {
DPRINTLN(DBG_WARN, F("Inverter serial does not match")); DPRINTLN(DBG_WARN, F("Inverter serial does not match"));
mWaitTimeout = millis() + timeout;
}
q->iv->radio->mBufCtrl.pop(); q->iv->radio->mBufCtrl.pop();
yield(); yield();
} }
mState = nextState; if(0 == q->attempts) {
cmdDone(q);
mState = States::RESET;
} else
mState = nextState;
} }
break; break;
@ -140,14 +152,15 @@ class Communication : public CommQueue<> {
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)
break; break;
i++;
} }
if(q->attempts) { if(q->attempts) {
q->iv->radio->sendCmdPacket(q->iv, TX_REQ_INFO, (ALL_FRAMES + i), true); q->iv->radio->sendCmdPacket(q->iv, TX_REQ_INFO, (SINGLE_FRAME + i), true);
q->iv->radioStatistics.retransmits++; q->iv->radioStatistics.retransmits++;
mWaitTimeout = millis() + 500; mWaitTimeout = millis() + timeout;
mState = States::WAIT; mState = States::WAIT;
} else { } else {
add(q, true); add(q, true);
@ -169,9 +182,9 @@ class Communication : public CommQueue<> {
DBGPRINTLN(F(" attempts left)")); DBGPRINTLN(F(" attempts left)"));
if(q->attempts) { if(q->attempts) {
q->iv->radio->sendCmdPacket(q->iv, TX_REQ_INFO, (ALL_FRAMES + i), true); q->iv->radio->sendCmdPacket(q->iv, TX_REQ_INFO, (SINGLE_FRAME + i), true);
q->iv->radioStatistics.retransmits++; q->iv->radioStatistics.retransmits++;
mWaitTimeout = millis() + 500; mWaitTimeout = millis() + timeout;
mState = States::WAIT; mState = States::WAIT;
} else { } else {
add(q, true); add(q, true);
@ -243,62 +256,12 @@ class Communication : public CommQueue<> {
// small MI or MI 1500 data responses to 0x09, 0x11, 0x36, 0x37, 0x38 and 0x39 // small MI or MI 1500 data responses to 0x09, 0x11, 0x36, 0x37, 0x38 and 0x39
//mPayload[iv->id].txId = p->packet[0]; //mPayload[iv->id].txId = p->packet[0];
miDataDecode(p, q); 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
inline void miDataDecode(packet_t *p, const queue_s *q) { rec->ts = q->ts;
record_t<> *rec = q->iv->getRecordStruct(RealTimeRunData_Debug); // choose the parser miStsConsolidate(q, ((p->packet[0] == 0x88) ? 1 : 2), rec, p->packet[10], p->packet[12], p->packet[9], p->packet[11]);
//rec->ts = mPayload[iv->id].ts;
//mPayload[iv->id].gotFragment = true;
//mPayload[iv->id].multi_parts += 4;
uint8_t datachan = ( p->packet[0] == (MI_REQ_CH1 + ALL_FRAMES) || p->packet[0] == (MI_REQ_4CH + ALL_FRAMES) ) ? CH1 :
( p->packet[0] == (MI_REQ_CH2 + ALL_FRAMES) || p->packet[0] == (0x37 + ALL_FRAMES) ) ? CH2 :
p->packet[0] == (0x38 + ALL_FRAMES) ? CH3 :
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);
q->iv->setValue(q->iv->getPosByChFld(datachan, FLD_IDC, rec), rec, (float)((p->packet[11] << 8) + p->packet[12])/10);
q->iv->setValue(q->iv->getPosByChFld(0, FLD_UAC, rec), rec, (float)((p->packet[13] << 8) + p->packet[14])/10);
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);
q->iv->setValue(q->iv->getPosByChFld(datachan, FLD_YD, rec), rec, (float)((p->packet[19] << 8) + p->packet[20])/1);
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 < 3 ) {
mPayload[q->iv->id].dataAB[datachan] = true;
}
if ( !mPayload[iv->id].dataAB[CH0] && mPayload[iv->id].dataAB[CH1] && mPayload[iv->id].dataAB[CH2] ) {
mPayload[iv->id].dataAB[CH0] = true;
}*/
if (p->packet[0] >= (MI_REQ_4CH + ALL_FRAMES) ) {
/*For MI1500:
if (MI1500) {
STAT = (uint8_t)(p->packet[25] );
FCNT = (uint8_t)(p->packet[26]);
FCODE = (uint8_t)(p->packet[27]);
}*/
//miStsConsolidate(iv, 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;
} else {
//miComplete(iv);
}
} else if((p->packet[0] == (MI_REQ_CH1 + ALL_FRAMES)) && q->iv->type == INV_TYPE_2CH ) {
addImportant(q->iv, MI_REQ_CH2);
} }
} }
@ -360,7 +323,7 @@ class Communication : public CommQueue<> {
} }
memcpy(&mPayload[len], mLocalBuf[i].buf, mLocalBuf[i].len); memcpy(&mPayload[len], mLocalBuf[i].buf, mLocalBuf[i].len);
len += mLocalBuf[i].len; len += mLocalBuf[i].len;
// get worst RSSI // get worst RSSI (high value is better)
if(mLocalBuf[i].rssi > rssi) if(mLocalBuf[i].rssi > rssi)
rssi = mLocalBuf[i].rssi; rssi = mLocalBuf[i].rssi;
} }
@ -408,6 +371,224 @@ class Communication : public CommQueue<> {
} }
} }
private:
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;
/*
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 }
};
*/
}
inline void miDataDecode(packet_t *p, const queue_s *q) {
record_t<> *rec = q->iv->getRecordStruct(RealTimeRunData_Debug); // choose the parser
rec->ts = q->ts;
q->iv->radioStatistics.rxSuccess++;
mState = States::RESET;
uint8_t datachan = ( p->packet[0] == (MI_REQ_CH1 + ALL_FRAMES) || p->packet[0] == (MI_REQ_4CH + ALL_FRAMES) ) ? CH1 :
( p->packet[0] == (MI_REQ_CH2 + ALL_FRAMES) || p->packet[0] == (0x37 + ALL_FRAMES) ) ? CH2 :
p->packet[0] == (0x38 + ALL_FRAMES) ? CH3 :
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);
q->iv->setValue(q->iv->getPosByChFld(datachan, FLD_IDC, rec), rec, (float)((p->packet[11] << 8) + p->packet[12])/10);
q->iv->setValue(q->iv->getPosByChFld(0, FLD_UAC, rec), rec, (float)((p->packet[13] << 8) + p->packet[14])/10);
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);
q->iv->setValue(q->iv->getPosByChFld(datachan, FLD_YD, rec), rec, (float)((p->packet[19] << 8) + p->packet[20])/1);
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 (p->packet[0] >= (MI_REQ_4CH + ALL_FRAMES) ) {
/*For MI1500:
if (MI1500) {
STAT = (uint8_t)(p->packet[25] );
FCNT = (uint8_t)(p->packet[26]);
FCODE = (uint8_t)(p->packet[27]);
}*/
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(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 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;
if (iv->type == INV_TYPE_1CH) {
if ((!iv->lastAlarm[0].code) || (iv->lastAlarm[0].code == 1))
ac_pow += iv->getValue(iv->getPosByChFld(1, FLD_PDC, rec), rec);
} else {
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();
}
private: private:
enum class States : uint8_t { enum class States : uint8_t {
RESET, START, WAIT, CHECK_FRAMES, CHECK_PACKAGE RESET, START, WAIT, CHECK_FRAMES, CHECK_PACKAGE

7
src/hm/hmInverter.h

@ -201,14 +201,13 @@ class Inverter {
cb(devControlCmd, true); cb(devControlCmd, true);
mDevControlRequest = false; mDevControlRequest = false;
} else if(0 == getFwVersion()) } else if(0 == getFwVersion())
cb(MI_REQ_CH1, false); // get firmware version cb(0x0f, false); // get firmware version; for MI, this makes part of polling the device software and hardware version number
//cb(InverterDevInform_All, false); // get firmware version
else { else {
record_t<> *rec = getRecordStruct(InverterDevInform_Simple); record_t<> *rec = getRecordStruct(InverterDevInform_Simple);
if (getChannelFieldValue(CH0, FLD_PART_NUM, rec) == 0) 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 else
cb(type == INV_TYPE_4CH ? MI_REQ_4CH : MI_REQ_CH1, false); cb(((type == INV_TYPE_4CH) ? MI_REQ_4CH : MI_REQ_CH1), false);
} }
} }
} }

Loading…
Cancel
Save