[Erledigt] ESP8266 NodeMCU - SD Card Server

Hallo :slight_smile:

Vielleicht habt ihr einen Rat f√ľr mich. Ich wollte mal folgendes Beispiel aus der ESP8266WebServer-Library ausprobieren:

https://github.com/esp8266/Arduino/tree/master/libraries/ESP8266WebServer/examples/SDWebServer

Das SD-Karten-Modul ist wie folgt angeschlossen:

CS => D8
MISO => D6
MOSI => D7
SCK => D5

Solange keine SD-Karte im Slot steckt, kann ich den Sketch hochladen und nach dem Reset meldet sich der WebServer √ľber den seriellen Monitor einwandfrei und startklar. Beim Aufruf via Browser kommt nat√ľrlich der Hinweis, dass keine SD-Karte gefunden wurde.

Sobald ich eine SD-Karte (1 GB, FAT16) einstecke, funktioniert nichts mehr. Ich kann den Sketch z. B. nicht hochladen (Fehlermeldung: esptool.FatalError: Failed to connect to ESP8266: Timed out waiting for packet Header), oder wenn ich nach einem Reset mit eingelegter Karte mir den seriellen Monitor anschaue, steht das nur Zeichenwirrwarr.

Ich habe gerade keine Ahnung, was ich falsch mache. Hab ihr einen Tipp f√ľr mich?

Herzlichen Dank schonmal und einen schönen Sonntag noch
Schattenfänger

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <SPI.h>
#include <SD.h>

#define DBG_OUTPUT_PORT Serial

#ifndef STASSID
#define STASSID "Geheim"
#define STAPSK  "Geheim"
#endif

const char* ssid = STASSID;
const char* password = STAPSK;
const char* host = "test";

ESP8266WebServer server(80);

static bool hasSD = false;
File uploadFile;


void returnOK() {
  server.send(200, "text/plain", "");
}

void returnFail(String msg) {
  server.send(500, "text/plain", msg + "\r\n");
}

bool loadFromSdCard(String path) {
  String dataType = "text/plain";
  if (path.endsWith("/")) {
    path += "index.htm";
  }

  if (path.endsWith(".src")) {
    path = path.substring(0, path.lastIndexOf("."));
  } else if (path.endsWith(".htm")) {
    dataType = "text/html";
  } else if (path.endsWith(".css")) {
    dataType = "text/css";
  } else if (path.endsWith(".js")) {
    dataType = "application/javascript";
  } else if (path.endsWith(".png")) {
    dataType = "image/png";
  } else if (path.endsWith(".gif")) {
    dataType = "image/gif";
  } else if (path.endsWith(".jpg")) {
    dataType = "image/jpeg";
  } else if (path.endsWith(".ico")) {
    dataType = "image/x-icon";
  } else if (path.endsWith(".xml")) {
    dataType = "text/xml";
  } else if (path.endsWith(".pdf")) {
    dataType = "application/pdf";
  } else if (path.endsWith(".zip")) {
    dataType = "application/zip";
  }

  File dataFile = SD.open(path.c_str());
  if (dataFile.isDirectory()) {
    path += "/index.htm";
    dataType = "text/html";
    dataFile = SD.open(path.c_str());
  }

  if (!dataFile) {
    return false;
  }

  if (server.hasArg("download")) {
    dataType = "application/octet-stream";
  }

  if (server.streamFile(dataFile, dataType) != dataFile.size()) {
    DBG_OUTPUT_PORT.println("Sent less data than expected!");
  }

  dataFile.close();
  return true;
}

void handleFileUpload() {
  if (server.uri() != "/edit") {
    return;
  }
  HTTPUpload& upload = server.upload();
  if (upload.status == UPLOAD_FILE_START) {
    if (SD.exists((char *)upload.filename.c_str())) {
      SD.remove((char *)upload.filename.c_str());
    }
    uploadFile = SD.open(upload.filename.c_str(), FILE_WRITE);
    DBG_OUTPUT_PORT.print("Upload: START, filename: "); DBG_OUTPUT_PORT.println(upload.filename);
  } else if (upload.status == UPLOAD_FILE_WRITE) {
    if (uploadFile) {
      uploadFile.write(upload.buf, upload.currentSize);
    }
    DBG_OUTPUT_PORT.print("Upload: WRITE, Bytes: "); DBG_OUTPUT_PORT.println(upload.currentSize);
  } else if (upload.status == UPLOAD_FILE_END) {
    if (uploadFile) {
      uploadFile.close();
    }
    DBG_OUTPUT_PORT.print("Upload: END, Size: "); DBG_OUTPUT_PORT.println(upload.totalSize);
  }
}

void deleteRecursive(String path) {
  File file = SD.open((char *)path.c_str());
  if (!file.isDirectory()) {
    file.close();
    SD.remove((char *)path.c_str());
    return;
  }

  file.rewindDirectory();
  while (true) {
    File entry = file.openNextFile();
    if (!entry) {
      break;
    }
    String entryPath = path + "/" + entry.name();
    if (entry.isDirectory()) {
      entry.close();
      deleteRecursive(entryPath);
    } else {
      entry.close();
      SD.remove((char *)entryPath.c_str());
    }
    yield();
  }

  SD.rmdir((char *)path.c_str());
  file.close();
}

