Eigenes Sensorshield - MQ7 MQ2 DHT22, LCD, SD-Card, Webserver

Hallo Forum,

ich baue gerade ein Sensorshield mit MQ-7 (CO-Sensor), MQ-2 (Rauchsensor), LCD-Display, DHT22 für Temperatur und Luftfeuchte, SD-Karte zum Loggen der Werte, Summer für Alarmton, und Webserver zum Anzeigen der Werte sowie Download der Logdatei per HTTP.
Das Ganze soll später auf einem Ethermega laufen. http://www.lipoly.de/index.php?main_page=product_info&products_id=214051 - und ein per Fritzing erstelltes Shield ist auch fast fertig.

Bisher ist folgendes fertig:

  • Hardware
  • Datum und Uhrzeit werden per NTP gezogen
  • Die Werte der Sensoren werden alle 15 Minuten in eine(!) Logdatei auf die SD-Karte geschrieben
  • Alarm funktioniert
  • Webserver zeigt die Werte an
  • Der Webserver zeigt mir alle vorhandenen Dateien auf der SD-Karte an

Was mir noch fehlt:

  • Eine Logdatei pro Tag erstellen
  • Download der Logdatei per HTTP
  • Die vorhanden Dateien auf der SD-Karte werden nur beim ersten Aufruf angezeigt, danach nicht mehr.

Bisher habe ich alles per Try-and-Error sowie "Codeklau" von diversen Internetseiten / Howtos / Foren zum laufen gebracht.
Der Code ist mit Sicherheit alles andere als perfekt.

Zum ersten Problem:
Ich kann mir auf der seriellen Konsole schön das Datum formatieren:

Jahr = year();
Monat = month();
Tag = day();
Serial.print(Jahr);
Serial.print('-');     
if(Monat < 10)
     Serial.print('0');
Serial.print(Monat);
Serial.print('-');
if(Tag < 10)
    Serial.print('0');      
Serial.println(Tag);

das Ergibt: "2013-08-03"
Wie kann ich mir daraus einen String bauen, damit ich Dateien nach dem Schema "2013-08-03.csv" auf der SD-Karte erstellen kann?
Bzw. prüfen kann, ob schon eine Datei "2013-08-03.csv" existiert?

Thombo84:
Wie kann ich mir daraus einen String bauen, damit ich Dateien nach dem Schema "2013-08-03.csv" auf der SD-Karte erstellen kann?
Bzw. prüfen kann, ob schon eine Datei "2013-08-03.csv" existiert?

Gar nicht. Beim einfachen FAT-Dateisystem bist Du auf 8.3 Dateinamen beschränkt.
"2013-08-03.csv" ist zwei Zeichen zu lang, um ihn als Dateinamen auf SD-Karte mit FAT-Dateisystem zu verwenden.

Möglich wäre ein Dateiname wie "20130803.csv", also maximal 8.3 Zeichen lang.

Beispiel einer Formatierungsfunktion mit Test-Sketch zum erzeugen zufälliger Dateinamen:

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
}

char* formatFilename(int day, int month, int year, char* ext)
{
  static char filename[13];
  snprintf(filename,sizeof(filename),"%04d%02d%02d.%s",year,month,day,ext);
  return filename;
}


void loop() {
  // put your main code here, to run repeatedly: 
  int day=random(1,31);
  int month=random(1,13);
  int year=random(2010,2014);
  char* filename=formatFilename(day,month,year,"CSV");
  Serial.println(filename);
}

Danke für den Tipp mit 8.3 :slight_smile:
Der Code funktioniert wunderbar. Vielen Dank.

Es stellte sich gerade heraus dass ich noch Probleme mit der SD-karte habe.
Ich habe eine Funktion welche mir alle Dateien auf der SD-karte ausgibt, was solange funktioniert bis ich etwas in die LogDatei schreibe. Danach funktioniert das Ausgeben aller Dateien nicht mehr.

root = SD.open("/");
printDirectoryNOLAN(root);
root.close();
  • Das fuktioniert .....
void printDirectoryNOLAN(File dir) {
   while(true) {
     File entry =  dir.openNextFile();
     if (! entry) {
       break;
     }
    Serial.println(entry.name());
    entry.close();
   }
}

....aber nur solange wie keine andere Datei geöffent wird. Sobald u.s. Code ausgeführt wurde erhalte ich von "printDirectoryNOLAN" direkt den "break;"

myFile = SD.open(LogName, FILE_WRITE);
    myFile.print(humidity);
    myFile.print(";");
    myFile.print(temperature);
    myFile.print(";");
    myFile.print(sensorValueMQ2);
    myFile.print(";");
    myFile.print(sensorValueMQ7);
    myFile.close();

Habe gelesen dass immer nur eine Datei geöffnet sein darf, aber ich schließe die Dateien ja auch direkt wieder.
Hat jemand eine Erklärung dafür?

So siehts bisher aus:

Scheint ein Bug zu sein...
http://code.google.com/p/arduino/issues/detail?id=904

Weil

The resolution is simple and does not require any modifications to SD.cpp.
Simply add entry.close() to the example code as shown below

entry.close ist gesetzt.

und laut: http://arduino.cc/de/Reference/FileRewindDirectory gibt es eine Option dir.rewindDirectory();
welche aber auch nicht zum Erfolg führt.

void printDirectoryNOLAN(File dir) {
  Serial.println("NOLAN : SD-Card vorhandene Dateien");
   while(true) {
     File entry =  dir.openNextFile();
     if (! entry) {
       Serial.println("break");
       dir.rewindDirectory();
       break;
     }
    Serial.print("Name: ");
    Serial.println(entry.name());
    entry.close();
   }
}

