Skip to content

Commit debd9a9

Browse files
committed
Add basic update progress page
1 parent 81af58b commit debd9a9

File tree

13 files changed

+382
-139
lines changed

13 files changed

+382
-139
lines changed

data/js/updates.js

Lines changed: 0 additions & 64 deletions
This file was deleted.

src/globals.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
#include <ArduinoJson.h>
88

99
#define PRODUCT_NAME "AirBear"
10-
#define FIRMWARE_VERSION "0.0.6"
10+
#define FIRMWARE_VERSION "0.0.3"
1111

1212
#define FAKE_RPM
1313

src/main.cpp

Lines changed: 74 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ void setup()
2828
initTimers();
2929
initBLE();
3030
initWiFi();
31-
updateFromRemote(); //Check for pending remote firmware updates
3231

3332
delay(1000);
3433
Serial.println("Connection Type: " + String(config.getUChar("connection_type")));
@@ -37,74 +36,88 @@ void setup()
3736
{
3837
initTCP();
3938
}
40-
if( (config.getUChar("connection_type") == CONNECTION_TYPE_WIFI) )
39+
40+
if(updatesPending())
4141
{
42-
//Init file system
43-
if (!SPIFFS.begin(true)) {
44-
Serial.println("An error has occurred while mounting SPIFFS");
45-
}
46-
initSSE();
47-
initSerialData();
48-
49-
//Init the web server
50-
// Web Server Root URL
42+
//When updates are pending, only show the minimum pages
43+
server.on("/updateStatus", HTTP_GET, [](AsyncWebServerRequest *request) {
44+
request->send(200, "text/json", update_progress_json(request));
45+
});
5146
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
52-
request->send(SPIFFS, "/index.html", "text/html");
53-
});
54-
55-
server.on("/data", HTTP_GET, [](AsyncWebServerRequest *request) {
56-
String jsonOutput;
57-
serializeJson(readings_JSON, jsonOutput);
58-
//request->send(200, "text/json", JSON.stringify(readings_JSON));
59-
request->send(200, "text/json", jsonOutput.c_str());
60-
});
61-
62-
server.serveStatic("/", SPIFFS, "/");
63-
47+
request->send(200, "text/html", updateInProgressPage());
48+
});
6449
}
6550
else
6651
{
67-
//If not using the web dash then the root URL will produce the config page
68-
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
69-
request->send(200, "text/html", webConfigRequest(request));
70-
});
71-
}
52+
if( (config.getUChar("connection_type") == CONNECTION_TYPE_WIFI) && (updatesPending() == false) )
53+
{
54+
//Init file system
55+
if (!SPIFFS.begin(true)) {
56+
Serial.println("An error has occurred while mounting SPIFFS");
57+
}
58+
initSSE();
59+
initSerialData();
60+
61+
//Init the web server
62+
// Web Server Root URL
63+
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
64+
request->send(SPIFFS, "/index.html", "text/html");
65+
});
66+
67+
server.on("/data", HTTP_GET, [](AsyncWebServerRequest *request) {
68+
String jsonOutput;
69+
serializeJson(readings_JSON, jsonOutput);
70+
//request->send(200, "text/json", JSON.stringify(readings_JSON));
71+
request->send(200, "text/json", jsonOutput.c_str());
72+
});
73+
74+
server.serveStatic("/", SPIFFS, "/");
7275

