Bibliotheken om het leven makkelijker te maken (part 3)

135 keer bekeken / views

Dit is het derde deel van drie posts over drie bibliotheken die ik heb geschreven om ons leven makkelijker te maken:

Dit is het 3e deel en gaat dus over de Filesystem Manager.
In mijn eerdere posts heb ik besproken hoe bibliotheken het leven van een ontwikkelaar kunnen vereenvoudigen. In dit derde deel richt ik me specifieke op de FSmanager bibliotheek. Deze bibliotheek biedt een eenvoudige manier om het bestandssysteem op ESP8266- en ESP32-microcontrollers te beheren via een webinterface.

Wat is FSmanager?

FSmanager is een C++-bibliotheek die gebruikmaakt van LittleFS om bestandssystemen op ESP-microcontrollers te beheren. Met FSmanager kun je bestanden uploaden, downloaden, verwijderen en mappen aanmaken of verwijderen. Daarnaast biedt het functies om de totale en gebruikte opslagruimte te tonen. De bibliotheek is ontworpen voor zowel ESP8266 als ESP32 en integreert naadloos met de Arduino-ontwikkelomgeving.

Belangrijkste functies

  • Bestandsbeheer: Uploaden, downloaden en verwijderen van bestanden.
  • Mapbeheer: Aanmaken en verwijderen van mappen (let op: alleen lege mappen kunnen worden verwijderd).
  • Opslaginformatie: Weergave van totale en gebruikte opslagruimte, met automatische aanpassing van de eenheden (B, KB, MB).
  • Systeembestandenbescherming: Voorkomt per ongeluk verwijderen van cruciale systeembestanden.

Installatie en gebruik

Voor PlatformIO-gebruikers:

Voeg de FSmanager-bibliotheek toe aan je platformio.ini-bestand:

lib_deps =
  https://github.com/mrWheel/esp-fsmanager

Zorg ervoor dat de benodigde afhankelijkheden, zoals LittleFS en de juiste webserverbibliotheek voor je microcontroller, zijn geïnstalleerd.

Voor Arduino IDE-gebruikers:

  • Download de FSmanager-bibliotheek als een .zip-bestand van de GitHub-pagina.
  • Open de Arduino IDE en selecteer Sketch > Add .ZIP Library….
  • Navigeer naar de locatie waar je het .zip-bestand hebt opgeslagen en voeg het toe.
  • Installeer de benodigde afhankelijkheden via de Bibliotheekbeheerder:

    * Voor ESP8266: ESP8266WebServer en LittleFS.
    * Voor ESP32: WebServer en LittleFS.

Voorbeeld van gebruik

Hier is een eenvoudig voorbeeld van hoe je FSmanager in je project kunt integreren:

#include <FSmanager.h>;

void setup()
{
    Serial.begin(115200);

    //-- Initialize WiFiManager
    wifiManager.autoConnect("FSManager-AP");

    LittleFS.begin();

    fsManager.begin(&Serial);

    fsManager.addSystemFile("/basicFSM.html");
    fsManager.addSystemFile("/basicFSM.js");

    server.on("/", HTTP_GET, []() {
        server.send(200, "text/html", readSystemHtml("/basicFSM.html"));
    });
    server.onNotFound([]() {
      Serial.printf("Not Found: %s\n", server.uri().c_str());
      server.send(404, "text/plain", "404 Not Found");
    });
    server.begin();
    Serial.println("Webserver started!");
}

void loop()
{
    server.handleClient();
}

Dit voorbeeld initialiseert FSmanager en start de webinterface, waardoor je via een webbrowser toegang krijgt tot het bestandssysteem van je ESP-microcontroller. Om een werkende FSmanager te krijgen moet je er wel voor zorgen dat de bestanden basicFSM.html en basicFSM.js bestaan in de root van het LittleFS!

Het basicFSM.js bestand beval alle Javascript functies die nodig zijn voor de werking van de FSmanager. Het basicFSM.html bestand ziet er zo uit:

<!DOCTYPE html>
<html>
<head>
  <title>FSManager</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
  <h1>FileSystem Manager</h1>

  <div id="fileList">
    <h2>Files</h2>
    <div id="files"></div>
    <div id="spaceInfo"></div>
  </div>

  <div style="display: gap: 20px; margin-bottom: 20px;">
    <div>
      <h2 id="uploadHeading">Upload File</h2>
      <form id="uploadForm" action="/fsm/upload" method="post" enctype="multipart/form-data" onsubmit="handleUpload(event)">
        <input type="file" name="file" required>
        <button type="submit" class="button upload">Upload File</button>
      </form>
    </div>

    <div id="folderInput">
      <h2>New Folder</h2>
      <div style="display: gap: 10px;">
        <input type="text" id="foldername" placeholder="Enter folder name">
        <button class="button" id="createFolderBtn" onclick="createFolder()">Create</button>
      </div>
    </div>
  </div>

 
  <div id="status"></div>

  <script src="/basicFSM.js"></script>
