Go Down

Topic: Auslesen von TXT Datein auf einer SD Karte (Read 7915 times) previous topic - next topic

Chaos_Lord

Hallo,

ein Problem gelöst und 3 neue Gefunden.

Ich habe es hinbekommen, Daten auf eine SD-Karte zu schreiben (sind Einstellungen, welche einen Reset überstehen sollen). Lese ich diese wie folgt aus:
Code: [Select]
 
myFile = SD.open("test.txt");
  if (myFile) {
    Serial.println("test.txt:");
   
    // read from the file until there's nothing else in it:
    while (myFile.available()) {
    Serial.write(myFile.read());
    }
   // close the file:
    myFile.close();
  } else {
  // if the file didn't open, print an error:
    Serial.println("error opening test.txt");
  }


Habe ich eine richtige Ausgabe in der Seriellen Konsole. Nun Versuche ich diese Daten in eine Variable zu schreiben, damit ich diese beim Neustart wieder verwenden kann. Leider scheinen die Daten nach der While Schleife weg zu sein.
Code: [Select]
 
myFile = SD.open("test.txt");
  if (myFile) {
    Serial.println("test.txt:");
   
    // read from the file until there's nothing else in it:
    while (myFile.available()) {
    //Serial.write(myFile.read());
    dummystring += myFile.read();
    Serial.write(dummystring);                                                           // Diese Zeile wird problemlos und richtig wiedergegeben
    }
    Serial.write("Test");
    Serial.write(dummystring);                                                           // DIese Zeile ist Leer
    // close the file:
    myFile.close();
  } else {
  // if the file didn't open, print an error:
    Serial.println("error opening test.txt");
  }


Warum ist wird die Ausgabe in der Schleife richtig wiedergegeben, und die drauf folgende, nachdem die SChleife durchlaufen ist nicht mehr.