73-
server.on(WEB_CONFIG_URL, HTTP_GET, [](AsyncWebServerRequest *request) {
74-
request->send(200, "text/html", webConfigRequest(request));
75-
});
76-
77-
server.on(WEB_CONFIG_URL, HTTP_POST, [](AsyncWebServerRequest *request) {
78-
request->send(200, "text/html", webConfigPOSTRequest(request));
79-
});
80-
81-
//Updates the firmware AND data from remote URLs
82-
server.on(UPDATE_REMOTE_URL, HTTP_POST, [](AsyncWebServerRequest *request) {
83-
request->send(200, "text/html", saveRemoteFW_URLs(request));
84-
ESP.restart();
85-
});
86-
//Scan the wifi networks and return them as JSON
87-
server.on("/wifi", HTTP_GET, [](AsyncWebServerRequest *request) {
88-
request->send(200, "text/json", scanWifi(request));
89-
});
90-
91-
server.on(UPDATE_DATA_UPLOAD_URL, HTTP_POST, [](AsyncWebServerRequest *request) {
76+
}
77+
else
78+
{
79+
//If not using the web dash then the root URL will produce the config page
80+
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
81+
request->send(200, "text/html", webConfigRequest(request));
82+
});
83+
}
84+
85+
server.on(WEB_CONFIG_URL, HTTP_GET, [](AsyncWebServerRequest *request) {
86+
request->send(200, "text/html", webConfigRequest(request));
87+
});
88+
89+
server.on(WEB_CONFIG_URL, HTTP_POST, [](AsyncWebServerRequest *request) {
90+
request->send(200, "text/html", webConfigPOSTRequest(request));
91+
});
92+
93+
//Updates the firmware AND data from remote URLs
94+
server.on(UPDATE_REMOTE_URL, HTTP_POST, [](AsyncWebServerRequest *request) {
95+
request->send(200, "text/html", saveRemoteFW_URLs(request));
96+
delay(1000); //Wait 1 second to allow the page to be sent before restarting
97+
ESP.restart();
98+
});
99+
//Scan the wifi networks and return them as JSON
100+
server.on("/wifi", HTTP_GET, [](AsyncWebServerRequest *request) {
101+
request->send(200, "text/json", scanWifi(request));
102+
});
103+
104+
server.on(UPDATE_DATA_UPLOAD_URL, HTTP_POST, [](AsyncWebServerRequest *request) {
105+
//This runs when the uplaod is completed
106+
partitionUploadComplete(request);
107+
},[](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
108+
//This runs each time a new chunk is received
109+
partitionUploadChunk(request, filename, index, data, len, final, U_SPIFFS);
110+
}
111+
);
112+
server.on(UPDATE_FW_UPLOAD_URL, HTTP_POST, [](AsyncWebServerRequest *request) {
92113
//This runs when the uplaod is completed
93114
partitionUploadComplete(request);
94-
},[](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
115+
},[](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
95116
//This runs each time a new chunk is received
96-
partitionUploadChunk(request, filename, index, data, len, final, U_SPIFFS);
97-
}
98-
);
99-
server.on(UPDATE_FW_UPLOAD_URL, HTTP_POST, [](AsyncWebServerRequest *request) {
100-
//This runs when the uplaod is completed
101-
partitionUploadComplete(request);
102-
},[](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
103-
//This runs each time a new chunk is received
104-
partitionUploadChunk(request, filename, index, data, len, final, U_FLASH);
105-
}
106-
);
107-
117+
partitionUploadChunk(request, filename, index, data, len, final, U_FLASH);
118+
}
119+
);
120+
}
108121

109122

110123
// Start server
@@ -116,6 +129,7 @@ void setup()
116129

117130
server.begin();
118131

132+
updateFromRemote(); //Check for pending remote firmware updates
119133

120134
//By default the ESP32-C3 will output a bunch of diag messages on bootup over UART.
121135
//This messes up the secondary serial on the Speeduino so these bootup messages are disabled.