</body>
</html>

Het belangrijkste onderdeel is:

<div id="fileList">
  <h2>Files</h2>
  <div id="files"></div>
  <div id="spaceInfo"></div>
</div>

In de <div> met id="fileList" zullen de mappen en bestanden van het LittleFS worden ingevuld en meer specifiek in de <div> met id="files". In de <div> met id="spaceInfo" komen de totale grootte en de gebruikte ruimte van het LittelFS te staan.

Onder deze <div>‘s komen twee knoppen. Deze dienen respectievelijk voor het uploaden van een bestand naar de ESP:

<div>
  <h2 id="uploadHeading">Upload File</h2>
  <form id="uploadForm" action="/fsm/upload" method="post" enctype="multipart/form-data" onsubmit="handleUpload(event)">
    <input type="file" name="file" required>
    <button type="submit" class="button upload">Upload File</button>
  </form>
</div>

en voor het aanmaken van een nieuwe map.

<div id="folderInput">
  <h2>New Folder</h2>
  <div style="display: gap: 10px;">
    <input type="text" id="foldername" placeholder="Enter folder name">
    <button class="button" id="createFolderBtn" onclick="createFolder()">Create</button>
  </div>
</div>

Het mag duidelijk zijn dat de gebruikte id‘s correct in het html bestand moeten staan omdat de Javascript functies anders niet werken.

Als je mij wil helpen om meer van dit soort posts te schrijven, overweeg dan om een kleine donatie te geven door op de knop hieronder te klikken.

FSmanager vereenvoudigt het beheer van het bestandssysteem op ESP8266- en ESP32-microcontrollers aanzienlijk. Door een intuïtieve webinterface te bieden, maakt het de interactie met bestanden en mappen op je apparaat eenvoudig en efficiënt. Of je nu bestanden wilt uploaden, verwijderen of de opslagruimte wilt controleren, FSmanager is het tool dat je hierbij helpt.

API Reference

Overview

De FSmanager-bibliotheek biedt een webgebaseerd bestandsbeheersysteem voor ESP32- en ESP8266-microcontrollers. Hiermee kunnen gebruikers bestanden in het LittleFS-bestandssysteem via een webinterface bekijken, uploaden, downloaden en beheren. De bibliotheek is ontworpen om te werken met zowel ESP32 als ESP8266 en past zich automatisch aan de juiste platformspecifieke API’s aan.

Class: FSmanager

Description

De FSmanager-klasse integreert met een ESP-webserver om bestandsbeheerfunctionaliteit te bieden. Het behandelt bestandsbewerkingen zoals het weergeven, uploaden, downloaden en verwijderen van bestanden, evenals het aanmaken en verwijderen van mappen.

Constructor

FSmanager(WebServerClass &server);

Aanmaken van een FSmanager-instantie

Maakt een nieuwe FSmanager-instantie aan.

Parameters:

  • server: Referentie naar een WebServer-instantie (WebServer voor ESP32 of ESP8266WebServer voor ESP8266).

Voorbeeld:

#ifdef ESP32
  WebServer server(80);
#else
  ESP8266WebServer server(80);
#endif

FSmanager fsManager(server);

Publieke methoden

begin

void begin(Stream* debugOutput = &Serial);

Initialiseert FSmanager en stelt de benodigde HTTP-handlers in voor bestandsbeheer.

Parameters:

• debugOutput: Optionele Stream* voor debug-uitvoer (standaard: Serial).

Voorbeeld:

void setup()
{
  Serial.begin(115200);
  LittleFS.begin();
  
  //-- Initialiseren met debug-uitvoer naar Serial
  fsManager.begin(&Serial);  
  
  server.begin();
}


setSystemFilePath

void setSystemFilePath(const std::string &path);

Stelt het pad in voor systeembestanden, die worden beschermd tegen verwijdering.

Parameters:

  • path: Pad voor systeembestanden (wordt genormaliseerd zodat het altijd begint met ‘/’ en nooit eindigt met ‘/’).

Voorbeeld:

//-- Stel het pad voor systeembestanden in op "/config"
fsManager.setSystemFilePath("/config");

