From 9a09b5f4f860c4d4c4ba4da5649863583c6ec1b6 Mon Sep 17 00:00:00 2001 From: lumapu Date: Sun, 17 Sep 2023 01:55:45 +0200 Subject: [PATCH] 0.7.54 * added active power control in `W` to live view #201, #673 * updated docu, active power control related #706 * added current AC-Power to `index` page and removed version #763 * improved statistic data, moved to entire struct * removed `/api/statistics` endpoint from REST-API --- User_Manual.md | 2 ++ src/CHANGES.md | 7 +++++ src/app.cpp | 17 +++++----- src/app.h | 16 +++++----- src/appInterface.h | 10 ++---- src/defines.h | 4 ++- src/hm/hmRadio.h | 17 ++++------ src/hms/hmsRadio.h | 12 +++---- src/platformio.ini | 2 +- src/web/RestApi.h | 55 +++++++++++++-------------------- src/web/html/index.html | 9 +++--- src/web/html/system.html | 49 ++++++++++++++++------------- src/web/html/visualization.html | 10 +++++- 13 files changed, 107 insertions(+), 103 deletions(-) diff --git a/User_Manual.md b/User_Manual.md index 6be66f69..b4ef8666 100644 --- a/User_Manual.md +++ b/User_Manual.md @@ -124,6 +124,8 @@ The AhoyDTU subscribes on following topics: 👆 `` is the number of the specific inverter in the setup page. +**NOTE:** Some users reported that a limit below 20W results in 0W output of the inverter. To reenable the inverter a reboot command was need to be sent, because a new limit with 100% didn't work. + ### Inverter restart ```mqtt diff --git a/src/CHANGES.md b/src/CHANGES.md index f1f348c2..347f9658 100644 --- a/src/CHANGES.md +++ b/src/CHANGES.md @@ -1,5 +1,12 @@ # Development Changes +## 0.7.54 - 2023-09-16 +* added active power control in `W` to live view #201, #673 +* updated docu, active power control related #706 +* added current AC-Power to `index` page and removed version #763 +* improved statistic data, moved to entire struct +* removed `/api/statistics` endpoint from REST-API + ## 0.7.53 - 2023-09-16 * fix ePaper / display night behaviour #1151 * fix ESP8266 compile error diff --git a/src/app.cpp b/src/app.cpp index 7733e885..396d6556 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -34,12 +34,12 @@ void app::setup() { DBGPRINTLN(F("false")); if(mConfig->nrf.enabled) { - mNrfRadio.setup(mConfig->nrf.amplifierPower, mConfig->nrf.pinIrq, mConfig->nrf.pinCe, mConfig->nrf.pinCs, mConfig->nrf.pinSclk, mConfig->nrf.pinMosi, mConfig->nrf.pinMiso); + mNrfRadio.setup(&mNrfStat, mConfig->nrf.amplifierPower, mConfig->nrf.pinIrq, mConfig->nrf.pinCe, mConfig->nrf.pinCs, mConfig->nrf.pinSclk, mConfig->nrf.pinMosi, mConfig->nrf.pinMiso); mNrfRadio.enableDebug(); } #if defined(ESP32) if(mConfig->cmt.enabled) { - mCmtRadio.setup(mConfig->cmt.pinSclk, mConfig->cmt.pinSdio, mConfig->cmt.pinCsb, mConfig->cmt.pinFcsb, false); + mCmtRadio.setup(&mCmtStat, mConfig->cmt.pinSclk, mConfig->cmt.pinSdio, mConfig->cmt.pinCsb, mConfig->cmt.pinFcsb, false); mCmtRadio.enableDebug(); } #endif @@ -62,17 +62,17 @@ void app::setup() { mSys.setup(&mTimestamp); mSys.addInverters(&mConfig->inst); if (mConfig->nrf.enabled) { - mPayload.setup(this, &mSys, &mNrfRadio, &mStat, mConfig->nrf.maxRetransPerPyld, &mTimestamp); + mPayload.setup(this, &mSys, &mNrfRadio, &mNrfStat, mConfig->nrf.maxRetransPerPyld, &mTimestamp); mPayload.enableSerialDebug(mConfig->serial.debug); mPayload.addPayloadListener(std::bind(&app::payloadEventListener, this, std::placeholders::_1, std::placeholders::_2)); - mMiPayload.setup(this, &mSys, &mNrfRadio, &mStat, mConfig->nrf.maxRetransPerPyld, &mTimestamp); + mMiPayload.setup(this, &mSys, &mNrfRadio, &mNrfStat, mConfig->nrf.maxRetransPerPyld, &mTimestamp); mMiPayload.enableSerialDebug(mConfig->serial.debug); mMiPayload.addPayloadListener(std::bind(&app::payloadEventListener, this, std::placeholders::_1, std::placeholders::_2)); } #if defined(ESP32) - mHmsPayload.setup(this, &mSys, &mCmtRadio, &mStat, 5, &mTimestamp); + mHmsPayload.setup(this, &mSys, &mCmtRadio, &mCmtStat, 5, &mTimestamp); mHmsPayload.enableSerialDebug(mConfig->serial.debug); mHmsPayload.addPayloadListener(std::bind(&app::payloadEventListener, this, std::placeholders::_1, std::placeholders::_2)); #endif @@ -133,7 +133,7 @@ void app::loop(void) { DBGPRINT(F("dBm | ")); ah::dumpBuf(p->packet, p->len); } - mStat.frmCnt++; + mNrfStat.frmCnt++; Inverter<> *iv = mSys.findInverter(&p->packet[1]); if (NULL != iv) { @@ -160,7 +160,7 @@ void app::loop(void) { DBGPRINT(F("dBm | ")); ah::dumpBuf(&p->data[1], p->data[0]); } - mStat.frmCnt++; + mCmtStat.frmCnt++; Inverter<> *iv = mSys.findInverter(&p->data[2]); if(NULL != iv) { @@ -525,7 +525,8 @@ void app::resetSystem(void) { mNetworkConnected = false; - memset(&mStat, 0, sizeof(statistics_t)); + memset(&mNrfStat, 0, sizeof(statistics_t)); + memset(&mCmtStat, 0, sizeof(statistics_t)); } //----------------------------------------------------------------------------- diff --git a/src/app.h b/src/app.h index 01b58e38..e601bf39 100644 --- a/src/app.h +++ b/src/app.h @@ -129,8 +129,12 @@ class app : public IApp, public ah::Scheduler { return mSaveReboot; } - statistics_t *getStatistics() { - return &mStat; + statistics_t *getNrfStatistics() { + return &mNrfStat; + } + + statistics_t *getCmtStatistics() { + return &mCmtStat; } #if !defined(ETHERNET) @@ -208,11 +212,6 @@ class app : public IApp, public ah::Scheduler { return mWeb.isProtected(request); } - void getNrfRadioCounters(uint32_t *sendCnt, uint32_t *retransmits) { - *sendCnt = mNrfRadio.mSendCnt; - *retransmits = mNrfRadio.mRetransmits; - } - bool getNrfEnabled(void) { return mConfig->nrf.enabled; } @@ -355,7 +354,8 @@ class app : public IApp, public ah::Scheduler { bool mNetworkConnected; - statistics_t mStat; + statistics_t mNrfStat; + statistics_t mCmtStat; // mqtt PubMqttType mMqtt; diff --git a/src/appInterface.h b/src/appInterface.h index a7299f7e..83170a0a 100644 --- a/src/appInterface.h +++ b/src/appInterface.h @@ -35,7 +35,8 @@ class IApp { #endif /* defined(ETHERNET) */ virtual void setRebootFlag() = 0; virtual const char *getVersion() = 0; - virtual statistics_t *getStatistics() = 0; + virtual statistics_t *getNrfStatistics() = 0; + virtual statistics_t *getCmtStatistics() = 0; #if !defined(ETHERNET) virtual void scanAvailNetworks() = 0; @@ -65,13 +66,8 @@ class IApp { virtual bool getProtection(AsyncWebServerRequest *request) = 0; - virtual void getNrfRadioCounters(uint32_t *sendCnt, uint32_t *retransmits) = 0; - //virtual void getCmtRadioCounters(uint32_t *sendCnt, uint32_t *retransmits) = 0; - virtual void* getRadioObj(bool nrf) = 0; - #if defined(ESP32) - //virtual const CmtRadioType& getCmtRadioObj(void) const = 0; - #endif + }; #endif /*__IAPP_H__*/ diff --git a/src/defines.h b/src/defines.h index fdc05907..c84b161d 100644 --- a/src/defines.h +++ b/src/defines.h @@ -13,7 +13,7 @@ //------------------------------------- #define VERSION_MAJOR 0 #define VERSION_MINOR 7 -#define VERSION_PATCH 53 +#define VERSION_PATCH 54 //------------------------------------- typedef struct { @@ -98,6 +98,8 @@ typedef struct { uint32_t rxFailNoAnser; uint32_t rxSuccess; uint32_t frmCnt; + uint32_t txCnt; + uint32_t retransmits; } statistics_t; #endif /*__DEFINES_H__*/ diff --git a/src/hm/hmRadio.h b/src/hm/hmRadio.h index ace1c212..b8365bf3 100644 --- a/src/hm/hmRadio.h +++ b/src/hm/hmRadio.h @@ -53,17 +53,15 @@ class HmRadio { mTxChIdx = 2; // Start TX with 40 mRxChIdx = 0; // Start RX with 03 - mSendCnt = 0; - mRetransmits = 0; - mSerialDebug = false; mIrqRcvd = false; } ~HmRadio() {} - void setup(uint8_t ampPwr = RF24_PA_LOW, uint8_t irq = IRQ_PIN, uint8_t ce = CE_PIN, uint8_t cs = CS_PIN, uint8_t sclk = SCLK_PIN, uint8_t mosi = MOSI_PIN, uint8_t miso = MISO_PIN) { + void setup(statistics_t *stat, uint8_t ampPwr = RF24_PA_LOW, uint8_t irq = IRQ_PIN, uint8_t ce = CE_PIN, uint8_t cs = CS_PIN, uint8_t sclk = SCLK_PIN, uint8_t mosi = MOSI_PIN, uint8_t miso = MISO_PIN) { DPRINTLN(DBG_VERBOSE, F("hmRadio.h:setup")); pinMode(irq, INPUT_PULLUP); + mStat = stat; uint32_t dtuSn = 0x87654321; uint32_t chipID = 0; // will be filled with last 3 bytes of MAC @@ -241,10 +239,6 @@ class HmRadio { } std::queue mBufCtrl; - - uint32_t mSendCnt; - uint32_t mRetransmits; - bool mSerialDebug; private: @@ -293,7 +287,7 @@ class HmRadio { void sendPacket(uint64_t invId, uint8_t len, bool isRetransmit, bool appendCrc16=true) { //DPRINTLN(DBG_VERBOSE, F("hmRadio.h:sendPacket")); - //DPRINTLN(DBG_VERBOSE, "sent packet: #" + String(mSendCnt)); + //DPRINTLN(DBG_VERBOSE, "sent packet: #" + String(mStat->txCnt)); // append crc's if (appendCrc16 && (len > 10)) { @@ -325,9 +319,9 @@ class HmRadio { mNrf24.startWrite(mTxBuf, len, false); // false = request ACK response if(isRetransmit) - mRetransmits++; + mStat->retransmits++; else - mSendCnt++; + mStat->txCnt++; } volatile bool mIrqRcvd; @@ -340,6 +334,7 @@ class HmRadio { SPIClass* mSpi; RF24 mNrf24; uint8_t mTxBuf[MAX_RF_PAYLOAD_SIZE]; + statistics_t *mStat; }; #endif /*__RADIO_H__*/ diff --git a/src/hms/hmsRadio.h b/src/hms/hmsRadio.h index 9b1323de..d8ac0131 100644 --- a/src/hms/hmsRadio.h +++ b/src/hms/hmsRadio.h @@ -29,8 +29,9 @@ class CmtRadio { mCmtAvail = false; } - void setup(uint8_t pinSclk, uint8_t pinSdio, uint8_t pinCsb, uint8_t pinFcsb, bool genDtuSn = true) { + void setup(statistics_t *stat, uint8_t pinSclk, uint8_t pinSdio, uint8_t pinCsb, uint8_t pinFcsb, bool genDtuSn = true) { mCmt.setup(pinSclk, pinSdio, pinCsb, pinFcsb); + mStat = stat; reset(genDtuSn); } @@ -135,13 +136,11 @@ class CmtRadio { } if(isRetransmit) - mRetransmits++; + mStat->retransmits++; else - mSendCnt++; + mStat->txCnt++; } - uint32_t mSendCnt; - uint32_t mRetransmits; std::queue mBufCtrl; private: @@ -156,8 +155,6 @@ class CmtRadio { mCmt.goRx(); } - mSendCnt = 0; - mRetransmits = 0; mSerialDebug = false; mIrqRcvd = false; mRqstGetRx = false; @@ -217,6 +214,7 @@ class CmtRadio { bool mIrqRcvd; bool mRqstGetRx; bool mCmtAvail; + statistics_t *mStat; }; #endif /*__HMS_RADIO_H__*/ diff --git a/src/platformio.ini b/src/platformio.ini index 11638512..a4a1d0f8 100644 --- a/src/platformio.ini +++ b/src/platformio.ini @@ -143,6 +143,6 @@ build_flags = ${env.build_flags} -DDEF_LED1=17 -DLED_ACTIVE_HIGH -DARDUINO_USB_MODE=1 - ;-DARDUINO_USB_CDC_ON_BOOT=1 + -DARDUINO_USB_CDC_ON_BOOT=1 monitor_filters = esp32_exception_decoder, colorize diff --git a/src/web/RestApi.h b/src/web/RestApi.h index 24122748..1eec1dcf 100644 --- a/src/web/RestApi.h +++ b/src/web/RestApi.h @@ -93,7 +93,6 @@ class RestApi { else if(path == "system") getSysInfo(request, root); else if(path == "generic") getGeneric(request, root); else if(path == "reboot") getReboot(request, root); - else if(path == "statistics") getStatistics(root); else if(path == "inverter/list") getInverterList(root); else if(path == "index") getIndex(request, root); else if(path == "setup") getSetup(request, root); @@ -161,7 +160,7 @@ class RestApi { ep[F("inverter/list")] = url + F("inverter/list"); ep[F("inverter/id/0")] = url + F("inverter/id/0"); ep[F("inverter/alarm/0")] = url + F("inverter/alarm/0"); - ep[F("statistics")] = url + F("statistics"); + ep[F("inverter/version/0")] = url + F("inverter/version/0"); ep[F("generic")] = url + F("generic"); ep[F("index")] = url + F("index"); ep[F("setup")] = url + F("setup"); @@ -246,7 +245,7 @@ class RestApi { getRadioCmtInfo(obj.createNestedObject(F("radioCmt"))); #endif getMqttInfo(obj.createNestedObject(F("mqtt"))); - getStatistics(obj.createNestedObject(F("statistics"))); + getStatistics(obj.createNestedArray(F("statistics"))); #if defined(ESP32) obj[F("chip_revision")] = ESP.getChipRevision(); @@ -311,14 +310,23 @@ class RestApi { obj[F("html")] = F("reboot. Autoreload after 10 seconds"); } - void getStatistics(JsonObject obj) { - statistics_t *stat = mApp->getStatistics(); - obj[F("rx_success")] = stat->rxSuccess; - obj[F("rx_fail")] = stat->rxFail; - obj[F("rx_fail_answer")] = stat->rxFailNoAnser; - obj[F("frame_cnt")] = stat->frmCnt; - obj[F("tx_cnt")] = mRadioNrf->mSendCnt; - obj[F("retransmits")] = mRadioNrf->mRetransmits; + void getStatistics(JsonArray arr) { + statistics_t *stat; + #if defined(ESP32) + for(uint8_t i = 0; i < 2; i++) { + stat = (0 == i) ? mApp->getNrfStatistics() : mApp->getCmtStatistics(); + #else + { + stat = mApp->getNrfStatistics(); + #endif + JsonObject obj = arr.createNestedObject(); + obj[F("rx_success")] = stat->rxSuccess; + obj[F("rx_fail")] = stat->rxFail; + obj[F("rx_fail_answer")] = stat->rxFailNoAnser; + obj[F("frame_cnt")] = stat->frmCnt; + obj[F("tx_cnt")] = stat->txCnt; + obj[F("retransmits")] = stat->retransmits; + } } void getInverterList(JsonObject obj) { @@ -369,6 +377,7 @@ class RestApi { obj[F("version")] = String(iv->getFwVersion()); obj[F("power_limit_read")] = ah::round3(iv->actPowerLimit); obj[F("power_limit_ack")] = iv->powerLimitAck; + obj[F("max_pwr")] = iv->getMaxPower(); obj[F("ts_last_success")] = rec->ts; obj[F("generation")] = iv->ivGen; obj[F("status")] = (uint8_t)iv->status; @@ -576,7 +585,7 @@ class RestApi { invObj[F("enabled")] = (bool)iv->config->enabled; invObj[F("id")] = i; invObj[F("name")] = String(iv->config->name); - invObj[F("version")] = String(iv->getFwVersion()); + invObj[F("cur_pwr")] = iv->getPosByChFld(CH0, FLD_PAC, rec); invObj[F("is_avail")] = iv->isAvailable(); invObj[F("is_producing")] = iv->isProducing(); invObj[F("ts_last_success")] = iv->getLastTs(rec); @@ -642,28 +651,6 @@ class RestApi { } } - /*void getRecord(JsonObject obj, uint8_t recType) { - JsonArray invArr = obj.createNestedArray(F("inverter")); - - Inverter<> *iv; - record_t<> *rec; - uint8_t pos; - for(uint8_t i = 0; i < MAX_NUM_INVERTERS; i ++) { - iv = mSys->getInverterByPos(i); - if(NULL != iv) { - rec = iv->getRecordStruct(recType); - JsonArray obj2 = invArr.createNestedArray(); - for(uint8_t j = 0; j < rec->length; j++) { - byteAssign_t *assign = iv->getByteAssign(j, rec); - pos = (iv->getPosByChFld(assign->ch, assign->fieldId, rec)); - obj2[j]["fld"] = (0xff != pos) ? String(iv->getFieldName(pos, rec)) : notAvail; - obj2[j]["unit"] = (0xff != pos) ? String(iv->getUnit(pos, rec)) : notAvail; - obj2[j]["val"] = (0xff != pos) ? String(iv->getValue(pos, rec)) : notAvail; - } - } - } - }*/ - bool setCtrl(JsonObject jsonIn, JsonObject jsonOut) { Inverter<> *iv = mSys->getInverterByPos(jsonIn[F("id")]); bool accepted = true; diff --git a/src/web/html/index.html b/src/web/html/index.html index 221c09e9..baa70742 100644 --- a/src/web/html/index.html +++ b/src/web/html/index.html @@ -134,15 +134,16 @@ } else { avail = "available and is "; if(false == i["is_producing"]) - avail += "not "; - else + avail += "not producing"; + else { icon = iconSuccessFull; - avail += "producing"; + avail += "producing " + i.cur_pwr + "W"; + } } p.append( svg(icon, 30, 30, "icon " + cl), - span("Inverter #" + i["id"] + ": " + i["name"] + " (v" + i["version"] + ") is " + avail), + span("Inverter #" + i["id"] + ": " + i["name"] + " is " + avail), br() ); diff --git a/src/web/html/system.html b/src/web/html/system.html index 9ef58d97..071ddb1d 100644 --- a/src/web/html/system.html +++ b/src/web/html/system.html @@ -57,45 +57,52 @@ ]); } + function parseStat(stat) { + return [ + tr("TX count", stat.tx_cnt), + tr("RX success", stat.rx_success), + tr("RX fail", stat.rx_fail), + tr("RX no answer", stat.rx_fail_answer), + tr("RX fragments", stat.frame_cnt), + tr("TX retransmits", stat.retransmits) + ]; + } + function parseRadio(obj) { const pa = ["MIN (recommended)", "LOW", "HIGH", "MAX"]; const dr = ["1 M", "2 M", "250 k"] - if(obj.radioNrf.en) + if(obj.radioNrf.en) { lines = [ tr("NRF24L01", badge(obj.radioNrf.isconnected, ((obj.radioNrf.isconnected) ? "" : "not ") + "connected")), tr("NRF24 Power Level", pa[obj.radioNrf.power_level]), tr("NRF24 Data Rate", dr[obj.radioNrf.dataRate] + "bps") ]; - else + Array.prototype.push.apply(lines, parseStat(obj.statistics[0])); + } else lines = [tr("NRF24L01", badge(false, "not enabled"))]; - /*IF_ESP32*/ - if(obj.radioCmt.en) - lines.push(tr("CMT2300A", badge(obj.radioCmt.isconnected, ((obj.radioCmt.isconnected) ? "" : "not ") + "connected"))); - else - lines.push(tr("CMT2300A", badge(false, "not enabled"))); - /*ENDIF_ESP32*/ - - var stat = obj.statistics; document.getElementById("info").append( - headline("Radio"), + headline("Radio NRF"), ml("table", {class: "table"}, ml("tbody", {}, lines) - ), + ) + ); + + /*IF_ESP32*/ + if(obj.radioCmt.en) { + cmt = [tr("CMT2300A", badge(obj.radioCmt.isconnected, ((obj.radioCmt.isconnected) ? "" : "not ") + "connected"))]; + Array.prototype.push.apply(cmt, parseStat(obj.statistics[1])); + } else + cmt = [tr("CMT2300A", badge(false, "not enabled"))]; - headline("Statistics"), + document.getElementById("info").append( + headline("Radio CMT"), ml("table", {class: "table"}, - ml("tbody", {}, [ - tr("TX count", stat.tx_cnt), - tr("RX success", stat.rx_success), - tr("RX fail", stat.rx_fail), - tr("RX no answer", stat.rx_fail_answer), - tr("RX fragments", stat.frame_cnt), - tr("TX retransmits", stat.retransmits) - ]) + ml("tbody", {}, cmt) ) ); + /*ENDIF_ESP32*/ } function parseMqtt(obj) { diff --git a/src/web/html/visualization.html b/src/web/html/visualization.html index 0b3efefd..b31ff048 100644 --- a/src/web/html/visualization.html +++ b/src/web/html/visualization.html @@ -98,6 +98,14 @@ var t = span(" °C"); var clh = (0 == obj.status) ? "iv-h-dis" : "iv-h"; var clbg = (0 == obj.status) ? "iv-bg-dis" : "iv-bg"; + var pwrLimit = "n/a"; + + if(65535 != obj.power_limit_read) { + pwrLimit = obj.power_limit_read + " %"; + if(0 != obj.max_pwr) + pwrLimit += ", " + (obj.max_pwr * obj.power_limit_read / 100) + "W"; + } + return ml("div", {class: "row mt-2"}, ml("div", {class: "col"}, [ ml("div", {class: "p-2 " + clh}, @@ -105,7 +113,7 @@ 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"}, "Active Power Control: " + ((obj.power_limit_read == 65535) ? "n/a" : (obj.power_limit_read + " %"))), + ml("div", {class: "col a-c"}, "Active Power Control: " + pwrLimit), ml("div", {class: "col a-c"}, ml("span", { class: "pointer", onclick: function() { getAjax("/api/inverter/alarm/" + obj.id, parseIvAlarm); }}, ("Alarms: " + obj.alarm_cnt))),