Oder habe ich einen kompletten Denkfehler was das Verwenden von gespeicherten Daten angeht und ich muß ganz anders dran gehen?
Leider sind alle Beispiele die ich gefunden habe so geschrieben, das die Daten in der Konsol direkt ausgegeben werden, sprich so wie ind er Schleife, außerhalb der Schleife habe ich leider nichts gefunden :(

Danke für alle Infos, Hinweise oder Links

CL

PS der Schreibcode (nicht das der Fehler hier liegt; ist im Prinzip nichts weiter als ein abgewandeltest WriteRead Exaple :D):
Code: [Select]

if (!SD.begin(4)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");
  SD.remove("/test.txt");
  // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.
  myFile = SD.open("test.txt", FILE_WRITE);
 
  // if the file opened okay, write to it:
  if (myFile) {
    Serial.print("Writing to test.txt...");
    myFile.println("testing 1, 2, 3.");
// close the file:
    myFile.close();
    Serial.println("done.");
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening test.txt");
  }

Chaos_Lord

Bin gerade eine Stufe weiter gekommen,

was ich nunn verstanden habe ist, das Zeichen für Zeichen und nicht wie ich dachte Zeile für Zeile ausgelesen wird. Eine Idee wäre also die Zeichen in ein Array zu lesen, und dann aus den Zeilen des Arrays wieder eine Zeichenkette zu machen. Aber da haperts im Moment, wie kann ich die einzelnen "Zeilen" zusammen fügen. Laufe ich da mit einer Schleife durch und mache einen neuen string mit += erhalte ich keine brauchbaren Ergebnisse :(

michael_x

#2
Jun 11, 2012, 02:51 pm Last Edit: Jun 11, 2012, 03:07 pm by michael_x Reason: 1
Strings sind böse ! ( Weil der Arduino RAM so klein ist. )

Eine ganz schlechte Idee ist, erstmal eine ganze Datei zu lesen, und dann mit der Arbeit anfangen.
Nur weil der + Befehl bei Strings so schnell zu tippen ist, heisst nicht, dass das eine gute Methode ist.


Lies bis ein NewLine kommt, wandle die Zahl in deine Variable und fertig.
Es gibt Experten, die wandeln jede gelesene Ziffer direkt, ansonsten reicht ein char buffer[20] insgesamt, der für jede Zeile wiederverwendet wird, dicke aus.

edit:
Wenn deine Datei tatsächlich mit
   myFile.println("testing 1, 2, 3.");
erzeugt werden muss, hast du es etwas schwerer:
Ich würde alles ignorieren, bis ein Leerzeichen ' ' kommt ( "testing " ), dann jeweils lesen bis ein ',' oder '.'  oder 0x0A (newline) kommt.

Schau dir evtl. an, wie Nick Gammon Serial Eingaben liest. http://gammon.com.au/serial
und   http://arduino.cc/forum/index.php/topic,99225.0.html

Chaos_Lord

Hi,

ok also keine Strings habe Verstanden  :smiley-roll:

Die Dateien haben immer nur eine Zeile, mehr brauch ich nicht. Strings zusammenfassen hatte ich mir auch nur überlegt, weil ich ja eben nur Zeichen für Zeichen so eingelesen bekommen habe. Ich möchte ja die gesamte Zeile in einer Variablen haben, damit ich mit der weiter arbeiten kann und genau da hapert es bei mir. Das Anzeigen, wie es in den Beispielen gemacht wird, bekomme ich ja hin, aber eben nicht das Speichern um diese gespeicherte Variable dann weiter zu verwenden.

Die Dateien dich ich benötige sind winzig und kann ich so basteln wie ich es brauche z.B:

VERSION.NUM
Code: [Select]
V1.0
oder
MAXTEMP.TXT
Code: [Select]
50

was ich dann eben brauche ist z.b. im Programm:
Code: [Select]
int maxtemp = readfromfile("MAXTEMP.TXT");
un dann in der Variablen eben 50 drin steht :(

Grüsse

CL

michael_x

Ja, das ist hübsch einfach, eine Datei für einen Wert.

Wenn readFromFile immer einen int zurückliefert, ist es ganz einfach ;)
Wenn du auch long, float, string brauchst, gibt es mehrere Möglichkeiten.

Die Einfachste - Verschiedene Funktionen

Code: [Select]
int readIntFromFile(const char* filename);
long readLongFromFile(const char* filename);
int readTextFromFile(const char* filename, char* buffer, int maxLen);


Eine Funktionsdefinition wäre z.B.
Code: [Select]
static const int BUFSIZE=50;
static char buffer[BUFSIZE];
int readIntFromFile(const char* filename)
{
   int pos=0;
   myFile = SD.open(filename);
   if (myFile) {
      // read from the file until there's nothing else in it:
      while (myFile.available() && pos < (BUFSIZE-1)) {
    buffer[pos++] = myFile.read();
      }
      myFile.close();
      buffer[pos]=0; // EndeKennung
      return (atoi(buffer));
 
  }
  return 0; // Fehler: Datei nicht gefunden
}

Chaos_Lord

Sup klappt :D

Wenn ich das richtig verstehe erstellst du ein arry mit den Namen buffer und der größe 50, wo du dann Zeichen für zeichen einließt und eben mit dem atoi befehl wieder einen int aus allem machst richtig?

Fürs verständniss bits sowas ähnliches auch für Zeichenfolgen?

CL

michael_x


Fürs verständniss bits sowas ähnliches auch für Zeichenfolgen?


Falls ich dich richtig verstehe, hast du erkannt, dass es mit Texten als Rückgabe etwas anders geht ;)
Du könntest natürlich buffer direkt zurückgeben, aber der Inhalt wird beim nächsten Lesen einer Datei wieder überschrieben.

Mit int readTextFromFile(const char* filename, char* buffer, int maxLen);
würde ich dir vorschlagen, der Aufrufer von readTextFromFile gibt den Speicher mit, in dem er das Ergebnis haben will.

Damit readTextFromFile weiss, wie groß dieser Speicher sein darf, wird diese Grenze mit übergeben.

Code: [Select]
char meineVersion[10];

if ( readTextFromFile("VERSION.TXT", meineVersion, 10) > 0 ) {
   Serial.println(meineVersion);
}
else {
   Serial.println("Fehler beim Lesen von VERSION.TXT" );
}
}


Die Funktion kannst du jetzt selbst schreiben. Viel Spass.

Und vergiss die 0 als letztes Zeichen nicht ;)

Chaos_Lord

