Erklärung SD Karte lesen und Serial.read

Hallo.
Kann mir vielleicht irgend jemand erklären wie das Lesen von der SD Karte funktioniert?
Bei Serial.read durchläuft es irgendwie irgend etwas und letzten Endes habe ich einfach nacheinander jedes einzelne Zeichen in einer Variable gespeichert. Wieso ist das so? Und wie kann man da einfach eine Zahlenkette in einer Variable speichern, und diese nachher wieder aufteilen in mehrere Variablen, zB die ersten 3 Zahlen in eine Variable, 4. - 8. in eine andere, und die letzten 5 wieder in eine andere?

Und kann das sein das beim lesen von der SD Karte wieder genau das gleiche ist und man irgendwie jedes Zeichen einzeln in einem Durchgang bekommt?
Wie lese ich den da eine bestimmte Zeile aus, zB Zeile 10, speichere diese Zeile (bestehend aus Buchstaben und Zahlen) in einer Variable, und Teile diese Variable wieder in mehrere auf? Evtl durch Codewort suche, steht in der Variable dieses Wort, dann setzte alles was zwischen zwei Kommas mit diesem Wort drin ist in eine andere Variable. Wie mach ich das?
Wie durchsuche ich überhaupt einen bestimmte Zeile mit Buchstaben-Zahlen-Text nach irgend welchen Schlüsselwörter, schneide mir alles was zwischen 3. Komma und 4. Komma ist heraus und speichere in einer andern Variable, und benütze die 5 Zahlen zwischen den Buchstaben vielleicht in einer anderen Variable um zu rechnen?

Wenn mir das jemand erklären könnte wäre es super, ich blicke hier überhaupt nicht durch.

Danke vielmals

Du suchst Zeichenketten (char-Arrays). Hier ein paar Infos dazu.

Gruß Tommy

Warum siehst du dir nicht mal die Beispiele der Libraries an.
Da findest du alles nötige für dein Vorhaben.

Und kann das sein das beim lesen von der SD Karte wieder genau das gleiche ist

Ja, da sowohl die SD Lib, als auch Serial, das Stream Interface implementieren, kann man sie gleich benutzen.

und man irgendwie jedes Zeichen einzeln in einem Durchgang bekommt?

Wenn man will.......
Man kann allerdings auch größere Blocks lesen, wenn man möchte.
Die Doku verschafft Klarheit.

Also der Link sieht schon etwas besser aus, aber ich verstehe es auch nicht wirklich. Das erste mal mit Serial.print das "uino Uno" ergibt scheint ja noch logisch, wieso beim nächsten Serial.print der Zeiger auf o ist kann ja vielleicht auch sein, aber spätestens beim nächsten Serial.print zeigt der Zeiger auf einmal auf das letzte o obwohl alles genau gleich ist ausser ein weiteres r in strrchr. Ist den das überhaupt ein normaler Name oder so etwas wie eine Funktion, oder woher kommt das aufeinmal?

Wie gibt man den einfach 4. bis 8. Zeichen aus aus einem Array? Oder vom 1. o bis 2. o? Das macht gar keinen Sinn das man einen Zeiger irgendwo drauf zeigen muss, eine Festplatte oder SD Karte kann doch gar nicht funktionieren wenn nicht so etwas wie ein Zeiger integriert wäre, sonnst müsste man ja um die letzte Datei aufzurufen den ganzen Speicher von Anfang an bis zum Schluss durchsuchen nach der letzten Datei?
Und auch mit dem Zeiger den ich nicht so ganz verstehe, wie gibt man den etwas zwischen zwei Zeigern aus?

Und in den Arduino Beispielen sehe ich nur etwas von ganzer Datei ausgeben, was wenn eine 3GB grosse Datei darauf ist, wie kann den der Arduino die auslesen und eine bestimmte Zeile heraus suchen? In einem Array wird er es wohl nicht speichern können nehme ich an? Sollte ja viel zu gross sein um auf dem Arduino zu speichern.
Kann man überhaupt so eine grosse Datei auslesen?

In den Dokumentationen finde ich auch nicht das was ich suche...

