diff --git a/.gitignore b/.gitignore
index 97877088..63ae6684 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,4 +6,5 @@
*.sw[op]
.vscode
.pio
-platformio-user.ini
\ No newline at end of file
+platformio-user.ini
+/src/version.h
diff --git a/addversion.py b/addversion.py
new file mode 100644
index 00000000..7453a4a1
--- /dev/null
+++ b/addversion.py
@@ -0,0 +1,16 @@
+import os
+
+FILENAME_VERSION_H = 'src/version.h'
+version = os.environ.get('GITHUB_REF')
+if version == None:
+ version = "SNAPSHOT"
+
+import datetime
+
+hf = """
+#ifndef VERSION
+ #define VERSION "{}"
+#endif
+""".format(version)
+with open(FILENAME_VERSION_H, 'w+') as f:
+ f.write(hf)
diff --git a/lib/HanConfigAp/src/HanConfigAp.cpp b/lib/HanConfigAp/src/HanConfigAp.cpp
index 0c16a393..8cfeef96 100644
--- a/lib/HanConfigAp/src/HanConfigAp.cpp
+++ b/lib/HanConfigAp/src/HanConfigAp.cpp
@@ -1,20 +1,5 @@
#include "HanConfigAp.h"
-#include "index_html.h"
-#include "configuration_html.h"
-#include "bootstrap_css.h"
-#include "application_css.h"
-#include "jquery_js.h"
-#include "gaugemeter_js.h"
-#include "index_js.h"
-
-#include "Base64.h"
-
-#if defined(ESP8266)
-ESP8266WebServer HanConfigAp::server(80);
-#elif defined(ESP32) // ARDUINO_ARCH_ESP32
-WebServer HanConfigAp::server(80);
-#endif
Stream* HanConfigAp::debugger;
bool HanConfigAp::hasConfig() {
@@ -79,264 +64,15 @@ void HanConfigAp::setup(int accessPointButtonPin, Stream* debugger)
}
}
-void HanConfigAp::enableWeb() {
- server.on("/", indexHtml);
- server.on("/configuration", configurationHtml);
- server.on("/css/bootstrap.css", bootstrapCss);
- server.on("/css/application.css", applicationCss);
- server.on("/js/jquery.js", jqueryJs);
- server.on("/js/gaugemeter.js", gaugemeterJs);
- server.on("/js/index.js", indexJs);
-
- server.on("/save", handleSave);
-
- server.begin(); // Web server start
-
- print("Web server is ready for config at http://");
- if(isActivated) {
- print(WiFi.softAPIP());
- } else {
- print(WiFi.localIP());
- }
- println("/");
-}
-
bool HanConfigAp::loop() {
if(isActivated) {
//DNS
dnsServer.processNextRequest();
}
- //HTTP
- server.handleClient();
-
return isActivated;
}
-void HanConfigAp::handleSave() {
- configuration *config = new configuration();
-
- String temp;
-
- temp = server.arg("ssid");
- config->ssid = new char[temp.length() + 1];
- temp.toCharArray(config->ssid, temp.length() + 1, 0);
-
- temp = server.arg("ssidPassword");
- config->ssidPassword = new char[temp.length() + 1];
- temp.toCharArray(config->ssidPassword, temp.length() + 1, 0);
-
- config->meterType = (byte)server.arg("meterType").toInt();
-
- temp = server.arg("mqtt");
- config->mqtt = new char[temp.length() + 1];
- temp.toCharArray(config->mqtt, temp.length() + 1, 0);
-
- config->mqttPort = (int)server.arg("mqttPort").toInt();
-
- temp = server.arg("mqttClientID");
- config->mqttClientID = new char[temp.length() + 1];
- temp.toCharArray(config->mqttClientID, temp.length() + 1, 0);
-
- temp = server.arg("mqttPublishTopic");
- config->mqttPublishTopic = new char[temp.length() + 1];
- temp.toCharArray(config->mqttPublishTopic, temp.length() + 1, 0);
-
- temp = server.arg("mqttSubscribeTopic");
- config->mqttSubscribeTopic = new char[temp.length() + 1];
- temp.toCharArray(config->mqttSubscribeTopic, temp.length() + 1, 0);
-
- temp = server.arg("mqttUser");
- config->mqttUser = new char[temp.length() + 1];
- temp.toCharArray(config->mqttUser, temp.length() + 1, 0);
-
- temp = server.arg("mqttPass");
- config->mqttPass = new char[temp.length() + 1];
- temp.toCharArray(config->mqttPass, temp.length() + 1, 0);
-
- config->authSecurity = (byte)server.arg("authSecurity").toInt();
-
- temp = server.arg("authUser");
- config->authUser = new char[temp.length() + 1];
- temp.toCharArray(config->authUser, temp.length() + 1, 0);
-
- temp = server.arg("authPass");
- config->authPass = new char[temp.length() + 1];
- temp.toCharArray(config->authPass, temp.length() + 1, 0);
-
- config->fuseSize = (int)server.arg("fuseSize").toInt();
-
- println("Saving configuration now...");
-
- if (HanConfigAp::debugger) config->print(HanConfigAp::debugger);
- if (config->save())
- {
- println("Successfully saved. Will reboot now.");
- String html = "
Successfully Saved!
Device is restarting now...
Go to index";
- server.send(200, "text/html", html);
- yield();
- delay(1000);
-#if defined(ESP8266)
- ESP.reset();
-#elif defined(ESP32)
- ESP.restart();
-#endif
- }
- else
- {
- println("Error saving configuration");
- String html = "Error saving configuration!
";
- server.send(500, "text/html", html);
- }
-}
-
-void HanConfigAp::indexHtml() {
- println("Serving /index.html over http...");
-
- server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
- server.sendHeader("Pragma", "no-cache");
- server.sendHeader("Expires", "-1");
- server.send(200, "text/html", INDEX_HTML, sizeof(INDEX_HTML)-1);
-
-}
-
-void HanConfigAp::configurationHtml() {
- println("Serving /configuration.html over http...");
-
- configuration *config = new configuration();
- config->load();
-
- bool access = !config->hasConfig() || config->authSecurity == 0;
- if(config->authSecurity > 0 && server.hasHeader("Authorization")) {
- String expectedAuth = String(config->authUser) + ":" + String(config->authPass);
-
- String providedPwd = server.header("Authorization");
- providedPwd.replace("Basic ", "");
- char inputString[providedPwd.length()];
- providedPwd.toCharArray(inputString, providedPwd.length()+1);
-
- int inputStringLength = sizeof(inputString);
- int decodedLength = Base64.decodedLength(inputString, inputStringLength);
- char decodedString[decodedLength];
- Base64.decode(decodedString, inputString, inputStringLength);
- print("Received auth: ");
- println(decodedString);
- access = String(decodedString).equals(expectedAuth);
- }
-
- if(!access) {
- server.sendHeader("WWW-Authenticate", "Basic realm=\"Secure Area\"");
- server.send(401, "text/html", "");
- return;
- }
- String html = CONFIGURATION_HTML;
-
- server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
- server.sendHeader("Pragma", "no-cache");
- server.sendHeader("Expires", "-1");
-
- if(config->hasConfig()) {
- html.replace("${config.ssid}", config->ssid);
- html.replace("${config.ssidPassword}", config->ssidPassword);
- html.replace("${config.meterType}", String(config->fuseSize));
- for(int i = 0; i<3; i++) {
- html.replace("${config.meterType" + String(i) + "}", config->meterType == i ? "selected" : "");
- }
- html.replace("${config.mqtt}", config->mqtt);
- html.replace("${config.mqttPort}", String(config->mqttPort));
- html.replace("${config.mqttClientID}", config->mqttClientID);
- html.replace("${config.mqttPublishTopic}", config->mqttPublishTopic);
- html.replace("${config.mqttSubscribeTopic}", config->mqttSubscribeTopic);
- html.replace("${config.mqttUser}", config->mqttUser);
- html.replace("${config.mqttPass}", config->mqttPass);
- html.replace("${config.authUser}", config->authUser);
- html.replace("${config.authSecurity}", String(config->authSecurity));
- for(int i = 0; i<2; i++) {
- html.replace("${config.authSecurity" + String(i) + "}", config->authSecurity == i ? "selected" : "");
- }
- html.replace("${config.authPass}", config->authPass);
- html.replace("${config.fuseSize}", String(config->fuseSize));
- for(int i = 0; i<63; i++) {
- html.replace("${config.fuseSize" + String(i) + "}", config->fuseSize == i ? "selected" : "");
- }
- } else {
- html.replace("${config.ssid}", "");
- html.replace("${config.ssidPassword}", "");
- html.replace("${config.meterType}", "");
- for(int i = 0; i<3; i++) {
- html.replace("${config.meterType" + String(i) + "}", i == 0 ? "selected" : "");
- }
- html.replace("${config.mqtt}", "");
- html.replace("${config.mqttPort}", "1883");
- html.replace("${config.mqttClientID}", "");
- html.replace("${config.mqttPublishTopic}", "");
- html.replace("${config.mqttSubscribeTopic}", "");
- html.replace("${config.mqttUser}", "");
- html.replace("${config.mqttPass}", "");
- html.replace("${config.authSecurity}", "");
- for(int i = 0; i<2; i++) {
- html.replace("${config.authSecurity" + String(i) + "}", i == 0 ? "selected" : "");
- }
- html.replace("${config.authUser}", "");
- html.replace("${config.authPass}", "");
- html.replace("${config.fuseSize}", "");
- for(int i = 0; i<63; i++) {
- html.replace("${config.fuseSize" + String(i) + "}", i == 0 ? "selected" : "");
- }
- }
- server.send(200, "text/html", html);
-}
-
-void HanConfigAp::bootstrapCss() {
- println("Serving /bootstrap.css over http...");
-
- server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
- server.sendHeader("Pragma", "no-cache");
- server.sendHeader("Expires", "-1");
- server.send(200, "text/html", BOOTSTRAP_CSS, sizeof(BOOTSTRAP_CSS)-1);
-
-}
-
-void HanConfigAp::applicationCss() {
- println("Serving /application.css over http...");
-
- server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
- server.sendHeader("Pragma", "no-cache");
- server.sendHeader("Expires", "-1");
- server.send(200, "text/html", APPLICATION_CSS, sizeof(APPLICATION_CSS)-1);
-
-}
-
-void HanConfigAp::jqueryJs() {
- println("Serving /jquery.js over http...");
-
- server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
- server.sendHeader("Pragma", "no-cache");
- server.sendHeader("Expires", "-1");
- server.send(200, "text/html", JQUERY_JS, sizeof(JQUERY_JS)-1);
-
-}
-
-void HanConfigAp::gaugemeterJs() {
- println("Serving /gaugemeter.js over http...");
-
- server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
- server.sendHeader("Pragma", "no-cache");
- server.sendHeader("Expires", "-1");
- server.send(200, "text/html", GAUEGMETER_JS, sizeof(GAUEGMETER_JS)-1);
-
-}
-
-void HanConfigAp::indexJs() {
- println("Serving /index.js over http...");
-
- server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
- server.sendHeader("Pragma", "no-cache");
- server.sendHeader("Expires", "-1");
- server.send(200, "text/html", INDEX_JS, sizeof(INDEX_JS)-1);
-
-}
-
size_t HanConfigAp::print(const char* text)
{
if (debugger) debugger->print(text);
diff --git a/lib/HanConfigAp/src/HanConfigAp.h b/lib/HanConfigAp/src/HanConfigAp.h
index 3450a347..e62dce16 100644
--- a/lib/HanConfigAp/src/HanConfigAp.h
+++ b/lib/HanConfigAp/src/HanConfigAp.h
@@ -11,10 +11,8 @@
#if defined(ESP8266)
#include
- #include
#elif defined(ESP32) // ARDUINO_ARCH_ESP32
#include
- #include
#else
#warning "Unsupported board type"
#endif
@@ -27,7 +25,6 @@
class HanConfigAp {
public:
void setup(int accessPointButtonPin, Stream* debugger);
- void enableWeb();
bool loop();
bool hasConfig();
configuration config;
@@ -45,23 +42,6 @@ private:
static size_t print(const Printable& data);
static size_t println(const Printable& data);
- // Web server
- static void indexHtml();
- static void configurationHtml();
- static void bootstrapCss();
- static void applicationCss();
- static void jqueryJs();
- static void gaugemeterJs();
- static void indexJs();
-
- static void handleSave();
-
-#if defined(ESP8266)
- static ESP8266WebServer server;
-#elif defined(ESP32) // ARDUINO_ARCH_ESP32
- static WebServer server;
-#endif
-
static Stream* debugger;
};
diff --git a/lib/HanConfigAp/src/index_js.h b/lib/HanConfigAp/src/index_js.h
deleted file mode 100644
index 5d00a1aa..00000000
--- a/lib/HanConfigAp/src/index_js.h
+++ /dev/null
@@ -1,13 +0,0 @@
-const char INDEX_JS[] PROGMEM = R"=="==(
-$(function() {
- $(".GaugeMeter").gaugeMeter();
-
- $('.update').on('click', function() {
- var el = $(".GaugeMeter");
- el.data('percent', 75);
- el.data('text', '33.8');
- el.gaugeMeter();
- console.log(el);
- });
-});
-)=="==";
diff --git a/platformio.ini b/platformio.ini
index 84250766..091c3d93 100755
--- a/platformio.ini
+++ b/platformio.ini
@@ -3,7 +3,7 @@ extra_configs = platformio-user.ini
[common]
framework = arduino
-lib_deps = HanConfigAp@1.0.0, HanReader@1.0.0, HanToJson@1.0.0, ArduinoJson@^6.0.0, MQTT@^2.4.0, DallasTemperature@^3.8.0, Base64@0.0.1
+lib_deps = HanConfigAp@1.0.0, HanReader@1.0.0, HanToJson@1.0.0, ArduinoJson@^6.0.0, MQTT@^2.4.0, DallasTemperature@^3.8.0
[env:esp12e]
platform = espressif8266
diff --git a/src/AmsToMqttBridge.ino b/src/AmsToMqttBridge.ino
index b3279749..69ce40c6 100644
--- a/src/AmsToMqttBridge.ino
+++ b/src/AmsToMqttBridge.ino
@@ -22,6 +22,7 @@
#include
#endif
+#include "AmsWebServer.h"
#include "HanConfigAp.h"
#include "HanReader.h"
#include "HanToJson.h"
@@ -48,6 +49,8 @@ DallasTemperature tempSensor(&oneWire);
// Object used to boot as Access Point
HanConfigAp ap;
+AmsWebServer ws;
+
// WiFi client and MQTT client
WiFiClient *client;
MQTTClient mqtt(384);
@@ -102,7 +105,7 @@ void setup() {
hanReader.compensateFor09HeaderBug = (ap.config.meterType == 1);
}
- ap.enableWeb();
+ ws.setup(&ap.config, debugger);
}
// the loop function runs over and over again until power down or reset
@@ -121,10 +124,7 @@ void loop()
// Reconnect to WiFi and MQTT as needed
if (!mqtt.connected()) {
MQTT_connect();
- }
- else
- {
- // Read data from the HAN port
+ } else {
readHanPort();
}
}
@@ -134,6 +134,7 @@ void loop()
if (millis() / 1000 % 2 == 0) led_on();
else led_off();
}
+ ws.loop();
}
@@ -204,7 +205,7 @@ void mqttMessageReceived(String &topic, String &payload)
void readHanPort()
{
- if (hanReader.read())
+ if (hanReader.read() && ap.config.hasConfig())
{
// Flash LED on, this shows us that data is received
led_on();
@@ -234,16 +235,14 @@ void readHanPort()
hanToJson(data, ap.config.meterType, hanReader);
- // Write the json to the debug port
- if (debugger) {
- debugger->print("Sending data to MQTT: ");
- serializeJsonPretty(json, *debugger);
- debugger->println();
- }
+ if(ap.config.mqtt != 0 && strlen(ap.config.mqtt) != 0 && ap.config.mqttPublishTopic != 0 && strlen(ap.config.mqttPublishTopic) != 0) {
+ // Write the json to the debug port
+ if (debugger) {
+ debugger->print("Sending data to MQTT: ");
+ serializeJsonPretty(json, *debugger);
+ debugger->println();
+ }
- // Make sure we have configured a publish topic
- if (! ap.config.mqttPublishTopic == 0 || strlen(ap.config.mqttPublishTopic) == 0)
- {
// Publish the json to the MQTT server
String msg;
serializeJson(json, msg);
@@ -251,6 +250,7 @@ void readHanPort()
mqtt.publish(ap.config.mqttPublishTopic, msg.c_str());
mqtt.loop();
}
+ ws.setJson(json);
// Flash LED off
led_off();
diff --git a/src/AmsWebServer.cpp b/src/AmsWebServer.cpp
new file mode 100644
index 00000000..aae9e3df
--- /dev/null
+++ b/src/AmsWebServer.cpp
@@ -0,0 +1,354 @@
+#include "AmsWebServer.h"
+#include "version.h"
+
+#include "index_html.h"
+#include "configuration_html.h"
+#include "bootstrap_css.h"
+#include "application_css.h"
+#include "jquery_js.h"
+#include "gaugemeter_js.h"
+#include "index_js.h"
+
+#include "Base64.h"
+
+#if defined(ESP8266)
+ESP8266WebServer server(80);
+#elif defined(ESP32) // ARDUINO_ARCH_ESP32
+WebServer server(80);
+#endif
+
+void AmsWebServer::setup(configuration* config, Stream* debugger) {
+ this->config = config;
+ this->debugger = debugger;
+
+ server.on("/", std::bind(&AmsWebServer::indexHtml, this));
+ server.on("/configuration", std::bind(&AmsWebServer::configurationHtml, this));
+ server.on("/css/bootstrap.css", std::bind(&AmsWebServer::bootstrapCss, this));
+ server.on("/css/application.css", std::bind(&AmsWebServer::applicationCss, this));
+ server.on("/js/jquery.js", std::bind(&AmsWebServer::jqueryJs, this));
+ server.on("/js/gaugemeter.js", std::bind(&AmsWebServer::gaugemeterJs, this));
+ server.on("/js/index.js", std::bind(&AmsWebServer::indexJs, this));
+ server.on("/data.json", std::bind(&AmsWebServer::dataJson, this));
+
+ server.on("/save", std::bind(&AmsWebServer::handleSave, this));
+
+ server.begin(); // Web server start
+
+ print("Web server is ready for config at http://");
+ if(WiFi.getMode() == WIFI_AP) {
+ print(WiFi.softAPIP());
+ } else {
+ print(WiFi.localIP());
+ }
+ println("/");
+
+ if(config->hasConfig() && config->fuseSize > 0) {
+ maxPwr = config->fuseSize * 230;
+ } else {
+ maxPwr = 20000;
+ }
+}
+
+void AmsWebServer::loop() {
+ server.handleClient();
+}
+
+void AmsWebServer::setJson(StaticJsonDocument<500> json) {
+ this->json = json;
+}
+
+bool AmsWebServer::checkSecurity(byte level) {
+ bool access = !config->hasConfig() || config->authSecurity < level;
+ if(!access && config->authSecurity >= level && server.hasHeader("Authorization")) {
+ println(" forcing web security");
+ String expectedAuth = String(config->authUser) + ":" + String(config->authPass);
+
+ String providedPwd = server.header("Authorization");
+ providedPwd.replace("Basic ", "");
+ char inputString[providedPwd.length()];
+ providedPwd.toCharArray(inputString, providedPwd.length()+1);
+
+ int inputStringLength = sizeof(inputString);
+ int decodedLength = Base64.decodedLength(inputString, inputStringLength);
+ char decodedString[decodedLength];
+ Base64.decode(decodedString, inputString, inputStringLength);
+ print("Received auth: ");
+ println(decodedString);
+ access = String(decodedString).equals(expectedAuth);
+ }
+
+ if(!access) {
+ println(" no access, requesting user/pass");
+ server.sendHeader("WWW-Authenticate", "Basic realm=\"Secure Area\"");
+ server.send(401, "text/html", "");
+ }
+ return access;
+}
+
+void AmsWebServer::indexHtml() {
+ println("Serving /index.html over http...");
+
+ if(!checkSecurity(2))
+ return;
+
+ String html = INDEX_HTML;
+ html.replace("${version}", VERSION);
+ if(WiFi.getMode() != WIFI_AP) {
+ html.replace("/css/bootstrap.css", "https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css");
+ html.replace("/js/jquery.js", "https://code.jquery.com/jquery-3.4.1.min.js");
+ }
+
+ server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
+ server.sendHeader("Pragma", "no-cache");
+ server.sendHeader("Expires", "-1");
+ server.send(200, "text/html", html);
+}
+
+void AmsWebServer::configurationHtml() {
+ println("Serving /configuration.html over http...");
+
+ if(!checkSecurity(1))
+ return;
+
+ String html = CONFIGURATION_HTML;
+ html.replace("${version}", VERSION);
+ if(WiFi.getMode() != WIFI_AP) {
+ html.replace("/css/bootstrap.css", "https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css");
+ html.replace("/js/jquery.js", "https://code.jquery.com/jquery-3.4.1.min.js");
+ }
+
+ server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
+ server.sendHeader("Pragma", "no-cache");
+ server.sendHeader("Expires", "-1");
+
+ if(config->hasConfig()) {
+ html.replace("${config.ssid}", config->ssid);
+ html.replace("${config.ssidPassword}", config->ssidPassword);
+ html.replace("${config.meterType}", String(config->fuseSize));
+ for(int i = 0; i<4; i++) {
+ html.replace("${config.meterType" + String(i) + "}", config->meterType == i ? "selected" : "");
+ }
+ html.replace("${config.mqtt}", config->mqtt);
+ html.replace("${config.mqttPort}", String(config->mqttPort));
+ html.replace("${config.mqttClientID}", config->mqttClientID);
+ html.replace("${config.mqttPublishTopic}", config->mqttPublishTopic);
+ html.replace("${config.mqttSubscribeTopic}", config->mqttSubscribeTopic);
+ html.replace("${config.mqttUser}", config->mqttUser);
+ html.replace("${config.mqttPass}", config->mqttPass);
+ html.replace("${config.authUser}", config->authUser);
+ html.replace("${config.authSecurity}", String(config->authSecurity));
+ for(int i = 0; i<3; i++) {
+ html.replace("${config.authSecurity" + String(i) + "}", config->authSecurity == i ? "selected" : "");
+ }
+ html.replace("${config.authPass}", config->authPass);
+ html.replace("${config.fuseSize}", String(config->fuseSize));
+ for(int i = 0; i<64; i++) {
+ html.replace("${config.fuseSize" + String(i) + "}", config->fuseSize == i ? "selected" : "");
+ }
+ } else {
+ html.replace("${config.ssid}", "");
+ html.replace("${config.ssidPassword}", "");
+ html.replace("${config.meterType}", "");
+ for(int i = 0; i<4; i++) {
+ html.replace("${config.meterType" + String(i) + "}", i == 0 ? "selected" : "");
+ }
+ html.replace("${config.mqtt}", "");
+ html.replace("${config.mqttPort}", "1883");
+ html.replace("${config.mqttClientID}", "");
+ html.replace("${config.mqttPublishTopic}", "");
+ html.replace("${config.mqttSubscribeTopic}", "");
+ html.replace("${config.mqttUser}", "");
+ html.replace("${config.mqttPass}", "");
+ html.replace("${config.authSecurity}", "");
+ for(int i = 0; i<3; i++) {
+ html.replace("${config.authSecurity" + String(i) + "}", i == 0 ? "selected" : "");
+ }
+ html.replace("${config.authUser}", "");
+ html.replace("${config.authPass}", "");
+ html.replace("${config.fuseSize}", "");
+ for(int i = 0; i<64; i++) {
+ html.replace("${config.fuseSize" + String(i) + "}", i == 0 ? "selected" : "");
+ }
+ }
+ server.send(200, "text/html", html);
+}
+
+void AmsWebServer::bootstrapCss() {
+ println("Serving /bootstrap.css over http...");
+
+ server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
+ server.sendHeader("Pragma", "no-cache");
+ server.sendHeader("Expires", "-1");
+ server.send(200, "text/css", BOOTSTRAP_CSS);
+}
+
+void AmsWebServer::applicationCss() {
+ println("Serving /application.css over http...");
+
+ server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
+ server.sendHeader("Pragma", "no-cache");
+ server.sendHeader("Expires", "-1");
+ server.send(200, "text/css", APPLICATION_CSS);
+}
+
+void AmsWebServer::jqueryJs() {
+ println("Serving /jquery.js over http...");
+
+ server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
+ server.sendHeader("Pragma", "no-cache");
+ server.sendHeader("Expires", "-1");
+ server.send(200, "application/javascript", JQUERY_JS);
+}
+
+void AmsWebServer::gaugemeterJs() {
+ println("Serving /gaugemeter.js over http...");
+
+ server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
+ server.sendHeader("Pragma", "no-cache");
+ server.sendHeader("Expires", "-1");
+ server.send(200, "application/javascript", GAUEGMETER_JS);
+}
+
+void AmsWebServer::indexJs() {
+ println("Serving /index.js over http...");
+
+ server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
+ server.sendHeader("Pragma", "no-cache");
+ server.sendHeader("Expires", "-1");
+ server.send(200, "application/javascript", INDEX_JS);
+}
+
+void AmsWebServer::dataJson() {
+ println("Serving /data.json over http...");
+
+ if(!checkSecurity(2))
+ return;
+
+ String jsonStr;
+ if(!json.isNull()) {
+ println(" json has data");
+
+ int u1 = json["data"]["U1"].as();
+ int u2 = json["data"]["U2"].as();
+ int u3 = json["data"]["U3"].as();
+ int pwr = json["data"]["P"].as();
+
+ if(config->hasConfig() && u1 > 0) {
+ maxPwr = config->fuseSize * u1;
+ if(u2 > 0) {
+ maxPwr += config->fuseSize * u2;
+ if(u3 > 0) {
+ maxPwr += config->fuseSize * u3;
+ }
+ }
+ }
+
+ json["maxPower"] = maxPwr;
+ json["pct"] = min(pwr*100/maxPwr, 100);
+ json["meterType"] = config->meterType;
+ json["currentMillis"] = millis();
+
+ serializeJson(json, jsonStr);
+ } else {
+ println(" json is empty");
+ jsonStr = "{}";
+ }
+
+ server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
+ server.sendHeader("Pragma", "no-cache");
+ server.sendHeader("Expires", "-1");
+ server.send(200, "application/json", jsonStr);
+}
+
+void AmsWebServer::handleSave() {
+ String temp;
+
+ temp = server.arg("ssid");
+ config->ssid = new char[temp.length() + 1];
+ temp.toCharArray(config->ssid, temp.length() + 1, 0);
+
+ temp = server.arg("ssidPassword");
+ config->ssidPassword = new char[temp.length() + 1];
+ temp.toCharArray(config->ssidPassword, temp.length() + 1, 0);
+
+ config->meterType = (byte)server.arg("meterType").toInt();
+
+ temp = server.arg("mqtt");
+ config->mqtt = new char[temp.length() + 1];
+ temp.toCharArray(config->mqtt, temp.length() + 1, 0);
+
+ config->mqttPort = (int)server.arg("mqttPort").toInt();
+
+ temp = server.arg("mqttClientID");
+ config->mqttClientID = new char[temp.length() + 1];
+ temp.toCharArray(config->mqttClientID, temp.length() + 1, 0);
+
+ temp = server.arg("mqttPublishTopic");
+ config->mqttPublishTopic = new char[temp.length() + 1];
+ temp.toCharArray(config->mqttPublishTopic, temp.length() + 1, 0);
+
+ temp = server.arg("mqttSubscribeTopic");
+ config->mqttSubscribeTopic = new char[temp.length() + 1];
+ temp.toCharArray(config->mqttSubscribeTopic, temp.length() + 1, 0);
+
+ temp = server.arg("mqttUser");
+ config->mqttUser = new char[temp.length() + 1];
+ temp.toCharArray(config->mqttUser, temp.length() + 1, 0);
+
+ temp = server.arg("mqttPass");
+ config->mqttPass = new char[temp.length() + 1];
+ temp.toCharArray(config->mqttPass, temp.length() + 1, 0);
+
+ config->authSecurity = (byte)server.arg("authSecurity").toInt();
+
+ temp = server.arg("authUser");
+ config->authUser = new char[temp.length() + 1];
+ temp.toCharArray(config->authUser, temp.length() + 1, 0);
+
+ temp = server.arg("authPass");
+ config->authPass = new char[temp.length() + 1];
+ temp.toCharArray(config->authPass, temp.length() + 1, 0);
+
+ config->fuseSize = (int)server.arg("fuseSize").toInt();
+
+ println("Saving configuration now...");
+
+ if (debugger) config->print(debugger);
+ if (config->save())
+ {
+ println("Successfully saved. Will reboot now.");
+ String html = "Successfully Saved!
Device is restarting now...
Go to index";
+ server.send(200, "text/html", html);
+ yield();
+ delay(1000);
+#if defined(ESP8266)
+ ESP.reset();
+#elif defined(ESP32)
+ ESP.restart();
+#endif
+ }
+ else
+ {
+ println("Error saving configuration");
+ String html = "Error saving configuration!
";
+ server.send(500, "text/html", html);
+ }
+}
+
+
+size_t AmsWebServer::print(const char* text)
+{
+ if (debugger) debugger->print(text);
+}
+size_t AmsWebServer::println(const char* text)
+{
+ if (debugger) debugger->println(text);
+}
+size_t AmsWebServer::print(const Printable& data)
+{
+ if (debugger) debugger->print(data);
+}
+size_t AmsWebServer::println(const Printable& data)
+{
+ if (debugger) debugger->println(data);
+}
diff --git a/src/AmsWebServer.h b/src/AmsWebServer.h
new file mode 100644
index 00000000..242be929
--- /dev/null
+++ b/src/AmsWebServer.h
@@ -0,0 +1,61 @@
+#ifndef _AMSWEBSERVER_h
+#define _AMSWEBSERVER_h
+
+#include
+#include "configuration.h"
+
+#if defined(ARDUINO) && ARDUINO >= 100
+ #include "Arduino.h"
+#else
+ #include "WProgram.h"
+#endif
+
+#if defined(ESP8266)
+ #include
+ #include
+#elif defined(ESP32) // ARDUINO_ARCH_ESP32
+ #include
+ #include
+#else
+ #warning "Unsupported board type"
+#endif
+
+class AmsWebServer {
+public:
+ void setup(configuration* config, Stream* debugger);
+ void loop();
+ void setJson(StaticJsonDocument<500> json);
+
+private:
+ configuration* config;
+ Stream* debugger;
+ StaticJsonDocument<500> json;
+ int maxPwr;
+
+#if defined(ESP8266)
+ ESP8266WebServer server;
+#elif defined(ESP32) // ARDUINO_ARCH_ESP32
+ WebServer server;
+#endif
+
+ bool checkSecurity(byte level);
+
+ void indexHtml();
+ void configurationHtml();
+ void bootstrapCss();
+ void applicationCss();
+ void jqueryJs();
+ void gaugemeterJs();
+ void indexJs();
+ void dataJson();
+
+ void handleSave();
+
+ size_t print(const char* text);
+ size_t println(const char* text);
+ size_t print(const Printable& data);
+ size_t println(const Printable& data);
+
+};
+
+#endif
diff --git a/src/Base64.cpp b/src/Base64.cpp
new file mode 100644
index 00000000..5cf68987
--- /dev/null
+++ b/src/Base64.cpp
@@ -0,0 +1,142 @@
+/*
+Copyright (C) 2016 Arturo Guadalupi. All right reserved.
+
+This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+*/
+
+#include "Base64.h"
+#include
+#if (defined(__AVR__))
+#include
+#else
+#include
+#endif
+const char PROGMEM _Base64AlphabetTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/";
+
+int Base64Class::encode(char *output, char *input, int inputLength) {
+ int i = 0, j = 0;
+ int encodedLength = 0;
+ unsigned char A3[3];
+ unsigned char A4[4];
+
+ while(inputLength--) {
+ A3[i++] = *(input++);
+ if(i == 3) {
+ fromA3ToA4(A4, A3);
+
+ for(i = 0; i < 4; i++) {
+ output[encodedLength++] = pgm_read_byte(&_Base64AlphabetTable[A4[i]]);
+ }
+
+ i = 0;
+ }
+ }
+
+ if(i) {
+ for(j = i; j < 3; j++) {
+ A3[j] = '\0';
+ }
+
+ fromA3ToA4(A4, A3);
+
+ for(j = 0; j < i + 1; j++) {
+ output[encodedLength++] = pgm_read_byte(&_Base64AlphabetTable[A4[j]]);
+ }
+
+ while((i++ < 3)) {
+ output[encodedLength++] = '=';
+ }
+ }
+ output[encodedLength] = '\0';
+ return encodedLength;
+}
+
+int Base64Class::decode(char * output, char * input, int inputLength) {
+ int i = 0, j = 0;
+ int decodedLength = 0;
+ unsigned char A3[3];
+ unsigned char A4[4];
+
+
+ while (inputLength--) {
+ if(*input == '=') {
+ break;
+ }
+
+ A4[i++] = *(input++);
+ if (i == 4) {
+ for (i = 0; i <4; i++) {
+ A4[i] = lookupTable(A4[i]);
+ }
+
+ fromA4ToA3(A3,A4);
+
+ for (i = 0; i < 3; i++) {
+ output[decodedLength++] = A3[i];
+ }
+ i = 0;
+ }
+ }
+
+ if (i) {
+ for (j = i; j < 4; j++) {
+ A4[j] = '\0';
+ }
+
+ for (j = 0; j <4; j++) {
+ A4[j] = lookupTable(A4[j]);
+ }
+
+ fromA4ToA3(A3,A4);
+
+ for (j = 0; j < i - 1; j++) {
+ output[decodedLength++] = A3[j];
+ }
+ }
+ output[decodedLength] = '\0';
+ return decodedLength;
+}
+
+int Base64Class::encodedLength(int plainLength) {
+ int n = plainLength;
+ return (n + 2 - ((n + 2) % 3)) / 3 * 4;
+}
+
+int Base64Class::decodedLength(char * input, int inputLength) {
+ int i = 0;
+ int numEq = 0;
+ for(i = inputLength - 1; input[i] == '='; i--) {
+ numEq++;
+ }
+
+ return ((6 * inputLength) / 8) - numEq;
+}
+
+//Private utility functions
+inline void Base64Class::fromA3ToA4(unsigned char * A4, unsigned char * A3) {
+ A4[0] = (A3[0] & 0xfc) >> 2;
+ A4[1] = ((A3[0] & 0x03) << 4) + ((A3[1] & 0xf0) >> 4);
+ A4[2] = ((A3[1] & 0x0f) << 2) + ((A3[2] & 0xc0) >> 6);
+ A4[3] = (A3[2] & 0x3f);
+}
+
+inline void Base64Class::fromA4ToA3(unsigned char * A3, unsigned char * A4) {
+ A3[0] = (A4[0] << 2) + ((A4[1] & 0x30) >> 4);
+ A3[1] = ((A4[1] & 0xf) << 4) + ((A4[2] & 0x3c) >> 2);
+ A3[2] = ((A4[2] & 0x3) << 6) + A4[3];
+}
+
+inline unsigned char Base64Class::lookupTable(char c) {
+ if(c >='A' && c <='Z') return c - 'A';
+ if(c >='a' && c <='z') return c - 71;
+ if(c >='0' && c <='9') return c + 4;
+ if(c == '+') return 62;
+ if(c == '/') return 63;
+ return -1;
+}
+
+Base64Class Base64;
diff --git a/src/Base64.h b/src/Base64.h
new file mode 100644
index 00000000..7330225e
--- /dev/null
+++ b/src/Base64.h
@@ -0,0 +1,26 @@
+/*
+Copyright (C) 2016 Arturo Guadalupi. All right reserved.
+
+This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+*/
+
+#ifndef _BASE64_H
+#define _BASE64_H
+
+class Base64Class{
+ public:
+ int encode(char *output, char *input, int inputLength);
+ int decode(char * output, char * input, int inputLength);
+ int encodedLength(int plainLength);
+ int decodedLength(char * input, int inputLength);
+
+ private:
+ inline void fromA3ToA4(unsigned char * A4, unsigned char * A3);
+ inline void fromA4ToA3(unsigned char * A3, unsigned char * A4);
+ inline unsigned char lookupTable(char c);
+};
+extern Base64Class Base64;
+
+#endif // _BASE64_H
diff --git a/lib/HanConfigAp/src/application_css.h b/src/application_css.h
similarity index 100%
rename from lib/HanConfigAp/src/application_css.h
rename to src/application_css.h
diff --git a/lib/HanConfigAp/src/bootstrap_css.h b/src/bootstrap_css.h
similarity index 100%
rename from lib/HanConfigAp/src/bootstrap_css.h
rename to src/bootstrap_css.h
diff --git a/lib/HanConfigAp/src/configuration_html.h b/src/configuration_html.h
similarity index 99%
rename from lib/HanConfigAp/src/configuration_html.h
rename to src/configuration_html.h
index f814502d..50145936 100644
--- a/lib/HanConfigAp/src/configuration_html.h
+++ b/src/configuration_html.h
@@ -13,7 +13,7 @@ const char CONFIGURATION_HTML[] PROGMEM = R"=="==(
AMS reader - configuration
- v1.0.0
+ ${version}