SD Card Daten loggen und via WEBServer logger-Datei für download anzeigen

Hallo an alle,

ich habe ein Problem mit der Anzeige der Dateien die sich auf der SD Karte befinden.

Dazu habe ich einen Webserver laufen, der alle log-Files auf einer HTML-Seite anzeigt und noch die Momentanwerte einzelner Sensoren:

Datenlog’s auf SD Card /:
LOST.DIR/
ANDROI~1/
DATALOG.TXT
20200112.TXT
Heizungsraum - Sensordaten
Zeitstempel: 2020 1 12 19:40:1
Temp-Kamin = 24.2
Temp-Kessel = 24.2

Will ich nun die Daten der einzelnen Sensoren parallel in ein Log-File schreiben, welches hier zum download auch angezeigt wird, passiert dieses:

Datenlog’s auf SD Card /:
Heizungsraum - Sensordaten
Zeitstempel: 2020 1 12 19:45:17
Temp-Kamin = 24.3
Temp-Kessel = 24.2

Es werden also die Dateien auf der Karte nicht mehr angezeigt.

Ich habe erst Vermutete, dass eine Datei noch offen ist, aber das ist nicht der Fall.

Evtl. habe ich im Sketch einen Denkfehler.

Hier der sketch:

void loop()
{
  
  String t_kamin_old = t_kamin;
  String t_kessel_old = t_kessel;
  t_kamin  = String(operatMax31865_1(), 1); String t_display_kamin  = t_kamin;  t_display_kamin.replace(".",""); 
  t_kessel = String(operatMax31865_2(), 1); String t_display_kessel = t_kessel; t_display_kessel.replace(".","");
  digitalClockDisplay(); Serial.print(" T_Kamin : "); Serial.println(t_kamin);
  digitalClockDisplay(); Serial.print(" T_Kessel: "); Serial.println(t_kessel);
  t_kamin_display.clear(); t_kamin_display.setColonOn(1); t_kamin_display.print(t_display_kamin); t_kamin_display.printRaw(rawData, 3);
  t_kessel_display.clear(); t_kessel_display.setColonOn(1); t_kessel_display.print(t_display_kessel); t_kessel_display.printRaw(rawData, 3);

  char clientline[BUFSIZ];
  char name[17];
  int index = 0;
  
  EthernetClient client = server.available();
  if (client) {
    // an http request ends with a blank line
    boolean current_line_is_blank = true;
    
    // reset the input buffer
    index = 0;
    
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        
        // If it isn't a new line, add the character to the buffer
        if (c != '\n' && c != '\r') {
          clientline[index] = c;
          index++;
          // are we too big for the buffer? start tossing out data
          if (index >= BUFSIZ) 
            index = BUFSIZ -1;
          
          // continue to read more data!
          continue;
        }
        
        // got a \n or \r new line, which means the string is done
        clientline[index] = 0;
        
        // Print it out for debugging
        Serial.println(clientline);
        
        // Look for substring such as a request to get the file
        if (strstr(clientline, "GET /") != 0) {
          // this time no space after the /, so a sub-file!
          char *filename;
          
          filename = clientline + 5; // look after the "GET /" (5 chars)  *******
          // a little trick, look for the " HTTP/1.1" string and 
          // turn the first character of the substring into a 0 to clear it out.
          (strstr(clientline, " HTTP"))[0] = 0;
 
          if(filename[strlen(filename)-1] == '/') {  // Trim a directory filename
            filename[strlen(filename)-1] = 0;        //  as Open throws error with trailing /
          }
          
          Serial.print(F("Web request for: ")); Serial.println(filename);  // print the file we want
 
          File file = SD.open(filename, O_READ);
          if ( file == 0 ) {  // Opening the file with return code of 0 is an error in SDFile.open
            client.println("HTTP/1.1 404 Not Found");
            client.println("Content-Type: text/html");
            client.println();
            client.println("<h2>File Not Found!</h2>");
            client.println("
<h3>Couldn't open the File!</h3>");
            break; 
          }
          
          Serial.println("File Opened!");
                    
          client.println("HTTP/1.1 200 OK");
          if (file.isDirectory()) {
            Serial.println("is a directory");
            client.println("Content-Type: text/html");
            client.println("Connection: close");  // the connection will be closed after completion of the response
            client.println("Refresh: 5");  // refresh the page automatically every 5 sec
            
            client.println();
            client.println("<!DOCTYPE HTML>");
            client.println("<html><head><title>Heizungsraum</title><meta charset=\"UTF-8\" /></head>");
            client.println("<body>");          
            client.print("<h2>Datenlog's auf SD Card /");
            client.print(filename); 
            client.println(":</h2>");
            ListFiles(client,LS_SIZE,file);  
            file.close();  
                    
          } else { // Any non-directory clicked, server will send file to client for download
            client.println("Content-Type: application/octet-stream");
            client.println();
          
            char file_buffer[16];
            int avail;
            while (avail = file.available()) {
              int to_read = min(avail, 16);
              if (to_read != file.read(file_buffer, to_read)) {
                break;
              }
              //Serial.write((char)c);
              client.write(file_buffer, to_read);
            }
            file.close();
          }
        } else {
          // everything else is a 404
          client.println("HTTP/1.1 404 Not Found");
          client.println("Content-Type: text/html");
          client.println();
          client.println("<h2>File Not Found!</h2>");
        }
        client.println("<hr>");
        client.println("<h1>Heizungsraum - Sensordaten</h1>");
        client.println("<h3>Zeitstempel: " + String(year()) + " " + month() + " " + day() + " " + hour() + ":" + minute() + ":" + second() + "</h3>");
        client.println("<ul>");
        client.println("<li>Temp-Kamin  = " + String(t_kamin)  + "</li>");
        client.println("<li>Temp-Kessel = " + String(t_kessel) + "</li>");
        client.println("</ul></body></html>");        
        
        
        break;
      }
    }
    // give the web browser time to receive the data
    delay(100);
    client.stop();
  }
  
  if (t_kamin != t_kamin_old || t_kessel != t_kessel_old) {newdata = true;} else  {newdata = false;}
