Micro-SD File Server mit D1 Mini

Dieser Beitrag beschreibt einen Webserver der über einen D1-Mini den Inhalt einer Micro SD Card zum Download zur Verfügung stellt. Als Kartenleser kommt ein Micro-Speicherkartenmodul zum Einsatz. Die Hardware ist ganz einfach. Kartenlese-Modul und D1-Mini werden über den SPI Bus verbunden. Siehe Schaltung.

Schaltung:

 

STL-Dateien zum Drucken eines passenden Gehäuses findet ihr auf meinem Thinigiverse Profil

 

Sketch:

 

/*
 * Copyright (c) 2015, Majenko Technologies
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * 
 * * Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 * 
 * * Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 * 
 * * Neither the name of Majenko Technologies nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/* Wir erzeugen einen Web-Server als Access Point, der den Inhalt einer Micro SD Card
 zuzm Download bereitstellt.
 Für die SD-Card werden die SPI -Pins benutzt
 D5 = GPIO14 als Clock
 D6 = GPIO12 als MISO
 D7 = GPIO13 als MOSI
 D8 = GPIO15 als Chip Select
  
 die verwendete SSID = SD_Server
 das Passwort = Micro
   
*/

//Wifi Bibliothek + Client + Web-Server
#include <ESP8266WiFi.h>
#include <WiFiClient.h> 
#include <ESP8266WebServer.h>
//Bibliothek sür SPI Bus
#include <SPI.h>
//Bibliothek für SD-Card Filesystem
#include <SdFat.h>

const uint8_t chipSelect = SS;


//Template für die HTML-Seite
const char HTML_HEADER[] =
"<! DOCTYPE HTML>"
"<html>"
"<head>"
"<meta name = \"viewport\" content = \"width = device-width, initial-scale = 1.0, maximum-scale = 1.0, user-scalable=0\">"
"<title>SD-Card reader</title>"
"<style>"
"body { background-color: #d2f3eb; font-family: Arial, Helvetica, Sans-Serif; Color: #000000;font-size:12pt; }"
"</style>"
"</head>"
"<body><div style='margin-left:30px;' >";
const char HTML_END[] =
"</div></body>"
"</html>";

//Globale Variablen
//Instanz der SdFat Bibliothek
 SdFat sd;
 //Globale Objekt Variablen zum Speichern von Files und Directories
 SdFile file;
 SdFile dirFile;
//Access Point
const char *ssid = "SD_Server"; //Name des WLAN 
const char *password = "Micro"; //Passwort für das WLAN
//Flag für die SD-Card Initialisierung
bool sdinit = false;



ESP8266WebServer server(80); //Web-Server starten auf Port 80

//Funktion zum Ermitteln des ContentTypes je nach Datei-Endung
String getContentType(String filename){   if(server.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(".png")) Return "image/png";   else If(Filename.Endswith(".gif")) Return "image/gif";   else If(Filename.Endswith(".jpg")) Return "image/word-------------;   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";   Return "text/plain";
}

Function to send a file
Path and File Name parameter
Bool Sendfile(String Path, String Fn) {   Char cpath[512];   uint32_t Filesize;   String Contenttype;   Char Cname[256];   File Myfile;   Create the full file name    and resave to a character array   Path = Path+"/"+Fn;   Path.Tochararray(cpath,512);   Convert file names to lowercase   to determine the file type   Fn.Tolowercase();   Contenttype = getContentType(Fn);   Open file on sD card   Myfile = Sd.Open(Path, O_READ);   and stream to the web server   Server.streamFile(Myfile, Contenttype);   Close file   File.Close();   Return True;

}

