From 866f8138e7477a16a82420297e892025679684bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Xose=20P=C3=A9rez?= Date: Sun, 13 Nov 2016 23:29:54 +0100 Subject: [PATCH] Migrated to AsyncWebServer, AsyncWebSocket and AsyncMqttClient --- code/html/custom.js | 8 +- code/html/fsversion | 2 +- code/html/index.html | 6 ++ code/platformio.ini | 40 ++++++---- code/src/defaults.h | 2 + code/src/main.ino | 8 +- code/src/mqtt.ino | 106 +++++++++++++------------- code/src/version.h | 2 +- code/src/{websockets.ino => web.ino} | 107 ++++++++++++++++----------- code/src/webserver.ino | 86 --------------------- 10 files changed, 165 insertions(+), 202 deletions(-) rename code/src/{websockets.ino => web.ino} (65%) delete mode 100644 code/src/webserver.ino diff --git a/code/html/custom.js b/code/html/custom.js index 287f90e6..828c26dc 100644 --- a/code/html/custom.js +++ b/code/html/custom.js @@ -40,6 +40,11 @@ function toggleMenu() { function processData(data) { + // messages + if ("message" in data) { + window.alert(data.message); + } + // pre-process if ("network" in data) { data.network = data.network.toUpperCase(); @@ -136,7 +141,8 @@ function init() { $(".pure-menu-link").on('click', showPanel); var host = window.location.hostname; - websock = new WebSocket('ws://' + host + ':81/'); + //host = '192.168.1.115'; + websock = new WebSocket('ws://' + host + '/ws'); websock.onopen = function(evt) {}; websock.onclose = function(evt) {}; websock.onerror = function(evt) {}; diff --git a/code/html/fsversion b/code/html/fsversion index e3e18070..3eefcb9d 100644 --- a/code/html/fsversion +++ b/code/html/fsversion @@ -1 +1 @@ -0.9.8 +1.0.0 diff --git a/code/html/index.html b/code/html/index.html index 3b82523d..fcb2cfd9 100644 --- a/code/html/index.html +++ b/code/html/index.html @@ -167,6 +167,12 @@ +
+ +
Change the password for the 'admin' user to access this web interface.
+ +
+ diff --git a/code/platformio.ini b/code/platformio.ini index 8400b7c6..a9cdcc9c 100644 --- a/code/platformio.ini +++ b/code/platformio.ini @@ -1,10 +1,13 @@ [platformio] env_default = sonoff-pow-debug +[common] +lib_install = 19,31,44,64,305,306,346,727 + [env:sonoff-debug] platform = espressif8266 framework = arduino -lib_install = 19,31,44,64,89,549,727 +lib_install = ${common.lib_install} extra_script = pio_hooks.py board = esp01_1m build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DSONOFF @@ -12,7 +15,7 @@ build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DSONOFF [env:sonoff-debug-ota] platform = espressif8266 framework = arduino -lib_install = 19,31,44,64,89,549,727 +lib_install = ${common.lib_install} extra_script = pio_hooks.py board = esp01_1m build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DSONOFF @@ -23,15 +26,26 @@ upload_flags = --auth=fibonacci --port 8266 [env:sonoff-pow-debug] platform = espressif8266 framework = arduino -lib_install = 19,31,44,64,89,549,727 +lib_install = ${common.lib_install} +extra_script = pio_hooks.py +board = esp01_1m +build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DSONOFF_POW + +[env:sonoff-pow-debug-ota] +platform = espressif8266 +framework = arduino +lib_install = ${common.lib_install} extra_script = pio_hooks.py board = esp01_1m build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DSONOFF_POW +upload_speed = 115200 +upload_port = "192.168.4.1" +upload_flags = --auth=fibonacci --port 8266 [env:slampher-debug] platform = espressif8266 framework = arduino -lib_install = 19,31,44,64,89,549,727 +lib_install = ${common.lib_install} extra_script = pio_hooks.py board = esp01_1m build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DSLAMPHER @@ -39,7 +53,7 @@ build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DSLAMPHER [env:slampher-debug-ota] platform = espressif8266 framework = arduino -lib_install = 19,31,44,64,89,549,727 +lib_install = ${common.lib_install} extra_script = pio_hooks.py board = esp01_1m build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DSLAMPHER @@ -50,7 +64,7 @@ upload_flags = --auth=fibonacci --port 8266 [env:s20-debug] platform = espressif8266 framework = arduino -lib_install = 19,31,44,64,89,549,727 +lib_install = ${common.lib_install} extra_script = pio_hooks.py board = esp01_1m build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DS20 @@ -58,7 +72,7 @@ build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DS20 [env:s20-debug-ota] platform = espressif8266 framework = arduino -lib_install = 19,31,44,64,89,549,727 +lib_install = ${common.lib_install} extra_script = pio_hooks.py board = esp01_1m build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DS20 @@ -69,7 +83,7 @@ upload_flags = --auth=fibonacci --port 8266 [env:node-debug] platform = espressif8266 framework = arduino -lib_install = 19,31,44,64,89,549,727 +lib_install = ${common.lib_install} extra_script = pio_hooks.py board = nodemcuv2 build_flags = -DNODEMCUV2 -DDEBUG_PORT=Serial @@ -77,7 +91,7 @@ build_flags = -DNODEMCUV2 -DDEBUG_PORT=Serial [env:node-debug-ota] platform = espressif8266 framework = arduino -lib_install = 19,31,44,64,89,549,727 +lib_install = ${common.lib_install} extra_script = pio_hooks.py board = nodemcuv2 build_flags = -DNODEMCUV2 -DDEBUG_PORT=Serial @@ -91,7 +105,7 @@ upload_flags = --auth=fibonacci --port 8266 topic = /home/cellar/airconditioner/ip platform = espressif8266 framework = arduino -lib_install = 19,31,44,64,89,549,727 +lib_install = ${common.lib_install} extra_script = pio_hooks.py board = esp01_1m build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DS20 @@ -103,7 +117,7 @@ upload_flags = --auth=fibonacci --port 8266 topic = /home/cellar/washer/ip platform = espressif8266 framework = arduino -lib_install = 19,31,44,64,89,549,727 +lib_install = ${common.lib_install} extra_script = pio_hooks.py board = esp01_1m upload_speed = 115200 @@ -115,7 +129,7 @@ build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DSONOFF -DENABLE topic = /home/studio/lamp/ip platform = espressif8266 framework = arduino -lib_install = 19,31,44,64,89,549,727 +lib_install = ${common.lib_install} extra_script = pio_hooks.py board = esp01_1m build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DSONOFF @@ -127,7 +141,7 @@ upload_flags = --auth=fibonacci --port 8266 topic = /home/living/lamp/ip platform = espressif8266 framework = arduino -lib_install = 19,31,44,64,89,549,727 +lib_install = ${common.lib_install} extra_script = pio_hooks.py board = esp01_1m build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DSONOFF diff --git a/code/src/defaults.h b/code/src/defaults.h index 996c05f3..bb99d593 100644 --- a/code/src/defaults.h +++ b/code/src/defaults.h @@ -87,6 +87,8 @@ #define MQTT_PORT 1883 #define MQTT_TOPIC "/test/switch/{identifier}" #define MQTT_RETAIN true +#define MQTT_QOS 0 +#define MQTT_KEEPALIVE 30 #define MQTT_RECONNECT_DELAY 10000 #define MQTT_STATUS_TOPIC "" #define MQTT_IP_TOPIC "/ip" diff --git a/code/src/main.ino b/code/src/main.ino index 26b91b36..7b8e90ba 100644 --- a/code/src/main.ino +++ b/code/src/main.ino @@ -28,7 +28,8 @@ along with this program. If not, see . // ----------------------------------------------------------------------------- #include -#include +#include +#include #include "FS.h" String getSetting(const String& key, String defaultValue = ""); @@ -144,8 +145,7 @@ void setup() { wifiSetup(); otaSetup(); mqttSetup(); - webServerSetup(); - webSocketSetup(); + webSetup(); ntpSetup(); #if ENABLE_NOFUSS @@ -174,8 +174,6 @@ void loop() { wifiLoop(); otaLoop(); mqttLoop(); - webServerLoop(); - webSocketLoop(); ntpLoop(); #if ENABLE_NOFUSS diff --git a/code/src/mqtt.ino b/code/src/mqtt.ino index 90f46c94..aceb501b 100644 --- a/code/src/mqtt.ino +++ b/code/src/mqtt.ino @@ -7,12 +7,11 @@ Copyright (C) 2016 by Xose Pérez */ -#include #include +#include + +AsyncMqttClient mqtt; -WiFiClient client; -PubSubClient mqtt(client); -boolean mqttStatus = false; String mqttTopic; bool isCallbackMessage = false; @@ -39,19 +38,48 @@ void mqttSend(char * topic, char * message) { if (isCallbackMessage) return; String path = mqttTopic + String(topic); DEBUG_MSG("[MQTT] Sending %s %s\n", (char *) path.c_str(), message); - mqtt.publish(path.c_str(), message, MQTT_RETAIN); + mqtt.publish(path.c_str(), MQTT_QOS, MQTT_RETAIN, message); } -void mqttCallback(char* topic, byte* payload, unsigned int length) { +void _mqttOnConnect(bool sessionPresent) { + + DEBUG_MSG("[MQTT] Connected!\n"); + + // Send status via webSocket + webSocketSend((char *) "{\"mqttStatus\": true}"); + + // Build MQTT topics + buildTopics(); + + // Say hello and report our IP and VERSION + mqttSend((char *) MQTT_IP_TOPIC, (char *) getIP().c_str()); + mqttSend((char *) MQTT_VERSION_TOPIC, (char *) APP_VERSION); + char buffer[10]; + getFSVersion(buffer); + mqttSend((char *) MQTT_FSVERSION_TOPIC, buffer); + + // Publish current relay status + mqttSend((char *) MQTT_STATUS_TOPIC, (char *) (digitalRead(RELAY_PIN) ? "1" : "0")); + + // Subscribe to topic + DEBUG_MSG("[MQTT] Subscribing to %s\n", (char *) mqttTopic.c_str()); + mqtt.subscribe(mqttTopic.c_str(), MQTT_QOS); + +} + +void _mqttOnDisconnect(AsyncMqttClientDisconnectReason reason) { + + // Send status via webSocket + webSocketSend((char *) "{\"mqttStatus\": false}"); + +} + +void _mqttOnMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) { static bool isFirstMessage = true; - #ifdef DEBUG_PORT - char buffer[length+1]; - memcpy(buffer, payload, length); - buffer[length] = 0; - DEBUG_MSG("[MQTT] Received %s %s\n", topic, buffer); - #endif + payload[len] = '\0'; + DEBUG_MSG("[MQTT] Received %s %s\n", topic, payload); // If relayMode is not SAME avoid responding to a retained message if (isFirstMessage) { @@ -89,52 +117,31 @@ void mqttConnect() { if (host.length() == 0) return; DEBUG_MSG("[MQTT] Connecting to broker at %s", (char *) host.c_str()); + mqtt.setServer(host.c_str(), port.toInt()); + mqtt + .setKeepAlive(MQTT_KEEPALIVE) + .setCleanSession(false) + //.setWill("topic/online", 2, true, "no") + .setClientId(getSetting("hostname", HOSTNAME).c_str()); if ((user != "") & (pass != "")) { - DEBUG_MSG(" as user %s: ", (char *) user.c_str()); - mqtt.connect(getSetting("hostname", HOSTNAME).c_str(), user.c_str(), pass.c_str()); + DEBUG_MSG(" as user %s.\n", (char *) user.c_str()); + mqtt.setCredentials(user.c_str(), pass.c_str()); } else { - DEBUG_MSG(" anonymously: "); - mqtt.connect(getSetting("hostname", HOSTNAME).c_str()); + DEBUG_MSG(" anonymously\n"); } - if (mqtt.connected()) { + mqtt.connect(); - DEBUG_MSG("connected!\n"); - - buildTopics(); - mqttStatus = true; - - // Send status via webSocket - webSocketSend((char *) "{\"mqttStatus\": true}"); - - // Say hello and report our IP and VERSION - mqttSend((char *) MQTT_IP_TOPIC, (char *) getIP().c_str()); - mqttSend((char *) MQTT_VERSION_TOPIC, (char *) APP_VERSION); - char buffer[10]; - getFSVersion(buffer); - mqttSend((char *) MQTT_FSVERSION_TOPIC, buffer); - - // Publish current relay status - mqttSend((char *) MQTT_STATUS_TOPIC, (char *) (digitalRead(RELAY_PIN) ? "1" : "0")); - - // Subscribe to topic - DEBUG_MSG("[MQTT] Subscribing to %s\n", (char *) mqttTopic.c_str()); - mqtt.subscribe(mqttTopic.c_str()); - - - } else { - - DEBUG_MSG("failed (rc=%d)\n", mqtt.state()); - - } } } void mqttSetup() { - mqtt.setCallback(mqttCallback); + mqtt.onConnect(_mqttOnConnect); + mqtt.onDisconnect(_mqttOnDisconnect); + mqtt.onMessage(_mqttOnMessage); } void mqttLoop() { @@ -145,11 +152,6 @@ void mqttLoop() { if (!mqtt.connected()) { - if (mqttStatus) { - webSocketSend((char *) "{\"mqttStatus\": false}"); - mqttStatus = false; - } - unsigned long currPeriod = millis() / MQTT_RECONNECT_DELAY; if (currPeriod != lastPeriod) { lastPeriod = currPeriod; @@ -158,8 +160,6 @@ void mqttLoop() { } - if (mqtt.connected()) mqtt.loop(); - } } diff --git a/code/src/version.h b/code/src/version.h index 624ada9e..a2b73d73 100644 --- a/code/src/version.h +++ b/code/src/version.h @@ -1,4 +1,4 @@ #define APP_NAME "Espurna" -#define APP_VERSION "0.9.9" +#define APP_VERSION "1.0.0" #define APP_AUTHOR "xose.perez@gmail.com" #define APP_WEBSITE "http://tinkerman.cat" diff --git a/code/src/websockets.ino b/code/src/web.ino similarity index 65% rename from code/src/websockets.ino rename to code/src/web.ino index 8bab19be..da532745 100644 --- a/code/src/websockets.ino +++ b/code/src/web.ino @@ -1,17 +1,22 @@ /* -RENTALITO +ESPurna WEBSERVER MODULE Copyright (C) 2016 by Xose Pérez */ -#include +#include +#include +#include +#include "FS.h" #include +#include "AsyncJson.h" #include -WebSocketsServer webSocket = WebSocketsServer(81); +AsyncWebServer server(80); +AsyncWebSocket ws("/ws"); // ----------------------------------------------------------------------------- // WEBSOCKETS @@ -19,21 +24,22 @@ WebSocketsServer webSocket = WebSocketsServer(81); bool webSocketSend(char * payload) { //DEBUG_MSG("[WEBSOCKET] Broadcasting '%s'\n", payload); - webSocket.broadcastTXT(payload); + ws.textAll(payload); } -bool webSocketSend(uint8_t num, char * payload) { - //DEBUG_MSG("[WEBSOCKET] Sending '%s' to #%d\n", payload, num); - webSocket.sendTXT(num, payload); +bool webSocketSend(uint32_t client_id, char * payload) { + //DEBUG_MSG("[WEBSOCKET] Sending '%s' to #%ld\n", payload, client_id); + ws.text(client_id, payload); } -void webSocketParse(uint8_t num, uint8_t * payload, size_t length) { +void webSocketParse(uint32_t client_id, uint8_t * payload, size_t length) { // Parse JSON input DynamicJsonBuffer jsonBuffer; JsonObject& root = jsonBuffer.parseObject((char *) payload); if (!root.success()) { DEBUG_MSG("[WEBSOCKET] Error parsing data\n"); + ws.text(client_id, "{\"message\": \"Error parsing data!\"}"); return; } @@ -62,8 +68,6 @@ void webSocketParse(uint8_t num, uint8_t * payload, size_t length) { for (unsigned int i=0; iid()); + DEBUG_MSG("[WEBSOCKET] #%u connected, ip: %d.%d.%d.%d, url: %s\n", client->id(), ip[0], ip[1], ip[2], ip[3], server->url()); + } + #endif + webSocketStart(client->id()); + } else if(type == WS_EVT_DISCONNECT) { + DEBUG_MSG("[WEBSOCKET] #%u disconnected\n", client->id()); + } else if(type == WS_EVT_ERROR) { + DEBUG_MSG("[WEBSOCKET] #%u error(%u): %s\n", client->id(), *((uint16_t*)arg), (char*)data); + } else if(type == WS_EVT_PONG) { + DEBUG_MSG("[WEBSOCKET] #%u pong(%u): %s\n", client->id(), len, len ? (char*) data : ""); + } else if(type == WS_EVT_DATA) { + webSocketParse(client->id(), data, len); } - } -void webSocketSetup() { - webSocket.begin(); - webSocket.onEvent(webSocketEvent); -} +// ----------------------------------------------------------------------------- +// WEBSERVER +// ----------------------------------------------------------------------------- + +void webSetup() { + + // Setup websocket plugin + ws.onEvent(webSocketEvent); + server.addHandler(&ws); + + // Serve static files + server.serveStatic("/", SPIFFS, "/").setDefaultFile("/index.html"); + + // 404 + server.onNotFound([](AsyncWebServerRequest *request){ + request->send(404); + }); + + // Run server + server.begin(); -void webSocketLoop() { - webSocket.loop(); } diff --git a/code/src/webserver.ino b/code/src/webserver.ino deleted file mode 100644 index 3374e958..00000000 --- a/code/src/webserver.ino +++ /dev/null @@ -1,86 +0,0 @@ -/* - -ESPurna -WEBSERVER MODULE - -Copyright (C) 2016 by Xose Pérez - -*/ - -#include -#include -#include "FS.h" - -ESP8266WebServer server(80); - -// ----------------------------------------------------------------------------- -// WEBSERVER -// ----------------------------------------------------------------------------- - -String getContentType(String filename) { - if (server.hasArg("download")) return "application/octet-stream"; - else if (filename.endsWith(".htm")) return "text/html"; - else if (filename.endsWith(".html")) return "text/html"; - else if (filename.endsWith(".css")) return "text/css"; - else if (filename.endsWith(".js")) return "application/javascript"; - else if (filename.endsWith(".png")) return "image/png"; - else if (filename.endsWith(".gif")) return "image/gif"; - else if (filename.endsWith(".jpg")) return "image/jpeg"; - else if (filename.endsWith(".ico")) return "image/x-icon"; - else if (filename.endsWith(".xml")) return "text/xml"; - else if (filename.endsWith(".pdf")) return "application/x-pdf"; - else if (filename.endsWith(".zip")) return "application/x-zip"; - else if (filename.endsWith(".gz")) return "application/x-gzip"; - return "text/plain"; -} - -bool handleFileRead(String path) { - - DEBUG_MSG("[WEBSERVER] Request: %s\n", (char *) path.c_str()); - - if (path.endsWith("/")) path += "index.html"; - String contentType = getContentType(path); - String pathWithGz = path + ".gz"; - if (SPIFFS.exists(pathWithGz)) path = pathWithGz; - - if (SPIFFS.exists(path)) { - File file = SPIFFS.open(path, "r"); - size_t sent = server.streamFile(file, contentType); - size_t contentLength = file.size(); - file.close(); - return true; - } - - return false; - -} - -void webServerSetup() { - - // Anything else - server.onNotFound([]() { - - // Hidden files - #ifndef DEBUG_PORT - if (server.uri().startsWith("/.")) { - server.send(403, "text/plain", "Forbidden"); - return; - } - #endif - - // Existing files in SPIFFS - if (!handleFileRead(server.uri())) { - server.send(404, "text/plain", "NotFound"); - return; - } - - }); - - // Run server - server.begin(); - -} - -void webServerLoop() { - server.handleClient(); -}