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 SD-Card Bibliotheken für die Arduino IDE finden Sie auf der verlinkten Produktseite.
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/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"; return "text/plain"; } //Funktion zum Senden einer Datei //Parameter Pfad und Dateiname bool sendFile(String path, String fn) { char cpath[512]; uint32_t filesize; String contentType; char cname[256]; File myfile; //Erstellen des vollständigen Dateinamens //und umspeichern in ein Character-Array path = path+"/"+fn; path.toCharArray(cpath,512); //Filenamen in Kleinbuchstaben umwandeln //zur Bestimmung des Dateityps fn.toLowerCase(); contentType = getContentType(fn); //File auf der SD card öffnen myfile = sd.open(path, O_READ); //und in den Web-Server streamen server.streamFile(myfile, contentType); //File schließen file.close(); return true; } //Ein Dateiverzeichnis senden bool sendDirectory(String path) { char cpath[512]; uint16_t n = 0; char cname[256]; String subdir; String parent; String name; path.toCharArray(cpath,512); //Wir versuchen den Pfad zu öffnen if (dirFile.open(cpath, O_READ)) { //ist die Aktion erfolgreich setzen wir die Contentlänge auf unbekannt server.setContentLength(CONTENT_LENGTH_UNKNOWN); //und senden den Header server.send(200, "text/html",HTML_HEADER); WiFiClient client = server.client(); if (path != "/") { //wenn der Pfad nicht auf das Rootverzeichnis zeigt fügen wir eine //Zeile mit ".." ein um eine Ebene zurück gehen zu können parent = path; //wir benötigen das übergeordnete Verzeichnis parent.remove(parent.lastIndexOf("/")); //und bilde daraus einen Link, den wir an den Client senden server.sendContent("<a href='http://192.168.4.1/?DIR="); server.sendContent(parent); server.sendContent("'>..</a><br>"); } //nun folgen die Zeilen für die Verzeichniseinträge while (file.openNext(&dirFile, O_READ)) { //am Anfang des Links setzen wir den Pfad server.sendContent("<a href='http://192.168.4.1/?DIR="); server.sendContent(path); //Filename lesen file.getName(cname,255); name = String(cname); if (file.isDir()) { //wenn der Eintrag ein Unterverzeichnis ist //hängen wir dieses an den Pfad an und schließen den Link subdir = "/"+name; server.sendContent(subdir); server.sendContent("'>"); } else { //ist es ein einfaches File fügen wir den Filenamen an server.sendContent("&FN="); server.sendContent(name); //zum Link addieren wir noch "target=''" damit das File //in einem eigenen Fenster geöffnet wird. //Dann schließen wir den Link server.sendContent("' target=''>"); } //zum Schluss folgt der Name als Label für den Link server.sendContent(name); //und das Ende vom Link sowie ein Zeilenvorschub server.sendContent("</a><br>"); //File schließen file.close(); } //Nachdem alle Einträge gesendet wurden, wird das Direktory geschlossen dirFile.close(); //Das Contentende dem Client mitteilen und die Verbindung beenden server.sendContent(HTML_END); client.stop(); return true; } else { return false;//Fehler der Pfad konnte nicht geöffnet werden } } //Diese Funktion wird aufgerufen wenn der Request an den Web-Server = "/" ist void handleRoot() { //Filename und Pfad mit Defaultwerten füllen String path = "/"; String fn = ""; //Falls der Request entsprechende Argumente enthält //Pfad und Filename mit den Daten des Requests füllen if (server.hasArg("DIR")) path = server.arg("DIR"); if (server.hasArg("FN")) fn = server.arg("FN"); Serial.print("Path ");Serial.print(path);Serial.print(" File: ");Serial.println(fn); String name; //Wenn die SD Card noch nicht initialisiert wurde diese initialisieren if (!sdinit) sdinit = sd.begin(chipSelect, SD_SCK_MHZ(50)); if (sdinit) { //War die Initialisierung erfolgreich, lesen wir Daten von der Karte if (fn=="") { //Wenn kein Filename angegeben wurde versuchen wir ein Verzeichnis zu senden if (!sendDirectory(path)) { //Konnte das Verzeichnis nicht gesendet werden setzen wir sdinit auf false //Da offenbar keine Karte im Reader ist. Wir senden an den Client eine entsprechende Warnung sdinit = false; server.send(200, "text/html", "Keine SD Karte"); } } else { //Ansonsten versuchen wir das angegebene File zu senden if (!sendFile(path,fn)) { //Konnte das Verzeichnis nicht gesendet werden setzen wir sdinit auf false //Da offenbar keine Karte im Reader ist. Wir senden an den Client eine entsprechende Warnung sdinit = false; server.send(200, "text/html", "Keine SD Karte"); } } } else { //Initialisierung nicht erfolgreich server.send(200, "text/html", "Keine SD Karte"); } } //Prozessor vorbereiten void setup() { Serial.begin(115200); Serial.println(); Serial.print("Configuring access point..."); //Access Point einrichten WiFi.softAP(ssid, password); IPAddress myIP = WiFi.softAPIP(); Serial.print("AP IP address: "); Serial.println(myIP); //ip ist immer 192.168.4.1 //Definition der Antwortfunktionen server.on("/", handleRoot); //Web Server starten server.begin(); Serial.println("HTTP server started"); } //Hauptschleife void loop() { //Auf Request prüfen server.handleClient(); }
Ich hoffe dieser Beitrag war hilfreich und freue mich wie immer über Kommentare und Feedback. Bis dahin noch ein schönes Wochenende!
19 Kommentare
Andreas Wolter
@Andreas: die Libraries wurden teilweise komplett in den ESP Core integriert und/oder nicht weiter gepflegt. Ich würde daher versuchen, es komplett anders aufzubauen.
Wenn die Zeit es zulässt, werden wir versuchen, den Beitrag zu aktualisieren.
Grüße,
Andreas Wolter
AZ-Delivery Blog
Andreas
Hallo,
ich habe mich heute Abend auch damit abgekämpft, diesen Sketch zum Laufen zu bringen. Auch ich bekomme die Fehlermeldung
‘operator=’ (operad types are ‘fs::File’ and ‘FsFile’)
Wenn ich es recht verstanden habe, kommt mit dem ESP8662-Library Paket auch eine FS.h, auf die dann von den anderen Libraries aus dem Paket zugegriffen wird. Und nun ist aber in unterschiedlichen Libraries der gleiche Ausdruck “File”, mit dem man die Variable “myfile” deklariert, vorhanden. Der Compiler weiß nun nicht, welche Deklaration aus welcher Library er für “File” verwenden soll. Es ist wohl ein Problem des ESP-Library-Pakets, das anscheinend auch nicht mehr gepflegt wird – jedenfalls hat ESP8266WiFi bei mir noch die Versionsnummer 1.0
Wie man das nun abstellt bzw. den Konflikt behebt, damit kenne ich mich leider zu wenig aus und bin auch aus den wenigen Foreneinträgen, die ich zu ähnlichen Problemen gefunden habe, nicht wirklich schlau geworden bzw. die Lösungscorschläge haben nicht funktioniert.
Markus
Hallo,
ich wäre auch an einer aktuellen Version interessiert. Leider sind meine eigenen Versuche bisher gescheitert :-( .
Grüße
Markus
Andreas Wolter
aus Zeitgründen aktuell noch nicht.
Vielleicht finden SIe auch selbst eine Lösung, die wir dann hier zeigen können. Es gibt sehr viele neue und alte Bibliotheken, die zusammen funktionieren müssen.
Grüße,
Andreas Wolter
Markus
Hallo,
gibt es schon einen neuen Sketch mit der SD Bibliothek?
Grüße
Markus
Andreas Wolter
ich habe mir das noch einmal angesehen. Es scheint, als wäre die Bibliothek und damit auch der Sketch in diesem Beitrag outdated. Wenn man den aktuellen ESP8266 Arduino Core installiert, wird dort die SD Bibliothek mit installiert. Außerdem die SDFAT Bibliothek. Beides scheint sich nicht zu vertragen. Wenn man nun noch versucht, manuell die angegebenen Bibliotheken zu installieren, geht das komplett in die Hose. Ich werde versuchen, mit der SD Bibliothek den Sketch zu aktualisieren. Das wird allerdings ein bisschen dauern.
Grüße,
Andreas Wolter
AZ-Delivery Blog
Norbert
Danke Herr Wolter für die schnelle Antwort.
Ich habe leider immer noch keinen Erfolg.
Die entsprechenden Bibliotheken (Ihr angegebener Link) habe ich installiert.
Leider kommt beim Kompilieren In Zeile 124 : " myfile = sd.open(path, O_READ);"
der Fehler " no match for ‘operator=’ (operad types are ‘fs::File’ and ‘FsFile’)
Ich verwende Arduino IDE 1.8.15
Ich würde gern den Micro-SD File Server nachbauen!
Könnten Sie mir bitte noch einen Tipp geben wo der Fehler liegen könnte.
Leider bin ich Anfänger und kann mit den Fehlermeldungen wenig anfangen.
Ich bedanke mich für Ihre Bemühungen
und verbleibe mit freundlichen Grüßen
Norbert
Im Fehlerfenster erscheint folgendes in Rot:
C:\Users\norbe\Desktop\Stromzähler – Messwerterfassung\PC und WEB-Server\Versuch3\Versuch3.ino: In function ‘bool sendFile(String, String)’:
Versuch3:81:32: error: no match for ‘operator=’ (operand types are ‘fs::File’ and ‘FsFile’)
In file included from C:\Users\norbe\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.0.2\libraries\ESP8266WiFi\src/CertStoreBearSSL.h:26,
from C:\Users\norbe\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.0.2\libraries\ESP8266WiFi\src/WiFiClientSecureBearSSL.h:30,
from C:\Users\norbe\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.0.2\libraries\ESP8266WiFi\src/WiFiClientSecure.h:23,
from C:\Users\norbe\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.0.2\libraries\ESP8266WiFi\src/WiFiServerSecure.h:20,
from C:\Users\norbe\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.0.2\libraries\ESP8266WiFi\src/ESP8266WiFi.h:41,
from C:\Users\norbe\Desktop\Stromzähler – Messwerterfassung\PC und WEB-Server\Versuch3\Versuch3.ino:2:
C:\Users\norbe\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.0.2\cores\esp8266/FS.h:52:7: note: candidate: ‘fs::File& fs::File::operator=(const fs::File&)’
52 | class File : public Stream
| ^~~~
C:\Users\norbe\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.0.2\cores\esp8266/FS.h:52:7: note: no known conversion for argument 1 from ‘FsFile’ to ‘const fs::File&’
C:\Users\norbe\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.0.2\cores\esp8266/FS.h:52:7: note: candidate: ‘fs::File& fs::File::operator=(fs::File&&)’
C:\Users\norbe\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.0.2\cores\esp8266/FS.h:52:7: note: no known conversion for argument 1 from ‘FsFile’ to ‘fs::File&&’
Bibliothek ESP8266WiFi in Version 1.0 im Ordner: C:\Users\norbe\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.0.2\libraries\ESP8266WiFi wird verwendet
Bibliothek ESP8266WebServer in Version 1.0 im Ordner: C:\Users\norbe\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.0.2\libraries\ESP8266WebServer wird verwendet
Bibliothek SPI in Version 1.0 im Ordner: C:\Users\norbe\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.0.2\libraries\SPI wird verwendet
Bibliothek SdFat-2.1.1 in Version 2.1.1 im Ordner: C:\Users\norbe\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.0.2\libraries\SdFat-2.1.1 wird verwendet
exit status 1
no match for ‘operator=’ (operand types are ‘fs::File’ and ‘FsFile’)
Andreas Wolter
Die SD-Card Bibliotheken finden Sie als Download auf der verlinkten Produktseite des SD-Card Moduls: https://cdn.shopify.com/s/files/1/1509/1638/files/Micro_Memory_Card_Arduino.rar?9306531954326920108
Wir ergänzen das im Beitrag
Andreas Wolter
AZ-Delivery Blog
Norbert Patschorke
Hallo,
ich möchte es gern nachbauen.
Aber ich finde nirgends die passende SDFAT-Bibliothek.
Bitte gebt mir eine Hinweis (Link).
Gruß Norbert
Lompe
Postscriptum:
Am PC mit dem Chrome-Browser geht es auch nicht.
Auch nicht, wenn die Anzeige unsicherer Seiten zu gelassen ist.
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
Gibt es einen funktionierenden Sketsch?
Grüße
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
That means i can serve files of the sd card? That would be nice even if it is slow!
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
Sorry, ich hatte das Programm vergessen. Ist jetzt dabei.
michael
Hallo Gerald,
hast Du dafür kein Programm?
Grüße michael
michael
Hallo, wo ist das Programm dazu?
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