Send a file directory
Bool sendDirectory(String Path) {   Char cpath[512];   uint16_t N = 0;   Char Cname[256];   String Subdir;   String Parent;   String Name;      Path.Tochararray(cpath,512);   We're trying to open the path   If (dirFile.Open(cpath, O_READ)) {     the action is successful we set the content length to unknown     Server.setContentLength(CONTENT_LENGTH_UNKNOWN);     and send the header     Server.send(200,  "text/html",HTML_HEADER);     WiFiClient Client = Server.Client();     If (Path != "/") {       if the path does not show on the root directory, we add a       Line with ".." to be able to go back a level       Parent = Path;       we need the higher-level directory       Parent.Remove(Parent.Lastindexof("/"));       and create a link from it, which we send to the client       Server.sendContent("<a href='http://192.168.4.1/?DIR=");       Server.sendContent(Parent);       Server.sendContent("'>.. </a><br>");     }     now follow the lines for the directory entries     while (File.openNext(&dirFile, O_READ)) {       at the beginning of the link we set the path       Server.sendContent("<a href='http://192.168.4.1/?DIR=");       Server.sendContent(Path);       Read Filename       File.Getname(Cname,255);         Name = String(Cname);       If (File.isDir()) {         if the entry is a subdirectory         we attach this to the path and close the link         Subdir = "/"+Name;         Server.sendContent(Subdir);          Server.sendContent("'>");            } else {         it is a simple file we add the file name         Server.sendContent("&FN=");         Server.sendContent(Name);         to the link we add "target=''" so that the file         opens in a separate window.         Then we close the link         Server.sendContent("' target=''>");       }       finally, the name follows as a label for the link       Server.sendContent(Name);       and the end of the link as well as a line feed       Server.sendContent("</a><br>");      Close file      File.Close();    }    After all entries have been sent, the directory is closed    dirFile.Close();    Communicate the end of the content to the client and terminate the connection    Server.sendContent(HTML_END);    client.Stop();    return true;   } else {     return false;// error the path could not be opened   }

}
// This function is called when the request to the web server = "/"
void handleRoot() {   // Fill the file name and path with default values   String path = "/";   String fn = "";   // If the request contains appropriate arguments   // Fill the path and file name with the data of the request   if (server.hasArg("TO YOU")) path = server.bad("TO YOU");   if (server.hasArg("FN")) fn = server.bad("FN");   Serial.print("Path");Serial.print(path);Serial.print("File:");Serial.println(fn);   String Surname;   // If the SD card has not yet been initialized, initialize it   if (!sdinit) sdinit = sd.begin(chipSelect, SD_SCK_MHZ(50));   if (sdinit) {     // If the initialization was successful, we read data from the card     if (fn=="") {       // If no file name was given we try to send a directory       if (!sendDirectory(path)) {         // If the directory could not be sent, we set sdinit to false         // Since there is apparently no card in the reader. We send a warning to the client         sdinit = false;         server.send(200, "text / html", "No SD card");       }     } else {       // Otherwise we try to send the specified file       if (!sendFile(path,fn)) {         // If the directory could not be sent, we set sdinit to false         // Since there is apparently no card in the reader. We send a warning to the client         sdinit = false;         server.send(200, "text / html", "No SD card");       }     }   } else {     // Initialization unsuccessful     server.send(200, "text / html", "No SD card");   }
}

// Prepare the processor
void set up() {   Serial.begin(115200);   Serial.println();   Serial.print("Configuring access point ...");   // Set up the access point   WiFi.softAP(ssid, password);   IPAddress myIP = WiFi.softAPIP();   Serial.print("AP IP address:");   Serial.println(myIP);   // ip is always 192.168.4.1   // Definition of the response functions   server.on("/", handleRoot);   // start web server   server.begin();   Serial.println("HTTP server started");
}

// main loop
void loop() {   // Check for request   server.handleClient();
}

 

 

I hope this post was helpful and I am always happy to receive comments and feedback. Until then, have a nice weekend!

Esp-8266Projects for beginners

10 comments

Lompe

Lompe

Postscriptum:

Am PC mit dem Chrome-Browser geht es auch nicht.
Auch nicht, wenn die Anzeige unsicherer Seiten zu gelassen ist.

Lompe

Lompe

Funktioniert einwandfrei auf dem WIN 10 pc im Browser (Firefox).
Aber nicht auf dem Android Smartphone wenn über den Access Point aufgerufen. Die Routinen werden abgearbeitet (Serial-Monitor der Arduino IDE zur Kontrolle) und die Seiten gesendet, aber auf dem Bildschirm des Smartphones erscheint nichts.

fritzoskar

fritzoskar

Gibt es einen funktionierenden Sketsch?
Grüße

Henkel

Henkel

Welche SDFat Library braucht man? Mit der hier geht es nicht!
https://github.com/greiman/SdFat

Bibliothek 1. ESP8266 2.6.3
Hardwareziel Wemos D1 Mini
Arduino 1.8.12
Fehlermeldungen:

In file included from L:\arduino-1.8.12\portable\sketchbook\libraries\SdFat\src/FatLib/FatLib.h:27:0,