Ein Unterschied ist aber dass die serielle Kommunikation sehr langsam ist. Wenn man vernünftig nicht-blockierend programmiert kann man daher immer nur ein Zeichen einlesen und muss immer wieder nachschauen bis die ganze Nachricht da ist.
Wenn man es richtig macht kann man da aber trotzdem die gleiche Funktion wie bei einer SD Karte verwenden

Und in den Arduino Beispielen sehe ich nur etwas von ganzer Datei ausgeben, was wenn eine 3GB grosse Datei darauf ist, wie kann den der Arduino die auslesen und eine bestimmte Zeile heraus suchen?

Zeilen sind mit CR/LF abgeschlossen (und auch bei Serial sollte man das LF als Endzeichen nehmen). Dann kann Zeile für Zeile einlesen und überprüfen ob man gefunden hat was man möchte. Der Puffer muss dann nur groß genug für eine Zeile sein

Eine andere Option ist es alle Zeilen gleich lang zu machen. Dann kann man den Lesezeiger mit seek() direkt auf die gewünschte Position setzen

lies die Datei ZEILENWEISE,
interpretiere die Zeile,
wenn nicht das passende drinnen steht, lies die nächste Zeile bis du am Dateiende bist.

@ Serenifly:
Weisst du ein Beispiel wie man 2 Zeichen über Serial in ein Array einliest ohne in einer Schleife gefangen zu sein und trotzdem zu einem Ende kommt?
Vielleicht indem man auf das End-Zeichen wartet? Aber wie ist das mit der Zeit, ich muss ja eine gewisse Zeit warten bis ich das nächste Zeichen erhalte, kann ich auch zu lange auf das nächste Zeichen warten? So das ich dann einfach das übernächste bekomme weil ich zu spät war oder wartet die Serielle Kommunikation darauf bis man das nächste Zeichen ausliest?

@ noiasca:
Aber so muss der Arduino ja um zur letzten Zeile zu kommen die ganze Datei auslesen, was bei 3GB eine weile dauern sollte. Sollte da nicht eben ein Zeiger irgendwo schon auf die letzte Zeile zeigen?

Du musst nur immer wieder nachschauen ob Zeichen da sind. Wenn nicht beendest du die Funktion gleich wieder:

const byte READ_BUFFER_SIZE = 11;    //Platz für 10 Zeichen + Terminator

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  char* str = readLine(Serial);
  if (str != NULL)      //liefert true wenn das LF eingelesen wurde
  {
    Serial.print("Gelesen: "); 
    Serial.println(str);
  }
}

char* readLine(Stream& stream)
{
  static byte index;
  static char buffer[READ_BUFFER_SIZE];

  while (stream.available())
  {
    char c = stream.read();

    if (c == '\n')              //wenn LF eingelesen
    {
      buffer[index] = '\0';     //String terminieren
      index = 0;
      return buffer;            //melden dass String fertig eingelesen wurde
    }
    else if (c >= 32 && index < READ_BUFFER_SIZE - 1)   //solange noch Platz im Puffer ist
    {
      buffer[index++] = c;    //Zeichen abspeichern und Index inkrementieren
    }
  }
  return NULL;                //noch nicht fertig
}

Sollte da nicht eben ein Zeiger irgendwo schon auf die letzte Zeile zeigen?

Wie gesagt, mache alle Zeilen gleich lang und verwende seek():

Oder denke darüber nach die Dateien kleiner zu machen. z.B. jeden Tag oder jede Woche eine neue Datei

Danke vielmals für das Beispiel. Ich verstehe aber so viel davon nicht...
Heisst das man könnte auch warten bis alle Zeichen da sind und dann alle Zeichen auf einmal lesen? Ich dachte man kann nur immer eines nach dem anderen abrufen. Die maximale Zeit sollte doch eigentlich auch berechnet werden können die es braucht um alle Zeichen zu senden?

Und seek() ist dazu da um zu einem bestimmten Byte in der Datei zu springen? Das wäre ja auch noch ok, manchmal vielleicht sogar besser.
Was passiert den eigentlich wenn ich lese bis zum Zeichen Zeilenende, dann etwas anderes mache, und wieder lese bis zum Zeilenende? Habe ich dann 2x die erste Zeile gelesen oder die erste und zweite Zeile?

