mirror of
				https://github.com/esp8266/Arduino.git
				synced 2025-10-30 04:26:50 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			636 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			636 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|   FSBrowser - A web-based FileSystem Browser for ESP8266 filesystems
 | |
| 
 | |
|   Copyright (c) 2015 Hristo Gochkov. All rights reserved.
 | |
|   This file is part of the ESP8266WebServer library for Arduino environment.
 | |
| 
 | |
|   This library is free software; you can redistribute it and/or
 | |
|   modify it under the terms of the GNU Lesser General Public
 | |
|   License as published by the Free Software Foundation; either
 | |
|   version 2.1 of the License, or (at your option) any later version.
 | |
| 
 | |
|   This library is distributed in the hope that it will be useful,
 | |
|   but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|   Lesser General Public License for more details.
 | |
| 
 | |
|   You should have received a copy of the GNU Lesser General Public
 | |
|   License along with this library; if not, write to the Free Software
 | |
|   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 | |
| 
 | |
|   See readme.md for more information.
 | |
| */
 | |
| 
 | |
| ////////////////////////////////
 | |
| 
 | |
| // Select the FileSystem by uncommenting one of the lines below
 | |
| 
 | |
| //#define USE_SPIFFS
 | |
| #define USE_LITTLEFS
 | |
| //#define USE_SDFS
 | |
| 
 | |
| // Uncomment the following line to embed a version of the web page in the code
 | |
| // (program code will be larger, but no file will have to be written to the filesystem).
 | |
| // Note: the source file "extras/index_htm.h" must have been generated by "extras/reduce_index.sh"
 | |
| 
 | |
| //#define INCLUDE_FALLBACK_INDEX_HTM
 | |
| 
 | |
| ////////////////////////////////
 | |
| 
 | |
| #include <ESP8266WiFi.h>
 | |
| #include <WiFiClient.h>
 | |
| #include <ESP8266WebServer.h>
 | |
| #include <ESP8266mDNS.h>
 | |
| #include <SPI.h>
 | |
| 
 | |
| #ifdef INCLUDE_FALLBACK_INDEX_HTM
 | |
| #include "extras/index_htm.h"
 | |
| #endif
 | |
| 
 | |
| #if defined USE_SPIFFS
 | |
| #include <FS.h>
 | |
| const char* fsName = "SPIFFS";
 | |
| FS* fileSystem = &SPIFFS;
 | |
| SPIFFSConfig fileSystemConfig = SPIFFSConfig();
 | |
| #elif defined USE_LITTLEFS
 | |
| #include <LittleFS.h>
 | |
| const char* fsName = "LittleFS";
 | |
| FS* fileSystem = &LittleFS;
 | |
| LittleFSConfig fileSystemConfig = LittleFSConfig();
 | |
| #elif defined USE_SDFS
 | |
| #include <SDFS.h>
 | |
| const char* fsName = "SDFS";
 | |
| FS* fileSystem = &SDFS;
 | |
| SDFSConfig fileSystemConfig = SDFSConfig();
 | |
| // fileSystemConfig.setCSPin(chipSelectPin);
 | |
| #else
 | |
| #error Please select a filesystem first by uncommenting one of the "#define USE_xxx" lines at the beginning of the sketch.
 | |
| #endif
 | |
| 
 | |
| 
 | |
| #define DBG_OUTPUT_PORT Serial
 | |
| 
 | |
| #ifndef STASSID
 | |
| #define STASSID "your-ssid"
 | |
| #define STAPSK  "your-password"
 | |
| #endif
 | |
| 
 | |
| const char* ssid = STASSID;
 | |
| const char* password = STAPSK;
 | |
| const char* host = "fsbrowser";
 | |
| 
 | |
| ESP8266WebServer server(80);
 | |
| 
 | |
| static bool fsOK;
 | |
| String unsupportedFiles = String();
 | |
| 
 | |
| File uploadFile;
 | |
| 
 | |
| static const char TEXT_PLAIN[] PROGMEM = "text/plain";
 | |
| static const char FS_INIT_ERROR[] PROGMEM = "FS INIT ERROR";
 | |
| static const char FILE_NOT_FOUND[] PROGMEM = "FileNotFound";
 | |
| 
 | |
| ////////////////////////////////
 | |
| // Utils to return HTTP codes, and determine content-type
 | |
| 
 | |
| void replyOK() {
 | |
|   server.send(200, FPSTR(TEXT_PLAIN), "");
 | |
| }
 | |
