Using websocket to get proper firmware upload byte count

This commit is contained in:
Ray Jones 2019-05-18 18:49:22 +10:00
parent da33a02844
commit fc8eef73a6
5 changed files with 158 additions and 33 deletions

View file

@ -564,7 +564,7 @@ void loop()
case CommStates::Idle:
#if USE_SW_WATCHDOG == 1
timerWrite(watchdogTimer, 0); //reset timer (feed watchdog)
feedWatchdog(); //reset timer (feed watchdog)
#endif
moderator = 50;
@ -1338,4 +1338,9 @@ int getBoardRevision()
void ShowOTAScreen(int percent, bool webupdate)
{
ScreenManager.showOTAMessage(percent, webupdate);
}
}
void feedWatchdog()
{
timerWrite(watchdogTimer, 0); //reset timer (feed watchdog)
}

View file

@ -0,0 +1,83 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<script>
// global variables
var sendSize;
var Socket;
function _(el) {
return document.getElementById(el);
}
function init() {
Socket = new WebSocket('ws://' + window.location.hostname + ':81/');
Socket.onmessage = function(event){
var response = JSON.parse(event.data);
var key;
for(key in response) {
console.log("JSON decode:", key, response[key]);
switch(key) {
case "progress":
// actual data bytes received as fed back via web socket
var bytes = response[key];
_("loaded_n_total").innerHTML = "Uploaded " + bytes + " bytes of " + sendSize;
var percent = Math.round( 100 * (bytes / sendSize));
_("progressBar").value = percent;
_("status").innerHTML = percent+"% uploaded.. please wait";
break;
}
}
}
}
function uploadFile() {
var file = _("file1").files[0];
sendSize = file.size;
var formdata = new FormData();
formdata.append("update", file);
var ajax = new XMLHttpRequest();
// progress is handled via websocket JSON sent from controller
// using server side progress only shows the buffer filling, not actual delivery.
ajax.addEventListener("load", completeHandler, false);
ajax.addEventListener("error", errorHandler, false);
ajax.addEventListener("abort", abortHandler, false);
ajax.open("POST", "/updatenow");
ajax.send(formdata);
}
function completeHandler(event) {
console.log(event);
_("status").innerHTML = event.target.responseText;
_("progressBar").value = 0;
_("loaded_n_total").innerHTML = "Uploaded " + sendSize + " bytes of " + sendSize;
setTimeout(function () {
window.location.assign("/");
}, 5000);
}
function errorHandler(event) {
console.log(event);
_("status").innerHTML = "Upload Failed";
}
function abortHandler(event) {
console.log(event);
_("status").innerHTML = "Upload Aborted";
}
</script>
<style>
body {font-family: Arial, Helvetica, sans-serif;}
</style>
<title>Afterburner firmware update</title>
</head>
<body onload="javascript:init()">
<h1>Afterburner firmware update</h1>
<form id='upload_form' method='POST' enctype='multipart/form-data'>
<input type='file' name='file1' id='file1'> <BR>
<input type='button' value='Update' onclick="uploadFile()">
<progress id="progressBar" value="0" max="100" style="width:300px;"></progress><BR>
<h3 id="status"></h3>
<p id="loaded_n_total"></p>
<BR>
<input type='button' onclick=window.location.assign("/") value='Cancel'>
</form>
</body>
</html>

View file

@ -63,6 +63,7 @@ extern int getBoardRevision();
extern void setupGPIO();
extern void setGPIO(int channel, bool state);
extern bool getGPIO(int channel);
extern void feedWatchdog();

View file