ka111:
Heisst das man könnte auch warten bis alle Zeichen da sind und dann alle Zeichen auf einmal lesen?

Ja, aber das ist unsinnig und ineffektiv

Die maximale Zeit sollte doch eigentlich auch berechnet werden können die es braucht um alle Zeichen zu senden?

Die Anzahl der Zeichen und die Baudrate können sich vielleicht auch mal ändern. Gewöhne dir gar nicht sowas an, sondern programmiere allgemein

Was passiert den eigentlich wenn ich lese bis zum Zeichen Zeilenende, dann etwas anderes mache, und wieder lese bis zum Zeilenende?

Wenn du die Datei offen lässt bleibt die Position dazwischen natürlich bestehen

Aber es wäre sehr einfach für mich, und es kommt ja wieder etwa auf das gleiche heraus wenn bei der SD Karte auch alle Zeilen gleich lang sein müssten. Ich könnte einfach bevor die Zeichenkette da ist etwas machen. Irgend etwas muss man ja sowieso machen in dieser Zeit wenn man alle Zeichen auf einmal braucht, also könnte man sich auch einfach einen Timer machen der Bescheid gibt wenn alle Zeichen da sind.

Und eine Zeichenkette in einem Array Speichern würde dann etwa so gehen?:

char var1[] = readLine(Serial);

Und kann es vielleicht sogar sein das man mit:

int var2 = var1[5];

eine Variable mit dem 5. Zeichen aus dem var1 Array erhält sofern dort eine Zahl steht?
Wie kann ich den mehrere Zeichen aus dem Array in die zweite Variable kriegen? Irgendwie so?:

int var2 = var1[1,2,3,4,5];
int var2 = var1[1-5];

Edit: Ah und mit "if (Serial.available() > 9)" könnte man auch schauen ob schon min 10 Zeichen angekommen sind, dann könnte man es ja auch einfach so machen :slight_smile:

Du musst etwas Grundlagen zu Arrays und Zeigern lernen. Die sind eng miteinander verwandt. Sonst wird das nichts und to tippt nur willkürlich Code ein. Es gibt dafür genug Anleitungen und Erklärungen im Internet

Und weiterhin was zu C Strings/Zeichenketten lesen. Siehe auch ASCII. C Strings sind erst mal nur Null-terminierte ASCII Zeichen. Keine Integer. Dir fehlt anscheinend jegliches Wissen zu Datentypen und Konvertierungen.

Wie man das umwandelt, hängt vom Aufbau des Strings ab. Gewöhnlich trennt man da Zahlen mit Kommas oder Strichpunkten. Dann kann man sowas machen:

char str[] = "100,200,300";
int var1 = atoi(strtok(str, ","));
int var2 = atoi(strtok(NULL, ","));
int var3 = atoi(strtok(NULL, ","));

atoi() (ascii to integer) und strtok() (string tokenizer) sind Standard-Funktionen die man nachschlagen kann. Da gibt es auch noch anderen Funktionen für andere Datentypen wie float oder long

@TO: Du solltest die verlinkten Infos auch lesen. Dann müsstest Du nicht so substanzlos herumspekulieren.

Gruß Tommy

-.-
Was ist wenn ich nur Zahlen von 0 - 9 verwende? Könnte ich dann alles so machen mit int anstatt mit char vor den Variablen?
Da tun sich immer nur mehr und mehr Befehle auf die ich nicht kenne und mich darüber informieren könnte, irgendwann muss man doch auch mal auf einen grünen Zweig kommen, da bin ich ja noch Monate dran wenn ich alles Nachlese, und ich finde nicht mal eine richtige Erklärung zu atoi, nur 20 Forum Einträge, aber ich verstehe nicht was all die Klammern bedeuten darin.
Kann man das nicht einfach nur mit Zahlen so machen wie ich es oben hin geschrieben habe? Irgendwie mit var1 = var2 aber mit Array Variablen?

@ Tommy56
Ich habe den Link durchgesehen, aber ich sehe nur 100 Seiten Sonderzeichen, und die meisten Zeilen verstehe ich nicht. Wenn ich alle Zeilen mit Serial.print heraussuche finde ich das auch nicht heraus, und wenn ich selber lese komme ich spätestens beim Zeiger zum 2. o nicht mehr mit.

