Changes after testing

This commit is contained in:
Gunnar Skjold
2021-01-22 08:01:23 +01:00
parent ff84278edf
commit 54fb950513
17 changed files with 240 additions and 158 deletions

View File

@@ -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 {

View File

@@ -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 =

View File

@@ -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(";");

View File

@@ -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;
}
}

View File

@@ -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);

View File

@@ -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() {

View File

@@ -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;

View File

@@ -5,6 +5,8 @@
#define INVALID_BUTTON_PIN 0xFFFFFFFF
#define EPOCH_2021_01_01 1609459200
#include <SoftwareSerial.h>
#if defined(ESP8266)

View File

@@ -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);

View File

@@ -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);

View File

@@ -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, "}}");

View File

@@ -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;

View File

@@ -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 = "<html><body><h1>Error saving configuration!</h1></form>";
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 = "<html><body><h1>Upload already in progress!</h1></form>";
@@ -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");
}

View File

@@ -7,7 +7,7 @@
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"></script>
<script src="gaugemeter.js"></script>
<script src="application.js"></script>
<script src="application-${version}.js"></script>
</body>
</html>

View File

@@ -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;

1
web/lowmem.html Normal file
View File

@@ -0,0 +1 @@
<div class="alert alert-info">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</div>

View File

@@ -1,5 +1,5 @@
<div id="sensors" class="my-3 p-3 bg-white rounded shadow">
<p>Price retrieval requires ENTSO-E API and NTP to be configured and working</p>
<p>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.</p>
<div class="row">
<div class="col-lg-2 col-md-3 col-sm-4">
<div class="row">