Skip to content

HTTPUpdateServer library #4244

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions libraries/HTTPUpdateServer/examples/WebUpdater/WebUpdater.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
To upload through terminal you can use: curl -F "[email protected]" esp32-webupdate.local/update
*/

#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <HTTPUpdateServer.h>

#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();
}
20 changes: 20 additions & 0 deletions libraries/HTTPUpdateServer/keywords.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#######################################
# Syntax Coloring Map For HTTPUpdateServer
#######################################

#######################################
# Datatypes (KEYWORD1)
#######################################

ESP32HTTPUpdateServer KEYWORD1

#######################################
# Methods and Functions (KEYWORD2)
#######################################

begin KEYWORD2
setup KEYWORD2

#######################################
# Constants (LITERAL1)
#######################################
9 changes: 9 additions & 0 deletions libraries/HTTPUpdateServer/library.properties
Original file line number Diff line number Diff line change
@@ -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
166 changes: 166 additions & 0 deletions libraries/HTTPUpdateServer/src/HTTPUpdateServer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
#ifndef __HTTP_UPDATE_SERVER_H
#define __HTTP_UPDATE_SERVER_H

#include<SPIFFS.h>
#include <StreamString.h>
#include <Update.h>
#include <WebServer.h>


static const char serverIndex[] PROGMEM =
R"(<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width,initial-scale=1'/>
</head>
<body>
<form method='POST' action='' enctype='multipart/form-data'>
Firmware:<br>
<input type='file' accept='.bin,.bin.gz' name='firmware'>
<input type='submit' value='Update Firmware'>
</form>
<form method='POST' action='' enctype='multipart/form-data'>
FileSystem:<br>
<input type='file' accept='.bin,.bin.gz,.image' name='filesystem'>
<input type='submit' value='Update FileSystem'>
</form>
</body>
</html>)";
static const char successResponse[] PROGMEM =
"<META http-equiv=\"refresh\" content=\"15;URL=/\">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