Arduino KFZ-Fahrtenbuch - Probleme mit Speicherung auf SD-Karte

Hallo,

ich habe mir aus einem Arduino Nano, einem neo gps modul, einen kleinen Nokia Display und einem SD-Kartenmodul ein Fahrtenbuch gebaut. Wenn ich mit meinem Auto durch die Gegend fahre speichert es alle 3 Sekunden GPS-Koordinaten, Uhrzeit u.s.w. auf einer SD-Karten. Auf dem Display wird u.a. die aktuelle Geschwindigkeit angezeigt. Das Ganze hängt am 12V Zigaretten Anschluss mit regulären 5V KFZ-Adapter und USB. Das Ganze habe ich jetzt schon 1-2 Jahre

Problem: Funktioniert an sich super, nur es kommt ab und zu mal vor, dass Fahrten nicht aufgezeichnet werden. Entweder die Fahrten werden komplett aufgezeichnet, dann ist meist alles gut oder ich finde gar keine Dateien für die Fahrt auf der SD-Karte.

Man könnte jetzt vermuten, dass die ganze Apparatur abgestürzt ist. Ist aber auf dem Display wird aber die ganze Fahrt über alles richtig ausgegeben. Es scheint also ein Problem zu sein was entweder das SD-Modul (Stromversorgung) oder die die SD-Library betrifft.

Die SD-Karte wird bei Fahrtbeginn auch erkannt, weil in setup() wird auf !SD.begin(chipSelect) getestet, ansonsten würde das Programm gar nicht starten.

Das Ganze ist auch sicher nicht ganz optimal programmiert, man könnte sicher auch ein paar Dinge anders machen. Ich weiß nicht was da hängt und was man evtl. als "Testparameter" einbauen könnte.

Hier Auszug aus dem Programm:

#include <TinyGPS++.h>
#include <SoftwareSerial.h>
#include <SPI.h>
#include <SD.h>
#include <NOKIA5110_TEXT.h>

static const int RXPin = 4, TXPin = 3;
static const uint32_t GPSBaud = 9600;
const int chipSelect = 10;
String fahrtID;
String dateiname;
unsigned long zeitstempel = 0;
unsigned long wartezeit;
float strecke = 0;
int start = 0;
int abtastrate = 1000;
double hoehe = 0;
double gefaelle = 0;

NOKIA5110_TEXT mylcd(2, 9, 8, 6, 7);

TinyGPSPlus gps;

SoftwareSerial ss(RXPin, TXPin);

void setup()
{
  Serial.begin(9600);
  delay(10); 
  pinMode(5, OUTPUT); //PIN für Hintergrundbeleuchtung des Displays. Wird in bestimmten Situationen an-/ausgeschaltet geschaltet, z.B. wenn SD-Test nicht erfolgreich war damit ich das optisch durch ein leuchtendes Display mitbekomme.
  delay(10); 
  
  mylcd.LCDInit(inverse, contrast, bias); // LCD initialisierung
  mylcd.LCDClear(0x00); // Display löschen
  mylcd.LCDFont(FontNumber); // Schriftart setzen

  printLCD("Warte auf", "SD-Karten", "Test"); //Anzeige, dass jetzt SD-Karte getestet wird

  digitalWrite(5, HIGH);
  delay(500); 
  if (!SD.begin(chipSelect)) 
  {
    while (1); //Wenn SD nicht erkannt wird, dann wird auf dem Display "Warte auf SD-Karten Test" angezeigt und ich weiß, dass es ein Problem gibt. 
  }
  delay(500);

  printLCD("Warte auf", "GPS-Signal", "");
  
  ss.begin(GPSBaud);
  delay(1000);
  wartezeit = millis();
  digitalWrite(5, LOW);
}

....

//Der Programmteil der die Zeichenkette definiert, die alle paar Sekunden auf der SD-Karte gespeichert wird. Ich erstelle einmal eine .txt für jede Fahrt, da kann ich gezielter kontrollieren oder etwas nachschauen. Der gleiche Datensatz wird aber noch mal in "GPSLOG.txt" erstellt, wo dann die Datensätze von allen Fahrten gespeichert werden. Die GPSLOG.txt wird einmal in der Woche in die Datenbank importiert.