void handleDelete() {
  if (server.args() == 0) {
    return returnFail("BAD ARGS");
  }
  String path = server.arg(0);
  if (path == "/" || !SD.exists((char *)path.c_str())) {
    returnFail("BAD PATH");
    return;
  }
  deleteRecursive(path);
  returnOK();
}

void handleCreate() {
  if (server.args() == 0) {
    return returnFail("BAD ARGS");
  }
  String path = server.arg(0);
  if (path == "/" || SD.exists((char *)path.c_str())) {
    returnFail("BAD PATH");
    return;
  }

  if (path.indexOf('.') > 0) {
    File file = SD.open((char *)path.c_str(), FILE_WRITE);
    if (file) {
      file.write((const char *)0);
      file.close();
    }
  } else {
    SD.mkdir((char *)path.c_str());
  }
  returnOK();
}

void printDirectory() {
  if (!server.hasArg("dir")) {
    return returnFail("BAD ARGS");
  }
  String path = server.arg("dir");
  if (path != "/" && !SD.exists((char *)path.c_str())) {
    return returnFail("BAD PATH");
  }
  File dir = SD.open((char *)path.c_str());
  path = String();
  if (!dir.isDirectory()) {
    dir.close();
    return returnFail("NOT DIR");
  }
  dir.rewindDirectory();
  server.setContentLength(CONTENT_LENGTH_UNKNOWN);
  server.send(200, "text/json", "");
  WiFiClient client = server.client();

  server.sendContent("[");
  for (int cnt = 0; true; ++cnt) {
    File entry = dir.openNextFile();
    if (!entry) {
      break;
    }

    String output;
    if (cnt > 0) {
      output = ',';
    }

    output += "{\"type\":\"";
    output += (entry.isDirectory()) ? "dir" : "file";
    output += "\",\"name\":\"";
    output += entry.name();
    output += "\"";
    output += "}";
    server.sendContent(output);
    entry.close();
  }
  server.sendContent("]");
  dir.close();
}

void handleNotFound() {
  if (hasSD && loadFromSdCard(server.uri())) {
    return;
  }
  String message = "SDCARD Not Detected\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";
  for (uint8_t i = 0; i < server.args(); i++) {
    message += " NAME:" + server.argName(i) + "\n VALUE:" + server.arg(i) + "\n";
  }
  server.send(404, "text/plain", message);
  DBG_OUTPUT_PORT.print(message);
}

void setup(void) {
  DBG_OUTPUT_PORT.begin(115200);
  DBG_OUTPUT_PORT.setDebugOutput(true);
  DBG_OUTPUT_PORT.print("\n");
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  DBG_OUTPUT_PORT.print("Connecting to ");
  DBG_OUTPUT_PORT.println(ssid);

  // Wait for connection
  uint8_t i = 0;
  while (WiFi.status() != WL_CONNECTED && i++ < 20) {//wait 10 seconds
    delay(500);
  }
  if (i == 21) {
    DBG_OUTPUT_PORT.print("Could not connect to");
    DBG_OUTPUT_PORT.println(ssid);
    while (1) {
      delay(500);
    }
  }
  DBG_OUTPUT_PORT.print("Connected! IP address: ");
  DBG_OUTPUT_PORT.println(WiFi.localIP());

  if (MDNS.begin(host)) {
    MDNS.addService("http", "tcp", 80);
    DBG_OUTPUT_PORT.println("MDNS responder started");
    DBG_OUTPUT_PORT.print("You can now connect to http://");
    DBG_OUTPUT_PORT.print(host);
    DBG_OUTPUT_PORT.println(".local");
  }


  server.on("/list", HTTP_GET, printDirectory);
  server.on("/edit", HTTP_DELETE, handleDelete);
  server.on("/edit", HTTP_PUT, handleCreate);
  server.on("/edit", HTTP_POST, []() {
    returnOK();
  }, handleFileUpload);
  server.onNotFound(handleNotFound);

  server.begin();
  DBG_OUTPUT_PORT.println("HTTP server started");

  if (SD.begin(SS)) {
    DBG_OUTPUT_PORT.println("SD Card initialized.");
    hasSD = true;
  }
}

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

Welchen ESP8266 verwendest Du (Link)? Beim ESP8266 ist es sicherer, mit den GPIO-Nummern zu arbeiten, da die Dx bei verschiedenen Modellen unterschiedlich sein können.

Gruß Tommy

Hallo Tommy,

es ist ein NodeMCU Lua Amica Modul V2 ESP8266 ESP-12E, √ľber Amazon bezogen.

Viele Gr√ľ√üe
Schattenfänger

Du meinst, wir suchen uns das jetzt bei Amazon zusammen? Die Wahrscheinlichkeit daf√ľr geht gegen null.
Wenn Du Hilfe willst, dann gib uns vern√ľnftige Informationen.

Gruß Tommy

Edit: auch einen Link zum SD-Card-Modul

Nein, ich nahm an, dass die alle gleich sind. Aber hier der Link:

