Added configuration of GPIO in UI. Added initial setup page in AP mode. Major changes in storing configuration.

This commit is contained in:
Gunnar Skjold
2020-05-03 16:29:38 +02:00
parent 1ea9da22c7
commit 2858123c1b
23 changed files with 1472 additions and 1232 deletions

View File

@@ -1,7 +1,10 @@
#include "HanReader.h" #include "HanReader.h"
HanReader::HanReader() HanReader::HanReader() {
{ // Central European Time (Frankfurt, Paris)
TimeChangeRule CEST = {"CEST", Last, Sun, Mar, 2, 120}; // Central European Summer Time
TimeChangeRule CET = {"CET ", Last, Sun, Oct, 3, 60}; // Central European Standard Time
localZone = new Timezone(CEST, CET);
} }
void HanReader::setup(Stream *hanPort, RemoteDebug *debug) void HanReader::setup(Stream *hanPort, RemoteDebug *debug)
@@ -10,11 +13,6 @@ void HanReader::setup(Stream *hanPort, RemoteDebug *debug)
bytesRead = 0; bytesRead = 0;
debugger = debug; debugger = debug;
// Central European Time (Frankfurt, Paris)
TimeChangeRule CEST = {"CEST", Last, Sun, Mar, 2, 120}; // Central European Summer Time
TimeChangeRule CET = {"CET ", Last, Sun, Oct, 3, 60}; // Central European Standard Time
localZone = new Timezone(CEST, CET);
if (debug) debug->println("MBUS serial setup complete"); if (debug) debug->println("MBUS serial setup complete");
} }

View File

@@ -1,6 +1,7 @@
import os import os
import re import re
import shutil import shutil
from css_html_js_minify import html_minify, js_minify, css_minify
webroot = "web" webroot = "web"
srcroot = "src/web/root" srcroot = "src/web/root"
@@ -20,11 +21,19 @@ for filename in os.listdir(webroot):
varname = basename.upper() varname = basename.upper()
with open(srcfile, encoding="utf-8") as f:
content = f.read()
if filename.endswith(".html"):
content = html_minify(content)
elif filename.endswith(".css"):
content = css_minify(content)
elif filename.endswith(".js") and filename != 'gaugemeter.js':
content = js_minify(content)
with open(dstfile, "w") as dst: with open(dstfile, "w") as dst:
dst.write("const char ") dst.write("const char ")
dst.write(varname) dst.write(varname)
dst.write("[] PROGMEM = R\"==\"==(\n") dst.write("[] PROGMEM = R\"==\"==(\n")
with open(srcfile, "r") as src: dst.write(content)
for line in src.readlines():
dst.write(line)
dst.write("\n)==\"==\";\n") dst.write("\n)==\"==\";\n")

View File

