From 3ed749b2b6ff2aad346ed78f75e5c322ddd3ba95 Mon Sep 17 00:00:00 2001 From: rejoe2 Date: Sun, 1 Oct 2023 07:54:20 +0200 Subject: [PATCH 1/8] MI control command review II patched against (non-working!) dev version 0.7.61 From 9b6c7728d2d8e21ba4649cfafb6a34f10c892d87 Mon Sep 17 00:00:00 2001 From: rejoe2 Date: Sun, 1 Oct 2023 07:58:41 +0200 Subject: [PATCH 2/8] MI controll command review II now with changes, but still base .61 is not working for HM/MI Types --- src/hm/hmPayload.h | 2 +- src/hm/hmRadio.h | 78 +++++++++++++----- src/hm/miPayload.h | 199 ++++++++++++++++++++++++++++----------------- src/hm/radio.h | 2 +- src/hms/hmsRadio.h | 2 +- 5 files changed, 185 insertions(+), 98 deletions(-) diff --git a/src/hm/hmPayload.h b/src/hm/hmPayload.h index 1f6e0468..faef831e 100644 --- a/src/hm/hmPayload.h +++ b/src/hm/hmPayload.h @@ -449,7 +449,7 @@ class HmPayload { return false; //requests to cause the next request to be executed immediately - if (mPayload[iv->id].gotFragment && ((mPayload[iv->id].txCmd < 11) || (mPayload[iv->id].txCmd > 18))) { + if (mPayload[iv->id].gotFragment && ((mPayload[iv->id].txCmd < RealTimeRunData_Debug) || (mPayload[iv->id].txCmd >= AlarmData))) { *fastNext = true; } diff --git a/src/hm/hmRadio.h b/src/hm/hmRadio.h index a808bd88..dc517ab8 100644 --- a/src/hm/hmRadio.h +++ b/src/hm/hmRadio.h @@ -16,6 +16,11 @@ const char* const rf24AmpPowerNames[] = {"MIN", "LOW", "HIGH", "MAX"}; +#define TX_REQ_DREDCONTROL 0x50 +#define DRED_A5 0xa5 +#define DRED_5A 0x5a +#define DRED_AA 0xaa +#define DRED_55 0x55 //----------------------------------------------------------------------------- // HM Radio class @@ -139,7 +144,7 @@ class HmRadio : public Radio { return mNrf24.isChipConnected(); } - void sendControlPacket(Inverter<> *iv, uint8_t cmd, uint16_t *data, bool isRetransmit, bool isNoMI = true, bool is4chMI = false) { + void sendControlPacket(Inverter<> *iv, 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(iv->radioId.u64, TX_REQ_DEVCONTROL, SINGLE_FRAME); @@ -155,31 +160,64 @@ class HmRadio : public Radio { } } else { //MI 2nd gen. specific switch (cmd) { + case Restart: case TurnOn: - //mTxBuf[0] = 0x50; - mTxBuf[9] = 0x55; - mTxBuf[10] = 0xaa; + mTxBuf[9] = DRED_55; + mTxBuf[10] = DRED_AA; break; case TurnOff: - mTxBuf[9] = 0xaa; - mTxBuf[10] = 0x55; + mTxBuf[9] = DRED_AA; + mTxBuf[10] = DRED_55; 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] = DRED_5A; + mTxBuf[10] = DRED_5A; + //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] = TX_REQ_DREDCONTROL; + + if (data[1] == 256UL) { // AbsolutPersistent + if (data[0] == 0 && !powerMax) { + mTxBuf[9] = DRED_A5; + mTxBuf[10] = DRED_A5; + } else if (data[0] == 0 || !powerMax || data[0] < powerMax/4 ) { + mTxBuf[9] = DRED_5A; + mTxBuf[10] = DRED_5A; + } else if (data[0] <= powerMax/4*3) { + mTxBuf[9] = DRED_AA; + mTxBuf[10] = DRED_55; + } else if (data[0] <= powerMax) { + mTxBuf[9] = DRED_5A; + mTxBuf[10] = DRED_55; + } else if (data[0] > powerMax*2) { + mTxBuf[9] = DRED_55; + mTxBuf[10] = DRED_AA; + } + } } - - break; default: return; diff --git a/src/hm/miPayload.h b/src/hm/miPayload.h index 6273d571..32b16696 100644 --- a/src/hm/miPayload.h +++ b/src/hm/miPayload.h @@ -10,20 +10,26 @@ #include "../config/config.h" #include +#define MI_REQ_CH1 0x09 +#define MI_REQ_CH2 0x11 +#define MI_REQ_4CH 0x36 + typedef struct { uint32_t ts; bool requested; bool limitrequested; uint8_t txCmd; uint8_t len[MAX_PAYLOAD_ENTRIES]; + int8_t rssi[4]; bool complete; bool dataAB[3]; bool stsAB[3]; - uint16_t sts[6]; + uint16_t sts[5]; uint8_t txId; uint8_t invId; uint8_t retransmits; bool gotFragment; + bool gotGPF; uint8_t rtrRes; // for limiting resets uint8_t multi_parts; // for quality bool rxTmo; @@ -45,7 +51,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; @@ -120,10 +127,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; - iv->radio->sendControlPacket(iv, iv->devControlCmd, iv->powerLimit, false, false, iv->type == INV_TYPE_4CH); + iv->radio->sendControlPacket(iv, iv->devControlCmd, iv->powerLimit, false, false, (iv->powerLimit[1] == RelativNonPersistent) ? 0 : iv->getMaxPower()); mPayload[iv->id].txCmd = iv->devControlCmd; mPayload[iv->id].limitrequested = true; @@ -132,21 +141,22 @@ 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); - DBGPRINT(F("prepareDevInformCmd 0x")); + DBGPRINT(F("legacy cmd 0x")); DBGHEXLN(cmd); } iv->radio->sendCmdPacket(iv, cmd, cmd2, false, false); @@ -167,29 +177,32 @@ 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)) { // 0x88; MI status response to 0x09 + if (p->packet[0] == (0x88)) { // 0x88 is MI status response to 0x09 miStsDecode(iv, p); } - else if (p->packet[0] == (0x11 + SINGLE_FRAME)) { // 0x92; MI status response to 0x11 + else if (p->packet[0] == (MI_REQ_CH2 + SINGLE_FRAME)) { // 0x92; MI status response to 0x11 miStsDecode(iv, p, CH2); - } - 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) + } 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) && 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! + || (p->packet[0] == 0xB6 && mPayload[iv->id].txCmd != MI_REQ_4CH)) { // strange short response from MI-1500 3rd gen; might be misleading! // atm, we just do nothing else than print out what we got... // for decoding see xls- Data collection instructions - #147ff //mPayload[iv->id].txId = p->packet[0]; @@ -207,9 +220,12 @@ 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 DEBUG_LEVEL >= DBG_DEBUG + if (mSerialDebug) { + DPRINT_IVID(DBG_DEBUG, iv->id); + DBGPRINTLN(F("Response from devcontrol request received")); + } +#endif mPayload[iv->id].txId = p->packet[0]; iv->clearDevControlRequest(); @@ -224,7 +240,8 @@ class MiPayload { DBGPRINTLN(String(iv->powerLimit[1])); } iv->clearCmdQueue(); - iv->enqueCommand(SystemConfigPara); // read back power limit + //does not work for MI + //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!!! @@ -298,14 +315,15 @@ class MiPayload { if ( !mPayload[iv->id].complete && (mPayload[iv->id].txId != (TX_REQ_INFO + ALL_FRAMES)) && - (mPayload[iv->id].txId < (0x36 + ALL_FRAMES)) && + (mPayload[iv->id].txId < (MI_REQ_4CH + ALL_FRAMES)) && (mPayload[iv->id].txId > (0x39 + ALL_FRAMES)) && - (mPayload[iv->id].txId != (0x09 + ALL_FRAMES)) && - (mPayload[iv->id].txId != (0x11 + ALL_FRAMES)) && + (mPayload[iv->id].txId != (MI_REQ_CH1 + ALL_FRAMES)) && + (mPayload[iv->id].txId != (MI_REQ_CH2 + ALL_FRAMES)) && (mPayload[iv->id].txId != (0x88)) && (mPayload[iv->id].txId != (0x92)) && - (mPayload[iv->id].txId != 0 && - mPayload[iv->id].txCmd != 0x0f)) { + (mPayload[iv->id].txId != 0) && + 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; @@ -326,7 +344,7 @@ class MiPayload { } else if(iv->devControlCmd == ActivePowerContr) { DPRINT_IVID(DBG_INFO, iv->id); DBGPRINTLN(F("retransmit power limit")); - iv->radio->sendControlPacket(iv, iv->devControlCmd, iv->powerLimit, true, false); + iv->radio->sendControlPacket(iv, iv->devControlCmd, iv->powerLimit, true, false, (iv->powerLimit[1] == RelativNonPersistent) ? 0 : iv->getMaxPower()); } else { uint8_t cmd = mPayload[iv->id].txCmd; if (mPayload[iv->id].retransmits < mMaxRetrans) { @@ -335,6 +353,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")); @@ -346,24 +365,24 @@ class MiPayload { mPayload[id].multi_parts = 0; } else { bool change = false; - if ( cmd >= 0x36 && cmd < 0x39 ) { // MI-1500 Data command - if (cmd > 0x36 && mPayload[iv->id].retransmits==1) // first request for the upper channels + if ( cmd >= MI_REQ_4CH && cmd < 0x39 ) { // MI-1500 Data command + if (cmd > MI_REQ_4CH && mPayload[iv->id].retransmits==1) // first request for the upper channels change = true; - } else if ( cmd == 0x09 ) {//MI single or dual channel device + } else if ( cmd == MI_REQ_CH1 ) {//MI single or dual channel device if ( mPayload[iv->id].dataAB[CH1] && iv->type == INV_TYPE_2CH ) { if (!mPayload[iv->id].stsAB[CH1] && mPayload[iv->id].retransmits<2) {} //first try to get missing sts for first channel a second time else if (!mPayload[iv->id].stsAB[CH2] || !mPayload[iv->id].dataAB[CH2] ) { - cmd = 0x11; + cmd = MI_REQ_CH2; change = true; if (mPayload[iv->id].rtrRes < 3) //only get back to first channel twice mPayload[iv->id].retransmits = 0; //reset counter } } - } else if ( cmd == 0x11) { + } else if ( cmd == MI_REQ_CH2) { if ( mPayload[iv->id].dataAB[CH2] ) { // data + status ch2 are there? if (mPayload[iv->id].stsAB[CH2] && (!mPayload[iv->id].stsAB[CH1] || !mPayload[iv->id].dataAB[CH1])) { - cmd = 0x09; + cmd = MI_REQ_CH1; change = true; } } @@ -408,19 +427,6 @@ class MiPayload { if (!fastNext) { mPayload[iv->id].rxTmo = true; } else { - /*iv->setQueuedCmdFinished(); - uint8_t cmd = iv->getQueuedCmd(); - if (mSerialDebug) { - DPRINT_IVID(DBG_INFO, iv->id); - DBGPRINT(F("fast mode ")); - DBGPRINT(F("prepareDevInformCmd 0x")); - DBGHEXLN(cmd); - } - iv->radioStatistics.rxSuccess++; - //iv->radio->prepareDevInformCmd(iv, cmd, mPayload[iv->id].ts, iv->alarmMesIndex, false); - iv->radio->prepareDevInformCmd(iv, iv->getType(), - iv->getNextTxChanIndex(), cmd, mPayload[iv->id].ts, iv->alarmMesIndex, false); - mPayload[iv->id].txCmd = cmd; */ if (mHighPrioIv == NULL) mHighPrioIv = iv; } @@ -471,17 +477,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(" (")); @@ -490,9 +508,9 @@ class MiPayload { } } - 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)]) { @@ -502,18 +520,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; - while(1) { - if(0 == iv->parseAlarmLog(i++, payload, payloadLen)) - break; - if (NULL != mCbAlarm) - (mCbAlarm)(iv); - yield(); - } - }*/ } void miDataDecode(Inverter<> *iv, packet_t *p) { @@ -522,8 +529,8 @@ class MiPayload { mPayload[iv->id].gotFragment = true; mPayload[iv->id].multi_parts += 4; - uint8_t datachan = ( p->packet[0] == 0x89 || p->packet[0] == (0x36 + ALL_FRAMES) ) ? CH1 : - ( p->packet[0] == 0x91 || p->packet[0] == (0x37 + ALL_FRAMES) ) ? CH2 : + 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 @@ -540,6 +547,7 @@ class MiPayload { yield(); iv->setValue(iv->getPosByChFld(0, FLD_T, rec), rec, (float) ((int16_t)(p->packet[21] << 8) + p->packet[22])/10); iv->setValue(iv->getPosByChFld(0, FLD_IRR, rec), rec, (float) (calcIrradiation(iv, datachan))); + mPayload[iv->id].rssi[(datachan-1)] = p->rssi; if ( datachan < 3 ) { mPayload[iv->id].dataAB[datachan] = true; @@ -548,7 +556,7 @@ class MiPayload { mPayload[iv->id].dataAB[CH0] = true; } - if (p->packet[0] >= (0x36 + ALL_FRAMES) ) { + if (p->packet[0] >= (MI_REQ_4CH + ALL_FRAMES) ) { /*For MI1500: if (MI1500) { STAT = (uint8_t)(p->packet[25] ); @@ -572,8 +580,10 @@ class MiPayload { if ( mPayload[iv->id].complete ) return; //if we got second message as well in repreated attempt mPayload[iv->id].complete = true; - DPRINT_IVID(DBG_INFO, iv->id); - DBGPRINTLN(F("got all msgs")); + if (mSerialDebug) { + DPRINT_IVID(DBG_INFO, iv->id); + DBGPRINTLN(F("got all msgs")); + } record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); iv->setValue(iv->getPosByChFld(0, FLD_YD, rec), rec, calcYieldDayCh0(iv,0)); @@ -587,6 +597,14 @@ class MiPayload { } ac_pow = (int) (ac_pow*9.5); iv->setValue(iv->getPosByChFld(0, FLD_PAC, rec), rec, (float) ac_pow/10); + int8_t rssi = -127; + for (uint8_t i = 0; i < 4; i++) { + // get best RSSI + if(mPayload[iv->id].rssi[i] > rssi) + rssi = mPayload[iv->id].rssi[i]; + yield(); + } + iv->rssi = rssi; iv->doCalculations(); // update status state-machine, @@ -609,14 +627,14 @@ class MiPayload { if(!*complete) { DPRINTLN(DBG_VERBOSE, F("incomlete, txCmd is 0x") + String(txCmd, HEX)); //we got some delayed status msgs?!? - if ((txCmd == 0x09) || (txCmd == 0x11)) { + if ((txCmd == MI_REQ_CH1) || (txCmd == MI_REQ_CH2)) { if (mPayload[iv->id].stsAB[CH0] && mPayload[iv->id].dataAB[CH0]) { miComplete(iv); return true; } return false; } - if (txCmd >= 0x36 && txCmd <= 0x39) { + if (txCmd >= MI_REQ_4CH && txCmd <= 0x39) { return false; } if (txCmd == 0x0f) { //hw info request, at least hw part nr. and version have to be there... @@ -751,6 +769,38 @@ const byteAssign_t InfoAssignment[] = { mPayload[iv->id].requested= false; iv->radioStatistics.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(); + iv->radioStatistics.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) { @@ -770,13 +820,12 @@ 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; mPayload[id].sts[CH4] = 0; - mPayload[id].sts[5] = 0; //remember last summarized state } } diff --git a/src/hm/radio.h b/src/hm/radio.h index e9b1e47b..a6a2f0e7 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, bool is4chMI = false) = 0; + virtual void sendControlPacket(Inverter<> *iv, uint8_t cmd, uint16_t *data, bool isRetransmit, bool isNoMI = true, uint16_t powerMax = 0) = 0; virtual bool switchFrequency(Inverter<> *iv, uint32_t fromkHz, uint32_t tokHz) { return true; } void handleIntr(void) { diff --git a/src/hms/hmsRadio.h b/src/hms/hmsRadio.h index b6b6bb48..d29eeaa0 100644 --- a/src/hms/hmsRadio.h +++ b/src/hms/hmsRadio.h @@ -47,7 +47,7 @@ class CmtRadio : public Radio { return mCmtAvail; } - void sendControlPacket(Inverter<> *iv, uint8_t cmd, uint16_t *data, bool isRetransmit, bool isNoMI = true, bool is4chMI = false) { + void sendControlPacket(Inverter<> *iv, 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(iv->radioId.u64, TX_REQ_DEVCONTROL, SINGLE_FRAME); From dae638f7c6b56a91e36b5c08b5d4f8b7e1dbd250 Mon Sep 17 00:00:00 2001 From: lumapu Date: Sun, 1 Oct 2023 12:12:21 +0200 Subject: [PATCH 3/8] 0.7.62 * fix communication to inverters #1198 --- src/CHANGES.md | 3 +++ src/app.cpp | 2 +- src/app.h | 9 +-------- src/appInterface.h | 2 -- src/defines.h | 2 +- src/hm/hmRadio.h | 2 +- src/hm/radio.h | 14 +++++++------- src/hms/hmsRadio.h | 2 +- 8 files changed, 15 insertions(+), 21 deletions(-) diff --git a/src/CHANGES.md b/src/CHANGES.md index 89f334cc..0f2bdda4 100644 --- a/src/CHANGES.md +++ b/src/CHANGES.md @@ -1,5 +1,8 @@ # Development Changes +## 0.7.62 - 2023-10-01 +* fix communication to inverters #1198 + ## 0.7.61 - 2023-10-01 * merged `hmPayload` and `hmsPayload` into single class * merged generic radio functions into new parent class `radio.h` diff --git a/src/app.cpp b/src/app.cpp index a58ae877..14c22e18 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -394,9 +394,9 @@ void app::tickSend(void) { if(mConfig->nrf.enabled) { if(!mNrfRadio.isChipConnected()) { DPRINTLN(DBG_WARN, F("NRF24 not connected!")); - return; } } + if (mIVCommunicationOn) { if (!mNrfRadio.mBufCtrl.empty()) { if (mConfig->serial.debug) { diff --git a/src/app.h b/src/app.h index 942848da..72fdbf44 100644 --- a/src/app.h +++ b/src/app.h @@ -80,6 +80,7 @@ class app : public IApp, public ah::Scheduler { #endif } } + #ifdef ESP32 void handleHmsIntr(void) { mCmtRadio.handleIntr(); @@ -125,14 +126,6 @@ class app : public IApp, public ah::Scheduler { return mSaveReboot; } - statistics_t *getNrfStatistics() { - return &mNrfStat; - } - - statistics_t *getCmtStatistics() { - return &mCmtStat; - } - #if !defined(ETHERNET) void scanAvailNetworks() { mWifi.scanAvailNetworks(); diff --git a/src/appInterface.h b/src/appInterface.h index e301bc89..d3bc8346 100644 --- a/src/appInterface.h +++ b/src/appInterface.h @@ -32,8 +32,6 @@ class IApp { virtual bool getShouldReboot() = 0; virtual void setRebootFlag() = 0; virtual const char *getVersion() = 0; - virtual statistics_t *getNrfStatistics() = 0; - virtual statistics_t *getCmtStatistics() = 0; #if !defined(ETHERNET) virtual void scanAvailNetworks() = 0; diff --git a/src/defines.h b/src/defines.h index b7b53887..98b9a726 100644 --- a/src/defines.h +++ b/src/defines.h @@ -13,7 +13,7 @@ //------------------------------------- #define VERSION_MAJOR 0 #define VERSION_MINOR 7 -#define VERSION_PATCH 61 +#define VERSION_PATCH 62 //------------------------------------- typedef struct { diff --git a/src/hm/hmRadio.h b/src/hm/hmRadio.h index a808bd88..ed9652bc 100644 --- a/src/hm/hmRadio.h +++ b/src/hm/hmRadio.h @@ -232,7 +232,7 @@ class HmRadio : public Radio { } void sendPacket(Inverter<> *iv, uint8_t len, bool isRetransmit, bool appendCrc16=true) { - updateCrcs(len, appendCrc16); + updateCrcs(&len, appendCrc16); // set TX and RX channels mTxChIdx = (mTxChIdx + 1) % RF_CHANNELS; diff --git a/src/hm/radio.h b/src/hm/radio.h index e9b1e47b..1a785318 100644 --- a/src/hm/radio.h +++ b/src/hm/radio.h @@ -64,17 +64,17 @@ class Radio { memset(&mTxBuf[10], 0x00, (MAX_RF_PAYLOAD_SIZE-10)); } - void updateCrcs(uint8_t len, bool appendCrc16=true) { + void updateCrcs(uint8_t *len, bool appendCrc16=true) { // append crc's - if (appendCrc16 && (len > 10)) { + if (appendCrc16 && (*len > 10)) { // crc control data - uint16_t crc = ah::crc16(&mTxBuf[10], len - 10); - mTxBuf[len++] = (crc >> 8) & 0xff; - mTxBuf[len++] = (crc ) & 0xff; + uint16_t crc = ah::crc16(&mTxBuf[10], *len - 10); + mTxBuf[(*len)++] = (crc >> 8) & 0xff; + mTxBuf[(*len)++] = (crc ) & 0xff; } // crc over all - mTxBuf[len] = ah::crc8(mTxBuf, len); - len++; + mTxBuf[*len] = ah::crc8(mTxBuf, *len); + (*len)++; } void generateDtuSn(void) { diff --git a/src/hms/hmsRadio.h b/src/hms/hmsRadio.h index b6b6bb48..1ea94c0d 100644 --- a/src/hms/hmsRadio.h +++ b/src/hms/hmsRadio.h @@ -82,7 +82,7 @@ class CmtRadio : public Radio { private: void sendPacket(Inverter<> *iv, uint8_t len, bool isRetransmit, bool appendCrc16=true) { - updateCrcs(len, appendCrc16); + updateCrcs(&len, appendCrc16); if(mSerialDebug) { DPRINT(DBG_INFO, F("TX ")); From 41ade24538c12031f029f6fb382e9de81a92c864 Mon Sep 17 00:00:00 2001 From: lumapu Date: Sun, 1 Oct 2023 12:36:18 +0200 Subject: [PATCH 4/8] 0.7.62 * add timeout before payload is tried to process (necessary for HMS/HMT) --- src/CHANGES.md | 1 + src/app.cpp | 7 ++----- src/app.h | 3 --- src/hm/hmInverter.h | 1 + src/hm/hmPayload.h | 5 +++++ 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/CHANGES.md b/src/CHANGES.md index 0f2bdda4..893b8a3a 100644 --- a/src/CHANGES.md +++ b/src/CHANGES.md @@ -2,6 +2,7 @@ ## 0.7.62 - 2023-10-01 * fix communication to inverters #1198 +* add timeout before payload is tried to process (necessary for HMS/HMT) ## 0.7.61 - 2023-10-01 * merged `hmPayload` and `hmsPayload` into single class diff --git a/src/app.cpp b/src/app.cpp index 14c22e18..762c0c34 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -133,10 +133,10 @@ void app::loop(void) { DBGPRINT(F("dBm | ")); ah::dumpBuf(p->packet, p->len); } - mNrfStat.frmCnt++; Inverter<> *iv = mSys.findInverter(&p->packet[1]); if (NULL != iv) { + iv->radioStatistics.frmCnt++; if (IV_MI == iv->ivGen) mMiPayload.add(iv, p); else @@ -160,10 +160,10 @@ void app::loop(void) { DBGPRINT(F("dBm | ")); ah::dumpBuf(p->packet, p->len); } - mCmtStat.frmCnt++; Inverter<> *iv = mSys.findInverter(&p->packet[1]); if(NULL != iv) { + iv->radioStatistics.frmCnt++; if((iv->ivGen == IV_HMS) || (iv->ivGen == IV_HMT)) mPayload.add(iv, p); } @@ -515,9 +515,6 @@ void app::resetSystem(void) { mSaveReboot = false; mNetworkConnected = false; - - memset(&mNrfStat, 0, sizeof(statistics_t)); - memset(&mCmtStat, 0, sizeof(statistics_t)); } //----------------------------------------------------------------------------- diff --git a/src/app.h b/src/app.h index 72fdbf44..ac516302 100644 --- a/src/app.h +++ b/src/app.h @@ -334,9 +334,6 @@ class app : public IApp, public ah::Scheduler { bool mNetworkConnected; - statistics_t mNrfStat; - statistics_t mCmtStat; - // mqtt PubMqttType mMqtt; bool mMqttReconnect; diff --git a/src/hm/hmInverter.h b/src/hm/hmInverter.h index 17ad6cc5..238e80e4 100644 --- a/src/hm/hmInverter.h +++ b/src/hm/hmInverter.h @@ -173,6 +173,7 @@ class Inverter { alarmCnt = 0; alarmLastId = 0; rssi = -127; + memset(&radioStatistics, 0, sizeof(statistics_t)); } ~Inverter() { diff --git a/src/hm/hmPayload.h b/src/hm/hmPayload.h index 1f6e0468..5689af9d 100644 --- a/src/hm/hmPayload.h +++ b/src/hm/hmPayload.h @@ -31,6 +31,7 @@ typedef struct { bool requested; bool gotFragment; bool rxTmo; + uint32_t sendMillis; } invPayload_t; @@ -247,6 +248,9 @@ class HmPayload { continue; // skip to next inverter } + if((mPayload[iv->id].sendMillis + 500) > millis()) + return; // to fast, wait until packets are received! + if (!mPayload[iv->id].complete) { bool crcPass, pyldComplete, fastNext; @@ -469,6 +473,7 @@ class HmPayload { mPayload[id].requested = false; mPayload[id].ts = *mTimestamp; mPayload[id].rxTmo = setTxTmo; // design: don't start with complete retransmit + mPayload[id].sendMillis = millis(); } IApp *mApp; From 169eea73e6333c0dd839897d4fe6fa5c22c3616f Mon Sep 17 00:00:00 2001 From: lumapu Date: Sun, 1 Oct 2023 12:55:59 +0200 Subject: [PATCH 5/8] 0.7.62 fix compile (prometheus) --- src/hm/hmPayload.h | 6 ++++-- src/hm/hmRadio.h | 8 ++++---- src/web/RestApi.h | 11 +++-------- src/web/web.h | 5 +++-- 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/hm/hmPayload.h b/src/hm/hmPayload.h index 5689af9d..bf3d3c66 100644 --- a/src/hm/hmPayload.h +++ b/src/hm/hmPayload.h @@ -248,8 +248,10 @@ class HmPayload { continue; // skip to next inverter } - if((mPayload[iv->id].sendMillis + 500) > millis()) - return; // to fast, wait until packets are received! + if((IV_HMS == iv->ivGen) || (IV_HMT == iv->ivGen)) { + if((mPayload[iv->id].sendMillis + 500) > millis()) + return; // to fast, wait until packets are received! + } if (!mPayload[iv->id].complete) { bool crcPass, pyldComplete, fastNext; diff --git a/src/hm/hmRadio.h b/src/hm/hmRadio.h index ed9652bc..9e015564 100644 --- a/src/hm/hmRadio.h +++ b/src/hm/hmRadio.h @@ -202,7 +202,7 @@ class HmRadio : public Radio { std::queue mBufCtrl; 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 @@ -219,10 +219,10 @@ class HmRadio : public Radio { if (p.packet[0] != 0x00) { mBufCtrl.push(p); 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 - 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 && + 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 && isLastPackage = true; // response from dev control command } } diff --git a/src/web/RestApi.h b/src/web/RestApi.h index a8843306..a3a0384c 100644 --- a/src/web/RestApi.h +++ b/src/web/RestApi.h @@ -36,10 +36,9 @@ class RestApi { public: RestApi() { mTimezoneOffset = 0; - mHeapFree = 0; - mHeapFreeBlk = 0; - mHeapFrag = 0; - nr = 0; + mHeapFree = 0; + mHeapFreeBlk = 0; + mHeapFrag = 0; } void setup(IApp *app, HMSYSTEM *sys, AsyncWebServer *srv, settings_t *config) { @@ -63,9 +62,6 @@ class RestApi { } void ctrlRequest(JsonObject obj) { - /*char out[128]; - serializeJson(obj, out, 128); - DPRINTLN(DBG_INFO, "RestApi: " + String(out));*/ DynamicJsonDocument json(128); JsonObject dummy = json.as(); if(obj[F("path")] == "ctrl") @@ -726,7 +722,6 @@ class RestApi { uint32_t mTimezoneOffset; uint32_t mHeapFree, mHeapFreeBlk; uint8_t mHeapFrag; - uint16_t nr; }; #endif /*__WEB_API_H__*/ diff --git a/src/web/web.h b/src/web/web.h index 20883c97..99dd5924 100644 --- a/src/web/web.h +++ b/src/web/web.h @@ -715,13 +715,14 @@ class Web { metrics += String(type) + String(topic); // NRF Statistics - stat = mApp->getNrfStatistics(); + // @TODO 2023-10-01: the statistic data is now available per inverter + /*stat = mApp->getNrfStatistics(); metrics += radioStatistic(F("rx_success"), stat->rxSuccess); metrics += radioStatistic(F("rx_fail"), stat->rxFail); metrics += radioStatistic(F("rx_fail_answer"), stat->rxFailNoAnser); metrics += radioStatistic(F("frame_cnt"), stat->frmCnt); metrics += radioStatistic(F("tx_cnt"), stat->txCnt); - metrics += radioStatistic(F("retrans_cnt"), stat->retransmits); + metrics += radioStatistic(F("retrans_cnt"), stat->retransmits);*/ len = snprintf((char *)buffer,maxLen,"%s",metrics.c_str()); // Next is Inverter information From ebb79d1d09904ea8a411c2f95c127f96bcee3ccf Mon Sep 17 00:00:00 2001 From: lumapu Date: Sun, 1 Oct 2023 16:44:20 +0200 Subject: [PATCH 6/8] 0.7.63 * fix NRF24 communication #1200 --- src/CHANGES.md | 3 +++ src/app.cpp | 4 ++-- src/defines.h | 2 +- src/hm/hmPayload.h | 2 +- src/hm/hmRadio.h | 6 ++++-- 5 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/CHANGES.md b/src/CHANGES.md index 893b8a3a..23d17732 100644 --- a/src/CHANGES.md +++ b/src/CHANGES.md @@ -1,5 +1,8 @@ # Development Changes +## 0.7.63 - 2023-10-01 +* fix NRF24 communication #1200 + ## 0.7.62 - 2023-10-01 * fix communication to inverters #1198 * add timeout before payload is tried to process (necessary for HMS/HMT) diff --git a/src/app.cpp b/src/app.cpp index 762c0c34..478cd1e5 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -143,9 +143,9 @@ void app::loop(void) { mPayload.add(iv, p); } mNrfRadio.mBufCtrl.pop(); + processPayload = true; yield(); } - processPayload = true; mMiPayload.process(true); } #if defined(ESP32) @@ -168,9 +168,9 @@ void app::loop(void) { mPayload.add(iv, p); } mCmtRadio.mBufCtrl.pop(); + processPayload = true; yield(); } - processPayload = true; } #endif diff --git a/src/defines.h b/src/defines.h index 98b9a726..5938213a 100644 --- a/src/defines.h +++ b/src/defines.h @@ -13,7 +13,7 @@ //------------------------------------- #define VERSION_MAJOR 0 #define VERSION_MINOR 7 -#define VERSION_PATCH 62 +#define VERSION_PATCH 63 //------------------------------------- typedef struct { diff --git a/src/hm/hmPayload.h b/src/hm/hmPayload.h index bf3d3c66..aa8d1090 100644 --- a/src/hm/hmPayload.h +++ b/src/hm/hmPayload.h @@ -249,7 +249,7 @@ class HmPayload { } if((IV_HMS == iv->ivGen) || (IV_HMT == iv->ivGen)) { - if((mPayload[iv->id].sendMillis + 500) > millis()) + if((mPayload[iv->id].sendMillis + 400) > millis()) return; // to fast, wait until packets are received! } diff --git a/src/hm/hmRadio.h b/src/hm/hmRadio.h index 9e015564..f037763e 100644 --- a/src/hm/hmRadio.h +++ b/src/hm/hmRadio.h @@ -57,6 +57,7 @@ class HmRadio : public Radio { pinMode(irq, INPUT_PULLUP); generateDtuSn(); + DTU_RADIO_ID = ((uint64_t)(((mDtuSn >> 24) & 0xFF) | ((mDtuSn >> 8) & 0xFF00) | ((mDtuSn << 8) & 0xFF0000) | ((mDtuSn << 24) & 0xFF000000)) << 8) | 0x01; #ifdef ESP32 #if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 @@ -81,7 +82,7 @@ class HmRadio : public Radio { mNrf24.enableDynamicPayloads(); mNrf24.setCRCLength(RF24_CRC_16); mNrf24.setAddressWidth(5); - mNrf24.openReadingPipe(1, reinterpret_cast(&mDtuSn)); + mNrf24.openReadingPipe(1, reinterpret_cast(&DTU_RADIO_ID)); // enable all receiving interrupts mNrf24.maskIRQ(false, false, false); @@ -202,7 +203,7 @@ class HmRadio : public Radio { std::queue mBufCtrl; private: - bool getReceived(void) { + inline bool getReceived(void) { bool tx_ok, tx_fail, rx_ready; mNrf24.whatHappened(tx_ok, tx_fail, rx_ready); // resets the IRQ pin to HIGH @@ -262,6 +263,7 @@ class HmRadio : public Radio { return iv->radioId.u64; } + uint64_t DTU_RADIO_ID; uint8_t mRfChLst[RF_CHANNELS]; uint8_t mTxChIdx; uint8_t mRxChIdx; From 1bbe979bcd13adab4f415db926a2f2c766123c7e Mon Sep 17 00:00:00 2001 From: lumapu Date: Mon, 2 Oct 2023 00:34:57 +0200 Subject: [PATCH 7/8] 0.7.64 * moved active power control to modal in `live` view (per inverter) by click on current APC state --- src/CHANGES.md | 3 + src/defines.h | 2 +- src/web/RestApi.h | 22 ++++-- src/web/html/includes/nav.html | 2 +- src/web/html/serial.html | 114 ++------------------------------ src/web/html/setup.html | 2 +- src/web/html/visualization.html | 103 ++++++++++++++++++++++++++++- 7 files changed, 130 insertions(+), 118 deletions(-) diff --git a/src/CHANGES.md b/src/CHANGES.md index 23d17732..1cc2f00a 100644 --- a/src/CHANGES.md +++ b/src/CHANGES.md @@ -1,5 +1,8 @@ # Development Changes +## 0.7.64 - 2023-10-02 +* moved active power control to modal in `live` view (per inverter) by click on current APC state + ## 0.7.63 - 2023-10-01 * fix NRF24 communication #1200 diff --git a/src/defines.h b/src/defines.h index 5938213a..ac96ff43 100644 --- a/src/defines.h +++ b/src/defines.h @@ -13,7 +13,7 @@ //------------------------------------- #define VERSION_MAJOR 0 #define VERSION_MINOR 7 -#define VERSION_PATCH 63 +#define VERSION_PATCH 64 //------------------------------------- typedef struct { diff --git a/src/web/RestApi.h b/src/web/RestApi.h index a3a0384c..39c97532 100644 --- a/src/web/RestApi.h +++ b/src/web/RestApi.h @@ -105,6 +105,8 @@ class RestApi { getIvVersion(root, request->url().substring(22).toInt()); else if(path.substring(0, 19) == "inverter/radiostat/") getIvStatistis(root, request->url().substring(24).toInt()); + else if(path.substring(0, 16) == "inverter/pwrack/") + getIvPowerLimitAck(root, request->url().substring(21).toInt()); else getNotFound(root, F("http://") + request->host() + F("/api/")); } @@ -276,7 +278,7 @@ class RestApi { void getHtmlSystem(AsyncWebServerRequest *request, JsonObject obj) { getSysInfo(request, obj.createNestedObject(F("system"))); getGeneric(request, obj.createNestedObject(F("generic"))); - obj[F("html")] = F("Factory Reset

Reboot"); + obj[F("html")] = F("AhoyFactory Reset

Reboot"); } void getHtmlLogout(AsyncWebServerRequest *request, JsonObject obj) { @@ -322,6 +324,15 @@ class RestApi { obj[F("retransmits")] = iv->radioStatistics.retransmits; } + void getIvPowerLimitAck(JsonObject obj, uint8_t id) { + Inverter<> *iv = mSys->getInverterByPos(id); + if(NULL == iv) { + obj[F("error")] = F("inverter not found!"); + return; + } + obj["ack"] = (bool)iv->powerLimitAck; + } + void getInverterList(JsonObject obj) { JsonArray invArr = obj.createNestedArray(F("inverter")); @@ -389,10 +400,10 @@ class RestApi { ch0[fld] = (0xff != pos) ? ah::round3(iv->getValue(pos, rec)) : 0.0; } } else { - for (uint8_t fld = 0; fld < sizeof(acList); fld++) { - pos = (iv->getPosByChFld(CH0, acList[fld], rec)); - ch0[fld] = (0xff != pos) ? ah::round3(iv->getValue(pos, rec)) : 0.0; - } + for (uint8_t fld = 0; fld < sizeof(acList); fld++) { + pos = (iv->getPosByChFld(CH0, acList[fld], rec)); + ch0[fld] = (0xff != pos) ? ah::round3(iv->getValue(pos, rec)) : 0.0; + } } // DC @@ -652,6 +663,7 @@ class RestApi { jsonOut[F("error")] = F("inverter index invalid: ") + jsonIn[F("id")].as(); return false; } + jsonOut[F("id")] = jsonIn[F("id")]; if(F("power") == jsonIn[F("cmd")]) accepted = iv->setDevControlRequest((jsonIn[F("val")] == 1) ? TurnOn : TurnOff); diff --git a/src/web/html/includes/nav.html b/src/web/html/includes/nav.html index 9d6b822c..91de5047 100644 --- a/src/web/html/includes/nav.html +++ b/src/web/html/includes/nav.html @@ -7,7 +7,7 @@
Live - Serial / Control + Webserial Settings Update diff --git a/src/web/html/serial.html b/src/web/html/serial.html index 442ba24a..10d1769e 100644 --- a/src/web/html/serial.html +++ b/src/web/html/serial.html @@ -12,53 +12,13 @@
-
connected:
+
console active:
Uptime:
-
+
-
-
-

Commands

-
-
-
Select Inverter
-
-
-
-
Power Limit Command
-
- -
-
-
-
Power Limit Value
-
-
-
-
-
-
-
-
Control Inverter
-
- - - -
-
-
-
Ctrl result
-
n/a
-
{#HTML_FOOTER} @@ -84,23 +44,11 @@ parseESP(obj); window.setInterval("getAjax('/api/generic', parseGeneric)", 10000); exeOnce = false; - getAjax("/api/inverter/list", parse); + setTimeOffset(); } } - function parse(root) { - select = document.getElementById('InvID'); - - if(null == root) return; - root = root.inverter; - for(var i = 0; i < root.length; i++) { - inv = root[i]; - var opt = document.createElement('option'); - opt.value = inv.id; - opt.innerHTML = inv.name; - select.appendChild(opt); - } - + function setTimeOffset() { // set time offset for serial console var obj = new Object(); obj.cmd = "serial_utc_offset"; @@ -119,12 +67,12 @@ if (!!window.EventSource) { var source = new EventSource('/events'); source.addEventListener('open', function(e) { - document.getElementById("connected").style.backgroundColor = "#0c0"; + document.getElementById("active").style.backgroundColor = "#0c0"; }, false); source.addEventListener('error', function(e) { if (e.target.readyState != EventSource.OPEN) { - document.getElementById("connected").style.backgroundColor = "#f00"; + document.getElementById("active").style.backgroundColor = "#f00"; } }, false); @@ -135,56 +83,6 @@ }, false); } - - function ctrlCb(obj) { - var e = document.getElementById("result"); - if(obj["success"]) - e.innerHTML = "ok"; - else - e.innerHTML = "Error: " + obj["error"]; - } - - function get_selected_iv() { - var e = document.getElementById("InvID"); - return parseInt(e.value); - } - - const wrapper = document.getElementById('power'); - - wrapper.addEventListener('click', (event) => { - var obj = new Object(); - obj.id = get_selected_iv(); - obj.cmd = "power"; - - switch (event.target.value) { - default: - case "Turn On": - obj.val = 1; - break; - case "Turn Off": - obj.val = 0; - break; - } - - getAjax("/api/ctrl", ctrlCb, "POST", JSON.stringify(obj)); - }); - - document.getElementById("sendpwrlim").addEventListener("click", function() { - var val = parseInt(document.getElementsByName('pwrlimval')[0].value); - var cmd = document.getElementsByName('pwrlimctrl')[0].value; - - if(isNaN(val)) { - document.getElementById("result").textContent = "value is missing"; - return; - } - - var obj = new Object(); - obj.id = get_selected_iv(); - obj.cmd = cmd; - obj.val = val; - getAjax("/api/ctrl", ctrlCb, "POST", JSON.stringify(obj)); - }); - getAjax("/api/generic", parseGeneric); diff --git a/src/web/html/setup.html b/src/web/html/setup.html index 8b28da14..d58aa226 100644 --- a/src/web/html/setup.html +++ b/src/web/html/setup.html @@ -683,7 +683,7 @@ if(!obj["pwd_set"]) e.value = ""; var d = document.getElementById("prot_mask"); - var a = ["Index", "Live", "Serial / Console", "Settings", "Update", "System"]; + var a = ["Index", "Live", "Webserial", "Settings", "Update", "System"]; var el = []; for(var i = 0; i < 6; i++) { var chk = ((obj["prot_mask"] & (1 << i)) == (1 << i)); diff --git a/src/web/html/visualization.html b/src/web/html/visualization.html index d790147c..24153389 100644 --- a/src/web/html/visualization.html +++ b/src/web/html/visualization.html @@ -20,6 +20,7 @@ var mIvHtml = []; var mNum = 0; var total = Array(5).fill(0); + var tPwrAck; function parseGeneric(obj) { if(true == exeOnce){ @@ -113,8 +114,10 @@ ml("div", {class: "col mx-2 mx-md-1"}, ml("span", { class: "pointer", onclick: function() { getAjax("/api/inverter/version/" + obj.id, parseIvVersion); }}, obj.name)), - ml("div", {class: "col a-c d-none d-sm-block"}, "Active Power Control: " + pwrLimit), - ml("div", {class: "col a-c d-block d-sm-none"}, "APC: " + pwrLimit), + ml("div", {class: "col a-c", onclick: function() {limitModal(obj)}}, [ + ml("span", {class: "d-none d-sm-block pointer"}, "Active Power Control: " + pwrLimit), + ml("span", {class: "d-block d-sm-none pointer"}, "APC: " + pwrLimit) + ]), ml("div", {class: "col a-c"}, ml("span", { class: "pointer", onclick: function() { getAjax("/api/inverter/alarm/" + obj.id, parseIvAlarm); }}, ("Alarms: " + obj.alarm_cnt))), @@ -305,6 +308,102 @@ modal("Radio statistics for inverter " + obj.name, ml("div", {}, html)); } + function limitModal(obj) { + var opt = [["pct", "%"], ["watt", "W"]]; + var html = ml("div", {}, [ + ml("div", {class: "row mb-3"}, [ + ml("div", {class: "col-12 col-sm-5 my-2"}, "Limit Value"), + ml("div", {class: "col-8 col-sm-5"}, ml("input", {name: "limit", type: "number"}, "")), + ml("div", {class: "col-4 col-sm-2"}, sel("type", opt, "pct")) + ]), + ml("div", {class: "row mb-3"}, [ + ml("div", {class: "col-8 col-sm-5"}, "Keep limit over inverter restart"), + ml("div", {class: "col-4 col-sm-7"}, ml("input", {type: "checkbox", name: "keep"})) + ]), + ml("div", {class: "row my-3"}, + ml("div", {class: "col a-r"}, ml("input", {type: "button", value: "Apply", class: "btn", onclick: function() { + applyLimit(obj.id); + }}, null)) + ), + ml("div", {class: "row my-4"}, [ + ml("div", {class: "col-12 col-sm-5 my-2"}, "Control"), + ml("div", {class: "col col-sm-7 a-r"}, [ + ml("input", {type: "button", value: "restart", class: "btn", onclick: function() { + applyCtrl(obj.id, "restart"); + }}, null), + ml("input", {type: "button", value: "turn off", class: "btn mx-1", onclick: function() { + applyCtrl(obj.id, "power", 0); + }}, null), + ml("input", {type: "button", value: "turn on", class: "btn", onclick: function() { + applyCtrl(obj.id, "power", 1); + }}, null) + ]) + ]), + ml("div", {class: "row mt-1"}, [ + ml("div", {class: "col-12 col-sm-5 my-2"}, "Result"), + ml("div", {class: "col-sm-7 my-2"}, ml("span", {name: "pwrres"}, "-")) + ]) + ]); + modal("Active Power Control for inverter " + obj.name, html); + } + + function applyLimit(id) { + var cmd = "limit_"; + if(!document.getElementsByName("keep")[0].checked) + cmd += "non"; + cmd += "persistent_"; + if(document.getElementsByName("type")[0].value == "pct") + cmd += "relative"; + else + cmd += "absolute"; + + var val = document.getElementsByName("limit")[0].value; + if(isNaN(val)) + val = 100; + + var obj = new Object(); + obj.id = id; + obj.cmd = cmd; + obj.val = val; + getAjax("/api/ctrl", ctrlCb, "POST", JSON.stringify(obj)); + } + + function applyCtrl(id, cmd, val=0) { + var obj = new Object(); + obj.id = id; + obj.cmd = cmd; + obj.val = val; + getAjax("/api/ctrl", ctrlCb2, "POST", JSON.stringify(obj)); + } + + function ctrlCb(obj) { + var e = document.getElementsByName("pwrres")[0]; + if(obj.success) { + e.innerHTML = "received command, waiting for inverter acknowledge ..."; + tPwrAck = window.setInterval("getAjax('/api/inverter/pwrack/" + obj.id + "', updatePwrAck)", 1000); + } + else + e.innerHTML = "Error: " + obj["error"]; + } + + function ctrlCb2(obj) { + var e = document.getElementsByName("pwrres")[0]; + if(obj.success) + e.innerHTML = "command received"; + else + e.innerHTML = "Error: " + obj["error"]; + } + + function updatePwrAck(obj) { + if(!obj.ack) + return; + var e = document.getElementsByName("pwrres")[0]; + clearInterval(tPwrAck); + if(null == e) + return; + e.innerHTML = "inverter acknowledged active power control command"; + } + function parse(obj) { if(null != obj) { parseGeneric(obj["generic"]); From 50ca076f55d0697c3da526de9d65a1ea52de70da Mon Sep 17 00:00:00 2001 From: lumapu Date: Mon, 2 Oct 2023 10:45:48 +0200 Subject: [PATCH 8/8] 0.7.65 * MI control command review #1197 --- src/CHANGES.md | 3 +++ src/defines.h | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/CHANGES.md b/src/CHANGES.md index 1cc2f00a..1b6ec262 100644 --- a/src/CHANGES.md +++ b/src/CHANGES.md @@ -1,5 +1,8 @@ # Development Changes +## 0.7.65 - 2023-10-02 +* MI control command review #1197 + ## 0.7.64 - 2023-10-02 * moved active power control to modal in `live` view (per inverter) by click on current APC state diff --git a/src/defines.h b/src/defines.h index ac96ff43..f5543752 100644 --- a/src/defines.h +++ b/src/defines.h @@ -13,7 +13,7 @@ //------------------------------------- #define VERSION_MAJOR 0 #define VERSION_MINOR 7 -#define VERSION_PATCH 64 +#define VERSION_PATCH 65 //------------------------------------- typedef struct {