src/static/static_html.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#include "static_html.h"
2+
3+
4+
String staticHTML_head()
5+
{
6+
return String("<!DOCTYPE html><html><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
7+
}

src/static/static_html.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#ifndef STATIC_HTML_H
2+
#define STATIC_HTML_H
3+
4+
#include "../globals.h"
5+
6+
String staticHTML_head();
7+
8+
#endif

src/static/static_js.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#include "static_js.h"
2+
3+
String staticJS_updates()
4+
{
5+
String js = "";
6+
js += "<script>";
7+
js += "function i(t){return document.getElementById(t)}function r(t,e){return t=t.replace('v',''),e=e.replace('v',''),t.startsWith(e+'-')?-1:e.startsWith(t+'-')?1:t.localeCompare(e,void 0,{numeric:!0,sensitivity:'case',caseFirst:'upper'})}function o(t){return t.browser_download_url.split('/').pop()}async function getLatestGithubRelease(t){var e,a=await(await fetch('https://api.github.com/repos/speeduino/AirBear/releases/latest')).json(),n=a.tag_name;if(1==r(i('latest_release_txt').innerHTML=n,t)){i('update_btn').disabled=!1;for(const s of a.assets)s.name.includes('littlefs')?(e='http://speeduino.com/fw/AirBear/'+n+'/'+o(s),i('newData_url').value=e):(e='http://speeduino.com/fw/AirBear/'+n+'/'+o(s),i('newFW_url').value=e)}}async function scanWifi(){var t=i('ssid');for(const a of(await(await fetch('/wifi')).json()).networks){var e=document.createElement('option');e.value=a.ssid,e.text=a.ssid,t.add(e)}}function toggleData(){var t=i('newData_url');t.disabled=!t.disabled}function a(t,e){t.innerHTML=e}function updateProgress(){setTimeout(async()=>{let t;try{var e=await fetch('/updateStatus');t=await e.json()}catch(t){updateProgress()}t&&(a(i('updateStatus'),t.updateStatus),a(i('updateComplete'),t.updateProgress),a(i('updateSize'),t.updateSize),e=Math.floor(t.updateProgress/t.updateSize*100),a(i('updatePercent'),e),98<=e?window.location.href='/':updateProgress())},1500)}";
8+
js += "</script>";
9+
10+
return js;
11+
}

src/static/static_js.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#ifndef STATIC_JS_H
2+
#define STATIC_JS_H
3+
4+
#include "../globals.h"
5+
6+
String staticJS_updates();
7+
8+
#endif

src/static/static_js.js

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
//This file is uglified using UglifyJS 3 and the config file within this directory.
2+
//The output of this is placed into static_js.cpp
3+
//This can be generaetd with the cmd: uglifyjs --config-file uglifyjs.config.json static_js.js
4+
5+
function getElementByID(id)
6+
{
7+
return document.getElementById(id)
8+
}
9+
10+
11+
function semverCompare(a, b)
12+
{
13+
a = a.replace('v','')
14+
b = b.replace('v','')
15+
if (a.startsWith(b + '-')) return -1
16+
if (b.startsWith(a + '-')) return 1
17+
return a.localeCompare(b, undefined, { numeric: true, sensitivity: 'case', caseFirst: 'upper' })
18+
}
19+
20+
function getfileName(asset)
21+
{
22+
return asset.browser_download_url.split('/').pop()
23+
}
24+
25+
async function getLatestGithubRelease(currentVersion)
26+
{
27+
const jsonData = await (await fetch('https://api.github.com/repos/speeduino/AirBear/releases/latest')).json();
28+
const latestVersion = jsonData.tag_name;
29+
getElementByID('latest_release_txt').innerHTML = latestVersion
30+
if(semverCompare(latestVersion, currentVersion) == 1) //Value of 1 means a > b
31+
{
32+
getElementByID('update_btn').disabled = false
33+
34+
for(const asset of jsonData.assets)
35+
{
36+
if(asset.name.includes('littlefs'))
37+
{
38+
const newData_url = 'http://speeduino.com/fw/AirBear/' + latestVersion + '/' + getfileName(asset)
39+
getElementByID('newData_url').value = newData_url
40+
console.log("Data file: " + newData_url)
41+
}
42+
else
43+
{
44+
const newFW_url = 'http://speeduino.com/fw/AirBear/' + latestVersion + '/' + getfileName(asset)
45+
getElementByID('newFW_url').value = newFW_url
46+
console.log("FW file: " + newFW_url)
47+
}
48+
}
49+
}
50+
}
51+
52+
async function scanWifi()
53+
{
54+
const s = getElementByID('ssid')
55+
const jsonData = await (await fetch('/wifi')).json()
56+
for(const network of jsonData.networks)
57+
{
58+
const opt = document.createElement('option');
59+
opt.value = network.ssid;
60+
opt.text = network.ssid;
61+
s.add(opt)
62+
}
63+
}
64+
65+
function toggleData()
66+
{
67+
const dataField = getElementByID('newData_url')
68+
dataField.disabled = !dataField.disabled
69+
}
70+
71+
function setInnerHTML(id, val)
72+
{
73+
id.innerHTML = val
74+
}
75+
76+
function updateProgress()
77+
{
78+
setTimeout(async () => {
79+
let jsonData
80+
try {
81+
const response = await fetch('/updateStatus')
82+
jsonData = await response.json();
83+
}
84+
catch (error)
85+
{
86+
console.log(error)
87+
updateProgress()
88+
}
89+
90+
if(jsonData)
91+
{
92+
setInnerHTML(getElementByID('updateStatus'), jsonData.updateStatus)
93+
setInnerHTML(getElementByID('updateComplete'), jsonData.updateProgress)
94+
setInnerHTML(getElementByID('updateSize'), jsonData.updateSize)
95+
const percentComplete = Math.floor((jsonData.updateProgress / jsonData.updateSize) * 100)
96+
setInnerHTML(getElementByID('updatePercent'), percentComplete)
97+
console.log(percentComplete)
98+
if(percentComplete >= 98) { window.location.href = '/' }
99+
else { updateProgress() }
100+
}
101+
}, 1500);
102+
}

0 commit comments

Comments
 (0)