diff --git a/.github/workflows/compile_development.yml b/.github/workflows/compile_development.yml index 7c3e9d76..65a22730 100644 --- a/.github/workflows/compile_development.yml +++ b/.github/workflows/compile_development.yml @@ -42,10 +42,6 @@ jobs: python -m pip install --upgrade pip pip install --upgrade platformio - - name: Convert HTML files - working-directory: src/web/html - run: python convert.py - - name: Run PlatformIO run: pio run -d src --environment esp8266 --environment esp8266-prometheus --environment esp8285 --environment esp32-wroom32 --environment esp32-wroom32-prometheus --environment esp32-wroom32-ethernet --environment esp32-s2-mini --environment opendtufusion diff --git a/.github/workflows/compile_release.yml b/.github/workflows/compile_release.yml index 87cabd22..cb4979b4 100644 --- a/.github/workflows/compile_release.yml +++ b/.github/workflows/compile_release.yml @@ -46,10 +46,6 @@ jobs: python -m pip install --upgrade pip pip install --upgrade platformio - - name: Convert HTML files - working-directory: src/web/html - run: python convert.py - - name: Run PlatformIO run: pio run -d src --environment esp8266 --environment esp8266-prometheus --environment esp8285 --environment esp32-wroom32 --environment esp32-wroom32-prometheus --environment esp32-wroom32-ethernet --environment esp32-s2-mini --environment opendtufusion diff --git a/doc/prometheus_ep_description.md b/doc/prometheus_ep_description.md index 755fd1e4..711299dd 100644 --- a/doc/prometheus_ep_description.md +++ b/doc/prometheus_ep_description.md @@ -15,37 +15,40 @@ Prometheus metrics provided at `/metrics`. | channel | Channel (Module) name from setup. Label only available if max power level of module is set to non-zero. Be sure to have a cannel name set in configuration. | ## Exported Metrics -| Metric name | Type | Description | Labels | -|----------------------------------------|---------|--------------------------------------------------------|--------------| -| `ahoy_solar_info` | Gauge | Information about the AhoyDTU device | version, image, devicename | -| `ahoy_solar_uptime` | Counter | Seconds since boot of the AhoyDTU device | devicename | -| `ahoy_solar_rssi_db` | Gauge | Quality of the Wifi STA connection | devicename | -| `ahoy_solar_inverter_info` | Gauge | Information about the configured inverter(s) | name, serial | -| `ahoy_solar_inverter_enabled` | Gauge | Is the inverter enabled? | inverter | -| `ahoy_solar_inverter_is_available` | Gauge | is the inverter available? | inverter | -| `ahoy_solar_inverter_is_producing` | Gauge | Is the inverter producing? | inverter | -| `ahoy_solar_U_AC_volt` | Gauge | AC voltage of inverter [V] | inverter | -| `ahoy_solar_I_AC_ampere` | Gauge | AC current of inverter [A] | inverter | -| `ahoy_solar_P_AC_watt` | Gauge | AC power of inverter [W] | inverter | -| `ahoy_solar_Q_AC_var` | Gauge | AC reactive power[var] | inverter | -| `ahoy_solar_F_AC_hertz` | Gauge | AC frequency [Hz] | inverter | -| `ahoy_solar_PF_AC` | Gauge | AC Power factor | inverter | -| `ahoy_solar_Temp_celsius` | Gauge | Temperature of inverter | inverter | -| `ahoy_solar_ALARM_MES_ID` | Gauge | Alarm message index of inverter | inverter | -| `ahoy_solar_LastAlarmCode` | Gauge | Last alarm code from inverter | inverter | -| `ahoy_solar_YieldDay_wattHours` | Counter | Energy converted to AC per day [Wh] | inverter | -| `ahoy_solar_YieldTotal_kilowattHours` | Counter | Energy converted to AC since reset [kWh] | inverter | -| `ahoy_solar_P_DC_watt` | Gauge | DC power of inverter [W] | inverter | -| `ahoy_solar_Efficiency_ratio` | Gauge | ration AC Power over DC Power [%] | inverter | -| `ahoy_solar_U_DC_volt` | Gauge | DC voltage of channel [V] | inverter, channel | -| `ahoy_solar_I_DC_ampere` | Gauge | DC current of channel [A] | inverter, channel | -| `ahoy_solar_P_DC_watt` | Gauge | DC power of channel [P] | inverter, channel | -| `ahoy_solar_YieldDay_wattHours` | Counter | Energy converted to AC per day [Wh] | inverter, channel | -| `ahoy_solar_YieldTotal_kilowattHours` | Counter | Energy converted to AC since reset [kWh] | inverter, channel | -| `ahoy_solar_Irradiation_ratio` | Gauge | ratio DC Power over set maximum power per channel [%] | inverter, channel | -| `ahoy_solar_radio_rx_success` | Gauge | NRF24 statistic | | -| `ahoy_solar_radio_rx_fail` | Gauge | NRF24 statistic | | -| `ahoy_solar_radio_rx_fail_answer` | Gauge | NRF24 statistic | | -| `ahoy_solar_radio_frame_cnt` | Gauge | NRF24 statistic | | -| `ahoy_solar_radio_tx_cnt` | Gauge | NRF24 statistic | | +| Metric name | Type | Description | Labels | +|----------------------------------------------|---------|----------------------------------------------------------|--------------| +| `ahoy_solar_info` | Gauge | Information about the AhoyDTU device | version, image, devicename | +| `ahoy_solar_uptime` | Counter | Seconds since boot of the AhoyDTU device | devicename | +| `ahoy_solar_rssi_db` | Gauge | Quality of the Wifi STA connection | devicename | +| `ahoy_solar_inverter_info` | Gauge | Information about the configured inverter(s) | name, serial | +| `ahoy_solar_inverter_enabled` | Gauge | Is the inverter enabled? | inverter | +| `ahoy_solar_inverter_is_available` | Gauge | is the inverter available? | inverter | +| `ahoy_solar_inverter_is_producing` | Gauge | Is the inverter producing? | inverter | +| `ahoy_solar_U_AC_volt` | Gauge | AC voltage of inverter [V] | inverter | +| `ahoy_solar_I_AC_ampere` | Gauge | AC current of inverter [A] | inverter | +| `ahoy_solar_P_AC_watt` | Gauge | AC power of inverter [W] | inverter | +| `ahoy_solar_Q_AC_var` | Gauge | AC reactive power[var] | inverter | +| `ahoy_solar_F_AC_hertz` | Gauge | AC frequency [Hz] | inverter | +| `ahoy_solar_PF_AC` | Gauge | AC Power factor | inverter | +| `ahoy_solar_Temp_celsius` | Gauge | Temperature of inverter | inverter | +| `ahoy_solar_ALARM_MES_ID` | Gauge | Alarm message index of inverter | inverter | +| `ahoy_solar_LastAlarmCode` | Gauge | Last alarm code from inverter | inverter | +| `ahoy_solar_YieldDay_wattHours` | Counter | Energy converted to AC per day [Wh] | inverter,channel | +| `ahoy_solar_YieldDay_wattHours_total` | Counter | Energy converted to AC per day [Wh] for all moduls | inverter | +| `ahoy_solar_YieldTotal_kilowattHours` | Counter | Energy converted to AC since reset [kWh] | inverter,channel | +| `ahoy_solar_YieldTotal_kilowattHours_total` | Counter | Energy converted to AC since reset [kWh] for all modules | inverter | +| `ahoy_solar_P_DC_watt` | Gauge | DC power of inverter [W] | inverter | +| `ahoy_solar_Efficiency_ratio` | Gauge | ration AC Power over DC Power [%] | inverter | +| `ahoy_solar_U_DC_volt` | Gauge | DC voltage of channel [V] | inverter, channel | +| `ahoy_solar_I_DC_ampere` | Gauge | DC current of channel [A] | inverter, channel | +| `ahoy_solar_P_DC_watt` | Gauge | DC power of channel [P] | inverter, channel | +| `ahoy_solar_P_DC_watt_total` | Gauge | DC power of inverter [P] | inverter | +| `ahoy_solar_YieldDay_wattHours` | Counter | Energy converted to AC per day [Wh] | inverter, channel | +| `ahoy_solar_YieldTotal_kilowattHours` | Counter | Energy converted to AC since reset [kWh] | inverter, channel | +| `ahoy_solar_Irradiation_ratio` | Gauge | ratio DC Power over set maximum power per channel [%] | inverter, channel | +| `ahoy_solar_radio_rx_success` | Gauge | NRF24 statistic | | +| `ahoy_solar_radio_rx_fail` | Gauge | NRF24 statistic | | +| `ahoy_solar_radio_rx_fail_answer` | Gauge | NRF24 statistic | | +| `ahoy_solar_radio_frame_cnt` | Gauge | NRF24 statistic | | +| `ahoy_solar_radio_tx_cnt` | Gauge | NRF24 statistic | | diff --git a/pics/PXL_20230824_204200660.jpg b/pics/PXL_20230824_204200660.jpg new file mode 100644 index 00000000..cfcb4668 Binary files /dev/null and b/pics/PXL_20230824_204200660.jpg differ diff --git a/pics/PXL_20230901_061927908.jpg b/pics/PXL_20230901_061927908.jpg new file mode 100644 index 00000000..dd8527f2 Binary files /dev/null and b/pics/PXL_20230901_061927908.jpg differ diff --git a/src/web/html/convert.py b/scripts/convertHtml.py similarity index 81% rename from src/web/html/convert.py rename to scripts/convertHtml.py index 00f398ac..70d5f6e2 100644 --- a/src/web/html/convert.py +++ b/scripts/convertHtml.py @@ -6,6 +6,7 @@ import shutil from datetime import date from pathlib import Path import subprocess +Import("env") def get_git_sha(): @@ -60,13 +61,41 @@ def htmlParts(file, header, nav, footer, version): link = 'GIT SHA: ' + get_git_sha() + ' :: ' + version + '' p = p.replace("{#VERSION}", version) p = p.replace("{#VERSION_GIT}", link) + + # remove if - endif ESP32 + p = checkIf(p) + f = open("tmp/" + file, "w") f.write(p); f.close(); return p +def checkIf(data): + if (env['PIOENV'][0:5] == "esp32") or env['PIOENV'][0:4] == "open": + data = data.replace("", "") + data = data.replace("", "") + data = data.replace("/*IF_ESP32*/", "") + data = data.replace("/*ENDIF_ESP32*/", "") + else: + while 1: + start = data.find("") + end = data.find("")+18 + if -1 == start: + break + else: + data = data[0:start] + data[end:] + while 1: + start = data.find("/*IF_ESP32*/") + end = data.find("/*ENDIF_ESP32*/")+15 + if -1 == start: + break + else: + data = data[0:start] + data[end:] + + return data + def convert2Header(inFile, version): - fileType = inFile.split(".")[1] + fileType = inFile.split(".")[1] define = inFile.split(".")[0].upper() define2 = inFile.split(".")[1].upper() inFileVarName = inFile.replace(".", "_") @@ -118,9 +147,7 @@ def convert2Header(inFile, version): f.close() # delete all files in the 'h' dir -wd = 'h' -if os.getcwd()[-4:] != "html": - wd = "web/html/" + wd +wd = 'web/html/h' if os.path.exists(wd): for f in os.listdir(wd): @@ -131,8 +158,7 @@ if os.path.exists(wd): os.remove(os.path.join(wd, f)) # grab all files with following extensions -if os.getcwd()[-4:] != "html": - os.chdir('./web/html') +os.chdir('./web/html') types = ('*.html', '*.css', '*.js', '*.ico') # the tuple of file types files_grabbed = [] for files in types: diff --git a/src/CHANGES.md b/src/CHANGES.md index d4898317..e85814ed 100644 --- a/src/CHANGES.md +++ b/src/CHANGES.md @@ -1,5 +1,19 @@ # Development Changes +## 0.7.50 - 2023-09-12 +* moved MqTT info to `system` +* added CMT info for ESP32 devices +* improved CMT settings, now `SCLK` and `SDIO` are configurable #1046, #1150 +* changed `Power-Limit` in live-view to `Active Power Control` +* increase length of update file selector #1132 + +## 0.7.49 - 2023-09-11 +* merge PR: symbolic icons for mono displays, PR #1136 +* merge MI code restructuring PR #1145 +* merge Prometheus PR #1148 +* add option to strip webUI for ESP8266 (reduce code size, add ESP32 special features; `IF_ESP32` directives) +* started to get CMT info into `system` - not finished + ## 0.7.48 - 2023-09-10 * fix SSD1309 2.42" display pinout * improved setup page: save and delete of inverters diff --git a/src/app.cpp b/src/app.cpp index b0401a22..50fd9e56 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -42,7 +42,7 @@ void app::setup() { } #if defined(ESP32) if(mConfig->cmt.enabled) { - mCmtRadio.setup(mConfig->cmt.pinCsb, mConfig->cmt.pinFcsb, false); + mCmtRadio.setup(mConfig->cmt.pinSclk, mConfig->cmt.pinSdio, mConfig->cmt.pinCsb, mConfig->cmt.pinFcsb, false); mCmtRadio.enableDebug(); } #endif @@ -111,11 +111,11 @@ void app::setup() { mWeb.setup(this, &mSys, mConfig); mWeb.setProtection(strlen(mConfig->sys.adminPwd) != 0); - mApi.setup(this, &mSys, &mNrfRadio, mWeb.getWebSrvPtr(), mConfig); + mApi.setup(this, &mSys, mWeb.getWebSrvPtr(), mConfig); // Plugins if (mConfig->plugin.display.type != 0) - mDisplay.setup(&mConfig->plugin.display, &mSys, &mTimestamp, mVersion); + mDisplay.setup(this, &mConfig->plugin.display, &mSys, &mNrfRadio, &mTimestamp); mPubSerial.setup(mConfig, &mSys, &mTimestamp); diff --git a/src/app.h b/src/app.h index 0d0f89e6..6f80e515 100644 --- a/src/app.h +++ b/src/app.h @@ -9,9 +9,9 @@ #include #include -#include "appInterface.h" #include "config/settings.h" #include "defines.h" +#include "appInterface.h" #include "hm/hmPayload.h" #include "hm/hmSystem.h" #include "hm/hmRadio.h" @@ -45,21 +45,23 @@ typedef HmSystem HmSystemType; typedef HmPayload> PayloadType; typedef MiPayload> MiPayloadType; #ifdef ESP32 -typedef CmtRadio> CmtRadioType; +typedef CmtRadio CmtRadioType; typedef HmsPayload HmsPayloadType; #endif typedef Web WebType; -typedef RestApi> RestApiType; +typedef RestApi RestApiType; typedef PubMqtt PubMqttType; typedef PubSerial PubSerialType; // PLUGINS #include "plugins/Display/Display.h" -#include "plugins/zeroExport/zeroExport.h" +#include "plugins/Display/Display_data.h" +typedef Display> DisplayType; -typedef Display DisplayType; +#include "plugins/zeroExport/zeroExport.h" typedef ZeroExport ZeroExportType; + class app : public IApp, public ah::Scheduler { public: app(); @@ -77,7 +79,17 @@ class app : public IApp, public ah::Scheduler { void handleIntr(void) { mNrfRadio.handleIntr(); } - + void* getRadioObj(bool nrf) { + if(nrf) + return (void*)&mNrfRadio; + else { + #ifdef ESP32 + return (void*)&mCmtRadio; + #else + return NULL; + #endif + } + } #ifdef ESP32 void handleHmsIntr(void) { mCmtRadio.handleIntr(); @@ -369,6 +381,8 @@ class app : public IApp, public ah::Scheduler { // plugins DisplayType mDisplay; + DisplayData mDispData; + ZeroExportType mzExport; }; diff --git a/src/appInterface.h b/src/appInterface.h index a6f85e84..a7299f7e 100644 --- a/src/appInterface.h +++ b/src/appInterface.h @@ -14,6 +14,11 @@ #include "ESPAsyncWebServer.h" #endif +//#include "hms/hmsRadio.h" +#if defined(ESP32) +//typedef CmtRadio> CmtRadioType; +#endif + // abstract interface to App. Make members of App accessible from child class // like web or API without forward declaration class IApp { @@ -62,6 +67,11 @@ class IApp { virtual void getNrfRadioCounters(uint32_t *sendCnt, uint32_t *retransmits) = 0; //virtual void getCmtRadioCounters(uint32_t *sendCnt, uint32_t *retransmits) = 0; + + virtual void* getRadioObj(bool nrf) = 0; + #if defined(ESP32) + //virtual const CmtRadioType& getCmtRadioObj(void) const = 0; + #endif }; #endif /*__IAPP_H__*/ diff --git a/src/config/config.h b/src/config/config.h index cbf99c49..35417b0e 100644 --- a/src/config/config.h +++ b/src/config/config.h @@ -74,15 +74,21 @@ #define DEF_NRF_IRQ_PIN 16 #endif #ifndef DEF_NRF_MISO_PIN - #define DEF_NRF_MISO_PIN 19 + #define DEF_NRF_MISO_PIN 12 #endif #ifndef DEF_NRF_MOSI_PIN - #define DEF_NRF_MOSI_PIN 23 + #define DEF_NRF_MOSI_PIN 13 #endif #ifndef DEF_NRF_SCLK_PIN - #define DEF_NRF_SCLK_PIN 18 + #define DEF_NRF_SCLK_PIN 14 #endif + #ifndef DEF_CMT_SCLK + #define DEF_CMT_SCLK 18 + #endif + #ifndef DEF_CMT_SDIO + #define DEF_CMT_SDIO 23 + #endif #ifndef DEF_CMT_CSB #define DEF_CMT_CSB 27 #endif diff --git a/src/config/settings.h b/src/config/settings.h index 5e6b1ef3..40540b96 100644 --- a/src/config/settings.h +++ b/src/config/settings.h @@ -94,6 +94,8 @@ typedef struct { typedef struct { bool enabled; + uint8_t pinSclk; + uint8_t pinSdio; uint8_t pinCsb; uint8_t pinFcsb; uint8_t pinIrq; @@ -419,10 +421,14 @@ class settings { mCfg.nrf.enabled = true; #if defined(ESP32) + mCfg.cmt.pinSclk = DEF_CMT_SCLK; + mCfg.cmt.pinSdio = DEF_CMT_SDIO; mCfg.cmt.pinCsb = DEF_CMT_CSB; mCfg.cmt.pinFcsb = DEF_CMT_FCSB; mCfg.cmt.pinIrq = DEF_CMT_IRQ; #else + mCfg.cmt.pinSclk = DEF_PIN_OFF; + mCfg.cmt.pinSdio = DEF_PIN_OFF; mCfg.cmt.pinCsb = DEF_PIN_OFF; mCfg.cmt.pinFcsb = DEF_PIN_OFF; mCfg.cmt.pinIrq = DEF_PIN_OFF; diff --git a/src/defines.h b/src/defines.h index 3436c6f7..a9f17f3b 100644 --- a/src/defines.h +++ b/src/defines.h @@ -13,7 +13,7 @@ //------------------------------------- #define VERSION_MAJOR 0 #define VERSION_MINOR 7 -#define VERSION_PATCH 48 +#define VERSION_PATCH 50 //------------------------------------- typedef struct { diff --git a/src/hm/hmPayload.h b/src/hm/hmPayload.h index 7826dbdd..deefe876 100644 --- a/src/hm/hmPayload.h +++ b/src/hm/hmPayload.h @@ -232,8 +232,9 @@ class HmPayload { } if (!mPayload[iv->id].complete) { - bool crcPass, pyldComplete; - crcPass = build(iv->id, &pyldComplete); + bool crcPass, pyldComplete, fastNext; + + crcPass = build(iv, &pyldComplete, &fastNext); if (!crcPass && !pyldComplete) { // payload not complete if ((mPayload[iv->id].requested) && (retransmit)) { if (mPayload[iv->id].retransmits < mMaxRetrans) { @@ -260,10 +261,12 @@ class HmPayload { } else { for (uint8_t i = 0; i < (mPayload[iv->id].maxPackId - 1); i++) { if (mPayload[iv->id].len[i] == 0) { - DPRINT_IVID(DBG_WARN, iv->id); - DBGPRINT(F("Frame ")); - DBGPRINT(String(i + 1)); - DBGPRINTLN(F(" missing: Request Retransmit")); + if (mSerialDebug) { + DPRINT_IVID(DBG_WARN, iv->id); + DBGPRINT(F("Frame ")); + DBGPRINT(String(i + 1)); + DBGPRINTLN(F(" missing: Request Retransmit")); + } mRadio->sendCmdPacket(iv->radioId.u64, TX_REQ_INFO, (SINGLE_FRAME + i), true); break; // only request retransmit one frame per loop } @@ -276,20 +279,25 @@ class HmPayload { } else if(!crcPass && pyldComplete) { // crc error on complete Payload if (mPayload[iv->id].retransmits < mMaxRetrans) { mPayload[iv->id].retransmits++; - DPRINTLN(DBG_WARN, F("CRC Error: Request Complete Retransmit")); mPayload[iv->id].txCmd = iv->getQueuedCmd(); - DPRINT_IVID(DBG_INFO, iv->id); - DBGPRINT(F("prepareDevInformCmd 0x")); - DBGHEXLN(mPayload[iv->id].txCmd); + if (mSerialDebug) { + DPRINTLN(DBG_WARN, F("CRC Error: Request Complete Retransmit")); + DPRINT_IVID(DBG_INFO, iv->id); + 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); } } else { // payload complete - DPRINT(DBG_INFO, F("procPyld: cmd: 0x")); - DBGHEXLN(mPayload[iv->id].txCmd); - //DPRINT(DBG_DEBUG, F("procPyld: txid: 0x")); - //DBGHEXLN(mPayload[iv->id].txId); - DPRINT(DBG_DEBUG, F("procPyld: max: ")); - DPRINTLN(DBG_DEBUG, String(mPayload[iv->id].maxPackId)); + if (mSerialDebug) { + DPRINT_IVID(DBG_INFO, iv->id); + DBGPRINT(F("procPyld: cmd: 0x")); + DBGHEXLN(mPayload[iv->id].txCmd); + //DPRINT(DBG_DEBUG, F("procPyld: txid: 0x")); + //DBGHEXLN(mPayload[iv->id].txId); + DPRINT(DBG_DEBUG, F("procPyld: max: ")); + DPRINTLN(DBG_DEBUG, String(mPayload[iv->id].maxPackId)); + } record_t<> *rec = iv->getRecordStruct(mPayload[iv->id].txCmd); // choose the parser mPayload[iv->id].complete = true; @@ -347,13 +355,25 @@ class HmPayload { yield(); } } - if( (InverterDevInform_All == mPayload[iv->id].txCmd) && (mHighPrioIv == NULL) ) // process next request immediately if possible - mHighPrioIv = iv; + if (fastNext) { + uint8_t cmd = iv->getQueuedCmd(); + if (mSerialDebug) { + DPRINT_IVID(DBG_INFO, iv->id); + DBGPRINT(F("fast mode ")); + DBGPRINT(F("prepareDevInformCmd 0x")); + DBGHEXLN(cmd); + } + mStat->rxSuccess++; + mRadio->prepareDevInformCmd(iv->radioId.u64, cmd, mPayload[iv->id].ts, iv->alarmMesIndex, false); + mPayload[iv->id].txCmd = cmd; + } } else { - DPRINT(DBG_ERROR, F("plausibility check failed, expected ")); - DBGPRINT(String(rec->pyldLen)); - DBGPRINTLN(F(" bytes")); + if (mSerialDebug) { + DPRINT(DBG_ERROR, F("plausibility check failed, expected ")); + DBGPRINT(String(rec->pyldLen)); + DBGPRINTLN(F(" bytes")); + } mStat->rxFail++; } @@ -370,33 +390,46 @@ class HmPayload { (mCbPayload)(val, iv); } - bool build(uint8_t id, bool *complete) { + bool build(Inverter<> *iv, bool *complete, bool *fastNext ) { DPRINTLN(DBG_VERBOSE, F("build")); uint16_t crc = 0xffff, crcRcv = 0x0000; - if (mPayload[id].maxPackId > MAX_PAYLOAD_ENTRIES) - mPayload[id].maxPackId = MAX_PAYLOAD_ENTRIES; + if (mPayload[iv->id].maxPackId > MAX_PAYLOAD_ENTRIES) + mPayload[iv->id].maxPackId = MAX_PAYLOAD_ENTRIES; // check if all fragments are there *complete = true; - for (uint8_t i = 0; i < mPayload[id].maxPackId; i++) { - if(mPayload[id].len[i] == 0) + *fastNext = false; + for (uint8_t i = 0; i < mPayload[iv->id].maxPackId; i++) { + if(mPayload[iv->id].len[i] == 0) { *complete = false; + } } if(!*complete) return false; - for (uint8_t i = 0; i < mPayload[id].maxPackId; i++) { - if (mPayload[id].len[i] > 0) { - if (i == (mPayload[id].maxPackId - 1)) { - crc = ah::crc16(mPayload[id].data[i], mPayload[id].len[i] - 2, crc); - crcRcv = (mPayload[id].data[i][mPayload[id].len[i] - 2] << 8) | (mPayload[id].data[i][mPayload[id].len[i] - 1]); + for (uint8_t i = 0; i < mPayload[iv->id].maxPackId; i++) { + if (mPayload[iv->id].len[i] > 0) { + if (i == (mPayload[iv->id].maxPackId - 1)) { + crc = ah::crc16(mPayload[iv->id].data[i], mPayload[iv->id].len[i] - 2, crc); + crcRcv = (mPayload[iv->id].data[i][mPayload[iv->id].len[i] - 2] << 8) | (mPayload[iv->id].data[i][mPayload[iv->id].len[i] - 1]); } else - crc = ah::crc16(mPayload[id].data[i], mPayload[id].len[i], crc); + crc = ah::crc16(mPayload[iv->id].data[i], mPayload[iv->id].len[i], crc); } yield(); } - return (crc == crcRcv) ? true : false; + //return (crc == crcRcv) ? true : false; + if (crc != crcRcv) + return false; + + //requests to cause the next request to be executed immediately + if ( mPayload[iv->id].txCmd < 11 || mPayload[iv->id].txCmd > 18 ) { + *fastNext = true; + //DPRINT_IVID(DBG_INFO, iv->id); + //DBGPRINTLN(F("fast next req")); + } + + return true; } void reset(uint8_t id) { diff --git a/src/hm/hmRadio.h b/src/hm/hmRadio.h index ee037adb..ace1c212 100644 --- a/src/hm/hmRadio.h +++ b/src/hm/hmRadio.h @@ -272,8 +272,8 @@ class HmRadio { isLastPackage = true; // response from dev control command } } - yield(); - } + yield(); + } return isLastPackage; } diff --git a/src/hm/miPayload.h b/src/hm/miPayload.h index ca2da50e..022f55f9 100644 --- a/src/hm/miPayload.h +++ b/src/hm/miPayload.h @@ -26,10 +26,8 @@ typedef struct { uint8_t invId; uint8_t retransmits; bool gotFragment; - /* - uint8_t data[MAX_PAYLOAD_ENTRIES][MAX_RF_PAYLOAD_SIZE]; - uint8_t maxPackId; - bool lastFound;*/ + uint8_t rtrRes; // for limiting resets + uint8_t multi_parts; // for quality } miPayload_t; @@ -91,21 +89,22 @@ class MiPayload { DPRINT_IVID(DBG_INFO, iv->id); if (!mPayload[iv->id].gotFragment) { mStat->rxFailNoAnser++; // got nothing - if (mSerialDebug) - DBGPRINTLN(F("enqueued cmd failed/timeout")); + if (mSerialDebug) + DBGPRINTLN(F("enqueued cmd failed/timeout")); } else { mStat->rxFail++; // got "fragments" (part of the required messages) // but no complete set of responses - if (mSerialDebug) { + if (mSerialDebug) { DBGPRINT(F("no complete Payload received! (retransmits: ")); - DBGPRINT(String(mPayload[iv->id].retransmits)); - DBGPRINTLN(F(")")); + DBGPRINT(String(mPayload[iv->id].retransmits)); + DBGPRINTLN(F(")")); + } } - } + mPayload[iv->id].complete = true; iv->setQueuedCmdFinished(); // command failed + } } } - } reset(iv->id); mPayload[iv->id].requested = true; @@ -131,12 +130,9 @@ class MiPayload { mPayload[iv->id].limitrequested = true; iv->clearCmdQueue(); - iv->enqueCommand(SystemConfigPara); // try to read back power limit + //iv->enqueCommand(SystemConfigPara); // read back power limit is not possible with MI } else { uint8_t cmd = iv->getQueuedCmd(); - DPRINT_IVID(DBG_INFO, iv->id); - DBGPRINT(F("prepareDevInformCmd 0x")); - DBGHEXLN(cmd); uint8_t cmd2 = cmd; if ( cmd == SystemConfigPara ) { //0x05 for HM-types if (!mPayload[iv->id].limitrequested) { // only do once at startup @@ -148,12 +144,15 @@ class MiPayload { } if (cmd == 0x01 || cmd == SystemConfigPara ) { //0x1 and 0x05 for HM-types - cmd = 0x0f; // for MI, these seem to make part of the Polling the device software and hardware version number command cmd2 = cmd == SystemConfigPara ? 0x01 : 0x00; //perhaps we can only try to get second frame? - mRadio->sendCmdPacket(iv->radioId.u64, cmd, cmd2, false, false); - } else { - mRadio->sendCmdPacket(iv->radioId.u64, cmd, cmd2, false, false); - }; + cmd = 0x0f; // for MI, these seem to make part of polling the device software and hardware version number command + } + if (mSerialDebug) { + DPRINT_IVID(DBG_INFO, iv->id); + DBGPRINT(F("prepareDevInformCmd 0x")); + DBGHEXLN(cmd); + } + mRadio->sendCmdPacket(iv->radioId.u64, cmd, cmd2, false, false); mPayload[iv->id].txCmd = cmd; if (iv->type == INV_TYPE_1CH || iv->type == INV_TYPE_2CH) { @@ -161,11 +160,10 @@ class MiPayload { mPayload[iv->id].stsAB[CH1] = false; mPayload[iv->id].dataAB[CH0] = false; mPayload[iv->id].stsAB[CH0] = false; - } - - if (iv->type == INV_TYPE_2CH) { - mPayload[iv->id].dataAB[CH2] = false; - mPayload[iv->id].stsAB[CH2] = false; + if (iv->type == INV_TYPE_2CH) { + mPayload[iv->id].dataAB[CH2] = false; + mPayload[iv->id].stsAB[CH2] = false; + } } } } @@ -190,125 +188,8 @@ class MiPayload { else if (p->packet[0] == ( 0x0f + ALL_FRAMES)) { // MI response from get hardware information request - 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 - start byte Command word routing address target address User data check end byte - byte[0] byte[1] byte[2] byte[3] byte[4] byte[5] byte[6] byte[7] byte[8] byte[9] byte[10] byte[11] byte[12] - 0x7e 0x0f xx xx xx xx YY YY YY YY 0x00 CRC 0x7f - Command Receipt - First Frame - start byte Command word target address routing address Multi-frame marking User data User data User data User data User data User data User data User data User data User data User data User data User data User data User data User data check end byte - byte[0] byte[1] byte[2] byte[3] byte[4] byte[5] byte[6] byte[7] byte[8] byte[9] byte[10] byte[11] byte[12] byte[13] byte[14] byte[15] byte[16] byte[17] byte[18] byte[19] byte[20] byte[21] byte[22] byte[23] byte[24] byte[25] byte[26] byte[27] byte[28] - 0x7e 0x8f YY YY YY YY xx xx xx xx 0x00 USFWBuild_VER APPFWBuild_VER APPFWBuild_YYYY APPFWBuild_MMDD APPFWBuild_HHMM APPFW_PN HW_VER CRC 0x7f - Command Receipt - Second Frame - start byte Command word target address routing address Multi-frame marking User data User data User data User data User data User data User data User data User data User data User data User data User data User data User data User data check end byte - byte[0] byte[1] byte[2] byte[3] byte[4] byte[5] byte[6] byte[7] byte[8] byte[9] byte[10] byte[11] byte[12] byte[13] byte[14] byte[15] byte[16] byte[17] byte[18] byte[19] byte[20] byte[21] byte[22] byte[23] byte[24] byte[25] byte[26] byte[27] byte[28] - 0x7e 0x8f YY YY YY YY xx xx xx xx 0x01 HW_PN HW_FB_TLmValue HW_FB_ReSPRT HW_GridSamp_ResValule HW_ECapValue Matching_APPFW_PN CRC 0x7f - Command receipt - third frame - start byte Command word target address routing address Multi-frame marking User data User data User data User data User data User data User data User data check end byte - byte[0] byte[1] byte[2] byte[3] byte[4] byte[5] byte[6] byte[7] byte[8] byte[9] byte[10] byte[11] byte[12] byte[13] byte[14] byte[15] byte[16] byte[15] byte[16] byte[17] byte[18] - 0x7e 0x8f YY YY YY YY xx xx xx xx 0x12 APPFW_MINVER HWInfoAddr PNInfoCRC_gusv PNInfoCRC_gusv CRC 0x7f -*/ - -/* -case InverterDevInform_All: - rec->length = (uint8_t)(HMINFO_LIST_LEN); - 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_BOOTLOADER_VER, UNIT_NONE, CH0, 8, 2, 1 } -}; -*/ - - if ( p->packet[9] == 0x00 ) {//first frame - //FLD_FW_VERSION - for (uint8_t i = 0; i < 5; i++) { - iv->setValue(i, rec, (float) ((p->packet[(12+2*i)] << 8) + p->packet[(13+2*i)])/1); - } - iv->isConnected = true; - 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])); - } - 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)")); - /* 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; - 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_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[] - } - - } 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++; - } + miHwDecode(iv, p); + mPayload[iv->id].txId = p->packet[0]; } else if ( p->packet[0] == (TX_REQ_INFO + ALL_FRAMES) // response from get information command || (p->packet[0] == 0xB6 && mPayload[iv->id].txCmd != 0x36)) { // strange short response from MI-1500 3rd gen; might be misleading! @@ -326,23 +207,7 @@ const byteAssign_t InfoAssignment[] = { iv->ivGen = IV_HM; iv->setQueuedCmdFinished(); iv->clearCmdQueue(); - //DPRINTLN(DBG_DEBUG, "PID: 0x" + String(*pid, HEX)); - /* (old else-tree) - if ((*pid & 0x7F) < MAX_PAYLOAD_ENTRIES) {^ - memcpy(mPayload[iv->id].data[(*pid & 0x7F) - 1], &p->packet[10], p->len - 11); - mPayload[iv->id].len[(*pid & 0x7F) - 1] = p->len - 11; - mPayload[iv->id].gotFragment = true; - } - if ((*pid & ALL_FRAMES) == ALL_FRAMES) { - // Last packet - if (((*pid & 0x7f) > mPayload[iv->id].maxPackId) || (MAX_PAYLOAD_ENTRIES == mPayload[iv->id].maxPackId)) { - mPayload[iv->id].maxPackId = (*pid & 0x7f); - if (*pid > 0x81) - mPayload[iv->id].lastFound = true; - } - }*/ } - //} } else if (p->packet[0] == (TX_REQ_DEVCONTROL + ALL_FRAMES ) // response from dev control command || p->packet[0] == (TX_REQ_DEVCONTROL + ALL_FRAMES -1)) { // response from DRED instruction DPRINT_IVID(DBG_DEBUG, iv->id); @@ -354,23 +219,27 @@ const byteAssign_t InfoAssignment[] = { if ((p->packet[9] == 0x5a) && (p->packet[10] == 0x5a)) { mApp->setMqttPowerLimitAck(iv); iv->powerLimitAck = true; - DPRINT_IVID(DBG_INFO, iv->id); - DBGPRINT(F("has accepted power limit set point ")); - DBGPRINT(String(iv->powerLimit[0])); - DBGPRINT(F(" with PowerLimitControl ")); - DBGPRINTLN(String(iv->powerLimit[1])); - + if (mSerialDebug) { + DPRINT_IVID(DBG_INFO, iv->id); + DBGPRINT(F("has accepted power limit set point ")); + DBGPRINT(String(iv->powerLimit[0])); + DBGPRINT(F(" with PowerLimitControl ")); + DBGPRINTLN(String(iv->powerLimit[1])); + } iv->clearCmdQueue(); iv->enqueCommand(SystemConfigPara); // read back power limit } iv->devControlCmd = Init; } else { // some other response; copied from hmPayload:process; might not be correct to do that here!!! - DPRINT(DBG_INFO, F("procPyld: cmd: 0x")); - DBGHEXLN(mPayload[iv->id].txCmd); - DPRINT(DBG_INFO, F("procPyld: txid: 0x")); - DBGHEXLN(mPayload[iv->id].txId); - //DPRINT(DBG_DEBUG, F("procPyld: max: ")); - //DBGPRINTLN(String(mPayload[iv->id].maxPackId)); + if (mSerialDebug) { + DPRINT_IVID(DBG_INFO,iv->id); + DBGPRINT(F("procPyld: cmd: 0x")); + DBGHEXLN(mPayload[iv->id].txCmd); + DPRINT_IVID(DBG_INFO,iv->id); + DBGPRINT(F("procPyld: txid: 0x")); + DBGHEXLN(mPayload[iv->id].txId); + } + record_t<> *rec = iv->getRecordStruct(mPayload[iv->id].txCmd); // choose the parser mPayload[iv->id].complete = true; @@ -379,11 +248,6 @@ const byteAssign_t InfoAssignment[] = { memset(payload, 0, 128); - /*for (uint8_t i = 0; i < (mPayload[iv->id].maxPackId); i++) { - memcpy(&payload[payloadLen], mPayload[iv->id].data[i], (mPayload[iv->id].len[i])); - payloadLen += (mPayload[iv->id].len[i]); - yield(); - }*/ payloadLen -= 2; if (mSerialDebug) { @@ -432,7 +296,7 @@ const byteAssign_t InfoAssignment[] = { if (NULL == iv) continue; // skip to next inverter - if (IV_HM == iv->ivGen) // only process MI inverters + if (IV_MI != iv->ivGen) // only process MI inverters continue; // skip to next inverter if ( !mPayload[iv->id].complete && @@ -443,24 +307,17 @@ const byteAssign_t InfoAssignment[] = { (mPayload[iv->id].txId != (0x11 + ALL_FRAMES)) && (mPayload[iv->id].txId != (0x88)) && (mPayload[iv->id].txId != (0x92)) && - (mPayload[iv->id].txId != 0 )) { + (mPayload[iv->id].txId != 0 && + mPayload[iv->id].txCmd != 0x0f)) { // no processing needed if txId is not one of 0x95, 0x88, 0x89, 0x91, 0x92 or response to 0x36ff mPayload[iv->id].complete = true; continue; // skip to next inverter } - //delayed next message? - //mPayload[iv->id].skipfirstrepeat++; - /*if (mPayload[iv->id].skipfirstrepeat) { - mPayload[iv->id].skipfirstrepeat = 0; //reset counter - continue; // skip to next inverter - }*/ - if (!mPayload[iv->id].complete) { - //DPRINTLN(DBG_INFO, F("Pyld incompl code")); //info for testing only - bool crcPass, pyldComplete; - crcPass = build(iv->id, &pyldComplete); - if (!crcPass && !pyldComplete) { // payload not complete + bool gotAllMsgParts, pyldComplete, fastNext; + gotAllMsgParts = build(iv, &pyldComplete, &fastNext); + if (!gotAllMsgParts && !pyldComplete) { // payload not complete if ((mPayload[iv->id].requested) && (retransmit)) { if (iv->devControlCmd == Restart || iv->devControlCmd == CleanState_LockAndAlarm) { // This is required to prevent retransmissions without answer. @@ -482,8 +339,7 @@ const byteAssign_t InfoAssignment[] = { } else if ( cmd == 0x0f ) { //hard/firmware request mRadio->sendCmdPacket(iv->radioId.u64, 0x0f, 0x00, true, false); - //iv->setQueuedCmdFinished(); - //cmd = iv->getQueuedCmd(); + mPayload[id].multi_parts = 0; } else { bool change = false; if ( cmd >= 0x36 && cmd < 0x39 ) { // MI-1500 Data command @@ -496,7 +352,8 @@ const byteAssign_t InfoAssignment[] = { else if (!mPayload[iv->id].stsAB[CH2] || !mPayload[iv->id].dataAB[CH2] ) { cmd = 0x11; change = true; - mPayload[iv->id].retransmits = 0; //reset counter + if (mPayload[iv->id].rtrRes < 3) //only get back to first channel twice + mPayload[iv->id].retransmits = 0; //reset counter } } } else if ( cmd == 0x11) { @@ -510,32 +367,48 @@ const byteAssign_t InfoAssignment[] = { DPRINT_IVID(DBG_INFO, iv->id); if (change) { DBGPRINT(F("next request is")); - //mPayload[iv->id].skipfirstrepeat = 0; mPayload[iv->id].txCmd = cmd; + mPayload[iv->id].rtrRes++; } else { DBGPRINT(F("sth.")); DBGPRINT(F(" missing: Request Retransmit")); } DBGPRINT(F(" 0x")); DBGHEXLN(cmd); + mPayload[id].multi_parts = 0; mRadio->sendCmdPacket(iv->radioId.u64, cmd, cmd, true, false); yield(); } } } } - } else if(!crcPass && pyldComplete) { // crc error on complete Payload + } else if(!gotAllMsgParts && pyldComplete) { // crc error on complete Payload if (mPayload[iv->id].retransmits < mMaxRetrans) { mPayload[iv->id].retransmits++; - DPRINT_IVID(DBG_WARN, iv->id); - DBGPRINTLN(F("CRC Error: Request Complete Retransmit")); mPayload[iv->id].txCmd = iv->getQueuedCmd(); - DPRINT_IVID(DBG_INFO, iv->id); - - DBGPRINT(F("prepareDevInformCmd 0x")); - DBGHEXLN(mPayload[iv->id].txCmd); + mPayload[id].multi_parts = 0; + if (mSerialDebug) { + DPRINT_IVID(DBG_WARN, iv->id); + DBGPRINTLN(F("CRC Error: Request Complete Retransmit")); + DPRINT_IVID(DBG_INFO, iv->id); + DBGPRINT(F("prepareDevInformCmd 0x")); + DBGHEXLN(mPayload[iv->id].txCmd); + } mRadio->sendCmdPacket(iv->radioId.u64, mPayload[iv->id].txCmd, mPayload[iv->id].txCmd, false, false); } + } else { + if (fastNext) { + uint8_t cmd = iv->getQueuedCmd(); + if (mSerialDebug) { + DPRINT_IVID(DBG_INFO, iv->id); + DBGPRINT(F("fast mode ")); + DBGPRINT(F("prepareDevInformCmd 0x")); + DBGHEXLN(cmd); + } + mStat->rxSuccess++; + mRadio->prepareDevInformCmd(iv->radioId.u64, cmd, mPayload[iv->id].ts, iv->alarmMesIndex, false); + mPayload[iv->id].txCmd = cmd; + } } } @@ -550,19 +423,15 @@ const byteAssign_t InfoAssignment[] = { } void miStsDecode(Inverter<> *iv, packet_t *p, uint8_t stschan = CH1) { - //DPRINTLN(DBG_INFO, F("(#") + String(iv->id) + F(") status msg 0x") + String(p->packet[0], HEX)); record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); // choose the record structure rec->ts = mPayload[iv->id].ts; mPayload[iv->id].gotFragment = true; + mPayload[iv->id].multi_parts += 3; mPayload[iv->id].txId = p->packet[0]; miStsConsolidate(iv, stschan, rec, p->packet[10], p->packet[12], p->packet[9], p->packet[11]); mPayload[iv->id].stsAB[stschan] = true; if (mPayload[iv->id].stsAB[CH1] && mPayload[iv->id].stsAB[CH2]) mPayload[iv->id].stsAB[CH0] = true; - //mPayload[iv->id].skipfirstrepeat = 1; - if (mPayload[iv->id].stsAB[CH0] && mPayload[iv->id].dataAB[CH0] && !mPayload[iv->id].complete) { - miComplete(iv); - } } void miStsConsolidate(Inverter<> *iv, uint8_t stschan, record_t<> *rec, uint8_t uState, uint8_t uEnum, uint8_t lState = 0, uint8_t lEnum = 0) { @@ -588,10 +457,12 @@ const byteAssign_t InfoAssignment[] = { uint16_t prntsts = statusMi == 3 ? 1 : statusMi; if ( statusMi != mPayload[iv->id].sts[stschan] ) { //sth.'s changed? mPayload[iv->id].sts[stschan] = statusMi; - DPRINT(DBG_WARN, F("Status change for CH")); - DBGPRINT(String(stschan)); DBGPRINT(F(" (")); - DBGPRINT(String(prntsts)); DBGPRINT(F("): ")); - DBGPRINTLN(iv->getAlarmStr(prntsts)); + if (mSerialDebug) { + DPRINT(DBG_WARN, F("New state on CH")); + DBGPRINT(String(stschan)); DBGPRINT(F(" (")); + DBGPRINT(String(prntsts)); DBGPRINT(F("): ")); + DBGPRINTLN(iv->getAlarmStr(prntsts)); + } } if ( !mPayload[iv->id].sts[0] || prntsts < mPayload[iv->id].sts[0] ) { @@ -599,12 +470,13 @@ const byteAssign_t InfoAssignment[] = { iv->setValue(iv->getPosByChFld(0, FLD_EVT, rec), rec, prntsts); } - if (iv->alarmMesIndex < rec->record[iv->getPosByChFld(0, FLD_EVT, rec)]){ + if (iv->alarmMesIndex < rec->record[iv->getPosByChFld(0, FLD_EVT, rec)]) { iv->alarmMesIndex = rec->record[iv->getPosByChFld(0, FLD_EVT, rec)]; // seems there's no status per channel in 3rd gen. models?!? - - DPRINT_IVID(DBG_INFO, iv->id); - DBGPRINT(F("alarm ID incremented to ")); - DBGPRINTLN(String(iv->alarmMesIndex)); + if (mSerialDebug) { + DPRINT_IVID(DBG_INFO, iv->id); + DBGPRINT(F("alarm ID incremented to ")); + DBGPRINTLN(String(iv->alarmMesIndex)); + } } /*if(AlarmData == mPayload[iv->id].txCmd) { uint8_t i = 0; @@ -625,6 +497,7 @@ const byteAssign_t InfoAssignment[] = { record_t<> *rec = iv->getRecordStruct(RealTimeRunData_Debug); // choose the parser rec->ts = mPayload[iv->id].ts; mPayload[iv->id].gotFragment = true; + mPayload[iv->id].multi_parts += 4; uint8_t datachan = ( p->packet[0] == 0x89 || p->packet[0] == (0x36 + ALL_FRAMES) ) ? CH1 : ( p->packet[0] == 0x91 || p->packet[0] == (0x37 + ALL_FRAMES) ) ? CH2 : @@ -653,50 +526,23 @@ const byteAssign_t InfoAssignment[] = { } if (p->packet[0] >= (0x36 + ALL_FRAMES) ) { - /*For MI1500: if (MI1500) { STAT = (uint8_t)(p->packet[25] ); FCNT = (uint8_t)(p->packet[26]); FCODE = (uint8_t)(p->packet[27]); }*/ - - /*uint16_t status = (uint8_t)(p->packet[23]); - mPayload[iv->id].sts[datachan] = status; - if ( !mPayload[iv->id].sts[0] || status < mPayload[iv->id].sts[0]) { - mPayload[iv->id].sts[0] = status; - iv->setValue(iv->getPosByChFld(0, FLD_EVT, rec), rec, status); - }*/ miStsConsolidate(iv, datachan, rec, p->packet[23], p->packet[24]); if (p->packet[0] < (0x39 + ALL_FRAMES) ) { mPayload[iv->id].txCmd++; mPayload[iv->id].retransmits = 0; // reserve retransmissions for each response mPayload[iv->id].complete = false; + } else { + miComplete(iv); } } -/* - if(AlarmData == mPayload[iv->id].txCmd) { - uint8_t i = 0; - uint16_t code; - uint32_t start, end; - while(1) { - code = iv->parseAlarmLog(i++, payload, payloadLen, &start, &end); - if(0 == code) - break; - if (NULL != mCbAlarm) - (mCbAl { FLD_YT, UNIT_KWH, CH0, CALC_YT_CH0, 0, CMD_CALC }, - - }*/ - - //if ( mPayload[iv->id].complete || //4ch device - if ( p->packet[0] == (0x39 + ALL_FRAMES) || //4ch device - last message - (iv->type != INV_TYPE_4CH //other devices - && mPayload[iv->id].dataAB[CH0] - && mPayload[iv->id].stsAB[CH0])) { - miComplete(iv); - } } void miComplete(Inverter<> *iv) { @@ -729,51 +575,166 @@ const byteAssign_t InfoAssignment[] = { notify(RealTimeRunData_Debug, iv); } - bool build(uint8_t id, bool *complete) { + bool build(Inverter<> *iv, bool *complete, bool *fastNext ) { DPRINTLN(DBG_VERBOSE, F("build")); // check if all messages are there - *complete = mPayload[id].complete; - uint8_t txCmd = mPayload[id].txCmd; + *complete = mPayload[iv->id].complete; + *fastNext = false; + uint8_t txCmd = mPayload[iv->id].txCmd; if(!*complete) { - DPRINTLN(DBG_VERBOSE, F("incomplete, txCmd is 0x") + String(txCmd, HEX)); - //DBGHEXLN(txCmd); - if (txCmd == 0x09 || txCmd == 0x11 || (txCmd >= 0x36 && txCmd <= 0x39)) + //we got some delayed status msgs?!? + if ((txCmd == 0x09) || (txCmd == 0x11)) { + if (mPayload[iv->id].stsAB[CH0] && mPayload[iv->id].dataAB[CH0]) { + miComplete(iv); + return true; + } + return false; + } + + DPRINTLN(DBG_VERBOSE, F("incomlete, txCmd is 0x") + String(txCmd, HEX)); + if (txCmd >= 0x36 && txCmd <= 0x39) { return false; + } + if (txCmd == 0x0f) { //hw info request, at least hw part nr. and version have to be there... + bool gotRelevant = iv->getFwVersion() + && iv->getChannelFieldValue(CH0, FLD_PART_NUM, iv->getRecordStruct(InverterDevInform_Simple)); + if (gotRelevant) + *fastNext = true; + return gotRelevant; + } } + //check if we want the next request to be executed faster + if (txCmd == 0x0f) + *fastNext = true; return true; } -/* uint16_t mParseAlarmLog(uint8_t id, uint8_t pyld[], uint8_t len, uint32_t *start, uint32_t *endTime) { - uint8_t startOff = 2 + id * ALARM_LOG_ENTRY_SIZE; - if((startOff + ALARM_LOG_ENTRY_SIZE) > len) - return 0; + void miHwDecode(Inverter<> *iv, packet_t *p ) { + record_t<> *rec = iv->getRecordStruct(InverterDevInform_All); // choose the record structure + rec->ts = mPayload[iv->id].ts; + mPayload[iv->id].gotFragment = true; + +/* +Polling the device software and hardware version number command +start byte Command word routing address target address User data check end byte +byte[0] byte[1] byte[2] byte[3] byte[4] byte[5] byte[6] byte[7] byte[8] byte[9] byte[10] byte[11] byte[12] +0x7e 0x0f xx xx xx xx YY YY YY YY 0x00 CRC 0x7f +Command Receipt - First Frame +start byte Command word target address routing address Multi-frame marking User data User data User data User data User data User data User data User data User data User data User data User data User data User data User data User data check end byte +byte[0] byte[1] byte[2] byte[3] byte[4] byte[5] byte[6] byte[7] byte[8] byte[9] byte[10] byte[11] byte[12] byte[13] byte[14] byte[15] byte[16] byte[17] byte[18] byte[19] byte[20] byte[21] byte[22] byte[23] byte[24] byte[25] byte[26] byte[27] byte[28] +0x7e 0x8f YY YY YY YY xx xx xx xx 0x00 USFWBuild_VER APPFWBuild_VER APPFWBuild_YYYY APPFWBuild_MMDD APPFWBuild_HHMM APPFW_PN HW_VER CRC 0x7f +Command Receipt - Second Frame +start byte Command word target address routing address Multi-frame marking User data User data User data User data User data User data User data User data User data User data User data User data User data User data User data User data check end byte +byte[0] byte[1] byte[2] byte[3] byte[4] byte[5] byte[6] byte[7] byte[8] byte[9] byte[10] byte[11] byte[12] byte[13] byte[14] byte[15] byte[16] byte[17] byte[18] byte[19] byte[20] byte[21] byte[22] byte[23] byte[24] byte[25] byte[26] byte[27] byte[28] +0x7e 0x8f YY YY YY YY xx xx xx xx 0x01 HW_PN HW_FB_TLmValue HW_FB_ReSPRT HW_GridSamp_ResValule HW_ECapValue Matching_APPFW_PN CRC 0x7f +Command receipt - third frame +start byte Command word target address routing address Multi-frame marking User data User data User data User data User data User data User data User data check end byte +byte[0] byte[1] byte[2] byte[3] byte[4] byte[5] byte[6] byte[7] byte[8] byte[9] byte[10] byte[11] byte[12] byte[13] byte[14] byte[15] byte[16] byte[15] byte[16] byte[17] byte[18] +0x7e 0x8f YY YY YY YY xx xx xx xx 0x12 APPFW_MINVER HWInfoAddr PNInfoCRC_gusv PNInfoCRC_gusv CRC 0x7f +*/ - uint16_t wCode = ((uint16_t)pyld[startOff]) << 8 | pyld[startOff+1]; - uint32_t startTimeOffset = 0, endTimeOffset = 0; +/* +case InverterDevInform_All: + rec->length = (uint8_t)(HMINFO_LIST_LEN); + rec->assign = (byteAssign_t *)InfoAssignment; + rec->pyldLen = HMINFO_PAYLOAD_LEN; + break; - if (((wCode >> 13) & 0x01) == 1) // check if is AM or PM - startTimeOffset = 12 * 60 * 60; - if (((wCode >> 12) & 0x01) == 1) // check if is AM or PM - endTimeOffset = 12 * 60 * 60; +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_BOOTLOADER_VER, UNIT_NONE, CH0, 8, 2, 1 } +}; +*/ - *start = (((uint16_t)pyld[startOff + 4] << 8) | ((uint16_t)pyld[startOff + 5])) + startTimeOffset; - *endTime = (((uint16_t)pyld[startOff + 6] << 8) | ((uint16_t)pyld[startOff + 7])) + endTimeOffset; + if ( p->packet[9] == 0x00 ) {//first frame + //FLD_FW_VERSION + for (uint8_t i = 0; i < 5; i++) { + iv->setValue(i, rec, (float) ((p->packet[(12+2*i)] << 8) + p->packet[(13+2*i)])/1); + } + iv->isConnected = 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])); + } + 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); + mPayload[iv->id].multi_parts +=4; + } 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)")); + /* 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])); + 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); - DPRINTLN(DBG_INFO, "Alarm #" + String(pyld[startOff+1]) + " '" + String(getAlarmStr(pyld[startOff+1])) + "' start: " + ah::getTimeStr(*start) + ", end: " + ah::getTimeStr(*endTime)); - return pyld[startOff+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_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); + mPayload[iv->id].multi_parts +=2; + notify(InverterDevInform_All, iv); + } else { + DBGPRINTLN(F("3rd gen. inverter!")); + } + + } 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 (this really is double mentionned in xlsx...) + */ + 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])); + } + mPayload[iv->id].multi_parts++; + } + if (mPayload[iv->id].multi_parts > 5) { + iv->setQueuedCmdFinished(); + mPayload[iv->id].complete = true; + mPayload[iv->id].requested= false; + mStat->rxSuccess++; + } } -*/ void reset(uint8_t id, bool clrSts = false) { - //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; - mPayload[id].lastFound = false;*/ + mPayload[id].rtrRes = 0; + mPayload[id].multi_parts = 0; mPayload[id].retransmits = 0; mPayload[id].complete = false; mPayload[id].dataAB[CH0] = true; //required for 1CH and 2CH devices @@ -796,8 +757,6 @@ const byteAssign_t InfoAssignment[] = { } } - - IApp *mApp; HMSYSTEM *mSys; HMRADIO *mRadio; diff --git a/src/hms/cmt2300a.h b/src/hms/cmt2300a.h index f27ce3c3..d99875b9 100644 --- a/src/hms/cmt2300a.h +++ b/src/hms/cmt2300a.h @@ -184,8 +184,8 @@ class Cmt2300a { public: Cmt2300a() {} - void setup(uint8_t pinCsb, uint8_t pinFcsb) { - mSpi.setup(pinCsb, pinFcsb); + void setup(uint8_t pinSclk, uint8_t pinSdio, uint8_t pinCsb, uint8_t pinFcsb) { + mSpi.setup(pinSclk, pinSdio, pinCsb, pinFcsb); init(); } @@ -315,6 +315,8 @@ class Cmt2300a { mSpi.writeReg(CMT2300A_CUS_MODE_STA, 0x52); mSpi.writeReg(0x62, 0x20); + if(mSpi.readReg(0x62) != 0x20) + return false; // not connected! for(uint8_t i = 0; i < 0x60; i++) { mSpi.writeReg(i, cmtConfig[i]); diff --git a/src/hms/esp32_3wSpi.h b/src/hms/esp32_3wSpi.h index 080e2251..4fe47497 100644 --- a/src/hms/esp32_3wSpi.h +++ b/src/hms/esp32_3wSpi.h @@ -11,14 +11,6 @@ #include "driver/spi_master.h" #include "esp_rom_gpio.h" // for esp_rom_gpio_connect_out_signal -#if CONFIG_IDF_TARGET_ESP32S3 -#define CLK_PIN 6 -#define MOSI_PIN 5 -#else -#define CLK_PIN 18 -#define MOSI_PIN 23 -#endif - #define SPI_CLK 1 * 1000 * 1000 // 1MHz #define SPI_PARAM_LOCK() \ @@ -31,19 +23,18 @@ // it is simply the first externally usable hardware SPI master controller #define SPI_CMT SPI2_HOST -template //, uint8_t GPIO3_PIN=15> class esp32_3wSpi { public: esp32_3wSpi() { mInitialized = false; } - void setup(uint8_t pinCsb = CSB_PIN, uint8_t pinFcsb = FCSB_PIN) { //, uint8_t pinGpio3 = GPIO3_PIN) { + void setup(uint8_t pinSclk = DEF_CMT_SCLK, uint8_t pinSdio = DEF_CMT_SDIO, uint8_t pinCsb = DEF_CMT_CSB, uint8_t pinFcsb = DEF_CMT_FCSB) { paramLock = xSemaphoreCreateMutex(); spi_bus_config_t buscfg = { - .mosi_io_num = MOSI_PIN, + .mosi_io_num = pinSdio, .miso_io_num = -1, // single wire MOSI/MISO - .sclk_io_num = CLK_PIN, + .sclk_io_num = pinSclk, .quadwp_io_num = -1, .quadhd_io_num = -1, .max_transfer_sz = 32, @@ -83,7 +74,7 @@ class esp32_3wSpi { }; ESP_ERROR_CHECK(spi_bus_add_device(SPI_CMT, &devcfg2, &spi_fifo)); - esp_rom_gpio_connect_out_signal(MOSI_PIN, spi_periph_signal[SPI_CMT].spid_out, true, false); + esp_rom_gpio_connect_out_signal(pinSdio, spi_periph_signal[SPI_CMT].spid_out, true, false); delay(100); //pinMode(pinGpio3, INPUT); @@ -162,13 +153,13 @@ class esp32_3wSpi { .rx_buffer = &rx_data }; - SPI_PARAM_LOCK(); + SPI_PARAM_LOCK(); for(uint8_t i = 0; i < len; i++) { ESP_ERROR_CHECK(spi_device_polling_transmit(spi_fifo, &t)); delayMicroseconds(4); // > 4 us buf[i] = rx_data; } - SPI_PARAM_UNLOCK(); + SPI_PARAM_UNLOCK(); } private: diff --git a/src/hms/hmsRadio.h b/src/hms/hmsRadio.h index c515ae0e..9b1323de 100644 --- a/src/hms/hmsRadio.h +++ b/src/hms/hmsRadio.h @@ -29,8 +29,8 @@ class CmtRadio { mCmtAvail = false; } - void setup(uint8_t pinCsb, uint8_t pinFcsb, bool genDtuSn = true) { - mCmt.setup(pinCsb, pinFcsb); + void setup(uint8_t pinSclk, uint8_t pinSdio, uint8_t pinCsb, uint8_t pinFcsb, bool genDtuSn = true) { + mCmt.setup(pinSclk, pinSdio, pinCsb, pinFcsb); reset(genDtuSn); } @@ -64,7 +64,7 @@ class CmtRadio { mSerialDebug = true; } - bool cmtIsAvail() { + bool isConnected() { return mCmtAvail; } @@ -151,8 +151,7 @@ class CmtRadio { if(!mCmt.reset()) { mCmtAvail = false; DPRINTLN(DBG_WARN, F("Initializing CMT2300A failed!")); - } - else { + } else { mCmtAvail = true; mCmt.goRx(); } diff --git a/src/platformio.ini b/src/platformio.ini index 85c4cf67..77b8e724 100644 --- a/src/platformio.ini +++ b/src/platformio.ini @@ -20,7 +20,7 @@ monitor_speed = 115200 extra_scripts = pre:../scripts/auto_firmware_version.py - pre:web/html/convert.py + pre:../scripts/convertHtml.py pre:../scripts/applyPatches.py lib_deps = diff --git a/src/plugins/Display/Display.h b/src/plugins/Display/Display.h index 36a7df46..93349769 100644 --- a/src/plugins/Display/Display.h +++ b/src/plugins/Display/Display.h @@ -5,6 +5,7 @@ #include #include "../../hm/hmSystem.h" +#include "../../hm/hmRadio.h" #include "../../utils/helper.h" #include "Display_Mono.h" #include "Display_Mono_128X32.h" @@ -12,21 +13,25 @@ #include "Display_Mono_84X48.h" #include "Display_Mono_64X48.h" #include "Display_ePaper.h" +#include "Display_data.h" -template +template class Display { public: Display() { mMono = NULL; } - void setup(display_t *cfg, HMSYSTEM *sys, uint32_t *utcTs, const char *version) { + void setup(IApp *app, display_t *cfg, HMSYSTEM *sys, HMRADIO *radio, uint32_t *utcTs) { + mApp = app; + mHmRadio = radio; mCfg = cfg; mSys = sys; mUtcTs = utcTs; mNewPayload = false; mLoopCnt = 0; - mVersion = version; + + mDisplayData.version = app->getVersion(); // version never changes, so only set once switch (mCfg->type) { case 0: mMono = NULL; break; @@ -41,7 +46,7 @@ class Display { 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); + mEpaper.init(mCfg->type, mCfg->disp_cs, mCfg->disp_dc, mCfg->disp_reset, mCfg->disp_busy, mCfg->disp_clk, mCfg->disp_data, mUtcTs, mDisplayData.version); break; #endif @@ -49,7 +54,7 @@ class Display { } 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); + mMono->init(mCfg->type, mCfg->rot, mCfg->disp_cs, mCfg->disp_dc, 0xff, mCfg->disp_clk, mCfg->disp_data, &mDisplayData); } } @@ -61,7 +66,7 @@ class Display { if (mMono != NULL) mMono->loop(mCfg->contrast); - if (mNewPayload || (((++mLoopCnt) % 30) == 0)) { + if (mNewPayload || (((++mLoopCnt) % 10) == 0)) { mNewPayload = false; mLoopCnt = 0; DataScreen(); @@ -75,14 +80,13 @@ class Display { void DataScreen() { if (mCfg->type == 0) return; - if (*mUtcTs == 0) - return; float totalPower = 0; float totalYieldDay = 0; float totalYieldTotal = 0; - uint8_t isprod = 0; + uint8_t nrprod = 0; + uint8_t nrsleep = 0; Inverter<> *iv; record_t<> *rec; @@ -93,19 +97,39 @@ class Display { continue; if (iv->isProducing()) - isprod++; + nrprod++; + else + nrsleep++; totalPower += iv->getChannelFieldValue(CH0, FLD_PAC, rec); totalYieldDay += iv->getChannelFieldValue(CH0, FLD_YD, rec); totalYieldTotal += iv->getChannelFieldValue(CH0, FLD_YT, rec); } + // prepare display data + mDisplayData.nrProducing = nrprod; + mDisplayData.nrSleeping = nrsleep; + mDisplayData.totalPower = totalPower; + mDisplayData.totalYieldDay = totalYieldDay; + mDisplayData.totalYieldTotal = totalYieldTotal; + mDisplayData.RadioSymbol = mHmRadio->isChipConnected(); + mDisplayData.WifiSymbol = (WiFi.status() == WL_CONNECTED); + mDisplayData.MQTTSymbol = mApp->getMqttIsConnected(); + mDisplayData.RadioRSSI = (0 < mDisplayData.nrProducing) ? 0 : SCHAR_MIN; // Workaround as NRF24 has no RSSI. Could be approximated by transmisson error heuristic in the future + mDisplayData.WifiRSSI = (WiFi.status() == WL_CONNECTED) ? WiFi.RSSI() : SCHAR_MIN; + mDisplayData.ipAddress = WiFi.localIP(); + time_t utc= mApp->getTimestamp(); + if (year(utc) > 2020) + mDisplayData.utcTs = utc; + else + mDisplayData.utcTs = 0; + if (mMono ) { - mMono->disp(totalPower, totalYieldDay, totalYieldTotal, isprod); + mMono->disp(); } #if defined(ESP32) else if (mCfg->type == 10) { - mEpaper.loop(totalPower, totalYieldDay, totalYieldTotal, isprod); + mEpaper.loop(totalPower, totalYieldDay, totalYieldTotal, nrprod); mRefreshCycle++; } @@ -117,12 +141,14 @@ class Display { } // private member variables + IApp *mApp; + DisplayData mDisplayData; bool mNewPayload; uint8_t mLoopCnt; uint32_t *mUtcTs; - const char *mVersion; display_t *mCfg; HMSYSTEM *mSys; + HMRADIO *mHmRadio; uint16_t mRefreshCycle; #if defined(ESP32) diff --git a/src/plugins/Display/Display_Mono.h b/src/plugins/Display/Display_Mono.h index 4739f83f..49fd18ec 100644 --- a/src/plugins/Display/Display_Mono.h +++ b/src/plugins/Display/Display_Mono.h @@ -9,6 +9,7 @@ #define DISP_FMT_TEXT_LEN 32 #define BOTTOM_MARGIN 5 +#include "defines.h" #ifdef ESP8266 #include @@ -16,25 +17,29 @@ #include #endif #include "../../utils/helper.h" +#include "Display_data.h" class DisplayMono { public: 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 init(uint8_t type, uint8_t rot, uint8_t cs, uint8_t dc, uint8_t reset, uint8_t clock, uint8_t data, DisplayData *displayData) = 0; + virtual void config(bool enPowerSave, bool enScreenSaver, uint8_t lum) = 0; virtual void loop(uint8_t lum) = 0; - virtual void disp(float totalPower, float totalYieldDay, float totalYieldTotal, uint8_t isprod) = 0; + virtual void disp(void) = 0; protected: U8G2* mDisplay; + DisplayData *mDisplayData; uint8_t mType; - bool mEnPowerSafe, mEnScreenSaver; + uint16_t mDispWidth; + uint16_t mDispHeight; + + bool mEnPowerSave, mEnScreenSaver; uint8_t mLuminance; uint8_t mLoopCnt; - uint32_t* mUtcTs; uint8_t mLineXOffsets[5] = {}; uint8_t mLineYOffsets[5] = {}; @@ -42,4 +47,83 @@ class DisplayMono { uint8_t mExtra; uint16_t mTimeout; - char mFmtText[DISP_FMT_TEXT_LEN];}; + char mFmtText[DISP_FMT_TEXT_LEN]; + + // Common initialization function to be called by subclasses + void monoInit(U8G2* display, uint8_t type, DisplayData *displayData) { + mDisplay = display; + mType = type; + mDisplayData = displayData; + mDisplay->begin(); + mDisplay->setContrast(mLuminance); + mDisplay->clearBuffer(); + mDispWidth = mDisplay->getDisplayWidth(); + mDispHeight = mDisplay->getDisplayHeight(); + } +}; + +/* adapted 5x8 Font for low-res displays with symbols +Symbols: + \x80 ... antenna + \x81 ... WiFi + \x82 ... suncurve + \x83 ... sum/sigma + \x84 ... antenna crossed + \x85 ... WiFi crossed + \x86 ... sun + \x87 ... moon + \x88 ... calendar/day + \x89 ... MQTT */ +const uint8_t u8g2_font_5x8_symbols_ahoy[1052] U8G2_FONT_SECTION("u8g2_font_5x8_symbols_ahoy") = + "j\0\3\2\4\4\3\4\5\10\10\0\377\6\377\6\0\1\61\2b\4\3 \5\0\304\11!\7a\306" + "\212!\11\42\7\63\335\212\304\22#\16u\304\232R\222\14JePJI\2$\14u\304\252l\251m" + "I\262E\0%\10S\315\212(\351\24&\13t\304\232(i\252\64%\1'\6\61\336\212\1(\7b" + "\305\32\245))\11b\305\212(\251(\0*\13T\304\212(Q\206D\211\2+\12U\304\252\60\32\244" + "\60\2,\7\63\275\32\245\4-\6\24\324\212!.\6\42\305\212!/\10d\304\272R[\6\60\14d" + "\304\32%R\206DJ\24\0\61\10c\305\232Dj\31\62\13d\304\32%\312\22%\33\2\63\13d\304" + "\212!\212D)Q\0\64\13d\304\252H\251\14Q\226\0\65\12d\304\212A\33\245D\1\66\13d\304" + "\32%[\42)Q\0\67\13d\304\212!\213\262(\213\0\70\14d\304\32%J\224HJ\24\0\71\13" + "d\304\32%\222\222-Q\0:\10R\305\212!\32\2;\10c\275\32\243R\2<\10c\305\252\244\224" + "\25=\10\64\314\212!\34\2>\11c\305\212\254\224\224\0?\11c\305\232\246$M\0@\15\205\274*" + ")\222\226DI\244\252\2A\12d\304\32%\222\206I\12B\14d\304\212%\32\222H\32\22\0C\12" + "d\304\32%\322J\211\2D\12d\304\212%r\32\22\0E\12d\304\212A[\262l\10F\12d\304" + "\212A[\262\32\0G\13d\304\32%\322\222)Q\0H\12d\304\212H\32&S\0I\10c\305\212" + "%j\31J\12d\304\232)\253\224\42\0K\13d\304\212HI\244\244S\0L\10d\304\212\254\333\20" + "M\12d\304\212h\70D\246\0N\12d\304\212h\31\226I\12O\12d\304\32%rJ\24\0P\13" + "d\304\212%\222\206$\313\0Q\12t\274\32%\222\26\307\0R\13d\304\212%\222\206$\222\2S\14" + "d\304\32%J\302$J\24\0T\11e\304\212A\12;\1U\11d\304\212\310S\242\0V\12d\304" + "\212\310)\221\24\0W\12d\304\212\310\64\34\242\0X\13d\304\212HJ$%\222\2Y\12e\304\212" + "LKja\11Z\12d\304\212!\213\332\206\0[\10c\305\212!j\32\134\10d\304\212,l\13]" + "\10c\305\212\251i\10^\6#\345\232\6_\6\24\274\212!`\6\42\345\212(a\11D\304\232!\222" + "\222\1b\13d\304\212,[\42iH\0c\7C\305\232)\23d\12d\304\272\312\20I\311\0e\11" + "D\304\32%\31\262\1f\12d\304\252Ji\312\42\0g\12T\274\32%J\266D\1h\12d\304\212" + ",[\42S\0i\10c\305\232P\252\14j\12s\275\252\64\212\224\12\0k\12d\304\212\254\64$\221" + "\24l\10c\305\12\251\313\0m\12E\304\12\245EI\224\2n\10D\304\212%\62\5o\11D\304\32" + "%\222\22\5p\12T\274\212%\32\222,\3q\11T\274\232!J\266\2r\11D\304\212$\261e\0" + "s\10C\305\232![\0t\13d\304\232,\232\262$J\0u\10D\304\212\310\224\14v\10C\305\212" + "\304R\1w\12E\304\212LI\224.\0x\11D\304\212(\221\224(y\13T\274\212HJ\206(Q" + "\0z\11D\304\212!*\15\1{\12t\304*%L\304(\24|\6a\306\212\3}\13t\304\12\61" + "\12\225\60\221\0~\10$\344\232DI\0\5\0\304\12\200\13u\274\212K\242T\266\260\4\201\14f" + "D\233!\11#-\312!\11\202\15hD<\65\12\243,\214\302$\16\203\15w<\214C\22F\71\220" + "\26\207A\204\16\205\274\212,)%Y\230%QR\13\205\17\206<\213\60\31\22\311\66D\245!\11\3" + "\206\20\210<\254\342\20]\302(L\246C\30E\0\207\15wD\334X\25\267\341\20\15\21\0\210\16w" + "<\214\203RQ\25I\212\324a\20\211\15f\304\213)\213\244,\222\222\245\0\0\0\0"; + + +const uint8_t u8g2_font_ncenB08_symbols8_ahoy[173] U8G2_FONT_SECTION("u8g2_font_ncenB08_symbols8_ahoy") = + "\13\0\3\2\4\4\1\2\5\10\11\0\0\10\0\10\0\0\0\0\0\0\224A\14\207\305\70H\321\222H" + "k\334\6B\20\230\305\32\262\60\211\244\266\60T\243\34\326\0C\20\210\305S\243\60\312\302(\214\302(" + "L\342\0D\16\210\315\70(i\224#\71\20W\207\3E\15\207\305xI\206\323\232nIS\1F\25" + "\230\305H\206\244\230$C\22\15Y\242\204j\224\205I$\5G\17\210\305*\16\321%\214\302d:\204" + "Q\4H\14w\307\215Uq\33\16\321\20\1I\21\227\305\311\222aP\245H\221\244H\212\324a\20J" + "\5\0\275\0K\5\0\315\0\0\0\0"; + +const uint8_t u8g2_font_ncenB10_symbols10_ahoy[207] U8G2_FONT_SECTION("u8g2_font_ncenB10_symbols10_ahoy") = + "\13\0\3\2\4\4\2\2\5\13\13\0\0\13\0\13\0\0\0\0\0\0\266A\15\267\212q\220\42\251\322" + "\266\306\275\1B\20\230\236\65da\22Ima\250F\71\254\1C\23\272\272\251\3Q\32\366Q\212\243" + "\70\212\243\70\311\221\0D\20\271\252\361\242F:\242#: {\36\16\1E\22\267\212\361\222\14I\242" + "\14\332\232\216[RJ\232\12F\25\250\233\221\14I\61I\206$\252%J\250Fa\224%J\71G\30" + "\273\312W\316r`T\262DJ\303\64L#%K\304\35\310\342,\3H\27\272\272\217\344P\16\351\210" + "\16\354\300<\244C\70,\303 \16!\0I\24\271\252\241\34\336\1-\223\64-\323\62-\323\62\35x" + "\10J\5\0\232\1K\5\0\232\1\0\0\0"; + diff --git a/src/plugins/Display/Display_Mono_128X32.h b/src/plugins/Display/Display_Mono_128X32.h index 5a6d6c7e..7012604b 100644 --- a/src/plugins/Display/Display_Mono_128X32.h +++ b/src/plugins/Display/Display_Mono_128X32.h @@ -9,45 +9,32 @@ class DisplayMono128X32 : public DisplayMono { public: DisplayMono128X32() : DisplayMono() { - mEnPowerSafe = true; + mEnPowerSave = true; mEnScreenSaver = true; mLuminance = 60; mExtra = 0; mDispY = 0; mTimeout = DISP_DEFAULT_TIMEOUT; // interval at which to power save (milliseconds) - mUtcTs = NULL; - mType = 0; } + void config(bool enPowerSave, bool enScreenSaver, uint8_t lum) { + mEnPowerSave = enPowerSave; + mEnScreenSaver = enScreenSaver; + mLuminance = lum; + } - 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) { - + void init(uint8_t type, uint8_t rotation, uint8_t cs, uint8_t dc, uint8_t reset, uint8_t clock, uint8_t data, DisplayData *displayData) { u8g2_cb_t *rot = (u8g2_cb_t *)((rotation != 0x00) ? U8G2_R2 : U8G2_R0); - mType = type; - mDisplay = new U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C(rot, reset, clock, data); - - mUtcTs = utcTs; - - mDisplay->begin(); - + monoInit(new U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C(rot, reset, clock, data), type, displayData); calcLinePositions(); - - mDisplay->clearBuffer(); - mDisplay->setContrast(mLuminance); - printText("AHOY!", 0); + printText("Ahoy!", 0); printText("ahoydtu.de", 2); - printText(version, 3); + printText(mDisplayData->version, 3); mDisplay->sendBuffer(); } - void config(bool enPowerSafe, bool enScreenSaver, uint8_t lum) { - mEnPowerSafe = enPowerSafe; - mEnScreenSaver = enScreenSaver; - mLuminance = lum; - } - void loop(uint8_t lum) { - if (mEnPowerSafe) { + if (mEnPowerSave) { if (mTimeout != 0) mTimeout--; } @@ -58,43 +45,43 @@ class DisplayMono128X32 : public DisplayMono { } } - void disp(float totalPower, float totalYieldDay, float totalYieldTotal, uint8_t isprod) { + void disp(void) { mDisplay->clearBuffer(); // set Contrast of the Display to raise the lifetime if (3 != mType) mDisplay->setContrast(mLuminance); - if ((totalPower > 0) && (isprod > 0)) { + if ((mDisplayData->totalPower > 0) && (mDisplayData->nrProducing > 0)) { mTimeout = DISP_DEFAULT_TIMEOUT; mDisplay->setPowerSave(false); - if (totalPower > 999) - snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%2.2f kW", (totalPower / 1000)); + if (mDisplayData->totalPower > 999) + snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%2.2f kW", (mDisplayData->totalPower / 1000)); else - snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%3.0f W", totalPower); + snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%3.0f W", mDisplayData->totalPower); printText(mFmtText, 0); } else { printText("offline", 0); // check if it's time to enter power saving mode if (mTimeout == 0) - mDisplay->setPowerSave(mEnPowerSafe); + mDisplay->setPowerSave(mEnPowerSave); } - snprintf(mFmtText, DISP_FMT_TEXT_LEN, "today: %4.0f Wh", totalYieldDay); + snprintf(mFmtText, DISP_FMT_TEXT_LEN, "today: %4.0f Wh", mDisplayData->totalYieldDay); printText(mFmtText, 1); - snprintf(mFmtText, DISP_FMT_TEXT_LEN, "total: %.1f kWh", totalYieldTotal); + snprintf(mFmtText, DISP_FMT_TEXT_LEN, "total: %.1f kWh", mDisplayData->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, "%d Inverter on", isprod); + snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%d Inverter on", mDisplayData->nrProducing); printText(mFmtText, 3); - } else if (NULL != mUtcTs) - printText(ah::getTimeStr(gTimezone.toLocal(*mUtcTs)).c_str(), 3); + } else if (0 != mDisplayData->utcTs) + printText(ah::getTimeStr(gTimezone.toLocal(mDisplayData->utcTs)).c_str(), 3); mDisplay->sendBuffer(); @@ -119,13 +106,13 @@ class DisplayMono128X32 : public DisplayMono { inline void setFont(uint8_t line) { switch (line) { case 0: - mDisplay->setFont(u8g2_font_9x15_tf); + mDisplay->setFont(u8g2_font_9x15_tr); break; case 3: - mDisplay->setFont(u8g2_font_tom_thumb_4x6_tf); + mDisplay->setFont(u8g2_font_tom_thumb_4x6_tr); break; default: - mDisplay->setFont(u8g2_font_tom_thumb_4x6_tf); + mDisplay->setFont(u8g2_font_tom_thumb_4x6_tr); break; } } diff --git a/src/plugins/Display/Display_Mono_128X64.h b/src/plugins/Display/Display_Mono_128X64.h index 438cb79e..4ea85d50 100644 --- a/src/plugins/Display/Display_Mono_128X64.h +++ b/src/plugins/Display/Display_Mono_128X64.h @@ -9,53 +9,42 @@ class DisplayMono128X64 : public DisplayMono { public: DisplayMono128X64() : DisplayMono() { - mEnPowerSafe = true; + mEnPowerSave = true; mEnScreenSaver = true; mLuminance = 60; 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) { + void config(bool enPowerSave, bool enScreenSaver, uint8_t lum) { + mEnPowerSave = enPowerSave; + mEnScreenSaver = enScreenSaver; + mLuminance = lum; + } + void init(uint8_t type, uint8_t rotation, uint8_t cs, uint8_t dc, uint8_t reset, uint8_t clock, uint8_t data, DisplayData *displayData) { u8g2_cb_t *rot = (u8g2_cb_t *)((rotation != 0x00) ? U8G2_R2 : U8G2_R0); - mType = type; - switch (type) { case 1: - mDisplay = new U8G2_SSD1306_128X64_NONAME_F_HW_I2C(rot, reset, clock, data); + monoInit(new U8G2_SSD1306_128X64_NONAME_F_HW_I2C(rot, reset, clock, data), type, displayData); break; case 2: - mDisplay = new U8G2_SH1106_128X64_NONAME_F_HW_I2C(rot, reset, clock, data); + monoInit(new U8G2_SH1106_128X64_NONAME_F_HW_I2C(rot, reset, clock, data), type, displayData); break; case 6: - mDisplay = new U8G2_SSD1309_128X64_NONAME0_F_HW_I2C(rot, reset, clock, data); + default: + monoInit(new U8G2_SSD1309_128X64_NONAME0_F_HW_I2C(rot, reset, clock, data), type, displayData); break; } - - mUtcTs = utcTs; - - mDisplay->begin(); calcLinePositions(); - - mDisplay->clearBuffer(); - mDisplay->setContrast(mLuminance); - printText("AHOY!", 0, 35); + printText("Ahoy!", 0, 35); printText("ahoydtu.de", 2, 20); - printText(version, 3, 46); + printText(mDisplayData->version, 3, 46); mDisplay->sendBuffer(); } - void config(bool enPowerSafe, bool enScreenSaver, uint8_t lum) { - mEnPowerSafe = enPowerSafe; - mEnScreenSaver = enScreenSaver; - mLuminance = lum; - } - void loop(uint8_t lum) { - if (mEnPowerSafe) { + if (mEnPowerSave) { if (mTimeout != 0) mTimeout--; } @@ -66,43 +55,43 @@ class DisplayMono128X64 : public DisplayMono { } } - void disp(float totalPower, float totalYieldDay, float totalYieldTotal, uint8_t isprod) { + void disp(void) { mDisplay->clearBuffer(); // set Contrast of the Display to raise the lifetime mDisplay->setContrast(mLuminance); - if ((totalPower > 0) && (isprod > 0)) { + if ((mDisplayData->totalPower > 0) && (mDisplayData->nrProducing > 0)) { mTimeout = DISP_DEFAULT_TIMEOUT; mDisplay->setPowerSave(false); - if (totalPower > 999) - snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%2.2f kW", (totalPower / 1000)); + if (mDisplayData->totalPower > 999) + snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%2.2f kW", (mDisplayData->totalPower / 1000)); else - snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%3.0f W", totalPower); + snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%3.0f W", mDisplayData->totalPower); printText(mFmtText, 0); } else { printText("offline", 0, 25); // check if it's time to enter power saving mode if (mTimeout == 0) - mDisplay->setPowerSave(mEnPowerSafe); + mDisplay->setPowerSave(mEnPowerSave); } - snprintf(mFmtText, DISP_FMT_TEXT_LEN, "today: %4.0f Wh", totalYieldDay); + snprintf(mFmtText, DISP_FMT_TEXT_LEN, "today: %4.0f Wh", mDisplayData->totalYieldDay); printText(mFmtText, 1); - snprintf(mFmtText, DISP_FMT_TEXT_LEN, "total: %.1f kWh", totalYieldTotal); + snprintf(mFmtText, DISP_FMT_TEXT_LEN, "total: %.1f kWh", mDisplayData->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, "%d Inverter on", isprod); + snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%d Inverter on", mDisplayData->nrProducing); printText(mFmtText, 3); - } else if (NULL != mUtcTs) - printText(ah::getDateTimeStr(gTimezone.toLocal(*mUtcTs)).c_str(), 3); + } else if (0 != mDisplayData->utcTs) + printText(ah::getDateTimeStr(gTimezone.toLocal(mDisplayData->utcTs)).c_str(), 3); mDisplay->sendBuffer(); @@ -126,7 +115,7 @@ class DisplayMono128X64 : public DisplayMono { mDisplay->setFont(u8g2_font_ncenB14_tr); break; case 3: - mDisplay->setFont(u8g2_font_5x8_tr); + mDisplay->setFont(u8g2_font_5x8_symbols_ahoy); break; default: mDisplay->setFont(u8g2_font_ncenB10_tr); diff --git a/src/plugins/Display/Display_Mono_64X48.h b/src/plugins/Display/Display_Mono_64X48.h index ae074c33..43c7e365 100644 --- a/src/plugins/Display/Display_Mono_64X48.h +++ b/src/plugins/Display/Display_Mono_64X48.h @@ -9,46 +9,33 @@ class DisplayMono64X48 : public DisplayMono { public: DisplayMono64X48() : DisplayMono() { - mEnPowerSafe = true; + mEnPowerSave = 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) { + void config(bool enPowerSave, bool enScreenSaver, uint8_t lum) { + mEnPowerSave = enPowerSave; + mEnScreenSaver = enScreenSaver; + mLuminance = lum; + } + void init(uint8_t type, uint8_t rotation, uint8_t cs, uint8_t dc, uint8_t reset, uint8_t clock, uint8_t data, DisplayData *displayData) { 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(); + monoInit(new U8G2_SSD1306_64X48_ER_F_HW_I2C(rot, reset, clock, data), type, displayData); calcLinePositions(); - - mDisplay->clearBuffer(); - mDisplay->setContrast(mLuminance); - - printText("AHOY!", 0); + printText("Ahoy!", 0); printText("ahoydtu.de", 1); - printText(version, 2); + printText(mDisplayData->version, 2); mDisplay->sendBuffer(); } - void config(bool enPowerSafe, bool enScreenSaver, uint8_t lum) { - mEnPowerSafe = enPowerSafe; - mEnScreenSaver = enScreenSaver; - mLuminance = lum; - } - void loop(uint8_t lum) { - if (mEnPowerSafe) { + if (mEnPowerSave) { if (mTimeout != 0) mTimeout--; } @@ -59,43 +46,43 @@ class DisplayMono64X48 : public DisplayMono { } } - void disp(float totalPower, float totalYieldDay, float totalYieldTotal, uint8_t isprod) { + void disp(void) { mDisplay->clearBuffer(); // set Contrast of the Display to raise the lifetime mDisplay->setContrast(mLuminance); - if ((totalPower > 0) && (isprod > 0)) { + if ((mDisplayData->totalPower > 0) && (mDisplayData->nrProducing > 0)) { mTimeout = DISP_DEFAULT_TIMEOUT; mDisplay->setPowerSave(false); - if (totalPower > 999) - snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%2.2f kW", (totalPower / 1000)); + if (mDisplayData->totalPower > 999) + snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%2.2f kW", (mDisplayData->totalPower / 1000)); else - snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%3.0f W", totalPower); + snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%3.0f W", mDisplayData->totalPower); printText(mFmtText, 0); } else { printText("offline", 0); // check if it's time to enter power saving mode if (mTimeout == 0) - mDisplay->setPowerSave(mEnPowerSafe); + mDisplay->setPowerSave(mEnPowerSave); } - snprintf(mFmtText, DISP_FMT_TEXT_LEN, "D: %4.0f Wh", totalYieldDay); + snprintf(mFmtText, DISP_FMT_TEXT_LEN, "D: %4.0f Wh", mDisplayData->totalYieldDay); printText(mFmtText, 1); - snprintf(mFmtText, DISP_FMT_TEXT_LEN, "T: %4.0f kWh", totalYieldTotal); + snprintf(mFmtText, DISP_FMT_TEXT_LEN, "T: %4.0f kWh", mDisplayData->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); + snprintf(mFmtText, DISP_FMT_TEXT_LEN, "active Inv: %d", mDisplayData->nrProducing); printText(mFmtText, 3); - } else if (NULL != mUtcTs) - printText(ah::getTimeStr(gTimezone.toLocal(*mUtcTs)).c_str(), 3); + } else if (0 != mDisplayData->utcTs) + printText(ah::getTimeStr(gTimezone.toLocal(mDisplayData->utcTs)).c_str(), 3); mDisplay->sendBuffer(); @@ -115,11 +102,11 @@ class DisplayMono64X48 : public DisplayMono { inline void setFont(uint8_t line) { switch (line) { case 0: - mDisplay->setFont(u8g2_font_fur11_tf); + mDisplay->setFont(u8g2_font_fur11_tr); break; case 1: case 2: - mDisplay->setFont(u8g2_font_6x10_tf); + mDisplay->setFont(u8g2_font_6x10_tr); break; case 3: mDisplay->setFont(u8g2_font_4x6_tr); diff --git a/src/plugins/Display/Display_Mono_84X48.h b/src/plugins/Display/Display_Mono_84X48.h index fff983bc..ed540523 100644 --- a/src/plugins/Display/Display_Mono_84X48.h +++ b/src/plugins/Display/Display_Mono_84X48.h @@ -5,50 +5,37 @@ #pragma once #include "Display_Mono.h" +#include "../../utils/dbg.h" class DisplayMono84X48 : public DisplayMono { public: DisplayMono84X48() : DisplayMono() { - mEnPowerSafe = true; + mEnPowerSave = true; mEnScreenSaver = true; - mLuminance = 60; + mLuminance = 140; mExtra = 0; mDispY = 0; mTimeout = DISP_DEFAULT_TIMEOUT; // interval at which to power save (milliseconds) - mUtcTs = NULL; - mType = 0; - mDispWidth = 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) { + void config(bool enPowerSave, bool enScreenSaver, uint8_t lum) { + mEnPowerSave = enPowerSave; + mEnScreenSaver = enScreenSaver; + mLuminance = lum; + } + void init(uint8_t type, uint8_t rotation, uint8_t cs, uint8_t dc, uint8_t reset, uint8_t clock, uint8_t data, DisplayData *displayData) { u8g2_cb_t *rot = (u8g2_cb_t *)((rotation != 0x00) ? U8G2_R2 : U8G2_R0); - mType = type; - mDisplay = new U8G2_PCD8544_84X48_F_4W_SW_SPI(rot, clock, data, cs, dc, reset); - - mUtcTs = utcTs; - - mDisplay->begin(); - mDispWidth = mDisplay->getDisplayWidth(); + monoInit(new U8G2_PCD8544_84X48_F_4W_SW_SPI(rot, clock, data, cs, dc, reset), type, displayData); calcLinePositions(); - - mDisplay->clearBuffer(); - mDisplay->setContrast(mLuminance); - - printText("AHOY!", l_Ahoy); - printText("ahoydtu.de", l_Website); - printText(version, l_Version); + printText("Ahoy!", l_Ahoy, 0xff); + printText("ahoydtu.de", l_Website, 0xff); + printText(mDisplayData->version, l_Version, 0xff); mDisplay->sendBuffer(); } - void config(bool enPowerSafe, bool enScreenSaver, uint8_t lum) { - mEnPowerSafe = enPowerSafe; - mEnScreenSaver = enScreenSaver; - mLuminance = lum; - } - void loop(uint8_t lum) { - if (mEnPowerSafe) { + if (mEnPowerSave) { if (mTimeout != 0) mTimeout--; } @@ -59,44 +46,88 @@ class DisplayMono84X48 : public DisplayMono { } } - void disp(float totalPower, float totalYieldDay, float totalYieldTotal, uint8_t isprod) { - mDisplay->clearBuffer(); + void disp(void) { + // Test + /* + mDisplayData->nrSleeping = 10; + mDisplayData->nrProducing = 10; + mDisplayData->totalPower = 12345.67; + mDisplayData->totalYieldDay = 12345.67; + mDisplayData->totalYieldTotal = 1234; + mDisplayData->utcTs += 1000000; + */ - // set Contrast of the Display to raise the lifetime - mDisplay->setContrast(mLuminance); + mDisplay->clearBuffer(); - if ((totalPower > 0) && (isprod > 0)) { + // print total power + if (mDisplayData->nrProducing > 0) { mTimeout = DISP_DEFAULT_TIMEOUT; mDisplay->setPowerSave(false); - if (totalPower > 999) - snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%.2f kW", (totalPower / 1000)); + if (mDisplayData->totalPower > 9999) + snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%.2fkW", (mDisplayData->totalPower / 1000)); // forgo spacing between value and SI unit in favor of second position after decimal point + else if (mDisplayData->totalPower > 999) + snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%.2f kW", (mDisplayData->totalPower / 1000)); else - snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%.0f W", totalPower); + snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%.1f W", mDisplayData->totalPower); - printText(mFmtText, l_TotalPower); + printText(mFmtText, l_TotalPower, 0xff); } else { - printText("offline", l_TotalPower); + printText("offline", l_TotalPower, 0xff); // check if it's time to enter power saving mode if (mTimeout == 0) - mDisplay->setPowerSave(mEnPowerSafe); + mDisplay->setPowerSave(mEnPowerSave); } - snprintf(mFmtText, DISP_FMT_TEXT_LEN, "Today: %4.0f Wh", totalYieldDay); - printText(mFmtText, l_YieldDay); + // print Date and time + if (0 != mDisplayData->utcTs) + printText(ah::getDateTimeStrShort(gTimezone.toLocal(mDisplayData->utcTs)).c_str(), l_Time, 0xff); - snprintf(mFmtText, DISP_FMT_TEXT_LEN, "Total: %.1f kWh", totalYieldTotal); - printText(mFmtText, l_YieldTotal); + // alternatively: + // print ip address + if (!(mExtra % 5) && (mDisplayData->ipAddress)) { + snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%s", (mDisplayData->ipAddress).toString().c_str()); + printText(mFmtText, l_Status, 0xff); + } + // print status of inverters + else { + if (0 == mDisplayData->nrSleeping) + snprintf(mFmtText, DISP_FMT_TEXT_LEN, "\x86"); + else if (0 == mDisplayData->nrProducing) + snprintf(mFmtText, DISP_FMT_TEXT_LEN, "\x87"); + else + snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%d\x86 %d\x87", mDisplayData->nrProducing, mDisplayData->nrSleeping); + setLineFont(l_Status); + printText(mFmtText, l_Status, (mDispWidth - mDisplay->getStrWidth(mFmtText)) / 2); + } - if (NULL != mUtcTs) - printText(ah::getDateTimeStrShort(gTimezone.toLocal(*mUtcTs)).c_str(), l_Time); + // print yields + printText("\x88", l_YieldDay, 11); // day symbol + printText("\x83", l_YieldTotal, 11); // total symbol + snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%7.0f Wh", mDisplayData->totalYieldDay); + printText(mFmtText, l_YieldDay, 18); + snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%7.1f kWh", mDisplayData->totalYieldTotal); + printText(mFmtText, l_YieldTotal, 18); + + // draw dynamic Nokia RSSI bars + int rssi_bar_height = 7; + for (int i=0; i<4;i++) { + int radio_rssi_threshold = -60 - i*10; // radio rssi not yet tested in reality! + int wifi_rssi_threshold = -60 - i*10; + if (mDisplayData->RadioRSSI > radio_rssi_threshold) + mDisplay->drawBox(0, 8+(rssi_bar_height+1)*i, 4-i,rssi_bar_height); + if (mDisplayData->WifiRSSI > wifi_rssi_threshold) + mDisplay->drawBox(mDispWidth-4+i, 8+(rssi_bar_height+1)*i, 4-i,rssi_bar_height); + } - IPAddress ip = WiFi.localIP(); - if (!(mExtra % 5) && (ip)) - snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%s", ip.toString().c_str()); + // draw dynamic antenna and WiFi symbols + snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%c", mDisplayData->RadioSymbol?'\x80':'\x84'); // NRF + printText(mFmtText, l_RSSI); + if (mDisplayData->MQTTSymbol) + snprintf(mFmtText, DISP_FMT_TEXT_LEN, "\x89"); // MQTT else - snprintf(mFmtText, DISP_FMT_TEXT_LEN, "Inv.On: %d", isprod); - printText(mFmtText, l_Status); + snprintf(mFmtText, DISP_FMT_TEXT_LEN, "%c", mDisplayData->WifiSymbol?'\x81':'\x85'); // Wifi connected + printText(mFmtText, l_RSSI, mDispWidth - 6); mDisplay->sendBuffer(); @@ -104,7 +135,6 @@ class DisplayMono84X48 : public DisplayMono { } private: - uint16_t mDispWidth; enum _dispLine { // start page l_Website = 0, @@ -116,6 +146,8 @@ class DisplayMono84X48 : public DisplayMono { l_TotalPower = 2, l_YieldDay = 3, l_YieldTotal = 4, + // run page - rssi bar symbols + l_RSSI = 4, // End l_MAX_LINES = 5, }; @@ -126,7 +158,7 @@ class DisplayMono84X48 : public DisplayMono { uint8_t asc, dsc; do { - setFont(i); + setLineFont(i); asc = mDisplay->getAscent(); yOff += asc; mLineYOffsets[i] = yOff; @@ -138,18 +170,23 @@ class DisplayMono84X48 : public DisplayMono { } while(l_MAX_LINES>i); } - inline void setFont(uint8_t line) { + inline void setLineFont(uint8_t line) { if ((line == l_TotalPower) || (line == l_Ahoy)) mDisplay->setFont(u8g2_font_logisoso16_tr); else - mDisplay->setFont(u8g2_font_5x8_tr); + mDisplay->setFont(u8g2_font_5x8_symbols_ahoy); } - void printText(const char *text, uint8_t line) { + void printText(const char *text, uint8_t line, uint8_t col=0) { uint8_t dispX; - setFont(line); - dispX = (mDispWidth - mDisplay->getStrWidth(text)) / 2; // center text - dispX += (mEnScreenSaver) ? (mExtra % 7) : 0; + + setLineFont(line); + if (0xff == col) + dispX = (mDispWidth - mDisplay->getStrWidth(text)) / 2; // center text + else + dispX = col; mDisplay->drawStr(dispX, mLineYOffsets[line], text); } }; + + diff --git a/src/plugins/Display/Display_data.h b/src/plugins/Display/Display_data.h new file mode 100644 index 00000000..32525bd4 --- /dev/null +++ b/src/plugins/Display/Display_data.h @@ -0,0 +1,22 @@ +#include "../../utils/helper.h" + +#ifndef __DISPLAY_DATA__ +#define __DISPLAY_DATA__ + +struct DisplayData { + const char *version=nullptr; + float totalPower=0.0f; // indicate current power (W) + float totalYieldDay=0.0f; // indicate day yield (W) + float totalYieldTotal=0.0f; // indicate total yield (W) + uint32_t utcTs=0; // indicate absolute timestamp (utc unix time). 0 = time is not synchonized + uint8_t nrProducing=0; // indicate number of producing inverters + uint8_t nrSleeping=0; // indicate number of sleeping inverters + bool WifiSymbol = false; // indicate if WiFi is connected + bool RadioSymbol = false; // indicate if radio module is connecting and working + bool MQTTSymbol = false; // indicate if MQTT is connected + int8_t WifiRSSI=SCHAR_MIN; // indicate RSSI value for WiFi + int8_t RadioRSSI=SCHAR_MIN; // indicate RSSI value for radio + IPAddress ipAddress; // indicate ip adress of ahoy +}; + +#endif /*__DISPLAY_DATA__*/ diff --git a/src/plugins/Display/Display_ePaper.cpp b/src/plugins/Display/Display_ePaper.cpp index 9a4b594c..d12da365 100644 --- a/src/plugins/Display/Display_ePaper.cpp +++ b/src/plugins/Display/Display_ePaper.cpp @@ -46,9 +46,9 @@ void DisplayEPaper::init(uint8_t type, uint8_t _CS, uint8_t _DC, uint8_t _RST, u } } -void DisplayEPaper::config(uint8_t rotation, bool enPowerSafe) { +void DisplayEPaper::config(uint8_t rotation, bool enPowerSave) { mDisplayRotation = rotation; - mEnPowerSafe = enPowerSafe; + mEnPowerSave = enPowerSave; } //*************************************************************************** @@ -209,7 +209,7 @@ void DisplayEPaper::actualPowerPaged(float totalPower, float totalYieldDay, floa } else snprintf(_fmtText, sizeof(_fmtText), "offline"); - if ((totalPower == 0) && (mEnPowerSafe)) { + if ((totalPower == 0) && (mEnPowerSave)) { _display->fillRect(0, mHeadFootPadding, 200, 200, GxEPD_BLACK); _display->drawBitmap(0, 0, logo, 200, 200, GxEPD_WHITE); } else { @@ -291,7 +291,7 @@ 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) && (mEnPowerSave)) offlineFooter(); _display->powerOff(); diff --git a/src/plugins/Display/Display_ePaper.h b/src/plugins/Display/Display_ePaper.h index 2ff7e58d..c9a0fbf5 100644 --- a/src/plugins/Display/Display_ePaper.h +++ b/src/plugins/Display/Display_ePaper.h @@ -28,7 +28,7 @@ class DisplayEPaper { 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 config(uint8_t rotation, bool enPowerSave); void loop(float totalPower, float totalYieldDay, float totalYieldTotal, uint8_t isprod); void refreshLoop(); void tickerSecond(); @@ -56,7 +56,7 @@ class DisplayEPaper { uint8_t mHeadFootPadding; GxEPD2_GFX* _display; uint32_t* mUtcTs; - bool mEnPowerSafe; + bool mEnPowerSave; const char* _version; RefreshStatus mRefreshState, mNextRefreshState; uint8_t mSecondCnt; diff --git a/src/web/RestApi.h b/src/web/RestApi.h index 1530c476..19a39f3a 100644 --- a/src/web/RestApi.h +++ b/src/web/RestApi.h @@ -31,7 +31,7 @@ const uint8_t acList[] = {FLD_UAC, FLD_IAC, FLD_PAC, FLD_F, FLD_PF, FLD_T, FLD_Y const uint8_t acListHmt[] = {FLD_UAC_1N, FLD_IAC_1, FLD_PAC, FLD_F, FLD_PF, FLD_T, FLD_YT, FLD_YD, FLD_PDC, FLD_EFF, FLD_Q, FLD_MP}; const uint8_t dcList[] = {FLD_UDC, FLD_IDC, FLD_PDC, FLD_YD, FLD_YT, FLD_IRR, FLD_MP}; -template +template class RestApi { public: RestApi() { @@ -42,12 +42,15 @@ class RestApi { nr = 0; } - void setup(IApp *app, HMSYSTEM *sys, HMRADIO *radio, AsyncWebServer *srv, settings_t *config) { - mApp = app; - mSrv = srv; - mSys = sys; - mRadio = radio; - mConfig = config; + void setup(IApp *app, HMSYSTEM *sys, AsyncWebServer *srv, settings_t *config) { + mApp = app; + mSrv = srv; + mSys = sys; + mRadioNrf = (HmRadio<>*)mApp->getRadioObj(true); + #if defined(ESP32) + mRadioCmt = (CmtRadio*)mApp->getRadioObj(false); + #endif + mConfig = config; mSrv->on("/api", HTTP_GET, std::bind(&RestApi::onApi, this, std::placeholders::_1)); mSrv->on("/api", HTTP_POST, std::bind(&RestApi::onApiPost, this, std::placeholders::_1)).onBody( std::bind(&RestApi::onApiPostBody, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5)); @@ -238,7 +241,11 @@ class RestApi { obj[F("sketch_used")] = ESP.getSketchSize() / 1024; // in kb getGeneric(request, obj); - getRadioNrf(obj.createNestedObject(F("radio"))); + getRadioNrf(obj.createNestedObject(F("radioNrf"))); + #if defined(ESP32) + getRadioCmtInfo(obj.createNestedObject(F("radioCmt"))); + #endif + getMqttInfo(obj.createNestedObject(F("mqtt"))); getStatistics(obj.createNestedObject(F("statistics"))); #if defined(ESP32) @@ -310,8 +317,8 @@ class RestApi { obj[F("rx_fail")] = stat->rxFail; obj[F("rx_fail_answer")] = stat->rxFailNoAnser; obj[F("frame_cnt")] = stat->frmCnt; - obj[F("tx_cnt")] = mRadio->mSendCnt; - obj[F("retransmits")] = mRadio->mRetransmits; + obj[F("tx_cnt")] = mRadioNrf->mSendCnt; + obj[F("retransmits")] = mRadioNrf->mRetransmits; } void getInverterList(JsonObject obj) { @@ -511,19 +518,28 @@ class RestApi { obj[F("led_high_active")] = mConfig->led.led_high_active; } + #if defined(ESP32) void getRadioCmt(JsonObject obj) { - obj[F("csb")] = mConfig->cmt.pinCsb; - obj[F("fcsb")] = mConfig->cmt.pinFcsb; - obj[F("gpio3")] = mConfig->cmt.pinIrq; - obj[F("en")] = (bool) mConfig->cmt.enabled; + obj[F("sclk")] = mConfig->cmt.pinSclk; + obj[F("sdio")] = mConfig->cmt.pinSdio; + obj[F("csb")] = mConfig->cmt.pinCsb; + obj[F("fcsb")] = mConfig->cmt.pinFcsb; + obj[F("gpio3")] = mConfig->cmt.pinIrq; + obj[F("en")] = (bool) mConfig->cmt.enabled; } + void getRadioCmtInfo(JsonObject obj) { + obj[F("en")] = (bool) mConfig->cmt.enabled; + obj[F("isconnected")] = mRadioCmt->isConnected(); + } + #endif + void getRadioNrf(JsonObject obj) { + obj[F("en")] = (bool) mConfig->nrf.enabled; + obj[F("isconnected")] = mRadioNrf->isChipConnected(); obj[F("power_level")] = mConfig->nrf.amplifierPower; - obj[F("isconnected")] = mRadio->isChipConnected(); - obj[F("DataRate")] = mRadio->getDataRate(); - obj[F("isPVariant")] = mRadio->isPVariant(); - obj[F("en")] = (bool) mConfig->nrf.enabled; + obj[F("dataRate")] = mRadioNrf->getDataRate(); + //obj[F("isPVariant")] = mRadioNrf->isPVariant(); } void getSerial(JsonObject obj) { @@ -555,6 +571,14 @@ class RestApi { obj[F("disp_bsy")] = (mConfig->plugin.display.type < 10) ? DEF_PIN_OFF : mConfig->plugin.display.disp_busy; } + void getMqttInfo(JsonObject obj) { + obj[F("enabled")] = (mConfig->mqtt.broker[0] != '\0'); + obj[F("connected")] = mApp->getMqttIsConnected(); + obj[F("tx_cnt")] = mApp->getMqttTxCnt(); + obj[F("rx_cnt")] = mApp->getMqttRxCnt(); + obj[F("interval")] = mConfig->mqtt.interval; + } + void getIndex(AsyncWebServerRequest *request, JsonObject obj) { getGeneric(request, obj.createNestedObject(F("generic"))); obj[F("ts_now")] = mApp->getTimestamp(); @@ -581,9 +605,9 @@ class RestApi { } JsonArray warn = obj.createNestedArray(F("warnings")); - if(!mRadio->isChipConnected() && mConfig->nrf.enabled) + if(!mRadioNrf->isChipConnected() && mConfig->nrf.enabled) warn.add(F("your NRF24 module can't be reached, check the wiring, pinout and enable")); - else if(!mRadio->isPVariant() && mConfig->nrf.enabled) + else if(!mRadioNrf->isPVariant() && mConfig->nrf.enabled) warn.add(F("your NRF24 module isn't a plus version(+), maybe incompatible")); if(!mApp->getSettingsValid()) warn.add(F("your settings are invalid")); @@ -591,19 +615,6 @@ class RestApi { warn.add(F("reboot your ESP to apply all your configuration changes")); if(0 == mApp->getTimestamp()) warn.add(F("time not set. No communication to inverter possible")); - - - /*if(0 == mSys->getNumInverters()) - warn.add(F("no inverter configured"));*/ - - if((!mApp->getMqttIsConnected()) && (String(mConfig->mqtt.broker).length() > 0)) - warn.add(F("MQTT is not connected")); - - JsonArray info = obj.createNestedArray(F("infos")); - if(mApp->getMqttIsConnected()) - info.add(F("MQTT is connected, ") + String(mApp->getMqttTxCnt()) + F(" packets sent, ") + String(mApp->getMqttRxCnt()) + F(" packets received")); - if(mConfig->mqtt.interval > 0) - info.add(F("MQTT publishes in a fixed interval of ") + String(mConfig->mqtt.interval) + F(" seconds")); } void getSetup(AsyncWebServerRequest *request, JsonObject obj) { @@ -614,7 +625,9 @@ class RestApi { getNtp(obj.createNestedObject(F("ntp"))); getSun(obj.createNestedObject(F("sun"))); getPinout(obj.createNestedObject(F("pinout"))); + #if defined(ESP32) getRadioCmt(obj.createNestedObject(F("radioCmt"))); + #endif getRadioNrf(obj.createNestedObject(F("radioNrf"))); getSerial(obj.createNestedObject(F("serial"))); getStaticIp(obj.createNestedObject(F("static_ip"))); @@ -743,7 +756,10 @@ class RestApi { IApp *mApp; HMSYSTEM *mSys; - HMRADIO *mRadio; + HmRadio<> *mRadioNrf; + #if defined(ESP32) + CmtRadio *mRadioCmt; + #endif AsyncWebServer *mSrv; settings_t *mConfig; diff --git a/src/web/html/index.html b/src/web/html/index.html index a7c79c25..088d6410 100644 --- a/src/web/html/index.html +++ b/src/web/html/index.html @@ -19,7 +19,6 @@

