From 6b0ec397598ba13f07304ba45a47a422e6091f78 Mon Sep 17 00:00:00 2001 From: Gunnar Skjold Date: Sun, 16 Jan 2022 09:58:13 +0100 Subject: [PATCH] Various updates --- src/AmsToMqttBridge.ino | 4 +- src/entsoe/DnbCurrParser.cpp | 20 +++++++++- src/entsoe/DnbCurrParser.h | 1 + src/entsoe/EntsoeApi.cpp | 74 ++++++++++++++++++++++++++---------- src/entsoe/EntsoeApi.h | 10 +++++ src/web/AmsWebServer.cpp | 26 +++++++++---- web/application.js | 41 +++++++++++++------- web/data.json | 3 +- web/wifi.html | 10 ++--- 9 files changed, 137 insertions(+), 52 deletions(-) diff --git a/src/AmsToMqttBridge.ino b/src/AmsToMqttBridge.ino index 590cc182..3bc5f680 100644 --- a/src/AmsToMqttBridge.ino +++ b/src/AmsToMqttBridge.ino @@ -751,7 +751,7 @@ bool readHanPort() { debugD("Frame dump (%db):", len); debugPrint(buf, 0, len); } - if(hc != NULL && Debug.isActive(RemoteDebug::DEBUG)) { + if(hc != NULL && Debug.isActive(RemoteDebug::VERBOSE)) { debugD("System title:"); debugPrint(hc->system_title, 0, 8); debugD("Initialization vector:"); @@ -764,7 +764,7 @@ bool readHanPort() { len = 0; while(hanSerial->available()) hanSerial->read(); if(pos > 0) { - debugI("Valid data, start at byte %d", pos); + debugD("Valid data, start at byte %d", pos); data = IEC6205675(((char *) (buf)) + pos, meterState.getMeterType(), meterConfig.distributionSystem, timestamp, hc); } else { if(Debug.isActive(RemoteDebug::WARNING)) { diff --git a/src/entsoe/DnbCurrParser.cpp b/src/entsoe/DnbCurrParser.cpp index 7f1aef02..00b6dc53 100644 --- a/src/entsoe/DnbCurrParser.cpp +++ b/src/entsoe/DnbCurrParser.cpp @@ -1,4 +1,5 @@ #include "DnbCurrParser.h" +#include "Arduino.h" #include "HardwareSerial.h" float DnbCurrParser::getValue() { @@ -35,7 +36,22 @@ size_t DnbCurrParser::write(uint8_t byte) { } } else if(byte == '>') { buf[pos++] = byte; - if(strncmp(buf, " -#elif defined(ESP32) // ARDUINO_ARCH_ESP32 - #include -#else - #warning "Unsupported board type" +#if defined(ESP32) +#include #endif EntsoeApi::EntsoeApi(RemoteDebug* Debug) { debugger = Debug; + client.setInsecure(); + https.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); + // Entso-E uses CET/CEST TimeChangeRule CEST = {"CEST", Last, Sun, Mar, 2, 120}; TimeChangeRule CET = {"CET ", Last, Sun, Oct, 3, 60}; @@ -26,6 +25,7 @@ void EntsoeApi::setup(EntsoeConfig& config) { this->config = new EntsoeConfig(); } memcpy(this->config, &config, sizeof(config)); + lastCurrencyFetch = 0; } char* EntsoeApi::getToken() { @@ -79,7 +79,6 @@ float EntsoeApi::getValueForHour(time_t cur, uint8_t hour) { bool EntsoeApi::loop() { if(strlen(getToken()) == 0) return false; - bool ret = false; uint64_t now = millis64(); if(now < 10000) return false; // Grace period @@ -121,15 +120,23 @@ bool EntsoeApi::loop() { d2.Year+1970, d2.Month, d2.Day, 23, 00, config->area, config->area); + #if defined(ESP32) + esp_task_wdt_reset(); + #elif defined(ESP8266) + ESP.wdtFeed(); + #endif + + if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EntsoeApi) Fetching prices for today\n"); if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("(EntsoeApi) url: %s\n", url); EntsoeA44Parser* a44 = new EntsoeA44Parser(); if(retrieve(url, a44) && a44->getPoint(0) != ENTSOE_NO_VALUE) { today = a44; - ret = true; + return true; } else if(a44 != NULL) { delete a44; today = NULL; + return false; } } @@ -151,23 +158,29 @@ bool EntsoeApi::loop() { d2.Year+1970, d2.Month, d2.Day, 23, 00, config->area, config->area); + #if defined(ESP32) + esp_task_wdt_reset(); + #elif defined(ESP8266) + ESP.wdtFeed(); + #endif + if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EntsoeApi) Fetching prices for tomorrow\n"); if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("(EntsoeApi) url: %s\n", url); EntsoeA44Parser* a44 = new EntsoeA44Parser(); if(retrieve(url, a44) && a44->getPoint(0) != ENTSOE_NO_VALUE) { tomorrow = a44; - ret = true; + return true; } else if(a44 != NULL) { delete a44; tomorrow = NULL; + return false; } } } - return ret; + return false; } bool EntsoeApi::retrieve(const char* url, Stream* doc) { - WiFiClientSecure client; #if defined(ESP8266) // https://arduino-esp8266.readthedocs.io/en/latest/esp8266wifi/bearssl-client-secure-class.html#mfln-or-maximum-fragment-length-negotiation-saving-ram /* Rumor has it that a client cannot request a lower max_fragment_length, so I guess thats why the following does not work. @@ -185,13 +198,15 @@ bool EntsoeApi::retrieve(const char* url, Stream* doc) { */ #endif - client.setInsecure(); - - HTTPClient https; - https.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); - if(https.begin(client, url)) { printD("Connection established"); + + #if defined(ESP32) + esp_task_wdt_reset(); + #elif defined(ESP8266) + ESP.wdtFeed(); + #endif + /* #if defined(ESP8266) if(!client.getMFLNStatus()) { @@ -204,6 +219,13 @@ bool EntsoeApi::retrieve(const char* url, Stream* doc) { */ int status = https.GET(); + + #if defined(ESP32) + esp_task_wdt_reset(); + #elif defined(ESP8266) + ESP.wdtFeed(); + #endif + if(status == HTTP_CODE_OK) { printD("Receiving data"); https.writeToStream(doc); @@ -241,15 +263,25 @@ float EntsoeApi::getCurrencyMultiplier(const char* from, const char* to) { uint64_t now = millis64(); if(lastCurrencyFetch == 0 || now - lastCurrencyFetch > (SECS_PER_HOUR * 1000)) { char url[256]; - snprintf(url, sizeof(url), "https://data.norges-bank.no/api/data/EXR/M.%s.%s.SP?lastNObservations=1", - from, - to - ); - DnbCurrParser p; + + snprintf(url, sizeof(url), "https://data.norges-bank.no/api/data/EXR/M.%s.NOK.SP?lastNObservations=1", from); + if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EntsoeApi) Retrieving %s to NOK conversion\n", from); + if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("(EntsoeApi) url: %s\n", url); if(retrieve(url, &p)) { + if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("(EntsoeApi) got exchange rate %.4f\n", p.getValue()); currencyMultiplier = p.getValue(); + if(strncmp(to, "NOK", 3) != 0) { + snprintf(url, sizeof(url), "https://data.norges-bank.no/api/data/EXR/M.%s.NOK.SP?lastNObservations=1", to); + if(debugger->isActive(RemoteDebug::INFO)) debugger->printf("(EntsoeApi) Retrieving %s to NOK conversion\n", to); + if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("(EntsoeApi) url: %s\n", url); + if(retrieve(url, &p)) { + if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("(EntsoeApi) got exchange rate %.4f\n", p.getValue()); + currencyMultiplier /= p.getValue(); + } + } } + if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf("(EntsoeApi) Resulting currency multiplier: %.4f\n", currencyMultiplier); lastCurrencyFetch = now; } return currencyMultiplier; diff --git a/src/entsoe/EntsoeApi.h b/src/entsoe/EntsoeApi.h index 885c8471..df5ef496 100644 --- a/src/entsoe/EntsoeApi.h +++ b/src/entsoe/EntsoeApi.h @@ -7,6 +7,14 @@ #include "EntsoeA44Parser.h" #include "AmsConfiguration.h" +#if defined(ESP8266) + #include +#elif defined(ESP32) // ARDUINO_ARCH_ESP32 + #include +#else + #warning "Unsupported board type" +#endif + #define ENTSOE_DEFAULT_MULTIPLIER 1.00 #define SSL_BUF_SIZE 512 @@ -24,6 +32,8 @@ public: private: RemoteDebug* debugger; EntsoeConfig* config = NULL; + WiFiClientSecure client; + HTTPClient https; uint64_t midnightMillis = 0; uint64_t lastTodayFetch = 0; diff --git a/src/web/AmsWebServer.cpp b/src/web/AmsWebServer.cpp index 3e878a28..b854bbfd 100644 --- a/src/web/AmsWebServer.cpp +++ b/src/web/AmsWebServer.cpp @@ -498,12 +498,21 @@ void AmsWebServer::configWifiHtml() { html.replace("{s}", wifi.ssid); html.replace("{p}", wifi.psk); - html.replace("{st}", strlen(wifi.ip) > 0 ? "checked" : ""); - html.replace("{i}", wifi.ip); - html.replace("{g}", wifi.gateway); - html.replace("{sn}", wifi.subnet); - html.replace("{d1}", wifi.dns1); - html.replace("{d2}", wifi.dns2); + if(strlen(wifi.ip) > 0) { + html.replace("{st}", "checked"); + html.replace("{i}", wifi.ip); + html.replace("{g}", wifi.gateway); + html.replace("{sn}", wifi.subnet); + html.replace("{d1}", wifi.dns1); + html.replace("{d2}", wifi.dns2); + } else { + html.replace("{st}", ""); + html.replace("{i}", WiFi.localIP().toString()); + html.replace("{g}", WiFi.gatewayIP().toString()); + html.replace("{sn}", WiFi.subnetMask().toString()); + html.replace("{d1}", WiFi.dnsIP().toString()); + html.replace("{d2}", ""); + } html.replace("{h}", wifi.hostname); html.replace("{m}", wifi.mdns ? "checked" : ""); @@ -783,7 +792,8 @@ void AmsWebServer::dataJson() { mqtt == NULL ? 0 : (int) mqtt->lastError(), price == ENTSOE_NO_VALUE ? "null" : String(price, 2).c_str(), time(nullptr), - meterState->getMeterType() + meterState->getMeterType(), + meterConfig->distributionSystem ); server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); @@ -1269,6 +1279,7 @@ void AmsWebServer::handleSave() { strcpy(entsoe.currency, server.arg("ecu").c_str()); entsoe.multiplier = server.arg("em").toFloat() * 1000; config->setEntsoeConfig(entsoe); + eapi->setup(entsoe); } printI("Saving configuration now..."); @@ -1564,6 +1575,7 @@ void AmsWebServer::firmwareUpload() { server.send(500, "text/plain", "500: couldn't create file"); } else { #if defined(ESP32) + esp_task_wdt_delete(NULL); esp_task_wdt_deinit(); #elif defined(ESP8266) ESP.wdtDisable(); diff --git a/web/application.js b/web/application.js index 478debd1..fa5ad144 100644 --- a/web/application.js +++ b/web/application.js @@ -1,5 +1,6 @@ var nextVersion; var im, em; +var ds = 0; // Price plot var pp; @@ -231,9 +232,9 @@ $(function() { // For wifi $('#st').on('change', function() { if($(this).is(':checked')) { - $('#i').show(); + $('.sip').prop('disabled', false); } else { - $('#i').hide(); + $('.sip').prop('disabled', true); } }); $('#st').trigger('change'); @@ -597,12 +598,6 @@ var fetch = function() { dataType: 'json', }).done(function(json) { retrycount = 0; - if(im) { - $(".SimpleMeter").hide(); - im.show(); - em.show(); - - } for(var id in json) { var str = json[id]; @@ -622,6 +617,8 @@ var fetch = function() { $('.ju').html(moment.duration(parseInt(json.u), 'seconds').humanize()); } + ds = parseInt(json.ds); + var kib = parseInt(json.m)/1000; $('.jm').html(kib.toFixed(1)); if(kib > 32) { @@ -677,6 +674,14 @@ var fetch = function() { } if(vp) { + switch(ds) { + case 1: + vo.title = 'Voltage between'; + break; + case 2: + vo.title = 'Phase voltage'; + break; + } var c = 0; var t = 0; var r = 1; @@ -686,21 +691,21 @@ var fetch = function() { t += u1; c++; var pct = (Math.max(parseFloat(json.u1)-195.5, 1)*100/69); - arr[r++] = ['L1', u1, "color: " + voltcol(pct) + ";opacity: 0.9;", u1 + "V"]; + arr[r++] = [ds == 1 ? 'L1-L2' : 'L1', u1, "color: " + voltcol(pct) + ";opacity: 0.9;", u1 + "V"]; } if(json.u2) { var u2 = parseFloat(json.u2); t += u2; c++; var pct = (Math.max(parseFloat(json.u2)-195.5, 1)*100/69); - arr[r++] = ['L2', u2, "color: " + voltcol(pct) + ";opacity: 0.9;", u2 + "V"]; + arr[r++] = [ds == 1 ? 'L1-L3' : 'L2', u2, "color: " + voltcol(pct) + ";opacity: 0.9;", u2 + "V"]; } if(json.u3) { var u3 = parseFloat(json.u3); t += u3; c++; var pct = (Math.max(parseFloat(json.u3)-195.5, 1)*100/69); - arr[r++] = ['L3', u3, "color: " + voltcol(pct) + ";opacity: 0.9;", u3 + "V"]; + arr[r++] = [ds == 1 ? 'L2-L3' : 'L3', u3, "color: " + voltcol(pct) + ";opacity: 0.9;", u3 + "V"]; } v = t/c; if(v > 0) { @@ -710,6 +715,14 @@ var fetch = function() { } if(ap && json.mf) { + switch(ds) { + case 1: + ao.title = 'Current between'; + break; + case 2: + ao.title = 'Phase current'; + break; + } var a = 0; var r = 1; var arr = [['Phase', 'Amperage', { role: 'style' }, { role: 'annotation' }]]; @@ -717,19 +730,19 @@ var fetch = function() { var i1 = parseFloat(json.i1); a = Math.max(a, i1); var pct = (parseFloat(json.i1)/parseInt(json.mf))*100; - arr[r++] = ['L1', pct, "color: " + ampcol(pct) + ";opacity: 0.9;", i1 + "A"]; + arr[r++] = [ds == 1 ? 'L1-L2' : 'L1', pct, "color: " + ampcol(pct) + ";opacity: 0.9;", i1 + "A"]; } if(json.i2) { var i2 = parseFloat(json.i2); a = Math.max(a, i2); var pct = (parseFloat(json.i2)/parseInt(json.mf))*100; - arr[r++] = ['L2', pct, "color: " + ampcol(pct) + ";opacity: 0.9;", i2 + "A"]; + arr[r++] = [ds == 1 ? 'L1-L3' : 'L2', pct, "color: " + ampcol(pct) + ";opacity: 0.9;", i2 + "A"]; } if(json.i3) { var i3 = parseFloat(json.i3); a = Math.max(a, i3); var pct = (parseFloat(json.i3)/parseInt(json.mf))*100; - arr[r++] = ['L3', pct, "color: " + ampcol(pct) + ";opacity: 0.9;", i3 + "A"]; + arr[r++] = [ds == 1 ? 'L2-L3' : 'L3', pct, "color: " + ampcol(pct) + ";opacity: 0.9;", i3 + "A"]; } if(a > 0) { aa = google.visualization.arrayToDataTable(arr); diff --git a/web/data.json b/web/data.json index 2b200341..d48cc336 100644 --- a/web/data.json +++ b/web/data.json @@ -32,5 +32,6 @@ "me" : %d, "p" : %s, "c" : %lu, - "mt" : %d + "mt" : %d, + "ds" : %d } \ No newline at end of file diff --git a/web/wifi.html b/web/wifi.html index b597f4e7..0b6b6aa2 100644 --- a/web/wifi.html +++ b/web/wifi.html @@ -42,7 +42,7 @@
IP
- +
@@ -50,7 +50,7 @@
Subnet
- +
@@ -58,7 +58,7 @@
Gateway
- +
@@ -66,7 +66,7 @@
DNS 1
- +
@@ -74,7 +74,7 @@
DNS 2
- +