Ich weiß zwar nicht, wie Du da auf 100 Seiten Sonderzeichen kommst, aber egal. Du suchst ja nur Gründe (ich könnte auch Ausreden sagen), um nichts lernen zu müssen.
Dann wirst Du da auch nicht weiter kommen.

Ist Dein Google defekt, dass Du keine der 2.280.000 Ergebnisse zu atoi findet? Z.B. diesen hier.

Gruß Tommy

Ich meinte Zeilen nicht Seiten...

Diesen Satz:

int atoi(const char *string);

verstehe ich nicht. Ich weis nicht wieso da const steht, auch nicht wieso char *string, wenn es doch zu int soll?? Ausserdem was ist den char jetzt? Eine Variable wie abc oder etwas wie int? Wieso der Stern am string klebend und nicht mit Abstand auch zwischen string?

Genau so mit fast allen anderen Zeilen auf der ersten Seite die du verlinkt hast, ich verstehe die Satzdarstellung nicht, oder die Wörter.

Die besten Referenzen gibt es auf Englisch:
http://www.cplusplus.com/reference/cstdlib/atoi/
Oder auch die AVR libc direkt (wo auch nur das drin ist was es in der Arduino Software gibt):
https://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html#ga3a1fe00c1327bbabc76688a7a1d73370

Aber auch auf Deutsch gibt es Seiten

verstehe ich nicht. Ich weis nicht wieso da const steht, auch nicht wieso char *string, wenn es doch zu int soll?

Wenn dir solche absoluten Grundlagen nicht klar sind, wieso versuchst du dann selbst Routinen zu entwickeln um eine Datei einzulesen? Das sind einfach Dinge die bekannt sein müssen auch wenn du nicht weißt welche Funktionen du genau verwenden musst.
Das mag harsch klingen, aber ein gewisses Grundwissen muss vorhanden sein wenn man sowas macht. Wenn nicht musst du kleiner anfangen um es zu lernen

char* ist ein Zeiger auf einen char. In C sind Zeichenketten Null-terminierte Arrays aus char. Wenn irgendwo was von "C String" steht ist das gemeint. Nicht zu verwechseln mit der Arduino String Klasse (welche gerade diese Teilungs- und Konvertierungsfunktionen nicht hat)
Array-Variablen sind äquivalent zu Zeigern auf das erste Element. Wenn man also ein Array an eine Funktion übergibt hat man einen Zeiger als Parameter.
Deshalb gebe ich in meiner Funktion auch einen char* zurück um von außen Zugriff auf das lokale Array zu haben (Achtung: das geht nur mit static Variablen!)

const bedeutet einfach "konstant" und heißt dass man den Inhalt nicht verändern kann. Wobei es da bei Zeigern ein paar feine Unterschiede gibt (ob der Zeiger selbst const ist oder das worauf er zeigt. Oder beides). Das ist hier nötig weil String Literale automatisch const sind. Wenn man ein Literal an eine Funktion übergibt bei der der Parameter nicht const ist bekommt man eine Warnung

Wieso der Stern am string klebend und nicht mit Abstand auch zwischen string?

Das ist egal

Wenn dir solche absoluten Grundlagen nicht klar sind, wieso versuchst du dann selbst Routinen zu entwickeln um eine Datei einzulesen?

Es hat bis jetzt immer gereicht...

Ich muss mir das Morgen mal anschauen.

Danke allen für die Hilfe.

Also ich habe mir das überlegt, vielleicht setzte ich besser auf eine gute Serielle Kommunikation mit dem Raspberry Pi. Dort habe ich auch keine Probleme mit Text, und kann mit diesen Befehlen die ich kenne eigentlich (fast) alles machen was ich will.

Eine Frage noch, wenn ich 64 Bytes über die Serielle Kommunikation auslese die schon da sind, ist das dann schneller als wenn ich 64 Bytes von einer SD Karte auslese? Weil es so wie es aussieht im Ram gespeichert wird? Nur das warten bis alle Zeichen da sind geht lange?