Skip to content

Commit 0da61f5

Browse files
committed
Adding example-02
- web page authentication - single webpage - file listing - file download button - file delete button - file upload button with upload progress status - reboot esp button - all done on a single web page that uses javascript to call /listfiles and /file?name=something&action=delete or /file?name=something&action=download
1 parent 3ece440 commit 0da61f5

File tree

4 files changed

+498
-0
lines changed

4 files changed

+498
-0
lines changed

example-02/README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# example-02
2+
3+
- web page authentication
4+
- single webpage
5+
- file listing
6+
- file download button
7+
- file delete button
8+
- file upload button with upload progress status
9+
- reboot esp button
10+
- all done on a single web page that uses javascript to call /listfiles and /file?name=something&action=delete or /file?name=something&action=download
11+
12+
## Configuration
13+
Edit these variables at the beginning of example-02.ino
14+
15+
```
16+
const String default_ssid = "somessid";
17+
const String default_wifipassword = "mypassword";
18+
const String default_httpuser = "admin";
19+
const String default_httppassword = "admin";
20+
const int default_webserverporthttp = 80;
21+
```

example-02/example-02.ino

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
#include <WiFi.h>
2+
#include <AsyncTCP.h>
3+
#include <ESPAsyncWebServer.h>
4+
#include <SPIFFS.h>
5+
#include "webpages.h"
6+
7+
#define FIRMWARE_VERSION "v0.0.1"
8+
9+
10+
const String default_ssid = "somessid";
11+
const String default_wifipassword = "mypassword";
12+
const String default_httpuser = "admin";
13+
const String default_httppassword = "admin";
14+
const int default_webserverporthttp = 80;
15+
16+
// configuration structure
17+
struct Config {
18+
String ssid; // wifi ssid
19+
String wifipassword; // wifi password
20+
String httpuser; // username to access web admin
21+
String httppassword; // password to access web admin
22+
int webserverporthttp; // http port number for web admin
23+
};
24+
25+
// variables
26+
Config config; // configuration
27+
bool shouldReboot = false; // schedule a reboot
28+
AsyncWebServer *server; // initialise webserver
29+
30+
// function defaults
31+
String listFiles(bool ishtml = false);
32+
33+
void setup() {
34+
Serial.begin(115200);
35+
36+
Serial.print("Firmware: "); Serial.println(FIRMWARE_VERSION);
37+
38+
Serial.println("Booting ...");
39+
40+
Serial.println("Mounting SPIFFS ...");
41+
if (!SPIFFS.begin(true)) {
42+
// if you have not used SPIFFS before on a ESP32, it will show this error.
43+
// after a reboot SPIFFS will be configured and will happily work.
44+
Serial.println("ERROR: Cannot mount SPIFFS, Rebooting");
45+
rebootESP("ERROR: Cannot mount SPIFFS, Rebooting");
46+
}
47+
48+
Serial.print("SPIFFS Free: "); Serial.println(humanReadableSize((SPIFFS.totalBytes() - SPIFFS.usedBytes())));
49+
Serial.print("SPIFFS Used: "); Serial.println(humanReadableSize(SPIFFS.usedBytes()));
50+
Serial.print("SPIFFS Total: "); Serial.println(humanReadableSize(SPIFFS.totalBytes()));
51+
52+
Serial.println(listFiles());
53+
54+
Serial.println("Loading Configuration ...");
55+
56+
config.ssid = default_ssid;
57+
config.wifipassword = default_wifipassword;
58+
config.httpuser = default_httpuser;
59+
config.httppassword = default_httppassword;
60+
config.webserverporthttp = default_webserverporthttp;
61+
62+
63+
Serial.print("\nConnecting to Wifi: ");
64+
WiFi.begin(config.ssid.c_str(), config.wifipassword.c_str());
65+
while (WiFi.status() != WL_CONNECTED) {
66+
delay(500);
67+
Serial.print(".");
68+
}
69+
70+
Serial.println("\n\nNetwork Configuration:");
71+
Serial.println("----------------------");
72+
Serial.print(" SSID: "); Serial.println(WiFi.SSID());
73+
Serial.print(" Wifi Status: "); Serial.println(WiFi.status());
74+
Serial.print("Wifi Strength: "); Serial.print(WiFi.RSSI()); Serial.println(" dBm");
75+
Serial.print(" MAC: "); Serial.println(WiFi.macAddress());
76+
Serial.print(" IP: "); Serial.println(WiFi.localIP());
77+
Serial.print(" Subnet: "); Serial.println(WiFi.subnetMask());
78+
Serial.print(" Gateway: "); Serial.println(WiFi.gatewayIP());
79+
Serial.print(" DNS 1: "); Serial.println(WiFi.dnsIP(0));
80+
Serial.print(" DNS 2: "); Serial.println(WiFi.dnsIP(1));
81+
Serial.print(" DNS 3: "); Serial.println(WiFi.dnsIP(2));
82+
Serial.println();
83+
84+
// configure web server
85+
Serial.println("Configuring Webserver ...");
86+
server = new AsyncWebServer(config.webserverporthttp);
87+
configureWebServer();
88+
89+
Serial.println("Starting Webserver ...");
90+
// startup web server
91+
server->begin();
92+
}
93+
94+
void loop() {
95+
// reboot if we've told it to reboot
96+
if (shouldReboot) {
97+
rebootESP("Web Admin Initiated Reboot");
98+
}
99+
}
100+
101+
102+
void rebootESP(String message) {
103+
Serial.print("Rebooting ESP32: "); Serial.println(message);
104+
ESP.restart();
105+
}
106+
107+
// list all of the files, if ishtml=true, return html rather than simple text
108+
String listFiles(bool ishtml) {
109+
String returnText = "";
110+
Serial.println("Listing files stored on SPIFFS");
111+
File root = SPIFFS.open("/");
112+
File foundfile = root.openNextFile();
113+
if (ishtml) {
114+
returnText += "<table><tr><th align='left'>Name</th><th align='left'>Size</th><th></th><th></th></tr>";
115+
}
116+
while (foundfile) {
117+
if (ishtml) {
118+
returnText += "<tr align='left'><td>" + String(foundfile.name()) + "</td><td>" + humanReadableSize(foundfile.size()) + "</td>";
119+
//"<td><a href='/file?name=" + String(foundfile.name()) + "&action=download'>Download</a></td><td><a href='/file?name=" + String(foundfile.name()) + "&action=delete'>Delete</a></td>";
120+
returnText += "<td><button onclick=\"downloadDeleteButton(\'" + String(foundfile.name()) + "\', \'download\')\">Download</button>";
121+
returnText += "<td><button onclick=\"downloadDeleteButton(\'" + String(foundfile.name()) + "\', \'delete\')\">Delete</button></tr>";
122+
} else {
123+
returnText += "File: " + String(foundfile.name()) + " Size: " + humanReadableSize(foundfile.size()) + "\n";
124+
}
125+
foundfile = root.openNextFile();
126+
}
127+
if (ishtml) {
128+
returnText += "</table>";
129+
}
130+
root.close();
131+
foundfile.close();
132+
return returnText;
133+
}
134+
135+
// Make size of files human readable
136+
// source: https://github.com/CelliesProjects/minimalUploadAuthESP32
137+
String humanReadableSize(const size_t bytes) {
138+
if (bytes < 1024) return String(bytes) + " B";
139+
else if (bytes < (1024 * 1024)) return String(bytes / 1024.0) + " KB";
140+
else if (bytes < (1024 * 1024 * 1024)) return String(bytes / 1024.0 / 1024.0) + " MB";
141+
else return String(bytes / 1024.0 / 1024.0 / 1024.0) + " GB";
142+
}

