From 8b379f768e281030545e9796da0f80d1dde7af42 Mon Sep 17 00:00:00 2001 From: lumapu Date: Sat, 23 Dec 2023 16:47:07 +0100 Subject: [PATCH 1/5] 0.8.29 * fix MqTT generic topic `comm_disabled` #1265 #1286 * potential fix of #1285 (reset yield day) * fix fraction of yield correction #1280 --- src/CHANGES.md | 5 +++++ src/app.cpp | 2 +- src/defines.h | 2 +- src/hm/hmInverter.h | 5 +++++ src/publisher/pubMqtt.h | 14 +++++++++----- src/publisher/pubMqttIvData.h | 2 +- src/web/RestApi.h | 2 +- src/web/html/setup.html | 2 +- 8 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/CHANGES.md b/src/CHANGES.md index 95fcae66..e9563510 100644 --- a/src/CHANGES.md +++ b/src/CHANGES.md @@ -1,5 +1,10 @@ # Development Changes +## 0.8.29 - 2023-12-23 +* fix MqTT generic topic `comm_disabled` #1265 #1286 +* potential fix of #1285 (reset yield day) +* fix fraction of yield correction #1280 + ## 0.8.28 - 2023-12-23 * fix bug heuristic * add version information to clipboard once 'copy' was clicked diff --git a/src/app.cpp b/src/app.cpp index 5a183cf4..88065e20 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -305,7 +305,7 @@ void app::tickMidnight(void) { continue; // skip to next inverter // reset alarms - if(InverterStatus::OFF == iv->status) + if(InverterStatus::OFF == iv->getStatus()) iv->resetAlarms(); // clear max values diff --git a/src/defines.h b/src/defines.h index b7ab4e55..2b304f9f 100644 --- a/src/defines.h +++ b/src/defines.h @@ -13,7 +13,7 @@ //------------------------------------- #define VERSION_MAJOR 0 #define VERSION_MINOR 8 -#define VERSION_PATCH 28 +#define VERSION_PATCH 29 //------------------------------------- typedef struct { diff --git a/src/hm/hmInverter.h b/src/hm/hmInverter.h index 2ae60e10..776b9c6f 100644 --- a/src/hm/hmInverter.h +++ b/src/hm/hmInverter.h @@ -458,6 +458,11 @@ class Inverter { return producing; } + InverterStatus getStatus(){ + isProducing(); // recalculate status + return status; + } + uint16_t getFwVersion() { record_t<> *rec = getRecordStruct(InverterDevInform_All); return getChannelFieldValue(CH0, FLD_FW_VERSION, rec); diff --git a/src/publisher/pubMqtt.h b/src/publisher/pubMqtt.h index 92f5de2f..e062439c 100644 --- a/src/publisher/pubMqtt.h +++ b/src/publisher/pubMqtt.h @@ -153,6 +153,10 @@ class PubMqtt { publish(mSubTopic, ((iv->commEnabled) ? dict[STR_TRUE] : dict[STR_FALSE]), true); } + + snprintf(mSubTopic, 32 + MAX_NAME_LENGTH, "comm_disabled"); + publish(mSubTopic, (((*mUtcTimestamp > (sunset + offs)) || (*mUtcTimestamp < (sunrise - offs))) ? dict[STR_TRUE] : dict[STR_FALSE]), true); + return true; } @@ -483,22 +487,22 @@ class PubMqtt { continue; // skip to next inverter // inverter status - iv->isProducing(); // recalculate status - if (InverterStatus::OFF < iv->status) + InverterStatus status = iv->getStatus(); + if (InverterStatus::OFF < status) anyAvail = true; else // inverter is enabled but not available allAvail = false; - if(mLastIvState[id] != iv->status) { + if(mLastIvState[id] != status) { // if status changed from producing to not producing send last data immediately if (InverterStatus::WAS_PRODUCING == mLastIvState[id]) sendData(iv, RealTimeRunData_Debug); - mLastIvState[id] = iv->status; + mLastIvState[id] = status; changed = true; snprintf(mSubTopic, 32 + MAX_NAME_LENGTH, "%s/available", iv->config->name); - snprintf(mVal, 40, "%d", (uint8_t)iv->status); + snprintf(mVal, 40, "%d", (uint8_t)status); publish(mSubTopic, mVal, true); } } diff --git a/src/publisher/pubMqttIvData.h b/src/publisher/pubMqttIvData.h index 3600e22c..9a364ec9 100644 --- a/src/publisher/pubMqttIvData.h +++ b/src/publisher/pubMqttIvData.h @@ -146,7 +146,7 @@ class PubMqttIvData { // calculate total values for RealTimeRunData_Debug if (CH0 == rec->assign[mPos].ch) { - if(mIv->status > InverterStatus::STARTING) { + if(mIv->getStatus() > InverterStatus::STARTING) { if(mIv->config->add2Total) { mTotalFound = true; switch (rec->assign[mPos].fieldId) { diff --git a/src/web/RestApi.h b/src/web/RestApi.h index e98b00fe..990f3e92 100644 --- a/src/web/RestApi.h +++ b/src/web/RestApi.h @@ -421,7 +421,7 @@ class RestApi { obj[F("max_pwr")] = iv->getMaxPower(); obj[F("ts_last_success")] = rec->ts; obj[F("generation")] = iv->ivGen; - obj[F("status")] = (uint8_t)iv->status; + obj[F("status")] = (uint8_t)iv->getStatus(); obj[F("alarm_cnt")] = iv->alarmCnt; obj[F("rssi")] = iv->rssi; diff --git a/src/web/html/setup.html b/src/web/html/setup.html index a1e42a5c..8187fe3b 100644 --- a/src/web/html/setup.html +++ b/src/web/html/setup.html @@ -676,7 +676,7 @@ ml("td", {}, String(i+1)), ml("td", {}, ml("input", {name: "ch_p"+i, class: "text", type: "number", max: 999, value: obj.ch_max_pwr[i]}, null)), ml("td", {}, ml("input", {name: "ch_n"+i, class: "text", type: "text", maxlength: 15, value: (undefined === obj.ch_name[i]) ? "" : obj.ch_name[i]}, null)), - ml("td", {}, ml("input", {name: "yld_c"+i, class: "text", type: "number", max: 999999, value: obj.ch_yield_cor[i]}, null)) + ml("td", {}, ml("input", {name: "yld_c"+i, class: "text", type: "number", max: 999999, value: obj.ch_yield_cor[i], step: "0.001"}, null)) ])); } From e5176b7430ab131cca5dd0800820d103dc99d049 Mon Sep 17 00:00:00 2001 From: kscholty Date: Sat, 23 Dec 2023 17:33:00 +0100 Subject: [PATCH 2/5] Added ESP32-C3 chip --- src/platformio.ini | 15 ++++++++++++ src/web/html/setup.html | 53 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 63 insertions(+), 5 deletions(-) diff --git a/src/platformio.ini b/src/platformio.ini index 54b63a22..2b4b25cf 100644 --- a/src/platformio.ini +++ b/src/platformio.ini @@ -123,6 +123,21 @@ build_flags = ${env.build_flags} monitor_filters = esp32_exception_decoder +[env:esp32-c3-mini] +platform = espressif32@6.4.0 +board = lolin_c3_mini +build_flags = ${env.build_flags} + -DUSE_HSPI_FOR_EPD + -DDEF_NRF_CS_PIN=5 + -DDEF_NRF_CE_PIN=0 + -DDEF_NRF_IRQ_PIN=1 + -DDEF_NRF_MISO_PIN=3 + -DDEF_NRF_MOSI_PIN=4 + -DDEF_NRF_SCLK_PIN=2 +monitor_filters = + esp32_exception_decoder + + [env:opendtufusion] platform = espressif32@6.4.0 board = esp32-s3-devkitc-1 diff --git a/src/web/html/setup.html b/src/web/html/setup.html index a1e42a5c..638ef8e0 100644 --- a/src/web/html/setup.html +++ b/src/web/html/setup.html @@ -435,6 +435,31 @@ [47, "GPIO47"], [48, "GPIO48"], ]; + var esp32c3pins = [ + [255, "off / default"], + [0, "GPIO0"], + [1, "GPIO1"], + [2, "GPIO2"], + [3, "GPIO3"], + [4, "GPIO4"], + [5, "GPIO5"], + [6, "GPIO6"], + [7, "GPIO7"], + [8, "GPIO8"], + [9, "GPIO9"], + [10, "GPIO10"], + [11, "GPIO11"], + [12, "GPIO12 (PSRAM/FLASH)"], + [13, "GPIO13 (PSRAM/FLASH)"], + [14, "GPIO14 (PSRAM/FLASH)"], + [15, "GPIO15 (PSRAM/FLASH)"], + [16, "GPIO16 (PSRAM/FLASH)"], + [17, "GPIO17 (PSRAM/FLASH)"], + [18, "GPIO18 (DONT USE - USB-)"], + [19, "GPIO19 (DONT USE - USB+)"], + [20, "GPIO20 (RX)"], + [21, "GPIO21 (TX)"], + ]; /*ENDIF_ESP32*/ var nrfPa = [ [0, "MIN (recommended)"], @@ -872,13 +897,17 @@ function parsePinout(obj, type, system) { var e = document.getElementById("pinout"); + var pinList = esp32pins; + if("ESP8266" == type) pinList = esp8266pins; + else if ("ESP32-S3" == system["chip_model"]) pinList = esp32s3pins; + else if("ESP32-C3" == system["chip_model"]) pinList = esp32c3pins; pins = [['led0', 'pinLed0', 'At least one inverter is producing'], ['led1', 'pinLed1', 'MqTT connected']]; for(p of pins) { e.append( ml("div", {class: "row mb-3"}, [ ml("div", {class: "col-12 col-sm-3 my-2"}, p[2]), 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], pinList, obj[p[0]]) ) ]) ); @@ -898,6 +927,11 @@ var en = inp("nrfEnable", null, null, ["cb"], "nrfEnable", "checkbox"); en.checked = obj["en"]; + var pinList = esp32pins; + if("ESP8266" == type) pinList = esp8266pins; + else if ("ESP32-S3" == system["chip_model"]) pinList = esp32s3pins; + else if("ESP32-C3" == system["chip_model"]) pinList = esp32c3pins; + e.replaceChildren ( ml("div", {class: "row mb-3"}, [ ml("div", {class: "col-8 col-sm-3 my-2"}, "NRF24 Enable"), @@ -915,7 +949,7 @@ ml("div", {class: "row mb-3"}, [ ml("div", {class: "col-12 col-sm-3 my-2"}, p[0].toUpperCase()), ml("div", {class: "col-12 col-sm-9"}, - sel(p[1], ("ESP8266" == type) ? esp8266pins : ("ESP32-S3" == system["chip_model"]) ? esp32s3pins : esp32pins, objPin[p[0]]) + sel(p[1], pinList, objPin[p[0]]) ) ]) ); @@ -926,6 +960,10 @@ function parseCmtRadio(obj, type, system) { var e = document.getElementById("cmt"); var en = inp("cmtEnable", null, null, ["cb"], "cmtEnable", "checkbox"); + var pinList = esp32pins; + if ("ESP32-S3" == system["chip_model"]) pinList = esp32s3pins; + else if("ESP32-C3" == system["chip_model"]) pinList = esp32c3pins; + en.checked = obj["en"]; e.replaceChildren ( @@ -940,7 +978,7 @@ ml("div", {class: "row mb-3"}, [ ml("div", {class: "col-12 col-sm-3 my-2"}, p[0].toUpperCase()), ml("div", {class: "col-12 col-sm-9"}, - sel(p[1], (("ESP32-S3" == system["chip_model"]) ? esp32s3pins : esp32pins), obj[p[0]]) + sel(p[1], pinList, obj[p[0]]) ) ]) ); @@ -954,6 +992,11 @@ } function parseDisplay(obj, type, system) { + var pinList = esp32pins; + if("ESP8266" == type) pinList = esp8266pirpins; + else if ("ESP32-S3" == system["chip_model"]) pinList = esp32s3pins; + else if("ESP32-C3" == system["chip_model"]) pinList = esp32c3pins; + for(var i of ["disp_pwr"]) document.getElementsByName(i)[0].checked = obj[i]; @@ -968,7 +1011,7 @@ ml("div", {class: "row mb-3", id: "row_" + p[1]}, [ 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[1]]) + sel(p[1], pinList, obj[p[1]]) ) ]) ); @@ -1018,7 +1061,7 @@ document.getElementById("pirPin").append( ml("div", {class: "row mb-3"}, [ ml("div", {class: "col-12 col-sm-3 my-2"}, "PIR sensor"), - ml("div", {class: "col-12 col-sm-9"}, sel("pir_pin", ("ESP8266" == type) ? esp8266pirpins : ("ESP32-S3" == system["chip_model"]) ? esp32s3pins : esp32pins, obj["pir_pin"])) + ml("div", {class: "col-12 col-sm-9"}, sel("pir_pin", pinList, obj["pir_pin"])) ]) ); From 4f75ce7dd1ab528018644d285bf6571c1d543e3c Mon Sep 17 00:00:00 2001 From: lumapu Date: Thu, 28 Dec 2023 02:21:37 +0100 Subject: [PATCH 3/5] 0.8.29 * fix crash if `getLossRate` was read from inverter #1288 #1290 * reduce reload time for opendtufusion ethernet variant to 5 seconds * added basic grid parser --- scripts/convertHtml.py | 2 +- src/CHANGES.md | 5 +- src/hm/Communication.h | 29 +- src/hm/hmRadio.h | 7 +- src/hms/hmsRadio.h | 5 +- src/web/RestApi.h | 11 +- src/web/html/api.js | 8 + src/web/html/grid_info.json | 764 ++++++++++++++++++++++++++++++++ src/web/html/save.html | 4 +- src/web/html/visualization.html | 75 +++- src/web/web.h | 12 + 11 files changed, 876 insertions(+), 46 deletions(-) create mode 100644 src/web/html/grid_info.json diff --git a/scripts/convertHtml.py b/scripts/convertHtml.py index 70d5f6e2..6eaa92a3 100644 --- a/scripts/convertHtml.py +++ b/scripts/convertHtml.py @@ -159,7 +159,7 @@ if os.path.exists(wd): # grab all files with following extensions os.chdir('./web/html') -types = ('*.html', '*.css', '*.js', '*.ico') # the tuple of file types +types = ('*.html', '*.css', '*.js', '*.ico', '*.json') # the tuple of file types files_grabbed = [] for files in types: files_grabbed.extend(glob.glob(files)) diff --git a/src/CHANGES.md b/src/CHANGES.md index e9563510..99b7dc64 100644 --- a/src/CHANGES.md +++ b/src/CHANGES.md @@ -1,9 +1,12 @@ # Development Changes -## 0.8.29 - 2023-12-23 +## 0.8.29 - 2023-12-27 * fix MqTT generic topic `comm_disabled` #1265 #1286 * potential fix of #1285 (reset yield day) * fix fraction of yield correction #1280 +* fix crash if `getLossRate` was read from inverter #1288 #1290 +* reduce reload time for opendtufusion ethernet variant to 5 seconds +* added basic grid parser ## 0.8.28 - 2023-12-23 * fix bug heuristic diff --git a/src/hm/Communication.h b/src/hm/Communication.h index 818b396d..23d20004 100644 --- a/src/hm/Communication.h +++ b/src/hm/Communication.h @@ -117,28 +117,6 @@ class Communication : public CommQueue<> { break; case States::WAIT: - /*if(millis() > mWaitTimeout_min) { - if(mIsRetransmit) { // we already have been through... - mWaitTimeout = mWaitTimeout_min; - } else if(q->iv->mGotFragment) { // nothing received yet? - if(q->iv->mGotLastMsg) { - //mState = States::CHECK_FRAMES; - mWaitTimeout = mWaitTimeout_min; - } - } else if(mFirstTry) { - if(*mSerialDebug) { - DPRINT_IVID(DBG_INFO, q->iv->id); - DBGPRINT(String(millis() - mWaitTimeout_min + mlastTO_min)); - DBGPRINTLN(F("ms - second try")); - } - mFirstTry = false; - mlastTO_min = timeout_min; - q->iv->radioStatistics.retransmits++; // got nothing - mState = States::START; - break; - } - - }*/ if(millis() < mWaitTimeout) return; mState = States::CHECK_FRAMES; @@ -187,7 +165,7 @@ class Communication : public CommQueue<> { if(parseMiFrame(p, q)) q->iv->curFrmCnt++; } - } //else // serial does not match + } //else -> serial does not match q->iv->radio->mBufCtrl.pop(); yield(); @@ -221,7 +199,6 @@ class Communication : public CommQueue<> { else closeRequest(q, true); } - } } @@ -271,7 +248,7 @@ class Communication : public CommQueue<> { compilePayload(q); - if((NULL != mCbPayload) && (GridOnProFilePara != q->cmd)) + if((NULL != mCbPayload) && (GridOnProFilePara != q->cmd) && (GetLossRate != q->cmd)) (mCbPayload)(q->cmd, q->iv); closeRequest(q, true); @@ -306,7 +283,7 @@ class Communication : public CommQueue<> { else ah::dumpBuf(p->packet, p->len); } else { - DBGPRINT(F("| 0x")); + DBGPRINT(F("| ")); DHEX(p->packet[0]); DBGPRINT(F(" ")); DBGHEXLN(p->packet[9]); diff --git a/src/hm/hmRadio.h b/src/hm/hmRadio.h index 6db39d7f..1c4e26e9 100644 --- a/src/hm/hmRadio.h +++ b/src/hm/hmRadio.h @@ -98,7 +98,7 @@ class HmRadio : public Radio { if(mNrf24->isChipConnected()) { DPRINTLN(DBG_INFO, F("Radio Config:")); mNrf24->printPrettyDetails(); - DPRINT(DBG_INFO, F("DTU_SN: 0x")); + DPRINT(DBG_INFO, F("DTU_SN: ")); DBGPRINTLN(String(mDtuSn, HEX)); } else DPRINTLN(DBG_WARN, F("WARNING! your NRF24 module can't be reached, check the wiring")); @@ -152,7 +152,7 @@ class HmRadio : public Radio { void sendControlPacket(Inverter<> *iv, uint8_t cmd, uint16_t *data, bool isRetransmit) { DPRINT_IVID(DBG_INFO, iv->id); - DBGPRINT(F("sendControlPacket cmd: 0x")); + DBGPRINT(F("sendControlPacket cmd: ")); DBGHEXLN(cmd); initPacket(iv->radioId.u64, TX_REQ_DEVCONTROL, SINGLE_FRAME); uint8_t cnt = 10; @@ -307,9 +307,8 @@ class HmRadio : public Radio { else ah::dumpBuf(mTxBuf, len); } else { - DBGPRINT(F("0x")); DHEX(mTxBuf[0]); - DBGPRINT(F(" 0x")); + DBGPRINT(F(" ")); DHEX(mTxBuf[10]); DBGPRINT(F(" ")); DBGHEXLN(mTxBuf[9]); diff --git a/src/hms/hmsRadio.h b/src/hms/hmsRadio.h index 80a2ecd9..2b147107 100644 --- a/src/hms/hmsRadio.h +++ b/src/hms/hmsRadio.h @@ -42,7 +42,7 @@ class CmtRadio : public Radio { } void sendControlPacket(Inverter<> *iv, uint8_t cmd, uint16_t *data, bool isRetransmit) { - DPRINT(DBG_INFO, F("sendControlPacket cmd: 0x")); + DPRINT(DBG_INFO, F("sendControlPacket cmd: ")); DBGHEXLN(cmd); initPacket(iv->radioId.u64, TX_REQ_DEVCONTROL, SINGLE_FRAME); uint8_t cnt = 10; @@ -96,9 +96,8 @@ class CmtRadio : public Radio { else ah::dumpBuf(mTxBuf, len); } else { - DBGPRINT(F("0x")); DHEX(mTxBuf[0]); - DBGPRINT(F(" 0x")); + DBGPRINT(F(" ")); DHEX(mTxBuf[10]); DBGHEXLN(mTxBuf[9]); } diff --git a/src/web/RestApi.h b/src/web/RestApi.h index 990f3e92..f2c193cd 100644 --- a/src/web/RestApi.h +++ b/src/web/RestApi.h @@ -323,9 +323,14 @@ class RestApi { void getHtmlSave(AsyncWebServerRequest *request, JsonObject obj) { getGeneric(request, obj.createNestedObject(F("generic"))); - obj["pending"] = (bool)mApp->getSavePending(); - obj["success"] = (bool)mApp->getLastSaveSucceed(); - obj["reboot"] = (bool)mApp->getShouldReboot(); + obj[F("pending")] = (bool)mApp->getSavePending(); + obj[F("success")] = (bool)mApp->getLastSaveSucceed(); + obj[F("reboot")] = (bool)mApp->getShouldReboot(); + #if defined(ETHERNET) && defined(CONFIG_IDF_TARGET_ESP32S3) + obj[F("reload")] = 5; + #else + obj[F("reload")] = 20; + #endif } void getReboot(AsyncWebServerRequest *request, JsonObject obj) { diff --git a/src/web/html/api.js b/src/web/html/api.js index e76aab2c..b059cc69 100644 --- a/src/web/html/api.js +++ b/src/web/html/api.js @@ -175,6 +175,14 @@ function getAjax(url, ptr, method="GET", json=null) { } } +const getJSON = async url => { + const re = await fetch(url); + if(!re.ok) + throw new Error(re.statusText); + const data = re.json(); + return data; +} + /** * CREATE DOM FUNCTIONS */ diff --git a/src/web/html/grid_info.json b/src/web/html/grid_info.json new file mode 100644 index 00000000..adb68393 --- /dev/null +++ b/src/web/html/grid_info.json @@ -0,0 +1,764 @@ +{ + "type": [ + {"0x0300": "DE_VDE4105_2018"}, + {"0x0a00": "DE NF_EN_50549-1:2019"}, + {"0x0c00": "AT_TOR_Erzeuger_default"}, + {"0x0d04": "France NF_EN_50549-1:2019"}, + {"0x1200": "2.0.4 (EU_EN50438)"}, + {"0x3700": "2.0.0 (CH_NA EEA-NE7–CH2020)"} + ], + "grp_codes": [ + {"0x00": "Voltage H/LVRT"}, + {"0x10": "Frequency H/LFRT"}, + {"0x20": "Islanding Detection"}, + {"0x30": "Reconnection"}, + {"0x40": "Ramp Rates"}, + {"0x50": "Frequency Watt"}, + {"0x60": "Volt Watt"}, + {"0x70": "Active Power Control"}, + {"0x80": "Volt Var"}, + {"0x90": "Specified Power Factor"}, + {"0xa0": "Reactive Power Control"}, + {"0xb0": "Watt Power Factor"} + ], + "group": [ + { + "0x0003": [ + { + "name": "Nominal Voltage", + "div": 10, + "def": 230, + "unit": "V" + }, + { + "name": "Low Voltage 1", + "div": 10, + "min": 180, + "max": 207, + "def": 184, + "unit": "V" + }, + { + "name": "LV1 Maximum Trip Time", + "div": 10, + "def": 1.5, + "unit": "s" + }, + { + "name": "High Voltage 1", + "div": 10, + "min": 250, + "max": 270, + "def": 253, + "unit": "V" + }, + { + "name": "HV1 Maximum Trip Time", + "div": 10, + "min": 0.1, + "max": 100, + "def": 0.1, + "unit": "s" + }, + { + "name": "Low Voltage 2", + "div": 10, + "min": 80, + "max": 161, + "def": 104, + "unit": "V" + }, + { + "name": "LV2 Maximum Trip Time", + "div": 100, + "min": 0.1, + "max": 5, + "def": 0.3, + "unit": "s" + }, + { + "name": "High Voltage 2", + "div": 10, + "min": 230, + "max": 299, + "def": 276, + "unit": "V" + }, + { + "name": "HV2 Maximum Trip Time", + "div": 100, + "min": 0, + "max": 5, + "def": 0.05, + "unit": "s" + } + ] + }, + { + "0x000a": [ + { + "name": "Nominal Voltage", + "div": 10, + "def": 230, + "unit": "V" + }, + { + "name": "Low Voltage 1", + "div": 10, + "min": 160, + "max": 195.5, + "def": 184, + "unit": "V" + }, + { + "name": "LV1 Maximum Trip Time", + "div": 10, + "def": 3, + "unit": "s" + }, + { + "name": "High Voltage 1", + "div": 10, + "min": 270, + "max": 287.5, + "def": 287.5, + "unit": "V" + }, + { + "name": "HV1 Maximum Trip Time", + "div": 10, + "def": 0.1, + "unit": "s" + }, + { + "name": "Low Voltage 2", + "div": 10, + "max": 150, + "min": 100, + "def": 103.5, + "unit": "V" + }, + { + "name": "LV2 Maximum Trip Time", + "div": 100, + "def": 0.3, + "unit": "s" + }, + { + "name": "10 mins Average High Voltage", + "div": 10, + "min": 250, + "max": 270, + "def": 253, + "unit": "V" + } + ] + }, + { + "0x000c": [ + { + "name": "Nominal Voltage", + "div": 10, + "unit": "V" + }, + { + "name": "Low Voltage 1", + "div": 10, + "min": 180, + "max": 207, + "def": 184, + "unit": "V" + }, + { + "name": "LV1 Maximum Trip Time", + "div": 10, + "min": 0.1, + "max": 5, + "def": 1.5, + "unit": "s" + }, + { + "name": "High Voltage 1", + "div": 10, + "min": 250, + "max": 270, + "def": 253, + "unit": "V" + }, + { + "name": "HV1 Maximum Trip Time", + "div": 10, + "min": 0.1, + "max": 100, + "def": 3, + "unit": "s" + }, + { + "name": "Low Voltage 2", + "div": 10, + "min": 80, + "max": 161, + "def": 161, + "unit": "V" + }, + { + "name": "LV2 Maximum Trip Time", + "div": 100, + "min": 0.1, + "max": 5, + "def": 0.2, + "unit": "s" + }, + { + "name": "High Voltage 2", + "div": 10, + "min": 230, + "max": 299, + "def": 264.5, + "unit": "V" + }, + { + "name": "HV2 Maximum Trip Time", + "div": 100, + "min": 0.1, + "max": 5, + "def": 0.2, + "unit": "s" + }, + { + "name": "High Voltage 3", + "div": 10, + "def": 276, + "unit": "V" + }, + { + "name": "HV3 Maximum Trip Time", + "div": 100, + "min": 0.1, + "max": 10, + "def": 0.1, + "unit": "s" + }, + { + "name": "10 mins Average High Voltage", + "div": 10, + "def": 253, + "unit": "V" + } + ] + }, + { + "0x1000": [ + { + "name": "Nominal Frequency", + "div": 100, + "def": 50, + "unit": "Hz" + }, + { + "name": "Low Frequency 1", + "div": 100, + "min": 47.5, + "max": 49.9, + "def": 47.5, + "unit": "Hz" + }, + { + "name": "LF1 Maximum Trip Time", + "div": 10, + "def": 0.1, + "unit": "s" + }, + { + "name": "High Frequency 1", + "div": 100, + "min": 50.1, + "max": 51.5, + "def": 51.5, + "unit": "Hz" + }, + { + "name": "HF1 Maximum Trip Time", + "div": 10, + "def": 0.1, + "unit": "s" + } + ] + }, + { + "0x1003": [ + { + "name": "Nominal Frequency", + "div": 100, + "def": 50, + "unit": "Hz" + }, + { + "name": "Low Frequency 1", + "div": 100, + "min": 45, + "max": 49.9, + "def": 48, + "unit": "Hz" + }, + { + "name": "LF1 Maximum Trip Time", + "div": 100, + "min": 0.1, + "max": 20, + "def": 2, + "unit": "s" + }, + { + "name": "High Frequency 1", + "div": 100, + "min": 50, + "max": 53, + "def": 51, + "unit": "Hz" + }, + { + "name": "HF1 Maximum Trip time", + "div": 10, + "min": 0.1, + "max": 20, + "def": 2, + "unit": "s" + }, + { + "name": "Low Frequency 2", + "div": 100, + "min": 45, + "max": 50, + "def": 47.5, + "unit": "Hz" + }, + { + "name": "LF2 Maximum Trip Time", + "div": 10, + "min": 0.1, + "max": 5, + "def": 0.5, + "unit": "s" + }, + { + "name": "High Frequency 2", + "div": 100, + "min": 50, + "max": 52, + "def": 52, + "unit": "Hz" + }, + { + "name": "HF2 Maximum Trip time", + "div": 10, + "min": 0.1, + "max": 5, + "def": 0.5, + "unit": "s" + } + ] + }, + { + "0x2000": [ + { + "name": "Island Detection Activated", + "div": 1, + "min": 0, + "max": 1, + "def": 1 + } + ] + }, + { + "0x3003": [ + { + "name": "Reconnect Time", + "div": 10, + "min": 10, + "max": 300, + "def": 60, + "unit": "s" + }, + { + "name": "Reconnect High Voltage", + "div": 10, + "min": 240, + "max": 276, + "def": 253, + "unit": "V" + }, + { + "name": "Reconnect Low Voltage", + "div": 10, + "min": 195.5, + "max": 210, + "def": 195.5, + "unit": "V" + }, + { + "name": "Reconnect High Frequency", + "div": 100, + "max": 50.9, + "min": 50.1, + "def": 50.2, + "unit": "Hz" + }, + { + "name": "Reconnect Low Frequency", + "div": 100, + "min": 47.5, + "max": 49.9, + "def": 49.5, + "unit": "Hz" + } + ] + }, + { + "0x4000": [ + { + "name": "Normal Ramp up Rate", + "div": 100, + "min": 0.1, + "max": 100, + "def": 20, + "unit": "Rated%/s" + }, + { + "name": "Soft Start Ramp up Rate ", + "div": 100, + "min": 0.1, + "max": 10, + "def": 0.16, + "unit": "Rated%/s" + } + ] + }, + { + "0x5001": [ + { + "name": "FW Function Activated", + "div": 1, + "min": 0, + "max": 1, + "def": 1 + }, + { + "name": "Start of Frequency Watt Droop", + "div": 100, + "min": 50.2, + "max": 53, + "def": 50.2, + "unit": "Hz" + }, + { + "name": "FW Droop Slope", + "div": 10, + "min": 16.7, + "max": 100, + "def": 40, + "unit": "Pn%/Hz" + }, + { + "name": "Recovery Ramp Rate", + "div": 100, + "min": 0.1, + "max": 100, + "def": 0.16, + "unit": "Pn%/s" + + }, + { + "name": "FW Setting Time", + "div": 10, + "min": 0, + "max": 2, + "def": 0, + "unit": "s" + } + ] + }, + { + "0x5008": [ + { + "name": "FW Function Activated", + "div": 1, + "min": 0, + "max": 1, + "def": 1 + }, + { + "name": "Start of Frequency Watt Droop", + "div": 100, + "min": 50.2, + "max": 52, + "def": 50.2, + "unit": "Hz" + }, + { + "name": "FW Droop Slope", + "div": 10, + "min": 16.7, + "max": 100, + "def": 40, + "unit": "Pn%/Hz" + }, + { + "name": "Recovery Ramp Rate", + "div": 100, + "min": 0.1, + "max": 50, + "def": 0.16, + "unit": "Pn%/s" + }, + { + "name": "Recovery High Frequency", + "div": 10, + "min": 50.1, + "max": 52, + "def": 50.2, + "unit": "Hz" + }, + { + "name": "Recovery Low Frequency", + "div": 100, + "min": 49, + "max": 49.9, + "def": 49.8, + "unit": "Hz" + } + ] + }, + { + "0x6000": [ + { + "name": "VW Function Activated", + "div": 1, + "min": 0, + "max": 1, + "def": 1 + }, + { + "name": "Start of Voltage Watt Droop", + "div": 10, + "def": 253, + "unit": "V" + }, + { + "name": "End of Voltage Watt Droop", + "div": 10, + "min": 258, + "max": 270, + "def": 265, + "unit": "V" + }, + { + "name": "VW Droop Slope", + "div": 100, + "min": 5, + "max": 18, + "def": 5.33, + "unit": "Pn%/V" + } + ] + }, + { + "0x7002": [ + { + "name": "APC Function Activated", + "div": 1, + "min": 0, + "max": 1, + "def": 1 + }, + { + "name": "Power Ramp Rate", + "div": 100, + "min": 0.33, + "max": 100, + "def": 100, + "unit": "Pn%/s" + } + ] + }, + { + "0x8000": [ + { + "name": "VV Function Activated", + "div": 1, + "min": 0, + "max": 1, + "def": 0 + }, + { + "name": "Voltage Set Point V1", + "div": 10, + "min": 184, + "max": 230, + "def": 213.9, + "unit": "V" + }, + { + "name": "Reactive Set Point Q1", + "div": 10, + "min": 0, + "max": 100, + "def": 30, + "unit": "%Pn" + }, + { + "name": "Voltage Set Point V2", + "div": 10, + "min": 210, + "max": 240, + "def": 223.1, + "unit": "V" + }, + { + "name": "Voltage Set Point V3", + "div": 10, + "min": 220, + "max": 240, + "def": 236.9, + "unit": "V" + }, + { + "name": "Voltage Set Point V4", + "div": 10, + "min": 230, + "max": 253, + "def": 246.1, + "unit": "V" + }, + { + "name": "Reactive Set Point Q4", + "div": 10, + "min": 0, + "max": 100, + "def": 30, + "unit": "%Pn" + } + ] + }, + { + "0x8001": [ + { + "name": "VV Function Activated", + "div": 1, + "min": 0, + "max": 1, + "def": 0 + }, + { + "name": "Voltage Set Point V1", + "div": 10, + "def": 213.9, + "unit": "V" + }, + { + "name": "Reactive Set Point Q1", + "div": 10, + "min": 0, + "max": 100, + "def": 30, + "unit": "%Pn" + }, + { + "name": "Voltage Set Point V2", + "div": 10, + "def": 223.1, + "unit": "V" + }, + { + "name": "Voltage Set Point V3", + "div": 10, + "def": 236.9, + "unit": "V" + }, + { + "name": "Voltage Set Point V4", + "div": 10, + "def": 246.1, + "unit": "V" + }, + { + "name": "Reactive Set Point Q4", + "div": 10, + "min": 0, + "max": 100, + "def": 30, + "unit": "%Pn" + }, + { + "name": "VV Setting Time", + "div": 10, + "min": 0, + "max": 60, + "def": 10, + "unit": "s" + } + ] + }, + { + "0x9000": [ + { + "name": "Specified Power Factor Activated", + "div": 1, + "min": 0, + "max": 1, + "def": 0 + }, + { + "name": "Power Factor", + "div": 100, + "min": 0.9, + "max": 1, + "def": 0.95 + } + ] + }, + { + "0xa002": [ + { + "name": "RPC Function Activated", + "div": 1, + "min": 0, + "max": 1, + "def": 0 + }, + { + "name": "Reactive Power", + "div": 100, + "min": 0, + "max": 50, + "def": 0, + "unit": "%Sn" + } + ] + }, + { + "0xb000": [ + { + "name": "WPF Function Activated", + "div": 1, + "min": 0, + "max": 1, + "def": 0 + }, + { + "name": "Start of Power of WPF", + "div": 10, + "def": 50, + "unit": "%Pn" + }, + { + "name": "Power Factor ar Rated Power", + "div": 100, + "min": 0.8, + "max": 1, + "def": 0.95 + } + ] + } + ] +} diff --git a/src/web/html/save.html b/src/web/html/save.html index ce50c6ca..9b5b4864 100644 --- a/src/web/html/save.html +++ b/src/web/html/save.html @@ -34,8 +34,8 @@ html = "Settings successfully saved. Automatic page reload in 3 seconds."; meta.content = 3; } else { - html = "Settings successfully saved. Rebooting. Automatic redirect in 20 seconds."; - meta.content = 20 + "; URL=/"; + html = "Settings successfully saved. Rebooting. Automatic redirect in " + obj.reload + " seconds."; + meta.content = obj.reload + "; URL=/"; } document.getElementsByTagName('head')[0].appendChild(meta); } else { diff --git a/src/web/html/visualization.html b/src/web/html/visualization.html index faf76432..b561d5cc 100644 --- a/src/web/html/visualization.html +++ b/src/web/html/visualization.html @@ -295,13 +295,76 @@ getAjax("/api/inverter/grid/" + obj.id, showGridProfile); }}, null)) ]) - ]); - modal("Info for inverter " + obj.name, ml("div", {}, html)); + ]) + modal("Info for inverter " + obj.name, ml("div", {}, html)) + } + + function getGridValue(g) { + var val = (parseInt(g.grid.substring(g.offs*3, g.offs*3+2), 16) * 256) + + parseInt(g.grid.substring(g.offs*3+3, g.offs*3+5), 16) + g.offs += 2 + return val + } + + function getGridIdentifier(g) { + return "0x" + getGridValue(g).toString(16).padStart(4, '0') + } + + function getGridType(t, id) { + for(e of t) { + if(undefined !== e[id]) + return e[id] + } + return null + } + + function parseGridGroup(g) { + var id = getGridIdentifier(g) + var type = getGridType(g.info.grp_codes, id.substring(0, 4)) + var content = [] + content.push(ml("div", {class: "row"}, + ml("div", {class: "col head p-2 mt-3"}, + ml("div", {class: "col a-c"}, type + " (Code " + id + ")") + ) + )) + content.push(ml("div", {class: "row my-2"}, [ + ml("div", {class: "col-4"}, ml("b", {}, "Name")), + ml("div", {class: "col-3"}, ml("b", {}, "Value")), + ml("div", {class: "col-3"}, ml("b", {}, "Range")), + ml("div", {class: "col-2"}, ml("b", {}, "Default")) + ])) + for(e of g.info.group) { + if(Array.isArray(e[id])) { + for(e of e[id]) { + var v = String(getGridValue(g) / e.div); + var vt = (v !== String(e.def)) ? "b" : "span"; + content.push(ml("div", {class: "row mt-2"}, [ + ml("div", {class: "col-4"}, e.name), + ml("div", {class: "col-3"}, ml(vt, {}, v + ((undefined !== e.unit) ? " [" + e.unit + "]" : ""))), + ml("div", {class: "col-3"}, (undefined !== e.min) ? (e.min + " - " + e.max) : "n/a"), + ml("div", {class: "col-2"}, String(e.def)) + ])) + } + } + } + + return ml("div", {class: "col"}, [...content]) } function showGridProfile(obj) { - var html = ml("pre", {}, obj.grid); - modal("Grid Profile for inverter " + obj.name, ml("div", {}, html)); + getJSON("/grid_info.json").then(data => { + var glob = {offs:0, grid:obj.grid, info: data} + var content = []; + content.push(ml("div", {class: "row"}, + ml("div", {class: "col my-3"}, ml("h5", {}, getGridType(glob.info.type, getGridIdentifier(glob)) + " (Version " + getGridValue(glob).toString(16) + ")")) + )) + + while((glob.offs*3) < glob.grid.length) { + content.push(parseGridGroup(glob)) + } + + modal("Grid Profile for inverter " + obj.name, ml("div", {}, ml("div", {class: "col mb-2"}, [...content]))) + }) } @@ -315,8 +378,8 @@ tr2(["RX fragments", obj.frame_cnt, ""]), tr2(["TX retransmits", obj.retransmits, ""]) ]) - ]); - modal("Radio statistics for inverter " + obj.name, ml("div", {}, html)); + ]) + modal("Radio statistics for inverter " + obj.name, ml("div", {}, html)) } function limitModal(obj) { diff --git a/src/web/web.h b/src/web/web.h index b104e9fd..451dc656 100644 --- a/src/web/web.h +++ b/src/web/web.h @@ -25,6 +25,7 @@ #include "html/h/colorBright_css.h" #include "html/h/colorDark_css.h" #include "html/h/favicon_ico.h" +#include "html/h/grid_info_json.h" #include "html/h/index_html.h" #include "html/h/login_html.h" #include "html/h/serial_html.h" @@ -65,6 +66,7 @@ class Web { mWeb.on("/colors.css", HTTP_GET, std::bind(&Web::onColor, this, std::placeholders::_1)); mWeb.on("/style.css", HTTP_GET, std::bind(&Web::onCss, this, std::placeholders::_1)); mWeb.on("/api.js", HTTP_GET, std::bind(&Web::onApiJs, this, std::placeholders::_1)); + mWeb.on("/grid_info.json", HTTP_GET, std::bind(&Web::onGridInfoJson, this, std::placeholders::_1)); mWeb.on("/favicon.ico", HTTP_GET, std::bind(&Web::onFavicon, this, std::placeholders::_1)); mWeb.onNotFound ( std::bind(&Web::showNotFound, this, std::placeholders::_1)); mWeb.on("/reboot", HTTP_ANY, std::bind(&Web::onReboot, this, std::placeholders::_1)); @@ -389,6 +391,16 @@ class Web { request->send(response); } + void onGridInfoJson(AsyncWebServerRequest *request) { + DPRINTLN(DBG_VERBOSE, F("onGridInfoJson")); + + AsyncWebServerResponse *response = request->beginResponse_P(200, F("application/json; charset=utf-8"), grid_info_json, grid_info_json_len); + response->addHeader(F("Content-Encoding"), "gzip"); + if(request->hasParam("v")) + response->addHeader(F("Cache-Control"), F("max-age=604800")); + request->send(response); + } + void onFavicon(AsyncWebServerRequest *request) { static const char favicon_type[] PROGMEM = "image/x-icon"; AsyncWebServerResponse *response = request->beginResponse_P(200, favicon_type, favicon_ico, favicon_ico_len); From cbb93cfd5a849c1d41ba8a0e993947256d28a290 Mon Sep 17 00:00:00 2001 From: lumapu Date: Thu, 28 Dec 2023 02:30:13 +0100 Subject: [PATCH 4/5] 0.8.29 * added ESP32-C3 mini environment #1289 --- .github/workflows/compile_development.yml | 2 +- .github/workflows/compile_release.yml | 2 +- scripts/getVersion.py | 13 +++++++++++++ src/CHANGES.md | 1 + 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/.github/workflows/compile_development.yml b/.github/workflows/compile_development.yml index 31049c21..6eba4879 100644 --- a/.github/workflows/compile_development.yml +++ b/.github/workflows/compile_development.yml @@ -43,7 +43,7 @@ jobs: pip install --upgrade platformio - 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 --environment opendtufusion-ethernet + 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 esp32-c3-mini --environment opendtufusion --environment opendtufusion-ethernet - name: Copy boot_app0.bin run: cp ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin src/.pio/build/opendtufusion/ota.bin diff --git a/.github/workflows/compile_release.yml b/.github/workflows/compile_release.yml index 8f493100..61c920b3 100644 --- a/.github/workflows/compile_release.yml +++ b/.github/workflows/compile_release.yml @@ -47,7 +47,7 @@ jobs: pip install --upgrade platformio - 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 --environment opendtufusion-ethernet + 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 esp32-c3-mini --environment opendtufusion --environment opendtufusion-ethernet - name: Copy boot_app0.bin run: cp ~/.platformio/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin src/.pio/build/opendtufusion/ota.bin diff --git a/scripts/getVersion.py b/scripts/getVersion.py index 4ea74d71..cdb39ae8 100644 --- a/scripts/getVersion.py +++ b/scripts/getVersion.py @@ -55,6 +55,7 @@ def readVersion(path, infile): os.mkdir(path + "firmware/ESP32/") os.mkdir(path + "firmware/ESP32-S2/") os.mkdir(path + "firmware/ESP32-S3/") + os.mkdir(path + "firmware/ESP32-C3/") os.mkdir(path + "firmware/ESP32-S3-ETH/") sha = os.getenv("SHA",default="sha") @@ -94,6 +95,11 @@ def readVersion(path, infile): dst = path + "firmware/ESP32-S2/" + versionout os.rename(src, dst) + versionout = version[:-1] + "_" + sha + "_esp32c3-mini.bin" + src = path + ".pio/build/esp32-c3-mini/firmware.bin" + dst = path + "firmware/ESP32-C3/" + versionout + os.rename(src, dst) + versionout = version[:-1] + "_" + sha + "_esp32s3.bin" src = path + ".pio/build/opendtufusion/firmware.bin" dst = path + "firmware/ESP32-S3/" + versionout @@ -118,6 +124,13 @@ def readVersion(path, infile): os.rename(src + "partitions.bin", dst + "partitions.bin") genOtaBin(dst) + # other ESP32-C3 bin files + src = path + ".pio/build/esp32-c3-mini/" + dst = path + "firmware/ESP32-C3/" + os.rename(src + "bootloader.bin", dst + "bootloader.bin") + os.rename(src + "partitions.bin", dst + "partitions.bin") + genOtaBin(dst) + # other ESP32-S3 bin files src = path + ".pio/build/opendtufusion/" dst = path + "firmware/ESP32-S3/" diff --git a/src/CHANGES.md b/src/CHANGES.md index 99b7dc64..fc8566cc 100644 --- a/src/CHANGES.md +++ b/src/CHANGES.md @@ -7,6 +7,7 @@ * fix crash if `getLossRate` was read from inverter #1288 #1290 * reduce reload time for opendtufusion ethernet variant to 5 seconds * added basic grid parser +* added ESP32-C3 mini environment #1289 ## 0.8.28 - 2023-12-23 * fix bug heuristic From 4e277e20c8b0b1663f365dd1d1c14fd6432311c5 Mon Sep 17 00:00:00 2001 From: lumapu Date: Thu, 28 Dec 2023 10:29:55 +0100 Subject: [PATCH 5/5] 0.8.29 * fix compile for ESP32-C3 --- patches/AsyncWeb_Prometheus.patch | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/patches/AsyncWeb_Prometheus.patch b/patches/AsyncWeb_Prometheus.patch index 7fb9209a..21fe22cd 100644 --- a/patches/AsyncWeb_Prometheus.patch +++ b/patches/AsyncWeb_Prometheus.patch @@ -1,3 +1,16 @@ +diff --git a/src/AsyncWebSocket.cpp b/src/AsyncWebSocket.cpp +index 12be5f8..cffeed7 100644 +--- a/src/AsyncWebSocket.cpp ++++ b/src/AsyncWebSocket.cpp +@@ -737,7 +737,7 @@ void AsyncWebSocketClient::binary(const __FlashStringHelper *data, size_t len) + IPAddress AsyncWebSocketClient::remoteIP() const + { + if (!_client) +- return IPAddress(0U); ++ return IPAddress(); + + return _client->remoteIP(); + } diff --git a/src/WebResponses.cpp b/src/WebResponses.cpp index 22a549f..e0b36b3 100644 --- a/src/WebResponses.cpp