//die folgende Zeile verursacht das Problem
  //if (sdcardok && timeok && newdata) {writeData();}
  delay(5000);
}

Die Funktion dazu:

void writeData(){
  getFileName();
  String dataString = "";
  dataString = String(year()) + "." + month() + "." + day() + " " + hour() + ":" + minute() + ":" + second() + ";Temp-Kamin;" + String(t_kamin) + ";Temp-Kessel;" + String(t_kessel);
   
  // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.
  File dataFile = SD.open(datafilename, FILE_WRITE);
  
  // if the file is available, write to it:
  if (dataFile) {
    dataFile.println(dataString);
    dataFile.close();
    // print to the serial port too:
    Serial.println(dataString);
  }
  // if the file isn't open, pop up an error:
  else {
    Serial.println("Fehler bei öffnen datalog.txt");
  }
}

Ich hoffe es kann jemand einen Lösungsansatz finden.

Aus den Fragmenten wird das wohl schwierig werden. Warum gibst Du uns nicht den vollständigen Code?
Wenn der zu lang ist, kannst Du den auch als ZIP-File anhängen.

Gruß Tommy

bitte lade einen ganzen kompilierbaren Sketch hoch.

Zeige auch die Serial-Ausgabe bei aktivierter Funktion.
Markiere uns, wann das Ereignis eintritt, das in die Funktion verzweigen soll (eine zusätzliche Debug.Ausgabe könnte dir dabei helfen).

Ich habe erst Vermutete, dass eine Datei noch offen ist, aber das ist nicht der Fall.

das wird im Code aber nicht geprüft. Warum tust du das nicht? Mach wenigsten eine Ausgabe wie viele derzeit offen sind.
(so ca vor dem ListFiles(client,LS_SIZE,file); )

Hallo,

sind die Daten denn auf der SD Karte enthalten ? Karte mal mit PC auslesen um den Fehler einzukreisen

Heinz

Hier zunächst einmal die serielle Ausgabe zum Sketch:

TimeNTP Initialisieren
IP Adresse ist 192.168.178.41
Transmit NTP Request
Receive NTP Response
syncronisiere Uhrzeit…ok
Initialisiere SD card…SD Card vorhanden
LOST.DIR/
ANDROI~1/
DATALOG.TXT 838217
20200112.TXT 347413
20200113.TXT 3978
Dateien in Verzeichnissen gefunden.

