Browse Source

Merge branch 'development03' into Zero-Export

pull/1155/head
DanielR92 2 years ago
parent
commit
402ad48dd3
  1. 17
      src/CHANGES.md
  2. 56
      src/app.cpp
  3. 1
      src/app.h
  4. 2
      src/config/config.h
  5. 2
      src/defines.h
  6. 55
      src/hm/hmDefines.h
  7. 9
      src/hm/hmInverter.h
  8. 3
      src/hm/hmPayload.h
  9. 7
      src/hm/hmRadio.h
  10. 72
      src/hm/miPayload.h
  11. 16
      src/hms/hmsRadio.h
  12. 6
      src/platformio.ini
  13. 12
      src/plugins/Display/Display.h
  14. 2
      src/plugins/Display/Display_Mono.h
  15. 7
      src/plugins/Display/Display_Mono_128X32.h
  16. 11
      src/plugins/Display/Display_Mono_128X64.h
  17. 9
      src/plugins/Display/Display_Mono_64X48.h
  18. 9
      src/plugins/Display/Display_Mono_84X48.h
  19. 198
      src/plugins/Display/Display_ePaper.cpp
  20. 60
      src/plugins/Display/Display_ePaper.h
  21. 41
      src/plugins/Display/imagedata.h
  22. 2
      src/web/RestApi.h
  23. 1
      src/web/html/colorBright.css
  24. 1
      src/web/html/colorDark.css
  25. 2
      src/web/html/setup.html
  26. 29
      src/web/html/style.css
  27. 58
      src/web/html/system.html

17
src/CHANGES.md

@ -1,5 +1,22 @@
# Development Changes
## 0.7.46 - 2023-09-04
* removed `delay` from ePaper
* started improvements of `/system`
* fix LEDs to check all configured inverters
* send loop skip disabled inverters fix
* print generated DTU SN to console
* HW Versions for MI series PR #1133
* 2.42" display (SSD1309) integration PR #1139
* update user manual PR #1121
* add / rename alarm codes PR #1118
* revert default pin ESP32 for NRF23-CE #1132
* luminance of display can be changed during runtime #1106
## 0.7.45 - 2023-08-29
* change ePaper text to symbols PR #1131
* added some invertes to dev info list #1111
## 0.7.44 - 2023-08-28
* fix `last_success` transmitted to often #1124

56
src/app.cpp

