Added support for SPIFFS

Fixed ESP32
This commit is contained in:
cschwinne
2019-03-16 02:09:37 +01:00
parent d4bf1cb23d
commit c8a7537157
11 changed files with 705 additions and 200 deletions
+586 -3
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+18 -10
View File
@@ -24,15 +24,21 @@
//#define WLED_DISABLE_INFRARED //there is no pin left for this on ESP8266-01
//#define WLED_DISABLE_MOBILE_UI
//to toggle usb serial debug (un)comment following line(s)
#define WLED_DISABLE_FILESYSTEM //SPIFFS is not used by any WLED feature yet
//#define WLED_ENABLE_FS_SERVING //Enable sending html file from SPIFFS before serving progmem version
//#define WLED_ENABLE_FS_EDITOR //enable /edit page for editing SPIFFS content. Will also be disabled with OTA lock
//to toggle usb serial debug (un)comment the following line
//#define WLED_DEBUG
//library inclusions
#include <Arduino.h>
#ifdef ARDUINO_ARCH_ESP32
#include <WiFi.h>
#include <ESPmDNS.h>
#include <AsyncTCP.h>
#include "SPIFFS.h"
#else
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
@@ -46,6 +52,7 @@
#ifndef WLED_DISABLE_OTA
#include <ArduinoOTA.h>
#endif
#include <SPIFFSEditor.h>
#include "src/dependencies/time/Time.h"
#include "src/dependencies/time/TimeLib.h"
#include "src/dependencies/timezone/Timezone.h"
@@ -69,6 +76,7 @@
#include "WS2812FX.h"
#include "ir_codes.h"
#if IR_PIN < 0
#ifndef WLED_DISABLE_INFRARED
#define WLED_DISABLE_INFRARED
@@ -89,7 +97,7 @@
//version code in format yymmddb (b = daily build)
#define VERSION 1903131
#define VERSION 1903161
char versionString[] = "0.8.4-dev";
@@ -98,10 +106,6 @@ char apPass[65] = "wled1234";
char otaPass[33] = "wledota";
//spiffs FS only useful for debug (only ESP8266)
//#define USEFS
//Hardware CONFIG (only changeble HERE, not at runtime)
//LED strip pin, button pin and IR pin changeable in NpbWrapper.h!
@@ -438,9 +442,12 @@ WS2812FX strip = WS2812FX();
#endif
//filesystem
#ifdef USEFS
#include <FS.h>;
File fsUploadFile;
#ifndef WLED_DISABLE_FILESYSTEM
#include <FS.h>
#ifdef ARDUINO_ARCH_ESP32
#include "SPIFFS.h"
#endif
#include "SPIFFSEditor.h"
#endif
//gamma 2.4 lookup table used for color correction
@@ -465,6 +472,7 @@ const byte gamma8[] = {
//function prototypes
void serveMessage(AsyncWebServerRequest*,uint16_t,String,String,byte);
//turns all LEDs off and restarts ESP
void reset()
{
@@ -514,7 +522,7 @@ void loop() {
userLoop();
yield();
handleButton();
handleIO();
handleIR();
handleNetworkTime();
if (!onlyAP) handleAlexa();
+5 -5
View File
@@ -577,13 +577,11 @@ void savePreset(byte index)
}
char* loadMacro(byte index)
void loadMacro(byte index, char* m)
{
index-=1;
char m[65];
if (index > 15) return m;
if (index > 15) return;
readStringFromEEPROM(1024+64*index, m, 64);
return m;
}
@@ -592,7 +590,9 @@ void applyMacro(byte index)
index-=1;
if (index > 15) return;
String mc="win&";
mc += loadMacro(index+1);
char m[65];
loadMacro(index+1, m);
mc += m;
mc += "&IN"; //internal, no XML response
if (!notifyMacro) mc += "&NN";
String forbidden = "&M="; //dont apply if called by the macro itself to prevent loop
+21 -13
View File
@@ -3,10 +3,17 @@
*/
//build XML response to HTTP /win API request
char* XML_response(AsyncWebServerRequest *request, bool includeTheme)
char* XML_response(AsyncWebServerRequest *request, bool includeTheme, char* dest = nullptr)
{
char sbuf[1024];
olen = 0; obuf = sbuf;
if (dest == nullptr) //allocate local buffer if none passed
{
char sbuf[1024];
obuf = sbuf;
} else {
obuf = dest;
}
olen = 0;
oappend("<?xml version=\"1.0\" ?><vs><ac>");
oappendi((nightlightActive && nightlightFade) ? briT : bri);
oappend("</ac>");
@@ -98,8 +105,7 @@ char* XML_response(AsyncWebServerRequest *request, bool includeTheme)
oappend("</cf></th>");
}
oappend("</vs>");
if (request != nullptr) request->send(200, "text/xml", sbuf);
return sbuf;
if (request != nullptr) request->send(200, "text/xml", obuf);
}
//append a numeric setting to string buffer
@@ -157,15 +163,15 @@ void sappends(char stype, char* key, char* val)
//get values for settings form in javascript
char* getSettingsJS(byte subPage)
void getSettingsJS(byte subPage, char* dest)
{
//0: menu 1: wifi 2: leds 3: ui 4: sync 5: time 6: sec
DEBUG_PRINT("settings resp");
DEBUG_PRINTLN(subPage);
char sbuf[2048];
olen = 0; obuf = sbuf;
obuf = dest;
olen = 0;
if (subPage <1 || subPage >6) return sbuf;
if (subPage <1 || subPage >6) return;
if (subPage == 1) {
sappends('s',"CS",clientSSID);
@@ -328,7 +334,9 @@ char* getSettingsJS(byte subPage)
sappend('c',"CF",!useAMPM);
sappend('i',"TZ",currentTimezone);
sappend('v',"UO",utcOffsetSecs);
sappends('m',"(\"times\")[0]",getTimeString());
char tm[32];
getTimeString(tm);
sappends('m',"(\"times\")[0]",tm);
sappend('i',"OL",overlayCurrent);
sappend('v',"O1",overlayMin);
sappend('v',"O2",overlayMax);
@@ -344,12 +352,13 @@ char* getSettingsJS(byte subPage)
sappend('v',"CH",countdownHour);
sappend('v',"CM",countdownMin);
sappend('v',"CS",countdownSec);
char k[4]; k[0]= 'M';
for (int i=1;i<17;i++)
{
char m[65];
loadMacro(i, m);
sprintf(k+1,"%i",i);
sappends('s',k,loadMacro(i));
sappends('s',k,m);
}
sappend('v',"MB",macroBoot);
@@ -386,7 +395,6 @@ char* getSettingsJS(byte subPage)
oappend(") OK\";");
}
oappend("}</script>");
return sbuf;
}
+22 -108
View File
@@ -37,129 +37,43 @@ void handleSerial()
}
}
#ifdef USEFS
String formatBytes(size_t bytes){
if (bytes < 1024){
return String(bytes)+"B";
} else if(bytes < (1024 * 1024)){
return String(bytes/1024.0)+"KB";
} else if(bytes < (1024 * 1024 * 1024)){
return String(bytes/1024.0/1024.0)+"MB";
} else {
return String(bytes/1024.0/1024.0/1024.0)+"GB";
}
}
String getContentType(String filename){
if(server->hasArg("download")) return "application/octet-stream";
#if !defined WLED_DISABLE_FILESYSTEM && defined WLED_ENABLE_FS_SERVING
//Un-comment any file types you need
String getContentType(AsyncWebServerRequest* request, String filename){
if(request->hasArg("download")) return "application/octet-stream";
else if(filename.endsWith(".htm")) return "text/html";
else if(filename.endsWith(".html")) return "text/html";
else if(filename.endsWith(".css")) return "text/css";
else if(filename.endsWith(".js")) return "application/javascript";
// else if(filename.endsWith(".css")) return "text/css";
// else if(filename.endsWith(".js")) return "application/javascript";
else if(filename.endsWith(".json")) return "application/json";
else if(filename.endsWith(".png")) return "image/png";
else if(filename.endsWith(".gif")) return "image/gif";
// else if(filename.endsWith(".gif")) return "image/gif";
else if(filename.endsWith(".jpg")) return "image/jpeg";
else if(filename.endsWith(".ico")) return "image/x-icon";
else if(filename.endsWith(".xml")) return "text/xml";
else if(filename.endsWith(".pdf")) return "application/x-pdf";
else if(filename.endsWith(".zip")) return "application/x-zip";
else if(filename.endsWith(".gz")) return "application/x-gzip";
// else if(filename.endsWith(".xml")) return "text/xml";
// else if(filename.endsWith(".pdf")) return "application/x-pdf";
// else if(filename.endsWith(".zip")) return "application/x-zip";
// else if(filename.endsWith(".gz")) return "application/x-gzip";
return "text/plain";
}
bool handleFileRead(String path){
DEBUG_PRINTLN("handleFileRead: " + path);
bool handleFileRead(AsyncWebServerRequest* request, String path){
DEBUG_PRINTLN("FileRead: " + path);
if(path.endsWith("/")) path += "index.htm";
String contentType = getContentType(path);
String contentType = getContentType(request, path);
String pathWithGz = path + ".gz";
if(SPIFFS.exists(pathWithGz) || SPIFFS.exists(path)){
if(SPIFFS.exists(pathWithGz))
path += ".gz";
File file = SPIFFS.open(path, "r");
size_t sent = server->streamFile(file, contentType);
file.close();
if(SPIFFS.exists(pathWithGz)){
request->send(SPIFFS, pathWithGz, contentType);
return true;
}
if(SPIFFS.exists(path)) {
request->send(SPIFFS, path, contentType);
return true;
}
return false;
}
void handleFileUpload(){
if(server->uri() != "/edit") return;
HTTPUpload& upload = server->upload();
if(upload.status == UPLOAD_FILE_START){
String filename = upload.filename;
if(!filename.startsWith("/")) filename = "/"+filename;
DEBUG_PRINT("handleFileUpload Name: "); DEBUG_PRINTLN(filename);
fsUploadFile = SPIFFS.open(filename, "w");
filename = String();
} else if(upload.status == UPLOAD_FILE_WRITE){
//DEBUG_PRINT("handleFileUpload Data: "); DEBUG_PRINTLN(upload.currentSize);
if(fsUploadFile)
fsUploadFile.write(upload.buf, upload.currentSize);
} else if(upload.status == UPLOAD_FILE_END){
if(fsUploadFile)
fsUploadFile.close();
DEBUG_PRINT("handleFileUpload Size: "); DEBUG_PRINTLN(upload.totalSize);
}
}
void handleFileDelete(){
if(server->args() == 0) return server->send(500, "text/plain", "BAD ARGS");
String path = server->arg(0);
DEBUG_PRINTLN("handleFileDelete: " + path);
if(path == "/")
return server->send(500, "text/plain", "BAD PATH");
if(!SPIFFS.exists(path))
return server->send(404, "text/plain", "FileNotFound");
SPIFFS.remove(path);
server->send(200, "text/plain", "");
path = String();
}
void handleFileList() {
if(!server->hasArg("dir")) {server->send(500, "text/plain", "BAD ARGS"); return;}
String path = server->arg("dir");
DEBUG_PRINTLN("handleFileList: " + path);
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 += "]";
server->send(200, "text/json", output);
}
void handleFileCreate(){
if(server->args() == 0)
return server->send(500, "text/plain", "BAD ARGS");
String path = server->arg(0);
DEBUG_PRINTLN("handleFileCreate: " + path);
if(path == "/")
return server->send(500, "text/plain", "BAD PATH");
if(SPIFFS.exists(path))
return server->send(500, "text/plain", "FILE EXISTS");
File file = SPIFFS.open(path, "w");
if(file)
file.close();
else
return server->send(500, "text/plain", "CREATE FAILED");
server->send(200, "text/plain", "");
path = String();
}
#else
bool handleFileRead(String path){return false;}
bool handleFileRead(AsyncWebServerRequest*, String path){return false;}
#endif
+5 -2
View File
@@ -30,8 +30,11 @@ void wledInit()
DEBUG_PRINT("LEDs inited. heap usage ~");
DEBUG_PRINTLN(heapPreAlloc - ESP.getFreeHeap());
#ifdef USEFS
SPIFFS.begin();
#ifndef WLED_DISABLE_FILESYSTEM
#ifdef ARDUINO_ARCH_ESP32
SPIFFS.begin(true);
#endif
SPIFFS.begin();
#endif
DEBUG_PRINTLN("Load EEPROM");
+1 -22
View File
@@ -123,10 +123,9 @@ void updateLocalTime()
local = timezones[currentTimezone]->toLocal(tmc);
}
char* getTimeString()
void getTimeString(char* out)
{
updateLocalTime();
char out[32];
sprintf(out,"%i-%i-%i, %i:%s%i:%s%i",year(local), month(local), day(local),
(useAMPM)? hour(local)%12:hour(local),
(minute(local)<10)?"0":"",minute(local),
@@ -135,26 +134,6 @@ char* getTimeString()
{
strcat(out,(hour(local) > 11)? " PM":" AM");
}
return out;
/*
String ret = year(local) + "-";
ret = ret + month(local);
ret = ret + "-";
ret = ret + day(local);
ret = ret + ", ";
ret += (useAMPM)? hour(local)%12:hour(local);
ret = ret + ":";
if (minute(local) < 10) ret = ret + "0";
ret = ret + minute(local);
ret = ret + ":";
if (second(local) < 10) ret = ret + "0";
ret = ret + second(local);
if (useAMPM)
{
ret += (hour(local) > 11)? " PM":" AM";
}
return ret;
*/
}
void setCountdown()
+3 -1
View File
@@ -88,9 +88,11 @@ void publishMqtt()
strcat(subuf, "/c");
mqtt->publish(subuf, 0, true, s);
char apires[1024];
XML_response(nullptr, false, apires);
strcpy(subuf, mqttDeviceTopic);
strcat(subuf, "/v");
mqtt->publish(subuf, 0, true, XML_response(nullptr, false));
mqtt->publish(subuf, 0, true, apires);
}
+43 -23
View File
@@ -15,7 +15,7 @@ void initServer()
});
server.on("/favicon.ico", HTTP_GET, [](AsyncWebServerRequest *request){
if(!handleFileRead("/favicon.ico"))
if(!handleFileRead(request, "/favicon.ico"))
{
request->send_P(200, "image/x-icon", favicon, 156);
}
@@ -102,14 +102,16 @@ void initServer()
//if OTA is allowed
if (!otaLock){
#if !defined WLED_DISABLE_FILESYSTEM && defined WLED_ENABLE_FS_EDITOR
#ifdef ARDUINO_ARCH_ESP32
server.addHandler(new SPIFFSEditor(SPIFFS));//http_username,http_password));
#else
server.addHandler(new SPIFFSEditor());//http_username,http_password));
#endif
#else
server.on("/edit", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, "text/html", PAGE_edit);
serveMessage(request, 501, "Not implemented", "The SPIFFS editor is disabled in this build.", 254);
});
#ifdef USEFS
server.on("/edit", HTTP_PUT, handleFileCreate);
server.on("/edit", HTTP_DELETE, handleFileDelete);
server.on("/edit", HTTP_POST, [](){ server->send(200, "text/plain", ""); }, handleFileUpload);
server.on("/list", HTTP_GET, handleFileList);
#endif
//init ota page
#ifndef WLED_DISABLE_OTA
@@ -146,19 +148,16 @@ void initServer()
#else
server.on("/update", HTTP_GET, [](AsyncWebServerRequest *request){
serveMessage(request, 500, "Not implemented", "OTA updates are unsupported in this build.", 254);
serveMessage(request, 501, "Not implemented", "OTA updates are disabled in this build.", 254);
});
#endif
} else
{
server.on("/edit", HTTP_GET, [](AsyncWebServerRequest *request){
serveMessage(request, 500, "Access Denied", "Please unlock OTA in security settings!", 254);
serveMessage(request, 500, "Access Denied", "Please unlock OTA in security settings!", 254);
});
server.on("/update", HTTP_GET, [](AsyncWebServerRequest *request){
serveMessage(request, 500, "Access Denied", "Please unlock OTA in security settings!", 254);
});
server.on("/list", HTTP_GET, [](AsyncWebServerRequest *request){
serveMessage(request, 500, "Access Denied", "Please unlock OTA in security settings!", 254);
serveMessage(request, 500, "Access Denied", "Please unlock OTA in security settings!", 254);
});
}
@@ -177,12 +176,14 @@ void initServer()
request->send(200); return;
}
if(!handleSet(request, request->url())){
#ifndef WLED_DISABLE_ALEXA
if(!espalexa.handleAlexaApiCall(request))
#endif
request->send(404, "text/plain", "Not Found");
}
if(handleSet(request, request->url())) return;
#ifndef WLED_DISABLE_ALEXA
if(espalexa.handleAlexaApiCall(request)) return;
#endif
#ifdef WLED_ENABLE_FS_SERVING
if(handleFileRead(request, request->url())) return;
#endif
request->send(404, "text/plain", "Not Found");
});
}
@@ -218,6 +219,16 @@ void serveIndex(AsyncWebServerRequest* request)
if (uiConfiguration == 0 && request->hasHeader("User-Agent")) serveMobile = checkClientIsMobile(request->getHeader("User-Agent")->value());
else if (uiConfiguration == 2) serveMobile = true;
#ifdef WLED_ENABLE_FS_SERVING
if (serveMobile)
{
if (handleFileRead(request, "/index_mobile.htm")) return;
} else
{
if (handleFileRead(request, "/index.htm")) return;
}
#endif
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html",
(serveMobile) ? (uint8_t*)PAGE_indexM : PAGE_index,
(serveMobile) ? PAGE_indexM_L : PAGE_index_L);
@@ -235,7 +246,13 @@ void serveIndex(AsyncWebServerRequest* request)
String msgProcessor(const String& var)
{
if (var == "CSS") return String(obuf);
if (var == "CSS") {
char css[512];
obuf = css;
olen = 0;
getCSSColors();
return String(obuf);
}
if (var == "MSG") {
String messageBody = messageHead;
messageBody += "</h2>";
@@ -266,8 +283,10 @@ String msgProcessor(const String& var)
void serveMessage(AsyncWebServerRequest* request, uint16_t code, String headl, String subl="", byte optionT=255)
{
char buf[512];
#ifndef ARDUINO_ARCH_ESP32
char buf[256];
obuf = buf;
#endif
olen = 0;
getCSSColors();
messageHead = headl;
@@ -281,9 +300,10 @@ void serveMessage(AsyncWebServerRequest* request, uint16_t code, String headl, S
String settingsProcessor(const String& var)
{
if (var == "CSS") {
char* buf = getSettingsJS(optionType);
char buf[2048];
getSettingsJS(optionType, buf);
getCSSColors();
return buf;
return String(buf);
}
if (var == "SCSS") return String(PAGE_settingsCss);
return String();
+1 -1
View File
@@ -195,7 +195,7 @@ void serializeInfo(JsonObject& root)
#ifndef WLED_DISABLE_CRONIXIE
os += 0x10;
#endif
#ifdef USEFS
#ifndef WLED_DISABLE_FILESYSTEM
os += 0x08;
#endif
#ifndef WLED_DISABLE_HUESYNC