example-02/webpages.h

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
const char index_html[] PROGMEM = R"rawliteral(
2+
<!DOCTYPE HTML>
3+
<html lang="en">
4+
<head>
5+
<meta name="viewport" content="width=device-width, initial-scale=1">
6+
<meta charset="UTF-8">
7+
</head>
8+
<body>
9+
<p>Main page</p>
10+
<p>Firmware: %FIRMWARE%</p>
11+
<p>Free Storage: <span id="freespiffs">%FREESPIFFS%</span> | Used Storage: <span id="usedspiffs">%USEDSPIFFS%</span> | Total Storage: <span id="totalspiffs">%TOTALSPIFFS%</span></p>
12+
<p>
13+
<button onclick="logoutButton()">Logout</button>
14+
<button onclick="rebootButton()">Reboot</button>
15+
<button onclick="listFilesButton()">List Files</button>
16+
<button onclick="showUploadButtonFancy()">Upload File</button>
17+
</p>
18+
<p id="status"></p>
19+
<p id="detailsheader"></p>
20+
<p id="details"></p>
21+
<script>
22+
function logoutButton() {
23+
var xhr = new XMLHttpRequest();
24+
xhr.open("GET", "/logout", true);
25+
xhr.send();
26+
setTimeout(function(){ window.open("/logged-out","_self"); }, 1000);
27+
}
28+
function rebootButton() {
29+
document.getElementById("statusdetails").innerHTML = "Invoking Reboot ...";
30+
var xhr = new XMLHttpRequest();
31+
xhr.open("GET", "/reboot", true);
32+
xhr.send();
33+
window.open("/reboot","_self");
34+
}
35+
function listFilesButton() {
36+
xmlhttp=new XMLHttpRequest();
37+
xmlhttp.open("GET", "/listfiles", false);
38+
xmlhttp.send();
39+
document.getElementById("detailsheader").innerHTML = "<h3>Files<h3>";
40+
document.getElementById("details").innerHTML = xmlhttp.responseText;
41+
}
42+
function downloadDeleteButton(filename, action) {
43+
var urltocall = "/file?name=" + filename + "&action=" + action;
44+
xmlhttp=new XMLHttpRequest();
45+
if (action == "delete") {
46+
xmlhttp.open("GET", urltocall, false);
47+
xmlhttp.send();
48+
document.getElementById("status").innerHTML = xmlhttp.responseText;
49+
xmlhttp.open("GET", "/listfiles", false);
50+
xmlhttp.send();
51+
document.getElementById("details").innerHTML = xmlhttp.responseText;
52+
}
53+
if (action == "download") {
54+
document.getElementById("status").innerHTML = "";
55+
window.open(urltocall,"_blank");
56+
}
57+
}
58+
function showUploadButtonFancy() {
59+
document.getElementById("detailsheader").innerHTML = "<h3>Upload File<h3>"
60+
document.getElementById("status").innerHTML = "";
61+
var uploadform = "<form method = \"POST\" action = \"/\" enctype=\"multipart/form-data\"><input type=\"file\" name=\"data\"/><input type=\"submit\" name=\"upload\" value=\"Upload\" title = \"Upload File\"></form>"
62+
document.getElementById("details").innerHTML = uploadform;
63+
var uploadform =
64+
"<form id=\"upload_form\" enctype=\"multipart/form-data\" method=\"post\">" +
65+
"<input type=\"file\" name=\"file1\" id=\"file1\" onchange=\"uploadFile()\"><br>" +
66+
"<progress id=\"progressBar\" value=\"0\" max=\"100\" style=\"width:300px;\"></progress>" +
67+
"<h3 id=\"status\"></h3>" +
68+
"<p id=\"loaded_n_total\"></p>" +
69+
"</form>";
70+
document.getElementById("details").innerHTML = uploadform;
71+
}
72+
function _(el) {
73+
return document.getElementById(el);
74+
}
75+
function uploadFile() {
76+
var file = _("file1").files[0];
77+
// alert(file.name+" | "+file.size+" | "+file.type);
78+
var formdata = new FormData();
79+
formdata.append("file1", file);
80+
var ajax = new XMLHttpRequest();
81+
ajax.upload.addEventListener("progress", progressHandler, false);
82+
ajax.addEventListener("load", completeHandler, false); // doesnt appear to ever get called even upon success
83+
ajax.addEventListener("error", errorHandler, false);
84+
ajax.addEventListener("abort", abortHandler, false);
85+
ajax.open("POST", "/");
86+
ajax.send(formdata);
87+
}
88+
function progressHandler(event) {
89+
//_("loaded_n_total").innerHTML = "Uploaded " + event.loaded + " bytes of " + event.total; // event.total doesnt show accurate total file size
90+
_("loaded_n_total").innerHTML = "Uploaded " + event.loaded + " bytes";
91+
var percent = (event.loaded / event.total) * 100;
92+
_("progressBar").value = Math.round(percent);
93+
_("status").innerHTML = Math.round(percent) + "% uploaded... please wait";
94+
if (percent >= 100) {
95+
_("status").innerHTML = "Please wait, writing file to filesystem";
96+
}
97+
}
98+
function completeHandler(event) {
99+
_("status").innerHTML = "Upload Complete";
100+
_("progressBar").value = 0;
101+
xmlhttp=new XMLHttpRequest();
102+
xmlhttp.open("GET", "/listfiles", false);
103+
xmlhttp.send();
104+
document.getElementById("status").innerHTML = "File Uploaded";
105+
document.getElementById("detailsheader").innerHTML = "<h3>Files<h3>";
106+
document.getElementById("details").innerHTML = xmlhttp.responseText;
107+
}
108+
function errorHandler(event) {
109+
_("status").innerHTML = "Upload Failed";
110+
}
111+
function abortHandler(event) {
112+
_("status").innerHTML = "inUpload Aborted";
113+
}
114+
</script>
115+
</body>
116+
</html>
117+
)rawliteral";
118+
119+
const char logout_html[] PROGMEM = R"rawliteral(
120+
<!DOCTYPE HTML>
121+
<html lang="en">
122+
<head>
123+
<meta name="viewport" content="width=device-width, initial-scale=1">
124+
<meta charset="UTF-8">
125+
</head>
126+
<body>
127+
<p><a href="/">Log Back In</a></p>
128+
</body>
129+
</html>
130+
)rawliteral";
131+
132+
// reboot.html base upon https://gist.github.com/Joel-James/62d98e8cb3a1b6b05102
133+
const char reboot_html[] PROGMEM = R"rawliteral(
134+
<!DOCTYPE HTML>
135+
<html lang="en">
136+
<head>
137+
<meta charset="UTF-8">
138+
</head>
139+
<body>
140+
<h3>
141+
Rebooting, returning to main page in <span id="countdown">30</span> seconds
142+
</h3>
143+
<script type="text/javascript">
144+
var seconds = 20;
145+
function countdown() {
146+
seconds = seconds - 1;
147+
if (seconds < 0) {
148+
window.location = "/";
149+
} else {
150+
document.getElementById("countdown").innerHTML = seconds;
151+
window.setTimeout("countdown()", 1000);
152+
}
153+
}
154+
countdown();
155+
</script>
156+
</body>
157+
</html>
158+
)rawliteral";

0 commit comments

Comments
 (0)