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/scripts/applyPatches.py b/scripts/applyPatches.py index 9537e0bd..657525e6 100644 --- a/scripts/applyPatches.py +++ b/scripts/applyPatches.py @@ -28,5 +28,5 @@ def applyPatch(libName, patchFile): # list of patches to apply (relative to /src) applyPatch("ESP Async WebServer", "../patches/AsyncWeb_Prometheus.patch") -if env['PIOENV'] == "opendtufusion": +if env['PIOENV'][:13] == "opendtufusion": applyPatch("GxEPD2", "../patches/GxEPD2_SW_SPI.patch") diff --git a/src/CHANGES.md b/src/CHANGES.md index e85814ed..af26a58b 100644 --- a/src/CHANGES.md +++ b/src/CHANGES.md @@ -1,5 +1,42 @@ # Development Changes +## 0.7.57 - 2023-09-18 +* fix Alarms are always in queue (since 0.7.56) +* fix display active power control to long for small devices #1165 + +## 0.7.56 - 2023-09-17 +* only request alarms which were not received before #1113 +* added flag if alarm was requested but not received and re-request it #1105 +* merge PR #1163 + +## 0.7.55 - 2023-09-17 +* fix prometheus builds +* fix ESP32 default pinout #1159 +* added `opendtufusion-dev` because of anoying `-DARDUINO_USB_CDC_ON_BOOT=1` flag +* fix display of current power on `index` +* fix OTA, was damaged by version `0.7.51`, need to use webinstaller (from `0.7.51` to `0.7.54`) + +## 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 + +## 0.7.52 - 2023-09-16 +* fix CMT configureable pins #1150, #1159 +* update MqTT lib to version `1.4.5` + +## 0.7.51 - 2023-09-16 +* fix CMT configureable pins #1150 +* fix default CMT pins for opendtufusion +* beautified `system` +* changed main loops, fix resets #1125, #1135 + ## 0.7.50 - 2023-09-12 * moved MqTT info to `system` * added CMT info for ESP32 devices diff --git a/src/app.cpp b/src/app.cpp index 50fd9e56..2aa9ddb5 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -11,10 +11,7 @@ //----------------------------------------------------------------------------- -app::app() - : ah::Scheduler {}, - mInnerLoopCb {nullptr} { -} +app::app() : ah::Scheduler {} {} //----------------------------------------------------------------------------- @@ -37,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 @@ -55,14 +52,6 @@ void app::setup() { DSERIAL.flush(); #endif // ETHERNET - #if !defined(ETHERNET) - #if defined(AP_ONLY) - mInnerLoopCb = std::bind(&app::loopStandard, this); - #else - mInnerLoopCb = std::bind(&app::loopWifi, this); - #endif - #endif /* !defined(ETHERNET) */ - #if !defined(ETHERNET) mWifi.setup(mConfig, &mTimestamp, std::bind(&app::onNetwork, this, std::placeholders::_1)); #if !defined(AP_ONLY) @@ -73,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,14 +122,6 @@ void app::setup() { //----------------------------------------------------------------------------- void app::loop(void) { - if (mInnerLoopCb) - mInnerLoopCb(); - #if !defined(ETHERNET) - #endif -} - -//----------------------------------------------------------------------------- -void app::loopStandard(void) { ah::Scheduler::loop(); if (mNrfRadio.loop() && mConfig->nrf.enabled) { @@ -157,7 +138,7 @@ void app::loopStandard(void) { DBGPRINT(F("dBm | ")); ah::dumpBuf(p->packet, p->len); } - mStat.frmCnt++; + mNrfStat.frmCnt++; Inverter<> *iv = mSys.findInverter(&p->packet[1]); if (NULL != iv) { @@ -184,7 +165,7 @@ void app::loopStandard(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) { @@ -203,47 +184,29 @@ void app::loopStandard(void) { mHmsPayload.loop(); #endif - if (mMqttEnabled) + if (mMqttEnabled && mNetworkConnected) mMqtt.loop(); } -#if !defined(ETHERNET) -//----------------------------------------------------------------------------- -void app::loopWifi(void) { - ah::Scheduler::loop(); - yield(); -} -#endif /* !defined(ETHERNET) */ - //----------------------------------------------------------------------------- void app::onNetwork(bool gotIp) { DPRINTLN(DBG_DEBUG, F("onNetwork")); - ah::Scheduler::resetTicker(); - regularTickers(); // reinstall regular tickers - if (gotIp) { - every(std::bind(&app::tickSend, this), mConfig->nrf.sendInterval, "tSend"); - #if defined(ESP32) - if(mConfig->cmt.enabled) - everySec(std::bind(&CmtRadioType::tickSecond, &mCmtRadio), "tsCmt"); - #endif - mMqttReconnect = true; - mSunrise = 0; // needs to be set to 0, to reinstall sunrise and ivComm tickers! - once(std::bind(&app::tickNtpUpdate, this), 2, "ntp2"); - #if !defined(ETHERNET) - if (WIFI_AP == WiFi.getMode()) { - mMqttEnabled = false; - } - everySec(std::bind(&ahoywifi::tickWifiLoop, &mWifi), "wifiL"); - #endif /* !defined(ETHERNET) */ - mInnerLoopCb = [this]() { this->loopStandard(); }; - } else { - #if defined(ETHERNET) - mInnerLoopCb = nullptr; - #else /* defined(ETHERNET) */ - mInnerLoopCb = [this]() { this->loopWifi(); }; - everySec(std::bind(&ahoywifi::tickWifiLoop, &mWifi), "wifiL"); - #endif /* defined(ETHERNET) */ + mNetworkConnected = gotIp; + every(std::bind(&app::tickSend, this), mConfig->nrf.sendInterval, "tSend"); + #if defined(ESP32) + if(mConfig->cmt.enabled) + everySec(std::bind(&CmtRadioType::tickSecond, &mCmtRadio), "tsCmt"); + #endif + mMqttReconnect = true; + mSunrise = 0; // needs to be set to 0, to reinstall sunrise and ivComm tickers! + //once(std::bind(&app::tickNtpUpdate, this), 2, "ntp2"); + tickNtpUpdate(); + #if !defined(ETHERNET) + if (WIFI_AP == WiFi.getMode()) { + mMqttEnabled = false; } + everySec(std::bind(&ahoywifi::tickWifiLoop, &mWifi), "wifiL"); + #endif /* !defined(ETHERNET) */ } //----------------------------------------------------------------------------- @@ -573,7 +536,10 @@ void app::resetSystem(void) { mSavePending = false; mSaveReboot = false; - memset(&mStat, 0, sizeof(statistics_t)); + 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 6f80e515..cbfb7402 100644 --- a/src/app.h +++ b/src/app.h @@ -70,9 +70,6 @@ class app : public IApp, public ah::Scheduler { void setup(void); void loop(void); void loopStandard(void); -#if !defined(ETHERNET) - void loopWifi(void); -#endif /* !defined(ETHERNET) */ void onNetwork(bool gotIp); void regularTickers(void); @@ -136,8 +133,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) @@ -149,9 +150,6 @@ class app : public IApp, public ah::Scheduler { return mWifi.getAvailNetworks(obj); } - void setOnUpdate() { - onNetwork(false); - } #endif /* !defined(ETHERNET) */ void setRebootFlag() { @@ -215,11 +213,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; } @@ -276,8 +269,6 @@ class app : public IApp, public ah::Scheduler { #define CHECK_AVAIL true #define SKIP_YIELD_DAY true - typedef std::function innerLoopCb; - void resetSystem(void); void zeroIvValues(bool checkAvail = false, bool skipYieldDay = true); @@ -333,8 +324,6 @@ class app : public IApp, public ah::Scheduler { void tickZeroValues(void); void tickMidnight(void); - innerLoopCb mInnerLoopCb; - HmSystemType mSys; HmRadio<> mNrfRadio; @@ -368,7 +357,10 @@ class app : public IApp, public ah::Scheduler { uint8_t mSendLastIvId; bool mSendFirst; - statistics_t mStat; + bool mNetworkConnected; + + statistics_t mNrfStat; + statistics_t mCmtStat; // mqtt PubMqttType mMqtt; diff --git a/src/appInterface.h b/src/appInterface.h index a7299f7e..e301bc89 100644 --- a/src/appInterface.h +++ b/src/appInterface.h @@ -30,12 +30,10 @@ class IApp { virtual bool getSavePending() = 0; virtual bool getLastSaveSucceed() = 0; virtual bool getShouldReboot() = 0; - #if !defined(ETHERNET) - virtual void setOnUpdate() = 0; - #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 +63,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/config/config.h b/src/config/config.h index 35417b0e..4cd333f6 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -74,20 +74,20 @@ #define DEF_NRF_IRQ_PIN 16 #endif #ifndef DEF_NRF_MISO_PIN - #define DEF_NRF_MISO_PIN 12 + #define DEF_NRF_MISO_PIN 19 #endif #ifndef DEF_NRF_MOSI_PIN - #define DEF_NRF_MOSI_PIN 13 + #define DEF_NRF_MOSI_PIN 23 #endif #ifndef DEF_NRF_SCLK_PIN - #define DEF_NRF_SCLK_PIN 14 + #define DEF_NRF_SCLK_PIN 18 #endif #ifndef DEF_CMT_SCLK - #define DEF_CMT_SCLK 18 + #define DEF_CMT_SCLK 12 #endif #ifndef DEF_CMT_SDIO - #define DEF_CMT_SDIO 23 + #define DEF_CMT_SDIO 14 #endif #ifndef DEF_CMT_CSB #define DEF_CMT_CSB 27 @@ -211,13 +211,6 @@ // reconnect delay #define MQTT_RECONNECT_DELAY 5000 -// Offset for midnight Ticker -// relative to UTC -// may be negative for later in the next day or positive for earlier in previous day -// may contain variable like mCalculatedTimezoneOffset -// must be in parentheses -#define MIDNIGHTTICKER_OFFSET (-1) - #if __has_include("config_override.h") #include "config_override.h" #endif diff --git a/src/config/config_override_example.h b/src/config/config_override_example.h index 7413831a..c653bf10 100644 --- a/src/config/config_override_example.h +++ b/src/config/config_override_example.h @@ -31,10 +31,6 @@ #undef DEF_SCLK_PIN #define DEF_SCLK_PIN 36 -// Offset for midnight Ticker Example: 1 second before midnight (local time) -#undef MIDNIGHTTICKER_OFFSET -#define MIDNIGHTTICKER_OFFSET (mCalculatedTimezoneOffset + 1) - // To enable the endpoint for prometheus to scrape data from at /metrics // #define ENABLE_PROMETHEUS_EP diff --git a/src/config/settings.h b/src/config/settings.h index 40540b96..e48ffbb2 100644 --- a/src/config/settings.h +++ b/src/config/settings.h @@ -568,19 +568,29 @@ class settings { } } + #if defined(ESP32) void jsonCmt(JsonObject obj, bool set = false) { if(set) { obj[F("csb")] = mCfg.cmt.pinCsb; obj[F("fcsb")] = mCfg.cmt.pinFcsb; obj[F("irq")] = mCfg.cmt.pinIrq; + obj[F("dio")] = mCfg.cmt.pinSdio; + obj[F("clk")] = mCfg.cmt.pinSclk; obj[F("en")] = (bool) mCfg.cmt.enabled; } else { mCfg.cmt.pinCsb = obj[F("csb")]; mCfg.cmt.pinFcsb = obj[F("fcsb")]; mCfg.cmt.pinIrq = obj[F("irq")]; + mCfg.cmt.pinSdio = obj[F("dio")]; + mCfg.cmt.pinSclk = obj[F("clk")]; mCfg.cmt.enabled = (bool) obj[F("en")]; + if(0 == mCfg.cmt.pinSclk) + mCfg.cmt.pinSclk = DEF_CMT_SCLK; + if(0 == mCfg.cmt.pinSdio) + mCfg.cmt.pinSdio = DEF_CMT_SDIO; } } + #endif void jsonNtp(JsonObject obj, bool set = false) { if(set) { diff --git a/src/defines.h b/src/defines.h index a9f17f3b..565e9dfb 100644 --- a/src/defines.h +++ b/src/defines.h @@ -13,7 +13,7 @@ //------------------------------------- #define VERSION_MAJOR 0 #define VERSION_MINOR 7 -#define VERSION_PATCH 50 +#define VERSION_PATCH 57 //------------------------------------- typedef struct { @@ -100,6 +100,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/hmInverter.h b/src/hm/hmInverter.h index a43788a9..43ba0810 100644 --- a/src/hm/hmInverter.h +++ b/src/hm/hmInverter.h @@ -145,13 +145,13 @@ class Inverter { record_t recordHwInfo; // structure for simple (hardware) info values record_t recordConfig; // structure for system config values record_t recordAlarm; // structure for alarm values - //String lastAlarmMsg; bool initialized; // needed to check if the inverter was correctly added (ESP32 specific - union types are never null) bool isConnected; // shows if inverter was successfully identified (fw version and hardware info) InverterStatus status; // indicates the current inverter status std::array lastAlarm; // holds last 10 alarms uint8_t alarmNxtWrPos; // indicates the position in array (rolling buffer) uint16_t alarmCnt; // counts the total number of occurred alarms + uint16_t alarmLastId; // lastId which was received int8_t rssi; // HMS and HMT inverters only @@ -173,6 +173,7 @@ class Inverter { status = InverterStatus::OFF; alarmNxtWrPos = 0; alarmCnt = 0; + alarmLastId = 0; rssi = -127; } @@ -208,6 +209,8 @@ class Inverter { enqueCommand(InverterDevInform_All); // firmware version else if (getHwVersion() == 0) enqueCommand(InverterDevInform_Simple); // hardware version + else if((alarmLastId != alarmMesIndex) && (alarmMesIndex != 0)) + enqueCommand(AlarmData); // alarm not answered enqueCommand(RealTimeRunData_Debug); // live data } else if (ivGen == IV_MI){ if (getFwVersion() == 0) { @@ -598,6 +601,7 @@ class Inverter { lastAlarm.fill({0, 0, 0}); alarmNxtWrPos = 0; alarmCnt = 0; + alarmLastId = 0; } uint16_t parseAlarmLog(uint8_t id, uint8_t pyld[], uint8_t len) { @@ -621,6 +625,7 @@ class Inverter { addAlarm(pyld[startOff+1], start, endTime); alarmCnt++; + alarmLastId = alarmMesIndex; return pyld[startOff+1]; } diff --git a/src/hm/hmPayload.h b/src/hm/hmPayload.h index deefe876..15a16bba 100644 --- a/src/hm/hmPayload.h +++ b/src/hm/hmPayload.h @@ -154,7 +154,7 @@ class HmPayload { DPRINT_IVID(DBG_INFO, iv->id); DBGPRINT(F("prepareDevInformCmd 0x")); DBGHEXLN(cmd); - mRadio->prepareDevInformCmd(iv->radioId.u64, cmd, mPayload[iv->id].ts, iv->alarmMesIndex, false); + mRadio->prepareDevInformCmd(iv->radioId.u64, cmd, mPayload[iv->id].ts, iv->alarmLastId, false); mPayload[iv->id].txCmd = cmd; } } @@ -253,7 +253,7 @@ class HmPayload { DPRINTLN(DBG_WARN, F("nothing received: Request Complete Retransmit")); mPayload[iv->id].txCmd = iv->getQueuedCmd(); DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") prepareDevInformCmd 0x") + String(mPayload[iv->id].txCmd, HEX)); - mRadio->prepareDevInformCmd(iv->radioId.u64, mPayload[iv->id].txCmd, mPayload[iv->id].ts, iv->alarmMesIndex, true); + mRadio->prepareDevInformCmd(iv->radioId.u64, mPayload[iv->id].txCmd, mPayload[iv->id].ts, iv->alarmLastId, true); */ DPRINT_IVID(DBG_INFO, iv->id); DBGPRINTLN(F("nothing received")); @@ -286,7 +286,7 @@ class HmPayload { DBGPRINT(F("prepareDevInformCmd 0x")); DBGHEXLN(mPayload[iv->id].txCmd); } - mRadio->prepareDevInformCmd(iv->radioId.u64, mPayload[iv->id].txCmd, mPayload[iv->id].ts, iv->alarmMesIndex, true); + mRadio->prepareDevInformCmd(iv->radioId.u64, mPayload[iv->id].txCmd, mPayload[iv->id].ts, iv->alarmLastId, true); } } else { // payload complete if (mSerialDebug) { @@ -364,7 +364,7 @@ class HmPayload { DBGHEXLN(cmd); } mStat->rxSuccess++; - mRadio->prepareDevInformCmd(iv->radioId.u64, cmd, mPayload[iv->id].ts, iv->alarmMesIndex, false); + mRadio->prepareDevInformCmd(iv->radioId.u64, cmd, mPayload[iv->id].ts, iv->alarmLastId, false); mPayload[iv->id].txCmd = cmd; } diff --git a/src/hm/hmRadio.h b/src/hm/hmRadio.h index ace1c212..aaa0d3e3 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 @@ -218,10 +216,10 @@ class HmRadio { mTxBuf[10] = cmd; // cid mTxBuf[11] = 0x00; CP_U32_LittleEndian(&mTxBuf[12], ts); - /*if (cmd == AlarmData ) { //cmd == RealTimeRunData_Debug || + if (cmd == AlarmData ) { //cmd == RealTimeRunData_Debug || mTxBuf[18] = (alarmMesId >> 8) & 0xff; mTxBuf[19] = (alarmMesId ) & 0xff; - }*/ + } sendPacket(invId, 24, isRetransmit); } @@ -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/hmsPayload.h b/src/hms/hmsPayload.h index 2ece3c45..e082f059 100644 --- a/src/hms/hmsPayload.h +++ b/src/hms/hmsPayload.h @@ -143,7 +143,7 @@ class HmsPayload { DPRINT_IVID(DBG_INFO, iv->id); DBGPRINT(F("prepareDevInformCmd 0x")); DBGHEXLN(cmd); - mRadio->prepareDevInformCmd(&iv->radioId.u64, cmd, mPayload[iv->id].ts, iv->alarmMesIndex, false); + mRadio->prepareDevInformCmd(&iv->radioId.u64, cmd, mPayload[iv->id].ts, iv->alarmLastId, false); mPayload[iv->id].txCmd = cmd; } } @@ -238,7 +238,7 @@ class HmsPayload { //DPRINTLN(DBG_WARN, F("nothing received: Request Complete Retransmit")); //mPayload[iv->id].txCmd = iv->getQueuedCmd(); //DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") prepareDevInformCmd 0x") + String(mPayload[iv->id].txCmd, HEX)); - //mRadio->prepareDevInformCmd(iv->radioId.u64, mPayload[iv->id].txCmd, mPayload[iv->id].ts, iv->alarmMesIndex, true); + //mRadio->prepareDevInformCmd(iv->radioId.u64, mPayload[iv->id].txCmd, mPayload[iv->id].ts, iv->alarmLastId, true); DPRINT_IVID(DBG_INFO, iv->id); DBGPRINTLN(F("nothing received")); @@ -267,7 +267,7 @@ class HmsPayload { DBGPRINT(String(iv->id)); DBGPRINT(F(") prepareDevInformCmd 0x")); DBGPRINTLN(String(mPayload[iv->id].txCmd, HEX)); - mRadio->prepareDevInformCmd(iv->radioId.u64, mPayload[iv->id].txCmd, mPayload[iv->id].ts, iv->alarmMesIndex, true); + mRadio->prepareDevInformCmd(iv->radioId.u64, mPayload[iv->id].txCmd, mPayload[iv->id].ts, iv->alarmLastId, true); } }*/ else { // payload complete DPRINT(DBG_INFO, F("procPyld: cmd: 0x")); diff --git a/src/hms/hmsRadio.h b/src/hms/hmsRadio.h index 9b1323de..e579deda 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); } @@ -103,10 +104,10 @@ class CmtRadio { initPacket(ivId, reqfld, ALL_FRAMES); mTxBuf[10] = cmd; CP_U32_LittleEndian(&mTxBuf[12], ts); - /*if (cmd == AlarmData ) { //cmd == RealTimeRunData_Debug || + if (cmd == AlarmData ) { //cmd == RealTimeRunData_Debug || mTxBuf[18] = (alarmMesId >> 8) & 0xff; mTxBuf[19] = (alarmMesId ) & 0xff; - }*/ + } sendPacket(24, isRetransmit); } @@ -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 c46b1305..942d2e15 100644 --- a/src/platformio.ini +++ b/src/platformio.ini @@ -27,7 +27,7 @@ lib_deps = https://github.com/yubox-node-org/ESPAsyncWebServer nrf24/RF24 @ 1.4.7 paulstoffregen/Time @ ^1.6.1 - https://github.com/bertmelis/espMqttClient#v1.4.4 + https://github.com/bertmelis/espMqttClient#v1.4.5 bblanchon/ArduinoJson @ ^6.21.3 https://github.com/JChristensen/Timezone @ ^1.2.4 olikraus/U8g2 @ ^2.34.17 @@ -143,6 +143,29 @@ build_flags = ${env.build_flags} -DDEF_LED1=17 -DLED_ACTIVE_HIGH -DARDUINO_USB_MODE=1 - ;-DARDUINO_USB_CDC_ON_BOOT=1 +monitor_filters = + esp32_exception_decoder, colorize + +[env:opendtufusion-dev] +platform = espressif32@6.3.2 +board = esp32-s3-devkitc-1 +upload_protocol = esp-builtin +build_flags = ${env.build_flags} + -DDEF_NRF_CS_PIN=37 + -DDEF_NRF_CE_PIN=38 + -DDEF_NRF_IRQ_PIN=47 + -DDEF_NRF_MISO_PIN=48 + -DDEF_NRF_MOSI_PIN=35 + -DDEF_NRF_SCLK_PIN=36 + -DDEF_CMT_CSB=4 + -DDEF_CMT_FCSB=21 + -DDEF_CMT_IRQ=8 + -DDEF_CMT_SDIO=5 + -DDEF_CMT_SCLK=6 + -DDEF_LED0=18 + -DDEF_LED1=17 + -DLED_ACTIVE_HIGH + -DARDUINO_USB_MODE=1 + -DARDUINO_USB_CDC_ON_BOOT=1 monitor_filters = esp32_exception_decoder, colorize diff --git a/src/plugins/Display/Display.h b/src/plugins/Display/Display.h index 93349769..8152f372 100644 --- a/src/plugins/Display/Display.h +++ b/src/plugins/Display/Display.h @@ -90,6 +90,7 @@ class Display { Inverter<> *iv; record_t<> *rec; + bool allOff = true; for (uint8_t i = 0; i < mSys->getNumInverters(); i++) { iv = mSys->getInverterByPos(i); rec = iv->getRecordStruct(RealTimeRunData_Debug); @@ -104,12 +105,17 @@ class Display { totalPower += iv->getChannelFieldValue(CH0, FLD_PAC, rec); totalYieldDay += iv->getChannelFieldValue(CH0, FLD_YD, rec); totalYieldTotal += iv->getChannelFieldValue(CH0, FLD_YT, rec); + + if(allOff) { + if(iv->status != InverterStatus::OFF) + allOff = false; + } } // prepare display data mDisplayData.nrProducing = nrprod; mDisplayData.nrSleeping = nrsleep; - mDisplayData.totalPower = totalPower; + mDisplayData.totalPower = (allOff) ? 0.0 : totalPower; // if all inverters are off, total power can't be greater than 0 mDisplayData.totalYieldDay = totalYieldDay; mDisplayData.totalYieldTotal = totalYieldTotal; mDisplayData.RadioSymbol = mHmRadio->isChipConnected(); diff --git a/src/web/RestApi.h b/src/web/RestApi.h index 19a39f3a..076fb4f0 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; @@ -597,7 +606,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")] = ah::round3(iv->getChannelFieldValue(CH0, FLD_PAC, rec)); invObj[F("is_avail")] = iv->isAvailable(); invObj[F("is_producing")] = iv->isProducing(); invObj[F("ts_last_success")] = iv->getLastTs(rec); @@ -667,28 +676,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 088d6410..baa70742 100644 --- a/src/web/html/index.html +++ b/src/web/html/index.html @@ -115,7 +115,7 @@ } } - function parseIv(obj) { + function parseIv(obj, ts) { var p = div(["none"]); for(var i of obj) { var icon = iconSuccess; @@ -125,7 +125,7 @@ icon = iconWarn; cl = "icon-warn"; avail = "disabled"; - } else if(false == i["is_avail"]) { + } else if((false == i["is_avail"]) || (0 == ts)) { icon = iconInfo; cl = "icon-info"; avail = "not yet available"; @@ -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() ); @@ -192,7 +193,7 @@ parseNav(obj["generic"]); parseGeneric(obj["generic"]); parseSys(obj); - parseIv(obj["inverter"]); + parseIv(obj["inverter"], obj.ts_now); parseWarn(obj["warnings"]); if(exeOnce) { window.setInterval("tick()", 1000); diff --git a/src/web/html/setup.html b/src/web/html/setup.html index c2f0ea3c..0a2e461b 100644 --- a/src/web/html/setup.html +++ b/src/web/html/setup.html @@ -849,7 +849,7 @@ ml("div", {class: "row mb-3"}, [ ml("div", {class: "col-12 col-sm-3 my-2"}, p[0].toUpperCase()), ml("div", {class: "col-12 col-sm-9"}, - sel(p[1], ("ESP32-S3" == system["chip_model"]) ? esp32s3pins : esp32pins, obj[p[0]]) + sel(p[1], (("ESP32-S3" == system["chip_model"]) ? esp32s3pins : esp32pins), obj[p[0]]) ) ]) ); diff --git a/src/web/html/style.css b/src/web/html/style.css index 0f053d98..15b35cef 100644 --- a/src/web/html/style.css +++ b/src/web/html/style.css @@ -266,6 +266,9 @@ p { .a-r { text-align: right; } .a-c { text-align: center; } +.d-none { display: none !important; } +.d-block { display: block !important; } + .row > * { padding-left: 0.5rem; padding-right: 0.5rem; @@ -304,6 +307,9 @@ p { .fs-sm-6 { font-size: 1.5rem; } .fs-sm-7 { font-size: 1.25rem; } .fs-sm-8 { font-size: 1rem; } + + .d-sm-block { display: block !important;} + .d-sm-none { display: none !important; } } /* md */ diff --git a/src/web/html/system.html b/src/web/html/system.html index 11be689a..071ddb1d 100644 --- a/src/web/html/system.html +++ b/src/web/html/system.html @@ -20,30 +20,24 @@ parseRssi(obj); } - function genTabRow(key, value) { - var r = div(["row", "p-1"]); - r.appendChild(div(["col"], key)); - r.appendChild(div(["col"], value)); - return r; - } - function parseSysInfo(obj) { const data = ["sdk", "cpu_freq", "chip_revision", "chip_model", "chip_cores", "esp_type", "mac", "wifi_rssi", "ts_uptime", "flash_size", "sketch_used", "heap_total", "heap_free", "heap_frag", "max_free_blk", "version", "core_version", "reboot_reason"]; - var main = document.getElementById("info"); - var h = div(["head", "p-2"]); - var r = div(["row"]); - r.appendChild(div(["col", "a-c"], "System Information")); - h.appendChild(r); - main.appendChild(h); - + lines = []; for (const [key, value] of Object.entries(obj)) { if(!data.includes(key) || (typeof value == 'undefined')) continue; - main.appendChild(genTabRow(key, value)); + lines.push(tr(key.replace('_', ' '), value)); } + + document.getElementById("info").append( + headline("System Information"), + ml("table", {class: "table"}, + ml("tbody", {}, lines) + ) + ); } function badge(success, text, second="error") { @@ -58,50 +52,57 @@ if(typeof val2 == "number") val2 = String(val2); return ml("tr", {}, [ - ml("th", {}, val1), + ml("th", {style: "width: 50%"}, val1), ml("td", {}, val2) ]); } + 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) - ), + ) + ); - headline("Statistics"), + /*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"))]; + + 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 ef9e0378..c95db7a0 100644 --- a/src/web/html/visualization.html +++ b/src/web/html/visualization.html @@ -99,6 +99,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}, @@ -106,7 +114,8 @@ 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 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"}, ml("span", { class: "pointer", onclick: function() { getAjax("/api/inverter/alarm/" + obj.id, parseIvAlarm); }}, ("Alarms: " + obj.alarm_cnt))), diff --git a/src/web/web.h b/src/web/web.h index 4f062713..4112ec23 100644 --- a/src/web/web.h +++ b/src/web/web.h @@ -148,10 +148,6 @@ class Web { } void showUpdate2(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) { - #if !defined(ETHERNET) - mApp->setOnUpdate(); - #endif /* !defined(ETHERNET) */ - if (!index) { Serial.printf("Update Start: %s\n", filename.c_str()); #ifndef ESP32 @@ -543,7 +539,7 @@ class Web { // pinout uint8_t pin; - for (uint8_t i = 0; i < 12; i++) { + for (uint8_t i = 0; i < 14; i++) { pin = request->arg(String(pinArgNames[i])).toInt(); switch(i) { case 0: mConfig->nrf.pinCs = ((pin != 0xff) ? pin : DEF_NRF_CS_PIN); break; @@ -730,16 +726,13 @@ class Web { metrics += String(type) + String(topic); // NRF Statistics - stat = mApp->getStatistics(); - uint32_t nrfSendCnt; - uint32_t nrfRetransmits; - mApp->getNrfRadioCounters(&nrfSendCnt, &nrfRetransmits); + 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"), nrfSendCnt); - metrics += radioStatistic(F("retrans_cnt"), nrfRetransmits); + metrics += radioStatistic(F("tx_cnt"), stat->txCnt); + metrics += radioStatistic(F("retrans_cnt"), stat->retransmits); len = snprintf((char *)buffer,maxLen,"%s",metrics.c_str()); // Next is Inverter information