//-- Bestanden toegevoegd met addSystemFile zijn nu relatief aan dit pad
fsManager.addSystemFile(fsManager.getSystemFilePath() + "/settings.json");


getSystemFilePath

std::string getSystemFilePath() const;

Geeft het huidige pad voor systeembestanden terug.

Returnwaarde:

  • Het huidige systeembestandspad als een std::string.

Voorbeeld:

//-- Haal het huidige systeembestandspad op
std::string sysPath = fsManager.getSystemFilePath();
Serial.printf("Huidig systeem pad: %s\n", sysPath.c_str());

//-- Gebruik het om een volledig pad te maken
std::string fullPath = sysPath + "/config.json";


addSystemFile

void addSystemFile(const std::string &fileName, bool setServe = true);

Voegt een bestand toe aan de lijst met systeembestanden (wordt beschermd tegen verwijdering via de webinterface).

Parameters:

  • fileName: Het pad naar het bestand dat als systeembestand moet worden toegevoegd.
  • setServe: (Optioneel) true om het bestand automatisch te serveren via de webserver (standaard: true).

Voorbeeld:

//-- Voeg "index.html" toe als systeembestand en serveer het
fsManager.addSystemFile("/index.html");

//-- Voeg een configuratiebestand toe als systeembestand, maar serveer het niet
fsManager.addSystemFile("/config/settings.json", false);

//-- Voeg een bestand toe relatief aan het systeempad
fsManager.setSystemFilePath("/app");
fsManager.addSystemFile(fsManager.getSystemFilePath() + "/app.js");


getCurrentFolder

std::string getCurrentFolder();

Geeft het huidige pad terug van de map die wordt bekeken in de file manager.

Returnwaarde:

  • Het huidige map-pad als een std::string.

Voorbeeld:

//-- Haal de huidige map op
std::string currentFolder = fsManager.getCurrentFolder();
Serial.printf("Momenteel in map: %s\n", currentFolder.c_str());

//-- Controleer of we in de rootmap zijn
if (currentFolder == "/") 
{
  Serial.println("In de rootmap");
}

Private methoden (voor begrip van de werking)

Hoewel deze methoden privé zijn en niet direct toegankelijk, helpt het begrijpen ervan bij het effectief gebruiken van de bibliotheek:

  • handleFileList – Behandelt HTTP-verzoeken om bestanden en mappen in een specifieke map weer te geven.
  • handleDelete – Behandelt HTTP-verzoeken om een bestand te verwijderen.
  • handleUpload – Behandelt HTTP-bestandsuploadverzoeken.
  • handleDownload – Behandelt HTTP-verzoeken om een bestand te downloaden.
  • handleCreateFolder – Behandelt HTTP-verzoeken om een nieuwe map aan te maken.
  • handleDeleteFolder – Behandelt HTTP-verzoeken om een map te verwijderen.
  • formatSize – Formatteert een bestandsgrootte in bytes naar een leesbaar formaat (B, KB, MB).
  • isSystemFile – Controleert of een bestand een systeembestand is (beschermd tegen verwijdering).
  • getTotalSpace – Geeft de totale beschikbare opslagruimte terug.
  • getUsedSpace – Geeft de gebruikte opslagruimte terug.
  • handleCheckSpace – Behandelt HTTP-verzoeken om te controleren of er voldoende ruimte is voor een upload.

Volledig gebruiksvoorbeeld

Hier is een compleet voorbeeld van hoe je FSmanager kunt instellen en gebruiken:

#include <Arduino.h>
#include <WiFiManager.h>

#ifdef ESP32
  #include <WebServer.h>
  #include <Update.h>
#else
  #include <ESP8266WebServer.h>
  #include <ESP8266HTTPUpdate.h>
#endif
#include <FSmanager.h>

WiFiManager wifiManager;
Stream* debug = nullptr;

#ifdef ESP32
  WebServer server(80);
#else
  ESP8266WebServer server(80);
#endif
FSmanager fsManager(server);

#include <LittleFS.h>