-

Support this project:

    @@ -157,14 +156,11 @@ document.getElementById("iv").replaceChildren(p); } - function parseWarnInfo(warn, success) { + function parseWarn(warn) { var p = div(["none"]); for(var w of warn) { p.append(svg(iconWarn, 30, 30, "icon icon-warn"), span(w), br()); } - for(var i of success) { - p.append(svg(iconSuccess, 30, 30, "icon icon-success"), span(i), br()); - } if(commInfo.length > 0) p.append(svg(iconInfo, 30, 30, "icon icon-info"), span(commInfo), br()); @@ -197,7 +193,7 @@ parseGeneric(obj["generic"]); parseSys(obj); parseIv(obj["inverter"]); - parseWarnInfo(obj["warnings"], obj["infos"]); + parseWarn(obj["warnings"]); if(exeOnce) { window.setInterval("tick()", 1000); exeOnce = false; diff --git a/src/web/html/setup.html b/src/web/html/setup.html index 37adc279..5dc0b5bb 100644 --- a/src/web/html/setup.html +++ b/src/web/html/setup.html @@ -36,10 +36,10 @@

    Radio (NRF24L01+)

    - +

    Radio (CMT2300A)

    (ESP32 only)
    - +

    Serial Console

    print inverter data
    @@ -398,6 +398,8 @@ [15, "D8 (GPIO15)"], [16, "D0 (GPIO16 - no IRQ!)"] ]; + + /*IF_ESP32*/ var esp32pins = [ [255, "off / default"], [0, "GPIO0"], @@ -406,26 +408,26 @@ [3, "RX (GPIO3)"], [4, "GPIO4"], [5, "GPIO5"], - [12, "GPIO12"], - [13, "GPIO13"], - [14, "GPIO14"], + [12, "GPIO12 (HSPI MISO)"], + [13, "GPIO13 (HSPI MOSI)"], + [14, "GPIO14 (HSPI SCLK)"], [15, "GPIO15"], [16, "GPIO16"], [17, "GPIO17"], - [18, "GPIO18"], - [19, "GPIO19"], + [18, "GPIO18 (VSPI SCLK)"], + [19, "GPIO19 (VSPI MISO)"], [21, "GPIO21 (SDA)"], [22, "GPIO22 (SCL)"], - [23, "GPIO23"], + [23, "GPIO23 (VSPI MOSI)"], [25, "GPIO25"], [26, "GPIO26"], [27, "GPIO27"], [32, "GPIO32"], [33, "GPIO33"], - [34, "GPIO34"], - [35, "GPIO35"], - [36, "VP (GPIO36)"], - [39, "VN (GPIO39)"] + [34, "GPIO34 (in only)"], + [35, "GPIO35 (in only)"], + [36, "VP (GPIO36, in only)"], + [39, "VN (GPIO39, in only)"] ]; var esp32s3pins = [ [255, "off / default"], @@ -475,6 +477,7 @@ [47, "GPIO47"], [48, "GPIO48"], ]; + /*ENDIF_ESP32*/ var led_high_active = [ [0, "low active"], [1, "high active"], @@ -828,6 +831,7 @@ ); } + /*IF_ESP32*/ function parseCmtRadio(obj, type, system) { var e = document.getElementById("cmt"); var en = inp("cmtEnable", null, null, ["cb"], "cmtEnable", "checkbox"); @@ -839,18 +843,19 @@ ml("div", {class: "col-4 col-sm-9"}, en) ]) ); - pins = [['csb', 'pinCsb'], ['fcsb', 'pinFcsb'], ['gpio3', 'pinGpio3']]; + pins = [['sclk', 'pinCmtSclk'], ['sdio', 'pinSdio'], ['csb', 'pinCsb'], ['fcsb', 'pinFcsb'], ['gpio3', 'pinGpio3']]; for(p of pins) { e.append( 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], ("ESP8266" == type) ? esp8266pins : ("ESP32-S3" == system["chip_model"]) ? esp32s3pins : esp32pins, obj[p[0]]) + sel(p[1], ("ESP32-S3" == system["chip_model"]) ? esp32s3pins : esp32pins, obj[p[0]]) ) ]) ); } } + /*ENDIF_ESP32*/ function parseSerial(obj) { for(var i of [["serEn", "show_live_data"], ["serDbg", "debug"]]) @@ -865,8 +870,9 @@ 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']); + /*IF_ESP32*/ + pins.push(['busy', 'disp_bsy']); + /*ENDIF_ESP32*/ for(p of pins) { e.append( ml("div", {class: "row mb-3", id: "row_" + p[1]}, [ @@ -880,8 +886,9 @@ // 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"], [6, "SSD1309 2.42\" 128X64"], [3, "Nokia5110"]]; - if("ESP32" == type) - opts.push([10, "ePaper"]); + /*IF_ESP32*/ + opts.push([10, "ePaper"]); + /*ENDIF_ESP32*/ var dispType = sel("disp_typ", opts, obj["disp_typ"]); document.getElementById("dispType").append( ml("div", {class: "row mb-3"}, [ @@ -894,10 +901,10 @@ }); opts = [[0, "0°"], [2, "180°"]]; - if("ESP32" == type) { + /*IF_ESP32*/ opts.push([1, "90°"]); opts.push([3, "270°"]); - } + /*ENDIF_ESP32*/ document.getElementById("dispRot").append( ml("div", {class: "row mb-3"}, [ ml("div", {class: "col-12 col-sm-3 my-2"}, "Rotation"), @@ -991,9 +998,14 @@ parseSun(root["sun"]); parsePinout(root["pinout"], root["system"]["esp_type"], root["system"]); parseNrfRadio(root["radioNrf"], root["pinout"], root["system"]["esp_type"], root["system"]); + + /*IF_ESP32*/ + parseCmtRadio(root["radioCmt"], root["system"]["esp_type"], root["system"]); + /*ENDIF_ESP32*/ + if(root["generic"]["esp_type"] == "ESP32") - parseCmtRadio(root["radioCmt"], root["system"]["esp_type"], root["system"]); parsezeroExport(root["zeroExport"], root["system"]["esp_type"]); + parseSerial(root["serial"]); parseDisplay(root["display"], root["system"]["esp_type"], root["system"]); diff --git a/src/web/html/style.css b/src/web/html/style.css index f0992313..0f053d98 100644 --- a/src/web/html/style.css +++ b/src/web/html/style.css @@ -20,6 +20,10 @@ fieldset, input[type=submit], .btn { border-radius: 4px; } +input[type=file] { + width: 100%; +} + #live span { color: var(--fg2); } @@ -760,7 +764,7 @@ h5 { .badge { display: inline-block; padding: .25em .4em; - font-size: 75%; + font-size: 85%; font-weight: 700; line-height: 1; text-align: center; diff --git a/src/web/html/system.html b/src/web/html/system.html index c504057f..11be689a 100644 --- a/src/web/html/system.html +++ b/src/web/html/system.html @@ -8,10 +8,7 @@ {#HTML_NAV}
    -
    
                     
    -
    -
    @@ -49,8 +46,8 @@ } } - function badge(success, text) { - return ml("span", {class: "badge badge-" + ((success) ? "success" : "error")}, text); + function badge(success, text, second="error") { + return ml("span", {class: "badge badge-" + ((success) ? "success" : second)}, text); } function headline(text) { @@ -66,21 +63,35 @@ ]); } - function parseRadio(obj, stat) { + function parseRadio(obj) { const pa = ["MIN (recommended)", "LOW", "HIGH", "MAX"]; - const datarate = ["1 MBps", "2 MBps", "250 kbps"]; + const dr = ["1 M", "2 M", "250 k"] - 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]) - ]) - ]), + 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 + 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"), + ml("table", {class: "table"}, + ml("tbody", {}, lines) + ), headline("Statistics"), - ml("table", {class: "table"}, [ + ml("table", {class: "table"}, ml("tbody", {}, [ tr("TX count", stat.tx_cnt), tr("RX success", stat.rx_success), @@ -89,24 +100,42 @@ tr("RX fragments", stat.frame_cnt), tr("TX retransmits", stat.retransmits) ]) - ]) + ) + ); + } + + function parseMqtt(obj) { + if(obj.enabled) { + lines = [ + tr("connected", badge(obj.connected, ((obj.connected) ? "true" : "false"))), + tr("#TX", obj.tx_cnt), + tr("#RX", obj.rx_cnt) + ]; + + } else + lines = tr("enabled", badge(false, "false")); + + document.getElementById("info").append( + headline("MqTT"), + ml("table", {class: "table"}, + ml("tbody", {}, lines) + ) ); } function parseIndex(obj) { - if(obj["ts_sunrise"] > 0) { - var h = div(["head", "p-2"]); - var r = div(["row"]); - r.appendChild(div(["col", "a-c"], "Sun")); - h.appendChild(r); - - document.getElementById("sun").append ( - h, - genTabRow("Sunrise", new Date(obj["ts_sunrise"] * 1000).toLocaleString('de-DE')), - genTabRow("Sunset", new Date(obj["ts_sunset"] * 1000).toLocaleString('de-DE')), - genTabRow("Communication start", new Date((obj["ts_sunrise"] - obj["ts_offset"]) * 1000).toLocaleString('de-DE')), - genTabRow("Communication stop", new Date((obj["ts_sunset"] + obj["ts_offset"]) * 1000).toLocaleString('de-DE')), - genTabRow("Night Communication", ((obj["disNightComm"]) ? "disabled" : "enabled")) + if(obj.ts_sunrise > 0) { + document.getElementById("info").append( + headline("Sun"), + ml("table", {class: "table"}, + ml("tbody", {}, [ + tr("Sunrise", new Date(obj.ts_sunrise * 1000).toLocaleString('de-DE')), + tr("Sunset", new Date(obj.ts_sunset * 1000).toLocaleString('de-DE')), + tr("Communication start", new Date((obj.ts_sunrise - obj.ts_offset) * 1000).toLocaleString('de-DE')), + tr("Communication stop", new Date((obj.ts_sunset + obj.ts_offset) * 1000).toLocaleString('de-DE')), + tr("Night behaviour", badge(obj.disNightComm, ((obj.disNightComm) ? "not" : "") + " communicating", "warning")) + ]) + ) ); } } @@ -122,8 +151,9 @@ document.getElementsByTagName('head')[0].appendChild(meta); } else { + parseRadio(obj.system); + parseMqtt(obj.system.mqtt); parseSysInfo(obj["system"]); - parseRadio(obj["system"]["radio"], obj["system"]["statistics"]); getAjax('/api/index', parseIndex); } document.getElementById("html").innerHTML = obj["html"]; diff --git a/src/web/html/visualization.html b/src/web/html/visualization.html index 4136bd50..ef9e0378 100644 --- a/src/web/html/visualization.html +++ b/src/web/html/visualization.html @@ -106,7 +106,7 @@ 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"}, "Power limit " + ((obj.power_limit_read == 65535) ? "n/a" : (obj.power_limit_read + " %"))), + ml("div", {class: "col a-c"}, "Active Power Control: " + ((obj.power_limit_read == 65535) ? "n/a" : (obj.power_limit_read + " %"))), ml("div", {class: "col a-c"}, ml("span", { class: "pointer", onclick: function() { getAjax("/api/inverter/alarm/" + obj.id, parseIvAlarm); }}, ("Alarms: " + obj.alarm_cnt))), diff --git a/src/web/web.h b/src/web/web.h index 41eaab29..4f062713 100644 --- a/src/web/web.h +++ b/src/web/web.h @@ -38,7 +38,7 @@ #define WEB_SERIAL_BUF_SIZE 2048 -const char* const pinArgNames[] = {"pinCs", "pinCe", "pinIrq", "pinSclk", "pinMosi", "pinMiso", "pinLed0", "pinLed1", "pinLedHighActive", "pinCsb", "pinFcsb", "pinGpio3"}; +const char* const pinArgNames[] = {"pinCs", "pinCe", "pinIrq", "pinSclk", "pinMosi", "pinMiso", "pinLed0", "pinLed1", "pinLedHighActive", "pinCmtSclk", "pinSdio", "pinCsb", "pinFcsb", "pinGpio3"}; template class Web { @@ -555,9 +555,11 @@ class Web { case 6: mConfig->led.led0 = pin; break; case 7: mConfig->led.led1 = pin; break; case 8: mConfig->led.led_high_active = pin; break; // this is not really a pin but a polarity, but handling it close to here makes sense - case 9: mConfig->cmt.pinCsb = pin; break; - case 10: mConfig->cmt.pinFcsb = pin; break; - case 11: mConfig->cmt.pinIrq = pin; break; + case 9: mConfig->cmt.pinSclk = pin; break; + case 10: mConfig->cmt.pinSdio = pin; break; + case 11: mConfig->cmt.pinCsb = pin; break; + case 12: mConfig->cmt.pinFcsb = pin; break; + case 13: mConfig->cmt.pinIrq = pin; break; } } @@ -811,16 +813,31 @@ class Web { // This is the correct field to report std::tie(promUnit, promType) = convertToPromUnits(iv->getUnit(metricsChannelId, rec)); // Declare metric only once - if (!metricDeclared) { + if (channel != 0 && !metricDeclared) { snprintf(type, sizeof(type), "# TYPE ahoy_solar_%s%s %s\n", iv->getFieldName(metricsChannelId, rec), promUnit.c_str(), promType.c_str()); metrics += type; metricDeclared = true; } // report value if (0 == channel) { - snprintf(topic, sizeof(topic), "ahoy_solar_%s%s{inverter=\"%s\"}", iv->getFieldName(metricsChannelId, rec), promUnit.c_str(), iv->config->name); + char total[7]; + total[0] = 0; + if (metricDeclared) { + // A declaration and value for channels has been delivered. So declare and deliver a _total metric + strncpy(total,"_total",sizeof(total)); + } + snprintf(type, sizeof(type), "# TYPE ahoy_solar_%s%s%s %s\n", iv->getFieldName(metricsChannelId, rec), promUnit.c_str(), total, promType.c_str()); + metrics += type; + snprintf(topic, sizeof(topic), "ahoy_solar_%s%s%s{inverter=\"%s\"}", iv->getFieldName(metricsChannelId, rec), promUnit.c_str(), total,iv->config->name); } else { - snprintf(topic, sizeof(topic), "ahoy_solar_%s%s{inverter=\"%s\",channel=\"%s\"}", iv->getFieldName(metricsChannelId, rec), promUnit.c_str(), iv->config->name,iv->config->chName[channel-1]); + // Use a fallback channel name (ch0, ch1, ...)if non is given by user + char chName[MAX_NAME_LENGTH]; + if (iv->config->chName[channel-1][0] != 0) { + strncpy(chName, iv->config->chName[channel-1], sizeof(chName)); + } else { + snprintf(chName,sizeof(chName),"ch%1d",channel); + } + snprintf(topic, sizeof(topic), "ahoy_solar_%s%s{inverter=\"%s\",channel=\"%s\"}", iv->getFieldName(metricsChannelId, rec), promUnit.c_str(), iv->config->name,chName); } snprintf(val, sizeof(val), " %.3f\n", iv->getValue(metricsChannelId, rec)); metrics += topic; diff --git a/tools/fonts/fontconv.bat b/tools/fonts/fontconv.bat new file mode 100644 index 00000000..e1097adc --- /dev/null +++ b/tools/fonts/fontconv.bat @@ -0,0 +1,6 @@ +.\bdfconv\bdfconv_2_22.exe -v -f 1 -m "32-137" u8g2_font_5x8_symbols_ahoy.bdf -o u8g2_font_5x8_symbols_ahoy.c_ -n u8g2_font_5x8_symbols_ahoy + +.\bdfconv\bdfconv_2_22.exe -v -f 1 -m "65-75" u8g2_font_ncenB10_symbols10_ahoy.bdf -o u8g2_font_ncenB10_symbols10_ahoy.c_ -n u8g2_font_ncenB10_symbols10_ahoy +.\bdfconv\bdfconv_2_22.exe -v -f 1 -m "65-75" u8g2_font_ncenB08_symbols8_ahoy.bdf -o u8g2_font_ncenB08_symbols8_ahoy.c_ -n u8g2_font_ncenB08_symbols8_ahoy + +pause \ No newline at end of file diff --git a/tools/fonts/u8g2 font-sources.txt b/tools/fonts/u8g2 font-sources.txt new file mode 100644 index 00000000..2fb70bd3 --- /dev/null +++ b/tools/fonts/u8g2 font-sources.txt @@ -0,0 +1,11 @@ +Useful sources to edit u8g2 fonts: + +bdf font files for u8g2 font library: +https://github.com/olikraus/u8g2/tree/master/tools/font/bdf + +Tool to edit bdf files: +https://github.com/olikraus/u8g2/tree/master/tools/font/fony + +Tool to convert bdf font files to u8g2 source code: +https://github.com/olikraus/u8g2/tree/master/tools/font/bdfconv + diff --git a/tools/fonts/u8g2_font_5x8_symbols_ahoy.bdf b/tools/fonts/u8g2_font_5x8_symbols_ahoy.bdf new file mode 100644 index 00000000..82338aa1 --- /dev/null +++ b/tools/fonts/u8g2_font_5x8_symbols_ahoy.bdf @@ -0,0 +1,1328 @@ +STARTFONT 2.1 +COMMENT Exported by Fony v1.4.7 +FONT u8g2_font_5x8_symbols_ahoy +SIZE 8 75 75 +FONTBOUNDINGBOX 8 7 0 -1 +STARTPROPERTIES 6 +COPYRIGHT "Public domain font. Share and enjoy." +RESOLUTION_X 75 +RESOLUTION_Y 75 +FONT_ASCENT 7 +FONT_DESCENT 1 +DEFAULT_CHAR 0 +ENDPROPERTIES +CHARS 106 +STARTCHAR 032 +ENCODING 32 +SWIDTH 216 0 +DWIDTH 3 0 +BBX 0 0 0 0 +BITMAP +ENDCHAR +STARTCHAR 033 +ENCODING 33 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 1 6 2 0 +BITMAP +80 +80 +80 +80 +00 +80 +ENDCHAR +STARTCHAR 034 +ENCODING 34 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 3 3 1 3 +BITMAP +A0 +A0 +A0 +ENDCHAR +STARTCHAR 035 +ENCODING 35 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 5 7 0 0 +BITMAP +50 +50 +F8 +50 +F8 +50 +50 +ENDCHAR +STARTCHAR 036 +ENCODING 36 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 5 7 0 0 +BITMAP +20 +70 +A0 +70 +28 +70 +20 +ENDCHAR +STARTCHAR 037 +ENCODING 37 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 3 5 1 1 +BITMAP +80 +A0 +40 +A0 +20 +ENDCHAR +STARTCHAR 038 +ENCODING 38 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 7 0 0 +BITMAP +40 +A0 +A0 +40 +A0 +A0 +50 +ENDCHAR +STARTCHAR 039 +ENCODING 39 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 1 3 2 3 +BITMAP +80 +80 +80 +ENDCHAR +STARTCHAR 040 +ENCODING 40 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 2 6 1 0 +BITMAP +40 +80 +80 +80 +80 +40 +ENDCHAR +STARTCHAR 041 +ENCODING 41 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 2 6 1 0 +BITMAP +80 +40 +40 +40 +40 +80 +ENDCHAR +STARTCHAR 042 +ENCODING 42 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 5 0 0 +BITMAP +90 +60 +F0 +60 +90 +ENDCHAR +STARTCHAR 043 +ENCODING 43 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 5 5 0 0 +BITMAP +20 +20 +F8 +20 +20 +ENDCHAR +STARTCHAR 044 +ENCODING 44 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 3 3 1 -1 +BITMAP +60 +40 +80 +ENDCHAR +STARTCHAR 045 +ENCODING 45 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 1 0 2 +BITMAP +F0 +ENDCHAR +STARTCHAR 046 +ENCODING 46 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 2 2 1 0 +BITMAP +C0 +C0 +ENDCHAR +STARTCHAR 047 +ENCODING 47 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +10 +10 +20 +40 +80 +80 +ENDCHAR +STARTCHAR 048 +ENCODING 48 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +60 +90 +B0 +D0 +90 +60 +ENDCHAR +STARTCHAR 049 +ENCODING 49 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 3 6 1 0 +BITMAP +40 +C0 +40 +40 +40 +E0 +ENDCHAR +STARTCHAR 050 +ENCODING 50 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +60 +90 +10 +60 +80 +F0 +ENDCHAR +STARTCHAR 051 +ENCODING 51 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +F0 +20 +60 +10 +90 +60 +ENDCHAR +STARTCHAR 052 +ENCODING 52 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +20 +60 +A0 +F0 +20 +20 +ENDCHAR +STARTCHAR 053 +ENCODING 53 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +F0 +80 +E0 +10 +90 +60 +ENDCHAR +STARTCHAR 054 +ENCODING 54 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +60 +80 +E0 +90 +90 +60 +ENDCHAR +STARTCHAR 055 +ENCODING 55 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +F0 +10 +20 +20 +40 +40 +ENDCHAR +STARTCHAR 056 +ENCODING 56 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +60 +90 +60 +90 +90 +60 +ENDCHAR +STARTCHAR 057 +ENCODING 57 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +60 +90 +90 +70 +10 +60 +ENDCHAR +STARTCHAR 058 +ENCODING 58 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 2 5 1 0 +BITMAP +C0 +C0 +00 +C0 +C0 +ENDCHAR +STARTCHAR 059 +ENCODING 59 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 3 6 1 -1 +BITMAP +60 +60 +00 +60 +40 +80 +ENDCHAR +STARTCHAR 060 +ENCODING 60 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 3 6 1 0 +BITMAP +20 +40 +80 +80 +40 +20 +ENDCHAR +STARTCHAR 061 +ENCODING 61 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 3 0 1 +BITMAP +F0 +00 +F0 +ENDCHAR +STARTCHAR 062 +ENCODING 62 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 3 6 1 0 +BITMAP +80 +40 +20 +20 +40 +80 +ENDCHAR +STARTCHAR 063 +ENCODING 63 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 3 6 1 0 +BITMAP +40 +A0 +20 +40 +00 +40 +ENDCHAR +STARTCHAR 064 +ENCODING 64 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 5 8 0 -1 +BITMAP +30 +48 +98 +A8 +A8 +90 +40 +30 +ENDCHAR +STARTCHAR 065 +ENCODING 65 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +60 +90 +90 +F0 +90 +90 +ENDCHAR +STARTCHAR 066 +ENCODING 66 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +E0 +90 +E0 +90 +90 +E0 +ENDCHAR +STARTCHAR 067 +ENCODING 67 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +60 +90 +80 +80 +90 +60 +ENDCHAR +STARTCHAR 068 +ENCODING 68 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +E0 +90 +90 +90 +90 +E0 +ENDCHAR +STARTCHAR 069 +ENCODING 69 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +F0 +80 +E0 +80 +80 +F0 +ENDCHAR +STARTCHAR 070 +ENCODING 70 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +F0 +80 +E0 +80 +80 +80 +ENDCHAR +STARTCHAR 071 +ENCODING 71 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +60 +90 +80 +B0 +90 +60 +ENDCHAR +STARTCHAR 072 +ENCODING 72 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +90 +90 +F0 +90 +90 +90 +ENDCHAR +STARTCHAR 073 +ENCODING 73 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 3 6 1 0 +BITMAP +E0 +40 +40 +40 +40 +E0 +ENDCHAR +STARTCHAR 074 +ENCODING 74 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +70 +20 +20 +20 +A0 +40 +ENDCHAR +STARTCHAR 075 +ENCODING 75 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +90 +A0 +C0 +A0 +A0 +90 +ENDCHAR +STARTCHAR 076 +ENCODING 76 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +80 +80 +80 +80 +80 +F0 +ENDCHAR +STARTCHAR 077 +ENCODING 77 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +90 +F0 +F0 +90 +90 +90 +ENDCHAR +STARTCHAR 078 +ENCODING 78 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +90 +D0 +F0 +B0 +90 +90 +ENDCHAR +STARTCHAR 079 +ENCODING 79 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +60 +90 +90 +90 +90 +60 +ENDCHAR +STARTCHAR 080 +ENCODING 80 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +E0 +90 +90 +E0 +80 +80 +ENDCHAR +STARTCHAR 081 +ENCODING 81 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 7 0 -1 +BITMAP +60 +90 +90 +D0 +B0 +60 +10 +ENDCHAR +STARTCHAR 082 +ENCODING 82 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +E0 +90 +90 +E0 +90 +90 +ENDCHAR +STARTCHAR 083 +ENCODING 83 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +60 +90 +40 +20 +90 +60 +ENDCHAR +STARTCHAR 084 +ENCODING 84 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 5 6 0 0 +BITMAP +F8 +20 +20 +20 +20 +20 +ENDCHAR +STARTCHAR 085 +ENCODING 85 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +90 +90 +90 +90 +90 +60 +ENDCHAR +STARTCHAR 086 +ENCODING 86 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +90 +90 +90 +90 +60 +60 +ENDCHAR +STARTCHAR 087 +ENCODING 87 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +90 +90 +90 +F0 +F0 +90 +ENDCHAR +STARTCHAR 088 +ENCODING 88 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +90 +90 +60 +60 +90 +90 +ENDCHAR +STARTCHAR 089 +ENCODING 89 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 5 6 0 0 +BITMAP +88 +88 +50 +20 +20 +20 +ENDCHAR +STARTCHAR 090 +ENCODING 90 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +F0 +10 +20 +40 +80 +F0 +ENDCHAR +STARTCHAR 091 +ENCODING 91 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 3 6 1 0 +BITMAP +E0 +80 +80 +80 +80 +E0 +ENDCHAR +STARTCHAR 092 +ENCODING 92 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +80 +80 +40 +20 +10 +10 +ENDCHAR +STARTCHAR 093 +ENCODING 93 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 3 6 1 0 +BITMAP +E0 +20 +20 +20 +20 +E0 +ENDCHAR +STARTCHAR 094 +ENCODING 94 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 3 2 1 4 +BITMAP +40 +A0 +ENDCHAR +STARTCHAR 095 +ENCODING 95 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 1 0 -1 +BITMAP +F0 +ENDCHAR +STARTCHAR 096 +ENCODING 96 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 2 2 1 4 +BITMAP +80 +40 +ENDCHAR +STARTCHAR 097 +ENCODING 97 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 4 0 0 +BITMAP +70 +90 +90 +70 +ENDCHAR +STARTCHAR 098 +ENCODING 98 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +80 +80 +E0 +90 +90 +E0 +ENDCHAR +STARTCHAR 099 +ENCODING 99 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 3 4 1 0 +BITMAP +60 +80 +80 +60 +ENDCHAR +STARTCHAR 100 +ENCODING 100 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +10 +10 +70 +90 +90 +70 +ENDCHAR +STARTCHAR 101 +ENCODING 101 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 4 0 0 +BITMAP +60 +B0 +C0 +70 +ENDCHAR +STARTCHAR 102 +ENCODING 102 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +20 +50 +40 +E0 +40 +40 +ENDCHAR +STARTCHAR 103 +ENCODING 103 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 5 0 -1 +BITMAP +60 +90 +70 +10 +60 +ENDCHAR +STARTCHAR 104 +ENCODING 104 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +80 +80 +E0 +90 +90 +90 +ENDCHAR +STARTCHAR 105 +ENCODING 105 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 3 6 1 0 +BITMAP +40 +00 +C0 +40 +40 +E0 +ENDCHAR +STARTCHAR 106 +ENCODING 106 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 3 7 1 -1 +BITMAP +20 +00 +20 +20 +20 +A0 +40 +ENDCHAR +STARTCHAR 107 +ENCODING 107 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +80 +80 +90 +E0 +90 +90 +ENDCHAR +STARTCHAR 108 +ENCODING 108 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 3 6 1 0 +BITMAP +C0 +40 +40 +40 +40 +E0 +ENDCHAR +STARTCHAR 109 +ENCODING 109 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 5 4 0 0 +BITMAP +D0 +A8 +A8 +A8 +ENDCHAR +STARTCHAR 110 +ENCODING 110 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 4 0 0 +BITMAP +E0 +90 +90 +90 +ENDCHAR +STARTCHAR 111 +ENCODING 111 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 4 0 0 +BITMAP +60 +90 +90 +60 +ENDCHAR +STARTCHAR 112 +ENCODING 112 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 5 0 -1 +BITMAP +E0 +90 +E0 +80 +80 +ENDCHAR +STARTCHAR 113 +ENCODING 113 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 5 0 -1 +BITMAP +70 +90 +70 +10 +10 +ENDCHAR +STARTCHAR 114 +ENCODING 114 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 4 0 0 +BITMAP +A0 +D0 +80 +80 +ENDCHAR +STARTCHAR 115 +ENCODING 115 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 3 4 1 0 +BITMAP +60 +C0 +20 +C0 +ENDCHAR +STARTCHAR 116 +ENCODING 116 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 6 0 0 +BITMAP +40 +40 +E0 +40 +50 +20 +ENDCHAR +STARTCHAR 117 +ENCODING 117 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 4 0 0 +BITMAP +90 +90 +90 +70 +ENDCHAR +STARTCHAR 118 +ENCODING 118 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 3 4 1 0 +BITMAP +A0 +A0 +A0 +40 +ENDCHAR +STARTCHAR 119 +ENCODING 119 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 5 4 0 0 +BITMAP +88 +A8 +A8 +50 +ENDCHAR +STARTCHAR 120 +ENCODING 120 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 4 0 0 +BITMAP +90 +60 +60 +90 +ENDCHAR +STARTCHAR 121 +ENCODING 121 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 5 0 -1 +BITMAP +90 +90 +70 +90 +60 +ENDCHAR +STARTCHAR 122 +ENCODING 122 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 4 0 0 +BITMAP +F0 +20 +40 +F0 +ENDCHAR +STARTCHAR 123 +ENCODING 123 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 7 0 0 +BITMAP +30 +40 +20 +C0 +20 +40 +30 +ENDCHAR +STARTCHAR 124 +ENCODING 124 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 1 6 2 0 +BITMAP +80 +80 +80 +80 +80 +80 +ENDCHAR +STARTCHAR 125 +ENCODING 125 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 7 0 0 +BITMAP +C0 +20 +40 +30 +40 +20 +C0 +ENDCHAR +STARTCHAR 126 +ENCODING 126 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 4 2 0 4 +BITMAP +50 +A0 +ENDCHAR +STARTCHAR 127 +ENCODING 127 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 0 0 0 0 +BITMAP +ENDCHAR +STARTCHAR 128 +ENCODING 128 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 5 7 0 -1 +BITMAP +F8 +A8 +A8 +70 +20 +20 +20 +ENDCHAR +STARTCHAR 129 +ENCODING 129 +SWIDTH 432 0 +DWIDTH 6 0 +BBX 6 6 0 0 +BITMAP +78 +84 +30 +48 +00 +30 +ENDCHAR +STARTCHAR 130 +ENCODING 130 +SWIDTH 576 0 +DWIDTH 8 0 +BBX 8 6 0 0 +BITMAP +18 +24 +24 +42 +42 +81 +ENDCHAR +STARTCHAR 131 +ENCODING 131 +SWIDTH 576 0 +DWIDTH 8 0 +BBX 7 7 0 -1 +BITMAP +FE +42 +20 +10 +20 +42 +FE +ENDCHAR +STARTCHAR 132 +ENCODING 132 +SWIDTH 360 0 +DWIDTH 5 0 +BBX 5 8 0 -1 +BITMAP +88 +50 +50 +20 +20 +50 +50 +88 +ENDCHAR +STARTCHAR 133 +ENCODING 133 +SWIDTH 432 0 +DWIDTH 6 0 +BBX 6 8 0 -1 +BITMAP +84 +78 +CC +30 +78 +48 +78 +84 +ENDCHAR +STARTCHAR 134 +ENCODING 134 +SWIDTH 576 0 +DWIDTH 8 0 +BBX 8 8 0 -1 +BITMAP +24 +3C +E7 +42 +42 +E7 +3C +24 +ENDCHAR +STARTCHAR 135 +ENCODING 135 +SWIDTH 576 0 +DWIDTH 8 0 +BBX 7 7 0 0 +BITMAP +04 +06 +06 +0E +1E +FC +78 +ENDCHAR +STARTCHAR 136 +ENCODING 136 +SWIDTH 576 0 +DWIDTH 8 0 +BBX 7 7 0 -1 +BITMAP +FE +AA +82 +B2 +92 +82 +FE +ENDCHAR +STARTCHAR 137 +ENCODING 137 +SWIDTH 504 0 +DWIDTH 7 0 +BBX 6 6 0 0 +BITMAP +E4 +10 +C8 +24 +94 +D4 +ENDCHAR +ENDFONT diff --git a/tools/fonts/u8g2_font_5x8_symbols_ahoy.c_ b/tools/fonts/u8g2_font_5x8_symbols_ahoy.c_ new file mode 100644 index 00000000..0f378f39 --- /dev/null +++ b/tools/fonts/u8g2_font_5x8_symbols_ahoy.c_ @@ -0,0 +1,40 @@ +/* + Fontname: u8g2_font_5x8_symbols_ahoy + Copyright: Public domain font. Share and enjoy. + Glyphs: 106/106 + BBX Build Mode: 0 +*/ +const uint8_t u8g2_font_5x8_symbols_ahoy[1052] U8G2_FONT_SECTION("u8g2_font_5x8_symbols_ahoy") = + "j\0\3\2\4\4\3\4\5\10\10\0\377\6\377\6\0\1\61\2b\4\3 \5\0\304\11!\7a\306" + "\212!\11\42\7\63\335\212\304\22#\16u\304\232R\222\14JePJI\2$\14u\304\252l\251m" + "I\262E\0%\10S\315\212(\351\24&\13t\304\232(i\252\64%\1'\6\61\336\212\1(\7b" + "\305\32\245))\11b\305\212(\251(\0*\13T\304\212(Q\206D\211\2+\12U\304\252\60\32\244" + "\60\2,\7\63\275\32\245\4-\6\24\324\212!.\6\42\305\212!/\10d\304\272R[\6\60\14d" + "\304\32%R\206DJ\24\0\61\10c\305\232Dj\31\62\13d\304\32%\312\22%\33\2\63\13d\304" + "\212!\212D)Q\0\64\13d\304\252H\251\14Q\226\0\65\12d\304\212A\33\245D\1\66\13d\304" + "\32%[\42)Q\0\67\13d\304\212!\213\262(\213\0\70\14d\304\32%J\224HJ\24\0\71\13" + "d\304\32%\222\222-Q\0:\10R\305\212!\32\2;\10c\275\32\243R\2<\10c\305\252\244\224" + "\25=\10\64\314\212!\34\2>\11c\305\212\254\224\224\0?\11c\305\232\246$M\0@\15\205\274*" + ")\222\226DI\244\252\2A\12d\304\32%\222\206I\12B\14d\304\212%\32\222H\32\22\0C\12" + "d\304\32%\322J\211\2D\12d\304\212%r\32\22\0E\12d\304\212A[\262l\10F\12d\304" + "\212A[\262\32\0G\13d\304\32%\322\222)Q\0H\12d\304\212H\32&S\0I\10c\305\212" + "%j\31J\12d\304\232)\253\224\42\0K\13d\304\212HI\244\244S\0L\10d\304\212\254\333\20" + "M\12d\304\212h\70D\246\0N\12d\304\212h\31\226I\12O\12d\304\32%rJ\24\0P\13" + "d\304\212%\222\206$\313\0Q\12t\274\32%\222\26\307\0R\13d\304\212%\222\206$\222\2S\14" + "d\304\32%J\302$J\24\0T\11e\304\212A\12;\1U\11d\304\212\310S\242\0V\12d\304" + "\212\310)\221\24\0W\12d\304\212\310\64\34\242\0X\13d\304\212HJ$%\222\2Y\12e\304\212" + "LKja\11Z\12d\304\212!\213\332\206\0[\10c\305\212!j\32\134\10d\304\212,l\13]" + "\10c\305\212\251i\10^\6#\345\232\6_\6\24\274\212!`\6\42\345\212(a\11D\304\232!\222" + "\222\1b\13d\304\212,[\42iH\0c\7C\305\232)\23d\12d\304\272\312\20I\311\0e\11" + "D\304\32%\31\262\1f\12d\304\252Ji\312\42\0g\12T\274\32%J\266D\1h\12d\304\212" + ",[\42S\0i\10c\305\232P\252\14j\12s\275\252\64\212\224\12\0k\12d\304\212\254\64$\221" + "\24l\10c\305\12\251\313\0m\12E\304\12\245EI\224\2n\10D\304\212%\62\5o\11D\304\32" + "%\222\22\5p\12T\274\212%\32\222,\3q\11T\274\232!J\266\2r\11D\304\212$\261e\0" + "s\10C\305\232![\0t\13d\304\232,\232\262$J\0u\10D\304\212\310\224\14v\10C\305\212" + "\304R\1w\12E\304\212LI\224.\0x\11D\304\212(\221\224(y\13T\274\212HJ\206(Q" + "\0z\11D\304\212!*\15\1{\12t\304*%L\304(\24|\6a\306\212\3}\13t\304\12\61" + "\12\225\60\221\0~\10$\344\232DI\0\5\0\304\12\200\13u\274\212K\242T\266\260\4\201\14f" + "D\233!\11#-\312!\11\202\15hD<\65\12\243,\214\302$\16\203\15w<\214C\22F\71\220" + "\26\207A\204\16\205\274\212,)%Y\230%QR\13\205\17\206<\213\60\31\22\311\66D\245!\11\3" + "\206\20\210<\254\342\20]\302(L\246C\30E\0\207\15wD\334X\25\267\341\20\15\21\0\210\16w" + "<\214\203RQ\25I\212\324a\20\211\15f\304\213)\213\244,\222\222\245\0\0\0\0"; diff --git a/tools/fonts/u8g2_font_5x8_symbols_ahoy.fon b/tools/fonts/u8g2_font_5x8_symbols_ahoy.fon new file mode 100644 index 00000000..891ea689 Binary files /dev/null and b/tools/fonts/u8g2_font_5x8_symbols_ahoy.fon differ diff --git a/tools/fonts/u8g2_font_ncenB08_symbols8_ahoy.bdf b/tools/fonts/u8g2_font_ncenB08_symbols8_ahoy.bdf new file mode 100644 index 00000000..f5ed551e --- /dev/null +++ b/tools/fonts/u8g2_font_ncenB08_symbols8_ahoy.bdf @@ -0,0 +1,166 @@ +STARTFONT 2.1 +COMMENT Exported by Fony v1.4.7 +FONT u8g2_font_ncenB08_symbols8_ahoy +SIZE 12 100 100 +FONTBOUNDINGBOX 9 11 0 -2 +STARTPROPERTIES 6 +COPYRIGHT "" +RESOLUTION_X 100 +RESOLUTION_Y 100 +FONT_ASCENT 10 +FONT_DESCENT 2 +DEFAULT_CHAR 0 +ENDPROPERTIES +CHARS 11 +STARTCHAR 065 +ENCODING 65 +SWIDTH 576 0 +DWIDTH 8 0 +BBX 7 8 0 0 +BITMAP +FE +92 +D6 +38 +10 +10 +10 +10 +ENDCHAR +STARTCHAR 066 +ENCODING 66 +SWIDTH 648 0 +DWIDTH 9 0 +BBX 8 9 1 0 +BITMAP +3C +42 +99 +24 +42 +18 +24 +00 +18 +ENDCHAR +STARTCHAR 067 +ENCODING 67 +SWIDTH 576 0 +DWIDTH 8 0 +BBX 8 8 0 0 +BITMAP +18 +24 +24 +42 +42 +42 +42 +81 +ENDCHAR +STARTCHAR 068 +ENCODING 68 +SWIDTH 648 0 +DWIDTH 9 0 +BBX 8 8 0 0 +BITMAP +FF +41 +20 +10 +10 +20 +41 +FF +ENDCHAR +STARTCHAR 069 +ENCODING 69 +SWIDTH 576 0 +DWIDTH 8 0 +BBX 7 8 0 0 +BITMAP +FE +D6 +FE +38 +10 +38 +54 +92 +ENDCHAR +STARTCHAR 070 +ENCODING 70 +SWIDTH 648 0 +DWIDTH 9 0 +BBX 8 9 1 0 +BITMAP +BD +42 +BD +3C +5A +18 +24 +42 +99 +ENDCHAR +STARTCHAR 071 +ENCODING 71 +SWIDTH 648 0 +DWIDTH 9 0 +BBX 8 8 0 0 +BITMAP +24 +3C +E7 +42 +42 +E7 +3C +24 +ENDCHAR +STARTCHAR 072 +ENCODING 72 +SWIDTH 576 0 +DWIDTH 8 0 +BBX 7 7 0 1 +BITMAP +04 +06 +06 +0E +1E +FC +78 +ENDCHAR +STARTCHAR 073 +ENCODING 73 +SWIDTH 576 0 +DWIDTH 8 0 +BBX 7 9 0 0 +BITMAP +44 +FE +82 +92 +B2 +92 +92 +82 +FE +ENDCHAR +STARTCHAR 074 +ENCODING 74 +SWIDTH 504 0 +DWIDTH 7 0 +BBX 0 0 0 0 +BITMAP +ENDCHAR +STARTCHAR 075 +ENCODING 75 +SWIDTH 648 0 +DWIDTH 9 0 +BBX 0 0 0 0 +BITMAP +ENDCHAR +ENDFONT diff --git a/tools/fonts/u8g2_font_ncenB08_symbols8_ahoy.c_ b/tools/fonts/u8g2_font_ncenB08_symbols8_ahoy.c_ new file mode 100644 index 00000000..bc237bc2 --- /dev/null +++ b/tools/fonts/u8g2_font_ncenB08_symbols8_ahoy.c_ @@ -0,0 +1,13 @@ +/* + Fontname: u8g2_font_ncenB08_symbols8_ahoy + Copyright: + Glyphs: 11/11 + BBX Build Mode: 0 +*/ +const uint8_t u8g2_font_ncenB08_symbols8_ahoy[173] U8G2_FONT_SECTION("u8g2_font_ncenB08_symbols8_ahoy") = + "\13\0\3\2\4\4\2\2\5\11\11\0\0\10\0\10\0\0\0\0\0\0\224A\14\207\212q\220\242%\221" + "\326\270\15B\20\230\233\65da\22Ima\250F\71\254\1C\20\210\212\247Fa\224\205Q\30\205Q" + "\230\304\1D\16\210\232qP\322(Gr \256\16\7E\15\207\212\361\222\14\247\65\335\222\246\2F\25" + "\230\233\221\14I\61I\206$\32\262D\11\325(\13\223H\12G\17\210\232U\34\242K\30\205\311t\10" + "\243\10H\14w\216\33\253\342\66\34\242!\2I\21\227\212\223%\303\240J\221\42I\221\24\251\303 J" + "\5\0z\1K\5\0\232\1\0\0\0"; diff --git a/tools/fonts/u8g2_font_ncenB08_symbols8_ahoy.fon b/tools/fonts/u8g2_font_ncenB08_symbols8_ahoy.fon new file mode 100644 index 00000000..e28b929f Binary files /dev/null and b/tools/fonts/u8g2_font_ncenB08_symbols8_ahoy.fon differ diff --git a/tools/fonts/u8g2_font_ncenB10_symbols10_ahoy.bdf b/tools/fonts/u8g2_font_ncenB10_symbols10_ahoy.bdf new file mode 100644 index 00000000..8fb61063 --- /dev/null +++ b/tools/fonts/u8g2_font_ncenB10_symbols10_ahoy.bdf @@ -0,0 +1,188 @@ +STARTFONT 2.1 +COMMENT Exported by Fony v1.4.7 +FONT u8g2_font_symbols10_ahoy +SIZE 16 100 100 +FONTBOUNDINGBOX 12 15 0 -3 +STARTPROPERTIES 6 +COPYRIGHT "" +RESOLUTION_X 100 +RESOLUTION_Y 100 +FONT_ASCENT 13 +FONT_DESCENT 3 +DEFAULT_CHAR 0 +ENDPROPERTIES +CHARS 11 +STARTCHAR 065 +ENCODING 65 +SWIDTH 576 0 +DWIDTH 8 0 +BBX 7 11 0 0 +BITMAP +FE +92 +92 +54 +38 +10 +10 +10 +10 +10 +10 +ENDCHAR +STARTCHAR 066 +ENCODING 66 +SWIDTH 648 0 +DWIDTH 9 0 +BBX 8 9 0 1 +BITMAP +3C +42 +99 +24 +42 +18 +24 +00 +18 +ENDCHAR +STARTCHAR 067 +ENCODING 67 +SWIDTH 792 0 +DWIDTH 11 0 +BBX 10 11 0 0 +BITMAP +0C00 +1200 +2100 +2100 +2100 +2180 +4080 +4080 +4080 +4080 +8040 +ENDCHAR +STARTCHAR 068 +ENCODING 68 +SWIDTH 720 0 +DWIDTH 10 0 +BBX 9 11 0 0 +BITMAP +FF80 +6080 +3000 +1800 +0C00 +0C00 +1800 +3000 +6000 +C080 +FF80 +ENDCHAR +STARTCHAR 069 +ENCODING 69 +SWIDTH 576 0 +DWIDTH 8 0 +BBX 7 11 0 0 +BITMAP +FE +D6 +D6 +7C +38 +10 +38 +38 +54 +54 +92 +ENDCHAR +STARTCHAR 070 +ENCODING 70 +SWIDTH 648 0 +DWIDTH 9 0 +BBX 8 10 1 0 +BITMAP +BD +42 +BD +24 +5A +18 +24 +24 +5A +81 +ENDCHAR +STARTCHAR 071 +ENCODING 71 +SWIDTH 864 0 +DWIDTH 12 0 +BBX 11 11 0 0 +BITMAP +1100 +1100 +0E00 +D160 +2080 +2080 +2080 +D160 +0E00 +1100 +1100 +ENDCHAR +STARTCHAR 072 +ENCODING 72 +SWIDTH 792 0 +DWIDTH 11 0 +BBX 10 11 0 0 +BITMAP +0080 +0080 +00C0 +00C0 +01C0 +01C0 +03C0 +0780 +1F80 +FF00 +3C00 +ENDCHAR +STARTCHAR 073 +ENCODING 73 +SWIDTH 720 0 +DWIDTH 10 0 +BBX 9 11 0 0 +BITMAP +DD80 +FF80 +8080 +8880 +9880 +8880 +8880 +8880 +8880 +8080 +FF80 +ENDCHAR +STARTCHAR 074 +ENCODING 74 +SWIDTH 648 0 +DWIDTH 9 0 +BBX 0 0 0 0 +BITMAP +ENDCHAR +STARTCHAR 075 +ENCODING 75 +SWIDTH 648 0 +DWIDTH 9 0 +BBX 0 0 0 0 +BITMAP +ENDCHAR +ENDFONT diff --git a/tools/fonts/u8g2_font_ncenB10_symbols10_ahoy.c_ b/tools/fonts/u8g2_font_ncenB10_symbols10_ahoy.c_ new file mode 100644 index 00000000..45d3979e --- /dev/null +++ b/tools/fonts/u8g2_font_ncenB10_symbols10_ahoy.c_ @@ -0,0 +1,14 @@ +/* + Fontname: u8g2_font_symbols10_ahoy + Copyright: + Glyphs: 11/11 + BBX Build Mode: 0 +*/ +const uint8_t u8g2_font_ncenB10_symbols10_ahoy[207] U8G2_FONT_SECTION("u8g2_font_ncenB10_symbols10_ahoy") = + "\13\0\3\2\4\4\2\2\5\13\13\0\0\13\0\13\0\0\0\0\0\0\266A\15\267\212q\220\42\251\322" + "\266\306\275\1B\20\230\236\65da\22Ima\250F\71\254\1C\23\272\272\251\3Q\32\366Q\212\243" + "\70\212\243\70\311\221\0D\20\271\252\361\242F:\242#: {\36\16\1E\22\267\212\361\222\14I\242" + "\14\332\232\216[RJ\232\12F\25\250\233\221\14I\61I\206$\252%J\250Fa\224%J\71G\30" + "\273\312W\316r`T\262DJ\303\64L#%K\304\35\310\342,\3H\27\272\272\217\344P\16\351\210" + "\16\354\300<\244C\70,\303 \16!\0I\24\271\252\241\34\336\1-\223\64-\323\62-\323\62\35x" + "\10J\5\0\232\1K\5\0\232\1\0\0\0"; diff --git a/tools/fonts/u8g2_font_ncenB10_symbols10_ahoy.fon b/tools/fonts/u8g2_font_ncenB10_symbols10_ahoy.fon new file mode 100644 index 00000000..8163a65b Binary files /dev/null and b/tools/fonts/u8g2_font_ncenB10_symbols10_ahoy.fon differ diff --git a/tools/fonts/used_fonts.txt b/tools/fonts/used_fonts.txt new file mode 100644 index 00000000..05de5bfd --- /dev/null +++ b/tools/fonts/used_fonts.txt @@ -0,0 +1,16 @@ +Display_Mono_64x48: + u8g2_font_fur11_tr + u8g2_font_6x10_tf + u8g2_font_4x6_tr + +Display_Mono_128x32: + u8g2_font_9x15_tr + u8g2_font_tom_thumb_4x6_tr + +Display_Mono_84x48: + u8g2_font_5x8_symbols_ahoy + u8g2_font_logisoso16_tr + +Display_Mono_128x64: + u8g2_font_ncenB08_symbols8_ahoy + u8g2_font_ncenB10_symbols10_ahoy \ No newline at end of file