I need a way to ensure that the css/images will never fail to load if possible. I can't just have the page randomly loading without the styles, that's ridiculous.
Another thing that's been happening is the first load of the page will occur pretty quickly, but then when I refresh, it takes forever for the page to load again.
Here's a list of the files I currently have on the server:
Here's the current version of my code:
Relevant Code from Primary ino File
// WEB HANDLER IMPLEMENTATION (Modified Server Library Code)
class SPIFFSEditor: public AsyncWebHandler {
private:
String _username;
String _password;
bool _uploadAuthenticated;
public:
SPIFFSEditor(String username=String(), String password=String()):_username(username),_password(password),_uploadAuthenticated(false){}
bool canHandle(AsyncWebServerRequest *request){
if(request->method() == HTTP_GET && request->url() == "/edit" && (SPIFFS.exists("/edit.htm") || SPIFFS.exists("/edit.htm.gz")))
return true;
else if(request->method() == HTTP_GET && request->url() == "/list")
return true;
else if(request->method() == HTTP_GET && (request->url().endsWith("/") || SPIFFS.exists(request->url()) || (!request->hasParam("download") && SPIFFS.exists(request->url()+".gz"))))
return true;
else if(request->method() == HTTP_POST && request->url() == "/edit")
return true;
else if(request->method() == HTTP_DELETE && request->url() == "/edit")
return true;
else if(request->method() == HTTP_PUT && request->url() == "/edit")
return true;
return false;
}
//Function used to serve webpage to user (Modified Server Library Code)
void handleRequest(AsyncWebServerRequest *request){
if(_username.length() && (request->method() != HTTP_GET || request->url() == "/edit" || request->url() == "/list") && !request->authenticate(_username.c_str(),_password.c_str()))
return request->requestAuthentication();
if(request->method() == HTTP_GET && request->url() == "/edit"){
request->send(SPIFFS, "/edit.htm");
} else if(request->method() == HTTP_GET && request->url() == "/list"){
if(request->hasParam("dir")){
String path = request->getParam("dir")->value();
Dir dir = SPIFFS.openDir(path);
path = String();
String output = "[";
while(dir.next()){
File entry = dir.openFile("r");
if (output != "[") output += ',';
bool isDir = false;
output += "{\"type\":\"";
output += (isDir)?"dir":"file";
output += "\",\"name\":\"";
output += String(entry.name()).substring(1);
output += "\"}";
entry.close();
}
output += "]";
request->send(200, "text/json", output);
output = String();
}
else
request->send(400);
} else if(request->method() == HTTP_GET){
String path = request->url();
if(path.endsWith("/"))
path += "index.htm";
request->send(SPIFFS, path, String(), request->hasParam("download"));
} else if(request->method() == HTTP_DELETE){
if(request->hasParam("path", true)){
ESP.wdtDisable(); SPIFFS.remove(request->getParam("path", true)->value()); ESP.wdtEnable(10);
request->send(200, "", "DELETE: "+request->getParam("path", true)->value());
} else
request->send(404);
} else if(request->method() == HTTP_POST){
if(request->hasParam("data", true, true) && SPIFFS.exists(request->getParam("data", true, true)->value()))
request->send(200, "", "UPLOADED: "+request->getParam("data", true, true)->value());
else
request->send(500);
} else if(request->method() == HTTP_PUT){
if(request->hasParam("path", true)){
String filename = request->getParam("path", true)->value();
if(SPIFFS.exists(filename)){
request->send(200);
} else {
File f = SPIFFS.open(filename, "w");
if(f){
f.write(0x00);
f.close();
request->send(200, "", "CREATE: "+filename);
} else {
request->send(500);
}
}
} else
request->send(400);
}
}
//Function used to handle uploading web files (Modified Server Library Code)
void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){
if(!index){
if(!_username.length() || request->authenticate(_username.c_str(),_password.c_str()))
_uploadAuthenticated = true;
request->_tempFile = SPIFFS.open(filename, "w");
}
if(_uploadAuthenticated && request->_tempFile && len){
ESP.wdtDisable(); request->_tempFile.write(data,len); ESP.wdtEnable(10);
}
if(_uploadAuthenticated && final)
if(request->_tempFile) request->_tempFile.close();
}
};
void setup(){
IPAddress ip(192, 168, 1, 16);
IPAddress subnet(255, 255, 255, 0);
IPAddress gt(192, 168, 1, 1);
//Initialize Serial Connections
initSerial();
//Initialize File System
SPIFFS.begin();
//Initialize WiFi Connection (Modified Server Library Code)
WiFi.mode(WIFI_STA);
//WiFi.config(ip, gt, subnet);
WiFi.begin(ssid, password);
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
Serial.printf("STA: Failed!\n");
WiFi.disconnect(false);
//Seems to work without this, not sure if necessary
//delay(1000);
WiFi.begin(ssid, password);
}
ArduinoOTA.begin();
//Serial.printf("format start\n"); SPIFFS.format(); Serial.printf("format end\n");
//NEW
ws.onEvent(onEvent);
server.addHandler(&ws);
server.serveStatic("/fs", SPIFFS, "/");
server.on("/heap", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, "text/plain", String(ESP.getFreeHeap()));
});
server.addHandler(new SPIFFSEditor(http_username,http_password));
server.onNotFound([](AsyncWebServerRequest *request){
if(mySerial)
{
mySerial.printf("NOT_FOUND: ");
} //end if
if(request->method() == HTTP_GET)
{
if(mySerial)
{
mySerial.printf("GET");
} //end if
} //end if
else if(request->method() == HTTP_POST)
{
if(mySerial)
{
mySerial.printf("POST");
} //end if
} //end else if
else if(request->method() == HTTP_DELETE)
{
if(mySerial)
{
mySerial.printf("DELETE");
} //end if
} //end else if
else if(request->method() == HTTP_PUT)
{
if(mySerial)
{
mySerial.printf("PUT");
} //end if
} //end else if
else if(request->method() == HTTP_PATCH)
{
if(mySerial)
{
mySerial.printf("PATCH");
} //end if
} //end else if
else if(request->method() == HTTP_HEAD)
{
if(mySerial)
{
mySerial.printf("HEAD");
} //end if
} //end else if
else if(request->method() == HTTP_OPTIONS)
{
if(mySerial)
{
mySerial.printf("OPTIONS");
} //end if
} //end else if
else
{
if(mySerial)
{
mySerial.printf("UNKNOWN");
} //end if
} //end else
//Continued Server Initialization (Modified Server Library Code)
if(mySerial)
{
mySerial.printf(" http://%s%s\n", request->host().c_str(), request->url().c_str());
} //end if
if(request->contentLength()){
if(mySerial)
{
mySerial.printf("_CONTENT_TYPE: %s\n", request->contentType().c_str());
mySerial.printf("_CONTENT_LENGTH: %u\n", request->contentLength());
} //end if
} //end if
int headers = request->headers();
int i;
for(i=0;i<headers;i++){
AsyncWebHeader* h = request->getHeader(i);
if(mySerial)
{
mySerial.printf("_HEADER[%s]: %s\n", h->name().c_str(), h->value().c_str());
} //end if
}
int params = request->params();
for(i=0;i<params;i++){
AsyncWebParameter* p = request->getParam(i);
if(p->isFile()){
if(mySerial)
{
mySerial.printf("_FILE[%s]: %s, size: %u\n", p->name().c_str(), p->value().c_str(), p->size());
} //end if
} else if(p->isPost()){
if(mySerial)
{
mySerial.printf("_POST[%s]: %s\n", p->name().c_str(), p->value().c_str());
} //end if
} else {
if(mySerial)
{
mySerial.printf("_GET[%s]: %s\n", p->name().c_str(), p->value().c_str());
} //end if
}
}
request->send(404);
});
server.onFileUpload([](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final){
if(!index)
{
if(mySerial)
{
mySerial.printf("UploadStart: %s\n", filename.c_str());
} //end if
} //end if
if(mySerial)
{
mySerial.printf("%s", (const char*)data);
} //end if
if(final)
{
if(mySerial)
{
mySerial.printf("UploadEnd: %s (%u)\n", filename.c_str(), index+len);
} //end if
} //end if
});
server.onRequestBody([](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total){
if(!index)
{
if(mySerial)
{
mySerial.printf("BodyStart: %u\n", total);
} //end if
} //end if
if(mySerial)
{
mySerial.printf("%s", (const char*)data);
} //end if
if(index + len == total)
{
if(mySerial)
{
mySerial.printf("BodyEnd: %u\n", total);
} //end if
} //end if
});
server.begin();
//Setup a timer for all WiFi status updates
updateTimerId = timer.setInterval(5000, sendInfoWifi);
}
void loop(){
if(wifiInit == 0)
{
initConnWifi();
} //end if
//Updates whatever timers have been defined
timer.run();
recInfoWifi();
ArduinoOTA.handle();
}
Relevant code from Header ino File
#include <SoftwareSerial.h>
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <ArduinoOTA.h>
#include <FS.h>
#include <Hash.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <WebSocketsServer.h>
// SKETCH BEGIN
AsyncWebServer server(80);
//NEW
AsyncWebSocket ws("/ws");
//Websocket
WebSocketsServer webSocket = WebSocketsServer(81);
index.html
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width initial-scale=1.0">
<title>FITS</title>
<!--CSS-->
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body style='background-color:#4a4a4c;' onload="drawGauges(); startSocket();"> <!--start()-->
<div class="centerPiece">
<h1 class="pageHdr">Fluid Infusion Training System</h1>
<div class="adminLoginButtonDiv">
<a id="adminLoginButton" class="styled-button-11 elementWrapper" onclick="admin_login_init()">Admin Login</a>
<a id="adminLoginCancel" class="styled-button-11RED deactiveSect elementWrapper" onclick="admin_login_cancel()">Cancel Login</a>
<a id="adminLogout" class="styled-button-11RED deactiveSect elementWrapper" onclick="admin_logout()">Admin Logout</a>
</div>
<div id="adminLoginCreds" class="adminLoginCreds deactiveSect">
<div class="elementWrapper">
<label id="adminLoginUserCredLabel" for="adminLoginNameCred">User: </label>
<input type="text" id="adminLoginUserCred">
</div>
<div class="elementWrapper">
<label id="adminLoginPassCredLabel" for="adminLoginPassCred">Password: </label>
<input type="password" id="adminLoginPassCred">
</div>
<div class="elementWrapper">
<a id="adminLoginSubmit" class="submit" onclick="admin_login_submit()">Submit</a>
</div>
</div>
<div style="clear:both;"></div>
<div class='counterContainer'>
<span id="counterText" style="color:white;"></span>
</div>
<div id='adminControls' class='adminControlsRow deactiveSect'>
<h2>Administrator Controls</h2>
<div id='Newbtn' class='powerSwitchContainer'>
<h2>HHIO PTT Power</h2>
<!--HHIO PTT Power Switch-->
<span class="switch">
<span class="switch-border1">
<span class="switch-border2">
<input id="switch1" type="checkbox" onclick='POWERswitch()' checked />
<label for="switch1"></label>
<span class="switch-top"></span>
<span class="switch-shadow"></span>
<span class="switch-handle"></span>
<span class="switch-handle-left"></span>
<span class="switch-handle-right"></span>
<span class="switch-handle-top"></span>
<span class="switch-handle-bottom"></span>
<span class="switch-handle-base"></span>
<span class="switch-led switch-led-green">
<span class="switch-led-border">
<span class="switch-led-light">
<span class="switch-led-glow"></span>
</span>
</span>
</span>
<span class="switch-led switch-led-red">
<span class="switch-led-border">
<span class="switch-led-light">
<span class="switch-led-glow"></span>
</span>
</span>
</span>
</span>
</span>
</span>
</div>
<!--Flushing Button-->
<div class='flushBtnContainer'>
<h2>Fluid Reservoir</h2>
<button id='flushBtn' onmousedown="FLUSHbegin();" onmouseup="FLUSHend();">Flush</button>
</div>
</div>
<div id="mainControls" class="mainControlsRow deactiveSect">
<h2 class="mainHdr">Maintenance Controls</h2>
</div>
<div class="battFluid">
<!--COLUMN HEADERS-->
<div class='headerRow'>
<div class="topHeader">
<h3 class="headerStyle under">Battery</h3>
</div>
<div class="topHeader">
<h3 class="headerStyle under">Marrow</h3>
</div>
</div>
<div id="battery0" class="battery">
<div id="battery100" class="juice"></div>
</div>
<svg width="50px" height="50px" viewBox="0 0 100 150" class="battChgContainer">
<polygon id="battChgContainer" stroke="gray" fill="grey" points="100,0 67,50 90,45 47,100 70,95 0,150 27,110 12,113 50,70 30,73 100,0" />
</svg>
<svg id="battLowContainer" width="120px" height="120px" viewBox="0 0 100 190" class="battLowContainer deactiveSect">
<polygon stroke="gray" fill="#1A1A1C" points="85,151 85,110 100,110 105,105 110,100 115,95 115,50 100,50 100,20 85,20 85,50 65,50 65,20 50,20 50,50 35,50 35,95 40,100 45,105 50,110 65,110 65,151"></polygon>
</svg>
<h1 id="hdr100" class="hdr100">100%</h1>
<!--FLUID GAUGE-->
<table class="fluidGauge">
<tr>
<td>
<div style="width: 140px; height: 225px;" class="fluidReservoir blood">
<div class="tube">
</div>
<div class="liquid">
<div class="head">
</div>
<div id="fluidReservoirHeight" class="body">
<div class="drop large">
</div>
<div class="drop medium">
</div>
<div class="drop small">
</div>
</div>
<div class="tail">
</div>
</div>
<div class="glitter">
</div>
</div>
</td>
</tr>
</table>
</div>
<!--COLUMN HEADERS-->
<div class='headerRow'>
<div class="header">
<h3 class="headerStyle under">Left</h3>
</div>
<div class="header">
<h3 class="headerStyle under">Sternum</h3>
</div>
<div class="header">
<h3 class="headerStyle under">Right</h3>
</div>
</div>
<!--MICROSWITCHES-->
<div class='microswitchRow'>
<div class="microswitchContainer">
<div id="microC" class="microswitch white">
<input type="radio" name="switchC" class="microswitchRadio" id="switchOffC" checked>
<input type="radio" name="switchC" class="microswitchRadio" id="switchOnC">
<label id="switchOffLabelC" for="switchOffC" class="headerStyle">Detached</label>
<label id="switchOnLabelC" for="switchOnC" class="headerStyle deactiveSect">Attached</label>
<span class="toggle"></span>
</div>
</div>
<div class="microswitchContainer">
<div id="microD" class="microswitch white">
<input type="radio" name="switchD" class="microswitchRadio" id="switchOffD" checked>
<input type="radio" name="switchD" class="microswitchRadio" id="switchOnD">
<label id="switchOffLabelD" for="switchOffD" class="headerStyle">Detached</label>
<label id="switchOnLabelD" for="switchOnD" class="headerStyle deactiveSect">Attached</label>
<span class="toggle"></span>
</div>
</div>
<div class="microswitchContainer">
<div id="microE" class="microswitch white">
<input type="radio" name="switchE" class="microswitchRadio" id="switchOffE" checked>
<input type="radio" name="switchE" class="microswitchRadio" id="switchOnE">
<label id="switchOffLabelE" for="switchOffE" class="headerStyle">Detached</label>
<label id="switchOnLabelE" for="switchOnE" class="headerStyle deactiveSect">Attached</label>
<span class="toggle"></span>
</div>
</div>
</div>
<!--VALVES-->
<div class='valveRow'>
<div class='valveContainer'>
<img id="openValveC" src="openvalve.png" alt="open valve C" class="openValvePic">
<img id="closedValveC" src="closedvalve.png" alt="closed valve C" class="deactiveSect closedValvePic">
</div>
<div class='valveContainer'>
<img id="openValveD" src="openvalve.png" alt="open valve D" class="openValvePic deactiveSect">
<img id="closedValveD" src="closedvalve.png" alt="closed valve D" class="closedValvePic">
</div>
<div class='valveContainer'>
<img id="openValveE" src="openvalve.png" alt="open valve E" class="openValvePic">
<img id="closedValveE" src="closedvalve.png" alt="closed valve E" class="deactiveSect closedValvePic">
</div>
</div>
<!--PRESSURE GAUGES-->
<div id="pGauges" class="pressureRow">
<div class="pressureContainer pContainC">
<div id="pGaugeC">
<canvas id="pressureGaugeC" class="gauge"></canvas>
</div>
<div class="pGaugeText">
<div class="bubble">
<div class="num"></div>
<div id="pgcValue" class="rotating">
0.00
</div>
</div>
<span class="psi">PSI</span>
</div>
</div>
<div class="pressureContainer pContainD">
<div id="pGaugeD">
<canvas id="pressureGaugeD" class="gauge"></canvas>
</div>
<div class="pGaugeText">
<div class="bubble">
<div class="num"></div>
<div id="pgdValue" class="rotating">
0.00
</div>
</div>
<span class="psi">PSI</span>
</div>
</div>
<div class="pressureContainer pContainE">
<div id="pGaugeE">
<canvas id="pressureGaugeE" class="gauge"></canvas>
</div>
<div class="pGaugeText">
<div class="bubble">
<div class="num"></div>
<div id="pgeValue" class="rotating">
0.00
</div>
</div>
<span class="psi">PSI</span>
</div>
</div>
</div>
</div>
<!--LED BUTTON-->
<div style='background-color:#1E1E20; clear:both;'>
<h3 class="headerStyle under">LED Controls</h3>
<div id='LEDbtn' class='onoffswitch'>
<input type='checkbox' name='onoffswitch' class='onoffswitch-checkbox' id='myonoffswitch' onclick='LEDswitch()'>
<label class='onoffswitch-label' for='myonoffswitch'>
<span class='onoffswitch-inner'></span>
<span class='onoffswitch-switch'></span>
</label>
</div>
</div>
<script src="general.js"></script>
</body>
</html>