@@ -1,75 +1,75 @@
#include "AmsConfiguration.h" #include "AmsConfiguration.h"
String AmsConfiguration::getWifiSsid() { char* AmsConfiguration::getWifiSsid() {
return wifiSsid; return config.wifiSsid;
} }
void AmsConfiguration::setWifiSsid(String wifiSsid) { void AmsConfiguration::setWifiSsid(const char* wifiSsid) {
wifiChanged |= this->wifiSsid != wifiSsid; wifiChanged |= strcmp(config.wifiSsid, wifiSsid) != 0;
this->wifiSsid = String(wifiSsid); strcpy(config.wifiSsid, wifiSsid);
} }
String AmsConfiguration::getWifiPassword() { char* AmsConfiguration::getWifiPassword() {
return wifiPassword; return config.wifiPassword;
} }
void AmsConfiguration::setWifiPassword(String wifiPassword) { void AmsConfiguration::setWifiPassword(const char* wifiPassword) {
wifiChanged |= this->wifiPassword != wifiPassword; wifiChanged |= strcmp(config.wifiPassword, wifiPassword) != 0;
this->wifiPassword = String(wifiPassword); strcpy(config.wifiPassword, wifiPassword);
} }
String AmsConfiguration::getWifiIp() { char* AmsConfiguration::getWifiIp() {
return wifiIp; return config.wifiIp;
} }
void AmsConfiguration::setWifiIp(String wifiIp) { void AmsConfiguration::setWifiIp(const char* wifiIp) {
wifiChanged |= this->wifiIp != wifiIp; wifiChanged |= strcmp(config.wifiIp, wifiIp) != 0;
this->wifiIp = String(wifiIp); strcpy(config.wifiIp, wifiIp);
} }
String AmsConfiguration::getWifiGw() { char* AmsConfiguration::getWifiGw() {
return wifiGw; return config.wifiGw;
} }
void AmsConfiguration::setWifiGw(String wifiGw) { void AmsConfiguration::setWifiGw(const char* wifiGw) {
wifiChanged |= this->wifiGw != wifiGw; wifiChanged |= strcmp(config.wifiGw, wifiGw) != 0;
this->wifiGw = String(wifiGw); strcpy(config.wifiGw, wifiGw);
} }
String AmsConfiguration::getWifiSubnet() { char* AmsConfiguration::getWifiSubnet() {
return wifiSubnet; return config.wifiSubnet;
} }
void AmsConfiguration::setWifiSubnet(String wifiSubnet) { void AmsConfiguration::setWifiSubnet(const char* wifiSubnet) {
wifiChanged |= this->wifiSubnet != wifiSubnet; wifiChanged |= strcmp(config.wifiSubnet, wifiSubnet) != 0;
this->wifiSubnet = String(wifiSubnet); strcpy(config.wifiSubnet, wifiSubnet);
} }
String AmsConfiguration::getWifiDns1() { char* AmsConfiguration::getWifiDns1() {
return wifiDns1; return config.wifiDns1;
} }
void AmsConfiguration::setWifiDns1(String wifiDns1) { void AmsConfiguration::setWifiDns1(const char* wifiDns1) {
wifiChanged |= this->wifiDns1 != wifiDns1; wifiChanged |= strcmp(config.wifiDns1, wifiDns1) != 0;
this->wifiDns1 = wifiDns1; strcpy(config.wifiDns1, wifiDns1);
} }
String AmsConfiguration::getWifiDns2() { char* AmsConfiguration::getWifiDns2() {
return wifiDns2; return config.wifiDns2;
} }
void AmsConfiguration::setWifiDns2(String wifiDns2) { void AmsConfiguration::setWifiDns2(const char* wifiDns2) {
wifiChanged |= this->wifiDns2 != wifiDns2; wifiChanged |= strcmp(config.wifiDns2, wifiDns2) != 0;
this->wifiDns2 = wifiDns2; strcpy(config.wifiDns2, wifiDns2);
} }
String AmsConfiguration::getWifiHostname() { char* AmsConfiguration::getWifiHostname() {
return wifiHostname; return config.wifiHostname;
} }
void AmsConfiguration::setWifiHostname(String wifiHostname) { void AmsConfiguration::setWifiHostname(const char* wifiHostname) {
wifiChanged |= this->wifiHostname != wifiHostname; wifiChanged |= strcmp(config.wifiHostname, wifiHostname) != 0;
this->wifiHostname = wifiHostname; strcpy(config.wifiHostname, wifiHostname);
} }
void AmsConfiguration::clearWifiIp() { void AmsConfiguration::clearWifiIp() {
@@ -89,84 +89,84 @@ void AmsConfiguration::ackWifiChange() {
} }
String AmsConfiguration::getMqttHost() { char* AmsConfiguration::getMqttHost() {
return mqttHost; return config.mqttHost;
} }
void AmsConfiguration::setMqttHost(String mqttHost) { void AmsConfiguration::setMqttHost(const char* mqttHost) {
mqttChanged |= this->mqttHost != mqttHost; mqttChanged |= strcmp(config.mqttHost, mqttHost) != 0;
this->mqttHost = String(mqttHost); strcpy(config.mqttHost, mqttHost);
} }
int AmsConfiguration::getMqttPort() { uint16_t AmsConfiguration::getMqttPort() {
return mqttPort; return config.mqttPort;
} }
void AmsConfiguration::setMqttPort(int mqttPort) { void AmsConfiguration::setMqttPort(uint16_t mqttPort) {
mqttChanged |= this->mqttPort != mqttPort; mqttChanged |= config.mqttPort != mqttPort;
this->mqttPort = mqttPort; config.mqttPort = mqttPort;
} }
String AmsConfiguration::getMqttClientId() { char* AmsConfiguration::getMqttClientId() {
return mqttClientId; return config.mqttClientId;
} }
void AmsConfiguration::setMqttClientId(String mqttClientId) { void AmsConfiguration::setMqttClientId(const char* mqttClientId) {
mqttChanged |= this->mqttClientId != mqttClientId; mqttChanged |= strcmp(config.mqttClientId, mqttClientId) != 0;
this->mqttClientId = String(mqttClientId); strcpy(config.mqttClientId, mqttClientId);
} }
String AmsConfiguration::getMqttPublishTopic() { char* AmsConfiguration::getMqttPublishTopic() {
return mqttPublishTopic; return config.mqttPublishTopic;
} }
void AmsConfiguration::setMqttPublishTopic(String mqttPublishTopic) { void AmsConfiguration::setMqttPublishTopic(const char* mqttPublishTopic) {
mqttChanged |= this->mqttPublishTopic != mqttPublishTopic; mqttChanged |= strcmp(config.mqttPublishTopic, mqttPublishTopic) != 0;
this->mqttPublishTopic = String(mqttPublishTopic); strcpy(config.mqttPublishTopic, mqttPublishTopic);
} }
String AmsConfiguration::getMqttSubscribeTopic() { char* AmsConfiguration::getMqttSubscribeTopic() {
return mqttSubscribeTopic; return config.mqttSubscribeTopic;
} }
void AmsConfiguration::setMqttSubscribeTopic(String mqttSubscribeTopic) { void AmsConfiguration::setMqttSubscribeTopic(const char* mqttSubscribeTopic) {
mqttChanged |= this->mqttSubscribeTopic != mqttSubscribeTopic; mqttChanged |= strcmp(config.mqttSubscribeTopic, mqttSubscribeTopic) != 0;
this->mqttSubscribeTopic = String(mqttSubscribeTopic); strcpy(config.mqttSubscribeTopic, mqttSubscribeTopic);
} }
String AmsConfiguration::getMqttUser() { char* AmsConfiguration::getMqttUser() {
return mqttUser; return config.mqttUser;
} }
void AmsConfiguration::setMqttUser(String mqttUser) { void AmsConfiguration::setMqttUser(const char* mqttUser) {
mqttChanged |= this->mqttUser != mqttUser; mqttChanged |= strcmp(config.mqttUser, mqttUser) != 0;
this->mqttUser = String(mqttUser); strcpy(config.mqttUser, mqttUser);
} }
String AmsConfiguration::getMqttPassword() { char* AmsConfiguration::getMqttPassword() {
return mqttPassword; return config.mqttPassword;
} }
void AmsConfiguration::setMqttPassword(String mqttPassword) { void AmsConfiguration::setMqttPassword(const char* mqttPassword) {
mqttChanged |= this->mqttPassword != mqttPassword; mqttChanged |= strcmp(config.mqttPassword, mqttPassword) != 0;
this->mqttPassword = String(mqttPassword); strcpy(config.mqttPassword, mqttPassword);
} }
int AmsConfiguration::getMqttPayloadFormat() { uint8_t AmsConfiguration::getMqttPayloadFormat() {
return this->mqttPayloadFormat; return config.mqttPayloadFormat;
} }
void AmsConfiguration::setMqttPayloadFormat(int mqttPayloadFormat) { void AmsConfiguration::setMqttPayloadFormat(uint8_t mqttPayloadFormat) {
this->mqttPayloadFormat = mqttPayloadFormat; config.mqttPayloadFormat = mqttPayloadFormat;
} }
bool AmsConfiguration::isMqttSsl() { bool AmsConfiguration::isMqttSsl() {
return this->mqttSsl; return config.mqttSsl;
} }
void AmsConfiguration::setMqttSsl(bool mqttSsl) { void AmsConfiguration::setMqttSsl(bool mqttSsl) {
mqttChanged |= this->mqttSsl != mqttSsl; mqttChanged |= config.mqttSsl != mqttSsl;
this->mqttSsl = mqttSsl; config.mqttSsl = mqttSsl;
} }
void AmsConfiguration::clearMqtt() { void AmsConfiguration::clearMqtt() {
@@ -193,27 +193,27 @@ void AmsConfiguration::ackMqttChange() {
byte AmsConfiguration::getAuthSecurity() { byte AmsConfiguration::getAuthSecurity() {
return authSecurity; return config.authSecurity;
} }
void AmsConfiguration::setAuthSecurity(byte authSecurity) { void AmsConfiguration::setAuthSecurity(byte authSecurity) {
this->authSecurity = authSecurity; config.authSecurity = authSecurity;
} }
String AmsConfiguration::getAuthUser() { char* AmsConfiguration::getAuthUser() {
return authUser; return config.authUser;
} }
void AmsConfiguration::setAuthUser(String authUser) { void AmsConfiguration::setAuthUser(const char* authUser) {
this->authUser = String(authUser); strcpy(config.authUser, authUser);
} }
String AmsConfiguration::getAuthPassword() { char* AmsConfiguration::getAuthPassword() {
return authPassword; return config.authPassword;
} }
void AmsConfiguration::setAuthPassword(String authPassword) { void AmsConfiguration::setAuthPassword(const char* authPassword) {
this->authPassword = String(authPassword); strcpy(config.authPassword, authPassword);
} }
void AmsConfiguration::clearAuth() { void AmsConfiguration::clearAuth() {
@@ -222,62 +222,194 @@ void AmsConfiguration::clearAuth() {
setAuthPassword(""); setAuthPassword("");
} }
int AmsConfiguration::getMeterType() { uint8_t AmsConfiguration::getMeterType() {
return this->meterType; return config.meterType;
} }
void AmsConfiguration::setMeterType(int meterType) { void AmsConfiguration::setMeterType(uint8_t meterType) {
this->meterType = meterType; config.meterType = meterType;
} }
int AmsConfiguration::getDistributionSystem() { uint8_t AmsConfiguration::getDistributionSystem() {
return this->distributionSystem; return config.distributionSystem;
} }
void AmsConfiguration::setDistributionSystem(int distributionSystem) { void AmsConfiguration::setDistributionSystem(uint8_t distributionSystem) {
this->distributionSystem = distributionSystem; config.distributionSystem = distributionSystem;
} }
int AmsConfiguration::getMainFuse() { uint8_t AmsConfiguration::getMainFuse() {
return this->mainFuse; return config.mainFuse;
} }
void AmsConfiguration::setMainFuse(int mainFuse) { void AmsConfiguration::setMainFuse(uint8_t mainFuse) {
this->mainFuse = mainFuse; config.mainFuse = mainFuse;
} }
int AmsConfiguration::getProductionCapacity() { uint8_t AmsConfiguration::getProductionCapacity() {
return this->productionCapacity; return config.productionCapacity;
} }
void AmsConfiguration::setProductionCapacity(int productionCapacity) { void AmsConfiguration::setProductionCapacity(uint8_t productionCapacity) {
this->productionCapacity = productionCapacity; config.productionCapacity = productionCapacity;
} }
bool AmsConfiguration::isDebugTelnet() { bool AmsConfiguration::isDebugTelnet() {
return this->debugTelnet; return config.debugTelnet;
} }
void AmsConfiguration::setDebugTelnet(bool debugTelnet) { void AmsConfiguration::setDebugTelnet(bool debugTelnet) {
this->debugTelnet = debugTelnet; config.debugTelnet = debugTelnet;
} }
bool AmsConfiguration::isDebugSerial() { bool AmsConfiguration::isDebugSerial() {
return this->debugSerial; return config.debugSerial;
} }
void AmsConfiguration::setDebugSerial(bool debugSerial) { void AmsConfiguration::setDebugSerial(bool debugSerial) {
this->debugSerial = debugSerial; config.debugSerial = debugSerial;
} }
int AmsConfiguration::getDebugLevel() { uint8_t AmsConfiguration::getDebugLevel() {
return this->debugLevel; return config.debugLevel;
} }
void AmsConfiguration::setDebugLevel(int debugLevel) { void AmsConfiguration::setDebugLevel(uint8_t debugLevel) {
this->debugLevel = debugLevel; config.debugLevel = debugLevel;
} }
bool AmsConfiguration::pinUsed(uint8_t pin) {
if(pin == 0xFF)
return false;
return
pin == config.hanPin ||
pin == config.apPin ||
pin == config.ledPinRed ||
pin == config.ledPinGreen ||
pin == config.ledPinBlue ||
pin == config.tempSensorPin ||
pin == config.vccPin
;
}
uint8_t AmsConfiguration::getHanPin() {
return config.hanPin;
}
void AmsConfiguration::setHanPin(uint8_t hanPin) {
if(!pinUsed(hanPin)) {
config.hanPin = hanPin;
}
}
uint8_t AmsConfiguration::getApPin() {
return config.apPin;
}
void AmsConfiguration::setApPin(uint8_t apPin) {
if(!pinUsed(apPin)) {
config.apPin = apPin;
if(apPin >= 0)
pinMode(apPin, INPUT_PULLUP);
}
}
uint8_t AmsConfiguration::getLedPin() {
return config.ledPin;
}
void AmsConfiguration::setLedPin(uint8_t ledPin) {
if(!pinUsed(ledPin)) {
config.ledPin = ledPin;
}
}
bool AmsConfiguration::isLedInverted() {
return config.ledInverted;
}
void AmsConfiguration::setLedInverted(bool ledInverted) {
config.ledInverted = ledInverted;
}
uint8_t AmsConfiguration::getLedPinRed() {
return config.ledPinRed;
}
void AmsConfiguration::setLedPinRed(uint8_t ledPinRed) {
if(!pinUsed(ledPinRed)) {
config.ledPinRed = ledPinRed;
}
}
uint8_t AmsConfiguration::getLedPinGreen() {
return config.ledPinGreen;
}
void AmsConfiguration::setLedPinGreen(uint8_t ledPinGreen) {
if(!pinUsed(ledPinGreen)) {
config.ledPinGreen = ledPinGreen;
}
}
uint8_t AmsConfiguration::getLedPinBlue() {
return config.ledPinBlue;
}
void AmsConfiguration::setLedPinBlue(uint8_t ledPinBlue) {
if(!pinUsed(ledPinBlue)) {
config.ledPinBlue = ledPinBlue;
}
}
bool AmsConfiguration::isLedRgbInverted() {
return config.ledRgbInverted;
}
void AmsConfiguration::setLedRgbInverted(bool ledRgbInverted) {
config.ledRgbInverted = ledRgbInverted;
}
uint8_t AmsConfiguration::getTempSensorPin() {
return config.tempSensorPin;
}
void AmsConfiguration::setTempSensorPin(uint8_t tempSensorPin) {
if(!pinUsed(tempSensorPin)) {
config.tempSensorPin = tempSensorPin;
}
}
uint8_t AmsConfiguration::getVccPin() {
return config.vccPin;
}
void AmsConfiguration::setVccPin(uint8_t vccPin) {
if(!pinUsed(vccPin)) {
config.vccPin = vccPin;
}
}
double AmsConfiguration::getVccMultiplier() {
return config.vccMultiplier / 100.0;
}
void AmsConfiguration::setVccMultiplier(double vccMultiplier) {
config.vccMultiplier = max(0.01, min(vccMultiplier, 655.5)) * 100;
}
double AmsConfiguration::getVccBootLimit() {
return config.vccBootLimit / 10;
}
void AmsConfiguration::setVccBootLimit(double vccBootLimit) {
if(vccBootLimit == 0.0)
config.vccBootLimit = 0;
else
config.vccBootLimit = max(2.5, min(vccBootLimit, 3.5)) * 10;
}
bool AmsConfiguration::hasConfig() { bool AmsConfiguration::hasConfig() {
if(configVersion == 0) { if(configVersion == 0) {
@@ -311,153 +443,25 @@ bool AmsConfiguration::load() {
int cs = EEPROM.read(address); int cs = EEPROM.read(address);
address++; address++;
switch(cs) { switch(cs) {
case 71: // Same as 72 case 80: // v1.1
case 72:
success = loadConfig72(address);
break;
case 75:
success = loadConfig75(address);
break;
case 80:
success = loadConfig80(address); success = loadConfig80(address);
break; break;
case 81: case 81: // v1.2
success = loadConfig81(address); success = loadConfig81(address);
break; break;
case 82: case 82: // v1.3
success = loadConfig82(address); EEPROM.get(address, config);
success = true;
break; break;
} }
EEPROM.end(); EEPROM.end();
if(config.apPin >= 0)
pinMode(config.apPin, INPUT_PULLUP);
return success; return success;
} }
bool AmsConfiguration::loadConfig72(int address) {
char* temp;
address += readString(address, &temp);
setWifiSsid(temp);
address += readString(address, &temp);
setWifiPassword(temp);
byte b;
address += readByte(address, &b);
setMeterType(b);
address += readString(address, &temp);
setMqttHost(temp);
int port;
address += readInt(address, &port);
setMqttPort(port);
address += readString(address, &temp);
setMqttClientId(temp);
address += readString(address, &temp);
setMqttPublishTopic(temp);
address += readString(address, &temp);
setMqttSubscribeTopic(temp);
bool secure = false;
address += readBool(address, &secure);
if (secure) {
address += readString(address, &temp);
setMqttUser(temp);
address += readString(address, &temp);
setMqttPassword(temp);
} else {
setMqttUser("");
setMqttPassword("");
}
setMqttPayloadFormat(0);
clearAuth();
setWifiIp("");
setWifiGw("");
setWifiSubnet("");
setMainFuse(0);
setProductionCapacity(0);
setDistributionSystem(0);
ackWifiChange();
setDebugLevel(3); // 3=Info
setDebugTelnet(false);
setDebugSerial(false);
return true;
}
bool AmsConfiguration::loadConfig75(int address) {
char* temp;
address += readString(address, &temp);
setWifiSsid(temp);
address += readString(address, &temp);
setWifiPassword(temp);
byte b;
address += readByte(address, &b);
setMeterType(b);
bool mqtt = false;
address += readBool(address, &mqtt);
if(mqtt) {
address += readString(address, &temp);
setMqttHost(temp);
int port;
address += readInt(address, &port);
setMqttPort(port);
address += readString(address, &temp);
setMqttClientId(temp);
address += readString(address, &temp);
setMqttPublishTopic(temp);
address += readString(address, &temp);
setMqttSubscribeTopic(temp);
}
bool secure = false;
address += readBool(address, &secure);
if (secure) {
address += readString(address, &temp);
setMqttUser(temp);
address += readString(address, &temp);
setMqttPassword(temp);
} else {
setMqttUser("");
setMqttPassword("");
}
setMqttPayloadFormat(0);
address += readByte(address, &authSecurity);
if (authSecurity > 0) {
address += readString(address, &temp);
setAuthUser(temp);
address += readString(address, &temp);
setAuthPassword(temp);
} else {
clearAuth();
}
int i;
address += readInt(address, &i);
setMainFuse(i);
address += readByte(address, &b);
setDistributionSystem(b);
setWifiIp("");
setWifiGw("");
setWifiSubnet("");
setProductionCapacity(0);
ackWifiChange();
setDebugLevel(3); // 3=Info
setDebugTelnet(false);
setDebugSerial(false);
return true;
}
bool AmsConfiguration::loadConfig80(int address) { bool AmsConfiguration::loadConfig80(int address) {
char* temp; char* temp;
@@ -504,8 +508,10 @@ bool AmsConfiguration::loadConfig80(int address) {
} }
setMqttPayloadFormat(0); setMqttPayloadFormat(0);
address += readByte(address, &authSecurity); byte b;
if (authSecurity > 0) { address += readByte(address, &b);
setAuthSecurity(b);
if (b > 0) {
address += readString(address, &temp); address += readString(address, &temp);
setAuthUser(temp); setAuthUser(temp);
address += readString(address, &temp); address += readString(address, &temp);
@@ -591,103 +597,10 @@ bool AmsConfiguration::loadConfig81(int address) {
clearMqtt(); clearMqtt();
} }
address += readByte(address, &authSecurity); byte b;
if (authSecurity > 0) { address += readByte(address, &b);
address += readString(address, &temp); setAuthSecurity(b);
setAuthUser(temp); if (b > 0) {
address += readString(address, &temp);
setAuthPassword(temp);
} else {
clearAuth();
}
int i;
address += readInt(address, &i);
setMeterType(i);
address += readInt(address, &i);
setDistributionSystem(i);
address += readInt(address, &i);
setMainFuse(i);
address += readInt(address, &i);
setProductionCapacity(i);
bool debugTelnet = false;
address += readBool(address, &debugTelnet);
setDebugTelnet(debugTelnet);
bool debugSerial = false;
address += readBool(address, &debugSerial);
setDebugSerial(debugSerial);
address += readInt(address, &i);
setDebugLevel(i);
ackWifiChange();
return true;
}
bool AmsConfiguration::loadConfig82(int address) {
char* temp;
address += readString(address, &temp);
setWifiSsid(temp);
address += readString(address, &temp);
setWifiPassword(temp);
bool staticIp = false;
address += readBool(address, &staticIp);
if(staticIp) {
address += readString(address, &temp);
setWifiIp(temp);
address += readString(address, &temp);
setWifiGw(temp);
address += readString(address, &temp);
setWifiSubnet(temp);
address += readString(address, &temp);
setWifiDns1(temp);
address += readString(address, &temp);
setWifiDns2(temp);
}
address += readString(address, &temp);
setWifiHostname(temp);
bool mqtt = false;
address += readBool(address, &mqtt);
if(mqtt) {
address += readString(address, &temp);
setMqttHost(temp);
int port;
address += readInt(address, &port);
setMqttPort(port);
address += readString(address, &temp);
setMqttClientId(temp);
address += readString(address, &temp);
setMqttPublishTopic(temp);
address += readString(address, &temp);
setMqttSubscribeTopic(temp);
bool secure = false;
address += readBool(address, &secure);
if (secure)
{
address += readString(address, &temp);
setMqttUser(temp);
address += readString(address, &temp);
setMqttPassword(temp);
} else {
setMqttUser("");
setMqttPassword("");
}
int payloadFormat;
address += readInt(address, &payloadFormat);
setMqttPayloadFormat(payloadFormat);
bool ssl = false;
address += readBool(address, &ssl);
setMqttSsl(ssl);
} else {
clearMqtt();
}
address += readByte(address, &authSecurity);
if (authSecurity > 0) {
address += readString(address, &temp); address += readString(address, &temp);
setAuthUser(temp); setAuthUser(temp);
address += readString(address, &temp); address += readString(address, &temp);
@@ -726,60 +639,11 @@ bool AmsConfiguration::save() {
EEPROM.begin(EEPROM_SIZE); EEPROM.begin(EEPROM_SIZE);
EEPROM.put(address, EEPROM_CHECK_SUM); EEPROM.put(address, EEPROM_CHECK_SUM);
address++; address++;
EEPROM.put(address, config);
address += saveString(address, wifiSsid.c_str());
address += saveString(address, wifiPassword.c_str());
if(!wifiIp.isEmpty()) {
address += saveBool(address, true);
address += saveString(address, wifiIp.c_str());
address += saveString(address, wifiGw.c_str());
address += saveString(address, wifiSubnet.c_str());
address += saveString(address, wifiDns1.c_str());
address += saveString(address, wifiDns2.c_str());
} else {
address += saveBool(address, false);
}
address += saveString(address, wifiHostname.c_str());
if(!mqttHost.isEmpty()) {
address += saveBool(address, true);
address += saveString(address, mqttHost.c_str());
address += saveInt(address, mqttPort);
address += saveString(address, mqttClientId.c_str());
address += saveString(address, mqttPublishTopic.c_str());
address += saveString(address, mqttSubscribeTopic.c_str());
if (!mqttUser.isEmpty()) {
address += saveBool(address, true);
address += saveString(address, mqttUser.c_str());
address += saveString(address, mqttPassword.c_str());
} else {
address += saveBool(address, false);
}
address += saveInt(address, mqttPayloadFormat);
address += saveBool(address, mqttSsl);
} else {
address += saveBool(address, false);
}
address += saveByte(address, authSecurity);
if (authSecurity > 0) {
address += saveString(address, authUser.c_str());
address += saveString(address, authPassword.c_str());
}
address += saveInt(address, meterType);
address += saveInt(address, distributionSystem);
address += saveInt(address, mainFuse);
address += saveInt(address, productionCapacity);
address += saveBool(address, debugTelnet);
address += saveBool(address, debugSerial);
address += saveInt(address, debugLevel);
bool success = EEPROM.commit(); bool success = EEPROM.commit();
EEPROM.end(); EEPROM.end();
configVersion = EEPROM_CHECK_SUM; configVersion = EEPROM_CHECK_SUM;
return success; return success;
} }
@@ -797,21 +661,6 @@ int AmsConfiguration::readString(int pAddress, char* pString[]) {
return address; return address;
} }
int AmsConfiguration::saveString(int pAddress, const char* pString) {
int address = 0;
int length = pString ? strlen(pString) + 1 : 0;
EEPROM.put(pAddress + address, length);
address++;
for (int i = 0; i < length; i++)
{
EEPROM.put(pAddress + address, pString[i]);
address++;
}
return address;
}
int AmsConfiguration::readInt(int address, int *value) { int AmsConfiguration::readInt(int address, int *value) {
int lower = EEPROM.read(address); int lower = EEPROM.read(address);
int higher = EEPROM.read(address + 1); int higher = EEPROM.read(address + 1);
@@ -819,79 +668,44 @@ int AmsConfiguration::readInt(int address, int *value) {
return 2; return 2;
} }
int AmsConfiguration::saveInt(int address, int value) {
byte lowByte = value & 0xFF;
byte highByte = ((value >> 8) & 0xFF);
EEPROM.write(address, lowByte);
EEPROM.write(address + 1, highByte);
return 2;
}
int AmsConfiguration::readBool(int address, bool *value) { int AmsConfiguration::readBool(int address, bool *value) {
byte y = EEPROM.read(address); byte y = EEPROM.read(address);
*value = (bool)y; *value = (bool)y;
return 1; return 1;
} }
int AmsConfiguration::saveBool(int address, bool value) {
byte y = (byte)value;
EEPROM.write(address, y);
return 1;
}
int AmsConfiguration::readByte(int address, byte *value) { int AmsConfiguration::readByte(int address, byte *value) {
*value = EEPROM.read(address); *value = EEPROM.read(address);
return 1; return 1;
} }
int AmsConfiguration::saveByte(int address, byte value) {
EEPROM.write(address, value);
return 1;
}
template <class T> int AmsConfiguration::writeAnything(int ee, const T& value) {
const byte* p = (const byte*)(const void*)&value;
unsigned int i;
for (i = 0; i < sizeof(value); i++)
EEPROM.write(ee++, *p++);
return i;
}
template <class T> int AmsConfiguration::readAnything(int ee, T& value) {
byte* p = (byte*)(void*)&value;
unsigned int i;
for (i = 0; i < sizeof(value); i++)
*p++ = EEPROM.read(ee++);
return i;
}
void AmsConfiguration::print(Print* debugger) void AmsConfiguration::print(Print* debugger)
{ {
debugger->println("Configuration:"); debugger->println("Configuration:");
debugger->println("-----------------------------------------------"); debugger->println("-----------------------------------------------");
debugger->printf("WiFi SSID: %s\r\n", this->getWifiSsid().c_str()); debugger->printf("WiFi SSID: '%s'\r\n", this->getWifiSsid());
debugger->printf("WiFi Psk: %s\r\n", this->getWifiPassword().c_str()); debugger->printf("WiFi Psk: '%s'\r\n", this->getWifiPassword());
if(!getWifiIp().isEmpty()) { if(strlen(getWifiIp()) > 0) {
debugger->printf("IP: %s\r\n", this->getWifiIp().c_str()); debugger->printf("IP: '%s'\r\n", this->getWifiIp());
debugger->printf("Gateway: %s\r\n", this->getWifiGw().c_str()); debugger->printf("Gateway: '%s'\r\n", this->getWifiGw());
debugger->printf("Subnet: %s\r\n", this->getWifiSubnet().c_str()); debugger->printf("Subnet: '%s'\r\n", this->getWifiSubnet());
debugger->printf("Primary DNS: %s\r\n", this->getWifiDns1().c_str()); debugger->printf("Primary DNS: '%s'\r\n", this->getWifiDns1());
debugger->printf("Secondary DNS: %s\r\n", this->getWifiDns2().c_str()); debugger->printf("Secondary DNS: '%s'\r\n", this->getWifiDns2());
} }
debugger->printf("WiFi Host: '%s'\r\n", this->getWifiHostname());
if(!getMqttHost().isEmpty()) { if(strlen(getMqttHost()) > 0) {
debugger->printf("mqttHost: %s\r\n", this->getMqttHost().c_str()); debugger->printf("mqttHost: '%s'\r\n", this->getMqttHost());
debugger->printf("mqttPort: %i\r\n", this->getMqttPort()); debugger->printf("mqttPort: %i\r\n", this->getMqttPort());
debugger->printf("mqttClientID: %s\r\n", this->getMqttClientId().c_str()); debugger->printf("mqttClientID: '%s'\r\n", this->getMqttClientId());
debugger->printf("mqttPublishTopic: %s\r\n", this->getMqttPublishTopic().c_str()); debugger->printf("mqttPublishTopic: '%s'\r\n", this->getMqttPublishTopic());
debugger->printf("mqttSubscribeTopic: %s\r\n", this->getMqttSubscribeTopic().c_str()); debugger->printf("mqttSubscribeTopic: '%s'\r\n", this->getMqttSubscribeTopic());
if (this->getMqttUser()) { if (this->getMqttUser()) {
debugger->printf("SECURE MQTT CONNECTION:\r\n"); debugger->printf("SECURE MQTT CONNECTION:\r\n");
debugger->printf("mqttUser: %s\r\n", this->getMqttUser().c_str()); debugger->printf("mqttUser: '%s'\r\n", this->getMqttUser());
debugger->printf("mqttPass: %s\r\n", this->getMqttPassword().c_str()); debugger->printf("mqttPass: '%s'\r\n", this->getMqttPassword());
} }
debugger->printf("payload format: %i\r\n", this->getMqttPayloadFormat()); debugger->printf("payload format: %i\r\n", this->getMqttPayloadFormat());
} }
@@ -899,8 +713,8 @@ void AmsConfiguration::print(Print* debugger)
if (this->getAuthSecurity()) { if (this->getAuthSecurity()) {
debugger->printf("WEB AUTH:\r\n"); debugger->printf("WEB AUTH:\r\n");
debugger->printf("authSecurity: %i\r\n", this->getAuthSecurity()); debugger->printf("authSecurity: %i\r\n", this->getAuthSecurity());
debugger->printf("authUser: %s\r\n", this->getAuthUser().c_str()); debugger->printf("authUser: '%s'\r\n", this->getAuthUser());
debugger->printf("authPass: %s\r\n", this->getAuthPassword().c_str()); debugger->printf("authPass: '%s'\r\n", this->getAuthPassword());
} }
debugger->printf("meterType: %i\r\n", this->getMeterType()); debugger->printf("meterType: %i\r\n", this->getMeterType());
@@ -908,5 +722,13 @@ void AmsConfiguration::print(Print* debugger)
debugger->printf("fuseSize: %i\r\n", this->getMainFuse()); debugger->printf("fuseSize: %i\r\n", this->getMainFuse());
debugger->printf("productionCapacity: %i\r\n", this->getProductionCapacity()); debugger->printf("productionCapacity: %i\r\n", this->getProductionCapacity());
debugger->printf("HAN pin: %i\r\n", this->getHanPin());
debugger->printf("LED pin: %i\r\n", this->getLedPin());
debugger->printf("LED red pin: %i\r\n", this->getLedPinRed());
debugger->printf("LED green pin: %i\r\n", this->getLedPinGreen());
debugger->printf("LED blue pin: %i\r\n", this->getLedPinBlue());
debugger->printf("AP pin: %i\r\n", this->getApPin());
debugger->printf("Temperature pin: %i\r\n", this->getTempSensorPin());
debugger->println("-----------------------------------------------"); debugger->println("-----------------------------------------------");
} }

View File

@@ -3,6 +3,50 @@
#include <EEPROM.h> #include <EEPROM.h>
#include "Arduino.h" #include "Arduino.h"
struct ConfigObject {
char wifiSsid[32];
char wifiPassword[64];
char wifiIp[15];
char wifiGw[15];
char wifiSubnet[15];
char wifiDns1[15];
char wifiDns2[15];
char wifiHostname[32];
char mqttHost[128];
uint16_t mqttPort;
char mqttClientId[32];
char mqttPublishTopic[64];
char mqttSubscribeTopic[64];
char mqttUser[64];
char mqttPassword[64];
uint8_t mqttPayloadFormat;
bool mqttSsl;
uint8_t authSecurity;
char authUser[64];
char authPassword[64];
uint8_t meterType;
uint8_t distributionSystem;
uint8_t mainFuse;
uint8_t productionCapacity;
bool debugTelnet;
bool debugSerial;
uint8_t debugLevel;
uint8_t hanPin;
uint8_t apPin;
uint8_t ledPin;
bool ledInverted;
uint8_t ledPinRed;
uint8_t ledPinGreen;
uint8_t ledPinBlue;
bool ledRgbInverted;
uint8_t tempSensorPin;
uint8_t vccPin;
uint16_t vccMultiplier;
uint8_t vccBootLimit;
};
class AmsConfiguration { class AmsConfiguration {
public: public:
bool hasConfig(); bool hasConfig();
@@ -11,43 +55,43 @@ public:
bool load(); bool load();
bool save(); bool save();
String getWifiSsid(); char* getWifiSsid();
void setWifiSsid(String wifiSsid); void setWifiSsid(const char* wifiSsid);
String getWifiPassword(); char* getWifiPassword();
void setWifiPassword(String wifiPassword); void setWifiPassword(const char* wifiPassword);
String getWifiIp(); char* getWifiIp();
void setWifiIp(String wifiIp); void setWifiIp(const char* wifiIp);
String getWifiGw(); char* getWifiGw();
void setWifiGw(String wifiGw); void setWifiGw(const char* wifiGw);
String getWifiSubnet(); char* getWifiSubnet();
void setWifiSubnet(String wifiSubnet); void setWifiSubnet(const char* wifiSubnet);
String getWifiDns1(); char* getWifiDns1();
void setWifiDns1(String wifiDns1); void setWifiDns1(const char* wifiDns1);
String getWifiDns2(); char* getWifiDns2();
void setWifiDns2(String wifiDns1); void setWifiDns2(const char* wifiDns1);
String getWifiHostname(); char* getWifiHostname();
void setWifiHostname(String wifiHostname); void setWifiHostname(const char* wifiHostname);
void clearWifiIp(); void clearWifiIp();
bool isWifiChanged(); bool isWifiChanged();
void ackWifiChange(); void ackWifiChange();
String getMqttHost(); char* getMqttHost();
void setMqttHost(String mqttHost); void setMqttHost(const char* mqttHost);
int getMqttPort(); uint16_t getMqttPort();
void setMqttPort(int mqttPort); void setMqttPort(uint16_t mqttPort);
String getMqttClientId(); char* getMqttClientId();
void setMqttClientId(String mqttClientId); void setMqttClientId(const char* mqttClientId);
String getMqttPublishTopic(); char* getMqttPublishTopic();
void setMqttPublishTopic(String mqttPublishTopic); void setMqttPublishTopic(const char* mqttPublishTopic);
String getMqttSubscribeTopic(); char* getMqttSubscribeTopic();
void setMqttSubscribeTopic(String mqttSubscribeTopic); void setMqttSubscribeTopic(const char* mqttSubscribeTopic);
String getMqttUser(); char* getMqttUser();
void setMqttUser(String mqttUser); void setMqttUser(const char* mqttUser);
String getMqttPassword(); char* getMqttPassword();
void setMqttPassword(String mqttPassword); void setMqttPassword(const char* mqttPassword);
int getMqttPayloadFormat(); uint8_t getMqttPayloadFormat();
void setMqttPayloadFormat(int mqttPayloadFormat); void setMqttPayloadFormat(uint8_t mqttPayloadFormat);
bool isMqttSsl(); bool isMqttSsl();
void setMqttSsl(bool mqttSsl); void setMqttSsl(bool mqttSsl);
void clearMqtt(); void clearMqtt();
@@ -58,27 +102,56 @@ public:
byte getAuthSecurity(); byte getAuthSecurity();
void setAuthSecurity(byte authSecurity); void setAuthSecurity(byte authSecurity);
String getAuthUser(); char* getAuthUser();
void setAuthUser(String authUser); void setAuthUser(const char* authUser);
String getAuthPassword(); char* getAuthPassword();
void setAuthPassword(String authPassword); void setAuthPassword(const char* authPassword);
void clearAuth(); void clearAuth();
int getMeterType(); uint8_t getMeterType();
void setMeterType(int meterType); void setMeterType(uint8_t meterType);
int getDistributionSystem(); uint8_t getDistributionSystem();
void setDistributionSystem(int distributionSystem); void setDistributionSystem(uint8_t distributionSystem);
int getMainFuse(); uint8_t getMainFuse();
void setMainFuse(int mainFuse); void setMainFuse(uint8_t mainFuse);
int getProductionCapacity(); uint8_t getProductionCapacity();
void setProductionCapacity(int productionCapacity); void setProductionCapacity(uint8_t productionCapacity);
bool isDebugTelnet(); bool isDebugTelnet();
void setDebugTelnet(bool debugTelnet); void setDebugTelnet(bool debugTelnet);
bool isDebugSerial(); bool isDebugSerial();
void setDebugSerial(bool debugSerial); void setDebugSerial(bool debugSerial);
int getDebugLevel(); uint8_t getDebugLevel();
void setDebugLevel(int debugLevel); void setDebugLevel(uint8_t debugLevel);
bool pinUsed(uint8_t pin);
uint8_t getHanPin();
void setHanPin(uint8_t hanPin);
uint8_t getApPin();
void setApPin(uint8_t apPin);
uint8_t getLedPin();
void setLedPin(uint8_t ledPin);
bool isLedInverted();
void setLedInverted(bool ledInverted);
uint8_t getLedPinRed();
void setLedPinRed(uint8_t ledPinRed);
uint8_t getLedPinGreen();
void setLedPinGreen(uint8_t ledPinGreen);
uint8_t getLedPinBlue();
void setLedPinBlue(uint8_t ledPinBlue);
bool isLedRgbInverted();
void setLedRgbInverted(bool ledRgbInverted);
uint8_t getTempSensorPin();
void setTempSensorPin(uint8_t tempSensorPin);
uint8_t getVccPin();
void setVccPin(uint8_t vccPin);
double getVccMultiplier();
void setVccMultiplier(double vccMultiplier);
double getVccBootLimit();
void setVccBootLimit(double vccBootLimit);
void print(Print* debugger); void print(Print* debugger);
@@ -86,57 +159,59 @@ protected:
private: private:
int configVersion = 0; int configVersion = 0;
ConfigObject config {
"", // SSID
"", // PSK
"", // IP
"", // GW
"", // Subnet
"", // DNS 1
"", // DNS 2
"", // Hostname
"", // MQTT host
1883, // Port
"", // Client ID
"", // Publish topic
"", // Subscribe topic
"", // Username
"", // Password
0, // Format
false, // SSL
0, // Web security
"", // Username
"", // Password
0, // Meter type
0, // Distribution system
0, // Main fuse
0, // Production capacity
false, // Debug telnet
false, // Debug serial
5, // Debug level
0x03, // HAN pin
0xFF, // AP pin
0x02, // LED pin
true, // Inverted
0xFF, // Red
0xFF, // Green
0xFF, // Blue
true, // Inverted
0xFF, // Temp sensor
0xFF, // Vcc
100, // Multiplier
0 // Boot limit
};
bool wifiChanged, mqttChanged;
String wifiSsid; const int EEPROM_SIZE = 2048;
String wifiPassword;
String wifiIp;
String wifiGw;
String wifiSubnet;
String wifiDns1;
String wifiDns2;
String wifiHostname;
bool wifiChanged = false;
String mqttHost;
int mqttPort = 1883;
String mqttClientId;
String mqttPublishTopic;
String mqttSubscribeTopic;
String mqttUser;
String mqttPassword;
int mqttPayloadFormat = 0;
bool mqttSsl;
bool mqttChanged = false;
byte authSecurity;
String authUser;
String authPassword;
int meterType = 0, distributionSystem = 0, mainFuse = 0, productionCapacity = 0;
bool debugTelnet = false, debugSerial = false;
int debugLevel = 3;
const int EEPROM_SIZE = 512;
const int EEPROM_CHECK_SUM = 82; // Used to check if config is stored. Change if structure changes const int EEPROM_CHECK_SUM = 82; // Used to check if config is stored. Change if structure changes
const int EEPROM_CONFIG_ADDRESS = 0; const int EEPROM_CONFIG_ADDRESS = 0;
bool loadConfig72(int address);
bool loadConfig75(int address);
bool loadConfig80(int address); bool loadConfig80(int address);
bool loadConfig81(int address); bool loadConfig81(int address);
bool loadConfig82(int address);
int saveString(int pAddress, const char* pString);
int readString(int pAddress, char* pString[]); int readString(int pAddress, char* pString[]);
int saveInt(int pAddress, int pValue);
int readInt(int pAddress, int *pValue); int readInt(int pAddress, int *pValue);
int saveBool(int pAddress, bool pValue);
int readBool(int pAddress, bool *pValue); int readBool(int pAddress, bool *pValue);
int saveByte(int pAddress, byte pValue);
int readByte(int pAddress, byte *pValue); int readByte(int pAddress, byte *pValue);
template <class T> int writeAnything(int ee, const T& value);
template <class T> int readAnything(int ee, T& value);
}; };
#endif #endif

View File

@@ -5,6 +5,7 @@
#define INVALID_BUTTON_PIN 0xFFFFFFFF #define INVALID_BUTTON_PIN 0xFFFFFFFF
#include <SoftwareSerial.h>
#if defined(ESP8266) #if defined(ESP8266)
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
@@ -17,62 +18,4 @@
#include "Update.h" #include "Update.h"
#endif #endif
#define RGB_RED 1
#define RGB_GREEN 2
#define RGB_YELLOW 3
#define RGB_ON 1
#define RGB_OFF 0
// Build settings for custom hardware by Roar Fredriksen
#if HW_ROARFRED
#define LED_PIN 2 // The blue on-board LED of the ESP8266 custom AMS board
#define LED_ACTIVE_HIGH 0
#define AP_BUTTON_PIN 0
HardwareSerial *hanSerial = &Serial;
// Build settings for Wemos Lolin D32
#elif defined(ARDUINO_LOLIN_D32)
#define LED_PIN 5
#define LED_ACTIVE_HIGH 0
#define AP_BUTTON_PIN 4
HardwareSerial *hanSerial = &Serial2;
// Build settings for Wemos D1 mini
#elif defined(ARDUINO_ESP8266_WEMOS_D1MINI)
#define LED_PIN D4
#define LED_ACTIVE_HIGH 0
#define AP_BUTTON_PIN D2
#define SOFTWARE_SERIAL 1
#include <SoftwareSerial.h>
SoftwareSerial *hanSerial = new SoftwareSerial(D1);
// Build settings for Adafruit Feather ESP32
#elif defined(ARDUINO_FEATHER_ESP32)
#define LED_PIN LED_BUILTIN
#define LED_ACTIVE_HIGH 1
#define AP_BUTTON_PIN INVALID_BUTTON_PIN
HardwareSerial *hanSerial = &Serial2;
// Default build for ESP32
#elif defined(ESP32)
#define LED_PIN INVALID_BUTTON_PIN
#define LED_ACTIVE_HIGH 1
#define AP_BUTTON_PIN INVALID_BUTTON_PIN
HardwareSerial *hanSerial = &Serial2;
// Default build settings
#else
#define LED_PIN 2
#define LED_ACTIVE_HIGH 0
#define AP_BUTTON_PIN INVALID_BUTTON_PIN
#define SOFTWARE_SERIAL 1
#include <SoftwareSerial.h>
SoftwareSerial *hanSerial = new SoftwareSerial(5);
#endif
#endif #endif

View File

@@ -55,32 +55,92 @@ AmsConfiguration config;
RemoteDebug Debug; RemoteDebug Debug;
AmsWebServer ws(&Debug); AmsWebServer ws(&Debug, &hw);
MQTTClient mqtt(512); MQTTClient mqtt(512);
HanReader hanReader; HanReader hanReader;
Stream *hanSerial;
int hanSerialPin = 0;
void setup() { void setup() {
if(config.hasConfig()) { if(config.hasConfig()) {
config.load(); config.load();
} }
#if HW_ROARFRED
if(config.getMeterType() == 3) { if(!config.hasConfig() || config.getConfigVersion() < 81) {
Serial.begin(2400, SERIAL_8N1); debugI("Setting default hostname");
} else { uint16_t chipId;
Serial.begin(2400, SERIAL_8E1); #if defined(ESP32)
chipId = ESP.getEfuseMac();
#else
chipId = ESP.getChipId();
#endif
config.setWifiHostname((String("ams-") + String(chipId, HEX)).c_str());
}
if(!config.hasConfig() || config.getConfigVersion() < 82) {
config.setVccMultiplier(1.0);
config.setVccBootLimit(0);
#if HW_ROARFRED
config.setHanPin(3);
config.setApPin(0);
config.setLedPin(2);
config.setLedInverted(true);
config.setTempSensorPin(5);
#elif defined(ARDUINO_ESP8266_WEMOS_D1MINI)
config.setHanPin(5);
config.setApPin(4);
config.setLedPin(2);
config.setLedInverted(true);
config.setTempSensorPin(14);
config.setVccMultiplier(1.1);
#elif defined(ARDUINO_LOLIN_D32)
config.setHanPin(16);
config.setLedPin(5);
config.setLedInverted(true);
config.setTempSensorPin(14);
config.setVccPin(35);
config.setVccMultiplier(2.25);
#elif defined(ARDUINO_FEATHER_ESP32)
config.setHanPin(16);
config.setLedPin(2);
config.setTempSensorPin(14);
#elif defined(ESP8266)
config.setHanPin(3);
config.setLedPin(2);
config.setLedInverted(true);
#elif defined(ESP32)
config.setHanPin(16);
config.setLedPin(2);
config.setLedInverted(true);
config.setTempSensorPin(14);
#endif
}
hw.setLed(config.getLedPin(), config.isLedInverted());
hw.setLedRgb(config.getLedPinRed(), config.getLedPinGreen(), config.getLedPinBlue(), config.isLedRgbInverted());
hw.setTempSensorPin(config.getTempSensorPin());
hw.setVccPin(config.getVccPin());
hw.setVccMultiplier(config.getVccMultiplier());
if(config.getHanPin() == 3) {
if(config.getMeterType() == 3) {
Serial.begin(2400, SERIAL_8N1);
} else {
Serial.begin(2400, SERIAL_8E1);
}
} else {
Serial.begin(115200);
} }
#else
Serial.begin(115200);
#endif
if(config.hasConfig() && config.isDebugSerial()) { if(config.hasConfig() && config.isDebugSerial()) {
Debug.setSerialEnabled(config.isDebugSerial()); Debug.setSerialEnabled(config.isDebugSerial());
} else { } else {
#if DEBUG_MODE #if DEBUG_MODE
Debug.setSerialEnabled(true); Debug.setSerialEnabled(true);
#endif #endif
} }
double vcc = hw.getVcc(); double vcc = hw.getVcc();
@@ -90,26 +150,16 @@ void setup() {
debugI("Voltage: %.2fV", vcc); debugI("Voltage: %.2fV", vcc);
} }
#if SELF_POWERED double vccBootLimit = config.getVccBootLimit();
if (vcc > 2.5 && vcc < 3.25) { // Only sleep if voltage is realistic and too low if(vccBootLimit > 0 && (config.getApPin() == -1 || digitalRead(config.getApPin()) == HIGH)) { // Skip if user is holding AP button while booting (HIGH = button is released)
if(Debug.isActive(RemoteDebug::INFO)) { if (vcc < vccBootLimit) {
debugI("Voltage is too low, sleeping"); if(Debug.isActive(RemoteDebug::INFO)) {
Serial.flush(); debugI("Voltage is too low, sleeping");
} Serial.flush();
ESP.deepSleep(10000000); //Deep sleep to allow output cap to charge up }
} ESP.deepSleep(10000000); //Deep sleep to allow output cap to charge up
#endif }
}
#if HAS_RGB_LED
// Initialize RGB LED pins
pinMode(LEDPIN_RGB_GREEN, OUTPUT);
pinMode(LEDPIN_RGB_RED, OUTPUT);
#endif
pinMode(LED_PIN, OUTPUT);
pinMode(AP_BUTTON_PIN, INPUT_PULLUP);
led_off();
WiFi.disconnect(true); WiFi.disconnect(true);
WiFi.softAPdisconnect(true); WiFi.softAPdisconnect(true);
@@ -178,17 +228,6 @@ void setup() {
} }
} }
if(!config.hasConfig() || config.getConfigVersion() < 81) {
debugI("Setting default hostname");
uint16_t chipId;
#if defined(ARDUINO_ARCH_ESP32)
chipId = ESP.getEfuseMac();
#else
chipId = ESP.getChipId();
#endif
config.setWifiHostname(String("ams-") + String(chipId, HEX));
}
if(config.hasConfig()) { if(config.hasConfig()) {
if(Debug.isActive(RemoteDebug::INFO)) config.print(&Debug); if(Debug.isActive(RemoteDebug::INFO)) config.print(&Debug);
WiFi_connect(); WiFi_connect();
@@ -199,49 +238,8 @@ void setup() {
swapWifiMode(); swapWifiMode();
} }
#if SOFTWARE_SERIAL
if(Debug.isActive(RemoteDebug::DEBUG)) debugD("HAN has software serial");
if(config.getMeterType() == 3) {
hanSerial->begin(2400, SWSERIAL_8N1);
} else {
hanSerial->begin(2400, SWSERIAL_8E1);
}
#else
if(Debug.isActive(RemoteDebug::DEBUG)) {
debugD("HAN has hardware serial");
Serial.flush();
}
if(config.getMeterType() == 3) {
hanSerial->begin(2400, SERIAL_8N1);
} else {
hanSerial->begin(2400, SERIAL_8E1);
}
#if UART2
hanSerial->swap();
#endif
#endif
hanReader.setup(hanSerial, &Debug);
// Compensate for the known Kaifa bug
hanReader.compensateFor09HeaderBug = (config.getMeterType() == 1);
// Empty buffer before starting
while (hanSerial->available() > 0) {
hanSerial->read();
}
ws.setup(&config, &mqtt); ws.setup(&config, &mqtt);
timeClient.begin(); timeClient.begin();
#if HAS_RGB_LED
//Signal startup by blinking red / green / yellow
rgb_led(RGB_RED, 2);
delay(250);
rgb_led(RGB_GREEN, 2);
delay(250);
rgb_led(RGB_YELLOW, 2);
#endif
} }
int buttonTimer = 0; int buttonTimer = 0;
@@ -254,7 +252,6 @@ bool wifiConnected = false;
unsigned long lastTemperatureRead = 0; unsigned long lastTemperatureRead = 0;
double temperature = -127; double temperature = -127;
bool even = true;
unsigned long lastRead = 0; unsigned long lastRead = 0;
unsigned long lastSuccessfulRead = 0; unsigned long lastSuccessfulRead = 0;
@@ -264,8 +261,8 @@ int lastError = 0;
void loop() { void loop() {
Debug.handle(); Debug.handle();
unsigned long now = millis(); unsigned long now = millis();
if(AP_BUTTON_PIN != INVALID_BUTTON_PIN) { if(config.getApPin() != INVALID_BUTTON_PIN) {
if (digitalRead(AP_BUTTON_PIN) == LOW) { if (digitalRead(config.getApPin()) == LOW) {
if (buttonActive == false) { if (buttonActive == false) {
buttonActive = true; buttonActive = true;
buttonTimer = now; buttonTimer = now;
@@ -300,8 +297,6 @@ void loop() {
// Only do normal stuff if we're not booted as AP // Only do normal stuff if we're not booted as AP
if (WiFi.getMode() != WIFI_AP) { if (WiFi.getMode() != WIFI_AP) {
led_off();
if (WiFi.status() != WL_CONNECTED) { if (WiFi.status() != WL_CONNECTED) {
wifiConnected = false; wifiConnected = false;
Debug.stop(); Debug.stop();
@@ -313,6 +308,7 @@ void loop() {
Debug.setPassword(config.getAuthPassword()); Debug.setPassword(config.getAuthPassword());
} }
Debug.begin(config.getWifiHostname(), (uint8_t) config.getDebugLevel()); Debug.begin(config.getWifiHostname(), (uint8_t) config.getDebugLevel());
Debug.setSerialEnabled(config.isDebugSerial());
if(!config.isDebugTelnet()) { if(!config.isDebugTelnet()) {
Debug.stop(); Debug.stop();
} }
@@ -320,12 +316,12 @@ void loop() {
debugI("Successfully connected to WiFi!"); debugI("Successfully connected to WiFi!");
debugI("IP: %s", WiFi.localIP().toString().c_str()); debugI("IP: %s", WiFi.localIP().toString().c_str());
} }
if(!config.getWifiHostname().isEmpty()) { if(strlen(config.getWifiHostname()) > 0) {
MDNS.begin(config.getWifiHostname().c_str()); MDNS.begin(config.getWifiHostname());
MDNS.addService("http", "tcp", 80); MDNS.addService("http", "tcp", 80);
} }
} }
if (!config.getMqttHost().isEmpty()) { if (strlen(config.getMqttHost()) > 0) {
mqtt.loop(); mqtt.loop();
delay(10); // Needed to preserve power. After adding this, the voltage is super smooth on a HAN powered device delay(10); // Needed to preserve power. After adding this, the voltage is super smooth on a HAN powered device
if(!mqtt.connected() || config.isMqttChanged()) { if(!mqtt.connected() || config.isMqttChanged()) {
@@ -341,10 +337,13 @@ void loop() {
} else { } else {
dnsServer.processNextRequest(); dnsServer.processNextRequest();
// Continously flash the LED when AP mode // Continously flash the LED when AP mode
if (now / 50 % 64 == 0) led_on(); hw.ledBlink(LED_INTERNAL, 1);
else led_off();
} }
if(hanSerialPin != config.getHanPin()) {
setupHanPort(config.getHanPin(), config.getMeterType());
}
if(now - lastRead > 100) { if(now - lastRead > 100) {
yield(); yield();
readHanPort(); readHanPort();
@@ -354,24 +353,67 @@ void loop() {
delay(1); // Needed for auto modem sleep delay(1); // Needed for auto modem sleep
} }
void setupHanPort(int pin, int meterType) {
debugI("Setting up HAN on pin %d for meter type %d", pin, meterType);
void led_on() HardwareSerial *hwSerial = NULL;
{ if(pin == 3) {
#if LED_ACTIVE_HIGH hwSerial = &Serial;
digitalWrite(LED_PIN, HIGH); }
#else #if defined(ESP32)
digitalWrite(LED_PIN, LOW); if(pin == 9) {
#endif hwSerial = &Serial1;
} }
if(pin == 16) {
hwSerial = &Serial2;
}
#endif
if(hwSerial != NULL) {
debugD("Hardware serial");
Serial.flush();
if(meterType == 3) {
hwSerial->begin(2400, SERIAL_8N1);
} else {
hwSerial->begin(2400, SERIAL_8E1);
}
hanSerialPin = pin;
hanSerial = hwSerial;
} else {
debugD("Software serial");
Serial.flush();
SoftwareSerial *swSerial = new SoftwareSerial(pin);
void led_off() if(meterType == 3) {
{ swSerial->begin(2400, SWSERIAL_8N1);
#if LED_ACTIVE_HIGH } else {
digitalWrite(LED_PIN, LOW); swSerial->begin(2400, SWSERIAL_8E1);
#else }
digitalWrite(LED_PIN, HIGH); hanSerialPin = pin;
#endif hanSerial = swSerial;
Serial.begin(115200);
}
hanReader.setup(hanSerial, &Debug);
// Compensate for the known Kaifa bug
hanReader.compensateFor09HeaderBug = (config.getMeterType() == 1);
// Empty buffer before starting
while (hanSerial->available() > 0) {
hanSerial->read();
}
if(config.hasConfig() && config.isDebugSerial()) {
if(WiFi.status() == WL_CONNECTED) {
Debug.begin(config.getWifiHostname(), (uint8_t) config.getDebugLevel());
}
Debug.setSerialEnabled(config.isDebugSerial());
if(!config.isDebugTelnet()) {
Debug.stop();
}
}
} }
void errorBlink() { void errorBlink() {
@@ -382,19 +424,19 @@ void errorBlink() {
switch(lastError) { switch(lastError) {
case 0: case 0:
if(lastErrorBlink - lastSuccessfulRead > 30000) { if(lastErrorBlink - lastSuccessfulRead > 30000) {
rgb_led(1, 2); // If no message received from AMS in 30 sec, blink once hw.ledBlink(LED_RED, 1); // If no message received from AMS in 30 sec, blink once
return; return;
} }
break; break;
case 1: case 1:
if(!config.getMqttHost().isEmpty() && mqtt.lastError() != 0) { if(strlen(config.getMqttHost()) > 0 && mqtt.lastError() != 0) {
rgb_led(1, 3); // If MQTT error, blink twice hw.ledBlink(LED_RED, 2); // If MQTT error, blink twice
return; return;
} }
break; break;
case 2: case 2:
if(WiFi.getMode() != WIFI_AP && WiFi.status() != WL_CONNECTED) { if(WiFi.getMode() != WIFI_AP && WiFi.status() != WL_CONNECTED) {
rgb_led(1, 4); // If WiFi not connected, blink three times hw.ledBlink(LED_RED, 3); // If WiFi not connected, blink three times
return; return;
} }
break; break;
@@ -403,7 +445,7 @@ void errorBlink() {
} }
void swapWifiMode() { void swapWifiMode() {
led_on(); hw.ledOn(LED_INTERNAL);
WiFiMode_t mode = WiFi.getMode(); WiFiMode_t mode = WiFi.getMode();
dnsServer.stop(); dnsServer.stop();
WiFi.disconnect(true); WiFi.disconnect(true);
@@ -423,7 +465,7 @@ void swapWifiMode() {
WiFi_connect(); WiFi_connect();
} }
delay(500); delay(500);
led_off(); hw.ledOff(LED_INTERNAL);
} }
void mqttMessageReceived(String &topic, String &payload) void mqttMessageReceived(String &topic, String &payload)
@@ -437,6 +479,7 @@ void mqttMessageReceived(String &topic, String &payload)
// Ideas could be to query for values or to initiate OTA firmware update // Ideas could be to query for values or to initiate OTA firmware update
} }
int currentMeterType = 0;
AmsData lastMqttData; AmsData lastMqttData;
void readHanPort() { void readHanPort() {
if (hanReader.read()) { if (hanReader.read()) {
@@ -448,13 +491,16 @@ void readHanPort() {
lastSuccessfulRead = millis(); lastSuccessfulRead = millis();
if(config.getMeterType() > 0) { if(config.getMeterType() > 0) {
rgb_led(RGB_GREEN, 2); if(config.getLedPinGreen() != 0xFF)
hw.ledBlink(LED_GREEN, 1);
else
hw.ledBlink(LED_INTERNAL, 1);
AmsData data(config.getMeterType(), hanReader); AmsData data(config.getMeterType(), hanReader);
if(data.getListType() > 0) { if(data.getListType() > 0) {
ws.setData(data); ws.setData(data);
if(!config.getMqttHost().isEmpty() && !config.getMqttPublishTopic().isEmpty()) { if(strlen(config.getMqttHost()) > 0 && strlen(config.getMqttPublishTopic()) > 0) {
if(config.getMqttPayloadFormat() == 0) { if(config.getMqttPayloadFormat() == 0) {
StaticJsonDocument<512> json; StaticJsonDocument<512> json;
hanToJson(json, data, hw, temperature); hanToJson(json, data, hw, temperature);
@@ -469,55 +515,55 @@ void readHanPort() {
serializeJson(json, msg); serializeJson(json, msg);
mqtt.publish(config.getMqttPublishTopic(), msg.c_str()); mqtt.publish(config.getMqttPublishTopic(), msg.c_str());
} else if(config.getMqttPayloadFormat() == 1 || config.getMqttPayloadFormat() == 2) { } else if(config.getMqttPayloadFormat() == 1 || config.getMqttPayloadFormat() == 2) {
mqtt.publish(config.getMqttPublishTopic() + "/meter/dlms/timestamp", String(data.getPackageTimestamp())); mqtt.publish(String(config.getMqttPublishTopic()) + "/meter/dlms/timestamp", String(data.getPackageTimestamp()));
switch(data.getListType()) { switch(data.getListType()) {
case 3: case 3:
// ID and type belongs to List 2, but I see no need to send that every 10s // ID and type belongs to List 2, but I see no need to send that every 10s
mqtt.publish(config.getMqttPublishTopic() + "/meter/id", data.getMeterId()); mqtt.publish(String(config.getMqttPublishTopic()) + "/meter/id", data.getMeterId());
mqtt.publish(config.getMqttPublishTopic() + "/meter/type", data.getMeterType()); mqtt.publish(String(config.getMqttPublishTopic()) + "/meter/type", data.getMeterType());
mqtt.publish(config.getMqttPublishTopic() + "/meter/clock", String(data.getMeterTimestamp())); mqtt.publish(String(config.getMqttPublishTopic()) + "/meter/clock", String(data.getMeterTimestamp()));
mqtt.publish(config.getMqttPublishTopic() + "/meter/import/reactive/accumulated", String(data.getReactiveImportCounter(), 2)); mqtt.publish(String(config.getMqttPublishTopic()) + "/meter/import/reactive/accumulated", String(data.getReactiveImportCounter(), 2));
mqtt.publish(config.getMqttPublishTopic() + "/meter/import/active/accumulated", String(data.getActiveImportCounter(), 2)); mqtt.publish(String(config.getMqttPublishTopic()) + "/meter/import/active/accumulated", String(data.getActiveImportCounter(), 2));
mqtt.publish(config.getMqttPublishTopic() + "/meter/export/reactive/accumulated", String(data.getReactiveExportCounter(), 2)); mqtt.publish(String(config.getMqttPublishTopic()) + "/meter/export/reactive/accumulated", String(data.getReactiveExportCounter(), 2));
mqtt.publish(config.getMqttPublishTopic() + "/meter/export/active/accumulated", String(data.getActiveExportCounter(), 2)); mqtt.publish(String(config.getMqttPublishTopic()) + "/meter/export/active/accumulated", String(data.getActiveExportCounter(), 2));
case 2: case 2:
// Only send data if changed. ID and Type is sent on the 10s interval only if changed // Only send data if changed. ID and Type is sent on the 10s interval only if changed
if(lastMqttData.getMeterId() != data.getMeterId() || config.getMqttPayloadFormat() == 2) { if(lastMqttData.getMeterId() != data.getMeterId() || config.getMqttPayloadFormat() == 2) {
mqtt.publish(config.getMqttPublishTopic() + "/meter/id", data.getMeterId()); mqtt.publish(String(config.getMqttPublishTopic()) + "/meter/id", data.getMeterId());
} }
if(lastMqttData.getMeterType() != data.getMeterType() || config.getMqttPayloadFormat() == 2) { if(lastMqttData.getMeterType() != data.getMeterType() || config.getMqttPayloadFormat() == 2) {
mqtt.publish(config.getMqttPublishTopic() + "/meter/type", data.getMeterType()); mqtt.publish(String(config.getMqttPublishTopic()) + "/meter/type", data.getMeterType());
} }
if(lastMqttData.getL1Current() != data.getL1Current() || config.getMqttPayloadFormat() == 2) { if(lastMqttData.getL1Current() != data.getL1Current() || config.getMqttPayloadFormat() == 2) {
mqtt.publish(config.getMqttPublishTopic() + "/meter/l1/current", String(data.getL1Current(), 2)); mqtt.publish(String(config.getMqttPublishTopic()) + "/meter/l1/current", String(data.getL1Current(), 2));
} }
if(lastMqttData.getL1Voltage() != data.getL1Voltage() || config.getMqttPayloadFormat() == 2) { if(lastMqttData.getL1Voltage() != data.getL1Voltage() || config.getMqttPayloadFormat() == 2) {
mqtt.publish(config.getMqttPublishTopic() + "/meter/l1/voltage", String(data.getL1Voltage(), 2)); mqtt.publish(String(config.getMqttPublishTopic()) + "/meter/l1/voltage", String(data.getL1Voltage(), 2));
} }
if(lastMqttData.getL2Current() != data.getL2Current() || config.getMqttPayloadFormat() == 2) { if(lastMqttData.getL2Current() != data.getL2Current() || config.getMqttPayloadFormat() == 2) {
mqtt.publish(config.getMqttPublishTopic() + "/meter/l2/current", String(data.getL2Current(), 2)); mqtt.publish(String(config.getMqttPublishTopic()) + "/meter/l2/current", String(data.getL2Current(), 2));
} }
if(lastMqttData.getL2Voltage() != data.getL2Voltage() || config.getMqttPayloadFormat() == 2) { if(lastMqttData.getL2Voltage() != data.getL2Voltage() || config.getMqttPayloadFormat() == 2) {
mqtt.publish(config.getMqttPublishTopic() + "/meter/l2/voltage", String(data.getL2Voltage(), 2)); mqtt.publish(String(config.getMqttPublishTopic()) + "/meter/l2/voltage", String(data.getL2Voltage(), 2));
} }
if(lastMqttData.getL3Current() != data.getL3Current() || config.getMqttPayloadFormat() == 2) { if(lastMqttData.getL3Current() != data.getL3Current() || config.getMqttPayloadFormat() == 2) {
mqtt.publish(config.getMqttPublishTopic() + "/meter/l3/current", String(data.getL3Current(), 2)); mqtt.publish(String(config.getMqttPublishTopic()) + "/meter/l3/current", String(data.getL3Current(), 2));
} }
if(lastMqttData.getL3Voltage() != data.getL3Voltage() || config.getMqttPayloadFormat() == 2) { if(lastMqttData.getL3Voltage() != data.getL3Voltage() || config.getMqttPayloadFormat() == 2) {
mqtt.publish(config.getMqttPublishTopic() + "/meter/l3/voltage", String(data.getL3Voltage(), 2)); mqtt.publish(String(config.getMqttPublishTopic()) + "/meter/l3/voltage", String(data.getL3Voltage(), 2));
} }
if(lastMqttData.getReactiveExportPower() != data.getReactiveExportPower() || config.getMqttPayloadFormat() == 2) { if(lastMqttData.getReactiveExportPower() != data.getReactiveExportPower() || config.getMqttPayloadFormat() == 2) {
mqtt.publish(config.getMqttPublishTopic() + "/meter/export/reactive", String(data.getReactiveExportPower())); mqtt.publish(String(config.getMqttPublishTopic()) + "/meter/export/reactive", String(data.getReactiveExportPower()));
} }
if(lastMqttData.getActiveExportPower() != data.getActiveExportPower() || config.getMqttPayloadFormat() == 2) { if(lastMqttData.getActiveExportPower() != data.getActiveExportPower() || config.getMqttPayloadFormat() == 2) {
mqtt.publish(config.getMqttPublishTopic() + "/meter/export/active", String(data.getActiveExportPower())); mqtt.publish(String(config.getMqttPublishTopic()) + "/meter/export/active", String(data.getActiveExportPower()));
} }
if(lastMqttData.getReactiveImportPower() != data.getReactiveImportPower() || config.getMqttPayloadFormat() == 2) { if(lastMqttData.getReactiveImportPower() != data.getReactiveImportPower() || config.getMqttPayloadFormat() == 2) {
mqtt.publish(config.getMqttPublishTopic() + "/meter/import/reactive", String(data.getReactiveImportPower())); mqtt.publish(String(config.getMqttPublishTopic()) + "/meter/import/reactive", String(data.getReactiveImportPower()));
} }
case 1: case 1:
if(lastMqttData.getActiveImportPower() != data.getActiveImportPower() || config.getMqttPayloadFormat() == 2) { if(lastMqttData.getActiveImportPower() != data.getActiveImportPower() || config.getMqttPayloadFormat() == 2) {
mqtt.publish(config.getMqttPublishTopic() + "/meter/import/active", String(data.getActiveImportPower())); mqtt.publish(String(config.getMqttPublishTopic()) + "/meter/import/active", String(data.getActiveImportPower()));
} }
} }
} }
@@ -565,22 +611,10 @@ void readHanPort() {
// Switch parity if meter is still not detected // Switch parity if meter is still not detected
if(config.getMeterType() == 0 && millis() - lastSuccessfulRead > 10000) { if(config.getMeterType() == 0 && millis() - lastSuccessfulRead > 10000) {
lastSuccessfulRead = millis(); lastSuccessfulRead = millis();
if(Debug.isActive(RemoteDebug::DEBUG)) debugD("No data for current setting, switching parity"); debugD("No data for current setting, switching parity");
Serial.flush(); Serial.flush();
#if SOFTWARE_SERIAL if(++currentMeterType == 4) currentMeterType = 1;
if(even) { setupHanPort(config.getHanPin(), currentMeterType);
hanSerial->begin(2400, SWSERIAL_8N1);
} else {
hanSerial->begin(2400, SWSERIAL_8E1);
}
#else
if(even) {
hanSerial->begin(2400, SERIAL_8N1);
} else {
hanSerial->begin(2400, SERIAL_8E1);
}
#endif
even = !even;
} }
} }
@@ -593,7 +627,7 @@ void WiFi_connect() {
} }
lastWifiRetry = millis(); lastWifiRetry = millis();
if (Debug.isActive(RemoteDebug::INFO)) debugI("Connecting to WiFi network: %s", config.getWifiSsid().c_str()); if (Debug.isActive(RemoteDebug::INFO)) debugI("Connecting to WiFi network: %s", config.getWifiSsid());
if (WiFi.status() != WL_CONNECTED) { if (WiFi.status() != WL_CONNECTED) {
MDNS.end(); MDNS.end();
@@ -602,7 +636,7 @@ void WiFi_connect() {
WiFi.enableAP(false); WiFi.enableAP(false);
WiFi.mode(WIFI_STA); WiFi.mode(WIFI_STA);
if(!config.getWifiIp().isEmpty()) { if(strlen(config.getWifiIp()) > 0) {
IPAddress ip, gw, sn(255,255,255,0), dns1, dns2; IPAddress ip, gw, sn(255,255,255,0), dns1, dns2;
ip.fromString(config.getWifiIp()); ip.fromString(config.getWifiIp());
gw.fromString(config.getWifiGw()); gw.fromString(config.getWifiGw());
@@ -611,21 +645,21 @@ void WiFi_connect() {
dns2.fromString(config.getWifiDns2()); dns2.fromString(config.getWifiDns2());
WiFi.config(ip, gw, sn, dns1, dns2); WiFi.config(ip, gw, sn, dns1, dns2);
} }
if(!config.getWifiHostname().isEmpty()) { if(strlen(config.getWifiHostname()) > 0) {
#if defined(ESP8266) #if defined(ESP8266)
WiFi.hostname(config.getWifiHostname()); WiFi.hostname(config.getWifiHostname());
#elif defined(ESP32) #elif defined(ESP32)
WiFi.setHostname(config.getWifiHostname().c_str()); WiFi.setHostname(config.getWifiHostname());
#endif #endif
} }
WiFi.begin(config.getWifiSsid().c_str(), config.getWifiPassword().c_str()); WiFi.begin(config.getWifiSsid(), config.getWifiPassword());
yield(); yield();
} }
} }
unsigned long lastMqttRetry = -10000; unsigned long lastMqttRetry = -10000;
void MQTT_connect() { void MQTT_connect() {
if(config.getMqttHost().isEmpty()) { if(strlen(config.getMqttHost()) == 0) {
if(Debug.isActive(RemoteDebug::WARNING)) debugW("No MQTT config"); if(Debug.isActive(RemoteDebug::WARNING)) debugW("No MQTT config");
return; return;
} }
@@ -635,7 +669,7 @@ void MQTT_connect() {
} }
lastMqttRetry = millis(); lastMqttRetry = millis();
if(Debug.isActive(RemoteDebug::INFO)) { if(Debug.isActive(RemoteDebug::INFO)) {
debugI("Connecting to MQTT %s:%d", config.getMqttHost().c_str(), config.getMqttPort()); debugI("Connecting to MQTT %s:%d", config.getMqttHost(), config.getMqttPort());
} }
mqtt.disconnect(); mqtt.disconnect();
@@ -680,22 +714,22 @@ void MQTT_connect() {
client = new WiFiClient(); client = new WiFiClient();
} }
mqtt.begin(config.getMqttHost().c_str(), config.getMqttPort(), *client); mqtt.begin(config.getMqttHost(), config.getMqttPort(), *client);
#if defined(ESP8266) #if defined(ESP8266)
if(secureClient) secureClient->setX509Time(timeClient.getEpochTime()); if(secureClient) secureClient->setX509Time(timeClient.getEpochTime());
#endif #endif
// Connect to a unsecure or secure MQTT server // Connect to a unsecure or secure MQTT server
if ((config.getMqttUser().isEmpty() && mqtt.connect(config.getMqttClientId().c_str())) || if ((strlen(config.getMqttUser()) == 0 && mqtt.connect(config.getMqttClientId())) ||
(!config.getMqttUser().isEmpty() && mqtt.connect(config.getMqttClientId().c_str(), config.getMqttUser().c_str(), config.getMqttPassword().c_str()))) { (strlen(config.getMqttUser()) > 0 && mqtt.connect(config.getMqttClientId(), config.getMqttUser(), config.getMqttPassword()))) {
if (Debug.isActive(RemoteDebug::INFO)) debugI("Successfully connected to MQTT!"); if (Debug.isActive(RemoteDebug::INFO)) debugI("Successfully connected to MQTT!");
config.ackMqttChange(); config.ackMqttChange();
// Subscribe to the chosen MQTT topic, if set in configuration // Subscribe to the chosen MQTT topic, if set in configuration
if (!config.getMqttSubscribeTopic().isEmpty()) { if (strlen(config.getMqttSubscribeTopic()) > 0) {
mqtt.subscribe(config.getMqttSubscribeTopic()); mqtt.subscribe(config.getMqttSubscribeTopic());
if (Debug.isActive(RemoteDebug::INFO)) debugI(" Subscribing to [%s]\r\n", config.getMqttSubscribeTopic().c_str()); if (Debug.isActive(RemoteDebug::INFO)) debugI(" Subscribing to [%s]\r\n", config.getMqttSubscribeTopic());
} }
if(config.getMqttPayloadFormat() == 0) { if(config.getMqttPayloadFormat() == 0) {
@@ -722,7 +756,7 @@ void MQTT_connect() {
void sendMqttData(String data) void sendMqttData(String data)
{ {
// Make sure we have configured a publish topic // Make sure we have configured a publish topic
if (config.getMqttPublishTopic().isEmpty()) if (strlen(config.getMqttPublishTopic()) == 0)
return; return;
// Build a json with the message in a "data" attribute // Build a json with the message in a "data" attribute
@@ -749,63 +783,20 @@ void sendMqttData(String data)
unsigned long lastSystemDataSent = -10000; unsigned long lastSystemDataSent = -10000;
void sendSystemStatusToMqtt() { void sendSystemStatusToMqtt() {
if (config.getMqttPublishTopic().isEmpty()) if (strlen(config.getMqttPublishTopic()) == 0)
return; return;
if(millis() - lastSystemDataSent < 10000) if(millis() - lastSystemDataSent < 10000)
return; return;
lastSystemDataSent = millis(); lastSystemDataSent = millis();
mqtt.publish(config.getMqttPublishTopic() + "/id", WiFi.macAddress()); mqtt.publish(String(config.getMqttPublishTopic()) + "/id", WiFi.macAddress());
mqtt.publish(config.getMqttPublishTopic() + "/uptime", String((unsigned long) millis64()/1000)); mqtt.publish(String(config.getMqttPublishTopic()) + "/uptime", String((unsigned long) millis64()/1000));
double vcc = hw.getVcc(); double vcc = hw.getVcc();
if(vcc > 0) { if(vcc > 0) {
mqtt.publish(config.getMqttPublishTopic() + "/vcc", String(vcc, 2)); mqtt.publish(String(config.getMqttPublishTopic()) + "/vcc", String(vcc, 2));
} }
mqtt.publish(config.getMqttPublishTopic() + "/rssi", String(hw.getWifiRssi())); mqtt.publish(String(config.getMqttPublishTopic()) + "/rssi", String(hw.getWifiRssi()));
if(temperature != DEVICE_DISCONNECTED_C) { if(temperature != DEVICE_DISCONNECTED_C) {
mqtt.publish(config.getMqttPublishTopic() + "/temperature", String(temperature, 2)); mqtt.publish(String(config.getMqttPublishTopic()) + "/temperature", String(temperature, 2));
} }
} }
void rgb_led(int color, int mode) {
// Activate red and green LEDs if RGB LED is present (HAS_RGB_LED=1)
// If no RGB LED present (HAS_RGB_LED=0 or not defined), all output goes to ESP onboard LED
// color: 1=red, 2=green, 3=yellow
// mode: 0=OFF, 1=ON, >=2 -> Short blink(s), number of blinks: (mode - 1)
#ifndef HAS_RGB_LED
#define LEDPIN_RGB_RED LED_PIN
#define LEDPIN_RGB_GREEN LED_PIN
#endif
int blinkduration = 50; // milliseconds
switch (mode) {
case RGB_OFF: //OFF
digitalWrite(LEDPIN_RGB_RED, HIGH);
digitalWrite(LEDPIN_RGB_GREEN, HIGH);
break;
case RGB_ON: //ON
switch (color) {
case RGB_RED: //Red
digitalWrite(LEDPIN_RGB_RED, LOW);
digitalWrite(LEDPIN_RGB_GREEN, HIGH);
break;
case RGB_GREEN: //Green
digitalWrite(LEDPIN_RGB_RED, HIGH);
digitalWrite(LEDPIN_RGB_GREEN, LOW);
break;
case RGB_YELLOW: //Yellow
digitalWrite(LEDPIN_RGB_RED, LOW);
digitalWrite(LEDPIN_RGB_GREEN, LOW);
break;
}
break;
default: // Blink
for(int i = 1; i < mode; i++) {
rgb_led(color, RGB_ON);
delay(blinkduration);
rgb_led(color, RGB_OFF);
if(i != mode)
delay(blinkduration);
}
break;
}
}

View File

@@ -1,44 +1,164 @@
#include "HwTools.h" #include "HwTools.h"
void HwTools::setTempSensorPin(int tempSensorPin) {
if(tempSensorPin != this->tempSensorPin) {
this->tempSensorInit = false;
if(tempSensor)
delete tempSensor;
if(oneWire)
delete oneWire;
if(tempSensorPin > 0 && tempSensorPin < 40) {
this->tempSensorPin = tempSensorPin;
pinMode(tempSensorPin, INPUT);
} else {
this->tempSensorPin = 0xFF;
}
}
}
void HwTools::setVccPin(int vccPin) {
if(vccPin > 0 && vccPin < 40) {
pinMode(vccPin, INPUT);
this->vccPin = vccPin;
} else {
this->vccPin = 0xFF;
}
}
void HwTools::setVccMultiplier(double vccMultiplier) {
this->vccMultiplier = vccMultiplier;
}
double HwTools::getVcc() { double HwTools::getVcc() {
double volts = 0.0; double volts = 0.0;
#if defined(ARDUINO_ESP8266_WEMOS_D1MINI) if(vccPin != 0xFF) {
volts = (((double) ESP.getVcc()) / 900.0); // This board has a voltage divider on VCC. #if defined(ESP8266)
#elif defined(ARDUINO_LOLIN_D32) volts = (analogRead(vccPin) / 1024.0) * 3.3;
volts = (analogRead(GPIO_NUM_35) / 4095.0) * 3.3 * 2.25; // We are actually reading battery voltage here #elif defined(ESP32)
#elif defined(ESP8266) volts = (analogRead(vccPin) / 4095.0) * 3.3;
volts = ((double) ESP.getVcc()) / 1024.0; #endif
#endif } else {
#if defined(ESP8266)
volts = ((double) ESP.getVcc()) / 1024.0;
#endif
}
#if defined(ESP_VCC_CALIB_FACTOR) return volts > 0.0 ? volts * vccMultiplier : 0.0;
return volts * ESP_VCC_CALIB_FACTOR;
#else
return volts;
#endif
} }
double HwTools::getTemperature() { double HwTools::getTemperature() {
if(tempSensorPin != 0xFF) {
if(!tempSensorInit) {
oneWire = new OneWire(tempSensorPin);
tempSensor = new DallasTemperature(this->oneWire);
tempSensor->begin();
delay(50);
tempSensor->requestTemperatures();
hasTempSensor = tempSensor->getTempCByIndex(0) != DEVICE_DISCONNECTED_C;
tempSensorInit = true;
}
#if defined TEMP_SENSOR_PIN if(hasTempSensor) {
if(!tempSensorInit) { tempSensor->requestTemperatures();
tempSensor->begin(); return tempSensor->getTempCByIndex(0);
delay(50); } else {
tempSensor->requestTemperatures(); return DEVICE_DISCONNECTED_C;
hasTempSensor = tempSensor->getTempCByIndex(0) != DEVICE_DISCONNECTED_C; }
tempSensorInit = true;
} }
if(hasTempSensor) {
tempSensor->requestTemperatures();
return tempSensor->getTempCByIndex(0);
} else {
return DEVICE_DISCONNECTED_C;
}
#endif
return DEVICE_DISCONNECTED_C; return DEVICE_DISCONNECTED_C;
} }
int HwTools::getWifiRssi() { int HwTools::getWifiRssi() {
int rssi = WiFi.RSSI(); int rssi = WiFi.RSSI();
return isnan(rssi) ? -100.0 : rssi; return isnan(rssi) ? -100.0 : rssi;
} }
void HwTools::setLed(uint8_t ledPin, bool ledInverted) {
if(ledPin > 0 && ledPin < 40) {
this->ledPin = ledPin;
this->ledInverted = ledInverted;
pinMode(ledPin, OUTPUT);
ledOff(LED_INTERNAL);
} else {
this->ledPin = 0xFF;
}
}
void HwTools::setLedRgb(uint8_t ledPinRed, uint8_t ledPinGreen, uint8_t ledPinBlue, bool ledRgbInverted) {
this->ledRgbInverted = ledRgbInverted;
if(ledPinRed > 0 && ledPinRed < 40) {
this->ledPinRed = ledPinRed;
pinMode(ledPinRed, OUTPUT);
ledOff(LED_RED);
} else {
this->ledPinRed = 0xFF;
}
if(ledPinGreen > 0 && ledPinGreen < 40) {
this->ledPinGreen = ledPinGreen;
pinMode(ledPinGreen, OUTPUT);
ledOff(LED_GREEN);
} else {
this->ledPinGreen = 0xFF;
}
if(ledPinBlue > 0 && ledPinBlue < 40) {
this->ledPinBlue = ledPinBlue;
pinMode(ledPinBlue, OUTPUT);
ledOff(LED_BLUE);
} else {
this->ledPinBlue = 0xFF;
}
}
void HwTools::ledOn(uint8_t color) {
if(color == LED_INTERNAL) {
writeLedPin(color, ledInverted ? LOW : HIGH);
} else {
writeLedPin(color, ledRgbInverted ? LOW : HIGH);
}
}
void HwTools::ledOff(uint8_t color) {
if(color == LED_INTERNAL) {
writeLedPin(color, ledInverted ? HIGH : LOW);
} else {
writeLedPin(color, ledRgbInverted ? HIGH : LOW);
}
}
void HwTools::ledBlink(uint8_t color, uint8_t blink) {
for(int i = 0; i < blink; i++) {
ledOn(color);
delay(50);
ledOff(color);
if(i != blink)
delay(50);
}
}
void HwTools::writeLedPin(uint8_t color, uint8_t state) {
switch(color) {
case LED_INTERNAL:
if(ledPin != 0xFF)
digitalWrite(ledPin, state);
break;
case LED_RED:
if(ledPinRed != 0xFF)
digitalWrite(ledPinRed, state);
break;
case LED_GREEN:
if(ledPinGreen != 0xFF)
digitalWrite(ledPinGreen, state);
break;
case LED_BLUE:
if(ledPinBlue != 0xFF)
digitalWrite(ledPinBlue, state);
break;
case LED_YELLOW:
if(ledPinRed != 0xFF && ledPinGreen != 0xFF) {
digitalWrite(ledPinRed, state);
digitalWrite(ledPinGreen, state);
}
break;
}
}

View File

@@ -12,35 +12,38 @@
#include <DallasTemperature.h> #include <DallasTemperature.h>
#include <OneWire.h> #include <OneWire.h>
#if HW_ROARFRED #define LED_INTERNAL 0
#define TEMP_SENSOR_PIN 5 #define LED_RED 1
#elif defined(ARDUINO_LOLIN_D32) #define LED_GREEN 2
#define TEMP_SENSOR_PIN 14 #define LED_BLUE 3
#elif defined(ARDUINO_ESP8266_WEMOS_D1MINI) #define LED_YELLOW 4
#define TEMP_SENSOR_PIN D5
#else
#define TEMP_SENSOR_PIN 0xFFFFFFFF
#endif
class HwTools { class HwTools {
public: public:
void setTempSensorPin(int tempSensorPin);
void setVccPin(int vccPin);
void setVccMultiplier(double vccMultiplier);
double getVcc(); double getVcc();
double getTemperature(); double getTemperature();
int getWifiRssi(); int getWifiRssi();
void setLed(uint8_t ledPin, bool ledInverted);
void setLedRgb(uint8_t ledPinRed, uint8_t ledPinGreen, uint8_t ledPinBlue, bool ledRgbInverted);
void ledOn(uint8_t color);
void ledOff(uint8_t color);
void ledBlink(uint8_t color, uint8_t blink);
HwTools() { HwTools() {};
#if defined(ARDUINO_LOLIN_D32)
pinMode(GPIO_NUM_35, INPUT);
#endif
oneWire = new OneWire(TEMP_SENSOR_PIN);
tempSensor = new DallasTemperature(this->oneWire);
};
private: private:
uint8_t tempSensorPin = -1;
uint8_t vccPin = -1;
uint8_t ledPin = -1, ledPinRed = -1, ledPinGreen = -1, ledPinBlue = -1;
bool ledInverted, ledRgbInverted;
double vccMultiplier = 1.0;
bool tempSensorInit, hasTempSensor; bool tempSensorInit, hasTempSensor;
OneWire *oneWire; OneWire *oneWire;
DallasTemperature *tempSensor; DallasTemperature *tempSensor;
void writeLedPin(uint8_t color, uint8_t state);
}; };
#endif #endif

View File

@@ -4,6 +4,7 @@
#include "root/index_html.h" #include "root/index_html.h"
#include "root/index_js.h" #include "root/index_js.h"
#include "root/setup_html.h"
#include "root/configmeter_html.h" #include "root/configmeter_html.h"
#include "root/configwifi_html.h" #include "root/configwifi_html.h"
#include "root/configmqtt_html.h" #include "root/configmqtt_html.h"
@@ -17,8 +18,9 @@
#include "Base64.h" #include "Base64.h"
AmsWebServer::AmsWebServer(RemoteDebug* Debug) { AmsWebServer::AmsWebServer(RemoteDebug* Debug, HwTools* hw) {
this->debugger = Debug; this->debugger = Debug;
this->hw = hw;
} }
void AmsWebServer::setup(AmsConfiguration* config, MQTTClient* mqtt) { void AmsWebServer::setup(AmsConfiguration* config, MQTTClient* mqtt) {
@@ -26,6 +28,7 @@ void AmsWebServer::setup(AmsConfiguration* config, MQTTClient* mqtt) {
this->mqtt = mqtt; this->mqtt = mqtt;
server.on("/", HTTP_GET, std::bind(&AmsWebServer::indexHtml, this)); server.on("/", HTTP_GET, std::bind(&AmsWebServer::indexHtml, this));
server.on("/", HTTP_POST, std::bind(&AmsWebServer::handleSetup, this));
server.on("/index.js", HTTP_GET, std::bind(&AmsWebServer::indexJs, this)); server.on("/index.js", HTTP_GET, std::bind(&AmsWebServer::indexJs, this));
server.on("/config-meter", HTTP_GET, std::bind(&AmsWebServer::configMeterHtml, this)); server.on("/config-meter", HTTP_GET, std::bind(&AmsWebServer::configMeterHtml, this));
server.on("/config-wifi", HTTP_GET, std::bind(&AmsWebServer::configWifiHtml, this)); server.on("/config-wifi", HTTP_GET, std::bind(&AmsWebServer::configWifiHtml, this));
@@ -38,7 +41,8 @@ void AmsWebServer::setup(AmsConfiguration* config, MQTTClient* mqtt) {
server.on("/save", HTTP_POST, std::bind(&AmsWebServer::handleSave, this)); server.on("/save", HTTP_POST, std::bind(&AmsWebServer::handleSave, this));
server.on("/config-system", HTTP_GET, std::bind(&AmsWebServer::configSystemHtml, this)); server.on("/config-system", HTTP_GET, std::bind(&AmsWebServer::configSystemHtml, this));
server.on("/config-system", HTTP_POST, std::bind(&AmsWebServer::uploadPost, this), std::bind(&AmsWebServer::configSystemUpload, this)); server.on("/firmware", HTTP_GET, std::bind(&AmsWebServer::firmwareHtml, this));
server.on("/firmware", HTTP_POST, std::bind(&AmsWebServer::uploadPost, this), std::bind(&AmsWebServer::firmwareUpload, this));
server.on("/restart-wait", HTTP_GET, std::bind(&AmsWebServer::restartWaitHtml, this)); server.on("/restart-wait", HTTP_GET, std::bind(&AmsWebServer::restartWaitHtml, this));
server.on("/is-alive", HTTP_GET, std::bind(&AmsWebServer::isAliveCheck, this)); server.on("/is-alive", HTTP_GET, std::bind(&AmsWebServer::isAliveCheck, this));
@@ -110,62 +114,64 @@ bool AmsWebServer::checkSecurity(byte level) {
void AmsWebServer::indexHtml() { void AmsWebServer::indexHtml() {
printD("Serving /index.html over http..."); printD("Serving /index.html over http...");
if(!checkSecurity(2)) String html;
return; if(WiFi.getMode() == WIFI_AP) {
html = String((const __FlashStringHelper*) SETUP_HTML);
html.replace("${version}", VERSION);
} else {
if(!checkSecurity(2))
return;
String html = String((const __FlashStringHelper*) INDEX_HTML); html = String((const __FlashStringHelper*) INDEX_HTML);
html.replace("${version}", VERSION); html.replace("${version}", VERSION);
if(WiFi.getMode() != WIFI_AP) { double u1 = data.getL1Voltage();
html.replace("boot.css", BOOTSTRAP_URL); double u2 = data.getL2Voltage();
double u3 = data.getL3Voltage();
double i1 = data.getL1Current();
double i2 = data.getL2Current();
double i3 = data.getL3Current();
double tpi = data.getActiveImportCounter();
double tpo = data.getActiveExportCounter();
double tqi = data.getReactiveImportCounter();
double tqo = data.getReactiveExportCounter();
html.replace("${data.P}", String(data.getActiveImportPower()));
html.replace("${data.PO}", String(data.getActiveExportPower()));
html.replace("${display.export}", config->getProductionCapacity() > 0 ? "" : "none");
html.replace("${text.import}", config->getProductionCapacity() > 0 ? "Import" : "Consumption");
html.replace("${data.U1}", u1 > 0 ? String(u1, 1) : "");
html.replace("${data.I1}", u1 > 0 ? String(i1, 1) : "");
html.replace("${display.P1}", u1 > 0 ? "" : "none");
html.replace("${data.U2}", u2 > 0 ? String(u2, 1) : "");
html.replace("${data.I2}", u2 > 0 ? String(i2, 1) : "");
html.replace("${display.P2}", u2 > 0 ? "" : "none");
html.replace("${data.U3}", u3 > 0 ? String(u3, 1) : "");
html.replace("${data.I3}", u3 > 0 ? String(i3, 1) : "");
html.replace("${display.P3}", u3 > 0 ? "" : "none");
html.replace("${data.tPI}", tpi > 0 ? String(tpi, 1) : "");
html.replace("${data.tPO}", tpi > 0 ? String(tpo, 1) : "");
html.replace("${data.tQI}", tpi > 0 ? String(tqi, 1) : "");
html.replace("${data.tQO}", tpi > 0 ? String(tqo, 1) : "");
html.replace("${display.accumulative}", tpi > 0 ? "" : "none");
double vcc = hw->getVcc();
html.replace("${vcc}", vcc > 0 ? String(vcc, 2) : "");
double temp = hw->getTemperature();
html.replace("${temp}", temp > 0 ? String(temp, 1) : "");
html.replace("${display.temp}", temp != DEVICE_DISCONNECTED_C ? "" : "none");
int rssi = hw->getWifiRssi();
html.replace("${wifi.rssi}", vcc > 0 ? String(rssi) : "");
html.replace("${wifi.channel}", WiFi.channel() > 0 ? String(WiFi.channel()) : "");
html.replace("${wifi.ssid}", !WiFi.SSID().isEmpty() ? String(WiFi.SSID()) : "");
} }
double u1 = data.getL1Voltage();
double u2 = data.getL2Voltage();
double u3 = data.getL3Voltage();
double i1 = data.getL1Current();
double i2 = data.getL2Current();
double i3 = data.getL3Current();
double tpi = data.getActiveImportCounter();
double tpo = data.getActiveExportCounter();
double tqi = data.getReactiveImportCounter();
double tqo = data.getReactiveExportCounter();
html.replace("${data.P}", String(data.getActiveImportPower()));
html.replace("${data.PO}", String(data.getActiveExportPower()));
html.replace("${display.export}", config->getProductionCapacity() > 0 ? "" : "none");
html.replace("${text.import}", config->getProductionCapacity() > 0 ? "Import" : "Consumption");
html.replace("${data.U1}", u1 > 0 ? String(u1, 1) : "");
html.replace("${data.I1}", u1 > 0 ? String(i1, 1) : "");
html.replace("${display.P1}", u1 > 0 ? "" : "none");
html.replace("${data.U2}", u2 > 0 ? String(u2, 1) : "");
html.replace("${data.I2}", u2 > 0 ? String(i2, 1) : "");
html.replace("${display.P2}", u2 > 0 ? "" : "none");
html.replace("${data.U3}", u3 > 0 ? String(u3, 1) : "");
html.replace("${data.I3}", u3 > 0 ? String(i3, 1) : "");
html.replace("${display.P3}", u3 > 0 ? "" : "none");
html.replace("${data.tPI}", tpi > 0 ? String(tpi, 1) : "");
html.replace("${data.tPO}", tpi > 0 ? String(tpo, 1) : "");
html.replace("${data.tQI}", tpi > 0 ? String(tqi, 1) : "");
html.replace("${data.tQO}", tpi > 0 ? String(tqo, 1) : "");
html.replace("${display.accumulative}", tpi > 0 ? "" : "none");
double vcc = hw.getVcc();
html.replace("${vcc}", vcc > 0 ? String(vcc, 2) : "");
double temp = hw.getTemperature();
html.replace("${temp}", temp > 0 ? String(temp, 1) : "");
html.replace("${display.temp}", temp != DEVICE_DISCONNECTED_C ? "" : "none");
int rssi = hw.getWifiRssi();
html.replace("${wifi.rssi}", vcc > 0 ? String(rssi) : "");
html.replace("${wifi.channel}", WiFi.channel() > 0 ? String(WiFi.channel()) : "");
html.replace("${wifi.ssid}", !WiFi.SSID().isEmpty() ? String(WiFi.SSID()) : "");
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache"); server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "-1"); server.sendHeader("Expires", "-1");
@@ -190,10 +196,6 @@ void AmsWebServer::configMeterHtml() {
String html = String((const __FlashStringHelper*) CONFIGMETER_HTML); String html = String((const __FlashStringHelper*) CONFIGMETER_HTML);
html.replace("${version}", VERSION); html.replace("${version}", VERSION);
if(WiFi.getMode() != WIFI_AP) {
html.replace("boot.css", BOOTSTRAP_URL);
}
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache"); server.sendHeader("Pragma", "no-cache");
@@ -224,16 +226,12 @@ void AmsWebServer::configWifiHtml() {
String html = String((const __FlashStringHelper*) CONFIGWIFI_HTML); String html = String((const __FlashStringHelper*) CONFIGWIFI_HTML);
html.replace("${version}", VERSION); html.replace("${version}", VERSION);
if(WiFi.getMode() != WIFI_AP) {
html.replace("boot.css", BOOTSTRAP_URL);
}
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache"); server.sendHeader("Pragma", "no-cache");
html.replace("${config.wifiSsid}", config->getWifiSsid()); html.replace("${config.wifiSsid}", config->getWifiSsid());
html.replace("${config.wifiPassword}", config->getWifiPassword()); html.replace("${config.wifiPassword}", config->getWifiPassword());
html.replace("${config.wifiIpType1}", config->getWifiIp().isEmpty() ? "" : "selected"); html.replace("${config.wifiIpType1}", strlen(config->getWifiIp()) > 0 ? "selected" : "");
html.replace("${config.wifiIp}", config->getWifiIp()); html.replace("${config.wifiIp}", config->getWifiIp());
html.replace("${config.wifiGw}", config->getWifiGw()); html.replace("${config.wifiGw}", config->getWifiGw());
html.replace("${config.wifiSubnet}", config->getWifiSubnet()); html.replace("${config.wifiSubnet}", config->getWifiSubnet());
@@ -254,14 +252,10 @@ void AmsWebServer::configMqttHtml() {
String html = String((const __FlashStringHelper*) CONFIGMQTT_HTML); String html = String((const __FlashStringHelper*) CONFIGMQTT_HTML);
html.replace("${version}", VERSION); html.replace("${version}", VERSION);
if(WiFi.getMode() != WIFI_AP) {
html.replace("boot.css", BOOTSTRAP_URL);
}
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache"); server.sendHeader("Pragma", "no-cache");
html.replace("${config.mqtt}", config->getMqttHost() == 0 ? "" : "checked"); html.replace("${config.mqtt}", strlen(config->getMqttHost()) == 0 ? "" : "checked");
html.replace("${config.mqttHost}", config->getMqttHost()); html.replace("${config.mqttHost}", config->getMqttHost());
if(config->getMqttPort() > 0) { if(config->getMqttPort() > 0) {
html.replace("${config.mqttPort}", String(config->getMqttPort())); html.replace("${config.mqttPort}", String(config->getMqttPort()));
@@ -311,10 +305,6 @@ void AmsWebServer::configWebHtml() {
String html = String((const __FlashStringHelper*) CONFIGWEB_HTML); String html = String((const __FlashStringHelper*) CONFIGWEB_HTML);
html.replace("${version}", VERSION); html.replace("${version}", VERSION);
if(WiFi.getMode() != WIFI_AP) {
html.replace("boot.css", BOOTSTRAP_URL);
}
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache"); server.sendHeader("Pragma", "no-cache");
@@ -414,10 +404,10 @@ void AmsWebServer::dataJson() {
json["maxPower"] = maxPwr; json["maxPower"] = maxPwr;
json["meterType"] = config->getMeterType(); json["meterType"] = config->getMeterType();
json["uptime_seconds"] = millis64() / 1000; json["uptime_seconds"] = millis64() / 1000;
double vcc = hw.getVcc(); double vcc = hw->getVcc();
json["vcc"] = serialized(String(vcc, 3)); json["vcc"] = serialized(String(vcc, 3));
double temp = hw.getTemperature(); double temp = hw->getTemperature();
json["temp"] = serialized(String(temp, 2)); json["temp"] = serialized(String(temp, 2));
json.createNestedObject("wifi"); json.createNestedObject("wifi");
@@ -455,7 +445,7 @@ void AmsWebServer::dataJson() {
json["status"]["han"] = hanStatus; json["status"]["han"] = hanStatus;
String wifiStatus; String wifiStatus;
if(config->getWifiSsid().isEmpty()) { if(strlen(config->getWifiSsid()) == 0) {
wifiStatus = "secondary"; wifiStatus = "secondary";
} else if(rssi > -75) { } else if(rssi > -75) {
wifiStatus = "success"; wifiStatus = "success";
@@ -467,7 +457,7 @@ void AmsWebServer::dataJson() {
json["status"]["wifi"] = wifiStatus; json["status"]["wifi"] = wifiStatus;
String mqttStatus; String mqttStatus;
if(config->getMqttHost().isEmpty()) { if(strlen(config->getMqttHost()) == 0) {
mqttStatus = "secondary"; mqttStatus = "secondary";
} else if(mqtt->connected()) { } else if(mqtt->connected()) {
mqttStatus = "success"; mqttStatus = "success";
@@ -491,6 +481,100 @@ void AmsWebServer::dataJson() {
server.send(200, "application/json", jsonStr); server.send(200, "application/json", jsonStr);
} }
void AmsWebServer::handleSetup() {
if(!server.hasArg("wifiSsid") || server.arg("wifiSsid").isEmpty() || !server.hasArg("wifiPassword") || server.arg("wifiPassword").isEmpty()) {
server.sendHeader("Location", String("/"), true);
server.send (302, "text/plain", "");
} else {
config->setVccMultiplier(1.0);
config->setVccBootLimit(0);
switch(server.arg("board").toInt()) {
case 0: // roarfred
config->setHanPin(3);
config->setApPin(0);
config->setLedPin(2);
config->setLedInverted(true);
config->setTempSensorPin(5);
break;
case 1: // Arnio Kamstrup
config->setHanPin(3);
config->setApPin(0);
config->setLedPin(2);
config->setLedInverted(true);
config->setTempSensorPin(5);
config->setLedPinRed(13);
config->setLedPinGreen(14);
config->setLedRgbInverted(true);
break;
case 2: // spenceme
config->setHanPin(3);
config->setApPin(0);
config->setLedPin(2);
config->setLedInverted(true);
config->setTempSensorPin(5);
config->setVccBootLimit(3.3);
break;
case 101: // D1
config->setHanPin(5);
config->setApPin(4);
config->setLedPin(2);
config->setLedInverted(true);
config->setTempSensorPin(14);
config->setVccMultiplier(1.1);
break;
case 199: // ESP8266
config->setHanPin(3);
config->setLedPin(2);
config->setLedInverted(true);
break;
case 201: // D32
config->setHanPin(16);
config->setApPin(4);
config->setLedPin(5);
config->setLedInverted(true);
config->setTempSensorPin(14);
config->setVccPin(35);
config->setVccMultiplier(2.25);
break;
case 202: // Feather ESP32
config->setHanPin(16);
config->setLedPin(2);
config->setLedInverted(false);
config->setTempSensorPin(14);
break;
case 299: // ESP32
config->setHanPin(16);
config->setLedPin(2);
config->setLedInverted(true);
config->setTempSensorPin(14);
break;
}
config->setMeterType(server.arg("meterType").toInt());
config->setWifiSsid(server.arg("wifiSsid").c_str());
config->setWifiPassword(server.arg("wifiPassword").c_str());
if(server.hasArg("wifiIpType") && server.arg("wifiIpType").toInt() == 1) {
config->setWifiIp(server.arg("wifiIp").c_str());
config->setWifiGw(server.arg("wifiGw").c_str());
config->setWifiSubnet(server.arg("wifiSubnet").c_str());
config->setWifiDns1(server.arg("wifiDns1").c_str());
} else {
config->clearWifiIp();
}
if(server.hasArg("wifiHostname") && !server.arg("wifiHostname").isEmpty()) {
config->setWifiHostname(server.arg("wifiHostname").c_str());
}
if(config->save()) {
performRestart = true;
server.sendHeader("Location","/restart-wait");
server.send(303);
} else {
printE("Error saving configuration");
String html = "<html><body><h1>Error saving configuration!</h1></form>";
server.send(500, "text/html", html);
}
}
}
void AmsWebServer::handleSave() { void AmsWebServer::handleSave() {
String temp; String temp;
@@ -502,30 +586,30 @@ void AmsWebServer::handleSave() {
} }
if(server.hasArg("wifiConfig") && server.arg("wifiConfig") == "true") { if(server.hasArg("wifiConfig") && server.arg("wifiConfig") == "true") {
config->setWifiSsid(server.arg("wifiSsid")); config->setWifiSsid(server.arg("wifiSsid").c_str());
config->setWifiPassword(server.arg("wifiPassword")); config->setWifiPassword(server.arg("wifiPassword").c_str());
if(server.hasArg("wifiIpType") && server.arg("wifiIpType").toInt() == 1) { if(server.hasArg("wifiIpType") && server.arg("wifiIpType").toInt() == 1) {
config->setWifiIp(server.arg("wifiIp")); config->setWifiIp(server.arg("wifiIp").c_str());
config->setWifiGw(server.arg("wifiGw")); config->setWifiGw(server.arg("wifiGw").c_str());
config->setWifiSubnet(server.arg("wifiSubnet")); config->setWifiSubnet(server.arg("wifiSubnet").c_str());
config->setWifiDns1(server.arg("wifiDns1")); config->setWifiDns1(server.arg("wifiDns1").c_str());
config->setWifiDns2(server.arg("wifiDns2")); config->setWifiDns2(server.arg("wifiDns2").c_str());
} else { } else {
config->clearWifiIp(); config->clearWifiIp();
} }
config->setWifiHostname(server.arg("wifiHostname")); config->setWifiHostname(server.arg("wifiHostname").c_str());
} }
if(server.hasArg("mqttConfig") && server.arg("mqttConfig") == "true") { if(server.hasArg("mqttConfig") && server.arg("mqttConfig") == "true") {
if(server.hasArg("mqtt") && server.arg("mqtt") == "true") { if(server.hasArg("mqtt") && server.arg("mqtt") == "true") {
config->setMqttHost(server.arg("mqttHost")); config->setMqttHost(server.arg("mqttHost").c_str());
int port = server.arg("mqttPort").toInt(); int port = server.arg("mqttPort").toInt();
config->setMqttPort(port == 0 ? 1883 : port); config->setMqttPort(port == 0 ? 1883 : port);
config->setMqttClientId(server.arg("mqttClientId")); config->setMqttClientId(server.arg("mqttClientId").c_str());
config->setMqttPublishTopic(server.arg("mqttPublishTopic")); config->setMqttPublishTopic(server.arg("mqttPublishTopic").c_str());
config->setMqttSubscribeTopic(server.arg("mqttSubscribeTopic")); config->setMqttSubscribeTopic(server.arg("mqttSubscribeTopic").c_str());
config->setMqttUser(server.arg("mqttUser")); config->setMqttUser(server.arg("mqttUser").c_str());
config->setMqttPassword(server.arg("mqttPassword")); config->setMqttPassword(server.arg("mqttPassword").c_str());
config->setMqttPayloadFormat(server.arg("mqttPayloadFormat").toInt()); config->setMqttPayloadFormat(server.arg("mqttPayloadFormat").toInt());
config->setMqttSsl(server.arg("mqttSsl") == "true"); config->setMqttSsl(server.arg("mqttSsl") == "true");
} else { } else {
@@ -536,8 +620,8 @@ void AmsWebServer::handleSave() {
if(server.hasArg("authConfig") && server.arg("authConfig") == "true") { if(server.hasArg("authConfig") && server.arg("authConfig") == "true") {
config->setAuthSecurity((byte)server.arg("authSecurity").toInt()); config->setAuthSecurity((byte)server.arg("authSecurity").toInt());
if(config->getAuthSecurity() > 0) { if(config->getAuthSecurity() > 0) {
config->setAuthUser(server.arg("authUser")); config->setAuthUser(server.arg("authUser").c_str());
config->setAuthPassword(server.arg("authPassword")); config->setAuthPassword(server.arg("authPassword").c_str());
debugger->setPassword(config->getAuthPassword()); debugger->setPassword(config->getAuthPassword());
} else { } else {
debugger->setPassword(""); debugger->setPassword("");
@@ -546,6 +630,19 @@ void AmsWebServer::handleSave() {
} }
if(server.hasArg("sysConfig") && server.arg("sysConfig") == "true") { if(server.hasArg("sysConfig") && server.arg("sysConfig") == "true") {
config->setHanPin(server.arg("hanPin").toInt());
config->setLedPin(server.arg("ledPin").toInt());
config->setLedInverted(server.hasArg("ledInverted") && server.arg("ledInverted") == "true");
config->setLedPinRed(server.arg("ledPinRed").toInt());
config->setLedPinGreen(server.arg("ledPinGreen").toInt());
config->setLedPinBlue(server.arg("ledPinBlue").toInt());
config->setLedRgbInverted(server.hasArg("ledRgbInverted") && server.arg("ledRgbInverted") == "true");
config->setApPin(server.arg("apPin").toInt());
config->setTempSensorPin(server.arg("tempSensorPin").toInt());
config->setVccPin(server.arg("vccPin").toInt());
config->setVccMultiplier(server.arg("vccMultiplier").toDouble());
config->setVccBootLimit(server.arg("vccBootLimit").toDouble());
config->setDebugTelnet(server.hasArg("debugTelnet") && server.arg("debugTelnet") == "true"); config->setDebugTelnet(server.hasArg("debugTelnet") && server.arg("debugTelnet") == "true");
config->setDebugSerial(server.hasArg("debugSerial") && server.arg("debugSerial") == "true"); config->setDebugSerial(server.hasArg("debugSerial") && server.arg("debugSerial") == "true");
config->setDebugLevel(server.arg("debugLevel").toInt()); config->setDebugLevel(server.arg("debugLevel").toInt());
@@ -575,6 +672,12 @@ void AmsWebServer::handleSave() {
} else { } else {
server.sendHeader("Location", String("/"), true); server.sendHeader("Location", String("/"), true);
server.send (302, "text/plain", ""); server.send (302, "text/plain", "");
hw->setLed(config->getLedPin(), config->isLedInverted());
hw->setLedRgb(config->getLedPinRed(), config->getLedPinGreen(), config->getLedPinBlue(), config->isLedRgbInverted());
hw->setTempSensorPin(config->getTempSensorPin());
hw->setVccPin(config->getVccPin());
hw->setVccMultiplier(config->getVccMultiplier());
} }
} else { } else {
printE("Error saving configuration"); printE("Error saving configuration");
@@ -592,9 +695,26 @@ void AmsWebServer::configSystemHtml() {
String html = String((const __FlashStringHelper*) CONFIGSYSTEM_HTML); String html = String((const __FlashStringHelper*) CONFIGSYSTEM_HTML);
html.replace("${version}", VERSION); html.replace("${version}", VERSION);
if(WiFi.getMode() != WIFI_AP) { #if defined(ESP32)
html.replace("boot.css", BOOTSTRAP_URL); html.replace("${gpio.max}", "39");
} #else
html.replace("${gpio.max}", "16");
#endif
html.replace("${options.han}", getSerialSelectOptions(config->getHanPin()));
html.replace("${config.ledPin}", config->getLedPin() == 0xFF ? "" : String(config->getLedPin()));
html.replace("${config.ledInverted}", String(config->isLedInverted()) ? "checked" : "");
html.replace("${config.ledPinRed}", config->getLedPinRed() == 0xFF ? "" : String(config->getLedPinRed()));
html.replace("${config.ledPinGreen}", config->getLedPinGreen() == 0xFF ? "" : String(config->getLedPinGreen()));
html.replace("${config.ledPinBlue}", config->getLedPinBlue() == 0xFF ? "" : String(config->getLedPinBlue()));
html.replace("${config.ledRgbInverted}", String(config->isLedRgbInverted() ? "checked" : ""));
html.replace("${config.apPin}", config->getApPin() == 0xFF ? "" : String(config->getApPin()));
html.replace("${config.tempSensorPin}", config->getTempSensorPin() == 0xFF ? "" : String(config->getTempSensorPin()));
html.replace("${config.vccPin}", config->getVccPin() == 0xFF ? "" : String(config->getVccPin()));
html.replace("${config.vccMultiplier}", String(config->getVccMultiplier()));
html.replace("${config.vccBootLimit}", String(config->getVccBootLimit()));
html.replace("${config.debugTelnet}", config->isDebugTelnet() ? "checked" : ""); html.replace("${config.debugTelnet}", config->isDebugTelnet() ? "checked" : "");
html.replace("${config.debugSerial}", config->isDebugSerial() ? "checked" : ""); html.replace("${config.debugSerial}", config->isDebugSerial() ? "checked" : "");
@@ -610,6 +730,44 @@ void AmsWebServer::configSystemHtml() {
server.send(200, "text/html", html); server.send(200, "text/html", html);
} }
String AmsWebServer::getSerialSelectOptions(int selected) {
String gpioOptions;
if(selected == 3) {
gpioOptions += "<option value=\"3\" selected>UART0</option>\n";
} else {
gpioOptions += "<option value=\"3\">UART0</option>\n";
}
#if defined(ESP32)
int numGpio = 24;
int gpios[] = {4,5,6,7,8,10,11,12,13,14,15,17,18,19,21,22,23,25,32,33,34,35,36,39};
if(selected == 9) {
gpioOptions += "<option value=\"9\" selected>UART1</option>\n";
} else {
gpioOptions += "<option value=\"9\">UART1</option>\n";
}
if(selected == 16) {
gpioOptions += "<option value=\"16\" selected>UART2</option>\n";
} else {
gpioOptions += "<option value=\"16\">UART2</option>\n";
}
#elif defined(ESP8266)
int numGpio = 9;
int gpios[] = {4,5,9,10,12,13,14,15,16};
#endif
for(int i = 0; i < numGpio; i++) {
int gpio = gpios[i];
char buffer [16];
sprintf(buffer, "%02u", gpio);
if(gpio == selected) {
gpioOptions += "<option value=\"" + String(gpio) + "\" selected>GPIO" + buffer + "</option>\n";
} else {
gpioOptions += "<option value=\"" + String(gpio) + "\">GPIO" + buffer + "</option>\n";
}
}
return gpioOptions;
}
void AmsWebServer::uploadPost() { void AmsWebServer::uploadPost() {
server.send(200); server.send(200);
} }
@@ -648,7 +806,13 @@ void AmsWebServer::deleteFile(const char* path) {
} }
} }
void AmsWebServer::configSystemUpload() { void AmsWebServer::firmwareHtml() {
printD("Serving /firmware.html over http...");
uploadHtml("CA file", "/firmware", "mqtt");
}
void AmsWebServer::firmwareUpload() {
HTTPUpload& upload = server.upload(); HTTPUpload& upload = server.upload();
if(upload.status == UPLOAD_FILE_START) { if(upload.status == UPLOAD_FILE_START) {
String filename = upload.filename; String filename = upload.filename;
@@ -676,7 +840,7 @@ void AmsWebServer::restartWaitHtml() {
if(WiFi.getMode() != WIFI_AP) { if(WiFi.getMode() != WIFI_AP) {
html.replace("boot.css", BOOTSTRAP_URL); html.replace("boot.css", BOOTSTRAP_URL);
} }
if(config->getWifiIp().isEmpty() && WiFi.getMode() != WIFI_AP) { if(strlen(config->getWifiIp()) == 0 && WiFi.getMode() != WIFI_AP) {
html.replace("${ip}", WiFi.localIP().toString()); html.replace("${ip}", WiFi.localIP().toString());
} else { } else {
html.replace("${ip}", config->getWifiIp()); html.replace("${ip}", config->getWifiIp());
@@ -713,10 +877,6 @@ void AmsWebServer::uploadHtml(const char* label, const char* action, const char*
html.replace("${form.action}", action); html.replace("${form.action}", action);
html.replace("${version}", VERSION); html.replace("${version}", VERSION);
if(WiFi.getMode() != WIFI_AP) {
html.replace("boot.css", BOOTSTRAP_URL);
}
html.replace("${menu." + String(menu) + ".class}", "active"); html.replace("${menu." + String(menu) + ".class}", "active");
html.replace("${menu.meter.class}", ""); html.replace("${menu.meter.class}", "");
html.replace("${menu.wifi.class}", ""); html.replace("${menu.wifi.class}", "");
@@ -737,10 +897,6 @@ void AmsWebServer::deleteHtml(const char* label, const char* action, const char*
html.replace("${form.action}", action); html.replace("${form.action}", action);
html.replace("${version}", VERSION); html.replace("${version}", VERSION);
if(WiFi.getMode() != WIFI_AP) {
html.replace("boot.css", BOOTSTRAP_URL);
}
html.replace("${menu." + String(menu) + ".class}", "active"); html.replace("${menu." + String(menu) + ".class}", "active");
html.replace("${menu.meter.class}", ""); html.replace("${menu.meter.class}", "");
html.replace("${menu.wifi.class}", ""); html.replace("${menu.wifi.class}", "");
@@ -759,7 +915,6 @@ void AmsWebServer::deleteHtml(const char* label, const char* action, const char*
void AmsWebServer::mqttCa() { void AmsWebServer::mqttCa() {
printD("Serving /mqtt-ca.html over http..."); printD("Serving /mqtt-ca.html over http...");
String html;
if(SPIFFS.begin()) { if(SPIFFS.begin()) {
if(SPIFFS.exists(FILE_MQTT_CA)) { if(SPIFFS.exists(FILE_MQTT_CA)) {
deleteHtml("CA file", "/mqtt-ca/delete", "mqtt"); deleteHtml("CA file", "/mqtt-ca/delete", "mqtt");
@@ -797,7 +952,6 @@ void AmsWebServer::mqttCaDelete() {
void AmsWebServer::mqttCert() { void AmsWebServer::mqttCert() {
printD("Serving /mqtt-cert.html over http..."); printD("Serving /mqtt-cert.html over http...");
String html;
if(SPIFFS.begin()) { if(SPIFFS.begin()) {
if(SPIFFS.exists(FILE_MQTT_CERT)) { if(SPIFFS.exists(FILE_MQTT_CERT)) {
deleteHtml("Certificate", "/mqtt-cert/delete", "mqtt"); deleteHtml("Certificate", "/mqtt-cert/delete", "mqtt");
@@ -835,7 +989,6 @@ void AmsWebServer::mqttCertDelete() {
void AmsWebServer::mqttKey() { void AmsWebServer::mqttKey() {
printD("Serving /mqtt-key.html over http..."); printD("Serving /mqtt-key.html over http...");
String html;
if(SPIFFS.begin()) { if(SPIFFS.begin()) {
if(SPIFFS.exists(FILE_MQTT_KEY)) { if(SPIFFS.exists(FILE_MQTT_KEY)) {
deleteHtml("Private key", "/mqtt-key/delete", "mqtt"); deleteHtml("Private key", "/mqtt-key/delete", "mqtt");

View File

@@ -31,7 +31,7 @@
class AmsWebServer { class AmsWebServer {
public: public:
AmsWebServer(RemoteDebug* Debug); AmsWebServer(RemoteDebug* Debug, HwTools* hw);
void setup(AmsConfiguration* config, MQTTClient* mqtt); void setup(AmsConfiguration* config, MQTTClient* mqtt);
void loop(); void loop();
@@ -40,7 +40,7 @@ public:
private: private:
RemoteDebug* debugger; RemoteDebug* debugger;
int maxPwr = 0; int maxPwr = 0;
HwTools hw; HwTools* hw;
AmsConfiguration* config; AmsConfiguration* config;
AmsData data; AmsData data;
MQTTClient* mqtt; MQTTClient* mqtt;
@@ -65,10 +65,13 @@ private:
void gaugemeterJs(); void gaugemeterJs();
void dataJson(); void dataJson();
void handleSetup();
void handleSave(); void handleSave();
void configSystemHtml(); void configSystemHtml();
void configSystemUpload(); String getSerialSelectOptions(int selected);
void firmwareHtml();
void firmwareUpload();
void restartWaitHtml(); void restartWaitHtml();
void isAliveCheck(); void isAliveCheck();

View File

@@ -56,24 +56,9 @@ body {
.bg-purple { .bg-purple {
background-color: var(--purple); background-color: var(--purple);
} }
.text-white-50 {
color: rgba(255,255,255,.5)!important;
}
.mb-0, .my-0 {
margin-bottom: 0!important;
}
.m-2 {
margin: .5rem!important;
}
.mb-2, .my-2 {
margin-bottom: .5rem!important;
}
.mt-2, .my-2 { .mt-2, .my-2 {
margin-top: .5rem!important; margin-top: .5rem!important;
} }
.pb-2, .py-2 {
padding-bottom: .5rem!important;
}
.p-3 { .p-3 {
padding: 1rem!important; padding: 1rem!important;
} }
@@ -89,19 +74,9 @@ body {
.shadow { .shadow {
box-shadow: 0 .5rem 1rem rgba(0,0,0,.15)!important; box-shadow: 0 .5rem 1rem rgba(0,0,0,.15)!important;
} }
.align-items-center {
-ms-flex-align: center!important;
align-items: center!important;
}
.border-bottom {
border-bottom: 1px solid #dee2e6!important;
}
.rounded { .rounded {
border-radius: .25rem!important; border-radius: .25rem!important;
} }
div {
display: block;
}
.container { .container {
width: 100%; width: 100%;
padding-right: 15px; padding-right: 15px;
@@ -115,15 +90,9 @@ article, aside, figcaption, figure, footer, header, hgroup, main, nav, section {
main { main {
display: block; display: block;
} }
.text-white {
color: #fff!important;
}
.text-right { .text-right {
text-align: right!important; text-align: right!important;
} }
.text-center {
text-align: center!important;
}
.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 { .h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 {
margin-bottom: .5rem; margin-bottom: .5rem;
font-weight: 500; font-weight: 500;
@@ -133,6 +102,9 @@ h1, h2, h3, h4, h5, h6 {
margin-top: 0; margin-top: 0;
margin-bottom: .5rem; margin-bottom: .5rem;
} }
.h5, h5 {
font-size: 1.25rem;
}
.h6, h6 { .h6, h6 {
font-size: 1rem; font-size: 1rem;
} }
@@ -150,48 +122,14 @@ h1, h2, h3, h4, h5, h6 {
padding-right: 15px; padding-right: 15px;
padding-left: 15px; padding-left: 15px;
} }
.col-2 {
-ms-flex: 0 0 16.666667%;
flex: 0 0 16.666667%;
max-width: 16.666667%;
}
.col-3 {
-ms-flex: 0 0 25%;
flex: 0 0 25%;
max-width: 25%;
}
.col-4 {
-ms-flex: 0 0 33.333333%;
flex: 0 0 33.333333%;
max-width: 33.333333%;
}
.col-5 {
-ms-flex: 0 0 41.666667%;
flex: 0 0 41.666667%;
max-width: 41.666667%;
}
.col-6 { .col-6 {
-ms-flex: 0 0 50%; -ms-flex: 0 0 50%;
flex: 0 0 50%; flex: 0 0 50%;
max-width: 50%; max-width: 50%;
} }
.col-8 {
-ms-flex: 0 0 66.666667%;
flex: 0 0 66.666667%;
max-width: 66.666667%;
}
.col-9 {
-ms-flex: 0 0 75%;
flex: 0 0 75%;
max-width: 75%;
}
.d-none { .d-none {
display: none!important; display: none!important;
} }
.d-flex {
display: -ms-flexbox!important;
display: flex!important;
}
.flex-row { .flex-row {
-ms-flex-direction: row!important; -ms-flex-direction: row!important;
flex-direction: row!important; flex-direction: row!important;
@@ -231,20 +169,11 @@ a {
line-height: 1.5; line-height: 1.5;
border-radius: .2rem; border-radius: .2rem;
} }
.btn-outline-secondary {
color: #6c757d;
border-color: #6c757d;
}
.btn-primary { .btn-primary {
color: #fff; color: #fff;
background-color: #007bff; background-color: #007bff;
border-color: #007bff; border-color: #007bff;
} }
.btn-danger {
color: #fff;
background-color: #dc3545;
border-color: #dc3545;
}
.navbar { .navbar {
position: relative; position: relative;
display: -ms-flexbox; display: -ms-flexbox;
@@ -306,46 +235,6 @@ a {
.navbar-dark .navbar-nav .active>.nav-link, .navbar-dark .navbar-nav .nav-link.active, .navbar-dark .navbar-nav .nav-link.show, .navbar-dark .navbar-nav .show>.nav-link { .navbar-dark .navbar-nav .active>.nav-link, .navbar-dark .navbar-nav .nav-link.active, .navbar-dark .navbar-nav .nav-link.show, .navbar-dark .navbar-nav .show>.nav-link {
color: #fff; color: #fff;
} }
.badge {
display: inline-block;
padding: .25em .4em;
font-size: 75%;
font-weight: 700;
line-height: 1;
text-align: center;
white-space: nowrap;
vertical-align: baseline;
border-radius: .25rem;
transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
}
.badge-secondary {
color: #fff;
background-color: #6c757d;
}
.badge-success {
color: #fff;
background-color: #28a745;
}
.badge-warning {
color: #212529;
background-color: #ffc107;
}
.badge-danger {
color: #fff;
background-color: #dc3545;
}
.alert {
position: relative;
padding: .75rem 1.25rem;
margin-bottom: 1rem;
border: 1px solid transparent;
border-radius: .25rem;
}
.alert-warning {
color: #856404;
background-color: #fff3cd;
border-color: #ffeeba;
}
.form-group { .form-group {
margin-bottom: 1rem; margin-bottom: 1rem;
} }
@@ -397,6 +286,9 @@ input[type="radio"], input[type="checkbox"] {
.input-group-append { .input-group-append {
margin-left: -1px; margin-left: -1px;
} }
.input-group-prepend {
margin-right: -1px;
}
.input-group-append, .input-group-prepend { .input-group-append, .input-group-prepend {
display: -ms-flexbox; display: -ms-flexbox;
display: flex; display: flex;
@@ -405,6 +297,14 @@ input[type="radio"], input[type="checkbox"] {
border-top-left-radius: 0; border-top-left-radius: 0;
border-bottom-left-radius: 0; border-bottom-left-radius: 0;
} }
.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle), .input-group>.input-group-append:last-child>.input-group-text:not(:last-child), .input-group>.input-group-append:not(:last-child)>.btn, .input-group>.input-group-append:not(:last-child)>.input-group-text, .input-group>.input-group-prepend>.btn, .input-group>.input-group-prepend>.input-group-text {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.input-group>.custom-select:not(:first-child), .input-group>.form-control:not(:first-child) {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.input-group-text { .input-group-text {
display: -ms-flexbox; display: -ms-flexbox;
display: flex; display: flex;
@@ -464,6 +364,11 @@ dl, ol, ul {
.container, .container-sm { .container, .container-sm {
max-width: 540px; max-width: 540px;
} }
.col-sm-6 {
-ms-flex: 0 0 50%;
flex: 0 0 50%;
max-width: 50%;
}
} }
@media (min-width: 768px) { @media (min-width: 768px) {
.container, .container-md, .container-sm { .container, .container-md, .container-sm {
@@ -474,11 +379,6 @@ dl, ol, ul {
flex: 0 0 25%; flex: 0 0 25%;
max-width: 25%; max-width: 25%;
} }
.col-md-4 {
-ms-flex: 0 0 33.333333%;
flex: 0 0 33.333333%;
max-width: 33.333333%;
}
.col-md-6 { .col-md-6 {
-ms-flex: 0 0 50%; -ms-flex: 0 0 50%;
flex: 0 0 50%; flex: 0 0 50%;
@@ -510,6 +410,21 @@ dl, ol, ul {
.container, .container-lg, .container-md, .container-sm, .container-xl { .container, .container-lg, .container-md, .container-sm, .container-xl {
max-width: 1140px; max-width: 1140px;
} }
.col-xl-2 {
-ms-flex: 0 0 16.666667%;
flex: 0 0 16.666667%;
max-width: 16.666667%;
}
.col-xl-3 {
-ms-flex: 0 0 25%;
flex: 0 0 25%;
max-width: 25%;
}
.col-xl-4 {
-ms-flex: 0 0 33.333333%;
flex: 0 0 33.333333%;
max-width: 33.333333%;
}
} }
*, ::after, ::before { *, ::after, ::before {

View File

@@ -4,7 +4,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>AMS reader - Meter configuration</title> <title>AMS reader - Meter configuration</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" type="text/css" href="boot.css"/> <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css"/>
</head> </head>
<body class="bg-light"> <body class="bg-light">
<main role="main" class="container"> <main role="main" class="container">

View File

@@ -2,9 +2,9 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>AMS reader - WiFi configuration</title> <title>AMS reader - MQTT configuration</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" type="text/css" href="boot.css"/> <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css"/>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
</head> </head>
<body class="bg-light"> <body class="bg-light">

View File

@@ -4,7 +4,6 @@
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>AMS reader - System configuration</title> <title>AMS reader - System configuration</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" type="text/css" href="boot.css"/>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css"/> <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css"/>
</head> </head>
<body class="bg-light"> <body class="bg-light">
@@ -38,10 +37,97 @@
</li> </li>
</ul> </ul>
</header> </header>
<div class="alert alert-warning">!!WARNING!!<br/>Do not change anything here unless you know exactly what you are doing! Changing things here coulds cause the device to stop responding</div>
<form method="post" action="/save"> <form method="post" action="/save">
<input type="hidden" name="sysConfig" value="true"/> <input type="hidden" name="sysConfig" value="true"/>
<div class="my-3 p-3 bg-white rounded shadow"> <div class="my-3 p-3 bg-white rounded shadow">
<div class="alert alert-warning">!!WARNING!!<br/>Remote debugging is considered insecure and should not be left enabled when not debugging</div> <h6>GPIO settings</h6>
<div class="row">
<div class="col-xl-2 col-md-3 col-sm-6 col-6">
<div class="m-2 input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">HAN</span>
</div>
<select name="hanPin" class="form-control">
${options.han}
</select>
</div>
</div>
<div class="col-xl-2 col-lg-3 col-md-4 col-sm-5 col-6">
<div class="m-2 input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">LED</span>
</div>
<input name="ledPin" type="number" min="2" max="${gpio.max}" class="form-control" value="${config.ledPin}"/>
<div class="input-group-append" title="Inverted">
<label class="input-group-text">
<input type="checkbox" name="ledInverted" ${config.ledInverted}/> inv
</label>
</div>
</div>
</div>
<div class="col-xl-3 col-lg-4 col-md-6">
<div class="m-2 input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">RGB</span>
</div>
<input name="ledPinRed" type="number" min="2" max="${gpio.max}" class="form-control" value="${config.ledPinRed}"/>
<input name="ledPinGreen" type="number" min="2" max="${gpio.max}" class="form-control" value="${config.ledPinGreen}"/>
<input name="ledPinBlue" type="number" min="2" max="${gpio.max}" class="form-control" value="${config.ledPinBlue}"/>
<div class="input-group-append" title="Inverted">
<label class="input-group-text">
<input type="checkbox" name="ledRgbInverted" ${config.ledRgbInverted}/> inv
</label>
</div>
</div>
</div>
<div class="col-lg-2 col-md-3 col-sm-6 col-6">
<div class="m-2 input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">AP button</span>
</div>
<input name="apPin" type="number" min="0" max="${gpio.max}" class="form-control" value="${config.apPin}"/>
</div>
</div>
<div class="col-xl-2 col-lg-3 col-md-4 col-sm-6 col-6">
<div class="m-2 input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">Temperature</span>
</div>
<input name="tempSensorPin" type="number" min="0" max="${gpio.max}" class="form-control" value="${config.tempSensorPin}"/>
</div>
</div>
<div class="col-xl-6 col-lg-8">
<div class="row p-2">
<div class="col-sm-3 col-5">
<div class="input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">Vcc</span>
</div>
<input name="vccPin" type="number" min="0" max="${gpio.max}" class="form-control" value="${config.vccPin}"/>
</div>
</div>
<div class="col-sm-4 col-7">
<div class="input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">Multiplier</span>
</div>
<input type="number" min="0.1" max="10" step="0.01" class="form-control" name="vccMultiplier" value="${config.vccMultiplier}" />
</div>
</div>
<div class="col-sm-4 col-7">
<div class="input-group input-group-sm">
<div class="input-group-prepend">
<span class="input-group-text">Boot limit</span>
</div>
<input type="number" min="2.5" max="3.5" step="0.1" class="form-control" name="vccBootLimit" value="${config.vccBootLimit}" />
</div>
</div>
</div>
</div>
</div>
</div>
<div class="my-3 p-3 bg-white rounded shadow">
<div class="row"> <div class="row">
<div class="col-md-3"> <div class="col-md-3">
<label><input type="checkbox" name="debugTelnet" value="true" ${config.debugTelnet}/> Telnet debugger</label> <label><input type="checkbox" name="debugTelnet" value="true" ${config.debugTelnet}/> Telnet debugger</label>
@@ -62,29 +148,10 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-1 text-right">
<button class="btn btn-primary">Save</button>
</div>
</div> </div>
</div> </div>
</form>
<form method="post" enctype="multipart/form-data">
<div class="my-3 p-3 bg-white rounded shadow"> <div class="my-3 p-3 bg-white rounded shadow">
<div class="alert alert-warning">!!WARNING!!<br/>Do not use this unless you know what you are doing. Uploading the wrong image could cause your device to stop working. Use with extreme caution!</div> <a href="/firmware" class="btn btn-sm btn-outline-secondary">Upload firmware</a>
<div class="row">
<div class="col-md-4">
<div class="row form-group">
<label class="col-4">Firmware</label>
<div class="col-8">
<input type="file" name="file"/>
</div>
</div>
</div>
<div class="col-md-8 text-right">
<button class="btn btn-primary">Upload</button>
</div>
</div>
</div> </div>
<hr/> <hr/>
<div class="row form-group"> <div class="row form-group">
@@ -92,6 +159,7 @@
<a href="/" class="btn btn-outline-secondary">Back</a> <a href="/" class="btn btn-outline-secondary">Back</a>
</div> </div>
<div class="col-6 text-right"> <div class="col-6 text-right">
<button class="btn btn-primary">Save</button>
</div> </div>
</div> </div>
</form> </form>

View File

@@ -2,9 +2,9 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>AMS reader - Meter configuration</title> <title>AMS reader - Web configuration</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" type="text/css" href="boot.css"/> <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css"/>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
</head> </head>
<body class="bg-light"> <body class="bg-light">

View File

@@ -4,7 +4,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>AMS reader - WiFi configuration</title> <title>AMS reader - WiFi configuration</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" type="text/css" href="boot.css"/> <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css"/>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
</head> </head>
<body class="bg-light"> <body class="bg-light">
@@ -46,19 +46,19 @@
<div class="row form-group"> <div class="row form-group">
<label class="col-3">SSID</label> <label class="col-3">SSID</label>
<div class="col-9"> <div class="col-9">
<input type="text" class="form-control" name="wifiSsid" value="${config.wifiSsid}"/> <input type="text" class="form-control" name="wifiSsid" value="${config.wifiSsid}" maxlength="32" placeholder="Name of your WiFi" required/>
</div> </div>
</div> </div>
<div class="row form-group"> <div class="row form-group">
<label class="col-3">Password</label> <label class="col-3">Password</label>
<div class="col-9"> <div class="col-9">
<input type="password" class="form-control" name="wifiPassword" value="${config.wifiPassword}"/> <input type="password" class="form-control" name="wifiPassword" value="${config.wifiPassword}" maxlength="63" placeholder="Password for WiFi" required/>
</div> </div>
</div> </div>
<div class="row form-group"> <div class="row form-group">
<label class="col-3">Hostname</label> <label class="col-3">Hostname</label>
<div class="col-9"> <div class="col-9">
<input type="text" class="form-control" name="wifiHostname" value="${config.wifiHostname}"/> <input type="text" class="form-control" name="wifiHostname" value="${config.wifiHostname}" maxlength="32" pattern="[a-z0-9_-]+" placeholder="Ex.: ams-reader" required/>
</div> </div>
</div> </div>
</div> </div>
@@ -75,13 +75,13 @@
<div class="row form-group"> <div class="row form-group">
<label class="col-6">IP</label> <label class="col-6">IP</label>
<div class="col-6"> <div class="col-6">
<input type="text" class="form-control wifiip-config" name="wifiIp" value="${config.wifiIp}"/> <input type="text" class="form-control wifiip-config" name="wifiIp" value="${config.wifiIp}" pattern="\d?\d?\d.\d?\d?\d.\d?\d?\d.\d?\d?\d" placeholder="Ex: 192.168.1.200"/>
</div> </div>
</div> </div>
<div class="row form-group"> <div class="row form-group">
<label class="col-6">Subnet</label> <label class="col-6">Subnet</label>
<div class="col-6"> <div class="col-6">
<input type="text" class="form-control wifiip-config" name="wifiSubnet" value="${config.wifiSubnet}"/> <input type="text" class="form-control wifiip-config" name="wifiSubnet" value="${config.wifiSubnet}" pattern="\d?\d?\d.\d?\d?\d.\d?\d?\d.\d?\d?\d" placeholder="Ex: 255.255.255.0"/>
</div> </div>
</div> </div>
</div> </div>
@@ -89,19 +89,19 @@
<div class="row form-group"> <div class="row form-group">
<label class="col-6">Gateway</label> <label class="col-6">Gateway</label>
<div class="col-6"> <div class="col-6">
<input type="text" class="form-control wifiip-config" name="wifiGw" value="${config.wifiGw}"/> <input type="text" class="form-control wifiip-config" name="wifiGw" value="${config.wifiGw}" pattern="\d?\d?\d.\d?\d?\d.\d?\d?\d.\d?\d?\d" placeholder="Ex: 192.168.1.1"/>
</div> </div>
</div> </div>
<div class="row form-group"> <div class="row form-group">
<label class="col-6">Primary DNS</label> <label class="col-6">Primary DNS</label>
<div class="col-6"> <div class="col-6">
<input type="text" class="form-control wifiip-config" name="wifiDns1" value="${config.wifiDns1}"/> <input type="text" class="form-control wifiip-config" name="wifiDns1" value="${config.wifiDns1}" pattern="\d?\d?\d.\d?\d?\d.\d?\d?\d.\d?\d?\d" placeholder="Ex: 192.168.1.1"/>
</div> </div>
</div> </div>
<div class="row form-group"> <div class="row form-group">
<label class="col-6">Secondary DNS</label> <label class="col-6">Secondary DNS</label>
<div class="col-6"> <div class="col-6">
<input type="text" class="form-control wifiip-config" name="wifiDns2" value="${config.wifiDns2}"/> <input type="text" class="form-control wifiip-config" name="wifiDns2" value="${config.wifiDns2}" pattern="\d?\d?\d.\d?\d?\d.\d?\d?\d.\d?\d?\d" placeholder="Ex: 8.8.8.8"/>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -2,9 +2,9 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>AMS reader - Meter configuration</title> <title>AMS reader - Delete ${file.label}</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" type="text/css" href="boot.css"/> <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css"/>
</head> </head>
<body class="bg-light"> <body class="bg-light">
<main role="main" class="container"> <main role="main" class="container">

View File

@@ -58,7 +58,7 @@
"White" === option.theme && (t = "#fff"), "White" === option.theme && (t = "#fff"),
"Black" === option.theme && (t = "#000"), "Black" === option.theme && (t = "#000"),
t; t;
} };
/* The label below gauge. */ /* The label below gauge. */
function createLabel(t, a) { function createLabel(t, a) {
if(t.children("b").length === 0){ if(t.children("b").length === 0){
@@ -67,7 +67,7 @@
color: option.label_color color: option.label_color
}); });
} }
} };
/* Prepend and append text, the gauge text or percentage value. */ /* Prepend and append text, the gauge text or percentage value. */
function createSpanTag(t) { function createSpanTag(t) {
var fgcolor = ""; var fgcolor = "";
@@ -90,7 +90,7 @@
"font-size": option.text_size * option.size + "px", "font-size": option.text_size * option.size + "px",
color: fgcolor color: fgcolor
}); });
} };
/* Get data attributes as options from div tag. Fall back to defaults when not exists. */ /* Get data attributes as options from div tag. Fall back to defaults when not exists. */
function getDataAttr(t) { function getDataAttr(t) {
$.each(dataAttr, function (index, element) { $.each(dataAttr, function (index, element) {
@@ -116,10 +116,10 @@
option[element] = parseFloat(option[element]); option[element] = parseFloat(option[element]);
} }
}); });
} };
/* Draws the gauge. */ /* Draws the gauge. */
function drawGauge(a) { function drawGauge(a) {
if(M < 0) M = 0; if(M < 0) M = 0;
if(M > 100) M = 100; if(M > 100) M = 100;
var lw = option.width < 1 || isNaN(option.width) ? option.size / 20 : option.width; var lw = option.width < 1 || isNaN(option.width) ? option.size / 20 : option.width;
g.clearRect(0, 0, b.width, b.height); g.clearRect(0, 0, b.width, b.height);
@@ -141,7 +141,7 @@
c > M && (M += z, requestAnimationFrame(function(){ c > M && (M += z, requestAnimationFrame(function(){
drawGauge(Math.min(M, c) / 100); drawGauge(Math.min(M, c) / 100);
}, p)); }, p));
} };
$(this).attr("data-id", $(this).attr("id")); $(this).attr("data-id", $(this).attr("id"));
var r, var r,

View File

@@ -4,7 +4,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>AMS reader</title> <title>AMS reader</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" type="text/css" href="boot.css"/> <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css"/>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
<script src="gaugemeter.js"></script> <script src="gaugemeter.js"></script>

View File

@@ -1,18 +1,22 @@
var im, em;
$(function() { $(function() {
im = $("#importMeter");
}); if(im && im.gaugeMeter) {
var im = $("#importMeter") im.gaugeMeter({
im.gaugeMeter({ percent: 0,
percent: 0, text: "-",
text: "-", append: "W"
append: "W" });
}); }
var em = $("#exportMeter") em = $("#exportMeter");
em.gaugeMeter({ if(em && em.gaugeMeter) {
percent: 0, em.gaugeMeter({
text: "-", percent: 0,
append: "W" text: "-",
append: "W"
});
}
}); });
var setStatus = function(id, status) { var setStatus = function(id, status) {
@@ -31,9 +35,11 @@ var fetch = function() {
timeout: 10000, timeout: 10000,
dataType: 'json', dataType: 'json',
}).done(function(json) { }).done(function(json) {
$(".SimpleMeter").hide(); if(im && em) {
im.show(); $(".SimpleMeter").hide();
em.show(); im.show();
em.show();
}
for(var id in json) { for(var id in json) {
var str = json[id]; var str = json[id];
@@ -89,11 +95,13 @@ var fetch = function() {
p_append = "kW"; p_append = "kW";
} }
} }
im.gaugeMeter({ if(im && im.gaugeMeter) {
percent: p_pct, im.gaugeMeter({
text: p, percent: p_pct,
append: p_append text: p,
}); append: p_append
});
}
var po = 0; var po = 0;
var po_pct = parseInt(json.po_pct); var po_pct = parseInt(json.po_pct);
@@ -105,11 +113,13 @@ var fetch = function() {
po_append = "kW"; po_append = "kW";
} }
} }
em.gaugeMeter({ if(em && em.gaugeMeter) {
percent: po_pct, em.gaugeMeter({
text: po, percent: po_pct,
append: po_append text: po,
}); append: po_append
});
}
for(var id in json.data) { for(var id in json.data) {
var str = json.data[id]; var str = json.data[id];
@@ -122,33 +132,41 @@ var fetch = function() {
} }
} }
} else { } else {
if(im && im.gaugeMeter) {
im.gaugeMeter({
percent: 0,
text: "-",
append: "W"
});
}
if(em && em.gaugeMeter) {
em.gaugeMeter({
percent: 0,
text: "-",
append: "W"
});
}
}
setTimeout(fetch, interval);
}).fail(function() {
setTimeout(fetch, interval*4);
if(im && im.gaugeMeter) {
im.gaugeMeter({ im.gaugeMeter({
percent: 0, percent: 0,
text: "-", text: "-",
append: "W" append: "W"
}); });
}
if(em && em.gaugeMeter) {
em.gaugeMeter({ em.gaugeMeter({
percent: 0, percent: 0,
text: "-", text: "-",
append: "W" append: "W"
}); });
} }
setTimeout(fetch, interval);
}).fail(function() {
setTimeout(fetch, interval*4);
im.gaugeMeter({
percent: 0,
text: "-",
append: "W"
});
em.gaugeMeter({
percent: 0,
text: "-",
append: "W"
});
setStatus("mqtt", "secondary"); setStatus("mqtt", "secondary");
setStatus("wifi", "secondary"); setStatus("wifi", "secondary");

View File

@@ -5,31 +5,11 @@
<title>AMS reader - Restarting, please wait</title> <title>AMS reader - Restarting, please wait</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" type="text/css" href="boot.css"/> <link rel="stylesheet" type="text/css" href="boot.css"/>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
</head> </head>
<body class="bg-light"> <body class="bg-light">
<main role="main" class="container"> <main role="main" class="container">
<header class="navbar navbar-expand navbar-dark flex-column flex-md-row bg-purple rounded mt-2 mb-4" style="background-color: var(--purple);"> <header class="navbar navbar-expand navbar-dark flex-column flex-md-row bg-purple rounded mt-2 mb-4" style="background-color: var(--purple);">
<a href="/" class=""><h6 class="navbar-brand">AMS reader <small>${version}</small></h6></a> <a href="/" class=""><h6 class="navbar-brand">AMS reader <small>${version}</small></h6></a>
<div class="navbar-nav-scroll">
<ul class="navbar-nav bd-navbar-nav flex-row">
<li class="nav-item">
<a class="nav-link" href="/config-meter">Meter</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/config-wifi">WiFi</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/config-mqtt">MQTT</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/config-web">Web</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/config-system">System</a>
</li>
</ul>
</div>
<ul class="navbar-nav flex-row ml-md-auto d-none d-md-flex"> <ul class="navbar-nav flex-row ml-md-auto d-none d-md-flex">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link p-2" href="https://github.com/gskjold/AmsToMqttBridge" target="_blank" rel="noopener" aria-label="GitHub"> <a class="nav-link p-2" href="https://github.com/gskjold/AmsToMqttBridge" target="_blank" rel="noopener" aria-label="GitHub">
@@ -44,7 +24,6 @@
</main> </main>
<script> <script>
var tries = 0; var tries = 0;
var ip = "${ip}"; var ip = "${ip}";
var hostname = "${hostname}"; var hostname = "${hostname}";
var url; var url;
@@ -60,15 +39,22 @@
} else { } else {
url = ""; url = "";
} }
if(console) console.log("Trying url " + url) if(console) console.log("Trying url " + url);
$.ajax({
url: url + '/is-alive', var retry = function() {
timeout: 5000
}).done(function(json) {
window.location.href = url ? url : "/";
}).fail(function() {
setTimeout(fetch, 1000); setTimeout(fetch, 1000);
};
var xhr = new XMLHttpRequest();
xhr.timeout = 5000;
xhr.addEventListener('abort', retry);
xhr.addEventListener('error', retry);
xhr.addEventListener('timeout', retry);
xhr.addEventListener('load', function(e) {
window.location.href = url ? url : "/";
}); });
xhr.open("GET", "/is-alive", true);
xhr.send();
}; };
setTimeout(fetch, 10000); setTimeout(fetch, 10000);
</script> </script>

136
web/setup.html Normal file
View File

@@ -0,0 +1,136 @@
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>AMS reader - Setup</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" type="text/css" href="boot.css"/>
</head>
<body class="bg-light">
<main role="main" class="container">
<header class="navbar navbar-expand navbar-dark flex-column flex-md-row bg-purple rounded mt-2 mb-4">
<a href="/" class=""><h6 class="navbar-brand">AMS reader <small>${version}</small></h6></a>
<ul class="navbar-nav flex-row ml-md-auto d-none d-md-flex">
<li class="nav-item">
<a class="nav-link p-2" href="https://github.com/gskjold/AmsToMqttBridge" target="_blank" rel="noopener" aria-label="GitHub">
<svg class="d-inline-block align-text-top" style="width: 2rem; height: 2rem;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 499.36" focusable="false"><title>GitHub</title><path d="M256 0C114.64 0 0 114.61 0 256c0 113.09 73.34 209 175.08 242.9 12.8 2.35 17.47-5.56 17.47-12.34 0-6.08-.22-22.18-.35-43.54-71.2 15.49-86.2-34.34-86.2-34.34-11.64-29.57-28.42-37.45-28.42-37.45-23.27-15.84 1.73-15.55 1.73-15.55 25.69 1.81 39.21 26.38 39.21 26.38 22.84 39.12 59.92 27.82 74.5 21.27 2.33-16.54 8.94-27.82 16.25-34.22-56.84-6.43-116.6-28.43-116.6-126.49 0-27.95 10-50.8 26.35-68.69-2.63-6.48-11.42-32.5 2.51-67.75 0 0 21.49-6.88 70.4 26.24a242.65 242.65 0 0 1 128.18 0c48.87-33.13 70.33-26.24 70.33-26.24 14 35.25 5.18 61.27 2.55 67.75 16.41 17.9 26.31 40.75 26.31 68.69 0 98.35-59.85 120-116.88 126.32 9.19 7.9 17.38 23.53 17.38 47.41 0 34.22-.31 61.83-.31 70.23 0 6.85 4.61 14.81 17.6 12.31C438.72 464.97 512 369.08 512 256.02 512 114.62 397.37 0 256 0z" fill="currentColor" fill-rule="evenodd"></path></svg>
</a>
</li>
</ul>
</header>
<form method="post">
<div class="my-3 p-3 bg-white rounded shadow">
<div class="row">
<div class="col-xl-4 col-md-6">
<h5>Hardware</h5>
<select name="board" class="form-control">
<optgroup label="Custom hardware">
<option value="0">Custom hardware by Roar Fredriksen</option>
<option value="1">Kamstrup module by Egil Opsahl</option>
<option value="2">Self powered 2.0 by Max Spencer</option>
</optgroup>
<optgroup label="ESP8266">
<option value="101">Wemos D1</option>
<option value="199">Generic ESP8266</option>
</optgroup>
<optgroup label="ESP32">
<option value="201">Wemos LOLIN D32</option>
<option value="202">Adafruit HUZZAH32</option>
<option value="299">Generic ESP32</option>
</optgroup>
</select>
</div>
<div class="col-xl-2 col-md-3">
<h5>Meter</h5>
<select name="meterType" class="form-control">
<option value="0">Autodetect</option>
<option value="1">Kaifa</option>
<option value="2">Aidon</option>
<option value="3">Kamstrup</option>
</select>
</div>
</div>
</div>
<div class="my-3 p-3 bg-white rounded shadow">
<h5>WiFi</h5>
<div class="row">
<div class="col-xl-3 col-md-6 form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">SSID</span>
</div>
<input type="text" name="wifiSsid" class="form-control" maxlength="32" placeholder="Name of your WiFi" required/>
</div>
</div>
<div class="col-xl-3 col-md-6 form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">PSK</span>
</div>
<input type="password" name="wifiPassword" class="form-control" maxlength="63" placeholder="Password for WiFi" required/>
</div>
</div>
<div class="col-xl-4 col-md-6 form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">Hostname</span>
</div>
<input type="text" name="wifiHostname" class="form-control" maxlength="32" pattern="[a-z0-9_-]+" placeholder="Optional, ex.: ams-reader"/>
</div>
</div>
<div class="col-xl-2 col-md-6 form-group">
<label><input type="checkbox" name="wifiIpType" value="1" onchange="staticChecked(this);"/> Static IP</label>
</div>
</div>
<div class="row" id="staticIp">
<div class="col-xl-3 col-lg-4 col-sm-6 form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">IP</span>
</div>
<input type="text" name="wifiIp" class="form-control" pattern="\d?\d?\d.\d?\d?\d.\d?\d?\d.\d?\d?\d" placeholder="Ex: 192.168.1.200"/>
</div>
</div>
<div class="col-xl-3 col-lg-4 col-sm-6 form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">Subnet</span>
</div>
<input type="text" name="wifiSubnet" class="form-control" pattern="\d?\d?\d.\d?\d?\d.\d?\d?\d.\d?\d?\d" placeholder="Ex.: 255.255.255.0"/>
</div>
</div>
<div class="col-xl-3 col-lg-4 col-sm-6 form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">Gateway</span>
</div>
<input type="text" name="wifiGw" class="form-control" pattern="\d?\d?\d.\d?\d?\d.\d?\d?\d.\d?\d?\d" placeholder="Ex.: 192.168.1.1"/>
</div>
</div>
<div class="col-xl-3 col-lg-4 col-sm-6 form-group">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">DNS</span>
</div>
<input type="text" name="wifiDns1" class="form-control" pattern="\d?\d?\d.\d?\d?\d.\d?\d?\d.\d?\d?\d" placeholder="Ex.: 192.168.1.1"/>
</div>
</div>
</div>
</div>
<hr/>
<div class="row">
<div class="col-6"></div>
<div class="col-6 text-right">
<button type="submit" class="btn btn-primary">Save & reboot</button>
</div>
</div>
</form>
</main>
<script>
document.getElementById('staticIp').style.display = "none";
var staticChecked = function(el) {
document.getElementById('staticIp').style.display = el.checked ? "" : "none";
}
</script>
</body>
</html>

View File

@@ -2,7 +2,7 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>AMS reader - Meter configuration</title> <title>AMS reader - Upload ${file.label}</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" type="text/css" href="boot.css"/> <link rel="stylesheet" type="text/css" href="boot.css"/>
</head> </head>