From 54fb950513793895d20452b70ee8dab90e37c908 Mon Sep 17 00:00:00 2001 From: Gunnar Skjold Date: Fri, 22 Jan 2021 08:01:23 +0100 Subject: [PATCH] Changes after testing --- lib/HanReader/src/HanReader.cpp | 20 +++-- platformio.ini | 3 +- scripts/makeweb.py | 2 +- src/AmsConfiguration.cpp | 100 +++++++++++++++++-------- src/AmsConfiguration.h | 5 +- src/AmsData.cpp | 22 +++--- src/AmsData.h | 4 +- src/AmsToMqttBridge.h | 2 + src/AmsToMqttBridge.ino | 55 +++++++++----- src/HwTools.cpp | 12 ++- src/mqtt/JsonMqttHandler.cpp | 21 +++--- src/mqtt/RawMqttHandler.cpp | 8 +- src/web/AmsWebServer.cpp | 126 +++++++++++++++++++------------- web/foot.html | 2 +- web/gaugemeter.js | 13 +--- web/lowmem.html | 1 + web/price.html | 2 +- 17 files changed, 240 insertions(+), 158 deletions(-) create mode 100644 web/lowmem.html diff --git a/lib/HanReader/src/HanReader.cpp b/lib/HanReader/src/HanReader.cpp index 3469854f..8cef346f 100644 --- a/lib/HanReader/src/HanReader.cpp +++ b/lib/HanReader/src/HanReader.cpp @@ -284,11 +284,8 @@ time_t HanReader::getTime(byte *buffer, int start, int length, bool respectTimez int minute = buffer[pos + 6]; int second = buffer[pos + 7]; // 8: Hundredths - int tzMinutes = buffer[pos + 9] << 8 | buffer[pos + 10]; - bool dsc = (buffer[pos + 11] & 0x01) == 0x01; - - printD("Time offset: %d", tzMinutes); - printD(dsc ? "DSC" : "not DSC"); + int16_t tzMinutes = buffer[pos + 9] << 8 | buffer[pos + 10]; + bool dsc = (buffer[pos + 11] & 0x80) == 0x80; tmElements_t tm; tm.Year = year - 1970; @@ -297,7 +294,18 @@ time_t HanReader::getTime(byte *buffer, int start, int length, bool respectTimez tm.Hour = hour; tm.Minute = minute; tm.Second = second; - return localZone->toUTC(makeTime(tm)); + + time_t time = makeTime(tm); + if(respectTimezone && tzMinutes != 0x8000) { + time -= tzMinutes * 60; + if(respectDsc && dsc) + time -= 3600; + } else { + if(respectDsc && dsc) + time += 3600; + time = localZone->toUTC(time); + } + return time; } else if(dataLength == 0) { return (time_t)0L; } else { diff --git a/platformio.ini b/platformio.ini index 869c2e18..376528ca 100755 --- a/platformio.ini +++ b/platformio.ini @@ -9,7 +9,7 @@ lib_deps = file://lib/HanReader, file://lib/Timezone, MQTT@2.4.8, DallasTemperat [env:esp8266] platform = espressif8266@2.6.2 board = esp12e -board_build.ldscript = eagle.flash.4m3m.ld +board_build.ldscript = eagle.flash.4m2m.ld framework = ${common.framework} lib_deps = ${common.lib_deps} extra_scripts = @@ -19,6 +19,7 @@ extra_scripts = [env:esp32] platform = espressif32@2.1.0 board = esp32dev +board_build.partitions = no_ota.csv framework = ${common.framework} lib_deps = ${common.lib_deps} extra_scripts = diff --git a/scripts/makeweb.py b/scripts/makeweb.py index 2d82721e..eb2b04ed 100644 --- a/scripts/makeweb.py +++ b/scripts/makeweb.py @@ -50,7 +50,7 @@ for filename in os.listdir(webroot): dst.write(")==\"==\";\n") dst.write("const int "); dst.write(varname) - dst.write("_LEN = "); + dst.write("_LEN PROGMEM = "); dst.write(str(len(content))) dst.write(";"); \ No newline at end of file diff --git a/src/AmsConfiguration.cpp b/src/AmsConfiguration.cpp index f44c9074..ca3b54d9 100644 --- a/src/AmsConfiguration.cpp +++ b/src/AmsConfiguration.cpp @@ -473,35 +473,35 @@ void AmsConfiguration::ackEntsoeChange() { } void AmsConfiguration::clear() { + EEPROM.begin(EEPROM_SIZE); MeterConfig meter; clearMeter(meter); - setMeterConfig(meter); + EEPROM.put(CONFIG_METER_START, meter); WiFiConfig wifi; clearWifi(wifi); - setWiFiConfig(wifi); + EEPROM.put(CONFIG_WIFI_START, wifi); MqttConfig mqtt; clearMqtt(mqtt); - setMqttConfig(mqtt); + EEPROM.put(CONFIG_MQTT_START, mqtt); WebConfig web; clearAuth(web); - setWebConfig(web); + EEPROM.put(CONFIG_WEB_START, web); DomoticzConfig domo; clearDomo(domo); - setDomoticzConfig(domo); + EEPROM.put(CONFIG_DOMOTICZ_START, domo); NtpConfig ntp; clearNtp(ntp); - setNtpConfig(ntp); + EEPROM.put(CONFIG_NTP_START, domo); EntsoeConfig entsoe; clearEntsoe(entsoe); - setEntsoeConfig(entsoe); + EEPROM.put(CONFIG_ENTSOE_START, domo); - EEPROM.begin(EEPROM_SIZE); EEPROM.put(EEPROM_CONFIG_ADDRESS, -1); EEPROM.commit(); EEPROM.end(); @@ -547,10 +547,13 @@ int AmsConfiguration::getConfigVersion() { } void AmsConfiguration::loadTempSensors() { - this->tempSensors = new TempSensorConfig*[32]; + EEPROM.begin(EEPROM_SIZE); + TempSensorConfig* tempSensors[32]; int address = EEPROM_TEMP_CONFIG_ADDRESS; int c = 0; int storedCount = EEPROM.read(address++); + Serial.print("SEnsors: "); + Serial.println(storedCount); if(storedCount > 0 && storedCount <= 32) { for(int i = 0; i < storedCount; i++) { TempSensorConfig* tsc = new TempSensorConfig(); @@ -561,7 +564,12 @@ void AmsConfiguration::loadTempSensors() { address += sizeof(*tsc); } } + this->tempSensors = new TempSensorConfig*[c]; + for(int i = 0; i < c; i++) { + this->tempSensors[i] = tempSensors[i]; + } tempSensorCount = c; + EEPROM.end(); } void AmsConfiguration::saveTempSensors() { @@ -580,7 +588,14 @@ bool AmsConfiguration::loadConfig82(int address) { ConfigObject82 c; EEPROM.begin(EEPROM_SIZE); EEPROM.get(address, c); - EEPROM.end(); + + EntsoeConfig entsoe; + clearEntsoe(entsoe); + EEPROM.put(CONFIG_ENTSOE_START, entsoe); + + NtpConfig ntp; + clearNtp(ntp); + EEPROM.put(CONFIG_NTP_START, ntp); DomoticzConfig domo { c.domoELIDX, @@ -589,7 +604,7 @@ bool AmsConfiguration::loadConfig82(int address) { c.domoVL3IDX, c.domoCL1IDX }; - setDomoticzConfig(domo); + EEPROM.put(CONFIG_DOMOTICZ_START, domo); GpioConfig gpio { c.hanPin, @@ -607,14 +622,14 @@ bool AmsConfiguration::loadConfig82(int address) { c.vccMultiplier, c.vccBootLimit }; - setGpioConfig(gpio); + EEPROM.put(CONFIG_GPIO_START, gpio); DebugConfig debug { c.debugTelnet, c.debugSerial, c.debugLevel }; - setDebugConfig(debug); + EEPROM.put(CONFIG_DEBUG_START, debug); MeterConfig meter { c.meterType, @@ -625,14 +640,14 @@ bool AmsConfiguration::loadConfig82(int address) { {0}, c.substituteMissing }; - setMeterConfig(meter); + EEPROM.put(CONFIG_METER_START, meter); WebConfig web { c.authSecurity }; strcpy(web.username, c.authUser); strcpy(web.password, c.authPassword); - setWebConfig(web); + EEPROM.put(CONFIG_WEB_START, web); MqttConfig mqtt; strcpy(mqtt.host, c.mqttHost); @@ -644,7 +659,7 @@ bool AmsConfiguration::loadConfig82(int address) { strcpy(mqtt.password, c.mqttPassword); mqtt.payloadFormat = c.mqttPayloadFormat; mqtt.ssl = c.mqttSsl; - setMqttConfig(mqtt); + EEPROM.put(CONFIG_MQTT_START, mqtt); WiFiConfig wifi; strcpy(wifi.ssid, c.wifiSsid); @@ -656,20 +671,27 @@ bool AmsConfiguration::loadConfig82(int address) { strcpy(wifi.dns2, c.wifiDns2); strcpy(wifi.hostname, c.wifiHostname); wifi.mdns = true; - setWiFiConfig(wifi); + EEPROM.put(CONFIG_WIFI_START, wifi); SystemConfig sys { c.boardType }; - setSystemConfig(sys); - return save(); + EEPROM.put(CONFIG_SYSTEM_START, sys); + + EEPROM.put(EEPROM_CONFIG_ADDRESS, EEPROM_CHECK_SUM); + bool ret = EEPROM.commit(); + EEPROM.end(); + + return ret; } bool AmsConfiguration::loadConfig83(int address) { ConfigObject83 c; EEPROM.begin(EEPROM_SIZE); EEPROM.get(address, c); - EEPROM.end(); + + EntsoeConfig entsoe {"", "", "", 1000}; + EEPROM.put(CONFIG_ENTSOE_START, entsoe); NtpConfig ntp { c.ntpEnable, @@ -678,7 +700,7 @@ bool AmsConfiguration::loadConfig83(int address) { c.ntpSummerOffset }; strcpy(ntp.server, c.ntpServer); - setNtpConfig(ntp); + EEPROM.put(CONFIG_NTP_START, ntp); DomoticzConfig domo { c.domoELIDX, @@ -687,7 +709,7 @@ bool AmsConfiguration::loadConfig83(int address) { c.domoVL3IDX, c.domoCL1IDX }; - setDomoticzConfig(domo); + EEPROM.put(CONFIG_DOMOTICZ_START, domo); GpioConfig gpio { c.hanPin, @@ -705,14 +727,14 @@ bool AmsConfiguration::loadConfig83(int address) { c.vccMultiplier, c.vccBootLimit }; - setGpioConfig(gpio); + EEPROM.put(CONFIG_GPIO_START, gpio); DebugConfig debug { c.debugTelnet, c.debugSerial, c.debugLevel }; - setDebugConfig(debug); + EEPROM.put(CONFIG_DEBUG_START, debug); MeterConfig meter { c.meterType, @@ -725,14 +747,14 @@ bool AmsConfiguration::loadConfig83(int address) { }; memcpy(meter.encryptionKey, c.meterEncryptionKey, 16); memcpy(meter.authenticationKey, c.meterAuthenticationKey, 16); - setMeterConfig(meter); + EEPROM.put(CONFIG_METER_START, meter); WebConfig web { c.authSecurity }; strcpy(web.username, c.authUser); strcpy(web.password, c.authPassword); - setWebConfig(web); + EEPROM.put(CONFIG_WEB_START, web); MqttConfig mqtt; strcpy(mqtt.host, c.mqttHost); @@ -744,7 +766,7 @@ bool AmsConfiguration::loadConfig83(int address) { strcpy(mqtt.password, c.mqttPassword); mqtt.payloadFormat = c.mqttPayloadFormat; mqtt.ssl = c.mqttSsl; - setMqttConfig(mqtt); + EEPROM.put(CONFIG_MQTT_START, mqtt); WiFiConfig wifi; strcpy(wifi.ssid, c.wifiSsid); @@ -756,13 +778,18 @@ bool AmsConfiguration::loadConfig83(int address) { strcpy(wifi.dns2, c.wifiDns2); strcpy(wifi.hostname, c.wifiHostname); wifi.mdns = c.mDnsEnable; - setWiFiConfig(wifi); + EEPROM.put(CONFIG_WIFI_START, wifi); SystemConfig sys { c.boardType }; - setSystemConfig(sys); - return save(); + EEPROM.put(CONFIG_SYSTEM_START, sys); + + EEPROM.put(EEPROM_CONFIG_ADDRESS, EEPROM_CHECK_SUM); + bool ret = EEPROM.commit(); + EEPROM.end(); + + return ret; } bool AmsConfiguration::save() { @@ -801,12 +828,21 @@ void AmsConfiguration::updateTempSensorConfig(uint8_t address[8], const char nam } } if(!found) { + TempSensorConfig** tempSensors = new TempSensorConfig*[tempSensorCount+1]; + if(this->tempSensors != NULL) { + for(int i = 0;i < tempSensorCount; i++) { + tempSensors[i] = this->tempSensors[i]; + } + } TempSensorConfig *data = new TempSensorConfig(); memcpy(data->address, address, 8); strcpy(data->name, name); data->common = common; - tempSensors[tempSensorCount] = data; - tempSensorCount++; + tempSensors[tempSensorCount++] = data; + if(this->tempSensors != NULL) { + delete this->tempSensors; + } + this->tempSensors = tempSensors; } } diff --git a/src/AmsConfiguration.h b/src/AmsConfiguration.h index e464b108..25178136 100644 --- a/src/AmsConfiguration.h +++ b/src/AmsConfiguration.h @@ -298,6 +298,8 @@ public: bool isEntsoeChanged(); void ackEntsoeChange(); + void loadTempSensors(); + void saveTempSensors(); uint8_t getTempSensorCount(); TempSensorConfig* getTempSensorConfig(uint8_t address[8]); void updateTempSensorConfig(uint8_t address[8], const char name[32], bool common); @@ -316,9 +318,6 @@ private: uint8_t tempSensorCount = 0; TempSensorConfig** tempSensors; - void loadTempSensors(); - void saveTempSensors(); - bool loadConfig82(int address); bool loadConfig83(int address); diff --git a/src/AmsData.cpp b/src/AmsData.cpp index a83adf53..a6dd97e2 100644 --- a/src/AmsData.cpp +++ b/src/AmsData.cpp @@ -57,7 +57,7 @@ void AmsData::extractFromKaifa(HanReader& hanReader, uint8_t listSize) { case (uint8_t)Kaifa::List3PhaseShort: listId = hanReader.getString( (int)Kaifa_List3Phase::ListVersionIdentifier); meterId = hanReader.getString( (int)Kaifa_List3Phase::MeterID); - meterType = hanReader.getString( (int)Kaifa_List3Phase::MeterType); + meterModel = hanReader.getString( (int)Kaifa_List3Phase::MeterType); activeImportPower = hanReader.getUint( (int)Kaifa_List3Phase::ActiveImportPower); reactiveImportPower = hanReader.getUint( (int)Kaifa_List3Phase::ReactiveImportPower); activeExportPower = hanReader.getUint( (int)Kaifa_List3Phase::ActiveExportPower); @@ -78,7 +78,7 @@ void AmsData::extractFromKaifa(HanReader& hanReader, uint8_t listSize) { case (uint8_t)Kaifa::List1PhaseShort: listId = hanReader.getString( (int)Kaifa_List1Phase::ListVersionIdentifier); meterId = hanReader.getString( (int)Kaifa_List1Phase::MeterID); - meterType = hanReader.getString( (int)Kaifa_List1Phase::MeterType); + meterModel = hanReader.getString( (int)Kaifa_List1Phase::MeterType); activeImportPower = hanReader.getUint( (int)Kaifa_List1Phase::ActiveImportPower); reactiveImportPower = hanReader.getUint( (int)Kaifa_List1Phase::ReactiveImportPower); activeExportPower = hanReader.getUint( (int)Kaifa_List1Phase::ActiveExportPower); @@ -122,7 +122,7 @@ void AmsData::extractFromAidon(HanReader& hanReader, uint8_t listSize, bool subs case (uint8_t)Aidon::List3PhaseShort: listId = hanReader.getString( (uint8_t)Aidon_List3Phase::ListVersionIdentifier); meterId = hanReader.getString( (uint8_t)Aidon_List3Phase::MeterID); - meterType = hanReader.getString( (uint8_t)Aidon_List3Phase::MeterType); + meterModel = hanReader.getString( (uint8_t)Aidon_List3Phase::MeterType); activeImportPower = hanReader.getUint( (uint8_t)Aidon_List3Phase::ActiveImportPower); reactiveImportPower = hanReader.getUint( (uint8_t)Aidon_List3Phase::ReactiveImportPower); activeExportPower = hanReader.getUint( (uint8_t)Aidon_List3Phase::ActiveExportPower); @@ -143,7 +143,7 @@ void AmsData::extractFromAidon(HanReader& hanReader, uint8_t listSize, bool subs case (uint8_t)Aidon::List1PhaseShort: listId = hanReader.getString( (uint8_t)Aidon_List1Phase::ListVersionIdentifier); meterId = hanReader.getString( (uint8_t)Aidon_List1Phase::MeterID); - meterType = hanReader.getString( (uint8_t)Aidon_List1Phase::MeterType); + meterModel = hanReader.getString( (uint8_t)Aidon_List1Phase::MeterType); activeImportPower = hanReader.getUint( (uint8_t)Aidon_List1Phase::ActiveImportPower); reactiveImportPower = hanReader.getUint( (uint8_t)Aidon_List1Phase::ReactiveImportPower); activeExportPower = hanReader.getUint( (uint8_t)Aidon_List1Phase::ActiveExportPower); @@ -160,7 +160,7 @@ void AmsData::extractFromAidon(HanReader& hanReader, uint8_t listSize, bool subs case (uint8_t)Aidon::List3PhaseITShort: listId = hanReader.getString( (uint8_t)Aidon_List3PhaseIT::ListVersionIdentifier); meterId = hanReader.getString( (uint8_t)Aidon_List3PhaseIT::MeterID); - meterType = hanReader.getString( (uint8_t)Aidon_List3PhaseIT::MeterType); + meterModel = hanReader.getString( (uint8_t)Aidon_List3PhaseIT::MeterType); activeImportPower = hanReader.getUint( (uint8_t)Aidon_List3PhaseIT::ActiveImportPower); reactiveImportPower = hanReader.getUint( (uint8_t)Aidon_List3PhaseIT::ReactiveImportPower); activeExportPower = hanReader.getUint( (uint8_t)Aidon_List3PhaseIT::ActiveExportPower); @@ -205,7 +205,7 @@ void AmsData::extractFromKamstrup(HanReader& hanReader, uint8_t listSize, bool s case (uint8_t)Kamstrup::List1PhaseShort: listId = hanReader.getString( (uint8_t)Kamstrup_List1Phase::ListVersionIdentifier); meterId = hanReader.getString( (uint8_t)Kamstrup_List1Phase::MeterID); - meterType = hanReader.getString( (uint8_t)Kamstrup_List1Phase::MeterType); + meterModel = hanReader.getString( (uint8_t)Kamstrup_List1Phase::MeterType); activeImportPower = hanReader.getInt( (uint8_t)Kamstrup_List1Phase::ActiveImportPower); reactiveImportPower = hanReader.getInt( (uint8_t)Kamstrup_List1Phase::ReactiveImportPower); activeExportPower = hanReader.getInt( (uint8_t)Kamstrup_List1Phase::ActiveExportPower); @@ -222,7 +222,7 @@ void AmsData::extractFromKamstrup(HanReader& hanReader, uint8_t listSize, bool s case (uint8_t)Kamstrup::List3PhaseShort: listId = hanReader.getString( (uint8_t)Kamstrup_List3Phase::ListVersionIdentifier); meterId = hanReader.getString( (uint8_t)Kamstrup_List3Phase::MeterID); - meterType = hanReader.getString( (uint8_t)Kamstrup_List3Phase::MeterType); + meterModel = hanReader.getString( (uint8_t)Kamstrup_List3Phase::MeterType); activeImportPower = hanReader.getInt( (uint8_t)Kamstrup_List3Phase::ActiveImportPower); reactiveImportPower = hanReader.getInt( (uint8_t)Kamstrup_List3Phase::ReactiveImportPower); activeExportPower = hanReader.getInt( (uint8_t)Kamstrup_List3Phase::ActiveExportPower); @@ -243,7 +243,7 @@ void AmsData::extractFromKamstrup(HanReader& hanReader, uint8_t listSize, bool s case (uint8_t)Kamstrup::List3PhaseITShort: listId = hanReader.getString( (uint8_t)Kamstrup_List3Phase::ListVersionIdentifier); meterId = hanReader.getString( (uint8_t)Kamstrup_List3Phase::MeterID); - meterType = hanReader.getString( (uint8_t)Kamstrup_List3Phase::MeterType); + meterModel = hanReader.getString( (uint8_t)Kamstrup_List3Phase::MeterType); activeImportPower = hanReader.getInt( (uint8_t)Kamstrup_List3Phase::ActiveImportPower); reactiveImportPower = hanReader.getInt( (uint8_t)Kamstrup_List3Phase::ReactiveImportPower); activeExportPower = hanReader.getInt( (uint8_t)Kamstrup_List3Phase::ActiveExportPower); @@ -321,7 +321,7 @@ void AmsData::apply(AmsData& other) { case 2: this->listId = other.getListId(); this->meterId = other.getMeterId(); - this->meterType = other.getMeterType(); + this->meterModel = other.getMeterModel(); this->reactiveImportPower = other.getReactiveImportPower(); this->activeExportPower = other.getActiveExportPower(); this->reactiveExportPower = other.getReactiveExportPower(); @@ -357,8 +357,8 @@ String AmsData::getMeterId() { return this->meterId; } -String AmsData::getMeterType() { - return this->meterType; +String AmsData::getMeterModel() { + return this->meterModel; } time_t AmsData::getMeterTimestamp() { diff --git a/src/AmsData.h b/src/AmsData.h index 7647a743..7476fd3b 100644 --- a/src/AmsData.h +++ b/src/AmsData.h @@ -25,7 +25,7 @@ public: String getListId(); String getMeterId(); - String getMeterType(); + String getMeterModel(); time_t getMeterTimestamp(); @@ -53,7 +53,7 @@ private: unsigned long lastUpdateMillis = 0; uint8_t listType = 0; time_t packageTimestamp = 0; - String listId, meterId, meterType; + String listId, meterId, meterModel; time_t meterTimestamp = 0; uint16_t activeImportPower = 0, reactiveImportPower = 0, activeExportPower = 0, reactiveExportPower = 0; float l1voltage = 0, l2voltage = 0, l3voltage = 0, l1current = 0, l2current = 0, l3current = 0; diff --git a/src/AmsToMqttBridge.h b/src/AmsToMqttBridge.h index 3762cf90..284e599e 100644 --- a/src/AmsToMqttBridge.h +++ b/src/AmsToMqttBridge.h @@ -5,6 +5,8 @@ #define INVALID_BUTTON_PIN 0xFFFFFFFF +#define EPOCH_2021_01_01 1609459200 + #include #if defined(ESP8266) diff --git a/src/AmsToMqttBridge.ino b/src/AmsToMqttBridge.ino index bd93432d..a5c5f522 100644 --- a/src/AmsToMqttBridge.ino +++ b/src/AmsToMqttBridge.ino @@ -7,13 +7,13 @@ * electricity providers in other countries. It was originally based on ESP8266, but have also been * adapted to work with ESP32. * - * @author Roar Fredriksen (@roarfred) - * The original developer for this project - * https://github.com/roarfred/AmsToMqttBridge - * * @author Gunnar Skjold (@gskjold) * Maintainer of current code * https://github.com/gskjold/AmsToMqttBridge + * + * @author Roar Fredriksen (@roarfred) + * The original developer for this project + * https://github.com/roarfred/AmsToMqttBridge */ #include "AmsToMqttBridge.h" @@ -76,6 +76,7 @@ bool mqttEnabled = false; uint8_t payloadFormat = 0; String topic = "ams"; AmsData meterState; +bool ntpEnabled = false; void setup() { WiFiConfig wifi; @@ -123,6 +124,7 @@ void setup() { if(gpioConfig.apPin >= 0) pinMode(gpioConfig.apPin, INPUT_PULLUP); + config.loadTempSensors(); hw.setup(&gpioConfig, &config); hw.ledBlink(LED_INTERNAL, 1); hw.ledBlink(LED_RED, 1); @@ -206,7 +208,7 @@ void setup() { WiFi.forceSleepBegin(); #endif int i = 0; - while(hw.getVcc() < 3.2 && i < 3) { + while(hw.getVcc() > 1.0 && hw.getVcc() < 3.2 && i < 3) { if(Debug.isActive(RemoteDebug::INFO)) debugI(" vcc not optimal, light sleep 10s"); #if defined(ESP8266) delay(10000); @@ -221,6 +223,7 @@ void setup() { File firmwareFile = SPIFFS.open(FILE_FIRMWARE, "r"); debugD(" firmware size: %d", firmwareFile.size()); uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; + debugD(" available: %d", maxSketchSpace); if (!Update.begin(maxSketchSpace, U_FLASH)) { if(Debug.isActive(RemoteDebug::ERROR)) { debugE("Unable to start firmware update"); @@ -260,10 +263,9 @@ void setup() { NtpConfig ntp; if(config.getNtpConfig(ntp)) { - if(ntp.enable) { - configTime(ntp.offset*10, ntp.summerOffset*10, ntp.server); - sntp_servermode_dhcp(ntp.dhcp ? 1 : 0); - } + configTime(ntp.offset*10, ntp.summerOffset*10, ntp.enable ? ntp.server : ""); + sntp_servermode_dhcp(ntp.enable && ntp.dhcp ? 1 : 0); + ntpEnabled = ntp.enable; TimeChangeRule std = {"STD", Last, Sun, Oct, 3, ntp.offset / 6}; TimeChangeRule dst = {"DST", Last, Sun, Mar, 2, (ntp.offset + ntp.summerOffset) / 6}; tz = new Timezone(dst, std); @@ -376,16 +378,17 @@ void loop() { } if(config.isNtpChanged()) { NtpConfig ntp; - if(config.getNtpConfig(ntp) && ntp.enable && strlen(ntp.server) > 0) { - configTime(ntp.offset*10, ntp.summerOffset*10, ntp.server); - sntp_servermode_dhcp(ntp.dhcp ? 1 : 0); - } + if(config.getNtpConfig(ntp)) { + configTime(ntp.offset*10, ntp.summerOffset*10, ntp.enable ? ntp.server : ""); + sntp_servermode_dhcp(ntp.enable && ntp.dhcp ? 1 : 0); + ntpEnabled = ntp.enable; - if(tz != NULL) delete tz; - TimeChangeRule std = {"STD", Last, Sun, Oct, 3, ntp.offset / 6}; - TimeChangeRule dst = {"DST", Last, Sun, Mar, 2, (ntp.offset + ntp.summerOffset) / 6}; - tz = new Timezone(dst, std); - ws.setTimezone(tz); + if(tz != NULL) delete tz; + TimeChangeRule std = {"STD", Last, Sun, Oct, 3, ntp.offset / 6}; + TimeChangeRule dst = {"DST", Last, Sun, Mar, 2, (ntp.offset + ntp.summerOffset) / 6}; + tz = new Timezone(dst, std); + ws.setTimezone(tz); + } config.ackNtpChange(); } @@ -609,12 +612,26 @@ void readHanPort() { if(data.getListType() > 0) { if(mqttEnabled && mqttHandler != NULL) { if(mqttHandler->publish(&data, &meterState)) { - if(eapi != NULL && data.getListType() == 3) { + if(data.getListType() == 3 && eapi != NULL) { mqttHandler->publishPrices(eapi); } if(data.getListType() >= 2) { mqttHandler->publishSystem(&hw); } + time_t now = time(nullptr); + if(now < EPOCH_2021_01_01 || data.getListType() == 3) { + if(data.getMeterTimestamp() > EPOCH_2021_01_01 || !ntpEnabled) { + debugI("Using timestamp from meter"); + now = data.getMeterTimestamp(); + } else if(data.getPackageTimestamp() > EPOCH_2021_01_01) { + debugI("Using timestamp from meter (DLMS)"); + now = data.getPackageTimestamp(); + } + if(now > EPOCH_2021_01_01) { + timeval tv { now, 0}; + settimeofday(&tv, nullptr); + } + } } mqtt.loop(); delay(10); diff --git a/src/HwTools.cpp b/src/HwTools.cpp index db44c373..22ba78df 100644 --- a/src/HwTools.cpp +++ b/src/HwTools.cpp @@ -4,8 +4,6 @@ void HwTools::setup(GpioConfig* config, AmsConfiguration* amsConf) { this->config = config; this->amsConf = amsConf; this->tempSensorInit = false; - if(this->tempSensors == NULL) - this->tempSensors = new TempSensorData*[32]; if(sensorApi != NULL) delete sensorApi; if(oneWire != NULL) @@ -81,7 +79,10 @@ uint8_t HwTools::getTempSensorCount() { } TempSensorData* HwTools::getTempSensorData(uint8_t i) { - return tempSensors[i]; + if(i < sensorCount) { + return tempSensors[i]; + } + return NULL; } bool HwTools::updateTemperatures() { @@ -96,6 +97,10 @@ bool HwTools::updateTemperatures() { DeviceAddress addr; sensorApi->requestTemperatures(); int c = sensorApi->getDeviceCount(); + if(this->tempSensors != NULL) { + delete this->tempSensors; + } + this->tempSensors = new TempSensorData*[c]; for(int i = 0; i < c; i++) { bool found = false; sensorApi->getAddress(addr, i); @@ -119,7 +124,6 @@ bool HwTools::updateTemperatures() { data->changed = data->lastValidRead != t; data->lastValidRead = t; } - tempSensors[sensorCount++] = data; } delay(10); diff --git a/src/mqtt/JsonMqttHandler.cpp b/src/mqtt/JsonMqttHandler.cpp index 2536801f..b6d40f99 100644 --- a/src/mqtt/JsonMqttHandler.cpp +++ b/src/mqtt/JsonMqttHandler.cpp @@ -36,7 +36,7 @@ bool JsonMqttHandler::publish(AmsData* data, AmsData* previousState) { hw->getTemperature(), data->getListId().c_str(), data->getMeterId().c_str(), - data->getMeterType().c_str(), + data->getMeterModel().c_str(), data->getActiveImportPower(), data->getReactiveImportPower(), data->getActiveExportPower(), @@ -61,7 +61,7 @@ bool JsonMqttHandler::publish(AmsData* data, AmsData* previousState) { hw->getTemperature(), data->getListId().c_str(), data->getMeterId().c_str(), - data->getMeterType().c_str(), + data->getMeterModel().c_str(), data->getActiveImportPower(), data->getReactiveImportPower(), data->getActiveExportPower(), @@ -94,14 +94,15 @@ bool JsonMqttHandler::publishTemperatures(AmsConfiguration* config, HwTools* hw) for(int i = 0; i < count; i++) { TempSensorData* data = hw->getTempSensorData(i); - TempSensorConfig* conf = config->getTempSensorConfig(data->address); - char* pos = buf+strlen(buf); - snprintf(pos, 26, "\"%s\":%.2f,", - toHex(data->address, 8).c_str(), - data->lastRead - ); - data->changed = false; - delay(1); + if(data != NULL) { + char* pos = buf+strlen(buf); + snprintf(pos, 26, "\"%s\":%.2f,", + toHex(data->address, 8).c_str(), + data->lastRead + ); + data->changed = false; + delay(1); + } } char* pos = buf+strlen(buf); snprintf(count == 0 ? pos : pos-1, 8, "}}"); diff --git a/src/mqtt/RawMqttHandler.cpp b/src/mqtt/RawMqttHandler.cpp index 36aa5378..bba597f2 100644 --- a/src/mqtt/RawMqttHandler.cpp +++ b/src/mqtt/RawMqttHandler.cpp @@ -13,7 +13,7 @@ bool RawMqttHandler::publish(AmsData* data, AmsData* meterState) { case 3: // ID and type belongs to List 2, but I see no need to send that every 10s mqtt->publish(topic + "/meter/id", data->getMeterId()); - mqtt->publish(topic + "/meter/type", data->getMeterType()); + mqtt->publish(topic + "/meter/type", data->getMeterModel()); mqtt->publish(topic + "/meter/clock", String(data->getMeterTimestamp())); mqtt->publish(topic + "/meter/import/reactive/accumulated", String(data->getReactiveImportCounter(), 2)); mqtt->publish(topic + "/meter/import/active/accumulated", String(data->getActiveImportCounter(), 2)); @@ -24,8 +24,8 @@ bool RawMqttHandler::publish(AmsData* data, AmsData* meterState) { if(full || meterState->getMeterId() != data->getMeterId()) { mqtt->publish(topic + "/meter/id", data->getMeterId()); } - if(full || meterState->getMeterType() != data->getMeterType()) { - mqtt->publish(topic + "/meter/type", data->getMeterType()); + if(full || meterState->getMeterModel() != data->getMeterModel()) { + mqtt->publish(topic + "/meter/type", data->getMeterModel()); } if(full || meterState->getL1Current() != data->getL1Current()) { mqtt->publish(topic + "/meter/l1/current", String(data->getL1Current(), 2)); @@ -66,7 +66,7 @@ bool RawMqttHandler::publishTemperatures(AmsConfiguration* config, HwTools* hw) uint8_t c = hw->getTempSensorCount(); for(int i = 0; i < c; i++) { TempSensorData* data = hw->getTempSensorData(i); - if(data->lastValidRead > -85) { + if(data != NULL && data->lastValidRead > -85) { if(data->changed || full) { mqtt->publish(topic + "/temperature/" + toHex(data->address), String(data->lastValidRead, 2)); data->changed = false; diff --git a/src/web/AmsWebServer.cpp b/src/web/AmsWebServer.cpp index 122861a9..fc81b161 100644 --- a/src/web/AmsWebServer.cpp +++ b/src/web/AmsWebServer.cpp @@ -30,6 +30,7 @@ #include "root/notfound_html.h" #include "root/data_json.h" #include "root/tempsensor_json.h" +#include "root/lowmem_html.h" #include "base64.h" @@ -45,9 +46,12 @@ void AmsWebServer::setup(AmsConfiguration* config, GpioConfig* gpioConfig, Meter this->meterState = meterState; this->mqtt = mqtt; + char jsuri[32]; + snprintf(jsuri, 32, "/application-%s.js", VERSION); + server.on("/", HTTP_GET, std::bind(&AmsWebServer::indexHtml, this)); server.on("/", HTTP_POST, std::bind(&AmsWebServer::handleSetup, this)); - server.on("/application.js", HTTP_GET, std::bind(&AmsWebServer::applicationJs, this)); + server.on(jsuri, HTTP_GET, std::bind(&AmsWebServer::applicationJs, this)); server.on("/temperature", HTTP_GET, std::bind(&AmsWebServer::temperature, this)); server.on("/temperature", HTTP_POST, std::bind(&AmsWebServer::temperaturePost, this)); server.on("/temperature.json", HTTP_GET, std::bind(&AmsWebServer::temperatureJson, this)); @@ -125,7 +129,6 @@ void AmsWebServer::loop() { bool AmsWebServer::checkSecurity(byte level) { bool access = WiFi.getMode() == WIFI_AP || webConfig.security < level; if(!access && webConfig.security >= level && server.hasHeader("Authorization")) { - printD(" forcing web security"); String expectedAuth = String(webConfig.username) + ":" + String(webConfig.password); String providedPwd = server.header("Authorization"); @@ -136,15 +139,10 @@ bool AmsWebServer::checkSecurity(byte level) { } if(!access) { - printD(" no access, requesting user/pass"); server.sendHeader("WWW-Authenticate", "Basic realm=\"Secure Area\""); server.setContentLength(0); server.send(401, "text/html", ""); } - if(access) - printD(" access granted"); - else - printD(" access denied"); return access; } @@ -183,10 +181,15 @@ void AmsWebServer::temperaturePost() { delay(1); } + if (debugger->isActive(RemoteDebug::DEBUG)) config->print(debugger); if(config->save()) { printD("Successfully saved temperature sensors"); server.sendHeader("Location", String("/temperature"), true); server.send (302, "text/plain", ""); + } else { + printE("Error saving configuration"); + String html = "

Error saving configuration!

"; + server.send(500, "text/html", html); } } @@ -204,6 +207,8 @@ void AmsWebServer::temperatureJson() { for(int i = 0; i < count; i++) { TempSensorData* data = hw->getTempSensorData(i); + if(data == NULL) continue; + TempSensorConfig* conf = config->getTempSensorConfig(data->address); char* pos = buf+strlen(buf); snprintf_P(pos, 72, TEMPSENSOR_JSON, @@ -236,30 +241,38 @@ void AmsWebServer::price() { server.sendHeader("Pragma", "no-cache"); server.sendHeader("Expires", "-1"); - String html = String((const __FlashStringHelper*) PRICE_HTML); - for(int i = 0; i < 24; i++) { - tmElements_t tm; - breakTime(tz->toLocal(time(nullptr)) + (SECS_PER_HOUR * i), tm); - char ts[5]; - sprintf(ts, "%02d:00", tm.Hour); - html.replace("${time" + String(i) + "}", String(ts)); + if(ESP.getFreeHeap() > 25000) { + String html = String((const __FlashStringHelper*) PRICE_HTML); + for(int i = 0; i < 24; i++) { + tmElements_t tm; + breakTime(tz->toLocal(time(nullptr)) + (SECS_PER_HOUR * i), tm); + char ts[5]; + sprintf(ts, "%02d:00", tm.Hour); + html.replace("${time" + String(i) + "}", String(ts)); - if(eapi != NULL) { - double price = eapi->getValueForHour(i); - if(price == ENTSOE_NO_VALUE) { - html.replace("${price" + String(i) + "}", "--"); + if(eapi != NULL) { + double price = eapi->getValueForHour(i); + if(price == ENTSOE_NO_VALUE) { + html.replace("${price" + String(i) + "}", "--"); + } else { + html.replace("${price" + String(i) + "}", String(price, 4)); + } } else { - html.replace("${price" + String(i) + "}", String(price, 4)); + html.replace("${price" + String(i) + "}", "--"); } - } else { - html.replace("${price" + String(i) + "}", "--"); } + + server.setContentLength(html.length() + HEAD_HTML_LEN + FOOT_HTML_LEN); + server.send_P(200, "text/html", HEAD_HTML); + server.sendContent(html); + server.sendContent_P(FOOT_HTML); + } else { + server.setContentLength(LOWMEM_HTML_LEN + HEAD_HTML_LEN + FOOT_HTML_LEN); + server.send_P(200, "text/html", HEAD_HTML); + server.sendContent_P(LOWMEM_HTML); + server.sendContent_P(FOOT_HTML); } - server.setContentLength(html.length() + HEAD_HTML_LEN + FOOT_HTML_LEN); - server.send_P(200, "text/html", HEAD_HTML); - server.sendContent(html); - server.sendContent_P(FOOT_HTML); } void AmsWebServer::indexHtml() { @@ -525,34 +538,41 @@ void AmsWebServer::configEntsoeHtml() { EntsoeConfig entsoe; config->getEntsoeConfig(entsoe); - String html = String((const __FlashStringHelper*) ENTSOE_HTML); + if(ESP.getFreeHeap() > 25000) { + String html = String((const __FlashStringHelper*) ENTSOE_HTML); - html.replace("{et}", entsoe.token); - html.replace("{em}", String(entsoe.multiplier / 1000.0, 3)); + html.replace("{et}", entsoe.token); + html.replace("{em}", String(entsoe.multiplier / 1000.0, 3)); - html.replace("{eaNo1}", strcmp(entsoe.area, "10YNO-1--------2") == 0 ? "selected" : ""); - html.replace("{eaNo2}", strcmp(entsoe.area, "10YNO-2--------T") == 0 ? "selected" : ""); - html.replace("{eaNo3}", strcmp(entsoe.area, "10YNO-3--------J") == 0 ? "selected" : ""); - html.replace("{eaNo4}", strcmp(entsoe.area, "10YNO-4--------9") == 0 ? "selected" : ""); - html.replace("{eaNo5}", strcmp(entsoe.area, "10Y1001A1001A48H") == 0 ? "selected" : ""); + html.replace("{eaNo1}", strcmp(entsoe.area, "10YNO-1--------2") == 0 ? "selected" : ""); + html.replace("{eaNo2}", strcmp(entsoe.area, "10YNO-2--------T") == 0 ? "selected" : ""); + html.replace("{eaNo3}", strcmp(entsoe.area, "10YNO-3--------J") == 0 ? "selected" : ""); + html.replace("{eaNo4}", strcmp(entsoe.area, "10YNO-4--------9") == 0 ? "selected" : ""); + html.replace("{eaNo5}", strcmp(entsoe.area, "10Y1001A1001A48H") == 0 ? "selected" : ""); - html.replace("{eaSe1}", strcmp(entsoe.area, "10Y1001A1001A44P") == 0 ? "selected" : ""); - html.replace("{eaSe2}", strcmp(entsoe.area, "10Y1001A1001A45N") == 0 ? "selected" : ""); - html.replace("{eaSe3}", strcmp(entsoe.area, "10Y1001A1001A46L") == 0 ? "selected" : ""); - html.replace("{eaSe4}", strcmp(entsoe.area, "10Y1001A1001A47J") == 0 ? "selected" : ""); + html.replace("{eaSe1}", strcmp(entsoe.area, "10Y1001A1001A44P") == 0 ? "selected" : ""); + html.replace("{eaSe2}", strcmp(entsoe.area, "10Y1001A1001A45N") == 0 ? "selected" : ""); + html.replace("{eaSe3}", strcmp(entsoe.area, "10Y1001A1001A46L") == 0 ? "selected" : ""); + html.replace("{eaSe4}", strcmp(entsoe.area, "10Y1001A1001A47J") == 0 ? "selected" : ""); - html.replace("{eaDk1}", strcmp(entsoe.area, "10YDK-1--------W") == 0 ? "selected" : ""); - html.replace("{eaDk2}", strcmp(entsoe.area, "10YDK-2--------M") == 0 ? "selected" : ""); + html.replace("{eaDk1}", strcmp(entsoe.area, "10YDK-1--------W") == 0 ? "selected" : ""); + html.replace("{eaDk2}", strcmp(entsoe.area, "10YDK-2--------M") == 0 ? "selected" : ""); - html.replace("{ecNOK}", strcmp(entsoe.area, "NOK") == 0 ? "selected" : ""); - html.replace("{ecSEK}", strcmp(entsoe.area, "SEK") == 0 ? "selected" : ""); - html.replace("{ecDKK}", strcmp(entsoe.area, "DKK") == 0 ? "selected" : ""); - html.replace("{ecEUR}", strcmp(entsoe.area, "EUR") == 0 ? "selected" : ""); + html.replace("{ecNOK}", strcmp(entsoe.area, "NOK") == 0 ? "selected" : ""); + html.replace("{ecSEK}", strcmp(entsoe.area, "SEK") == 0 ? "selected" : ""); + html.replace("{ecDKK}", strcmp(entsoe.area, "DKK") == 0 ? "selected" : ""); + html.replace("{ecEUR}", strcmp(entsoe.area, "EUR") == 0 ? "selected" : ""); - server.setContentLength(html.length() + HEAD_HTML_LEN + FOOT_HTML_LEN); - server.send_P(200, "text/html", HEAD_HTML); - server.sendContent(html); - server.sendContent_P(FOOT_HTML); + server.setContentLength(html.length() + HEAD_HTML_LEN + FOOT_HTML_LEN); + server.send_P(200, "text/html", HEAD_HTML); + server.sendContent(html); + server.sendContent_P(FOOT_HTML); + } else { + server.setContentLength(LOWMEM_HTML_LEN + HEAD_HTML_LEN + FOOT_HTML_LEN); + server.send_P(200, "text/html", HEAD_HTML); + server.sendContent_P(LOWMEM_HTML); + server.sendContent_P(FOOT_HTML); + } } void AmsWebServer::configWebHtml() { @@ -1185,7 +1205,6 @@ void AmsWebServer::uploadPost() { void AmsWebServer::uploadFile(const char* path) { HTTPUpload& upload = server.upload(); if(upload.status == UPLOAD_FILE_START){ - String filename = upload.filename; if(uploading) { printE("Upload already in progress"); String html = "

Upload already in progress!

"; @@ -1196,18 +1215,23 @@ void AmsWebServer::uploadFile(const char* path) { server.send(500, "text/html", html); } else { uploading = true; - printD("handleFileUpload Name: %s", filename.c_str()); + if(debugger->isActive(RemoteDebug::DEBUG)) { + debugger->printf("handleFileUpload file: %s\n", path); + } file = SPIFFS.open(path, "w"); - filename = String(); + file.write(upload.buf, upload.currentSize); } } else if(upload.status == UPLOAD_FILE_WRITE) { if(file) file.write(upload.buf, upload.currentSize); } else if(upload.status == UPLOAD_FILE_END) { if(file) { + file.flush(); file.close(); SPIFFS.end(); - printD("handleFileUpload Size: %d", upload.totalSize); + if(debugger->isActive(RemoteDebug::DEBUG)) { + debugger->printf("handleFileUpload Size: %lu\n", upload.totalSize); + } } else { server.send(500, "text/plain", "500: couldn't create file"); } diff --git a/web/foot.html b/web/foot.html index 784a76c7..4e5f1a17 100644 --- a/web/foot.html +++ b/web/foot.html @@ -7,7 +7,7 @@ - + diff --git a/web/gaugemeter.js b/web/gaugemeter.js index 6978a738..cf16e6a7 100644 --- a/web/gaugemeter.js +++ b/web/gaugemeter.js @@ -55,18 +55,7 @@ "LightGreen-DarkGreen" === option.theme && (e > 0 && (t = "#3afc00"), e > 10 && (t = "#39f900"), e > 20 && (t = "#38f600"), e > 30 && (t = "#38f100"), e > 40 && (t = "#37ec00"), e > 50 && (t = "#36e700"), e > 60 && (t = "#34e200"), e > 70 && (t = "#34df00"), e > 80 && (t = "#33db00"), e > 90 && (t = "#32d900")), "DarkGold-LightGold" === option.theme && (e > 0 && (t = "#ffb800"), e > 10 && (t = "#ffba00"), e > 20 && (t = "#ffbd00"), e > 30 && (t = "#ffc200"), e > 40 && (t = "#ffc600"), e > 50 && (t = "#ffcb00"), e > 60 && (t = "#ffcf00"), e > 70 && (t = "#ffd400"), e > 80 && (t = "#ffd600"), e > 90 && (t = "#ffd900")), "LightGold-DarkGold" === option.theme && (e > 0 && (t = "#ffd900"), e > 10 && (t = "#ffd600"), e > 20 && (t = "#ffd400"), e > 30 && (t = "#ffcf00"), e > 40 && (t = "#ffcb00"), e > 50 && (t = "#ffc600"), e > 60 && (t = "#ffc200"), e > 70 && (t = "#ffbd00"), e > 80 && (t = "#ffba00"), e > 90 && (t = "#ffb800")), - "Voltage" === option.theme && ( - e > 0 && (t = "#d90000"), - e > 10 && (t = "#f35100"), - e > 20 && (t = "#ffb800"), - e > 30 && (t = "#a6d900"), - e > 40 && (t = "#32d900"), - e > 50 && (t = "#32d900"), - e > 60 && (t = "#a6d900"), - e > 70 && (t = "#ffb800"), - e > 80 && (t = "#f35100"), - e > 90 && (t = "#d90000") - ), + "Voltage" === option.theme && (e <= 0 && (t = "#d90000"), e > 0 && (t = "#e32100"), e > 10 && (t = "#ffb800"), e > 20 && (t = "#dcd800"), e > 30 && (t = "#32d900"), e > 40 && (t = "#32d900"), e > 50 && (t = "#32d900"), e > 60 && (t = "#32d900"), e > 70 && (t = "#dcd800"), e > 80 && (t = "#ffb800"), e > 90 && (t = "#e32100"), e >= 100 && (t = "#d90000")), "White" === option.theme && (t = "#fff"), "Black" === option.theme && (t = "#000"), t; diff --git a/web/lowmem.html b/web/lowmem.html new file mode 100644 index 00000000..f722449b --- /dev/null +++ b/web/lowmem.html @@ -0,0 +1 @@ +
There is currently not enough available heap space on your device to support this feature. If you are using ESP8266, you can consider switching to ESP32 to get enable this feature
diff --git a/web/price.html b/web/price.html index d172861b..74d9d194 100644 --- a/web/price.html +++ b/web/price.html @@ -1,5 +1,5 @@
-

Price retrieval requires ENTSO-E API and NTP to be configured and working

+

Price retrieval requires ENTSO-E API to be configured and valid timestamp available. The timestamp can either come from a working NTP configuration or valid timestamp from the meter.