diff --git a/tools/esp8266/app.cpp b/tools/esp8266/app.cpp index a0a01229..125a04ab 100644 --- a/tools/esp8266/app.cpp +++ b/tools/esp8266/app.cpp @@ -84,6 +84,29 @@ void app::loop(void) { Inverter<> *iv = mSys->findInverter(&p->packet[1]); if(NULL != iv && p->packet[0] == (TX_REQ_INFO + 0x80)) { // response from get information command DPRINTLN(DBG_DEBUG, F("Response from info request received")); + uint8_t *pid = &p->packet[9]; + if (*pid == 0x00) + { + DPRINT(DBG_DEBUG, "fragment number zero received and ignored"); + } + else + { + if ((*pid & 0x7F) < 5) + { + memcpy(mPayload[iv->id].data[(*pid & 0x7F) - 1], &p->packet[10], len - 11); + mPayload[iv->id].len[(*pid & 0x7F) - 1] = len - 11; + } + + if ((*pid & 0x80) == 0x80) + { // Last packet + if ((*pid & 0x7f) > mPayload[iv->id].maxPackId) + { + mPayload[iv->id].maxPackId = (*pid & 0x7f); + if (*pid > 0x81) + mLastPacketId = *pid; + } + } + } switch (mSys->InfoCmd){ case InverterDevInform_Simple: { @@ -94,7 +117,6 @@ void app::loop(void) { case InverterDevInform_All: { DPRINT(DBG_INFO, "Response from inform all\n"); - mSys->InfoCmd = RealTimeRunData_Debug; // Set back to default break; } case GetLossRate: @@ -117,23 +139,6 @@ void app::loop(void) { } case RealTimeRunData_Debug: { - uint8_t *pid = &p->packet[9]; - if (*pid == 0x00) { - DPRINT(DBG_DEBUG, "fragment number zero received and ignored"); - } else { - if((*pid & 0x7F) < 5) { - memcpy(mPayload[iv->id].data[(*pid & 0x7F) - 1], &p->packet[10], len-11); - mPayload[iv->id].len[(*pid & 0x7F) - 1] = len-11; - } - - if((*pid & 0x80) == 0x80) { // Last packet - if((*pid & 0x7f) > mPayload[iv->id].maxPackId) { - mPayload[iv->id].maxPackId = (*pid & 0x7f); - if(*pid > 0x81) - mLastPacketId = *pid; - } - } - } break; } } @@ -175,7 +180,7 @@ void app::loop(void) { if(rxRdy) { - processPayload(true); + processPayload(true,mSys->InfoCmd); } } @@ -261,7 +266,7 @@ void app::loop(void) { if(NULL != iv) { if(!mPayload[iv->id].complete) - processPayload(false); + processPayload(false,mSys->InfoCmd); if(!mPayload[iv->id].complete) { mRxFailed++; @@ -271,13 +276,7 @@ void app::loop(void) { } } - // reset payload data - memset(mPayload[iv->id].len, 0, MAX_PAYLOAD_ENTRIES); - mPayload[iv->id].retransmits = 0; - mPayload[iv->id].maxPackId = 0; - mPayload[iv->id].complete = false; - mPayload[iv->id].requested = true; - mPayload[iv->id].ts = mTimestamp; + resetPayload(iv); yield(); if(mConfig.serialDebug) @@ -335,6 +334,9 @@ bool app::buildPayload(uint8_t id) { //----------------------------------------------------------------------------- void app::processPayload(bool retransmit) { + processPayload(retransmit, RealTimeRunData_Debug); +} +void app::processPayload(bool retransmit, uint8_t cmd = RealTimeRunData_Debug) { // cmd value decides which parser is used to decode payload #ifdef __MQTT_AFTER_RX__ boolean doMQTT = false; @@ -390,12 +392,14 @@ void app::processPayload(bool retransmit) { mSys->Radio.dumpBuf(NULL, payload, offs); } mRxSuccess++; + mSys->InfoCmd = RealTimeRunData_Debug; // On success set back to default + iv->getAssignment(cmd); // choose the parser for(uint8_t i = 0; i < iv->listLen; i++) { - iv->addValue(i, payload); + iv->addValue(i, payload,cmd); // cmd value decides which parser is used to decode payload yield(); } - iv->doCalculations(); + iv->doCalculations(cmd); // cmd value decides which parser is used to decode payload #ifdef __MQTT_AFTER_RX__ doMQTT = true; @@ -444,12 +448,10 @@ void app::cbMqtt(char* topic, byte* payload, unsigned int length) { iv->powerLimit[1] = AbsolutNonPersistent; else iv->powerLimit[1] = std::stoi(token); - DPRINTLN(DBG_VERBOSE, F("iv->powerLimit[1]=") + String(iv->powerLimit[1])); - DPRINTLN(DBG_VERBOSE, F("length=") + String(length)); if (length<=5){ // if (std::stoi((char*)payload) > 0) more error handling powerlimit needed? if (iv->powerLimit[1] >= AbsolutNonPersistent && iv->powerLimit[1] <= RelativPersistent){ iv->devControlCmd = ActivePowerContr; - iv->powerLimit[0] = std::stoi((char*)payload); + iv->powerLimit[0] = std::stoi(std::string((char*)payload, (unsigned int)length)); // THX to @silversurfer if (iv->powerLimit[1] & 0x0001) DPRINTLN(DBG_INFO, F("Power limit for inverter ") + String(iv->id) + F(" set to ") + String(iv->powerLimit[0]) + F("%") ); else @@ -517,7 +519,7 @@ String app::getStatistics(void) { iv = mSys->getInverterByPos(i); if(NULL != iv) { bool avail = true; - content += F("Inverter '") + String(iv->name) + F("' is "); + content += F("Inverter '") + String(iv->name) + F(" (FW-Version: ") + String(iv->fwVersion) +F(")") + F("' is "); if(!iv->isAvailable(mTimestamp)) { content += F("not "); avail = false; @@ -936,3 +938,15 @@ void app::setupMqtt(void) { } } } + +//----------------------------------------------------------------------------- +void app::resetPayload(Inverter<>* iv) +{ + // reset payload data + memset(mPayload[iv->id].len, 0, MAX_PAYLOAD_ENTRIES); + mPayload[iv->id].retransmits = 0; + mPayload[iv->id].maxPackId = 0; + mPayload[iv->id].complete = false; + mPayload[iv->id].requested = true; + mPayload[iv->id].ts = mTimestamp; +} \ No newline at end of file diff --git a/tools/esp8266/app.h b/tools/esp8266/app.h index ef1737e3..e7e9ab61 100644 --- a/tools/esp8266/app.h +++ b/tools/esp8266/app.h @@ -69,6 +69,7 @@ class app { void handleIntr(void); void cbMqtt(char* topic, byte* payload, unsigned int length); void saveValues(void); + void resetPayload(Inverter<>* iv); String getStatistics(void); String getLiveData(void); String getJson(void); @@ -147,9 +148,10 @@ class app { void loadDefaultConfig(void); void loadEEpconfig(void); void setupMqtt(void); - + bool buildPayload(uint8_t id); void processPayload(bool retransmit); + void processPayload(bool retransmit, uint8_t cmd); void sendMqttDiscoveryConfig(void); const char* getFieldDeviceClass(uint8_t fieldId); diff --git a/tools/esp8266/defines.h b/tools/esp8266/defines.h index 0cd29fca..339ef89e 100644 --- a/tools/esp8266/defines.h +++ b/tools/esp8266/defines.h @@ -13,7 +13,7 @@ //------------------------------------- #define VERSION_MAJOR 0 #define VERSION_MINOR 5 -#define VERSION_PATCH 13 +#define VERSION_PATCH 14 //------------------------------------- diff --git a/tools/esp8266/hmDefines.h b/tools/esp8266/hmDefines.h index 1450379b..e69bc071 100644 --- a/tools/esp8266/hmDefines.h +++ b/tools/esp8266/hmDefines.h @@ -17,15 +17,15 @@ union serial_u { // units -enum {UNIT_V = 0, UNIT_A, UNIT_W, UNIT_WH, UNIT_KWH, UNIT_HZ, UNIT_C, UNIT_PCT, UNIT_VA, UNIT_ALARM_MES_ID}; +enum {UNIT_V = 0, UNIT_A, UNIT_W, UNIT_WH, UNIT_KWH, UNIT_HZ, UNIT_C, UNIT_PCT, UNIT_VA, UNIT_NONE}; const char* const units[] = {"V", "A", "W", "Wh", "kWh", "Hz", "°C", "%","VAr",""}; // field types enum {FLD_UDC = 0, FLD_IDC, FLD_PDC, FLD_YD, FLD_YW, FLD_YT, - FLD_UAC, FLD_IAC, FLD_PAC, FLD_F, FLD_T, FLD_PCT, FLD_EFF, FLD_IRR, FLD_PRA,FLD_ALARM_MES_ID}; + FLD_UAC, FLD_IAC, FLD_PAC, FLD_F, FLD_T, FLD_PCT, FLD_EFF, FLD_IRR, FLD_PRA,FLD_ALARM_MES_ID,FLD_FW_VERSION,FLD_FW_BUILD_YEAR,FLD_FW_BUILD_MONTH_DAY,FLD_HW_ID}; const char* const fields[] = {"U_DC", "I_DC", "P_DC", "YieldDay", "YieldWeek", "YieldTotal", - "U_AC", "I_AC", "P_AC", "Freq", "Temp", "Pct", "Efficiency", "Irradiation","P_ACr","ALARM_MES_ID"}; + "U_AC", "I_AC", "P_AC", "Freq", "Temp", "Pct", "Efficiency", "Irradiation","P_ACr","ALARM_MES_ID","FWVersion","FWBuildYear","FWBuildMonthDay","HWPartId"}; // mqtt discovery device classes enum {DEVICE_CLS_NONE = 0, DEVICE_CLS_CURRENT, DEVICE_CLS_ENERGY, DEVICE_CLS_PWR, DEVICE_CLS_VOLTAGE, DEVICE_CLS_FREQ, DEVICE_CLS_TEMP}; @@ -81,6 +81,19 @@ typedef struct { * (complete payload in buffer) * */ +//------------------------------------- +// HM-Series +//------------------------------------- +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_HW_ID, UNIT_NONE, CH0, 8, 2, 1 } +}; +#define HMINFO_LIST_LEN (sizeof(InfoAssignment) / sizeof(byteAssign_t)) + + + //------------------------------------- // HM300, HM350, HM400 //------------------------------------- @@ -98,7 +111,7 @@ const byteAssign_t hm1chAssignment[] = { { FLD_PRA, UNIT_VA, CH0, 20, 2, 10 }, { FLD_F, UNIT_HZ, CH0, 16, 2, 100 }, { FLD_T, UNIT_C, CH0, 26, 2, 10 }, - { FLD_ALARM_MES_ID, UNIT_ALARM_MES_ID, CH0, 28, 2, 1 }, + { FLD_ALARM_MES_ID, UNIT_NONE, CH0, 28, 2, 1 }, { FLD_PDC, UNIT_W, CH0, CALC_PDC_CH0, 0, CMD_CALC }, { FLD_EFF, UNIT_PCT, CH0, CALC_EFF_CH0, 0, CMD_CALC } }; @@ -129,7 +142,7 @@ const byteAssign_t hm2chAssignment[] = { { FLD_PRA, UNIT_VA, CH0, 32, 2, 10 }, { FLD_F, UNIT_HZ, CH0, 28, 2, 100 }, { FLD_T, UNIT_C, CH0, 38, 2, 10 }, - { FLD_ALARM_MES_ID, UNIT_ALARM_MES_ID, CH0, 40, 2, 1 }, + { FLD_ALARM_MES_ID, UNIT_NONE, CH0, 40, 2, 1 }, { FLD_YD, UNIT_WH, CH0, CALC_YD_CH0, 0, CMD_CALC }, { FLD_YT, UNIT_KWH, CH0, CALC_YT_CH0, 0, CMD_CALC }, { FLD_PDC, UNIT_W, CH0, CALC_PDC_CH0, 0, CMD_CALC }, @@ -178,7 +191,7 @@ const byteAssign_t hm4chAssignment[] = { { FLD_F, UNIT_HZ, CH0, 48, 2, 100 }, { FLD_PCT, UNIT_PCT, CH0, 56, 2, 10 }, { FLD_T, UNIT_C, CH0, 58, 2, 10 }, - { FLD_ALARM_MES_ID, UNIT_ALARM_MES_ID, CH0, 60, 2, 1 }, + { FLD_ALARM_MES_ID, UNIT_NONE, CH0, 60, 2, 1 }, { FLD_YD, UNIT_WH, CH0, CALC_YD_CH0, 0, CMD_CALC }, { FLD_YT, UNIT_KWH, CH0, CALC_YT_CH0, 0, CMD_CALC }, { FLD_PDC, UNIT_W, CH0, CALC_PDC_CH0, 0, CMD_CALC }, diff --git a/tools/esp8266/hmInverter.h b/tools/esp8266/hmInverter.h index ca1e1bb6..32a7505c 100644 --- a/tools/esp8266/hmInverter.h +++ b/tools/esp8266/hmInverter.h @@ -70,6 +70,7 @@ class Inverter { byteAssign_t* assign; // type of inverter uint8_t listLen; // length of assignments uint16_t alarmMesIndex; // Last recorded Alarm Message Index + uint16_t fwVersion; // Firmware Version from Info Command Request uint16_t powerLimit[2]; // limit power output uint8_t devControlCmd; // carries the requested cmd bool devControlRequest; // true if change needed @@ -87,6 +88,7 @@ class Inverter { powerLimit[1] = 0x0000; // devControlRequest = false; devControlCmd = 0xff; + fwVersion = 0; } ~Inverter() { @@ -128,7 +130,7 @@ class Inverter { return assign[pos].ch; } - void addValue(uint8_t pos, uint8_t buf[]) { + void addValue(uint8_t pos, uint8_t buf[],uint8_t cmd) { DPRINTLN(DBG_VERBOSE, F("hmInverter.h:addValue")); uint8_t ptr = assign[pos].start; uint8_t end = ptr + assign[pos].num; @@ -142,9 +144,18 @@ class Inverter { record[pos] = (RECORDTYPE)(val) / (RECORDTYPE)(div); } - // get last alarm message index and save it in the inverter instance - if (getPosByChFld(0, FLD_ALARM_MES_ID) == pos){ - alarmMesIndex = record[pos]; + if (cmd == RealTimeRunData_Debug) { + // get last alarm message index and save it in the inverter object + if (getPosByChFld(0, FLD_ALARM_MES_ID) == pos){ + alarmMesIndex = record[pos]; + } + } + if (cmd == InverterDevInform_All) { + // get at least the firmware version and save it to the inverter object + if (getPosByChFld(0, FLD_FW_VERSION) == pos){ + fwVersion = record[pos]; + DPRINT(DBG_DEBUG, F("Inverter FW-Version: ") + String(fwVersion)); + } } } @@ -153,13 +164,16 @@ class Inverter { return record[pos]; } - void doCalculations(void) { + void doCalculations(uint8_t cmd=RealTimeRunData_Debug) { DPRINTLN(DBG_VERBOSE, F("hmInverter.h:doCalculations")); - for(uint8_t i = 0; i < listLen; i++) { - if(CMD_CALC == assign[i].div) { - record[i] = calcFunctions[assign[i].start].func(this, assign[i].num); + getAssignment(cmd); + if (cmd == RealTimeRunData_Debug){ + for(uint8_t i = 0; i < listLen; i++) { + if(CMD_CALC == assign[i].div) { + record[i] = calcFunctions[assign[i].start].func(this, assign[i].num); + } + yield(); } - yield(); } } @@ -182,6 +196,36 @@ class Inverter { return ts; } + void getAssignment(uint8_t cmd=RealTimeRunData_Debug) { + DPRINTLN(DBG_VERBOSE, F("hmInverter.h:getAssignment")); + if(cmd == RealTimeRunData_Debug){ + if(INV_TYPE_1CH == type) { + listLen = (uint8_t)(HM1CH_LIST_LEN); + assign = (byteAssign_t*)hm1chAssignment; + channels = 1; + } + else if(INV_TYPE_2CH == type) { + listLen = (uint8_t)(HM2CH_LIST_LEN); + assign = (byteAssign_t*)hm2chAssignment; + channels = 2; + } + else if(INV_TYPE_4CH == type) { + listLen = (uint8_t)(HM4CH_LIST_LEN); + assign = (byteAssign_t*)hm4chAssignment; + channels = 4; + } + else { + listLen = 0; + channels = 0; + assign = NULL; + } + } + if(cmd == InverterDevInform_All){ + listLen = (uint8_t)(HMINFO_LIST_LEN); + assign = (byteAssign_t*)InfoAssignment; + } + } + private: void toRadioId(void) { DPRINTLN(DBG_VERBOSE, F("hmInverter.h:toRadioId")); @@ -192,30 +236,6 @@ class Inverter { radioId.b[1] = serial.b[3]; radioId.b[0] = 0x01; } - - void getAssignment(void) { - DPRINTLN(DBG_VERBOSE, F("hmInverter.h:getAssignment")); - if(INV_TYPE_1CH == type) { - listLen = (uint8_t)(HM1CH_LIST_LEN); - assign = (byteAssign_t*)hm1chAssignment; - channels = 1; - } - else if(INV_TYPE_2CH == type) { - listLen = (uint8_t)(HM2CH_LIST_LEN); - assign = (byteAssign_t*)hm2chAssignment; - channels = 2; - } - else if(INV_TYPE_4CH == type) { - listLen = (uint8_t)(HM4CH_LIST_LEN); - assign = (byteAssign_t*)hm4chAssignment; - channels = 4; - } - else { - listLen = 0; - channels = 0; - assign = NULL; - } - } }; diff --git a/tools/esp8266/hmRadio.h b/tools/esp8266/hmRadio.h index 957ace68..57d95ec8 100644 --- a/tools/esp8266/hmRadio.h +++ b/tools/esp8266/hmRadio.h @@ -164,20 +164,10 @@ class HmRadio { mTxBuf[10] = cmd; // cmd --> 0x0b => Type_ActivePowerContr, 0 on, 1 off, 2 restart, 12 reactive power, 13 power factor mTxBuf[10 + (++cnt)] = 0x00; if (cmd >= ActivePowerContr && cmd <= PFSet){ - // 4 bytes control data - // Power Limit fix point 10 eg. 30 W --> 0d300 = 0x012c - // -1 = 0xffff --> no limit - uint16_t powerLimit = data[0]; - uint16_t powerLimitSetting = data[1]; - if (powerLimit == 0xffff){ - powerLimit &= 0xffff; // ToDo: unlimit value is needed and is inverter specific! --> get it via RF from inverter or via user interface - } else { - powerLimit *= 10; // will overwrite the data bc it is a pointer - } - mTxBuf[10 + (++cnt)] = (powerLimit >> 8) & 0xff; // power limit - mTxBuf[10 + (++cnt)] = (powerLimit ) & 0xff; // power limit - mTxBuf[10 + (++cnt)] = (powerLimitSetting >> 8) & 0xff; // setting for persistens handling - mTxBuf[10 + (++cnt)] = (powerLimitSetting ) & 0xff; // setting for persistens handling + mTxBuf[10 + (++cnt)] = ((data[0] * 10) >> 8) & 0xff; // power limit + mTxBuf[10 + (++cnt)] = ((data[0] * 10) ) & 0xff; // power limit + mTxBuf[10 + (++cnt)] = ((data[1] ) >> 8) & 0xff; // setting for persistens handlings + mTxBuf[10 + (++cnt)] = ((data[1] ) ) & 0xff; // setting for persistens handling } // crc control data uint16_t crc = crc16(&mTxBuf[10], cnt+1); diff --git a/tools/esp8266/web.cpp b/tools/esp8266/web.cpp index ce406a86..77339653 100644 --- a/tools/esp8266/web.cpp +++ b/tools/esp8266/web.cpp @@ -417,9 +417,9 @@ void web::showJson(void) { mWeb->send(200, F("application/json"), mMain->getJson()); } - //----------------------------------------------------------------------------- -void web::showWebApi(void) { +void web::showWebApi(void) +{ DPRINTLN(DBG_VERBOSE, F("web::showWebApi")); DPRINTLN(DBG_DEBUG, mWeb->arg("plain")); const size_t capacity = 200; // Use arduinojson.org/assistant to compute the capacity. @@ -429,39 +429,50 @@ void web::showWebApi(void) { deserializeJson(response, mWeb->arg("plain")); // ToDo: error handling for payload uint8_t iv_id = response["inverter"]; - if (response["tx_request"] == (uint8_t)TX_REQ_INFO) { - mMain->mSys->InfoCmd = response["cmd"]; - if (mMain->mSys->InfoCmd == AlarmData){ - Inverter<> *iv = mMain->mSys->getInverterByPos(iv_id); - if (NULL != iv){ + Inverter<> *iv = mMain->mSys->getInverterByPos(iv_id); + if (NULL != iv) + { + if (response["tx_request"] == (uint8_t)TX_REQ_INFO) + { + mMain->mSys->InfoCmd = response["cmd"]; + mMain->resetPayload(iv); // start request from new + // process payload from web request corresponding to the cmd + if (mMain->mSys->InfoCmd == AlarmData) iv->alarmMesIndex = response["payload"]; - } + DPRINTLN(DBG_INFO, F("Will make tx-request 0x15 with subcmd ") + String(mMain->mSys->InfoCmd) + F(" and payload ") + String(response["payload"])); } - DPRINTLN(DBG_INFO, F("Will make tx-request 0x15 with subcmd ") + String(mMain->mSys->InfoCmd) + F(" and payload ") + String(response["payload"])); - } - if (response["tx_request"] == (uint8_t)TX_REQ_DEVCONTROL){ - if(response["cmd"] == (uint8_t)ActivePowerContr){ - if (iv_id >= 0 && iv_id <= MAX_NUM_INVERTERS){ - Inverter<> *iv = mMain->mSys->getInverterByPos(iv_id); + + + if (response["tx_request"] == (uint8_t)TX_REQ_DEVCONTROL) + { + if (response["cmd"] == (uint8_t)ActivePowerContr) + { uint16_t webapiPayload = response["payload"]; uint16_t webapiPayload2 = response["payload2"]; - if (webapiPayload > 0 && webapiPayload < 10000){ + if (webapiPayload > 0 && webapiPayload < 10000) + { iv->devControlCmd = ActivePowerContr; iv->powerLimit[0] = webapiPayload; - if (webapiPayload2 > 0){ + if (webapiPayload2 > 0) + { iv->powerLimit[1] = webapiPayload2; // dev option, no sanity check - } else { // if not set, set it to 0x0000 default + } + else + { // if not set, set it to 0x0000 default iv->powerLimit[1] = AbsolutNonPersistent; // payload will be seted temporay in Watt absolut } - if (iv->powerLimit[1] & 0x0001 ){ - DPRINTLN(DBG_INFO, F("Power limit for inverter ") + String(iv->id) + F(" set to ") + String(iv->powerLimit[0]) + F("% via REST API") ); - } else { - DPRINTLN(DBG_INFO, F("Power limit for inverter ") + String(iv->id) + F(" set to ") + String(iv->powerLimit[0]) + F("W via REST API") ); + if (iv->powerLimit[1] & 0x0001) + { + DPRINTLN(DBG_INFO, F("Power limit for inverter ") + String(iv->id) + F(" set to ") + String(iv->powerLimit[0]) + F("% via REST API")); + } + else + { + DPRINTLN(DBG_INFO, F("Power limit for inverter ") + String(iv->id) + F(" set to ") + String(iv->powerLimit[0]) + F("W via REST API")); } iv->devControlRequest = true; // queue it in the request loop } } } } - mWeb->send ( 200, "text/json", "{success:true}" ); + mWeb->send(200, "text/json", "{success:true}"); }