From 3054bdf5a5d4d9fe79d36d0dd1b287f5a70109f3 Mon Sep 17 00:00:00 2001
From: Hristo Kapanakov
Date: Mon, 2 Nov 2020 19:16:23 +0200
Subject: [PATCH] HTTPUpdateServer library (#4244)
---
.../examples/WebUpdater/WebUpdater.ino | 51 ++++++
libraries/HTTPUpdateServer/keywords.txt | 20 +++
libraries/HTTPUpdateServer/library.properties | 9 +
.../HTTPUpdateServer/src/HTTPUpdateServer.h | 166 ++++++++++++++++++
4 files changed, 246 insertions(+)
create mode 100644 libraries/HTTPUpdateServer/examples/WebUpdater/WebUpdater.ino
create mode 100644 libraries/HTTPUpdateServer/keywords.txt
create mode 100644 libraries/HTTPUpdateServer/library.properties
create mode 100644 libraries/HTTPUpdateServer/src/HTTPUpdateServer.h
diff --git a/libraries/HTTPUpdateServer/examples/WebUpdater/WebUpdater.ino b/libraries/HTTPUpdateServer/examples/WebUpdater/WebUpdater.ino
new file mode 100644
index 00000000..f2503a7e
--- /dev/null
+++ b/libraries/HTTPUpdateServer/examples/WebUpdater/WebUpdater.ino
@@ -0,0 +1,51 @@
+/*
+ To upload through terminal you can use: curl -F "image=@firmware.bin" esp32-webupdate.local/update
+*/
+
+#include
+#include
+#include
+#include
+#include
+
+#ifndef STASSID
+#define STASSID "your-ssid"
+#define STAPSK "your-password"
+#endif
+
+const char* host = "esp32-webupdate";
+const char* ssid = STASSID;
+const char* password = STAPSK;
+
+WebServer httpServer(80);
+HTTPUpdateServer httpUpdater;
+
+void setup(void) {
+
+ Serial.begin(115200);
+ Serial.println();
+ Serial.println("Booting Sketch...");
+ WiFi.mode(WIFI_AP_STA);
+ WiFi.begin(ssid, password);
+
+ while (WiFi.waitForConnectResult() != WL_CONNECTED) {
+ WiFi.begin(ssid, password);
+ Serial.println("WiFi failed, retrying.");
+ }
+
+ MDNS.begin(host);
+ if (MDNS.begin("esp32")) {
+ Serial.println("mDNS responder started");
+ }
+
+
+ httpUpdater.setup(&httpServer);
+ httpServer.begin();
+
+ MDNS.addService("http", "tcp", 80);
+ Serial.printf("HTTPUpdateServer ready! Open http://%s.local/update in your browser\n", host);
+}
+
+void loop(void) {
+ httpServer.handleClient();
+}
\ No newline at end of file
diff --git a/libraries/HTTPUpdateServer/keywords.txt b/libraries/HTTPUpdateServer/keywords.txt
new file mode 100644
index 00000000..a6f8a0c1
--- /dev/null
+++ b/libraries/HTTPUpdateServer/keywords.txt
@@ -0,0 +1,20 @@
+#######################################
+# Syntax Coloring Map For HTTPUpdateServer
+#######################################
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+
+ESP32HTTPUpdateServer KEYWORD1
+
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+
+begin KEYWORD2
+setup KEYWORD2
+
+#######################################
+# Constants (LITERAL1)
+#######################################
\ No newline at end of file
diff --git a/libraries/HTTPUpdateServer/library.properties b/libraries/HTTPUpdateServer/library.properties
new file mode 100644
index 00000000..0601fd57
--- /dev/null
+++ b/libraries/HTTPUpdateServer/library.properties
@@ -0,0 +1,9 @@
+name=HTTPUpdateServer
+version=1.0
+author=Hristo Kapanakov
+maintainer=
+sentence=Simple HTTP Update server based on the WebServer
+paragraph=The library accepts HTTP post requests to the /update url, and updates the ESP32 firmware.
+category=Communication
+url=
+architectures=esp32
\ No newline at end of file
diff --git a/libraries/HTTPUpdateServer/src/HTTPUpdateServer.h b/libraries/HTTPUpdateServer/src/HTTPUpdateServer.h
new file mode 100644
index 00000000..aa53ce6b
--- /dev/null
+++ b/libraries/HTTPUpdateServer/src/HTTPUpdateServer.h
@@ -0,0 +1,166 @@
+#ifndef __HTTP_UPDATE_SERVER_H
+#define __HTTP_UPDATE_SERVER_H
+
+#include
+#include
+#include
+#include
+
+
+static const char serverIndex[] PROGMEM =
+R"(
+
+
+
+
+
+
+
+
+
+ )";
+static const char successResponse[] PROGMEM =
+"Update Success! Rebooting...";
+
+class HTTPUpdateServer
+{
+public:
+ HTTPUpdateServer(bool serial_debug=false) {
+ _serial_output = serial_debug;
+ _server = NULL;
+ _username = emptyString;
+ _password = emptyString;
+ _authenticated = false;
+ }
+
+ void setup(WebServer *server)
+ {
+ setup(server, emptyString, emptyString);
+ }
+
+ void setup(WebServer *server, const String& path)
+ {
+ setup(server, path, emptyString, emptyString);
+ }
+
+ void setup(WebServer *server, const String& username, const String& password)
+ {
+ setup(server, "/update", username, password);
+ }
+
+ void setup(WebServer *server, const String& path, const String& username, const String& password)
+ {
+
+ _server = server;
+ _username = username;
+ _password = password;
+
+ // handler for the /update form page
+ _server->on(path.c_str(), HTTP_GET, [&]() {
+ if (_username != emptyString && _password != emptyString && !_server->authenticate(_username.c_str(), _password.c_str()))
+ return _server->requestAuthentication();
+ _server->send_P(200, PSTR("text/html"), serverIndex);
+ });
+
+ // handler for the /update form POST (once file upload finishes)
+ _server->on(path.c_str(), HTTP_POST, [&]() {
+ if (!_authenticated)
+ return _server->requestAuthentication();
+ if (Update.hasError()) {
+ _server->send(200, F("text/html"), String(F("Update error: ")) + _updaterError);
+ }
+ else {
+ _server->client().setNoDelay(true);
+ _server->send_P(200, PSTR("text/html"), successResponse);
+ delay(100);
+ _server->client().stop();
+ ESP.restart();
+ }
+ }, [&]() {
+ // handler for the file upload, get's the sketch bytes, and writes
+ // them through the Update object
+ HTTPUpload& upload = _server->upload();
+
+ if (upload.status == UPLOAD_FILE_START) {
+ _updaterError.clear();
+ if (_serial_output)
+ Serial.setDebugOutput(true);
+
+ _authenticated = (_username == emptyString || _password == emptyString || _server->authenticate(_username.c_str(), _password.c_str()));
+ if (!_authenticated) {
+ if (_serial_output)
+ Serial.printf("Unauthenticated Update\n");
+ return;
+ }
+
+ if (_serial_output)
+ Serial.printf("Update: %s\n", upload.filename.c_str());
+ if (upload.name == "filesystem") {
+ if (!Update.begin(SPIFFS.totalBytes(), U_SPIFFS)) {//start with max available size
+ if (_serial_output) Update.printError(Serial);
+ }
+ }
+ else {
+ uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
+ if (!Update.begin(maxSketchSpace, U_FLASH)) {//start with max available size
+ _setUpdaterError();
+ }
+ }
+ }
+ else if (_authenticated && upload.status == UPLOAD_FILE_WRITE && !_updaterError.length()) {
+ if (_serial_output) Serial.printf(".");
+ if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
+ _setUpdaterError();
+ }
+ }
+ else if (_authenticated && upload.status == UPLOAD_FILE_END && !_updaterError.length()) {
+ if (Update.end(true)) { //true to set the size to the current progress
+ if (_serial_output) Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
+ }
+ else {
+ _setUpdaterError();
+ }
+ if (_serial_output) Serial.setDebugOutput(false);
+ }
+ else if (_authenticated && upload.status == UPLOAD_FILE_ABORTED) {
+ Update.end();
+ if (_serial_output) Serial.println("Update was aborted");
+ }
+ delay(0);
+ });
+ }
+
+ void updateCredentials(const String& username, const String& password)
+ {
+ _username = username;
+ _password = password;
+ }
+
+protected:
+ void _setUpdaterError()
+ {
+ if (_serial_output) Update.printError(Serial);
+ StreamString str;
+ Update.printError(str);
+ _updaterError = str.c_str();
+ }
+
+private:
+ bool _serial_output;
+ WebServer *_server;
+ String _username;
+ String _password;
+ bool _authenticated;
+ String _updaterError;
+};
+
+
+#endif
\ No newline at end of file