@ -26,6 +26,7 @@
#include "../Protocol/TxManage.h"
#include "../Protocol/helpers.h"
#include "../cfg/pins.h"
#include "../cfg/BTCConfig.h"
#include "Index.h"
#include "../Utility/BTC_JSON.h"
#include "../Utility/Moderator.h"
@ -106,10 +107,6 @@ void handleReset() {
server.send(200, "text/plain", "Start Config Portal - Resetting Wifi credentials!");
DebugPort.println("diconnecting client and wifi, then rebooting");
delay(500);
//client.disconnect();
// wifi_station_disconnect();
// wm.disconnect();
// wm.resetSettings();
wifiEnterConfigPortal(true, true, 3000);
}
@ -118,10 +115,6 @@ void handleFormat() {
DebugPort.println("Formatting SPIFFS partition");
delay(500);
SPIFFS.format();
//client.disconnect();
// wifi_station_disconnect();
// wm.disconnect();
// wm.resetSettings();
}
void handleBTCNotFound() {
@ -142,10 +135,58 @@ void handleBTCNotFound() {
}
const char* serverIndex = R"=====(
<!DOCTYPE html>
<style>body {font-family: Arial, Helvetica, sans-serif;}</style>
<html>
<head>
<script>
function _(el) {
return document.getElementById(el);
}
function uploadFile() {
var file = _("update").files[0];
var formdata = new FormData();
formdata.append("update", file);
var ajax = new XMLHttpRequest();
ajax.upload.addEventListener("progress", progressHandler, false);
ajax.addEventListener("load", completeHandler, false);
ajax.addEventListener("error", errorHandler, false);
ajax.addEventListener("abort", abortHandler, false);
ajax.open("POST", "/updatenow")
ajax.send(formdata);
}
function progressHandler(event) {
_("loaded_n_total").innerHTML = "Uploaded "+event.loaded+" bytes of "event.total;
var percent = (event.load / event.total) * 100;
_("progressBar").value = Math.round(percent);
_("status").innerHTML = Math.round(percent)+"% uploaded.. please wait";
}
function completeHandler(event) {
_("status").innerHTML = event.target.responseText;
_("progressBar").value = 0;
}
function errorHandler(event) {
_("status").innerHTML = "Upload Failed";
}
function abortHandler(event) {
_("status").innerHTML = "Upload Aborted";
}
</script>
</head>
<body>
<title>Afterburner firmware update</title>
<h1>Afterburner firmware update</h1>
<form method='POST' action='/updatenow' enctype='multipart/form-data'><input type='file' name='update'><BR><BR><input type='submit' value='Update'> <input type='button' onclick=window.location.assign('/') value='Cancel'></form>
<form method='POST' action='/updatenow' enctype='multipart/form-data'>
<input type='file' name='update'>
<BR>
<BR>
<input type='button' value='Update' onclick="uploadFile()"> <input type='button' onclick=window.location.assign('/') value='Cancel'>
<progress id="progressBar" value="0" max="100" style="width:300px;"></progress>
<h3 id="status"></h3>
<p id="loaded_n_total"</p>
</form>
</body>
</html>
)=====";
const char* rootIndex = R"=====(
@ -191,8 +232,6 @@ void initWebServer(void) {
server.on("/tst", HTTP_GET, []() {
rootRedirect();
// server.sendHeader("Connection", "close");
// server.send(200, "text/html", rootIndex);
});
// magical code shamelessly lifted from Arduino WebUpdate example, modified
// this allows pushing new firmware to the ESP from a WEB BROWSER!
@ -206,23 +245,19 @@ void initWebServer(void) {
}
bUpdateAccessed = true;
server.sendHeader("Connection", "close");
server.send(200, "text/html", serverIndex);
// server.send(200, "text/html", serverIndex); // transition to file upload page
server.sendHeader("Cache-Control", "no-cache");
handleFileRead("/uploadfirmware.html");
});
server.on("/updatenow", HTTP_GET, []() { // handle attempts to just browse the /updatenow path - force redirect to root
rootRedirect();
// server.sendHeader("Connection", "close");
// server.send(200, "text/html", rootIndex);
});
// actual guts that manages the new firmware upload
server.on("/updatenow", HTTP_POST, []() {
// completion functionality
server.sendHeader("Connection", "close");
server.send(200, "text/plain", (Update.hasError()) ? "FAIL - Afterburner will reboot shortly" : "OK - Afterburner will reboot shortly");
delay(1000);
rootRedirect();
// server.sendHeader("Connection", "close");
// server.send(200, "text/html", rootIndex); // req browser to redirect to root
delay(1000);
ESP.restart(); // reboot
}, []() {
if(bUpdateAccessed) { // only allow progression via /update, attempts to directly access /updatenow will fail
@ -234,13 +269,16 @@ void initWebServer(void) {
Update.printError(DebugPort);
}
} else if (upload.status == UPLOAD_FILE_WRITE) {
#if USE_SW_WATCHDOG == 1
feedWatchdog();
#endif
if(upload.totalSize) {
char JSON[64];
sprintf(JSON, "{\"progress\":%d}", upload.totalSize);
sendWebServerString(JSON);
// DebugPort.print(JSON);
}
DebugPort.print(".");
// server.sendHeader("Connection", "close");
// char web[128];
// int progress = upload.currentSize / upload.totalSize;
// sprintf(web, "<progress id=\"file\" max=\"100\" value=\"%d\" </progress>", progress);
// server.send(200, "text/html", web);
// server.send(200, "text/plain", ".");
if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
Update.printError(DebugPort);
}
@ -260,8 +298,6 @@ void initWebServer(void) {
else {
// attempt to POST without using /update - force redirect to root
rootRedirect();
// server.sendHeader("Connection", "close");
// server.send(200, "text/html", rootIndex); // req browser to redirect to root
}
});

View file

@ -113,7 +113,7 @@ bool initWifi(int initpin,const char *failedssid, const char *failedpassword)
// if you get here you have connected to the WiFi
isSTA = true;
DebugPort.println("WiFiManager connected in STA mode OK");
DebugPort.printf(" STA IP address: %s\r\n", WiFi.localIP().toString().c_str());
DebugPort.printf(" STA IP address: %s\r\n", getWifiSTAAddrStr());
// must use same radio channel as STA to go to STA+AP, otherwise we drop the STA!
chnl = WiFi.channel();
DebugPort.println("Now promoting to STA+AP mode...");
@ -128,7 +128,7 @@ bool initWifi(int initpin,const char *failedssid, const char *failedpassword)
WiFi.softAP(APname, failedpassword, chnl);
WiFi.enableAP(true);
DebugPort.printf(" AP SSID: %s\r\n", WiFi.softAPgetHostname());
DebugPort.printf(" AP IP address: %s\r\n", WiFi.softAPIP().toString().c_str());
DebugPort.printf(" AP IP address: %s\r\n", getWifiAPAddrStr());
DebugPort.printf("WifiMode after initWifi = %d\r\n", WiFi.getMode());
#endif
@ -267,7 +267,7 @@ void APstartedCallback(WiFiManager*)
const char* getWifiAPAddrStr()
{
noInterrupts();
IPAddress IPaddr = WiFi.softAPIP(); // use stepping stone - function returns an automatic stack var - LAME!
static IPAddress IPaddr = WiFi.softAPIP(); // use stepping stone - function returns an automatic stack var - LAME!
interrupts();
return IPaddr.toString().c_str();
}
@ -275,7 +275,7 @@ const char* getWifiAPAddrStr()
const char* getWifiSTAAddrStr()
{
noInterrupts();
IPAddress IPaddr = WiFi.localIP(); // use stepping stone - function returns an automatic stack var - LAME!
static IPAddress IPaddr = WiFi.localIP(); // use stepping stone - function returns an automatic stack var - LAME!
interrupts();
return IPaddr.toString().c_str();
}