@ -463,24 +463,26 @@ void app::tickSend(void) {
int8_t maxLoop = MAX_NUM_INVERTERS;
Inverter<> *iv = mSys.getInverterByPos(mSendLastIvId);
do {
mSendLastIvId = ((MAX_NUM_INVERTERS - 1) == mSendLastIvId) ? 0 : mSendLastIvId + 1;
iv = mSys.getInverterByPos(mSendLastIvId);
} while ((NULL == iv) && ((maxLoop--) > 0));
do {
mSendLastIvId = ((MAX_NUM_INVERTERS - 1) == mSendLastIvId) ? 0 : mSendLastIvId + 1;
iv = mSys.getInverterByPos(mSendLastIvId);
} while ((NULL == iv) && ((maxLoop--) > 0));
} while((!iv->config->enabled) && (maxLoop > 0));
if (NULL != iv) {
if (iv->config->enabled) {
if(mConfig->nrf.enabled) {
if (iv->ivGen == IV_HM)
mPayload.ivSend(iv);
else if(iv->ivGen == IV_MI)
mMiPayload.ivSend(iv);
}
#if defined(ESP32)
if(mConfig->cmt.enabled) {
if((iv->ivGen == IV_HMS) || (iv->ivGen == IV_HMT))
mHmsPayload.ivSend(iv);
}
#endif
if (iv->ivGen == IV_HM)
mPayload.ivSend(iv);
else if(iv->ivGen == IV_MI)
mMiPayload.ivSend(iv);
}
#if defined(ESP32)
if(mConfig->cmt.enabled) {
if((iv->ivGen == IV_HMS) || (iv->ivGen == IV_HMT))
mHmsPayload.ivSend(iv);
}
#endif
}
}
} else {
@ -577,11 +579,11 @@ void app::mqttSubRxCb(JsonObject obj) {
void app::setupLed(void) {
uint8_t led_off = (mConfig->led.led_high_active) ? LOW : HIGH;
if (mConfig->led.led0 != 0xff) {
if (mConfig->led.led0 != DEF_PIN_OFF) {
pinMode(mConfig->led.led0, OUTPUT);
digitalWrite(mConfig->led.led0, led_off);
}
if (mConfig->led.led1 != 0xff) {
if (mConfig->led.led1 != DEF_PIN_OFF) {
pinMode(mConfig->led.led1, OUTPUT);
digitalWrite(mConfig->led.led1, led_off);
}
@ -592,17 +594,23 @@ void app::updateLed(void) {
uint8_t led_off = (mConfig->led.led_high_active) ? LOW : HIGH;
uint8_t led_on = (mConfig->led.led_high_active) ? HIGH : LOW;
if (mConfig->led.led0 != 0xff) {
Inverter<> *iv = mSys.getInverterByPos(0);
if (NULL != iv) {
if (iv->isProducing())
digitalWrite(mConfig->led.led0, led_on);
else
digitalWrite(mConfig->led.led0, led_off);
if (mConfig->led.led0 != DEF_PIN_OFF) {
Inverter<> *iv;
for (uint8_t id = 0; id < mSys.getNumInverters(); id++) {
iv = mSys.getInverterByPos(id);
if (NULL != iv) {
if (iv->isProducing()) {
// turn on when at least one inverter is producing
digitalWrite(mConfig->led.led0, led_on);
break;
}
else if(iv->config->enabled)
digitalWrite(mConfig->led.led0, led_off);
}
}
}
if (mConfig->led.led1 != 0xff) {
if (mConfig->led.led1 != DEF_PIN_OFF) {
if (getMqttIsConnected()) {
digitalWrite(mConfig->led.led1, led_on);
} else {

1
src/app.h

@ -276,6 +276,7 @@ class app : public IApp, public ah::Scheduler {
#endif
if(mConfig->plugin.display.type != 0)
mDisplay.payloadEventListener(cmd);
updateLed();
}
void mqttSubRxCb(JsonObject obj);

2
src/config/config.h

@ -68,7 +68,7 @@
#define DEF_NRF_CS_PIN 5
#endif
#ifndef DEF_NRF_CE_PIN
#define DEF_NRF_CE_PIN 17
#define DEF_NRF_CE_PIN 4
#endif
#ifndef DEF_NRF_IRQ_PIN
#define DEF_NRF_IRQ_PIN 16

2
src/defines.h

@ -13,7 +13,7 @@
//-------------------------------------
#define VERSION_MAJOR 0
#define VERSION_MINOR 7
#define VERSION_PATCH 44
#define VERSION_PATCH 46
//-------------------------------------
typedef struct {

55
src/hm/hmDefines.h

@ -25,7 +25,7 @@ enum {FLD_UDC = 0, FLD_IDC, FLD_PDC, FLD_YD, FLD_YW, FLD_YT,
FLD_FW_BUILD_MONTH_DAY, FLD_FW_BUILD_HOUR_MINUTE, FLD_BOOTLOADER_VER,
FLD_ACT_ACTIVE_PWR_LIMIT, FLD_PART_NUM, FLD_HW_VERSION, FLD_GRID_PROFILE_CODE,
FLD_GRID_PROFILE_VERSION, /*FLD_ACT_REACTIVE_PWR_LIMIT, FLD_ACT_PF,*/ FLD_LAST_ALARM_CODE, FLD_MP};
const char* const fields[] = {"U_DC", "I_DC", "P_DC", "YieldDay", "YieldWeek", "YieldTotal",
"U_AC", "U_AC_1N", "U_AC_2N", "U_AC_3N", "UAC_12", "UAC_23", "UAC_31", "I_AC",
"IAC_1", "I_AC_2", "I_AC_3", "P_AC", "F_AC", "Temp", "PF_AC", "Efficiency", "Irradiation","Q_AC",
@ -259,20 +259,60 @@ typedef struct {
uint16_t maxPower;
} devInfo_t;
// https://github.com/lumapu/ahoy/issues/1111
// Hardware number:
// 0xAABBCCDD
// ^^ ------- always 10 (for MI, HM, HMS, HMT)
// ^ ------ 0 = MI
// 1 = HM
// 1, 2 = HMS (version)
// 3 = HMT
// ^ ----- 0 = 1 Input
// 1 = 2 Inputs
// 2 = 4 Inputs
// 3 = 6 Inputs
// ^ ---- 0 = smallest with x inputs
// 7 = biggest with x inputs
const devInfo_t devInfo[] = {
// MI 2nd gen; only 0x001311 is tested,
// others (starting with MI-250) according to https://github.com/lumapu/ahoy/issues/1111#issuecomment-1698100571
{ 0x000111, 250 },
{ 0x000311, 300 },
{ 0x000411, 350 },
{ 0x001111, 500 },
{ 0x001311, 600 },
{ 0x001321, 600 },
{ 0x001421, 700 },
{ 0x001411, 700 },
{ 0x002111, 1000 },
{ 0x002311, 1200 },
{ 0x002511, 1500 },
{ 0x002411, 1500 },
// MI 3rd gen
{ 0x100000, 250 },
{ 0x100010, 300 },
{ 0x100020, 350 },
{ 0x100030, 400 },
{ 0x100100, 500 },
{ 0x100110, 600 },
{ 0x100120, 700 },
{ 0x100130, 800 },
{ 0x100200, 1000 },
{ 0x100210, 1200 },
{ 0x100230, 1500 },
// HM
{ 0x101010, 300 },
{ 0x101020, 350 },
{ 0x101030, 400 },
{ 0x101040, 400 },
{ 0x101110, 600 }, // [TSOL800(DE) ..20, HWv=??], [HM-600 ..20, HWv=2.66]
{ 0x101110, 600 }, // [TSOL800(DE) ..20, HWv=2.66], [HM-600 ..20, HWv=2.66]
{ 0x101120, 700 },
{ 0x101130, 800 },
{ 0x101140, 800 },
{ 0x101210, 1200 }, // ..00
{ 0x101200, 1000 },
{ 0x101210, 1200 },
{ 0x101230, 1500 },
// HMS
@ -281,18 +321,19 @@ const devInfo_t devInfo[] = {
{ 0x101051, 450 },
{ 0x101071, 500 },
{ 0x102111, 600 },
{ 0x101120, 700 },
{ 0x102141, 800 },
{ 0x101151, 900 },
{ 0x102171, 1000 },
{ 0x102241, 1600 },
{ 0x101251, 1800 },
{ 0x102251, 1800 },
{ 0x101271, 2000 }, // ..00
{ 0x102271, 2000 },
{ 0x101271, 2000 }, // v1 grey backplane, 14A
{ 0x102271, 2000 }, // v2 black backplane, 16A
// HMT
{ 0x103311, 1800 },
{ 0x103331, 2250 } // ..00
{ 0x103331, 2250 }
};
#endif /*__HM_DEFINES_H__*/

9
src/hm/hmInverter.h

@ -210,8 +210,13 @@ class Inverter {
enqueCommand<InfoCommand>(InverterDevInform_Simple); // hardware version
enqueCommand<InfoCommand>(RealTimeRunData_Debug); // live data
} else if (ivGen == IV_MI){
if (getFwVersion() == 0)
enqueCommand<InfoCommand>(InverterDevInform_All); // firmware version; might not work, esp. for 1/2 ch hardware
if (getFwVersion() == 0) {
enqueCommand<InfoCommand>(InverterDevInform_All); // hard- and firmware version
} else {
record_t<> *rec = getRecordStruct(InverterDevInform_Simple);
if (getChannelFieldValue(CH0, FLD_PART_NUM, rec) == 0)
enqueCommand<InfoCommand>(InverterDevInform_All); // hard- and firmware version for missing HW part nr, delivered by frame 1
}
if (type == INV_TYPE_4CH) {
enqueCommand<InfoCommand>(0x36);
} else {

3
src/hm/hmPayload.h

@ -347,6 +347,9 @@ class HmPayload {
yield();
}
}
if( (InverterDevInform_All == mPayload[iv->id].txCmd) && (mHighPrioIv == NULL) ) // process next request immediately if possible
mHighPrioIv = iv;
} else {
DPRINT(DBG_ERROR, F("plausibility check failed, expected "));
DBGPRINT(String(rec->pyldLen));

7
src/hm/hmRadio.h

@ -32,7 +32,7 @@ class HmRadio {
public:
HmRadio() : mNrf24(CE_PIN, CS_PIN, SPI_SPEED) {
if(mSerialDebug) {
DPRINT(DBG_VERBOSE, F("hmRadio.h : HmRadio():mNrf24(CE_PIN: "));
DPRINT(DBG_VERBOSE, F("hmRadio.h : HmRadio():mNrf24(CE_PIN: "));
DBGPRINT(String(CE_PIN));
DBGPRINT(F(", CS_PIN: "));
DBGPRINT(String(CS_PIN));
@ -117,8 +117,9 @@ class HmRadio {
if(mNrf24.isChipConnected()) {
DPRINTLN(DBG_INFO, F("Radio Config:"));
mNrf24.printPrettyDetails();
}
else
DPRINT(DBG_INFO, F("DTU_SN: 0x"));
DBGPRINTLN(String(DTU_RADIO_ID, HEX));
} else
DPRINTLN(DBG_WARN, F("WARNING! your NRF24 module can't be reached, check the wiring"));
}

72
src/hm/miPayload.h

@ -193,6 +193,8 @@ class MiPayload {
record_t<> *rec = iv->getRecordStruct(InverterDevInform_All); // choose the record structure
rec->ts = mPayload[iv->id].ts;
mPayload[iv->id].gotFragment = true;
if(mHighPrioIv == NULL) // process next request immediately if possible
mHighPrioIv = iv;
/*
Polling the device software and hardware version number command
@ -219,12 +221,13 @@ case InverterDevInform_All:
rec->assign = (byteAssign_t *)InfoAssignment;
rec->pyldLen = HMINFO_PAYLOAD_LEN;
break;
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_FW_BUILD_HOUR_MINUTE, UNIT_NONE, CH0, 6, 2, 1 },
{ FLD_HW_ID, UNIT_NONE, CH0, 8, 2, 1 }
{ FLD_BOOTLOADER_VER, UNIT_NONE, CH0, 8, 2, 1 }
};
*/
@ -237,27 +240,48 @@ const byteAssign_t InfoAssignment[] = {
mPayload[iv->id].gotFragment = true;
if(mSerialDebug) {
DPRINT_IVID(DBG_INFO, iv->id);
DPRINT(DBG_INFO,F("HW_VER is "));
DBGPRINTLN(String((p->packet[24] << 8) + p->packet[25]));
DPRINT(DBG_INFO,F("HW_VER is "));
DBGPRINTLN(String((p->packet[24] << 8) + p->packet[25]));
}
record_t<> *rec = iv->getRecordStruct(InverterDevInform_Simple); // choose the record structure
rec->ts = mPayload[iv->id].ts;
iv->setValue(1, rec, (uint32_t) ((p->packet[24] << 8) + p->packet[25])/1);
//notify(InverterDevInform_All, iv);
//28737
} else if ( p->packet[9] == 0x01 || p->packet[9] == 0x10 ) {//second frame for MI, 3rd gen. answers in 0x10
DPRINT_IVID(DBG_INFO, iv->id);
if ( p->packet[9] == 0x01 ) {
DBGPRINTLN(F("got 2nd frame (hw info)"));
DPRINT(DBG_INFO,F("HW_PartNo "));
DBGPRINTLN(String((uint32_t) (((p->packet[10] << 8) | p->packet[11]) << 8 | p->packet[12]) << 8 | p->packet[13]));
/* according to xlsx (different start byte -1!)
byte[11] to byte[14] HW_PN
byte[15] byte[16] HW_FB_TLmValue
byte[17] byte[18] HW_FB_ReSPRT
byte[19] byte[20] HW_GridSamp_ResValule
byte[21] byte[22] HW_ECapValue
byte[23] to byte[26] Matching_APPFW_PN
*/
DPRINT(DBG_INFO,F("HW_PartNo "));
DBGPRINTLN(String((uint32_t) (((p->packet[10] << 8) | p->packet[11]) << 8 | p->packet[12]) << 8 | p->packet[13]));
mPayload[iv->id].gotFragment = true;
iv->setValue(iv->getPosByChFld(0, FLD_YT, rec), rec, (float) ((p->packet[20] << 8) + p->packet[21])/1);
record_t<> *rec = iv->getRecordStruct(InverterDevInform_Simple); // choose the record structure
rec->ts = mPayload[iv->id].ts;
iv->setValue(0, rec, (uint32_t) ((((p->packet[10] << 8) | p->packet[11]) << 8 | p->packet[12]) << 8 | p->packet[13])/1);
if(mSerialDebug) {
DPRINT(DBG_INFO,F("HW_FB_TLmValue "));
DBGPRINTLN(String((p->packet[14] << 8) + p->packet[15]));
DPRINT(DBG_INFO,F("HW_FB_ReSPRT "));
DBGPRINTLN(String((p->packet[16] << 8) + p->packet[17]));
DPRINT(DBG_INFO,F("HW_GridSamp_ResValule "));
DBGPRINTLN(String((p->packet[18] << 8) + p->packet[19]));
DPRINT(DBG_INFO,F("HW_FB_TLmValue "));
DBGPRINTLN(String((p->packet[14] << 8) + p->packet[15]));
DPRINT(DBG_INFO,F("HW_FB_ReSPRT "));
DBGPRINTLN(String((p->packet[16] << 8) + p->packet[17]));
DPRINT(DBG_INFO,F("HW_GridSamp_ResValule "));
DBGPRINTLN(String((p->packet[18] << 8) + p->packet[19]));
DPRINT(DBG_INFO,F("HW_ECapValue "));
DBGPRINTLN(String((p->packet[20] << 8) + p->packet[21]));
}
DPRINT(DBG_INFO,F("Matching_APPFW_PN "));
DBGPRINTLN(String((uint32_t) (((p->packet[22] << 8) | p->packet[23]) << 8 | p->packet[24]) << 8 | p->packet[25]));
}
//notify(InverterDevInform_Simple, iv);
notify(InverterDevInform_All, iv);
} else {
DBGPRINTLN(F("3rd gen. inverter!")); // see table in OpenDTU code, DevInfoParser.cpp devInfo[]
}
@ -265,6 +289,22 @@ const byteAssign_t InfoAssignment[] = {
} else if ( p->packet[9] == 0x12 ) {//3rd frame
DPRINT_IVID(DBG_INFO, iv->id);
DBGPRINTLN(F("got 3rd frame (hw info)"));
/* according to xlsx (different start byte -1!)
byte[11] byte[12] APPFW_MINVER
byte[13] byte[14] HWInfoAddr
byte[15] byte[16] PNInfoCRC_gusv
byte[15] byte[16] PNInfoCRC_gusv
*/
if(mSerialDebug) {
DPRINT(DBG_INFO,F("APPFW_MINVER "));
DBGPRINTLN(String((p->packet[10] << 8) + p->packet[11]));
DPRINT(DBG_INFO,F("HWInfoAddr "));
DBGPRINTLN(String((p->packet[12] << 8) + p->packet[13]));
DPRINT(DBG_INFO,F("PNInfoCRC_gusv "));
DBGPRINTLN(String((p->packet[14] << 8) + p->packet[15]));
DPRINT(DBG_INFO,F("PNInfoCRC_gusv (pt. 2?) "));
DBGPRINTLN(String((p->packet[16] << 8) + p->packet[17]));
}
iv->setQueuedCmdFinished();
mPayload[iv->id].complete = true;
mStat->rxSuccess++;
@ -697,7 +737,7 @@ const byteAssign_t InfoAssignment[] = {
uint8_t txCmd = mPayload[id].txCmd;
if(!*complete) {
DPRINTLN(DBG_VERBOSE, F("incomlete, txCmd is 0x") + String(txCmd, HEX));
DPRINTLN(DBG_VERBOSE, F("incomplete, txCmd is 0x") + String(txCmd, HEX));
//DBGHEXLN(txCmd);
if (txCmd == 0x09 || txCmd == 0x11 || (txCmd >= 0x36 && txCmd <= 0x39))
return false;
@ -728,8 +768,8 @@ const byteAssign_t InfoAssignment[] = {
*/
void reset(uint8_t id, bool clrSts = false) {
DPRINT_IVID(DBG_INFO, id);
DBGPRINTLN(F("resetPayload"));
//DPRINT_IVID(DBG_INFO, id);
//DBGPRINTLN(F("resetPayload"));
memset(mPayload[id].len, 0, MAX_PAYLOAD_ENTRIES);
mPayload[id].gotFragment = false;
/*mPayload[id].maxPackId = MAX_PAYLOAD_ENTRIES;

16
src/hms/hmsRadio.h

@ -25,7 +25,8 @@ class CmtRadio {
typedef Cmt2300a<SpiType> CmtType;
public:
CmtRadio() {
mDtuSn = DTU_SN;
mDtuSn = DTU_SN;
mCmtAvail = false;
}
void setup(uint8_t pinCsb, uint8_t pinFcsb, bool genDtuSn = true) {
@ -63,6 +64,10 @@ class CmtRadio {
mSerialDebug = true;
}
bool cmtIsAvail() {
return mCmtAvail;
}
void sendControlPacket(const uint64_t *ivId, uint8_t cmd, uint16_t *data, bool isRetransmit) {
DPRINT(DBG_INFO, F("sendControlPacket cmd: 0x"));
DBGHEXLN(cmd);
@ -143,10 +148,14 @@ class CmtRadio {
inline void reset(bool genDtuSn) {
if(genDtuSn)
generateDtuSn();
if(!mCmt.reset())
if(!mCmt.reset()) {
mCmtAvail = false;
DPRINTLN(DBG_WARN, F("Initializing CMT2300A failed!"));
else
}
else {
mCmtAvail = true;
mCmt.goRx();
}
mSendCnt = 0;
mRetransmits = 0;
@ -208,6 +217,7 @@ class CmtRadio {
bool mSerialDebug;
bool mIrqRcvd;
bool mRqstGetRx;
bool mCmtAvail;
};
#endif /*__HMS_RADIO_H__*/

6
src/platformio.ini

@ -127,8 +127,6 @@ monitor_filters =
platform = espressif32@6.3.2
board = esp32-s3-devkitc-1
upload_protocol = esp-builtin
debug_tool = esp-builtin
debug_speed = 12000
build_flags = ${env.build_flags}
-DDEF_NRF_CS_PIN=37
-DDEF_NRF_CE_PIN=38
@ -142,5 +140,7 @@ build_flags = ${env.build_flags}
-DDEF_LED0=18
-DDEF_LED1=17
-DLED_ACTIVE_HIGH
-DARDUINO_USB_MODE=1
-DARDUINO_USB_CDC_ON_BOOT=1
monitor_filters =
esp32_exception_decoder
esp32_exception_decoder, colorize

12
src/plugins/Display/Display.h

@ -35,7 +35,7 @@ class Display {
case 3: mMono = new DisplayMono84X48(); break;
case 4: mMono = new DisplayMono128X32(); break;
case 5: mMono = new DisplayMono64X48(); break;
case 6: mMono = new DisplayMono128X64(); break;
#if defined(ESP32)
case 10:
mMono = NULL; // ePaper does not use this
@ -59,13 +59,16 @@ class Display {
void tickerSecond() {
if (mMono != NULL)
mMono->loop();
mMono->loop(mCfg->contrast);
if (mNewPayload || ((++mLoopCnt % 10) == 0)) {
if (mNewPayload || (((++mLoopCnt) % 30) == 0)) {
mNewPayload = false;
mLoopCnt = 0;
DataScreen();
}
#if defined(ESP32)
mEpaper.tickerSecond();
#endif
}
private:
@ -102,13 +105,10 @@ class Display {
}
#if defined(ESP32)
else if (mCfg->type == 10) {
mEpaper.loop(totalPower, totalYieldDay, totalYieldTotal, isprod);
mRefreshCycle++;
}
#endif
#if defined(ESP32)
if (mRefreshCycle > 480) {
mEpaper.fullRefresh();
mRefreshCycle = 0;

2
src/plugins/Display/Display_Mono.h

@ -23,7 +23,7 @@ class DisplayMono {
virtual void init(uint8_t type, uint8_t rot, uint8_t cs, uint8_t dc, uint8_t reset, uint8_t clock, uint8_t data, uint32_t* utcTs, const char* version) = 0;
virtual void config(bool enPowerSafe, bool enScreenSaver, uint8_t lum) = 0;
virtual void loop(void) = 0;
virtual void loop(uint8_t lum) = 0;
virtual void disp(float totalPower, float totalYieldDay, float totalYieldTotal, uint8_t isprod) = 0;
protected:

7
src/plugins/Display/Display_Mono_128X32.h

@ -46,11 +46,16 @@ class DisplayMono128X32 : public DisplayMono {
mLuminance = lum;
}
void loop(void) {
void loop(uint8_t lum) {
if (mEnPowerSafe) {
if (mTimeout != 0)
mTimeout--;
}
if(mLuminance != lum) {
mLuminance = lum;
mDisplay->setContrast(mLuminance);
}
}
void disp(float totalPower, float totalYieldDay, float totalYieldTotal, uint8_t isprod) {

11
src/plugins/Display/Display_Mono_128X64.h

@ -27,10 +27,12 @@ class DisplayMono128X64 : public DisplayMono {
case 1:
mDisplay = new U8G2_SSD1306_128X64_NONAME_F_HW_I2C(rot, reset, clock, data);
break;
default:
case 2:
mDisplay = new U8G2_SH1106_128X64_NONAME_F_HW_I2C(rot, reset, clock, data);
break;
case 6:
mDisplay = new U8G2_SSD1309_128X64_NONAME0_F_HW_I2C(rot, reset, clock, data);
break;
}
mUtcTs = utcTs;
@ -52,11 +54,16 @@ class DisplayMono128X64 : public DisplayMono {
mLuminance = lum;
}
void loop(void) {
void loop(uint8_t lum) {
if (mEnPowerSafe) {
if (mTimeout != 0)
mTimeout--;
}
if(mLuminance != lum) {
mLuminance = lum;
mDisplay->setContrast(mLuminance);
}
}
void disp(float totalPower, float totalYieldDay, float totalYieldTotal, uint8_t isprod) {

9
src/plugins/Display/Display_Mono_64X48.h

@ -47,10 +47,15 @@ class DisplayMono64X48 : public DisplayMono {
mLuminance = lum;
}
void loop(void) {
void loop(uint8_t lum) {
if (mEnPowerSafe) {
if (mTimeout != 0)
mTimeout--;
mTimeout--;
}
if(mLuminance != lum) {
mLuminance = lum;
mDisplay->setContrast(mLuminance);
}
}

9
src/plugins/Display/Display_Mono_84X48.h

@ -47,10 +47,15 @@ class DisplayMono84X48 : public DisplayMono {
mLuminance = lum;
}
void loop(void) {
void loop(uint8_t lum) {
if (mEnPowerSafe) {
if (mTimeout != 0)
mTimeout--;
mTimeout--;
}
if(mLuminance != lum) {
mLuminance = lum;
mDisplay->setContrast(mLuminance);
}
}

198
src/plugins/Display/Display_ePaper.cpp

@ -1,9 +1,9 @@
#include "Display_ePaper.h"
#ifdef ESP8266
#include <ESP8266WiFi.h>
#include <ESP8266WiFi.h>
#elif defined(ESP32)
#include <WiFi.h>
#include <WiFi.h>
#endif
#include "../../utils/helper.h"
#include "imagedata.h"
@ -26,6 +26,9 @@ DisplayEPaper::DisplayEPaper() {
void DisplayEPaper::init(uint8_t type, uint8_t _CS, uint8_t _DC, uint8_t _RST, uint8_t _BUSY, uint8_t _SCK, uint8_t _MOSI, uint32_t *utcTs, const char *version) {
mUtcTs = utcTs;
mRefreshState = RefreshStatus::LOGO;
mSecondCnt = 0;
if (type == 10) {
Serial.begin(115200);
_display = new GxEPD2_BW<GxEPD2_150_BN, GxEPD2_150_BN::HEIGHT>(GxEPD2_150_BN(_CS, _DC, _RST, _BUSY));
@ -39,23 +42,7 @@ void DisplayEPaper::init(uint8_t type, uint8_t _CS, uint8_t _DC, uint8_t _RST, u
_display->init(115200, true, 20, false);
_display->setRotation(mDisplayRotation);
_display->setFullWindow();
// Logo
_display->fillScreen(GxEPD_BLACK);
_display->drawBitmap(0, 0, logo, 200, 200, GxEPD_WHITE);
while (_display->nextPage())
;
// clean the screen
delay(2000);
_display->fillScreen(GxEPD_WHITE);
while (_display->nextPage())
;
headlineIP();
// call the PowerPage to change the PV Power Values
actualPowerPaged(0, 0, 0, 0);
_version = version;
}
}
@ -66,15 +53,51 @@ void DisplayEPaper::config(uint8_t rotation, bool enPowerSafe) {
//***************************************************************************
void DisplayEPaper::fullRefresh() {
// screen complete black
_display->fillScreen(GxEPD_BLACK);
while (_display->nextPage())
;
delay(2000);
// screen complete white
_display->fillScreen(GxEPD_WHITE);
while (_display->nextPage())
;
if(RefreshStatus::DONE != mRefreshState)
return;
mSecondCnt = 2;
mRefreshState = RefreshStatus::BLACK;
}
//***************************************************************************
void DisplayEPaper::refreshLoop() {
switch(mRefreshState) {
case RefreshStatus::LOGO:
_display->fillScreen(GxEPD_BLACK);
_display->drawBitmap(0, 0, logo, 200, 200, GxEPD_WHITE);
mNextRefreshState = RefreshStatus::PARTITIALS;
mRefreshState = RefreshStatus::WAIT;
break;
case RefreshStatus::BLACK:
_display->fillScreen(GxEPD_BLACK);
mNextRefreshState = RefreshStatus::WHITE;
mRefreshState = RefreshStatus::WAIT;
break;
case RefreshStatus::WHITE:
if(mSecondCnt == 0) {
_display->fillScreen(GxEPD_WHITE);
mNextRefreshState = RefreshStatus::PARTITIALS;
mRefreshState = RefreshStatus::WAIT;
}
break;
case RefreshStatus::WAIT:
if(!_display->nextPage())
mRefreshState = mNextRefreshState;
break;
case RefreshStatus::PARTITIALS:
headlineIP();
versionFooter();
mSecondCnt = 4; // display Logo time during boot up
mRefreshState = RefreshStatus::DONE;
break;
default: // RefreshStatus::DONE
break;
}
}
//***************************************************************************
void DisplayEPaper::headlineIP() {
@ -123,6 +146,26 @@ void DisplayEPaper::lastUpdatePaged() {
} while (_display->nextPage());
}
//***************************************************************************
void DisplayEPaper::versionFooter() {
int16_t tbx, tby;
uint16_t tbw, tbh;
_display->setFont(&FreeSans9pt7b);
_display->setTextColor(GxEPD_WHITE);
_display->setPartialWindow(0, _display->height() - mHeadFootPadding, _display->width(), mHeadFootPadding);
_display->fillScreen(GxEPD_BLACK);
do {
snprintf(_fmtText, sizeof(_fmtText), "Version: %s", _version);
_display->getTextBounds(_fmtText, 0, 0, &tbx, &tby, &tbw, &tbh);
uint16_t x = ((_display->width() - tbw) / 2) - tbx;
_display->setCursor(x, (_display->height() - 3));
_display->println(_fmtText);
} while (_display->nextPage());
}
//***************************************************************************
void DisplayEPaper::offlineFooter() {
int16_t tbx, tby;
uint16_t tbw, tbh;
@ -154,18 +197,20 @@ void DisplayEPaper::actualPowerPaged(float totalPower, float totalYieldDay, floa
_display->setPartialWindow(0, mHeadFootPadding, _display->width(), _display->height() - (mHeadFootPadding * 2));
_display->fillScreen(GxEPD_WHITE);
do {
// actual Production
if (totalPower > 9999) {
snprintf(_fmtText, sizeof(_fmtText), "%.1f kW", (totalPower / 10000));
snprintf(_fmtText, sizeof(_fmtText), "%.1f kW", (totalPower / 1000));
_changed = true;
} else if ((totalPower > 0) && (totalPower <= 9999)) {
snprintf(_fmtText, sizeof(_fmtText), "%.0f W", totalPower);
_changed = true;
} else {
} else
snprintf(_fmtText, sizeof(_fmtText), "offline");
}
if (totalPower == 0){
_display->fillRect(0, mHeadFootPadding, 200,200, GxEPD_BLACK);
if ((totalPower == 0) && (mEnPowerSafe)) {
_display->fillRect(0, mHeadFootPadding, 200, 200, GxEPD_BLACK);
_display->drawBitmap(0, 0, logo, 200, 200, GxEPD_WHITE);
} else {
_display->getTextBounds(_fmtText, 0, 0, &tbx, &tby, &tbw, &tbh);
@ -173,42 +218,69 @@ void DisplayEPaper::actualPowerPaged(float totalPower, float totalYieldDay, floa
_display->setCursor(x, mHeadFootPadding + tbh + 10);
_display->print(_fmtText);
if ((totalYieldDay > 0) && (totalYieldTotal > 0)) {
// Today Production
bool kwh = (totalYieldDay > 9999);
if(kwh)
snprintf(_fmtText, _display->width(), "%.1f", (totalYieldDay / 1000));
else
snprintf(_fmtText, _display->width(), "%.0f", (totalYieldDay));
_display->setFont(&FreeSans18pt7b);
y = _display->height() / 2;
_display->setCursor(5, y);
_display->getTextBounds(_fmtText, 0, 0, &tbx, &tby, &tbw, &tbh);
_display->drawInvertedBitmap(5, ((kwh) ? (y - ((tbh + 30) / 2)) : (y - tbh)), myToday, 30, 30, GxEPD_BLACK);
x = ((_display->width() - tbw - 20) / 2) - tbx;
_display->setCursor(x, y);
_display->print(_fmtText);
_display->setCursor(_display->width() - ((kwh) ? 50 : 38), y);
_display->setFont(&FreeSans12pt7b);
_display->println((kwh) ? "kWh" : "Wh");
y = y + tbh + 15;
// Total Production
bool mwh = (totalYieldTotal > 9999);
if(mwh)
snprintf(_fmtText, _display->width(), "%.1f", (totalYieldTotal / 1000));
else
snprintf(_fmtText, _display->width(), "%.0f", (totalYieldTotal));
_display->setFont(&FreeSans18pt7b);
_display->setCursor(5, y);
_display->getTextBounds(_fmtText, 0, 0, &tbx, &tby, &tbw, &tbh);
_display->drawInvertedBitmap(5, y - tbh, mySigma, 30, 30, GxEPD_BLACK);
x = ((_display->width() - tbw - 20) / 2) - tbx;
_display->setCursor(x, y);
_display->print(_fmtText);
_display->setCursor(_display->width() - ((mwh) ? 59 : 50), y);
_display->setFont(&FreeSans12pt7b);
_display->println((mwh) ? "MWh" : "kWh");
}
// Inverter online
_display->setFont(&FreeSans12pt7b);
y = _display->height() / 2;
_display->setCursor(5, y);
_display->print("today:");
snprintf(_fmtText, _display->width(), "%.0f", totalYieldDay);
y = _display->height() - (mHeadFootPadding + 10);
snprintf(_fmtText, sizeof(_fmtText), " %d online", isprod);
_display->getTextBounds(_fmtText, 0, 0, &tbx, &tby, &tbw, &tbh);
x = ((_display->width() - tbw) / 2) - tbx;
_display->drawInvertedBitmap(10, y - tbh, myWR, 20, 20, GxEPD_BLACK);
x = ((_display->width() - tbw - 20) / 2) - tbx;
_display->setCursor(x, y);
_display->print(_fmtText);
_display->setCursor(_display->width() - 38, y);
_display->println("Wh");
y = y + tbh + 7;
_display->setCursor(5, y);
_display->print("total:");
snprintf(_fmtText, _display->width(), "%.1f", totalYieldTotal);
_display->getTextBounds(_fmtText, 0, 0, &tbx, &tby, &tbw, &tbh);
x = ((_display->width() - tbw) / 2) - tbx;
_display->setCursor(x, y);
_display->print(_fmtText);
_display->setCursor(_display->width() - 50, y);
_display->println("kWh");
_display->setCursor(10, _display->height() - (mHeadFootPadding + 10));
snprintf(_fmtText, sizeof(_fmtText), "%d Inverter online", isprod);
_display->println(_fmtText);
}
yield();
} while (_display->nextPage());
}
//***************************************************************************
void DisplayEPaper::loop(float totalPower, float totalYieldDay, float totalYieldTotal, uint8_t isprod) {
if(RefreshStatus::DONE != mRefreshState)
return;
// check if the IP has changed
if (_settedIP != WiFi.localIP().toString().c_str()) {
if (_settedIP != WiFi.localIP().toString()) {
// save the new IP and call the Headline Function to adapt the Headline
_settedIP = WiFi.localIP().toString().c_str();
_settedIP = WiFi.localIP().toString();
headlineIP();
}
@ -219,10 +291,16 @@ void DisplayEPaper::loop(float totalPower, float totalYieldDay, float totalYield
if ((isprod > 0) && (_changed)) {
_changed = false;
lastUpdatePaged();
} else if((0 == totalPower) && (mEnPowerSafe))
} else if ((0 == totalPower) && (mEnPowerSafe))
offlineFooter();
_display->powerOff();
}
//***************************************************************************
#endif // ESP32
void DisplayEPaper::tickerSecond() {
if(mSecondCnt != 0)
mSecondCnt--;
refreshLoop();
}
#endif // ESP32

60
src/plugins/Display/Display_ePaper.h

@ -24,28 +24,42 @@
// GDEH0154D67 1.54" b/w 200x200
class DisplayEPaper {
public:
DisplayEPaper();
void fullRefresh();
void init(uint8_t type, uint8_t _CS, uint8_t _DC, uint8_t _RST, uint8_t _BUSY, uint8_t _SCK, uint8_t _MOSI, uint32_t *utcTs, const char* version);
void config(uint8_t rotation, bool enPowerSafe);
void loop(float totalPower, float totalYieldDay, float totalYieldTotal, uint8_t isprod);
private:
void headlineIP();
void actualPowerPaged(float _totalPower, float _totalYieldDay, float _totalYieldTotal, uint8_t _isprod);
void lastUpdatePaged();
void offlineFooter();
uint8_t mDisplayRotation;
bool _changed = false;
char _fmtText[35];
const char* _settedIP;
uint8_t mHeadFootPadding;
GxEPD2_GFX* _display;
uint32_t *mUtcTs;
bool mEnPowerSafe;
public:
DisplayEPaper();
void fullRefresh();
void init(uint8_t type, uint8_t _CS, uint8_t _DC, uint8_t _RST, uint8_t _BUSY, uint8_t _SCK, uint8_t _MOSI, uint32_t* utcTs, const char* version);
void config(uint8_t rotation, bool enPowerSafe);
void loop(float totalPower, float totalYieldDay, float totalYieldTotal, uint8_t isprod);
void refreshLoop();
void tickerSecond();
private:
void headlineIP();
void actualPowerPaged(float _totalPower, float _totalYieldDay, float _totalYieldTotal, uint8_t _isprod);
void lastUpdatePaged();
void offlineFooter();
void versionFooter();
enum class RefreshStatus : uint8_t {
DONE,
BLACK,
WHITE,
WAIT,
PARTITIALS,
LOGO
};
uint8_t mDisplayRotation;
bool _changed = false;
char _fmtText[35];
String _settedIP;
uint8_t mHeadFootPadding;
GxEPD2_GFX* _display;
uint32_t* mUtcTs;
bool mEnPowerSafe;
const char* _version;
RefreshStatus mRefreshState, mNextRefreshState;
uint8_t mSecondCnt;
};
#endif // ESP32
#endif // ESP32

41
src/plugins/Display/imagedata.h

@ -9,6 +9,47 @@
#include <pgmspace.h>
#endif
// 'Sigma', 30x30px
const unsigned char mySigma[] PROGMEM = {
0x80, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xf0, 0x3f, 0xff, 0xff, 0xf0,
0x3f, 0x80, 0x07, 0xf0, 0x3f, 0x80, 0x07, 0xf0, 0x3f, 0x9f, 0xe7, 0xf0, 0x3f, 0xcf, 0xe7, 0xf0,
0x3f, 0xcf, 0xff, 0xf0, 0x3f, 0xe7, 0xff, 0xf0, 0x3f, 0xe7, 0xff, 0xf0, 0x3f, 0xf3, 0xff, 0xf0,
0x3f, 0xf3, 0xff, 0xf0, 0x3f, 0xf9, 0xff, 0xf0, 0x3f, 0xf8, 0xff, 0xf0, 0x3f, 0xf8, 0xff, 0xf0,
0x3f, 0xf9, 0xff, 0xf0, 0x3f, 0xf3, 0xff, 0xf0, 0x3f, 0xf3, 0xff, 0xf0, 0x3f, 0xe7, 0xff, 0xf0,
0x3f, 0xe7, 0xff, 0xf0, 0x3f, 0xcf, 0xff, 0xf0, 0x3f, 0xcf, 0xe7, 0xf0, 0x3f, 0x9f, 0xe7, 0xf0,
0x3f, 0x80, 0x07, 0xf0, 0x3f, 0x80, 0x07, 0xf0, 0x3f, 0xff, 0xff, 0xf0, 0x3f, 0xff, 0xff, 0xf0,
0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x04
};
// 'Sun', 30x30px
const unsigned char mySun[] PROGMEM = {
0x80, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xf0, 0x3f, 0xff, 0xfb, 0xf0,
0x3f, 0xfc, 0xfb, 0xf0, 0x3f, 0xfc, 0xfb, 0xf0, 0x3e, 0xfc, 0xf3, 0xf0, 0x3c, 0x7c, 0xf3, 0xf0,
0x3e, 0x30, 0x33, 0xf0, 0x3f, 0x0f, 0xcb, 0xf0, 0x3f, 0x9f, 0xeb, 0xf0, 0x3f, 0xbf, 0xeb, 0xf0,
0x3f, 0x7f, 0xd8, 0x30, 0x3f, 0x7f, 0xdf, 0xb0, 0x30, 0x7f, 0xdf, 0xb0, 0x30, 0x7f, 0xbf, 0x70,
0x3f, 0x7f, 0xbf, 0x70, 0x3f, 0x7f, 0x83, 0x70, 0x3f, 0xbf, 0xf2, 0xf0, 0x3f, 0x9f, 0xe2, 0xf0,
0x3f, 0x0f, 0xca, 0xf0, 0x3e, 0x30, 0x39, 0xf0, 0x3c, 0x7c, 0xf9, 0xf0, 0x3e, 0xfc, 0xf9, 0xf0,
0x3f, 0xfc, 0xfb, 0xf0, 0x3f, 0xfc, 0xfb, 0xf0, 0x3f, 0xff, 0xfb, 0xf0, 0x3f, 0xff, 0xff, 0xf0,
0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x04
};
// 'Today', 30x30px
const unsigned char myToday[] PROGMEM = {
0xf3, 0xff, 0xff, 0x3c, 0xf3, 0xff, 0xff, 0x3c, 0x80, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
0x33, 0xff, 0xff, 0x30, 0x33, 0xff, 0xff, 0x30, 0x3f, 0xff, 0xff, 0xf0, 0x3f, 0xff, 0xff, 0xf0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xf0, 0x3f, 0xff, 0xff, 0xf0,
0x3f, 0xff, 0xff, 0xf0, 0x3f, 0xff, 0xfc, 0xf0, 0x3f, 0xff, 0xf8, 0xf0, 0x3f, 0xff, 0xf1, 0xf0,
0x3f, 0xff, 0xe3, 0xf0, 0x3f, 0xff, 0xc7, 0xf0, 0x3f, 0xff, 0x8f, 0xf0, 0x3f, 0xff, 0x1f, 0xf0,
0x3f, 0xfe, 0x3f, 0xf0, 0x3e, 0x7c, 0x7f, 0xf0, 0x3e, 0x38, 0xff, 0xf0, 0x3f, 0x11, 0xff, 0xf0,
0x3f, 0x83, 0xff, 0xf0, 0x3f, 0xc7, 0xff, 0xf0, 0x3f, 0xef, 0xff, 0xf0, 0x3f, 0xff, 0xff, 0xf0,
0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x04
};
// 'WR', 20x20px
const unsigned char myWR[] PROGMEM = {
0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3f, 0xff, 0x80, 0x3f, 0xff, 0x40, 0x30, 0x7e, 0xc0, 0x3f,
0xfd, 0xc0, 0x30, 0x7b, 0xc0, 0x3f, 0xf7, 0xc0, 0x3f, 0xef, 0xc0, 0x3f, 0xdf, 0xc0, 0x3f, 0xbf,
0xc0, 0x3f, 0x7f, 0xc0, 0x3e, 0xff, 0xc0, 0x3d, 0xf7, 0xc0, 0x3b, 0xea, 0xc0, 0x37, 0xfd, 0xc0,
0x2f, 0xff, 0xc0, 0x1f, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x80, 0x00, 0x10
};
// 'Logo', 200x200px
const unsigned char logo[] PROGMEM = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,

2
src/web/RestApi.h

@ -224,7 +224,7 @@ class RestApi {
#endif /* !defined(ETHERNET) */
obj[F("device_name")] = mConfig->sys.deviceName;
obj[F("dark_mode")] = (bool)mConfig->sys.darkMode;
obj[F("sched_reboot")] = (bool)mConfig->sys.schedReboot;
obj[F("sched_reboot")] = (bool)mConfig->sys.schedReboot;
obj[F("mac")] = WiFi.macAddress();
obj[F("hostname")] = mConfig->sys.deviceName;

1
src/web/html/colorBright.css

@ -8,6 +8,7 @@
--success: #009900;
--input-bg: #eee;
--table-border: #ccc;
--nav-bg: #333;
--primary: #006ec0;

1
src/web/html/colorDark.css

@ -8,6 +8,7 @@
--success: #00bb00;
--input-bg: #333;
--table-border: #333;
--nav-bg: #333;
--primary: #004d87;

2
src/web/html/setup.html

@ -869,7 +869,7 @@
}
// keep display types grouped
var opts = [[0, "None"], [2, "SH1106 1.3\" 128X64"], [5, "SSD1306 0.66\" 64X48 (Wemos OLED Shield)"], [4, "SSD1306 0.91\" 128X32"], [1, "SSD1306 0.96\" 128X64"], [3, "Nokia5110"]];
var opts = [[0, "None"], [2, "SH1106 1.3\" 128X64"], [5, "SSD1306 0.66\" 64X48 (Wemos OLED Shield)"], [4, "SSD1306 0.91\" 128X32"], [1, "SSD1306 0.96\" 128X64"], [6, "SSD1309 2.42\" 128X64"], [3, "Nokia5110"]];
if("ESP32" == type)
opts.push([10, "ePaper"]);
var dispType = sel("disp_typ", opts, obj["disp_typ"]);

29
src/web/html/style.css

@ -329,7 +329,7 @@ th {
.table td, .table th {
padding: .75rem;
border-bottom: 1px solid var(--nav-bg);
border-bottom: 1px solid var(--table-border);
}
#wrapper {
@ -737,3 +737,30 @@ h5 {
.pointer {
cursor: pointer;
}
.badge-success {
color: #fff;
background-color: #28a745;
}
.badge-warning {
color: #212529;
background-color: #ffc107;
}
.badge-error {
color: #fff;
background-color: #dc3545;
}
.badge {
display: inline-block;
padding: .25em .4em;
font-size: 75%;
font-weight: 700;
line-height: 1;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: .25rem;
}

58
src/web/html/system.html

@ -49,33 +49,47 @@
}
}
function badge(success, text) {
return ml("span", {class: "badge badge-" + ((success) ? "success" : "error")}, text);
}
function headline(text) {
return ml("div", {class: "head p-2 mt-3"}, ml("div", {class: "row"}, ml("div", {class: "col a-c"}, text)))
}
function tr(val1, val2) {
if(typeof val2 == "number")
val2 = String(val2);
return ml("tr", {}, [
ml("th", {}, val1),
ml("td", {}, val2)
]);
}
function parseRadio(obj, stat) {
const pa = ["MIN (recommended)", "LOW", "HIGH", "MAX"];
const datarate = ["1 MBps", "2 MBps", "250 kbps"];
var main = document.getElementById("radio");
var h = div(["head", "p-2"]);
var r = div(["row"]);
r.appendChild(div(["col", "a-c"], "Radio"));
h.appendChild(r);
main.appendChild(h);
main.appendChild(
genTabRow("nrf24l01" + (obj["isPVariant"] ? "+ " : ""), (obj["isconnected"] ? "is connected " : "is not connected "))
);
if(obj["isconnected"]) {
main.appendChild(genTabRow("Datarate", datarate[obj["DataRate"]]));
main.appendChild(genTabRow("Power Level", pa[obj["power_level"]]));
}
document.getElementById("radio").append(
headline("NRF Radio"),
ml("table", {class: "table"}, [
ml("tbody", {}, [
tr("NRF24L01", badge(obj.isconnected, ((obj.isconnected) ? "" : "not ") + "connected")),
tr("Power Level", pa[obj.power_level])
])
]),
main.append(
genTabRow("TX count", stat["tx_cnt"]),
genTabRow("RX success", stat["rx_success"]),
genTabRow("RX fail", stat["rx_fail"]),
genTabRow("RX no answer", stat["rx_fail_answer"]),
genTabRow("RX fragments", stat["frame_cnt"]),
genTabRow("TX retransmits", stat["retransmits"])
headline("Statistics"),
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)
])
])
);
}

Loading…
Cancel
Save