Browse Source

0.5.14 Bug fix mqtt payload & get FWVersion

Changes 0.5.14.
- bug fix in mqtt payload handling thx to @klahus1 and silversurfer
- small improvements in code styling
- refactoring to get have the option to implement different parse for InfoCommands
- Get FWVersion by REST API call
- Display FWVersion in webui (only after REST API call)
pull/157/head^2
Andreas Schiffler 2 years ago
parent
commit
3e1b120b74
  1. 80
      tools/esp8266/app.cpp
  2. 4
      tools/esp8266/app.h
  3. 2
      tools/esp8266/defines.h
  4. 25
      tools/esp8266/hmDefines.h
  5. 86
      tools/esp8266/hmInverter.h
  6. 18
      tools/esp8266/hmRadio.h
  7. 55
      tools/esp8266/web.cpp

80
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;
}

4
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);

2
tools/esp8266/defines.h

@ -13,7 +13,7 @@
//-------------------------------------
#define VERSION_MAJOR 0
#define VERSION_MINOR 5
#define VERSION_PATCH 13
#define VERSION_PATCH 14
//-------------------------------------

25
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 },

86
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<RECORDTYPE>[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<RECORDTYPE>[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;
}
}
};

18
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);

55
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}");
}

Loading…
Cancel
Save