Und hier zum SD-Karten-Modul:

Edit: Einen Link zum Hersteller habe ich eben gefunden:
http://www.chinalctech.com/cpzx/Programmer/406.html

Zwischenzeitlich habe ich auch mal die Ports auf der anderen Seite gem√§√ü folgender Pinout-Layout probiert (linke Seite, gr√ľne Bezeichner MOSI, CS, MISO, SCLK - von oben nach unten):

So läuft zwar der Upload, aber danach kommt:
esptool.FatalError: MD5 of file does not match data in flash!

Beste Gr√ľ√üe
Schattenfänger

Gemäß dem mittlerweile auch von Dir gefundenem Pinout hast Du MOSI und MISO vertauscht.

Zum SD-Modul sehe ich leider keine Daten. Es k√∂nnte sein, dass das nur f√ľr 5V ausgelegt ist.
Speist Du 5V oder 3,3V ein?

Gruß Tommy

Edit: Warum willst Du eigentlich SD verwenden und nimmst nicht SPIFFS?

Hi Tommy,

nein, 3,3 V kann es (hatte eben den Link zum Hersteller gefunden und im Post dar√ľber noch eingef√ľgt). Bezieht sich Deine Aussage bzgl. der vertauschten Anschl√ľsse auf den ersten Post?

Ich w√ľrde gern folgendes machen: Daten eines DHT22 auf SD-Karte loggen und diese ggf. die Log-Datei auch mal per WLAN auf den PC ziehen. Das war der Plan.

Edit: ich habe vor einiger Zeit mein Projekt mit einem Arduino und einem Datalogger-Shield realisiert und das l√§uft wunderbar. Aber um an die Daten zu kommen, muss ich immer runter in den Garten rennen (Gartenparzelle hinter einem Miethaus) und wenn ich die SD-Karte entnehme, habe ich ggf. eine L√ľcke in den geloggten Daten. Nun war der Plan, dass mit einem ESP8266 zu machen, da dieser eben die WLAN-Konnektivit√§t hat. Allerdings habe ich mit dem ESP8266 noch nicht so die Erfahrung. Ich habe das Ding erst seit ein paar Tagen und schaue mir die Beispiele an, um mich einzuarbeiten.

Viele Gr√ľ√üe
Schattenfänger

Ja, das bezog sich auf den Anschluss im 1. Posting von Dir.

Du hast WLAN bis in den Garten?

SD-Webserver

Gruß Tommy

Hi Tommy,

ja, dank eines Repeaters. Den Sketch aus Deinem Link habe ich (hatte ich auch oben schon verlinkt). Aber bei eingesteckter SD-Karte läuft gar nichts. Ein Upload auf den Amica NodeMCU ist nicht möglich (s. Fehlermeldung aus dem ersten Post) bzw. nach einem Upload ohne Karte und einem Start ohne Karte läfut es auch. Sobald eine SD-Karte drin steckt, ist Feierabend. :frowning:

Ein Vertauschen der Pins (MISO und MOSI) ändert leider nichts daran. Hast Du noch irgendeine Idee woran es liegen könnte?

Gruß
Schattenfänger

Hast Du mal eine andere SD Karte probiert?

Dein Fehlerbild ist mir leider völlig unbekannt.

Gruß Tommy

Ja, habe ich. Auch ein anderes SD-Card-Modul. Es ist schon komisch. Ich tippe auf die Verkabelung, aber so richtig komme ich da nicht weiter... sobald ich die Verbindung zu CS entferne, läuft der Upload und der Sketch, nur die SD-Karte wird nicht erkannt... :-/

Was mir noch auffällt, Du machst ein SD.begin(SS), aber SS ist nirgendwo deklariert.

Setze doch oben mal ein

#define SS 15

rein.

Gruß Tommy

Hey Tommy!

Das war es! Also fast... benutze ich D8 (also GPIO15), dann blockiert es weiterhin mit der Meldung, dass der COM-Port oder das Board nicht vorhanden wären:

esptool.FatalError: Failed to connect to ESP8266: Timed out waiting for packet Header

Klemme ich aber CS auf z. B. D1 (GPIO5) und definiere dann SS auf 5, dann geht es. Ich versuche morgen mal herauszufinden, warum es mit D8 nicht klappt. Vielleicht finde ich dazu was im Netz. Oder es ist einfach nur eine "Merkw√ľrdigkeit" meines Boards. Denn in den Tutorials zu dem Sketch, wird es eigentlich immer so verkabelt (CS auf D8).

Viele Gr√ľ√üe
Schattenfänger

Vermutlich wäre das der Grund. Teste ich morgen mal.

Schönen Abend noch. :slight_smile:

Ja, das d√ľrfte es sein.

Gruß Tommy

Moin,

mal ein Feedback:

Ich habe den Pin D8 (GPIO15) mit einem 5k-Widerstand auf Masse gezogen, dann funktioniert das auch mit der Verkabelung wie im Eingangspost beschrieben. Vielleicht hilft die Info dem einen oder anderen.

Schönen Tag noch und nochmals herzlichen Dank an Tommy56.

Besten Gruß
Schattenfänger