Hallo läuft nun danke erstmal habe aber nicht so wirklich verstanden wie du das meinst. habe es leider nur so hinbekommen, das ich der Funktion ein char* vorgesetzt habe, nicht ein int. Hat natürlich das Problem, das ich damit nicht den return 0 abfangen kann um festzustellen ob die Datei existiert. Wenn ich innerhalb der Funktion buffer = internerbuffer setzt, kann ich wie am Anfang innerhalb der Funktion in meineVersion diese auslesen, sobald ich das danach aber mach ist es leer :(

CL

michael_x

Der Rückgabe - Datentyp kann , wenn du willst, ruhig ein char* sein. Aber die Speicherzellen, die den ErgebnisText enthalten,
müssen vom Aufrufer bereitgestellt werden.
Ach ja, und falls du den Text nach dem Ende von setup() noch brauchst, darf das natürlich keine lokale temporäre Variable in setup sein. 

Code: [Select]
buffer = internerbuffer
Welcher ist was ?
Ein Tip: den buffer der für readIntFromFile() zeitweise gebraucht wurde, ist hier gar nicht erforderlich.
So sollte es auch gehen:

Code: [Select]
char * readTextFromFile(const char* filename, char* buf, int maxBuf) {
   File myFile = SD.open(filename);
   int pos=0;
   if (myFile) {
      // read from the file until there's nothing else in it:
      while (myFile.available() && pos < (maxBuf-1)) {
    buf[pos++] = myFile.read();
      }
      myFile.close();
      buf[pos]=0; // EndeKennung
      return buf;
   }
   else {
      return NULL;
   }       
}

Chaos_Lord

ah ok,

das meine ich mit dem internen, das der ja eigentlich überfüssig ist :D Nun läufts danke für die Hilfe

Steve

Daniel_S

Hi, ich denke ich stehe vor einem sehr ähnlichen Problem, bin aber bei dem ganzen noch nicht bis zum Kern durchgedrungen.
meine Aufgabe ist wie folgt:
1. Ich habe eine txt-Datei auf der SD Karte, diese enthält Werte in folgender Weise:
2000
2345
1345
...
2. Diese Werte müssen nacheinander, also erst die 2000 dann die 2345.... ausgelesen werden und nacheinander in eine variable gespeichert werden. (Es reicht völlig, wenn es eine Variable ist, der jeweilige Wert wird dann direkt weiter verarbeitet und die Variable kann mit dem neuen Wert überschrieben werden.)

Ich bin bisher hier:
Meine Datei enthält einen einzigen Wert z.B. 1500. Dieser wird dann wie folgt ausgelesen:

Code: [Select]

test = SD.open("test.txt"); //test.txt öffnen
  if (test)
  {
    float decade = pow(10, (test.available() -1));
    while(test.available())
    {
      float temp (test.read() - '0');
      data = temp*decade+data;             //data den entsprechenden Wert zuweisen
      decade = decade/10;
    }
    Serial.print(data);                                 // "data" ausgeben
    test.close();
  }

michael_x

#11
Jul 10, 2014, 03:15 pm Last Edit: Jul 10, 2014, 03:31 pm by michael_x Reason: 1
Quote
Code: [Select]
float decade = pow(10, (test.available() -1));


Sehr witzig!

Du bist sicher dass in deiner Datei keine überflüssigen Zeichen ( Leerzeichen, newLine) stehen ?
Sei froh, wenn du keine float Zahlen brauchst.

Was üblicherweise mit Seriell empfangenen Zahlen gemacht wird, geht natürlich auch mit Dateien, ausser dass available () sich auf die gesamte Datei bezieht.

http://www.gammon.com.au/forum/?id=11425
Siehe auch weiter unten: How to receive numbers.

Nachtrag:
Wenn du Start/Endezeichen nicht magst, und in der Datei nur Ziffern und NewLine Zeichen sind, ist es auch einfach:
NewLine ist Ende und gleichzeitig Anfang einer Neuen Zahl.
( Bei windows haben Text-Dateien übrigens zwei Zeichen als Zeilentrenner:
'\r' = 0x0D = CR und '\n' =0x0A = NL )
Und optionale Leerzeichen solltest du auch zulassen: erleichtert dir das Testen.

Daniel_S

Hey, Danke für die Hilfe! habe mir meinen Teil daraus gezogen und mein eigenes System dafür geschrieben(//finally), falls es jemanden Interessiert:

Die Aufgabe war von einer SD-Karte aus einer Txt-Datei, Werte Zeile für Zeile auszulesen, zB:
0500
1000
2000
4000
...

Für den Code braucht die Txt-Datei lediglich zwei Vorgaben: 1. die Erste Zeile ist frei (Enter ... '\n') 2. in jeder Zeile hat die Zahl x=4 Stellen (darum die führende Null bei: 500=0500).
Funktionieren tut das ganze sehr ähnlich meinen ersten Code, siehe weiter oben. es gibt lediglich folgende Änderungen:

Code: [Select]


//float decade = pow(10, (test.available() -1));    //Hier auszukommentieren...jede Zahl hat vier stellen!

while(test.available())
    {
     
            if(test.read() =='\n')                                           //Immer wenn ein Enter gelesen wird (Darum erste Zeile frei = Enter)
          {
                  for (int i=1; i<=4; i++)                                   //jede Zahl hat vier Stellen also vier Berechnungsschritte
                    {
                      float temp (test.read() - '0');
                      data = temp*decade+data;
                      decade = decade/10;
                    }
            Serial.println(data);                                            //Datenausgabe bzw mögliche Datenverarbeitung
            decade = 1000;                                                   //Für die nächste For-Schleife die Decade zurücksetzten (4 Stellen ... 1000)
            data = 0.0;                                                            //Zurücksetzten der Daten-Variable für die nächste For-Schleife
          }
    }


michael_x

schon besser, aber wofür brauchst du float ?
Code: [Select]
unsigned int data=0;
for (int i = 0; i< 4; i++)
   data = data * 10 + test.read() - '0';


( Wenn du bei den genau 4 Ziffern je Zeile bleibst)

Go Up