| 
 | |
| void replyOKWithMsg(String msg) {
 | |
|   server.send(200, FPSTR(TEXT_PLAIN), msg);
 | |
| }
 | |
| 
 | |
| void replyNotFound(String msg) {
 | |
|   server.send(404, FPSTR(TEXT_PLAIN), msg);
 | |
| }
 | |
| 
 | |
| void replyBadRequest(String msg) {
 | |
|   DBG_OUTPUT_PORT.println(msg);
 | |
|   server.send(400, FPSTR(TEXT_PLAIN), msg + "\r\n");
 | |
| }
 | |
| 
 | |
| void replyServerError(String msg) {
 | |
|   DBG_OUTPUT_PORT.println(msg);
 | |
|   server.send(500, FPSTR(TEXT_PLAIN), msg + "\r\n");
 | |
| }
 | |
| 
 | |
| #ifdef USE_SPIFFS
 | |
| /*
 | |
|    Checks filename for character combinations that are not supported by FSBrowser (alhtough valid on SPIFFS).
 | |
|    Returns an empty String if supported, or detail of error(s) if unsupported
 | |
| */
 | |
| String checkForUnsupportedPath(String filename) {
 | |
|   String error = String();
 | |
|   if (!filename.startsWith("/")) {
 | |
|     error += F("!NO_LEADING_SLASH! ");
 | |
|   }
 | |
|   if (filename.indexOf("//") != -1) {
 | |
|     error += F("!DOUBLE_SLASH! ");
 | |
|   }
 | |
|   if (filename.endsWith("/")) {
 | |
|     error += F("!TRAILING_SLASH! ");
 | |
|   }
 | |
|   return error;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| 
 | |
| ////////////////////////////////
 | |
| // Request handlers
 | |
| 
 | |
| /*
 | |
|    Return the FS type, status and size info
 | |
| */
 | |
| void handleStatus() {
 | |
|   DBG_OUTPUT_PORT.println("handleStatus");
 | |
|   FSInfo fs_info;
 | |
|   String json;
 | |
|   json.reserve(128);
 | |
| 
 | |
|   json = "{\"type\":\"";
 | |
|   json += fsName;
 | |
|   json += "\", \"isOk\":";
 | |
|   if (fsOK) {
 | |
|     fileSystem->info(fs_info);
 | |
|     json += F("\"true\", \"totalBytes\":\"");
 | |
|     json += fs_info.totalBytes;
 | |
|     json += F("\", \"usedBytes\":\"");
 | |
|     json += fs_info.usedBytes;
 | |
|     json += "\"";
 | |
|   } else {
 | |
|     json += "\"false\"";
 | |
|   }
 | |
|   json += F(",\"unsupportedFiles\":\"");
 | |
|   json += unsupportedFiles;
 | |
|   json += "\"}";
 | |
| 
 | |
|   server.send(200, "application/json", json);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|    Return the list of files in the directory specified by the "dir" query string parameter.
 | |
|    Also demonstrates the use of chunked responses.
 | |
| */
 | |
| void handleFileList() {
 | |
|   if (!fsOK) {
 | |
|     return replyServerError(FPSTR(FS_INIT_ERROR));
 | |
|   }
 | |
| 
 | |
|   if (!server.hasArg("dir")) {
 | |
|     return replyBadRequest(F("DIR ARG MISSING"));
 | |
|   }
 | |
| 
 | |
|   String path = server.arg("dir");
 | |
|   if (path != "/" && !fileSystem->exists(path)) {
 | |
|     return replyBadRequest("BAD PATH");
 | |
|   }
 | |
| 
 | |
|   DBG_OUTPUT_PORT.println(String("handleFileList: ") + path);
 | |
|   Dir dir = fileSystem->openDir(path);
 | |
|   path.clear();
 | |
| 
 | |
|   // use HTTP/1.1 Chunked response to avoid building a huge temporary string
 | |
|   if (!server.chunkedResponseModeStart(200, "text/json")) {
 | |
|     server.send(505, F("text/html"), F("HTTP1.1 required"));
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // use the same string for every line
 | |
|   String output;
 | |
|   output.reserve(64);
 | |
|   while (dir.next()) {
 | |
| #ifdef USE_SPIFFS
 | |
|     String error = checkForUnsupportedPath(dir.fileName());
 | |
|     if (error.length() > 0) {
 | |
|       DBG_OUTPUT_PORT.println(String("Ignoring ") + error + dir.fileName());
 | |
|       continue;
 | |
|     }
 | |
| #endif
 | |
|     if (output.length()) {
 | |
|       // send string from previous iteration
 | |
|       // as an HTTP chunk
 | |
|       server.sendContent(output);
 | |
|       output = ',';
 | |
|     } else {
 | |
|       output = '[';
 | |
|     }
 | |
| 
 | |
|     output += "{\"type\":\"";
 | |
|     if (dir.isDirectory()) {
 | |
|       output += "dir";
 | |
|     } else {
 | |
|       output += F("file\",\"size\":\"");
 | |
|       output += dir.fileSize();
 | |
|     }
 | |
| 
 | |
|     output += F("\",\"name\":\"");
 | |
|     // Always return names without leading "/"
 | |
|     if (dir.fileName()[0] == '/') {
 | |
|       output += &(dir.fileName()[1]);
 | |
|     } else {
 | |
|       output += dir.fileName();
 | |
|     }
 | |
| 
 | |
|     output += "\"}";
 | |
|   }
 | |
| 
 | |
|   // send last string
 | |
|   output += "]";
 | |
|   server.sendContent(output);
 | |
|   server.chunkedResponseFinalize();
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|    Read the given file from the filesystem and stream it back to the client
 | |
| */
 | |
| bool handleFileRead(String path) {
 | |
|   DBG_OUTPUT_PORT.println(String("handleFileRead: ") + path);
 | |
|   if (!fsOK) {
 | |
|     replyServerError(FPSTR(FS_INIT_ERROR));
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   if (path.endsWith("/")) {
 | |
|     path += "index.htm";
 | |
|   }
 | |
| 
 | |
|   String contentType;
 | |
|   if (server.hasArg("download")) {
 | |
|     contentType = F("application/octet-stream");
 | |
|   } else {
 | |
|     contentType = mime::getContentType(path);
 | |
|   }
 | |
| 
 | |
|   if (!fileSystem->exists(path)) {
 | |
|     // File not found, try gzip version
 | |
|     path = path + ".gz";
 | |
|   }
 | |
|   if (fileSystem->exists(path)) {
 | |
|     File file = fileSystem->open(path, "r");
 | |
|     if (server.streamFile(file, contentType) != file.size()) {
 | |
|       DBG_OUTPUT_PORT.println("Sent less data than expected!");
 | |
|     }
 | |
|     file.close();
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|    As some FS (e.g. LittleFS) delete the parent folder when the last child has been removed,
 | |
|    return the path of the closest parent still existing
 | |
| */
 | |
| String lastExistingParent(String path) {
 | |
|   while (!path.isEmpty() && !fileSystem->exists(path)) {
 | |
|     if (path.lastIndexOf('/') > 0) {
 | |
|       path = path.substring(0, path.lastIndexOf('/'));
 | |
|     } else {
 | |
|       path = String();  // No slash => the top folder does not exist
 | |
|     }
 | |
|   }
 | |
|   DBG_OUTPUT_PORT.println(String("Last existing parent: ") + path);
 | |
|   return path;
 | |
| }
 | |
| 
 | |
| /*
 | |
|    Handle the creation/rename of a new file
 | |
|    Operation      | req.responseText
 | |
|    ---------------+--------------------------------------------------------------
 | |
|    Create file    | parent of created file
 | |
|    Create folder  | parent of created folder
 | |
|    Rename file    | parent of source file
 | |
|    Move file      | parent of source file, or remaining ancestor
 | |
|    Rename folder  | parent of source folder
 | |
|    Move folder    | parent of source folder, or remaining ancestor
 | |
| */
 | |
| void handleFileCreate() {
 | |
|   if (!fsOK) {
 | |
|     return replyServerError(FPSTR(FS_INIT_ERROR));
 | |
|   }
 | |
| 
 | |
|   String path = server.arg("path");
 | |
|   if (path.isEmpty()) {
 | |
|     return replyBadRequest(F("PATH ARG MISSING"));
 | |
|   }
 | |
| 
 | |
| #ifdef USE_SPIFFS
 | |
|   if (checkForUnsupportedPath(path).length() > 0) {
 | |
|     return replyServerError(F("INVALID FILENAME"));
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   if (path == "/") {
 | |
|     return replyBadRequest("BAD PATH");
 | |
|   }
 | |
|   if (fileSystem->exists(path)) {
 | |
|     return replyBadRequest(F("PATH FILE EXISTS"));
 | |
|   }
 | |
| 
 | |
|   String src = server.arg("src");
 | |
|   if (src.isEmpty()) {
 | |
|     // No source specified: creation
 | |
|     DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path);
 | |
|     if (path.endsWith("/")) {
 | |
|       // Create a folder
 | |
|       path.remove(path.length() - 1);
 | |
|       if (!fileSystem->mkdir(path)) {
 | |
|         return replyServerError(F("MKDIR FAILED"));
 | |
|       }
 | |
|     } else {
 | |
|       // Create a file
 | |
|       File file = fileSystem->open(path, "w");
 | |
|       if (file) {
 | |
|         file.write((const char *)0);
 | |
|         file.close();
 | |
|       } else {
 | |
|         return replyServerError(F("CREATE FAILED"));
 | |
|       }
 | |
|     }
 | |
|     if (path.lastIndexOf('/') > -1) {
 | |
|       path = path.substring(0, path.lastIndexOf('/'));
 | |
|     }
 | |
|     replyOKWithMsg(path);
 | |
|   } else {
 | |
|     // Source specified: rename
 | |
|     if (src == "/") {
 | |
|       return replyBadRequest("BAD SRC");
 | |
|     }
 | |
|     if (!fileSystem->exists(src)) {
 | |
|       return replyBadRequest(F("SRC FILE NOT FOUND"));
 | |
|     }
 | |
| 
 | |
|     DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path + " from " + src);
 | |
| 
 | |
|     if (path.endsWith("/")) {
 | |
|       path.remove(path.length() - 1);
 | |
|     }
 | |
|     if (src.endsWith("/")) {
 | |
|       src.remove(src.length() - 1);
 | |
|     }
 | |
|     if (!fileSystem->rename(src, path)) {
 | |
|       return replyServerError(F("RENAME FAILED"));
 | |
|     }
 | |
|     replyOKWithMsg(lastExistingParent(src));
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|    Delete the file or folder designed by the given path.
 | |
|    If it's a file, delete it.
 | |
|    If it's a folder, delete all nested contents first then the folder itself
 | |
| 
 | |
|    IMPORTANT NOTE: using recursion is generally not recommended on embedded devices and can lead to crashes (stack overflow errors).
 | |
|    This use is just for demonstration purpose, and FSBrowser might crash in case of deeply nested filesystems.
 | |
|    Please don't do this on a production system.
 | |
| */
 | |
| void deleteRecursive(String path) {
 | |
|   File file = fileSystem->open(path, "r");
 | |
|   bool isDir = file.isDirectory();
 | |
|   file.close();
 | |
| 
 | |
|   // If it's a plain file, delete it
 | |
|   if (!isDir) {
 | |
|     fileSystem->remove(path);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // Otherwise delete its contents first
 | |
|   Dir dir = fileSystem->openDir(path);
 | |
| 
 | |
|   while (dir.next()) {
 | |
|     deleteRecursive(path + '/' + dir.fileName());
 | |
|   }
 | |
| 
 | |
|   // Then delete the folder itself
 | |
|   fileSystem->rmdir(path);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|    Handle a file deletion request
 | |
|    Operation      | req.responseText
 | |
|    ---------------+--------------------------------------------------------------
 | |
|    Delete file    | parent of deleted file, or remaining ancestor
 | |
|    Delete folder  | parent of deleted folder, or remaining ancestor
 | |
| */
 | |
| void handleFileDelete() {
 | |
|   if (!fsOK) {
 | |
|     return replyServerError(FPSTR(FS_INIT_ERROR));
 | |
|   }
 | |
| 
 | |
|   String path = server.arg(0);
 | |
|   if (path.isEmpty() || path == "/") {
 | |
|     return replyBadRequest("BAD PATH");
 | |
|   }
 | |
| 
 | |
|   DBG_OUTPUT_PORT.println(String("handleFileDelete: ") + path);
 | |
|   if (!fileSystem->exists(path)) {
 | |
|     return replyNotFound(FPSTR(FILE_NOT_FOUND));
 | |
|   }
 | |
|   deleteRecursive(path);
 | |
| 
 | |
|   replyOKWithMsg(lastExistingParent(path));
 | |
| }
 | |
| 
 | |
| /*
 | |
|    Handle a file upload request
 | |
| */
 | |
| void handleFileUpload() {
 | |
|   if (!fsOK) {
 | |
|     return replyServerError(FPSTR(FS_INIT_ERROR));
 | |
|   }
 | |
|   if (server.uri() != "/edit") {
 | |
|     return;
 | |
|   }
 | |
|   HTTPUpload& upload = server.upload();
 | |
|   if (upload.status == UPLOAD_FILE_START) {
 | |
|     String filename = upload.filename;
 | |
|     // Make sure paths always start with "/"
 | |
|     if (!filename.startsWith("/")) {
 | |
|       filename = "/" + filename;
 | |
|     }
 | |
|     DBG_OUTPUT_PORT.println(String("handleFileUpload Name: ") + filename);
 | |
|     uploadFile = fileSystem->open(filename, "w");
 | |
|     if (!uploadFile) {
 | |
|       return replyServerError(F("CREATE FAILED"));
 | |
|     }
 | |
|     DBG_OUTPUT_PORT.println(String("Upload: START, filename: ") + filename);
 | |
|   } else if (upload.status == UPLOAD_FILE_WRITE) {
 | |
|     if (uploadFile) {
 | |
|       size_t bytesWritten = uploadFile.write(upload.buf, upload.currentSize);
 | |
|       if (bytesWritten != upload.currentSize) {
 | |
|         return replyServerError(F("WRITE FAILED"));
 | |
|       }
 | |
|     }
 | |
|     DBG_OUTPUT_PORT.println(String("Upload: WRITE, Bytes: ") + upload.currentSize);
 | |
|   } else if (upload.status == UPLOAD_FILE_END) {
 | |
|     if (uploadFile) {
 | |
|       uploadFile.close();
 | |
|     }
 | |
|     DBG_OUTPUT_PORT.println(String("Upload: END, Size: ") + upload.totalSize);
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|    The "Not Found" handler catches all URI not explicitly declared in code
 | |
|    First try to find and return the requested file from the filesystem,
 | |
|    and if it fails, return a 404 page with debug information
 | |
| */
 | |
| void handleNotFound() {
 | |
|   if (!fsOK) {
 | |
|     return replyServerError(FPSTR(FS_INIT_ERROR));
 | |
|   }
 | |
| 
 | |
|   String uri = ESP8266WebServer::urlDecode(server.uri()); // required to read paths with blanks
 | |
| 
 | |
|   if (handleFileRead(uri)) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // Dump debug data
 | |
|   String message;
 | |
|   message.reserve(100);
 | |
|   message = F("Error: File not found\n\nURI: ");
 | |
|   message += uri;
 | |
|   message += F("\nMethod: ");
 | |
|   message += (server.method() == HTTP_GET) ? "GET" : "POST";
 | |
|   message += F("\nArguments: ");
 | |
|   message += server.args();
 | |
|   message += '\n';
 | |
|   for (uint8_t i = 0; i < server.args(); i++) {
 | |
|     message += F(" NAME:");
 | |
|     message += server.argName(i);
 | |
|     message += F("\n VALUE:");
 | |
|     message += server.arg(i);
 | |
|     message += '\n';
 | |
|   }
 | |
|   message += "path=";
 | |
|   message += server.arg("path");
 | |
|   message += '\n';
 | |
|   DBG_OUTPUT_PORT.print(message);
 | |
| 
 | |
|   return replyNotFound(message);
 | |
| }
 | |
| 
 | |
| /*
 | |
|    This specific handler returns the index.htm (or a gzipped version) from the /edit folder.
 | |
|    If the file is not present but the flag INCLUDE_FALLBACK_INDEX_HTM has been set, falls back to the version
 | |
|    embedded in the program code.
 | |
|    Otherwise, fails with a 404 page with debug information
 | |
| */
 | |
| void handleGetEdit() {
 | |
|   if (handleFileRead(F("/edit/index.htm"))) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
| #ifdef INCLUDE_FALLBACK_INDEX_HTM
 | |
|   server.sendHeader(F("Content-Encoding"), "gzip");
 | |
|   server.send(200, "text/html", index_htm_gz, index_htm_gz_len);
 | |
| #else
 | |
|   replyNotFound(FPSTR(FILE_NOT_FOUND));
 | |
| #endif
 | |
| 
 | |
| }
 | |
| 
 | |
| void setup(void) {
 | |
|   ////////////////////////////////
 | |
|   // SERIAL INIT
 | |
|   DBG_OUTPUT_PORT.begin(115200);
 | |
|   DBG_OUTPUT_PORT.setDebugOutput(true);
 | |
|   DBG_OUTPUT_PORT.print('\n');
 | |
| 
 | |
|   ////////////////////////////////
 | |
|   // FILESYSTEM INIT
 | |
| 
 | |
|   fileSystemConfig.setAutoFormat(false);
 | |
|   fileSystem->setConfig(fileSystemConfig);
 | |
|   fsOK = fileSystem->begin();
 | |
|   DBG_OUTPUT_PORT.println(fsOK ? F("Filesystem initialized.") : F("Filesystem init failed!"));
 | |
| 
 | |
| #ifdef USE_SPIFFS
 | |
|   // Debug: dump on console contents of filesystem with no filter and check filenames validity
 | |
|   Dir dir = fileSystem->openDir("");
 | |
|   DBG_OUTPUT_PORT.println(F("List of files at root of filesystem:"));
 | |
|   while (dir.next()) {
 | |
|     String error = checkForUnsupportedPath(dir.fileName());
 | |
|     String fileInfo = dir.fileName() + (dir.isDirectory() ? " [DIR]" : String(" (") + dir.fileSize() + "b)");
 | |
|     DBG_OUTPUT_PORT.println(error + fileInfo);
 | |
|     if (error.length() > 0) {
 | |
|       unsupportedFiles += error + fileInfo + '\n';
 | |
|     }
 | |
|   }
 | |
|   DBG_OUTPUT_PORT.println();
 | |
| 
 | |
|   // Keep the "unsupportedFiles" variable to show it, but clean it up
 | |
|   unsupportedFiles.replace("\n", "<br/>");
 | |
|   unsupportedFiles = unsupportedFiles.substring(0, unsupportedFiles.length() - 5);
 | |
| #endif
 | |
| 
 | |
|   ////////////////////////////////
 | |
|   // WI-FI INIT
 | |
|   DBG_OUTPUT_PORT.printf("Connecting to %s\n", ssid);
 | |
|   WiFi.mode(WIFI_STA);
 | |
|   WiFi.begin(ssid, password);
 | |
|   // Wait for connection
 | |
|   while (WiFi.status() != WL_CONNECTED) {
 | |
|     delay(500);
 | |
|     DBG_OUTPUT_PORT.print(".");
 | |
|   }
 | |
|   DBG_OUTPUT_PORT.println("");
 | |
|   DBG_OUTPUT_PORT.print(F("Connected! IP address: "));
 | |
|   DBG_OUTPUT_PORT.println(WiFi.localIP());
 | |
| 
 | |
|   ////////////////////////////////
 | |
|   // MDNS INIT
 | |
|   if (MDNS.begin(host)) {
 | |
|     MDNS.addService("http", "tcp", 80);
 | |
|     DBG_OUTPUT_PORT.print(F("Open http://"));
 | |
|     DBG_OUTPUT_PORT.print(host);
 | |
|     DBG_OUTPUT_PORT.println(F(".local/edit to open the FileSystem Browser"));
 | |
|   }
 | |
| 
 | |
|   ////////////////////////////////
 | |
|   // WEB SERVER INIT
 | |
| 
 | |
|   // Filesystem status
 | |
|   server.on("/status", HTTP_GET, handleStatus);
 | |
| 
 | |
|   // List directory
 | |
|   server.on("/list", HTTP_GET, handleFileList);
 | |
| 
 | |
|   // Load editor
 | |
|   server.on("/edit", HTTP_GET, handleGetEdit);
 | |
| 
 | |
|   // Create file
 | |
|   server.on("/edit",  HTTP_PUT, handleFileCreate);
 | |
| 
 | |
|   // Delete file
 | |
|   server.on("/edit",  HTTP_DELETE, handleFileDelete);
 | |
| 
 | |
|   // Upload file
 | |
|   // - first callback is called after the request has ended with all parsed arguments
 | |
|   // - second callback handles file upload at that location
 | |
|   server.on("/edit",  HTTP_POST, replyOK, handleFileUpload);
 | |
| 
 | |
|   // Default handler for all URIs not defined above
 | |
|   // Use it to read files from filesystem
 | |
|   server.onNotFound(handleNotFound);
 | |
| 
 | |
|   // Start server
 | |
|   server.begin();
 | |
|   DBG_OUTPUT_PORT.println("HTTP server started");
 | |
| }
 | |
| 
 | |
| 
 | |
| void loop(void) {
 | |
|   server.handleClient();
 | |
|   MDNS.update();
 | |
| }
 |