Dateien von SD-Karte "parsen" ?

Hallochen.
Ich mal wieder mit nem Punkt, an dem ich nicht so richtig weiter komme:

Der Monstertruck hat eine SD-Karte.
Auf der gibts ein Verzeichnis/routen und darin später eine unbekannte Anzahl Dateien:
-route1.txt
-route2.txt
und so weiter.

Normale Textdateien, die jede eine GPS-Route enthalten...
Da ich Länge und Breite sowieso als Long`s brauche, ist es simpel, die Datei sieht momentan innen so aus:

Routenname
1,5076654,1374625
2,5076645,137468

// und so weiter

ein Wegpunkt pro Zeile, die erste Zahl ist eine fortlaufende Nummer (dmit ich später weiss, an welchem Wegpunkt grad gearbeitet wird, so bin ich in der Anzahl flexibel, die zweite Zahl ist Länge, die dritte Breite.

Das einlesen klappt auch, und zwar mache ich das so:

#include <SD.h>
const int chipSelect = 53;

void setup()
{
  Serial.begin(9600);
  if (!SD.begin(53)) {
    Serial.println("Laden misslungen!");
    return;
  }
  Serial.println("Lade Route...");
File myFile = SD.open("routen/route1.gps");
      if (myFile) 
      {
        Serial.println("route1.gps");
       while (myFile.available()) {
          int wp = myFile.parseInt();
          long lat = myFile.parseInt();
          long lon = myFile.parseInt();
          Serial.print("Wegpunkt: ");
          Serial.print(wp);
          Serial.print(" Breite: ");
           Serial.print(lat);
          Serial.print(" Laenge: ");
           Serial.println(lon);
          
         }// close the file:
          myFile.close();
          } 
          else
          {
  	// if the file didn't open, print an error:
      Serial.println("error opening test.txt");
      }
}
void loop()

In der Konsole sieht das dann so aus:

Lade Route...
route1.gps
Wegpunkt: 1 Breite: 5076654 Laenge: 1374625
Wegpunkt: 2 Breite: 5076645 Laenge: 137468
Wegpunkt: 3 Breite: 507663 Laenge: 1374674
Wegpunkt: 4 Breite: 507664 Laenge: 1374615
Wegpunkt: 0 Breite: 0 Laenge: 0

Nun hab ich da zwei Problemchen:
-erstens: die letzte Zeile: Wegpunkt 0 steht gar nich in der Datei-woher kommt die und wie kann ich das unterdrücken?
Zweitens: da ich später mehr als eine Route auf der Karte ablegen will, muss ich auch den "Header" (oben: Routenname) mal einlesen und als Klartext ausgeben können, um die richtige Route wählen zu können. Routenname soll aus maximal 16 Zeichen bestehen, damit er später aufs Display passt...
Wie mach ich das?

Das ist ähnlich wie bei Serial. Am Ende der Zeile kommt LF + CR (ich glaube es ist beides). Dann macht man solange read() bis das kommt und liest die Zeichen in ein Array ein.

Dein zusätzliches Einlesen am Ende kommt vielleicht auch daher dass da noch mal Steuerzeichen am Ende kommen. Das einfachste wäre du fragst ab ob "lat" und "lon" ungleich 0 sind.

Generell finde ich hierfür SdFat angenehmer und einfacher zu handhaben. Da gibt es eine fgets() Methode die jeweils die komplette nächste Zeile in einen Puffer einließt und dabei CR + LF entfernt. Dann kann man jede Zeile beim Lesen gleich behandeln und dann unterschiedlich parsen je nach dem ob da Text oder Zahlen drinstehen.

EDIT:
Das sollte bei der SD Klasse auch zum Einlesen von Strings gehen:

Das liegt an parseInt.
Da - wie du - keiner Fehlerprüfungen macht, sind auch keine vorgesehen :wink:
Du könntest nach parseInt available() abfragen, aber in deinem Fall ist es noch einfacher, da eine 0 in deiner Datei gar nicht vorkommt.

Hallo,
keine Ahnung ob es funktioniert und wie es geht, aber vielleicht hilft es
auf die Sprünge…

Du legst ja so das File an:

xxx = SD.open("Route-1.txt", FILE_WRITE);

Du hast also "Route-1"

da müßte man einen String draus machen.

  1. String = Route-

dann hast Du die "1"

die kann man hochzählen

int ZählerRoute = 1

ZählerRoute++

diese Ziffer wird dann :
2. String = ZählerRoute

nun müßte man die zusammensetzen:

  1. String = 1. String + 2. String

dann:

xxx = SD.open("3. String.txt", FILE_WRITE);

aber frag mich mal??
Gruß und Spaß
Andreas

Ok-Problem eins kapiert- so ähnlich wär ich da auch rangegangen: lat=0 und/oder lon=0 kann nicht sein, also ignorieren.

Nun aber Problem2: ich kann nicht die Routennamen als Dateinamen verwenden, einfach, weil mehr als 8 Zeichen pro Dateiname nicht drin sind (die Endung benutze ich, um die Routen-Dateien zu identifizieren, noch weiss ich nicht, was ich mit der SD noch alles treiben werd).
Da reichlich Platz auf der Karte ist, hab ich nicht vor, da dauernd Routen runterzulöschen, sondern einfach neue hinzuzufügen später.
Deshalb der jetztige Name: Route1.
Das kann ich dann, wie Andreas schon vorschlug, so verwursten, dass beim Einstellen der richtigen Route einfach die Namen durchgegangen werden..dann aber muss die erste Zeile (von mir aus auch die letzte wenn das irgendwelche Vorteile hätte) eingelesen werden, weil da der Name der Route im Klartext steht.
Beispielsweise "vonZuHauseZurOma". Dieser Name soll auf dem Display erscheinen, damit ich weiss, welche Route das ist.
Der restliche Inhalt der Datei interessiert zu dem Zeitpunkt gar nicht, erst, wenn ich die ausgewählte Datei bestätige, sollen dann die nötigen Wegpunkte eingelesen werden (je nach Länge natürlich nicht alle auf einmal, ich glaub, den Teil krieg ich schon hin).

Es geht also darum, einen String in der Datei zu finden, einzulesen und zum Display zu schicken. NUR diesen String eben..

Wie gesagt, machst du dir das Leben etwas einfacher wenn du SdFat und fgets() verwendest.

Mit der Standard SD Lib kannst du aber auch zeilenweise C Strings einlesen. Entweder Zeichen für Zeichen mit read() oder mit readBytesUntil(). Jeweils bis ein CR oder LF kommt. Alternativ mach dir ein extra Trennzeichen wie #. Ist alles in der Stream Klasse:

Lass die Finger nur von readString()/readStringUntil(). Das verwendet die Arduino String Klasse

Hallo,
also das mit der Route-1, 2, 3 u.s.w würde ich schon machen.
So- und jetzt weiß ich nicht wo Dein Problem ist.

"vonZuHauseZurOma"
diese Route gibts Du doch vorher vom PC aus ein? Oder hast Du eine Tastatur
angeschlossen- wohl nicht?

Du könnste Dir das gleich in die Datei schreiben. Aber das willst Du ja nicht.

Also irgendwo im Programm steht "vonZuHauseZurOma" das wählst Du dann irgendwie
aus, drückst eine Taste, und es wird in Route-1 geschrieben.
Das ist KEIN Problem.

Du mußt doch nur dafür sorgen, das es nur einmal geschrieben wird- das geht
über Merker oder Zähler.
Das ist auch KEIN Problem.

Wenn das- vorzugsweise, in der ersten Zeile steht, dann kannst Du es doch
ganz einfach auslesen.
In den Beispielen zur SDLib ist das doch alles beschrieben und beispielhaft
dokumentiert.

Du kannst doch mit einem Taster die Route-1 aufrufen, die öffnen, erste Zeile
holen, schließen.
falsche Route-
wieder Taste
Route-2 aufrufen, die öffnen, erste Zeile holen, schließen.
richtige Route, wieder öffnen- und machen was es soll.

Also- schaue Dir ein die BeispielDateien zur SD-Lib an. Da steht ganz genau
wie schreiben und lesen geht.

"Es geht also darum, einen String in der Datei zu finden, einzulesen und zum
Display zu schicken. NUR diesen String eben.."

Datei öffnen, erste Zeile holen, Datei schließen.

Sollte der String nicht in der ersten Zeile stehen, dann mußt Du wissen wo er
steht- oder Du liest eine Zeile nach der anderen.

Wenn Du allerdings auf etwas wie "cmd+F" spekulierst, dann vergesse es einfach.
Vielleicht bin ich auch zu blöde, um Dich zu verstehen.
Gruß und Spaß
Andreas

Ich habs.
Der Tipp mit dem stream.readBytesUntil(character, buffer, length) war der entscheidende Hinweis.

Da es sicherlich auch für andere mal nützlich sein kiann (oder auch ich selber es in nen paar Wochen wieder mal suchen werd.... :roll_eyes: ) hier mal das Testprogramm (nich wundern, die Routen-Dateien liegen in nem eigenen Verzeichnis):

#include <SD.h>
const int chipSelect = 53; //is nen Mega 2560
char name[16]; //länger werden meine Namen eh nicht
void setup()
{
  Serial.begin(9600);
  if (!SD.begin(53)) {
    Serial.println("Laden misslungen!");
    return;
  }
  Serial.println("Lade Route...");
File myFile = SD.open("routen/route1.gps");// Datei liegt im verzeichnis:/Routen
    if (myFile) 
      {
         myFile.readBytesUntil('#',name,17); //Ersten String lesen, der den Namen enthält
         Serial.print("Routenname: "); //String ausgeben
         Serial.println(name);
       while (myFile.available()) { //Datei nach Zahlen absuchen
          int wp = myFile.parseInt(); //WegPunkt-Nr. zuerst (Zeilenanfang)
          long lat = myFile.parseInt();// als zweites kommt lat
          long lon = myFile.parseInt();// dann lon
         Serial.print(" Wegpunkt: "); // und alles ausgeben, sooft es nötig ist
          Serial.print(wp);
          Serial.print(" Breite: ");
           Serial.print(lat);
          Serial.print(" Laenge: ");
           Serial.println(lon);
          
         }// close the file:
          myFile.close(); //Klappe zu, Affe dot
          } 
          else
          {
  	   Serial.println("Nette Fehlermeldung");
      }
}
void loop()
{
}

Und hier beispielhaft die Textdatei: der Name geht nur bis zum ersten "#", alles andere ist mehr Kommentar, danach werden Zeile für Zeile die drei Werte eingelesen und zur Konsole geschafft.
Wichtig: nach der letzten Zeile NICHT noch mal die eingabetaste drücken- sonst gibts eine zusätzliche Zeile mit drei "0"-Werten. :wink:

TeichParkplatz#Runde am Parkplatz beim Teich
1,5076654,1374625
2,5076645,137468
3,507663,1374674
4,507664,1374615

Läuft...

@SkobyMobil:
Du hast des Pudels Kern scheinbar nicht ganz verstanden.
Genau das, was ich hier oben gepostet hab, wollte ich erreichen.

Aus diese Weise kann ich dann beliebig viele Routen-Dateien auf die Karte legen, geb mir in nem Menü die eingelesenen Routennamen aus (die acht Zeichen vom Dateinamen sind mir da einfach zu wenig, 16 passen auf`s Display), indem ich eine Datei nach der anderen nach dem Routennamen absuche, und wenn der richtige im Display erscheint, bestätige ich und Dinochen weiss, welche wir nehmen wollen.
DANN liest er von dieser Datei die Wegpunkte ein, und kann los.