String datensatz = "(" + String(fahrtID) + ",'" + gps.date.year() + "-" + gps.date.month() + "-" + gps.date.day() + " " + gps.time.hour() + ":" + gps.time.minute() + ":" + gps.time.second() + "'," + String(gps.location.lat(), 6) + "," + String(gps.location.lng(), 6) + "," + gps.speed.kmph() + "," + gps.altitude.meters() + "," + gps.satellites.value()  + "," + String(strecke/1000,2) + "),";      
      //Serial.println(datensatz);
      if(geschwindigkeit >=1)
      {
        schreibeSD(SD.open(dateiname, FILE_WRITE), datensatz);
        delay(50);
        schreibeSD(SD.open("GPSLOG.txt", FILE_WRITE), datensatz);
        delay(50);
      }
      else
      {
        //Serial.println("geschwindigkeit zu gering");
        delay(100);
      }



//Hier der Teil wo dann eigentlich auf die SD-geschrieben wird
void schreibeSD(File file, String datensatz)
{
  if (file) 
  {
    file.println(datensatz);
    file.close();
  }
}


I

Um sicher zu gehen, das überhaupt ein öffnen des Files möglich war, könntest Du nach der Prüfung etwa ausgeben, wenn das nicht geklappt hat.

void schreibeSD(File file, String datensatz)
{
  if (file)
  {
    file.println(datensatz);
    file.close();
  }
  else
  {
    lcdPrint("SD defekt", "");
    delay(2000);
  }
}

Auf den ersten Blick sehe ich hier ein paar Probleme:

  1. Das große Kapitel der Störungen im KFZ Bordnetz. Das ist nicht zu unterschätzen! Lustigerweise finden sich auf den Packungen von "regulären 5V KFZ-Adaptern" gerne Sachen wie "nur bei abgeschaltetem Motor nutzen".

  2. Rüttelfestigkeit!
    Weder die 12V Zigarettenanzünder Dosen/Stecker, noch USB Verbindungen haben sich als sonderlich zuverlässig erwiesen. Ich persönlich traue denen nicht weiter, als ich sie werfen kann.

  3. Bei dir wird mit String gehuddelt!
    Unmengen an malloc() Aufrufen. Der zu schreibende String wird nicht als Referenz übergeben. Speicher ist bei dir sowieso schon knapp, da ist erhöhte Sorgfalt Pflicht.

  4. Dann noch ein paar Kleinigkeiten im Code, welche mir aber nicht als sonderlich wichtig erscheinen.

Zu 3, der wohl wichtigste Punkt:
Ich rate dir zur Streaming Lib, mit der kannst du leicht auf das ganze String Gedöns verzichten, und es sieht immer noch halbwegs gut aus.

Dann würde ich mehr Fehlerprüfungen einbauen.
Damit findet man Rütteleffekte.
Ruhig mit loggen.

Einen Resetzähler einbauen!
Auch diesen loggen, oder wenigstens anzeigen.

Wenn Du nach dem Schreiben einer Zeile diese liest und im Display anzeigst, kannst Du erkennen, wann der Fehler auftritt.

Vielen dank für die schnellen und ausführlichen Antworten:

@my_xy_projekt @agmue
vielen Dank, werde ich gleich als erstes einbauen. Das mit dem Testparametern ist im Betrieb immer etwas das Problem, ich hab ja nur das Display um was auszugeben.

@combie
1.)
Das ist auch etwas meine Sorge, dass die Spannung oder Verhalten vom Bordnetz Probleme macht. Vielleicht ziehendie Module auch zu viel und der Arduino Nano macht das nicht mit? Das GPS-Modul muss sich ja auch erst mal starten. Deswegen hab ich da auch öfter mal "Delays" reingemacht, damit das Programm nicht zu schnell durchläuft. Ich hab auch die Sorge, dass beim Motorstart die Spannung kurz "dreckig" ist und zu sehr schwankt und daraus wieder Problem resultieren. Zum Motorstart muss man auch sagen, dass wenn ich die Zündung anschalte kurz der Strom vom Bordnetz da ist, wenn ich dann den Schlüssel auf Motor starten drehe ist der Strom kurz weg.

2.) War insb. bei dem vorherigen SD-Kartenmodul ein Problem. Mit der Micro-SD-Karte geht es jetzt und die Jumper-Kabel sitzen auch fest. Ist als tendenzielles Problem erkannt, aber sollte aktuell wenig Probleme machen. Wenn die Fahrtaufzeichnungen zwischendrin einfach abbrechen, dort komische Zeichen sind dann würde ich denken, dass es Rüttelprobleme sind. Das kommt auch ab und zu mal vor, dass große Problem ist aktuell, dass teilweise gar keine Dateien erstellt oder ganze Fahrten von Anfang an nicht geschrieben werden. Ich zeige mir auf dem Display auch die gefahrene Strecke an, der Arduino stürzt zwischendrin auch nicht ab oder so.

