SD-Karte beschreiben --> Datei beschädigt

Hallo,

ich habe mir aus einem Arduino Uno und einem "ARD SHD SD V4 Arduino Shield" eine Wetterstation gebaut, welche die Daten einiger Sensoren alle 10 Minuten auf eine SD-Karte schreibt.

Zunächst mal: Es funktioniert alles grundsätzlich problemlos, die Funktion ist wie gewollt gegeben,
ABER: wenn ich die SD-Karte nach ein paar Tagen herausnehme, ist die Datei beschädigt, d.h. die Datei kann nicht gelesen werden. Die Daten sind somit futsch :frowning:

Ich kann den Zeitraum (noch nicht) genauer eingrenzen, ab wann es passiert, jedoch sollte ich die SD-Karte schon mal einen Monat drin lassen können ohne zwischendurch Daten auslesen zu müssen..
Ich habe zwei SD-Karten getestet, der Fehler tritt unabhängig von den Karten auf.

Hat jemand eine Erklärung woran es liegen könnte ?

Im voraus vielen Dank! :slight_smile:

Hier der Code, den ich zum beschreiben der SD Karte nutze:

/////AUSGABE SENSORDATEN AUF SD KARTE 

if (millis()-zeitakt > zeitabfrage) {

  zeitakt=millis(); //Zeitakt aktualiseren


//STROM ausgeben
I=ina219.getCurrent_mA();

shuntvoltage = ina219.getShuntVoltage_mV();
busvoltage = ina219.getBusVoltage_V();
U = busvoltage + (shuntvoltage / 1000);

//Serial.println(U);
//Serial.println(I);


 //Sensoren abfragen
 
 TA=dhtA.readTemperature();
 HA=dhtA.readHumidity();
 TB=dhtB.readTemperature();
HB=dhtB.readHumidity();
PC=bmp.readPressure()/100; //Druck in mbar
TC=bmp.readTemperature()-2.6;//inkl. Tempkorrektur 16.01.20 Zimmer
 


//Formatierung de  SD KARTE: W95 FAT32 KEIN (LBA) !!!!
//SD Karte öffnen
File dataFile = SD.open("Daten.csv", FILE_WRITE);

if (dataFile) {
 // Auf die SD-Karte schreiben

printTime(dataFile);
dataFile.print(String(";"));
dataFile.print(TA);
dataFile.print(String(";"));
dataFile.print(HA);
dataFile.print(String(";"));
dataFile.print(TB);
dataFile.print(String(";"));
dataFile.print(HB);
dataFile.print(String(";"));
dataFile.print(PC);
dataFile.print(String(";"));
dataFile.print(TC);
dataFile.print(String(";"));
dataFile.print(I);
dataFile.print(String(";"));
dataFile.print(U);
dataFile.print('\n');
   
   
   
   
   dataFile.close();       // Datei schließen
   
 } else {

  //Display Rot
   int red = 255;
  int green = 0;
  int blue = 0;
  lcd.setRGB(red, green, blue); 
//Fehlermeldung oder Meldung ausgeben
  lcd.setCursor(0, 0); // Cursor im Display auf den Anfang der ersten Zeile setzen
lcd.print("Datei nicht           ");  
lcd.setCursor(0, 1);
lcd.print("geoeffnet        "); 
delay (2000); //WARTEN
   
 }

Evtl. wollte er gerade in dem Moment auf die SD schreiben.

Gruß Tommy

Tommy56:
Evtl. wollte er gerade in dem Moment auf die SD schreiben.

Gruß Tommy

Das war auch mein erster Gedanke, jedoch halte ich das für unwahrscheinlich, der Schreibvorgang dauert nur max. 1 s, was alle 10 Minuten auftritt. Genau diese Sekunde zu treffen ist also recht schwierig.
Um dies dennoch auszuschließen habe ich das Schreibintervall auf 10 Sekunden gekürzt und nach einiger Zeit mehrmals die Karte gezogen. Alle Daten waren ohne Probleme auszulesen.

Was ich mir noch vorstellen könnte wäre, dass die Variable für millis() überläuft.- Soweit ich es verstanden habe gibt millis() ja die Zeit seit dem Start des Programms an. Könnte dies das Problem sein ?

Wenn Du konsequent millis()-alteMillis >= intervall rechnest, hat der Überlauf keine Auswirkungen.

Auch unwahrscheinliche Fehlzustände können auftreten.

Gruß Tommy

Hi

Auch ist das Austesten nicht gerade zeitnah möglich - millis braucht 49,x Tage zum Überlaufen - denke, so lange hast Du nicht gewartet :wink:

MfG

dataFile.close();       // Datei schließen

Schieb da mal noch eine zusätzlich Zeile ein:

datafile.flush();

https://www.arduino.cc/en/Reference/FileFlush

Da steht zwar, das das automiatisch passieren soll, aber das gilt nur für die SD.lib;

Das ist Unsinn:

dataFile.print(String(";"));

Das macht was du willst, aber du quälst nur unnötig den µC. Das String Objekt ist einfach nur ein Umweg.

Besser:

dataFile.print(';');

Schieb da mal noch eine zusätzlich Zeile ein:

datafile.flush();

Arduino - FileFlush

Da steht zwar, das das automiatisch passieren soll, aber das gilt nur für die SD.lib;

Unsinn. Vor close() braucht man kein flush(), weil das mitgemacht wird. Kenne jedenfalls keine SD library, bei der das anders wäre.

Allerdings funktioniert das { open(); print(); close();} nicht, wenn zwischendurch die Karte entfernt wurde und wieder eingesetzt wird. ( Manchmal noch nicht mal, wenn kein anderer in der Zwischenzeit die Karte beschrieben hat. )

Und bevor SD.begin() erneut funktioniert, muss man in diesem Fall auch SD.end() gemacht haben. Oder bei jedem Kartenwechsel den Arduino resetten.

michael_x:
Und bevor SD.begin() erneut funktioniert, muss man in diesem Fall auch SD.end() gemacht haben. Oder bei jedem Kartenwechsel den Arduino resetten.

Bevor ich die Karte entferne setze ich den Arduino immer stromlos, setze dann die neue Karte ein und starte wieder.
Ein Reset wird also bei jedem Kartenwechsel vorgenommen.

Hallo,

also. Deine wilden Kartenentfernungen sind grob fahrlässig. Murphy schlägt erbarmungslos zu.
Du hast absolut keine Karten Lese-/Schreib Zugriffskontrolle. Alles ist Zufall. Dann darfst du dich echt nicht wundern wenn nichts klappt.

Mein Akku betriebener Datalogger arbeitet wie folgt.
Akkuspannung wird überwacht. Schwellwert ist in Software eingestellt.
Jeglicher Kartenzugriff wird bei unterschreiten der Mindestspannung unterbunden. Der letzte Zugriff wird noch beendet, dann wird die Karte “abgemeldet”. Solange bis Akku neu geladen oder ich sonstwas gemacht habe.
Während des Betriebs leuchtet bei Kartenzugriff kurz eine rote Led - Aktivitätsanzeige.
Wenn ich während des Betriebs die Karte entnehmen möchte, drücke ich einen Taster.
Damit signalisiere ich der Software ich möchte die Karte entnehmen.
Falls in dem Moment ein Zugriff stattfindet wird der normal beendet und danach wird jeder Zugriff unterbunden und die Karte abgemeldet. Dieser sichere Zustand signalisiert mir eine grüne Led. Jetzt kann ich mit der Karte machen was ich will. Ist sie wieder eingesetzt drücke ich wieder den Taster und sage damit der Software - Karte ist wieder für dich da. Sie wird initialisiert und weiter gehts mit Daten loggen.
Habe damit noch nie Probleme gehabt. Die Karten sind 0815 SD Karten.

(deleted)

Doc_Arduino:
Habe noch Probleme gehabt.

nie
:wink:

Peter-CAD-HST:
oder die Speicherkapazität der SD-Karte ist erreicht ?

und gesund bleiben.

Gesund bleibe ich schon mal, ich sitze nur daheim und programmiere, sogar draußen an der frischen Luft, weil die ganze Wetterstation draußen steht, deshalb gehe ich jetzt immer mit dem Laptop raus, wenn ich was probieren möchte :wink:

Die SD-Karten haben 4 GB Kapazität, bei 2 Jahren Daueraufnahme brauche ich nur ca. 2 - 3 MB, das sollte also reichen.

Über Nacht habe ich mal Daten aufnehmen lassen, heute morgen war noch alles da, d.h. innerhalb einer Nacht tritt kein Datenverlust ein.

Um Schreibfehler zu vermeiden, habe ich heute einen Button installiert, über den die SD-Karte beendet wird. Ich drücke erst den Button, setze dann den Arduino stromlos und entferne dann die Karte. Danach die neue Karte rein und wieder unter Strom setzen.
Hier der Code, der beim Drücken des Buttons ausgeführt wird. Ist dies die richtige Vorgehensweise, um die SD-Karte zu entfernen ?

void clickedIt(){ // Abfrage Taster 

SD.end();


//hier wird eine Anzeige auf dem Display generiert, damit ich weiß, dass die Karte entfernt werden kann:
int red = 0;
int green = 0;
int blue = 60;
lcd.setRGB(red, green, blue);
lcd.setCursor(0, 0); // Cursor im Display auf den Anfang der ersten Zeile setzen
lcd.print("SD entnehmen     ");
lcd.setCursor(0, 1); // Cursor im Display auf den Anfang der ersten Zeile setzen
lcd.print("stromlos setzen!              ");

////////////////////

delay(20000); //genügend zeit um Arduino stromlos zu setzen
 
 }

my_xy_projekt:
nie
:wink:

Danke. :grin:

Die Standard-Lib für SD funktioniert bei mir auch nicht.
Ich habe gute Erfahrung mit

gemacht.
Auch im Dauereinsatz.

So Fazit nach einem Tag Testbetrieb mit dem Button, der "SD.end()" ausführt:

die Datei ist zwar nicht beschädigt, enthält jedoch jetzt gar keine Daten mehr :frowning:

keine Ahnung was jetzt wieder los ist...

die Datei ist zwar nicht beschädigt, enthält jedoch jetzt gar keine Daten mehr :frowning:

keine Ahnung was jetzt wieder los ist…

Stell doch mal den sketch ein, dann kann man draufschaun.

Aber vorher: In der IDE STRG-T drücken, damit der Code so formatiert ist, das man den lesen kann.

... Schuss ins Blaue: Wenn der Taster gelöst wird, bevor der Arduino richtig tot ist, erstellt Der eine neue Datei - und Die ist leer.
Fehlende Verriegelung nehme ich an.

MfG

postmaster-ino:
... Schuss ins Blaue: Wenn der Taster gelöst wird, bevor der Arduino richtig tot ist, erstellt Der eine neue Datei - und Die ist leer.
Fehlende Verriegelung nehme ich an.

Ich hatte auch erst auf prellen getippt, bin mir aber nicht so sicher, was dann passiert.
Warum dann eine neue leere Datei erstellen? open() nimmt doch eine vorhandene wieder auf.

Also der Taster führt schon die Funktion korrekt aus.
Ich habe ja die Kontrolle, da nach “SD.end()” sich die Farbe des Displays ändert und der Text angezeigt wird. Vorher muss also SD.end() ausgeführt worden sein.

Der Vollständigkeit halber hier der Code der aufgespielt wurde:

//INA219 einbinden
#include <Adafruit_INA219.h>
Adafruit_INA219 ina219;

//Daten für SD Shield
#include <SD.h>
int SELECTED_CHIP = 4;

#include <SPI.h> // Bibliothek für die serielle Schnittstelle laden
#include <DHT.h> // Bibliothek für den Sensor laden

//Bibliothek Display
#include <rgb_lcd.h>
rgb_lcd lcd; // Instanz der Display-Schnittstelle erstellen

//ONE BUTTON

#include "OneButton.h"
OneButton tasterw(7, false);


//Uhr einbinden
#include <DS1307.h>
DS1307 clock; // ein Uhr-Objekt anlegen


//Bibliothek BMP Sensor Adadruit Druck und Temp:
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BMP280.h>
#define BMP_SCK 13
#define BMP_MISO 12

#define BMP_MOSI 11
#define BMP_CS 10
Adafruit_BMP280 bmp; // I2C

// Instanz der Sensorschnittstelle erstellen.
// A0 ist der Anschluss am Grove Base Shield, DHT22 der Sensortyp --> Temp und Luftfeuchte
DHT dhtA(A0, DHT22);
DHT dhtB(A1, DHT22);


//Eingabe PIN Wechseltaster
int wechsler = 7;

//Zeiten

long zeitabfrage = 600000; /////////////////////////////////////////////////////////////////////Abfragezeit der Sensoren alle 10 Minuten
int zeitabfragedisp = 6000; //Abfragezeit der Sensoren alle 5 Sekunden
long zeitakt = 0; //Wird immer aktualisiert, Zeitpunkt der letzten Abfrage
long zeitaktdisp = 0; //Wird immer aktualisiert, Zeitpunkt der letzten Abfrage

////SENSORDATEN///////////

//Sensor A
//Temperatur A
float TA;
//Luftfeuchte A
float HA;
//Sensor B
//Temperatur
float TB;
//Luftfeuchte B
float HB;
//Sensor C
//Luftdruck C
float PC;
//Temp C Luftdrucksensor
float TC;

//Spannung und Strom
//Stromstärke
float I;

//Spannung
float U;
float shuntvoltage = 0;
float busvoltage = 0;


void setup() {

  ina219.begin(); //INA219 initialisieren

  pinMode(wechsler, INPUT);
  pinMode(2, INPUT); //interrupt pin =2
  pinMode(A3, INPUT);
  pinMode(A2, INPUT);
  pinMode(A1, INPUT);
  pinMode(A0, INPUT);

  dhtA.begin(); // Sensor  1 initialisieren

  Serial.begin(9600); // serielle Schnittstelle starten

  
  //DISPLAY//////////////////////////////////////////////////
  lcd.begin(16, 2); // Display initialisieren - 2 Zeilen mit jeweils 16 Zeichen
  lcd.createChar(DEGREE_SYMBOL, degree); // Das neue "°"-Symbol beim Display registrieren


  /////////Button definieren
  tasterw.attachClick(clickedIt);
  clock.begin(); // Verbindung zur Echtzeituhr herstellen

  //Prüfen ob SD-Karte verfügbar!
  checksd();
  delay(zeitabfragedisp + 1000); //kurz warten bis dahin sollte sich das Display aktualisiert haben

  File dataFile;

}

void loop() {

  // Button abfragen
  tasterw.tick();

   /////AUSGABE SENSORDATEN AUF SD KARTE

  if (millis() - zeitakt > zeitabfrage) {

    zeitakt = millis(); //Zeitakt aktualiseren


    //STROM ausgeben
    I = ina219.getCurrent_mA();

    shuntvoltage = ina219.getShuntVoltage_mV();
    busvoltage = ina219.getBusVoltage_V();
    U = busvoltage + (shuntvoltage / 1000);

    //Serial.println(U);
    //Serial.println(I);


    //Sensoren abfragen

    TA = dhtA.readTemperature();
    HA = dhtA.readHumidity();
    TB = dhtB.readTemperature();
    HB = dhtB.readHumidity();
    PC = bmp.readPressure() / 100; //Druck in mbar
    TC = bmp.readTemperature() - 2.6; //inkl. Tempkorrektur 16.01.20 Zimmer



    //Formatierung de  SD KARTE: W95 FAT32 KEIN (LBA) !!!!
    //SD Karte öffnen
    File dataFile = SD.open("Daten.csv", FILE_WRITE);

    if (dataFile) {
      // Auf die SD-Karte schreiben

      printTime(dataFile);
      dataFile.print(";");
      dataFile.print(TA);
      dataFile.print(";");
      dataFile.print(HA);
      dataFile.print(";");
      dataFile.print(TB);
      dataFile.print(";");
      dataFile.print(HB);
      dataFile.print(";");
      dataFile.print(PC);
      dataFile.print(";");
      dataFile.print(TC);
      dataFile.print(";");
      dataFile.print(I);
      dataFile.print(";");
      dataFile.print(U);
      dataFile.print('\n');

      dataFile.close();       // Datei schließen

    } else {

      //Display Rot
      int red = 255;
      int green = 0;
      int blue = 0;
      lcd.setRGB(red, green, blue);
      //Fehlermeldung oder Meldung ausgeben
      lcd.setCursor(0, 0); // Cursor im Display auf den Anfang der ersten Zeile setzen
      lcd.print("Datei nicht           ");
      lcd.setCursor(0, 1);
      lcd.print("geoeffnet        ");
      delay (2000); //WARTEN

    }

  
  }

 
void clickedIt() { // Abfrage Taster Wechsler

  SD.end();

  int red = 0;
  int green = 0;
  int blue = 60;
  lcd.setRGB(red, green, blue);
  lcd.setCursor(0, 0); // Cursor im Display auf den Anfang der ersten Zeile setzen
  lcd.print("SD entnehmen     ");
  lcd.setCursor(0, 1); // Cursor im Display auf den Anfang der ersten Zeile setzen
  lcd.print("stromlos setzen!              ");
  delay(20000); //genügend zeit um arduino stromlos zu setzen

}

void printTime(File dataFile) {
  clock.getTime(); // Zeit vom Chip abfragen

  dataFile.print(clock.dayOfMonth, DEC);
  dataFile.print(".");
  dataFile.print(clock.month, DEC);
  dataFile.print(".");
  dataFile.print(clock.year, DEC);
  dataFile.print(" ");
  dataFile.print(clock.hour, DEC);
  dataFile.print(":");
  dataFile.print(clock.minute, DEC);
  dataFile.print(":");
  dataFile.print(clock.second, DEC);

}


/////SD-KARTE prüfen
void checksd() {
  if (SD.begin(SELECTED_CHIP)) {

    //Display auf Grün
    int red = 0;
    int green = 100;
    int blue = 0;
    lcd.setRGB(red, green, blue);
    //Fehlermeldung oder Meldung ausgeben
    lcd.setCursor(0, 0); // Cursor im Display auf den Anfang der ersten Zeile setzen
    lcd.print("SD-Card bereit   ");
    lcd.setCursor(0, 1);
    lcd.print("                 ");

  }
  else {
    //Display Rot
    int red = 255;
    int green = 0;
    int blue = 0;
    lcd.setRGB(red, green, blue);
    //Fehlermeldung oder Meldung ausgeben
    lcd.setCursor(0, 0); // Cursor im Display auf den Anfang der ersten Zeile setzen
    lcd.print("SD-Card nicht  ");
    lcd.setCursor(0, 1);
    lcd.print("erkannt             ");
  }

  tasterw.tick();

}