Browse Source

Merge branch 'development03' into Zero-Export

pull/1155/head
DanielR92 2 years ago
parent
commit
8ab30885c2
  1. 2
      User_Manual.md
  2. 2
      scripts/applyPatches.py
  3. 37
      src/CHANGES.md
  4. 66
      src/app.cpp
  5. 28
      src/app.h
  6. 13
      src/appInterface.h
  7. 17
      src/config/config.h
  8. 4
      src/config/config_override_example.h
  9. 10
      src/config/settings.h
  10. 4
      src/defines.h
  11. 7
      src/hm/hmInverter.h
  12. 8
      src/hm/hmPayload.h
  13. 21
      src/hm/hmRadio.h
  14. 6
      src/hms/hmsPayload.h
  15. 16
      src/hms/hmsRadio.h
  16. 27
      src/platformio.ini
  17. 8
      src/plugins/Display/Display.h
  18. 47
      src/web/RestApi.h
  19. 15
      src/web/html/index.html
  20. 2
      src/web/html/setup.html
  21. 6
      src/web/html/style.css
  22. 75
      src/web/html/system.html
  23. 11
      src/web/html/visualization.html
  24. 15
      src/web/web.h

2
User_Manual.md

@ -124,6 +124,8 @@ The AhoyDTU subscribes on following topics:
👆 `<INVERTER_ID>` 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

2
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")

37
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

66
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,24 +184,14 @@ 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) {
mNetworkConnected = gotIp;
every(std::bind(&app::tickSend, this), mConfig->nrf.sendInterval, "tSend");
#if defined(ESP32)
if(mConfig->cmt.enabled)
@ -228,22 +199,14 @@ void app::onNetwork(bool gotIp) {
#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");
//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) */
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) */
}
}
//-----------------------------------------------------------------------------
@ -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));
}
//-----------------------------------------------------------------------------

28
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<void()> 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;

13
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__*/

17
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

4
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

10
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) {

4
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__*/

7
src/hm/hmInverter.h

@ -145,13 +145,13 @@ class Inverter {
record_t<REC_TYP> recordHwInfo; // structure for simple (hardware) info values
record_t<REC_TYP> recordConfig; // structure for system config values
record_t<REC_TYP> 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<alarm_t, 10> 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<InfoCommand>(InverterDevInform_All); // firmware version
else if (getHwVersion() == 0)
enqueCommand<InfoCommand>(InverterDevInform_Simple); // hardware version
else if((alarmLastId != alarmMesIndex) && (alarmMesIndex != 0))
enqueCommand<InfoCommand>(AlarmData); // alarm not answered
enqueCommand<InfoCommand>(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];
}

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

21
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<packet_t> 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__*/

6
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"));

16
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<hmsPacket_t> 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__*/

27
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

8
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();

47
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();
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")] = mRadioNrf->mSendCnt;
obj[F("retransmits")] = mRadioNrf->mRetransmits;
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;

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

2
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]])
)
])
);

6
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 */

75
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) {

11
src/web/html/visualization.html

@ -99,6 +99,14 @@
var t = span("&nbsp;&deg;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 + "&nbsp;%";
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 + "&nbsp;%"))),
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))),

15
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

Loading…
Cancel
Save