void handleFileRequest(String path) 
{
  //-- First try with systemPath if available
  std::string sysPath = fsManager.getSystemFilePath();
  String fullPath;

  if (!sysPath.empty() && sysPath.back() == '/') 
  {
    sysPath.pop_back();
  }
  Serial.printf("handleFileRequest(): sysPath[%s], path[%s]\n", sysPath.c_str(), path.c_str());
  
  if (sysPath.empty()) fullPath = path;
  else                 fullPath = (sysPath + path.c_str()).c_str();
  Serial.printf("handleFileRequest(): fullPath[%s]\n", fullPath.c_str());
  
  if (LittleFS.exists(fullPath)) 
  {
    File file = LittleFS.open(fullPath, "r");

    //-- Determine the correct MIME type based on file extension
    String contentType = "text/plain"; //-- Default
    if (fullPath.endsWith(".html"))       contentType = "text/html";
    else if (fullPath.endsWith(".css"))   contentType = "text/css";
    else if (fullPath.endsWith(".js"))    contentType = "application/javascript";
    else if (fullPath.endsWith(".png"))   contentType = "image/png";
    else if (fullPath.endsWith(".jpg") || fullPath.endsWith(".jpeg")) contentType = "image/jpeg";
    else if (fullPath.endsWith(".gif"))   contentType = "image/gif";
    else if (fullPath.endsWith(".ico"))   contentType = "image/x-icon";
    else if (fullPath.endsWith(".svg"))   contentType = "image/svg+xml";
    else if (fullPath.endsWith(".woff"))  contentType = "font/woff";
    else if (fullPath.endsWith(".woff2")) contentType = "font/woff2";
    else if (fullPath.endsWith(".ttf"))   contentType = "font/ttf";
    else if (fullPath.endsWith(".eot"))   contentType = "text/plain";
    else if (fullPath.endsWith(".json"))  contentType = "application/json";

    Serial.printf("handleFileRequest: [%s] contentType[%s]\n", fullPath.c_str(), contentType.c_str());
    //-- Send the file with the correct content type
    server.streamFile(file, contentType);
    file.close();
    return;
  }
  else 
  {
    server.send(404, "text/plain", "File Not Found");
  }

} // handleFileRequest()


String readSystemHtml(const char* htmlFile)
{
  //-- First try with systemPath if available
  std::string sysPath = fsManager.getSystemFilePath();
  File file;
  
  if (!sysPath.empty()) 
  {
    std::string fullPath = sysPath + htmlFile;
    file = LittleFS.open(fullPath.c_str(), "r");
  }
  
  //-- If file not found with systemPath or systemPath is empty, try original path
  if (!file) 
  {
    file = LittleFS.open(htmlFile, "r");
  }
  
  if (!file) 
  {
    Serial.println("Failed to open file for reading");
    //-- Return empty string if file not found
    return String();  
  }

  String fileContent;
  while (file.available()) 
  {
    //-- Read byte by byte and append to String
    fileContent += (char)file.read();  
  }

  file.close();
  return fileContent;
  
} // readSystemHtml()


void setup()
{
    Serial.begin(115200);

    //-- Initialize WiFiManager
    wifiManager.autoConnect("fancyFSM-AP");

    LittleFS.begin();

    fsManager.begin(&Serial);
    fsManager.addSystemFile("/index.html");
    fsManager.addSystemFile("/favicon.ico");

    fsManager.setSystemFilePath("/fancyFSM");
    fsManager.addSystemFile(fsManager.getSystemFilePath() + "/fancyFSM.html");
    fsManager.addSystemFile(fsManager.getSystemFilePath() + "/fancyFSM.js");
    fsManager.addSystemFile(fsManager.getSystemFilePath() + "/fancyFSM.css");

    server.on("/", HTTP_GET, []() {
        server.send(200, "text/html", readSystemHtml("/fancyFSM.html"));
    });
    server.onNotFound([]() {
      Serial.printf("Not Found: %s\n", server.uri().c_str());
      server.send(404, "text/plain", "404 Not Found");
    });

    server.begin();
    Serial.println("Webserver started!");
}

void loop()
{
    server.handleClient();
}

Dit voorbeeld gaat er van uit dat er drie bestanden in de map fancyFSM staan

  • fancyFSM.html
  • fancyFSM.css
  • fancyFSM.js

Examples

Alle in deze post gepresenteerde code staat in de examples folder van de github repository.

Webinterface API-endpoints

De FSmanager stelt de volgende HTTP-endpoints in:

EndpointMethodeBeschrijving
/fsm/filelistGETLijst met bestanden in een map ophalen
/fsm/deletePOSTBestand verwijderen
/fsm/uploadPOSTBestand uploaden
/fsm/downloadGETBestand downloaden
/fsm/checkSpaceGETControleren of er voldoende ruimte is voor een upload
/fsm/createFolderPOSTNieuwe map aanmaken
/fsm/deleteFolderPOSTMap verwijderen
This entry was posted in Uncategorised and tagged , , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

The maximum upload file size: 4 MB. You can upload: image, other. Links to YouTube, Facebook, Twitter and other services inserted in the comment text will be automatically embedded. Drop file here

This site uses Akismet to reduce spam. Learn how your comment data is processed.