from L:\arduino-1.8.12\portable\sketchbook\libraries\SdFat\src/SdFat.h:33, from L:\arduino-1.8.12\portable\sketchbook\ESP8266WebserverSD\ESP8266WebserverSD\ESP8266WebserverSD.ino:51:

L:\arduino-1.8.12\portable\sketchbook\libraries\SdFat\src/FatLib/ArduinoFiles.h:122:7: error: redefinition of ‘class fs::File’

class File : public FatFile, public Stream { ^

In file included from L:\arduino-1.8.12\portable\packages\esp8266\hardware\esp8266\2.6.3\libraries\ESP8266WiFi\src/CertStoreBearSSL.h:26:0,

from L:\arduino-1.8.12\portable\packages\esp8266\hardware\esp8266\2.6.3\libraries\ESP8266WiFi\src/WiFiClientSecureBearSSL.h:30, from L:\arduino-1.8.12\portable\packages\esp8266\hardware\esp8266\2.6.3\libraries\ESP8266WiFi\src/WiFiClientSecure.h:41, from L:\arduino-1.8.12\portable\packages\esp8266\hardware\esp8266\2.6.3\libraries\ESP8266WiFi\src/WiFiServerSecure.h:20, from L:\arduino-1.8.12\portable\packages\esp8266\hardware\esp8266\2.6.3\libraries\ESP8266WiFi\src/ESP8266WiFi.h:41, from L:\arduino-1.8.12\portable\sketchbook\ESP8266WebserverSD\ESP8266WebserverSD\ESP8266WebserverSD.ino:45:

L:\arduino-1.8.12\portable\packages\esp8266\hardware\esp8266\2.6.3\cores\esp8266/FS.h:52:7: error: previous definition of ‘class fs::File’

class File : public Stream ^

In file included from L:\arduino-1.8.12\portable\sketchbook\libraries\SdFat\src/FatLib/FatLib.h:28:0,

from L:\arduino-1.8.12\portable\sketchbook\libraries\SdFat\src/SdFat.h:33, from L:\arduino-1.8.12\portable\sketchbook\ESP8266WebserverSD\ESP8266WebserverSD\ESP8266WebserverSD.ino:51:

L:\arduino-1.8.12\portable\sketchbook\libraries\SdFat\src/FatLib/FatFileSystem.h: In member function ‘fs::File FatFileSystem::open(const char*, oflag_t)’:

L:\arduino-1.8.12\portable\sketchbook\libraries\SdFat\src/FatLib/FatFileSystem.h:95:13: error: ‘class fs::File’ has no member named ‘open’

tmpFile.open(vwd(), path, oflag); ^

Mehrere Bibliotheken wurden für “SdFat.h” gefunden
Benutzt: L:\arduino-1.8.12\portable\sketchbook\libraries\SdFat
Nicht benutzt: L:\arduino-1.8.12\portable\packages\esp8266\hardware\esp8266\2.6.3\libraries\ESP8266SdFat
exit status 1
Fehler beim Kompilieren für das Board LOLIN D1 R2 & mini.

Moritz

Moritz

That means i can serve files of the sd card? That would be nice even if it is slow!

jean-marie

jean-marie

Guten Abend, ich mache alle Projekte für Ihre Website.
Sie sind großartig, aber ich habe ein Problem mit der SDFAT-Bibliothek. Ich habe den Fehler “‘SdFat’ benennt keinen Typ”.
Kannst du mir helfen?
Ich habe schon im Netz gesucht aber ….
++

jm

Gerald

Gerald

Sorry, ich hatte das Programm vergessen. Ist jetzt dabei.

michael

michael

Hallo Gerald,
hast Du dafür kein Programm?
Grüße michael

michael

michael

Hallo, wo ist das Programm dazu?

Frank Eisenwiener

Frank Eisenwiener

Danke erstmal für die anregenden Projekte!

Irgendwie fehlt mir persönlich bei diesem hier aber die Software (oder habe ich was überlesen?)…

Gruß
Frank

Leave a comment

All comments are moderated before being published

Recommended blog posts

  1. Install ESP32 now from the board manager
  2. Lüftersteuerung Raspberry Pi
  3. Arduino IDE - Programmieren für Einsteiger - Teil 1
  4. ESP32 - das Multitalent
  5. OTA - Over the Air - ESP programming via WLAN