3.) Das muss ich mir auch anschauen. Wie wahrscheinlich sind Speicherprobleme? Müsste dann nicht das Programm komplett abkacken? Ich hab noch nie beobachtet, dass während der Fahrt das Display ausgeht oder sich das Programm resettet.

Was meist du konkret mit "Resetzähler"?

Nöö ....
Fehlverhalten ist zu erwarten!
Aber welcher Art, ist völlig unbestimmt.

Resets zählen und im Display anzeigen.

Auch von mir ein NÖ.
Ich habe das gleiche Problem an einem nicht ordentlich gemachtem Aufbau für Langzeitmessungen, - Das Display zeigt alles normal, aber auf der SD wird nicht weitergeschrieben.
Damit das nicht unbemerkt bleibt habe ich mir eine Zeile eingebaut die mir den Schreibfehler auf dem Disolay anzeigt.

Denen traue ich so weit wie Combie den Zigarettenanzünder Steckern... Verwende ich eigentlich nur noch für Versuchsaufbauten oder Mindestens 2 Pins für einen Anschluß.

Bei meiner "Wohnmobil Innenraumbatterieüberwachung" habe so viel wie möglich gelötet, wo Stecker nötig waren habe ich kleine Rundstecker verwendet.
Weil ich die Batterie etwas "ungewöhnlich" lade, und mich die Lade, - Entladekurven interessieren, zeichne ich auch hier auf. Bei dieser Installation hatte ich noch keine Aussetzer.

Ich habe mir [diesen] (M5Stack GPS Logger - Hackster.io) gebaut und die Texte in der Anzeige auf Deutsch übersetzt.
Mit externer GPS Antenne im Auto ein geiles Teil.

Siehe die zwei Edits am Ende:

Mhh, ich konnte jetzt den vermeintlichen Fehler am PC offenbar reproduzieren.

Ich habe den Code wie nachfolgend angepasst, nach ~11 Sekunden sollte man im Serial Monitor eigentlich die Ausgabe "Schleife" und "Schreibe Datei" sehen.

Das funktioniert auch, es gibt aber Situationen wo es im Serial Monitor gar keine Ausgabe kommt. Und zwar dann, wenn man kurz mal das USB-Kabel zieht und sofort wieder ansteckt. Ist die Stromunterbrechung zu kurz, dann gibt es scheinbar Probleme mit dem Schreibvorgang ohne dass im Display ersichtlich ist, dass vom Programm irgendwie was falsch läuft. Das würde die Probleme bei der Fahrtaufzeichnung erklären.

void setup()
{
  Serial.begin(9600);
  delay(10);
  pinMode(5, OUTPUT);
  delay(10);
  
  mylcd.LCDInit(inverse, contrast, bias); // init  the lCD
  mylcd.LCDClear(0x00); // Clear whole screen
  mylcd.LCDFont(FontNumber); // Set the font

  printLCD("Warte auf", "SD-Karten", "Test");

  digitalWrite(5, HIGH);
  delay(500);
  if (!SD.begin(chipSelect)) 
  {
    while (1);
  }
  delay(500);

  printLCD("Warte auf", "GPS-Signal", "");
  delay(10000);

  while(true)
  {
    schreibeSD(SD.open("Log.txt", FILE_WRITE), "Test");
    delay(3000);
    Serial.println("Schleife");
  }
  
  ss.begin(GPSBaud);
  delay(1000);
  wartezeit = millis();
  digitalWrite(5, LOW);
}

void schreibeSD(File file, String datensatz)
{
  if (file) 
  {
    file.println(datensatz);
    file.close();
    Serial.println("Schreibe Datei");
  }
  else
  {
    printLCD("SD defekt", "", "");
    delay(2000);
    Serial.println("Fehler");
  }
}

Edit: Nein, dass was ich geschrieben habe ist Quatsch. In dem Moment hängt das gesamte Programm und er geht nicht mal in Loop rein. Das passt nicht zum Fehler, da ja das Display funktioniert.

Edit II: Doch, er geht in den Loop... Das ist kein Quatsch. Auf den LCD-Bildschirm wird etwas ausgegeben, aber beim Seriellen Monitor gibt es keine Ausgaben "Schleife" mehr? Das passiert scheinbar immer dann, wenn ich kurz mal den Strom wegnehme und direkt wieder anstecke- Wenn ich 2,3,4 Sekunden warten, dann sind die Ausgaben im Seriellen Monitor wieder da.

