diff --git a/src/.vscode/settings.json b/src/.vscode/settings.json index 58a2c3c7..e61851fb 100644 --- a/src/.vscode/settings.json +++ b/src/.vscode/settings.json @@ -8,7 +8,7 @@ "files.eol": "\n", "files.trimTrailingWhitespace": true, "diffEditor.ignoreTrimWhitespace": true, - "files.autoSave": "afterDelay", + "files.autoSave": "off", "editor.tabSize": 4, "editor.insertSpaces": true, // `editor.tabSize` and `editor.insertSpaces` will be detected based on the file contents. @@ -79,7 +79,8 @@ "mutex": "cpp", "ranges": "cpp", "stop_token": "cpp", - "thread": "cpp" + "thread": "cpp", + "variant": "cpp" }, "cmake.configureOnOpen": false, "editor.formatOnSave": false, diff --git a/src/plugins/Display/Display.h b/src/plugins/Display/Display.h index b26218e6..9849f03e 100644 --- a/src/plugins/Display/Display.h +++ b/src/plugins/Display/Display.h @@ -10,6 +10,7 @@ #include "Display_Mono_128X32.h" #include "Display_Mono_128X64.h" #include "Display_Mono_84X48.h" +#include "Display_Mono_64X48.h" #include "Display_ePaper.h" template <class HMSYSTEM> @@ -25,31 +26,29 @@ class Display { mLoopCnt = 0; mVersion = version; - if (mCfg->type == 0) - return; + switch (mCfg->type) { + case 0: mMono = NULL; break; + case 1: // fall-through + case 2: mMono = new DisplayMono128X64(); break; + case 3: mMono = new DisplayMono84X48(); break; + case 4: mMono = new DisplayMono128X32(); break; + case 5: mMono = new DisplayMono64X48(); break; - if ((0 < mCfg->type) && (mCfg->type < 10)) { - switch (mCfg->type) { - case 2: - case 1: - default: - mMono = new DisplayMono128X64(); - break; - case 3: - mMono = new DisplayMono84X48(); - break; - case 4: - mMono = new DisplayMono128X32(); - break; - } - mMono->config(mCfg->pwrSaveAtIvOffline, mCfg->pxShift, mCfg->contrast); - mMono->init(mCfg->type, mCfg->rot, mCfg->disp_cs, mCfg->disp_dc, 0xff, mCfg->disp_clk, mCfg->disp_data, mUtcTs, mVersion); - } else if (mCfg->type >= 10) { #if defined(ESP32) - mRefreshCycle = 0; - mEpaper.config(mCfg->rot, mCfg->pwrSaveAtIvOffline); - mEpaper.init(mCfg->type, mCfg->disp_cs, mCfg->disp_dc, mCfg->disp_reset, mCfg->disp_busy, mCfg->disp_clk, mCfg->disp_data, mUtcTs, mVersion); + case 10: + mMono = NULL; // ePaper does not use this + mRefreshCycle = 0; + mEpaper.config(mCfg->rot, mCfg->pwrSaveAtIvOffline); + mEpaper.init(mCfg->type, mCfg->disp_cs, mCfg->disp_dc, mCfg->disp_reset, mCfg->disp_busy, mCfg->disp_clk, mCfg->disp_data, mUtcTs, mVersion); + break; #endif + + default: mMono = NULL; break; + } + if(mMono) + { + mMono->config(mCfg->pwrSaveAtIvOffline, mCfg->pxShift, mCfg->contrast); + mMono->init(mCfg->type, mCfg->rot, mCfg->disp_cs, mCfg->disp_dc, 0xff, mCfg->disp_clk, mCfg->disp_data, mUtcTs, mVersion); } } @@ -97,14 +96,16 @@ class Display { totalYieldTotal += iv->getChannelFieldValue(CH0, FLD_YT, rec); } - if ((0 < mCfg->type) && (mCfg->type < 10) && (mMono != NULL)) { + if (mMono ) { mMono->disp(totalPower, totalYieldDay, totalYieldTotal, isprod); - } else if (mCfg->type >= 10) { + } #if defined(ESP32) + else if (mCfg->type == 10) { + mEpaper.loop(totalPower, totalYieldDay, totalYieldTotal, isprod); mRefreshCycle++; -#endif } +#endif #if defined(ESP32) if (mRefreshCycle > 480) { @@ -126,7 +127,7 @@ class Display { #if defined(ESP32) DisplayEPaper mEpaper; #endif - DisplayMono *mMono; + DisplayMono *mMono = NULL; //default !!! }; #endif /*__DISPLAY__*/ diff --git a/src/plugins/Display/Display_Mono.h b/src/plugins/Display/Display_Mono.h index ed9154af..42eea5f3 100644 --- a/src/plugins/Display/Display_Mono.h +++ b/src/plugins/Display/Display_Mono.h @@ -35,8 +35,8 @@ class DisplayMono { uint8_t mLoopCnt; uint32_t* mUtcTs; - uint8_t mLineXOffsets[5]; - uint8_t mLineYOffsets[5]; + uint8_t mLineXOffsets[5] = {}; + uint8_t mLineYOffsets[5] = {}; uint16_t mDispY; diff --git a/src/plugins/Display/Display_Mono_128X32.h b/src/plugins/Display/Display_Mono_128X32.h index 9d5ade7e..e9e09d28 100644 --- a/src/plugins/Display/Display_Mono_128X32.h +++ b/src/plugins/Display/Display_Mono_128X32.h @@ -21,8 +21,6 @@ class DisplayMono128X32 : public DisplayMono { void init(uint8_t type, uint8_t rotation, uint8_t cs, uint8_t dc, uint8_t reset, uint8_t clock, uint8_t data, uint32_t *utcTs, const char *version) { - if((0 == type) || (type > 4)) - return; u8g2_cb_t *rot = (u8g2_cb_t *)((rotation != 0x00) ? U8G2_R2 : U8G2_R0); mType = type; diff --git a/src/plugins/Display/Display_Mono_128X64.h b/src/plugins/Display/Display_Mono_128X64.h index 3d4f91ee..a828816c 100644 --- a/src/plugins/Display/Display_Mono_128X64.h +++ b/src/plugins/Display/Display_Mono_128X64.h @@ -19,8 +19,6 @@ class DisplayMono128X64 : public DisplayMono { } void init(uint8_t type, uint8_t rotation, uint8_t cs, uint8_t dc, uint8_t reset, uint8_t clock, uint8_t data, uint32_t *utcTs, const char *version) { - if((0 == type) || (type > 4)) - return; u8g2_cb_t *rot = (u8g2_cb_t *)((rotation != 0x00) ? U8G2_R2 : U8G2_R0); mType = type; @@ -65,8 +63,7 @@ class DisplayMono128X64 : public DisplayMono { mDisplay->clearBuffer(); // set Contrast of the Display to raise the lifetime - if (3 != mType) - mDisplay->setContrast(mLuminance); + mDisplay->setContrast(mLuminance); if ((totalPower > 0) && (isprod > 0)) { mTimeout = DISP_DEFAULT_TIMEOUT; diff --git a/src/plugins/Display/Display_Mono_64X48.h b/src/plugins/Display/Display_Mono_64X48.h new file mode 100644 index 00000000..8c355322 --- /dev/null +++ b/src/plugins/Display/Display_Mono_64X48.h @@ -0,0 +1,134 @@ +//----------------------------------------------------------------------------- +// 2023 Ahoy, https://ahoydtu.de +// Creative Commons - https://creativecommons.org/licenses/by-nc-sa/4.0/deed +//----------------------------------------------------------------------------- + +#pragma once +#include "Display_Mono.h" + +class DisplayMono64X48 : public DisplayMono { + public: + DisplayMono64X48() : DisplayMono() { + mEnPowerSafe = true; + mEnScreenSaver = false; + mLuminance = 20; + mExtra = 0; + mDispY = 0; + mTimeout = DISP_DEFAULT_TIMEOUT; // interval at which to power save (milliseconds) + mUtcTs = NULL; + mType = 0; + } + + void init(uint8_t type, uint8_t rotation, uint8_t cs, uint8_t dc, uint8_t reset, uint8_t clock, uint8_t data, uint32_t *utcTs, const char *version) { + + u8g2_cb_t *rot = (u8g2_cb_t *)((rotation != 0x00) ? U8G2_R2 : U8G2_R0); + mType = type; + + // Wemos OLed Shield is not defined in u8 lib -> use nearest compatible + mDisplay = new U8G2_SSD1306_64X48_ER_F_HW_I2C(rot, reset, clock, data); + + mUtcTs = utcTs; + + mDisplay->begin(); + calcLinePositions(); + + mDisplay->clearBuffer(); + mDisplay->setContrast(mLuminance); + + printText("AHOY!", 0); + printText("ahoydtu.de", 1); + printText(version, 2); + mDisplay->sendBuffer(); + } + + void config(bool enPowerSafe, bool enScreenSaver, uint8_t lum) { + mEnPowerSafe = enPowerSafe; + mEnScreenSaver = enScreenSaver; + mLuminance = lum; + } + + void loop(void) { + if (mEnPowerSafe) { + if (mTimeout != 0) + mTimeout--; + } + } + + void disp(float totalPower, float totalYieldDay, float totalYieldTotal, uint8_t isprod) { + mDisplay->clearBuffer(); + + // set Contrast of the Display to raise the lifetime + mDisplay->setContrast(mLuminance); + + if ((totalPower > 0) && (isprod > 0)) { + mTimeout = DISP_DEFAULT_TIMEOUT; + mDisplay->setPowerSave(false); + + if (totalPower > 999) + snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%2.2f kW", (totalPower / 1000)); + else + snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%3.0f W", totalPower); + + printText(mFmtText, 0); + } else { + printText("offline", 0); + // check if it's time to enter power saving mode + if (mTimeout == 0) + mDisplay->setPowerSave(mEnPowerSafe); + } + + snprintf(mFmtText, DISP_FMT_TEXT_LEN, "D: %4.0f Wh", totalYieldDay); + printText(mFmtText, 1); + + snprintf(mFmtText, DISP_FMT_TEXT_LEN, "T: %4.0f kWh", totalYieldTotal); + printText(mFmtText, 2); + + IPAddress ip = WiFi.localIP(); + if (!(mExtra % 10) && (ip)) + printText(ip.toString().c_str(), 3); + else if (!(mExtra % 5)) { + snprintf(mFmtText, DISP_FMT_TEXT_LEN, "active Inv: %d", isprod); + printText(mFmtText, 3); + } else if (NULL != mUtcTs) + printText(ah::getTimeStr(gTimezone.toLocal(*mUtcTs)).c_str(), 3); + + mDisplay->sendBuffer(); + + mExtra++; + } + + private: + void calcLinePositions() { + uint8_t yOff = 0; + for (uint8_t i = 0; i < 4; i++) { + setFont(i); + yOff += (mDisplay->getMaxCharHeight()); + mLineYOffsets[i] = yOff; + } + } + + inline void setFont(uint8_t line) { + switch (line) { + case 0: + mDisplay->setFont(u8g2_font_fur11_tf); + break; + case 1: + case 2: + mDisplay->setFont(u8g2_font_6x10_tf); + break; + case 3: + mDisplay->setFont(u8g2_font_4x6_tr); + break; + case 4: + mDisplay->setFont(u8g2_font_4x6_tr); + break; + } + } + + void printText(const char *text, uint8_t line) { + uint8_t dispX = 0; //small display, use all we have + dispX += (mEnScreenSaver) ? (mExtra % 4) : 0; + setFont(line); + mDisplay->drawStr(dispX, mLineYOffsets[line], text); + } +}; diff --git a/src/plugins/Display/Display_Mono_84X48.h b/src/plugins/Display/Display_Mono_84X48.h index 82aa83fa..490fdf9e 100644 --- a/src/plugins/Display/Display_Mono_84X48.h +++ b/src/plugins/Display/Display_Mono_84X48.h @@ -20,8 +20,6 @@ class DisplayMono84X48 : public DisplayMono { } void init(uint8_t type, uint8_t rotation, uint8_t cs, uint8_t dc, uint8_t reset, uint8_t clock, uint8_t data, uint32_t *utcTs, const char *version) { - if((0 == type) || (type > 4)) - return; u8g2_cb_t *rot = (u8g2_cb_t *)((rotation != 0x00) ? U8G2_R2 : U8G2_R0); mType = type; @@ -33,8 +31,8 @@ class DisplayMono84X48 : public DisplayMono { calcLinePositions(); mDisplay->clearBuffer(); - if (3 != mType) - mDisplay->setContrast(mLuminance); + mDisplay->setContrast(mLuminance); + printText("AHOY!", 0); printText("ahoydtu.de", 2); printText(version, 3); @@ -58,8 +56,7 @@ class DisplayMono84X48 : public DisplayMono { mDisplay->clearBuffer(); // set Contrast of the Display to raise the lifetime - if (3 != mType) - mDisplay->setContrast(mLuminance); + mDisplay->setContrast(mLuminance); if ((totalPower > 0) && (isprod > 0)) { mTimeout = DISP_DEFAULT_TIMEOUT; @@ -95,7 +92,7 @@ class DisplayMono84X48 : public DisplayMono { mDisplay->sendBuffer(); - mExtra = 1; + mExtra++; } private: diff --git a/src/plugins/Display/Display_ePaper.cpp b/src/plugins/Display/Display_ePaper.cpp index 924961a3..74000180 100644 --- a/src/plugins/Display/Display_ePaper.cpp +++ b/src/plugins/Display/Display_ePaper.cpp @@ -26,7 +26,7 @@ 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; - if (type > 9) { + if (type == 10) { Serial.begin(115200); _display = new GxEPD2_BW<GxEPD2_150_BN, GxEPD2_150_BN::HEIGHT>(GxEPD2_150_BN(_CS, _DC, _RST, _BUSY)); hspi.begin(_SCK, _BUSY, _MOSI, _CS); diff --git a/src/web/html/setup.html b/src/web/html/setup.html index c0f7cf83..1ee056df 100644 --- a/src/web/html/setup.html +++ b/src/web/html/setup.html @@ -791,6 +791,7 @@ document.getElementsByName(i)[0].checked = obj[i]; var e = document.getElementById("dispPins"); + //KEEP this order !!! var pins = [['clock', 'disp_clk'], ['data', 'disp_data'], ['cs', 'disp_cs'], ['dc', 'disp_dc'], ['reset', 'disp_rst']]; if("ESP32" == type) pins.push(['busy', 'disp_bsy']); @@ -805,7 +806,8 @@ ); } - var opts = [[0, "None"], [1, "SSD1306 0.96\" 128X64"], [2, "SH1106 1.3\""], [3, "Nokia5110"], [4, "SSD1306 0.91\" 128X32"]]; + // 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"]]; if("ESP32" == type) opts.push([10, "ePaper"]); var dispType = sel("disp_typ", opts, obj["disp_typ"]); @@ -816,7 +818,7 @@ ]) ); dispType.addEventListener('change', (e) => { - hideDispPins(pins, e.target.value) + hideDispPins(pins, parseInt(e.target.value)) }); opts = [[0, "0°"], [2, "180°"]]; @@ -836,23 +838,26 @@ } function hideDispPins(pins, dispType) { + // create pin map for each display type. + // It depends on fix pin array (see var pins) + // var pins = [['clock', 'disp_clk'], ['data', 'disp_data'], ['cs', 'disp_cs'], ['dc', 'disp_dc'], ['reset', 'disp_rst']]; + const pinMap = new Map([ + [0, [0,0,0,0,0]], //none + [1, [1,1,0,0,0]], //SSD1306_128X64 + [2, [1,1,0,0,0]], //SH1106_128X64 + [3, [1,1,1,1,0]], //PCD8544_84X48 /nokia5110 + [4, [1,1,0,0,0]], //SSD1306_128X32 + [5, [1,1,0,0,0]], //SSD1306_64X48 + [10, [1,1,1,1,1]] //ePaper + ]) for(var i = 0; i < pins.length; i++) { var cl = document.getElementById("row_" + pins[i][1]).classList; - - if(0 == dispType) - cl.add("hide"); - else if(dispType <= 2 || dispType == 4) { // OLED - if(i < 2) - cl.remove("hide"); - else - cl.add("hide"); - } else if(dispType == 3) { // Nokia - if(i < 4) - cl.remove("hide"); - else - cl.add("hide"); - } else // ePaper + if(pinMap.get(dispType)[i]) { cl.remove("hide"); + } + else { + cl.add("hide"); + } } } diff --git a/src/web/web.h b/src/web/web.h index f9f04291..70fae62e 100644 --- a/src/web/web.h +++ b/src/web/web.h @@ -701,14 +701,15 @@ class Web { // NRF Statistics stat = mApp->getStatistics(); - uint32_t *nrfSendCnt, *nrfRetransmits; - mApp->getNrfRadioCounters(nrfSendCnt, nrfRetransmits); + uint32_t nrfSendCnt; + uint32_t nrfRetransmits; + mApp->getNrfRadioCounters(&nrfSendCnt, &nrfRetransmits); 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"), nrfSendCnt); + metrics += radioStatistic(F("retrans_cnt"), nrfRetransmits); len = snprintf((char *)buffer,maxLen,"%s",metrics.c_str()); // Next is Inverter information