Geschrieben wird bis hierhin vom Dino erstmal GAR NIX.
Das wird kein Logger (kommt später vielleicht noch, ich könnt mir z.B. durchaus vorstellen, dass der Truck die Routen, aus wenigen vorgegebenen Punkten zu fahren versucht, und sie nach Erfordernis (Hindernisse die umfahren werden müssen usw.) selber verfeinert, aber da bin ich noch laaange nich...), sondern nur eine Art Karte, die er befolgt.
Es sind quasi nur Brotkrumen, an denen er sich von A nach B hangeln kann.
Weiter gar nix...

Nun läuft das so wie ichs mir vorgestellt hab-möglich, dass es nen besseren Weg gibt, aber der hier funktioniert.
JETZT komm ich an den Punkt mit "Route1.dat, Route2.dat....", da muss erstmal der ganze Ordner abgesucht werden, um rauszufinden, welche Routen überhaupt vorhanden sind.
Das probier ich aber -wie üblich- erstmal alleine hinzukriegen.

Du übergibst 17 als Länge aber dein Puffer ist nur 16 groß. Der Delimiter (hier #) zählt da glaube ich nicht dazu.

1.) Dein Puffer sollte 1 länger als die maximale Zeichenkette sein, damit noch Platz für den Terminator ist
2.) mach vorher sicherheitshalber memset(name, 0, sizeof(name)). Damit ist sichergestellt, dass der String korrekt terminiert ist. Sowie es aussieht stellt die Funktion selbst nämlich keine Terminierung sicher, da es keine reine String Funktion ist
3.) genauso solltest du wahrscheinlich auch bei readBytesUntil() die Größe als sizeof(name) - 1 angeben. Dann passt das immer. -1 eben wegen dem Terminator

EDIT:
Im Moment hast du das korrekt terminiert da das Array global ist und damit steht alles auf 0. Damit wäre memset() nicht unbedingt nötig. Aber wenn du den Code mal in einer Funktion mehrmals hintereinander verwendest und mal ein String kürzer ist als der vorher, geht es eventuell nicht mehr. Genauso wenn der Puffer nur lokal ist.

Ja, danke. Das mit den verschiedenen Grössen war mir schon aufgefallen.
Das andere hab ich mit reingenommen, ohne es so richtig zu verstehn- aber das dämmert schon gaaanz leicht.

Hab inzwischen mal ne zweite Datei dazugetan und kann die nun abwechselnd( nen File-Parser ist noch nicht implementiert) fein auslesen.