Done
2020 1 13 17:50:16 T_Kamin : 21.5
2020 1 13 17:50:16 T_Kessel: 19.7
2020.1.13 17:50:16;Temp-Kamin;21.5;Temp-Kessel;19.7
2020 1 13 17:50:21 T_Kamin : 21.5
2020 1 13 17:50:21 T_Kessel: 19.7
…usw…
2020 1 13 17:51:29 T_Kamin : 21.6
2020 1 13 17:51:29 T_Kessel: 19.7
2020 1 13 17:51:34 T_Kamin : 21.6
2020 1 13 17:51:34 T_Kessel: 19.7
GET / HTTP/1.1
Web request for:
File Opened!
is a directory
2020 1 13 17:51:39 T_Kamin : 21.6
2020 1 13 17:51:39 T_Kessel: 19.7
GET /favicon.ico HTTP/1.1
Web request for: favicon.ico
2020 1 13 17:51:45 T_Kamin : 21.6
2020 1 13 17:51:45 T_Kessel: 19.7
GET / HTTP/1.1
Web request for:
File Opened!
is a directory
2020 1 13 17:51:50 T_Kamin : 21.6
2020 1 13 17:51:50 T_Kessel: 19.7
2020 1 13 17:51:55 T_Kamin : 21.6

Kompletter Sketch hängt als Zip an

heizungssteuerung3.zip (4.93 KB)

schön, aber

Markiere uns, wann das Ereignis eintritt, das in die Funktion verzweigen soll (eine zusätzliche Debug.Ausgabe könnte dir dabei helfen).

???

@noiasca

im loop findet sich folgende Zeile zum Schreiben der Daten auf Karte:

if (sdcardok && timeok && newdata) {writeData();}

Die Funktion macht die aktuelle Datei auf, schreibt und schließt wieder. Sollte auch funktionieren, da ich Daten in den Files sehen kann.

Der WEB-Server stellt eine HTML Seite zur Verfügung die ein Client abruft und dabei mittels

File file = SD.open(filename, O_READ);

die SD Card untersucht. Ist das Objekt file vorhanden, soll die Funtion ListFiles mit den entsprechenden Parametern aufgerufen werden.

Das macht es, wenn ich nicht mit writeData() ein Filehandling produziere.
Anderenfalls, ja da weiß ich nicht weiter…

da würde ich halt anfangen, Serial.println zum debuggen einbauen:

Das wäre das erste ca Zeile 230:

         File file = SD.open(filename, O_READ);
          if ( file == 0 ) {  // Opening the file with return code of 0 is an error in SDFile.open

Serial.print(F("Fehler ")); Serial.println(file);
Serial.print(F("filename")); Serial.println(filename);

            client.println("HTTP/1.1 404 Not Found");

Immer Fehlercodes und alle interessanten Variablen ausgeben!
Fehlercode in der Lib-Beschreibung nachschauen was er in Klartext heißt.
Ich unterstelle, hier wirfts dir schon was raus.

P.S.: Dein Code ist ein schönes Monster, ...

Monster hin oder Monster her - mit <5kB sollte Es möglich sein, auch ein Monster in einen Code-Tag zu packen.
Das Zeichenmaximum ist derzeit nämlich 9000 - und laut Mathe-App ist Das mehr als 5000 … müsste also passen!

Zunächst einmal vielen Dank, dass ihr euch meiner annehmt!

@noiasca

An der vorgeschlagenen Stelle ist das nicht sinnvoll. Denke ich. Die Browser-Seite hat dort kein Problem und Postet das client.println(“

File Not Found!

”); garnicht erst. Damit ist klar es Springt weiter

Ich habe ein paar Zeilen weiter unten folgendes eingebaut:

  • client.print(“

    Datenlog’s auf SD Card /”);*

  • client.print(filename);*
  • client.println(":");*
  • Serial.print("ListFiles-Param: “); Serial.print(client); Serial.print(” “); Serial.print(LS_SIZE); Serial.print(” "); Serial.println(file);*
  • ListFiles(client,LS_SIZE,file);*

Die serielle Schnittstelle zeigt daraufhin ListFiles-Param: 1 2 1 siehe Ausschnitt hier:

2020 1 13 19:47:13 T_Kessel: 20.9
GET / HTTP/1.1
Web request for:
File Opened!
is a directory
ListFiles-Param: 1 2 1
2020 1 13 19:47:19 T_Kamin : 23.6

Was macht dann aber die Funktion “ListFiles” mit der 1 an der Stelle “File dir”. Ist das korrekt?

Hallo Gemeinde,

die Nuss ist geknackt. Hat mich zwei Abende gekostet. Egal.

Für alle die es interessiert:

Das Problem war der File-Pointer. Dieser ist beim Datenloggen logischer Weise wo anders als beim Lesen des Inhaltes der SD Card mittels "openNextFiles()".

Hier hilft an der richtigen Stelle, bei mir in der Funktion "ListFiles()", den Pointer wieder auf Anfang zu stellen.

Also dir.rewindDirectory(); bevor das openNextFile() wieder abfragt.

Danke an alle die sich in irgend einer Weise bemüht habe.