void loop()
{
    while(true)
  {
    Serial.println("Schleife");
    delay(1000);
    printLCD("AAA", "BBB", "Test");
  }

Womit wir wieder bei schlechten Kabelverbindungen wären... :wink:

Wenn die Spannung einbricht, wenn gestartet wird, kommt es zu dem selben Effekt.

Wenn das dann noch ein Auto mit Abschaltautomatik ist, dann wird das öfter gestartet.

Gruß Tommy

Aber wie kommt sowas bzw. was hat das mit dem kurzen stromlosen Abständen zu tun?

Das Programm startet ja scheinbar Jedes mal wirklich korrekt neu. Ist da bei Teilen der Hardware noch kurz was gepuffert?

Wie löst man das jetzt ohne nach dem Motorstart noch manuell irgendeinen Schalter umzulegen?

Abfragen, ob geschrieben wurde. -> wurde Dir oben schon erläutert.
Wenn nicht geschrieben, einen reset ausführen lassen.
Muss man testen, ob ein Soft-reset ausreicht.

Wenn man noch einen freien Pin hat, kann man auch einen Hardreset (evtl. auch fürs Display) auslösen.
Zusätzlich würde ich der Versorgung mal einen großen Elko parallel einen KeKo spendieren.

Gruß Tommy

Ein Gedanke kreiselt durch mein Gehirn und jetzt schreibe ich ihn einfach mal auf.

Dein µC wird nur dann mit Strom versorgt, wenn der Motor läuft?

Dann stelle ich mir vor, der µC hat Daten gesammelt, schreibt diese gerade auf die SD-Karte und dabei verschwindet der Strom. Ob danach die Daten noch lesbar sind?

Für solche Fälle wurden USVs entwickelt. Ein Verbrennerauto hat diese in Form der Starterbatterie eingebaut.

Zum Testen µC ständig mit Strom versorgen, aber nur bei laufenden Motor Daten empfangen und schreiben. Das Schreiben der Daten immer erst ordentlich abschließen, dann nachschauen, ob der Motor noch läuft, um ggf. neue Daten zu erfassen.

Bei Erfolg kann man, wenn notwendig, eine Selbsthaltung konstruieren, damit bei längerem Stillstand die Starterbatterie nicht leergesaugt wird.

Nur so eine Idee :slightly_smiling_face:

Mhh, ich hab etwas den Eindruck ein Soft-Reset scheint nicht zu reichen. Das ist Scheiße....

Mit den Hinweisen war jetzt meine Idee irgendwo am Anfang erstmal eine Testdatei zu erzeugen, schauen ob diese existiert. Wenn diese nicht existiert, dann Software-reset durch "asm volatile ("jmp 0");" Wenn "SD.exists(...)" ausgeführt wird wird aber nie was gefunden, weil .open(...) scheinbar auch nach dem Reset nichts erstellt.

Dann stelle ich mir vor, der µC hat Daten gesammelt, schreibt diese gerade auf die SD-Karte und dabei verschwindet der Strom. Ob danach die Daten noch lesbar sind?

Kann die SD-Karte crashen, war insb, beim Abstellen des Motors ein Problem. Weil das Programm ja nicht vorher weiß wann ich den Motor abstelle. Wenn in dem Moment was geschrieben wurde und ich den Motor abgestellt habe, dann waren die Daten teilweise nicht lesbar.

Hab ich dann so gelöst, dass nichts auf die Karte geschrieben wird wenn das Fahrzeug steht. Ich muss nur beim Abstellen drauf achten, dass auf dem Display 0km/h angezeigt werden und erst dann den Motor abstellen.

Na ja, Autos haben auch Kabel wo immer Strom drauf ist...
Weil hier die Spannung aber beim Anlassen schwankt Tommis Kondensatoren dazu

Die Frage habe ich mir auch schön öffters gestellt...
Wenn ich die Versorgungsspannung abschalte weiss ich ja auch nicht ob gerade geschrieben wird oder nicht.
Ich speichere alle Sekunde, die Chance dass ich den Schreibzyklus erwische ist dann 1 / Schreibzyklusdauer (wie lange ist die etwa ? )

Bei meinem fällt wärend des Anlassens die Spannung auf 9,5 Volt ab (gemessen), bleibt die Frage wie schnell das DC DC Modul (ich denke der TO hat so was davor) nachregelt.

Das ist Unfug....
Das resettet nichts.
Macht nur Probleme.