Thombo84:
Hat jemand eine Erklärung dafür?

Probier mal:

void printDirectoryNOLAN(File dir) 
{
  File entry =  dir.openNextFile();
  while (entry)
  {
    Serial.println(entry.name());
    entry.close();
    entry=  dir.openNextFile();
  }
  dir.rewindDirectory();
}

Mit einem Aufruf von "dir.rewindDirectory()" am Ende.

Das ist leider auch nicht die Lösung :frowning:

Ich habe mal die SD.cpp bearbeitet:

//  if (mode & (O_APPEND | O_WRITE)) 
 //   file.seekSet(file.fileSize());
 // return File(file, filepath);
 
 
  if (mode & (O_APPEND | O_WRITE)) {
    file.seekSet(file.fileSize());
  } else if (mode & (O_READ)) {
    file.seekSet(0);
  }
return File(file, filepath);

bringt aber auch nichts.

Thombo84:
Das ist leider auch nicht die Lösung :frowning:

Bei mir schon, allerdings nicht mit Schreiben einer Datei, sondern mit Lesen einer Datei zwischen zwei Directory-Listings.

Vielleicht solltest Du in Deinen Code zum Schreiben in Datei mal etwas Fehlerbehandlungs-Code einbauen.

[/quote]
Vielleicht solltest Du in Deinen Code zum Schreiben in Datei mal etwas Fehlerbehandlungs-Code einbauen.
[/quote]

Die Dateien werden fehlerfrei auf die SD geschrieben.

if(currentMillis - previousMillisLog > intervalLog) {
  previousMillisLog = currentMillis; 
    myFile = SD.open(LogName, FILE_WRITE);
    if (myFile) {
     Serial.print(F("Writing to "));
    Serial.println(LogName);
    Serial.print(hour());
    Serial.print(":");
    Serial.println(minute());
    lcd.clear() ;
    lcd.print(Ethernet.localIP());
    lcd.setCursor(0, 1) ;
    lcd.print(hour());
    lcd.print(":");
    lcd.print(minute());
    lcd.print("h UTC");
    float humidity = dht.getHumidity();
    float temperature = dht.getTemperature();
    myFile.print(hour());
    myFile.print(":");
    myFile.print(minute());
    myFile.print(";");
    myFile.print(humidity);
    myFile.print(";");
    myFile.print(temperature);
    myFile.print(";");
    myFile.print(sensorValueMQ2);
    myFile.print(";");
    myFile.println(sensorValueMQ7);
    myFile.close();
    Serial.println(F("Writing done."));
    }
    else {
    Serial.println(F("FEHLER BEIM SCHREIBEN!"));
    }
  }
void printDirectoryNOLAN(File dir) 
{
  Serial.println("Lese SD-Card");
  File entry =  dir.openNextFile();
  while (entry)
  {
    Serial.println(entry.name());
    entry.close();
    entry = dir.openNextFile();
  }
  dir.rewindDirectory();
}

Thombo84:
Die Dateien werden fehlerfrei auf die SD geschrieben.

Ist das ein größerer Sketch?
RAM-Speicher vielleicht mehr als vollständig aufgebraucht?

Ist wohl irgendwas im Zusammenspiel bzw. Nichtzusammenspiel der verschienen Komponenten und Funktionen.

mittels

int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

wird mir eingebaut in die loop() Schleife immer "5481" ausgegeben. Ich denke das ist genug freier Speicher.
Die Sktechgröße beträgt 39.236 Bytes von 258.048 Bytes.

EDIT:
Ich habs! Die Lösung ist

 dir.seek(0);
void printDirectoryNOLAN(File dir) 
{
  dir.seek(0);
  Serial.println("Lese SD-Card");
  File entry =  dir.openNextFile();
  while (entry)
  {
    Serial.println(entry.name());
    entry.close();
    entry = dir.openNextFile();
  }
  dir.rewindDirectory();
}

bzw.

void printDirectory(File dir, EthernetClient client) {
  dir.seek(0);
  client.print("vorhandene Dateien");
  Serial.println("SD-Card vorhandene Dateien");
   while(true) {
     File entry =  dir.openNextFile();
     if (! entry) {
       Serial.println("break");
       break;
     }
    client.print("<li><a href=\"");
    client.print(entry.name());
    Serial.print("Name: ");
    Serial.println(entry.name());
    client.print("\">");
    client.print(entry.name());
    client.println("</a>");
    entry.close();
   }
}

Beides funktioniert.

Thombo84:
wird mir eingebaut in die loop() Schleife immer "5481" ausgegeben. Ich denke das ist genug freier Speicher.

Ja, halbwegs "normale" Arduino-Programme laufen selbst mit nur ca. 250 Bytes freiem Speicher nach Initialisierung aller Objekte und Variablen noch einwandfrei. Du hast ja noch mehr RAM frei als ein UNO überhaupt eingebaut hat.

Thombo84:
EDIT:
Ich habs! Die Lösung ist

 dir.seek(0);

Danke für das Feedback, dann kann ich ja aufhören zu überlegen und Du hast eine Lösung selbst gefunden!

Für Interessierte anbei der bisherige Code (der bei weitem nicht perfekt ist).
Der Code ist leider zu groß fürs Forum, daher gibts den im Anhang.
Verbesserungen werden immer gerne angenommen.

UPDATE: Mittlerweile ist der Code erweitert worden

  • DCF77 für Zeit
  • E-Mail Benachrichtigung bei Alarm
  • zusätzlicher DS1820 Temperatursensor.
    ... und etwas aufgeräumt im Code. Bei Interesse bitte melden.

Sensorboard_final_working.ino (13.6 KB)