Skip to content

HTTPUpdateServer callbacks for progress/errors #2694

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

Closed
wants to merge 4 commits into from
Closed
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
173 changes: 123 additions & 50 deletions libraries/ESP8266HTTPUpdateServer/src/ESP8266HTTPUpdateServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,20 @@ R"(<html><body><form method='POST' action='' enctype='multipart/form-data'>
const char* ESP8266HTTPUpdateServer::_failedResponse = R"(Update Failed!)";
const char* ESP8266HTTPUpdateServer::_successResponse = "<META http-equiv=\"refresh\" content=\"15;URL=\">Update Success! Rebooting...";

template<class ...Ts>
void ESP8266HTTPUpdateServer::debug(const char *fmt, Ts... args) {
if (_serial_output) {
Serial.printf(fmt, args...);
}
}

ESP8266HTTPUpdateServer::ESP8266HTTPUpdateServer(bool serial_debug)
{
_serial_output = serial_debug;
_server = NULL;
_username = NULL;
_password = NULL;
_authenticated = false;
_serial_output = serial_debug;
_server = NULL;
_username = NULL;
_password = NULL;
_authenticated = false;
}

void ESP8266HTTPUpdateServer::setup(ESP8266WebServer *server, const char * path, const char * username, const char * password)
Expand All @@ -31,57 +38,123 @@ void ESP8266HTTPUpdateServer::setup(ESP8266WebServer *server, const char * path,
_password = (char *)password;

// handler for the /update form page
_server->on(path, HTTP_GET, [&](){
if(_username != NULL && _password != NULL && !_server->authenticate(_username, _password))
return _server->requestAuthentication();
_server->send(200, "text/html", _serverIndex);
_server->on(path, HTTP_GET, [&]() {
if (_username != NULL && _password != NULL && !_server->authenticate(_username, _password)) {
return _server->requestAuthentication();
}
_server->send(200, "text/html", _serverIndex);
});

// handler for the /update form POST (once file upload finishes)
_server->on(path, HTTP_POST, [&](){
if(!_authenticated)
return _server->requestAuthentication();
_server->send(200, "text/html", Update.hasError() ? _failedResponse : _successResponse);
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){
if (_serial_output)
Serial.setDebugOutput(true);

_authenticated = (_username == NULL || _password == NULL || _server->authenticate(_username, _password));
if(!_authenticated){
if (_serial_output)
Serial.printf("Unauthenticated Update\n");
return;
_server->on(path, HTTP_POST, [&]() {
if (!_authenticated) {
return _server->requestAuthentication();
}
_server->send(200, "text/html", Update.hasError() ? _failedResponse : _successResponse);
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) {
if (_serial_output) {
Serial.setDebugOutput(true);
}
handle_start();

WiFiUDP::stopAll();
if (_serial_output)
Serial.printf("Update: %s\n", upload.filename.c_str());
uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
if(!Update.begin(maxSketchSpace)){//start with max available size
if (_serial_output) Update.printError(Serial);
}
} else if(_authenticated && upload.status == UPLOAD_FILE_WRITE){
if (_serial_output) Serial.printf(".");
if(Update.write(upload.buf, upload.currentSize) != upload.currentSize){
if (_serial_output) Update.printError(Serial);
_authenticated = (_username == NULL || _password == NULL || _server->authenticate(_username, _password));
if (!_authenticated) {
debug("Unauthenticated Update\n");
handle_error(1);
return;
}

WiFiUDP::stopAll();
debug("Update: %s\n", upload.filename.c_str());
uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
if (!Update.begin(maxSketchSpace)) {//start with max available size
if (_serial_output) {
Update.printError(Serial);
handle_error(2);
}
}
handle_progress(0, upload.totalSize);
} else if (_authenticated && upload.status == UPLOAD_FILE_WRITE) {
debug(".");
if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
if (_serial_output) {
Update.printError(Serial);
handle_error(3);
}
}
handle_progress(upload.currentSize, upload.totalSize);
} else if (_authenticated && upload.status == UPLOAD_FILE_END) {
if (Update.end(true)) { //true to set the size to the current progress
debug("Update Success: %u\nRebooting...\n", upload.totalSize);
handle_progress(upload.totalSize, upload.totalSize);
handle_end();
} else {
if (_serial_output) {
Update.printError(Serial);
handle_error(4);
}
}
if (_serial_output) {
Serial.setDebugOutput(false);
}
} else if (_authenticated && upload.status == UPLOAD_FILE_ABORTED) {
Update.end();
debug("Update was aborted");
handle_error(5);
}
} else if(_authenticated && upload.status == UPLOAD_FILE_END){
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 {
if (_serial_output) Update.printError(Serial);
}
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);
delay(0);
});
}

void ESP8266HTTPUpdateServer::onStart(THandlerFunction fn)
{
_cb_start = fn;
}

void ESP8266HTTPUpdateServer::onEnd(THandlerFunction fn)
{
_cb_end = fn;
}

void ESP8266HTTPUpdateServer::onError(THandlerFunction_Error fn)
{
_cb_error = fn;
}

void ESP8266HTTPUpdateServer::onProgress(THandlerFunction_Progress fn)
{
_cb_progress = fn;
}

void ESP8266HTTPUpdateServer::handle_start()
{
if (_cb_start) {
_cb_start();
}
}

void ESP8266HTTPUpdateServer::handle_end()
{
if (_cb_end) {
_cb_end();
}
}

void ESP8266HTTPUpdateServer::handle_error(int err)
{
if (_cb_error) {
_cb_error(err);
}
}

void ESP8266HTTPUpdateServer::handle_progress(unsigned int i, unsigned int j)
{
if (_cb_progress) {
_cb_progress(i, j);
}
}
60 changes: 46 additions & 14 deletions libraries/ESP8266HTTPUpdateServer/src/ESP8266HTTPUpdateServer.h
Original file line number Diff line number Diff line change
@@ -1,38 +1,70 @@
#ifndef __HTTP_UPDATE_SERVER_H
#define __HTTP_UPDATE_SERVER_H

#include <functional>

class ESP8266WebServer;

class ESP8266HTTPUpdateServer
{
private:
bool _serial_output;
ESP8266WebServer *_server;
static const char *_serverIndex;
static const char *_failedResponse;
static const char *_successResponse;
char * _username;
char * _password;
bool _authenticated;
public:
ESP8266HTTPUpdateServer(bool serial_debug=false);
public:
typedef std::function<void(void)> THandlerFunction;
typedef std::function<void(int)> THandlerFunction_Error; // fixme enum?
typedef std::function<void(unsigned int chunk, unsigned int total)> THandlerFunction_Progress;

ESP8266HTTPUpdateServer(bool serial_debug = false);
void setup(ESP8266WebServer *server)
{
setup(server, NULL, NULL);
setup(server, NULL, NULL);
}

void setup(ESP8266WebServer *server, const char * path)
{
setup(server, path, NULL, NULL);
setup(server, path, NULL, NULL);
}

void setup(ESP8266WebServer *server, const char * username, const char * password)
{
setup(server, "/update", username, password);
setup(server, "/update", username, password);
}

void setup(ESP8266WebServer *server, const char * path, const char * username, const char * password);

//This callback will be called when an update starts
void onStart(THandlerFunction fn);

//This callback will be called when an update has finished
void onEnd(THandlerFunction fn);

//This callback will be called when an update encounters any error
void onError(THandlerFunction_Error fn);

//This callback will be called when an update is under way
void onProgress(THandlerFunction_Progress fn);


private:
bool _serial_output;
ESP8266WebServer *_server;
static const char *_serverIndex;
static const char *_failedResponse;
static const char *_successResponse;
char * _username;
char * _password;
bool _authenticated;

THandlerFunction _cb_start;
THandlerFunction _cb_end;
THandlerFunction_Error _cb_error;
THandlerFunction_Progress _cb_progress;
protected:
template<class ...Ts>
void debug(const char *fmt, Ts... args);
void handle_start();
void handle_end();
void handle_error(int);
void handle_progress(unsigned int, unsigned int);

};


Expand Down