From f3192b49ab82e3b9863749c3805d85e33c27234d Mon Sep 17 00:00:00 2001 From: lumapu Date: Fri, 1 Sep 2023 07:58:33 +0200 Subject: [PATCH] 0.7.46 * removed `delay` from ePaper * started improvements of `/system` * fix LEDs to check all configured inverters --- src/CHANGES.md | 5 + src/app.cpp | 26 ++-- src/app.h | 1 + src/hm/miPayload.h | 4 +- src/hms/hmsRadio.h | 16 ++- src/platformio.ini | 6 +- src/plugins/Display/Display.h | 8 +- src/plugins/Display/Display_Mono_64X48.h | 2 +- src/plugins/Display/Display_Mono_84X48.h | 2 +- src/plugins/Display/Display_ePaper.cpp | 166 +++++++++++++---------- src/plugins/Display/Display_ePaper.h | 15 +- src/web/RestApi.h | 2 +- src/web/html/colorBright.css | 1 + src/web/html/colorDark.css | 1 + src/web/html/style.css | 29 +++- src/web/html/system.html | 58 +++++--- 16 files changed, 218 insertions(+), 124 deletions(-) diff --git a/src/CHANGES.md b/src/CHANGES.md index 974c7282..c81162ad 100644 --- a/src/CHANGES.md +++ b/src/CHANGES.md @@ -1,5 +1,10 @@ # Development Changes +## 0.7.46 - 2023-09-01 +* removed `delay` from ePaper +* started improvements of `/system` +* fix LEDs to check all configured inverters + ## 0.7.45 - 2023-08-29 * change ePaper text to symbols PR #1131 * added some invertes to dev info list #1111 diff --git a/src/app.cpp b/src/app.cpp index 25a849ee..15fc432c 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -567,11 +567,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); } @@ -582,17 +582,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 { diff --git a/src/app.h b/src/app.h index fb3da7f6..5a7831c1 100644 --- a/src/app.h +++ b/src/app.h @@ -273,6 +273,7 @@ class app : public IApp, public ah::Scheduler { #endif if(mConfig->plugin.display.type != 0) mDisplay.payloadEventListener(cmd); + updateLed(); } void mqttSubRxCb(JsonObject obj); diff --git a/src/hm/miPayload.h b/src/hm/miPayload.h index e461a8dc..5619b1d6 100644 --- a/src/hm/miPayload.h +++ b/src/hm/miPayload.h @@ -728,8 +728,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; diff --git a/src/hms/hmsRadio.h b/src/hms/hmsRadio.h index dc79afcb..c515ae0e 100644 --- a/src/hms/hmsRadio.h +++ b/src/hms/hmsRadio.h @@ -25,7 +25,8 @@ class CmtRadio { typedef Cmt2300a 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__*/ diff --git a/src/platformio.ini b/src/platformio.ini index 1e61dda1..42c1557a 100644 --- a/src/platformio.ini +++ b/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 diff --git a/src/plugins/Display/Display.h b/src/plugins/Display/Display.h index 2340fd42..584d1c84 100644 --- a/src/plugins/Display/Display.h +++ b/src/plugins/Display/Display.h @@ -61,11 +61,14 @@ class Display { if (mMono != NULL) mMono->loop(); - 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; diff --git a/src/plugins/Display/Display_Mono_64X48.h b/src/plugins/Display/Display_Mono_64X48.h index 8c355322..67b38e44 100644 --- a/src/plugins/Display/Display_Mono_64X48.h +++ b/src/plugins/Display/Display_Mono_64X48.h @@ -50,7 +50,7 @@ class DisplayMono64X48 : public DisplayMono { void loop(void) { if (mEnPowerSafe) { if (mTimeout != 0) - mTimeout--; + mTimeout--; } } diff --git a/src/plugins/Display/Display_Mono_84X48.h b/src/plugins/Display/Display_Mono_84X48.h index aefe1372..9f7761dc 100644 --- a/src/plugins/Display/Display_Mono_84X48.h +++ b/src/plugins/Display/Display_Mono_84X48.h @@ -50,7 +50,7 @@ class DisplayMono84X48 : public DisplayMono { void loop(void) { if (mEnPowerSafe) { if (mTimeout != 0) - mTimeout--; + mTimeout--; } } diff --git a/src/plugins/Display/Display_ePaper.cpp b/src/plugins/Display/Display_ePaper.cpp index c2f93938..9a4b594c 100644 --- a/src/plugins/Display/Display_ePaper.cpp +++ b/src/plugins/Display/Display_ePaper.cpp @@ -21,10 +21,14 @@ DisplayEPaper::DisplayEPaper() { mHeadFootPadding = 16; } + //*************************************************************************** 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(_CS, _DC, _RST, _BUSY)); @@ -38,26 +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(); - _version = version; - versionFooter(); - - // call the PowerPage to change the PV Power Values - actualPowerPaged(0, 0, 0, 0); } } @@ -68,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() { @@ -185,9 +206,8 @@ void DisplayEPaper::actualPowerPaged(float totalPower, float totalYieldDay, floa } 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) && (mEnPowerSafe)) { _display->fillRect(0, mHeadFootPadding, 200, 200, GxEPD_BLACK); @@ -200,57 +220,43 @@ void DisplayEPaper::actualPowerPaged(float totalPower, float totalYieldDay, floa 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); - - if (totalYieldDay > 9999) { - snprintf(_fmtText, _display->width(), "%.1f", (totalYieldDay / 1000)); - _display->getTextBounds(_fmtText, 0, 0, &tbx, &tby, &tbw, &tbh); - _display->drawInvertedBitmap(5, y - ((tbh + 30) / 2), myToday, 30, 30, GxEPD_BLACK); - x = ((_display->width() - tbw - 20) / 2) - tbx; - _display->setCursor(x, y); - _display->print(_fmtText); - _display->setCursor(_display->width() - 50, y); - _display->setFont(&FreeSans12pt7b); - _display->println("kWh"); - } else if (totalYieldDay <= 9999) { - snprintf(_fmtText, _display->width(), "%.0f", (totalYieldDay)); - _display->getTextBounds(_fmtText, 0, 0, &tbx, &tby, &tbw, &tbh); - _display->drawInvertedBitmap(5, 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() - 38, y); - _display->setFont(&FreeSans12pt7b); - _display->println("Wh"); - } + _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 - _display->setFont(&FreeSans18pt7b); - _display->setCursor(5, y); - if (totalYieldTotal > 9999) { + bool mwh = (totalYieldTotal > 9999); + if(mwh) snprintf(_fmtText, _display->width(), "%.1f", (totalYieldTotal / 1000)); - _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() - 59, y); - _display->setFont(&FreeSans12pt7b); - _display->println("MWh"); - } else if (totalYieldTotal <= 9999) { + else snprintf(_fmtText, _display->width(), "%.0f", (totalYieldTotal)); - _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() - 50, y); - _display->setFont(&FreeSans12pt7b); - _display->println("kWh"); - } + + _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 @@ -263,14 +269,18 @@ void DisplayEPaper::actualPowerPaged(float totalPower, float totalYieldDay, floa _display->setCursor(x, y); _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(); } @@ -286,5 +296,11 @@ void DisplayEPaper::loop(float totalPower, float totalYieldDay, float totalYield _display->powerOff(); } + //*************************************************************************** +void DisplayEPaper::tickerSecond() { + if(mSecondCnt != 0) + mSecondCnt--; + refreshLoop(); +} #endif // ESP32 diff --git a/src/plugins/Display/Display_ePaper.h b/src/plugins/Display/Display_ePaper.h index e89c191b..2ff7e58d 100644 --- a/src/plugins/Display/Display_ePaper.h +++ b/src/plugins/Display/Display_ePaper.h @@ -30,6 +30,8 @@ class DisplayEPaper { 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(); @@ -38,15 +40,26 @@ class DisplayEPaper { void offlineFooter(); void versionFooter(); + enum class RefreshStatus : uint8_t { + DONE, + BLACK, + WHITE, + WAIT, + PARTITIALS, + LOGO + }; + uint8_t mDisplayRotation; bool _changed = false; char _fmtText[35]; - const char* _settedIP; + String _settedIP; uint8_t mHeadFootPadding; GxEPD2_GFX* _display; uint32_t* mUtcTs; bool mEnPowerSafe; const char* _version; + RefreshStatus mRefreshState, mNextRefreshState; + uint8_t mSecondCnt; }; #endif // ESP32 diff --git a/src/web/RestApi.h b/src/web/RestApi.h index 095b065e..7f722d89 100644 --- a/src/web/RestApi.h +++ b/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; diff --git a/src/web/html/colorBright.css b/src/web/html/colorBright.css index 255b5130..47382daa 100644 --- a/src/web/html/colorBright.css +++ b/src/web/html/colorBright.css @@ -8,6 +8,7 @@ --success: #009900; --input-bg: #eee; + --table-border: #ccc; --nav-bg: #333; --primary: #006ec0; diff --git a/src/web/html/colorDark.css b/src/web/html/colorDark.css index d143f8be..3d9d167a 100644 --- a/src/web/html/colorDark.css +++ b/src/web/html/colorDark.css @@ -8,6 +8,7 @@ --success: #00bb00; --input-bg: #333; + --table-border: #333; --nav-bg: #333; --primary: #004d87; diff --git a/src/web/html/style.css b/src/web/html/style.css index 4d4dc6f8..6d19bd21 100644 --- a/src/web/html/style.css +++ b/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; +} diff --git a/src/web/html/system.html b/src/web/html/system.html index d51c01cc..c504057f 100644 --- a/src/web/html/system.html +++ b/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) + ]) + ]) ); }