Browse Source

MI command review

Payload now will be build according to xlsx, but note: this seems not to work at least for the MI-600 used for testing...
- non-persistent relative  will treated as short percentage messages (may need review, if multiplying by 10 is needed!)
- non-persistent absolute will treated same as "as is" in DTUSimMI, so always "10%" will be filled as percentage (this may not work for all type of MI)
- persistent will be handled as DRED command, allowing only a few presets. So use absolute values, these will be translated as follows:
-- 0x55AA	 Boot without DRM restrictions (value higher than double of inverter max power (IMP))
-- 0xA5A5	 DRM0 shutdown (not supported yet)
-- 0x5A5A	 DRM5 power limit 0% (values lower than 25% of IMP)
-- 0xAA55	 DRM6 power limit 50% (values between 25% and 75% of IMP)
-- 0x5A55	 DRM8 unlimited power operation (values between 75% and 100% of IMP)
pull/1190/head
rejoe2 2 years ago
committed by GitHub
parent
commit
03b295b6b8
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      src/hm/hmInverter.h
  2. 66
      src/hm/hmRadio.h
  3. 124
      src/hm/miPayload.h

4
src/hm/hmInverter.h

@ -142,7 +142,7 @@ class Inverter {
uint8_t channels; // number of PV channels (1-4)
record_t<REC_TYP> recordMeas; // structure for measured values
record_t<REC_TYP> recordInfo; // structure for info values
record_t<REC_TYP> recordHwInfo; // structure for simple (hardware) info values
record_t<REC_TYP> recordHwInfo; // structure for simple (hardware) info values
record_t<REC_TYP> recordConfig; // structure for system config values
record_t<REC_TYP> recordAlarm; // structure for alarm values
bool initialized; // needed to check if the inverter was correctly added (ESP32 specific - union types are never null)
@ -152,7 +152,7 @@ class Inverter {
uint8_t alarmNxtWrPos; // indicates the position in array (rolling buffer)
uint16_t alarmCnt; // counts the total number of occurred alarms
uint16_t alarmLastId; // lastId which was received
int8_t rssi; // HMS and HMT inverters only
int8_t rssi; // accurate for HMS and HMT inverters only
static uint32_t *timestamp; // system timestamp

66
src/hm/hmRadio.h

@ -169,7 +169,7 @@ class HmRadio {
mSerialDebug = true;
}
void sendControlPacket(uint64_t invId, uint8_t cmd, uint16_t *data, bool isRetransmit, bool isNoMI = true, bool is4chMI = false) {
void sendControlPacket(uint64_t invId, uint8_t cmd, uint16_t *data, bool isRetransmit, bool isNoMI = true, uint16_t powerMax = 0) {
DPRINT(DBG_INFO, F("sendControlPacket cmd: 0x"));
DBGHEXLN(cmd);
initPacket(invId, TX_REQ_DEVCONTROL, SINGLE_FRAME);
@ -185,6 +185,7 @@ class HmRadio {
}
} else { //MI 2nd gen. specific
switch (cmd) {
case Restart:
case TurnOn:
//mTxBuf[0] = 0x50;
mTxBuf[9] = 0x55;
@ -195,21 +196,54 @@ class HmRadio {
mTxBuf[10] = 0x55;
break;
case ActivePowerContr:
mTxBuf[9] = 0x5a;
mTxBuf[10] = 0x5a;
//Testing only! Original NRF24_DTUMIesp.ino code #L612-L613:
//UsrData[0]=0x5A;UsrData[1]=0x5A;UsrData[2]=100;//0x0a;// 10% limit
//UsrData[3]=((Limit*10) >> 8) & 0xFF; UsrData[4]= (Limit*10) & 0xFF; //WR needs 1 dec= zB 100.1 W
if (is4chMI) {
mTxBuf[cnt++] = 100; //10% limit, seems to be necessary to send sth. at all, but for MI-1500 this has no effect
//works (if ever!) only for absulute power limits!
mTxBuf[cnt++] = ((data[0] * 10) >> 8) & 0xff; // power limit
mTxBuf[cnt++] = ((data[0] * 10) ) & 0xff; // power limit
} else {
mTxBuf[cnt++] = data[0]*10; // power limit
if (data[1]<256) { // non persistent
mTxBuf[9] = 0x5a;
mTxBuf[10] = 0x5a;
//Testing only! Original NRF24_DTUMIesp.ino code #L612-L613:
//UsrData[0]=0x5A;UsrData[1]=0x5A;UsrData[2]=100;//0x0a;// 10% limit
//UsrData[3]=((Limit*10) >> 8) & 0xFF; UsrData[4]= (Limit*10) & 0xFF; //WR needs 1 dec= zB 100.1 W
if (!data[1]) { // AbsolutNonPersistent
mTxBuf[++cnt] = 100; //10% limit, seems to be necessary to send sth. at all, but for MI-1500 this has no effect
//works (if ever!) only for absulute power limits!
mTxBuf[++cnt] = ((data[0] * 10) >> 8) & 0xff; // power limit in W
mTxBuf[++cnt] = ((data[0] * 10) ) & 0xff; // power limit in W
} else if (powerMax) { //relative, but 4ch-MI (if ever) only accepts absolute values
mTxBuf[++cnt] = data[0]; // simple power limit in %, might be necessary to multiply by 10?
mTxBuf[++cnt] = ((data[0] * 10 * powerMax) >> 8) & 0xff; // power limit
mTxBuf[++cnt] = ((data[0] * 10 * powerMax) ) & 0xff; // power limit
} else { // might work for 1/2ch MI (if ever)
mTxBuf[++cnt] = data[0]; // simple power limit in %, might be necessary to multiply by 10?
}
} else { // persistent power limit needs to be translated in DRED command (?)
/* DRED instruction
Order Function
0x55AA Boot without DRM restrictions
0xA5A5 DRM0 shutdown
0x5A5A DRM5 power limit 0%
0xAA55 DRM6 power limit 50%
0x5A55 DRM8 unlimited power operation
*/
mTxBuf[0] = 0x50;
if (data[1] == 256UL) { // AbsolutPersistent
if (data[0] == 0 && !powerMax) {
mTxBuf[9] = 0xa5;
mTxBuf[10] = 0xa5;
} else if (data[0] == 0 || !powerMax || data[0] < powerMax/4 ) {
mTxBuf[9] = 0x5a;
mTxBuf[10] = 0x5a;
} else if (data[0] <= powerMax/4*3) {
mTxBuf[9] = 0xaa;
mTxBuf[10] = 0x55;
} else if (data[0] <= powerMax) {
mTxBuf[9] = 0x5a;
mTxBuf[10] = 0x55;
} else if (data[0] > powerMax*2) {
mTxBuf[9] = 0x55;
mTxBuf[10] = 0xaa;
}
}
}
break;
default:
return;
@ -254,7 +288,7 @@ class HmRadio {
bool mSerialDebug;
private:
bool getReceived(void) {
bool getReceived(void) {
bool tx_ok, tx_fail, rx_ready;
mNrf24.whatHappened(tx_ok, tx_fail, rx_ready); // resets the IRQ pin to HIGH

124
src/hm/miPayload.h

@ -26,6 +26,7 @@ typedef struct {
uint8_t invId;
uint8_t retransmits;
bool gotFragment;
bool gotGPF;
uint8_t rtrRes; // for limiting resets
uint8_t multi_parts; // for quality
bool rxTmo;
@ -49,7 +50,8 @@ class MiPayload {
mTimestamp = timestamp;
for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i++) {
reset(i, false, true);
mPayload[i].limitrequested = true;
mPayload[i].limitrequested = false;
mPayload[i].gotGPF = false;
}
mSerialDebug = false;
mHighPrioIv = NULL;
@ -124,10 +126,12 @@ class MiPayload {
DBGPRINT(F("Devcontrol request 0x"));
DHEX(iv->devControlCmd);
DBGPRINT(F(" power limit "));
DBGPRINTLN(String(iv->powerLimit[0]));
DBGPRINT(String(iv->powerLimit[0]));
DBGPRINT(F(" with PowerLimitControl "));
DBGPRINTLN(String(iv->powerLimit[1]));
}
iv->powerLimitAck = false;
mRadio->sendControlPacket(iv->radioId.u64, iv->devControlCmd, iv->powerLimit, false, false, iv->type == INV_TYPE_4CH);
mRadio->sendControlPacket(iv->radioId.u64, iv->devControlCmd, iv->powerLimit, false, false, iv->powerLimit[1] == RelativNonPersistent ? 0 : iv->getMaxPower()); // might be dependent on IV-Type as well, iv->type == INV_TYPE_4CH?
mPayload[iv->id].txCmd = iv->devControlCmd;
mPayload[iv->id].limitrequested = true;
@ -136,17 +140,18 @@ class MiPayload {
uint8_t cmd = iv->getQueuedCmd();
uint8_t cmd2 = cmd;
if ( cmd == SystemConfigPara ) { //0x05 for HM-types
if (!mPayload[iv->id].limitrequested) { // only do once at startup
if (mPayload[iv->id].gotGPF) {
iv->setQueuedCmdFinished();
cmd = iv->getQueuedCmd();
} else {
mPayload[iv->id].limitrequested = false;
}
}
if (cmd == 0x01 || cmd == SystemConfigPara ) { //0x1 and 0x05 for HM-types
cmd2 = cmd == SystemConfigPara ? 0x01 : 0x00; //perhaps we can only try to get second frame?
cmd = 0x0f; // for MI, these seem to make part of polling the device software and hardware version number command
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
} else if (cmd == SystemConfigPara ) { // 0x05 for HM-types
cmd2 = 0x00;
cmd = 0x10; // legacy GPF request
}
if (mSerialDebug) {
DPRINT_IVID(DBG_INFO, iv->id);
@ -177,21 +182,24 @@ class MiPayload {
else if (p->packet[0] == (0x11 + SINGLE_FRAME)) { // 0x92; MI status response to 0x11
miStsDecode(iv, p, CH2);
}
else if ( p->packet[0] == 0x09 + ALL_FRAMES ||
} else if ( p->packet[0] == 0x09 + ALL_FRAMES ||
p->packet[0] == 0x11 + ALL_FRAMES ||
( p->packet[0] >= (0x36 + ALL_FRAMES) && p->packet[0] < (0x39 + SINGLE_FRAME)
&& mPayload[iv->id].txCmd != 0x0f) ) { // small MI or MI 1500 data responses to 0x09, 0x11, 0x36, 0x37, 0x38 and 0x39
mPayload[iv->id].txId = p->packet[0];
miDataDecode(iv,p);
}
else if (p->packet[0] == ( 0x0f + ALL_FRAMES)) {
} else if (p->packet[0] == ( 0x0f + ALL_FRAMES)) {
// MI response from get hardware information request
miHwDecode(iv, p);
mPayload[iv->id].txId = p->packet[0];
} else if (p->packet[0] == ( 0x10 + ALL_FRAMES)) {
// MI response from get Grid Profile information request
miGPFDecode(iv, p);
mPayload[iv->id].txId = p->packet[0];
} else if ( p->packet[0] == (TX_REQ_INFO + ALL_FRAMES) // response from get information command
|| (p->packet[0] == 0xB6 && mPayload[iv->id].txCmd != 0x36)) { // strange short response from MI-1500 3rd gen; might be misleading!
// atm, we just do nothing else than print out what we got...
@ -211,11 +219,13 @@ class MiPayload {
}
} else if (p->packet[0] == (TX_REQ_DEVCONTROL + ALL_FRAMES ) // response from dev control command
|| p->packet[0] == (TX_REQ_DEVCONTROL + ALL_FRAMES -1)) { // response from DRED instruction
DPRINT_IVID(DBG_DEBUG, iv->id);
DBGPRINTLN(F("Response from devcontrol request received"));
if (mSerialDebug) {
DPRINT_IVID(DBG_DEBUG, iv->id);
DBGPRINTLN(F("Response from devcontrol request received"));
}
mPayload[iv->id].txId = p->packet[0];
iv->clearDevControlRequest();
mStat->rxSuccess++;
if ((p->packet[9] == 0x5a) && (p->packet[10] == 0x5a)) {
mApp->setMqttPowerLimitAck(iv);
@ -228,7 +238,8 @@ class MiPayload {
DBGPRINTLN(String(iv->powerLimit[1]));
}
iv->clearCmdQueue();
iv->enqueCommand<InfoCommand>(SystemConfigPara); // read back power limit
//does not work for MI
//iv->enqueCommand<InfoCommand>(SystemConfigPara); // read back power limit
}
iv->devControlCmd = Init;
} else { // some other response; copied from hmPayload:process; might not be correct to do that here!!!
@ -309,7 +320,8 @@ class MiPayload {
(mPayload[iv->id].txId != (0x88)) &&
(mPayload[iv->id].txId != (0x92)) &&
(mPayload[iv->id].txId != 0 &&
mPayload[iv->id].txCmd != 0x0f)) {
mPayload[iv->id].txCmd != 0x0f
&& !iv->getDevControlRequest())) {
// no processing needed if txId is not one of 0x95, 0x88, 0x89, 0x91, 0x92 or response to 0x36ff
mPayload[iv->id].complete = true;
mPayload[iv->id].rxTmo = true;
@ -330,7 +342,7 @@ class MiPayload {
} else if(iv->devControlCmd == ActivePowerContr) {
DPRINT_IVID(DBG_INFO, iv->id);
DBGPRINTLN(F("retransmit power limit"));
mRadio->sendControlPacket(iv->radioId.u64, iv->devControlCmd, iv->powerLimit, true, false);
mRadio->sendControlPacket(iv->radioId.u64, iv->devControlCmd, iv->powerLimit, true, false, iv->getMaxPower());
} else {
uint8_t cmd = mPayload[iv->id].txCmd;
if (mPayload[iv->id].retransmits < mMaxRetrans) {
@ -339,6 +351,7 @@ class MiPayload {
DPRINT_IVID(DBG_INFO, iv->id);
DBGPRINTLN(F("nothing received"));
mPayload[iv->id].retransmits = mMaxRetrans;
mPayload[iv->id].requested = false; //close failed request
} else if( !mPayload[iv->id].gotFragment && !mPayload[iv->id].rxTmo ) {
DPRINT_IVID(DBG_INFO, iv->id);
DBGPRINTLN(F("retransmit on failed first request"));
@ -475,17 +488,29 @@ class MiPayload {
}
uint16_t prntsts = statusMi == 3 ? 1 : statusMi;
bool stsok = true;
if ( statusMi != mPayload[iv->id].sts[stschan] ) { //sth.'s changed?
iv->alarmCnt = 1; // minimum...
if ((iv->type != INV_TYPE_1CH) && ((statusMi != 3) //sth is or was wrong!
|| ((mPayload[iv->id].sts[stschan] && statusMi == 3) && (mPayload[iv->id].sts[stschan] != 3)))
//sth is or was wrong?
if ( (iv->type != INV_TYPE_1CH) && ( (statusMi != 3)
|| ((mPayload[iv->id].sts[stschan]) && (statusMi == 3) && (mPayload[iv->id].sts[stschan] != 3)))
) {
iv->lastAlarm[stschan] = alarm_t(prntsts, mPayload[iv->id].ts,mPayload[iv->id].ts);
iv->lastAlarm[stschan] = alarm_t(prntsts, mPayload[iv->id].ts,0);
iv->alarmCnt = iv->type == INV_TYPE_2CH ? 3 : 5;
iv->alarmLastId = iv->alarmMesIndex;
}
iv->alarmLastId = prntsts; //iv->alarmMesIndex;
mPayload[iv->id].sts[stschan] = statusMi;
stsok = false;
if (iv->alarmCnt > 1) { //more than one channel
for (uint8_t ch = 0; ch < (iv->alarmCnt); ++ch) { //start with 1
if (mPayload[iv->id].sts[ch] == 3) {
stsok = true;
break;
}
}
}
if (mSerialDebug) {
DPRINT(DBG_WARN, F("New state on CH"));
DBGPRINT(String(stschan)); DBGPRINT(F(" ("));
@ -494,9 +519,11 @@ class MiPayload {
}
}
if ( !mPayload[iv->id].sts[0] || prntsts < mPayload[iv->id].sts[0] ) {
mPayload[iv->id].sts[0] = prntsts;
//if ( !mPayload[iv->id].sts[0] || prntsts < mPayload[iv->id].sts[0] ) {
//mPayload[iv->id].sts[0] = prntsts;
if (!stsok) {
iv->setValue(iv->getPosByChFld(0, FLD_EVT, rec), rec, prntsts);
iv->lastAlarm[0] = alarm_t(prntsts, mPayload[iv->id].ts, 0);
}
if (iv->alarmMesIndex < rec->record[iv->getPosByChFld(0, FLD_EVT, rec)]) {
@ -506,21 +533,7 @@ class MiPayload {
DBGPRINT(F("alarm ID incremented to "));
DBGPRINTLN(String(iv->alarmMesIndex));
}
iv->lastAlarm[0] = alarm_t(prntsts, mPayload[iv->id].ts, mPayload[iv->id].ts);
}
/*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 != mCbAlarm)
(mCbAlarm)(code, start, end);
yield();
}
}*/
}
void miDataDecode(Inverter<> *iv, packet_t *p) {
@ -758,6 +771,37 @@ const byteAssign_t InfoAssignment[] = {
mPayload[iv->id].requested= false;
mStat->rxSuccess++;
}
if (mHighPrioIv == NULL)
mHighPrioIv = iv;
}
void miGPFDecode(Inverter<> *iv, packet_t *p ) {
mPayload[iv->id].gotFragment = true;
mPayload[iv->id].gotGPF = true;
record_t<> *rec = iv->getRecordStruct(InverterDevInform_Simple); // choose the record structure
rec->ts = mPayload[iv->id].ts;
iv->setValue(2, rec, (uint32_t) (((p->packet[10] << 8) | p->packet[11]))); //FLD_GRID_PROFILE_CODE
iv->setValue(3, rec, (uint32_t) (((p->packet[12] << 8) | p->packet[13]))); //FLD_GRID_PROFILE_VERSION
iv->setQueuedCmdFinished();
mStat->rxSuccess++;
/* according to xlsx (different start byte -1!)
Polling Grid-connected Protection Parameter File Command - Receipt
byte[10] ST1 indicates the status of the grid-connected protection file. ST1=1 indicates the default grid-connected protection file, ST=2 indicates that the grid-connected protection file is configured and normal, ST=3 indicates that the grid-connected protection file cannot be recognized, ST=4 indicates that the grid-connected protection file is damaged
byte[11] byte[12] CountryStd variable indicates the national standard code of the grid-connected protection file
byte[13] byte[14] Version indicates the version of the grid-connected protection file
byte[15] byte[16]
*/
if(mSerialDebug) {
DPRINT(DBG_INFO,F("ST1 "));
DBGPRINTLN(String(p->packet[9]));
DPRINT(DBG_INFO,F("CountryStd "));
DBGPRINTLN(String((p->packet[10] << 8) + p->packet[11]));
DPRINT(DBG_INFO,F("Version "));
DBGPRINTLN(String((p->packet[12] << 8) + p->packet[13]));
}
if (mHighPrioIv == NULL)
mHighPrioIv = iv;
}
void reset(uint8_t id, bool setTxTmo = true, bool clrSts = false) {
@ -777,8 +821,8 @@ const byteAssign_t InfoAssignment[] = {
mPayload[id].txCmd = 0;
mPayload[id].requested = false;
mPayload[id].ts = *mTimestamp;
mPayload[id].sts[0] = 0;
if (clrSts) { // only clear channel states at startup
mPayload[id].sts[0] = 0;
mPayload[id].sts[CH1] = 0;
mPayload[id].sts[CH2] = 0;
mPayload[id].sts[CH3] = 0;

Loading…
Cancel
Save