SD Beschreiben und wieder einlesen

Nabend,
ich komme leider nicht weiter und finde auch nichts was mir das Brett vom Kopft nimmt :confused:
Also ich möchte gerne eine SD mit daten Füttern und sie anschließend wieder auslesen aber aber aber …

#include <SPI.h>
#include <SD.h>

File setup_file;

int array_einstell[10];
int array_zahl = 0;


void setup(){
	Serial.begin(9600);
	while (!Serial) {
		; // wait for serial port to connect. Needed for Leonardo only
	}
	pinMode(53, OUTPUT);
	Serial.print("Suche SD Karte...");
	if (!SD.begin(4)) {
		Serial.println("nicht gefunden!");
		return;
	}
	Serial.println("gefunden.");
}

void loop(){
	while (Serial.available() > 0){ // wenn Daten empfangen
		char zeichenStart = Serial.read(); // wenn Startzeichen (Header)
		if (zeichenStart=='y'){
			lesen();
		}
		if (zeichenStart=='x'){ 
			sd_card_schreiben();
		}
	}
}

void lesen(){
	setup_file = SD.open("SETUP.TXT",FILE_READ);
	if (setup_file) {
		Serial.print("Lese -> SETUP.TXT:");
		while (setup_file.available()) {
			array_einstell[array_zahl] = setup_file.parseInt();
			array_zahl = array_zahl +1;
		}
		setup_file.close();
	        for (int kd = 0; kd <= 9; kd++){
		        Serial.println(array_einstell[kd]);
	        }
		Serial.println("Fertig.");
	}
	else {
		Serial.println("Lese fehler -> SETUP.TXT");
	}
}

void sd_card_schreiben(){
	SD.remove("SETUP.TXT") ;
	delay(100);
	SD.open("SETUP.TXT", FILE_WRITE);
	setup_file.close();
	setup_file = SD.open("SETUP.TXT", FILE_WRITE);
	if (setup_file){
		Serial.print("Schreibe Neu -> SETUP.TXT...");
		for (int kd = 0; kd <= 9; kd++){
			int schreib = array_einstell[kd]+21;
			setup_file.println(schreib);
		}		  
		setup_file.close();
		Serial.println("Fertig.");
	} 
	else  {
		Serial.println("Schreibe fehler -> SETUP.TXT");
	}  
}

Lesen geht aber nach dem schreiben bekomme ich nur die alten daten, wenn ich resete und auslese erhalte ich die neuen :’(
Wäre toll wenn einer meinen fehler entdeckt
MfG Sven

Llzzard:
Lesen geht aber nach dem schreiben bekomme ich nur die alten daten, wenn ich resete und auslese erhalte ich die neuen :cry:
Wäre toll wenn einer meinen fehler entdeckt

Ich hatte exakt die gleichen Symptome mit einem RPi und (inkompatibler) SD-Karte. Nach einem Neustart war alles wie vor meinen Änderungen.

Daher mein Tipp: Probiere mal eine andere SD-Karte

Gruß

Gregor

gregorss:
Ich hatte exakt die gleichen Symptome mit einem RPi und (inkompatibler) SD-Karte. Nach einem Neustart war alles wie vor meinen Änderungen.

Daher mein Tipp: Probiere mal eine andere SD-Karte

Gruß

Gregor

hey ,
erst mal danke für die schnelle antwort
aber leider mit anderer karte auch kein erfolg :frowning:

Hallo,

ich sehe erstmal 2 Dinge. Du definierst am Anfang

File setup_file;

nutzt das aber nicht konsequent im Code. Wobei der Name für die Verwendung der SD lib ohne Bedeutung ist. Man kann das auf default "myFile" belassen.

Alle weiteren Aktionen mit der SD-Karte müssen dann in Deinem Fall das "Schlüsselwort" oder wie das heißt "setup_file" enthalten bzw. vorangestellt sein.

Bsp. Du willst was in die Datei schreiben.

setup_file.print("test text"); // schreibt "test text" auf die SD-Karte
setup_file.close(); // schließt das aktuelle File

Entscheidend ist für die richtige Datei die gelesen oder in die geschrieben werden soll der Aufruf.

defaultmäßig schreiben:

myFile = SD.open("SETUP.TXT", FILE_WRITE);
 if (myFile)
   {
    Serial.println(F("writing ... "));  
    myFile.print(" ... ");       
    myFile.print(" ... ");
     .....
    myFile.close();
    Serial.println(F("writing done"));

In Deinem Fall müßtest Du überall myFile durch setup_file ersetzen. Das spielt aber wie gesagt keine Rolle. Wüßte jetzt nicht wofür. Entscheidend ist der Dateiname hinter SD.open

Guck Dir nochmal die Bsp. der SD Lib an.

Wenn Du eine neue Datei anlegen möchtest mußt Du setup_file oder myFile nicht ändern. Nur den Dateinamen hinter SD.open SETUP.TXT zum Bsp. ändern in SETUP2.TXT oder so.

Bsp. alles lesen was in der Datei auf der Karte steht.

myFile = SD.open("SETUP.TXT");
   if (myFile)
     {
      Serial.println(F("open file ... "));  
      while (myFile.available())    // read from the file until there's nothing else in it:
        {
         Serial.write(myFile.read());
        }
      myFile.close();  // close the file:
      Serial.println(F("file closed"));
     }

Ich weis nicht was Du beim lesen parsen möchtest.

Hey,
erst mal danke für die antwort :slight_smile:
ich sehe jetzt nicht wo ich

setup_file;

nicht nutze :sweat_smile: habs halt nur für mich später zum einfacheren auseinander halten umgenannt.
Ich denke auch das ich denn aufruf richtig mache da ich jetzt generell keine probleme beim einzelnen schreiben oder lesen habe, nur halt in abfolge bekomme ich nicht richtige ergebnisse bzw er startet jetzt immer neu :cry:
Das mit dem parsen hab ich jetzt genutzt da ich damit die richtigen ergebnisse bekomme mit "read" nicht.
Wie ihr schon merkt hab ich nicht all zuviel Ahnung :blush: aber versuche so weit wie möglich alleine klar zukommen nur jetzt bin ich an einem punkt wo ich nicht weiter komme
MfG Sven

Hallo,

okay, dann war das mein Fehler beim Code lesen.

Was möchtest Du wie genau machen?
Wofür ist das Array? Wofür das parseint?

Beim lesen brauchste kein parsen. Einfach die Datei komplett auslesen lassen. Fertig. Siehe meinem Bsp. und der Bsp. der SD Lib. Wobei mein Bsp. von der SD Lib abstammt.

Hey,
also ich möchte die sd als scheicherort für meine einstellungen die ich für mein projekt nutze speichern und halt abfragen, verändern und so weiter.
Das Array nutze ich halt für die bestimmten daten sodas ich sie einfacher aufrufen kann, ich hatte es mir so vorgestellt das jede zeile in der txt datei für einen daten satz steht denn ich halt im array fest halte.
Ja das parseInt nutze ich halt weil ich da die richtigen zahlen bekomme die ich halt beim read nicht bekomme, hab es aus irgend einer code zeile meines projekts genommen und es hat dort auch mit den einzelnen auslesen der daten gefunzt da dachte ich es mir hier gehts auch :grinning:
Bei der serielen ausgabe geht das aber wenn ichs mit read in den array schreiben nicht daher das parseInt.
MfG Sven

Hallo,

um gezielt Daten bzw. Zeilen aus einer Datei auszulesen übersteigt auch meine Kenntnisse. Entschuldigung.
Ich weis nur das es dazu schon einmal einen Thread gab. Link? Nach der Methode von Zeilennummer suchen und dann Zeile lesen.

Hallo,

ich versuche dennoch zu helfen, wenn ich mich an etwas erinnern kann.
Vielleicht hilft das schon weiter: http://forum.arduino.cc/index.php?topic=109527.0

Am allgemeinsten geht es wenn man erst mal die komplette Zeile in ein char Array einliest (d.h. einen C String). Das ist die gleiche Vorgehensweise wie wenn man Daten von Serial einliest. Dann kann man sich einen eigenen Parser für den String schreiben (strtok() + atoi()/atof() )

Diese Funktion liest eine Zeile aus (auch von Serial):

/*
Stream auslesen

stream: Referenz auf Stream Objekt, z.B. SD oder Serial
buffer: Ziel-Puffer
buffersize: Puffer Größe

Rückgabewert: true, wenn LF eingelesen wurde, sonst false
*/
bool read_stream(Stream& stream, char* buffer, unsigned int buffersize)
{
  static unsigned int index; //Index im Ziel-Puffer

  while (stream.available()) //solange Zeichen vorhanden sind
  {
    char c = stream.read(); //Zeichen auslesen

    if (c >= 32 && index < buffersize - 1) //wenn Zeichen kein Steuerzeichen und Platz im Puffer
    {
      buffer[index++] = c; //Zeichen abspeichern und Index inkrementieren
    }
    else if (c == '\n') //Zeilen-Ende
    {
      buffer[index] = '\0'; //String terminieren
      index = 0; //Index zurücksetzen
      return true;
    }
  }

  return false;
}

Wichtig: die Zeile muss mit einem CR/LF abgeschlossen sein. Also auch bei einer Zeile muss hinten ein Zeilenumbruch stehen

Hier ist eine Funktion der mal einen Dateinamen Zeilennummer übergibt in die diese Zeile gezielt ausliest

/*
Eine Zeile auslesen in in stringBuffer schreiben

filename: Dateiname
line: gewünschte Zeilennummer

Rückgabewert: Erfolg oder Misserfolg
*/
bool read_line_from_SD(const char* filename, unsigned int line)
{
  File file = SD.open(filename, FILE_READ); //Datei öffnen

  if (file) //wenn Datei geöffnet
  {
    bool found = false;
    unsigned int count = 0; //Zähl-Variable für Zeile (zählt nicht jede einzelne Zeile!)

    while (!found) //solange bis Zeile gefunden
    {
      if (read_stream(file, stringBuffer, STRING_BUFFER_SIZE)) //Zeile auslesen
      {
        if (stringBuffer[0] != 0) //leere Zeile? Wenn nein, weiter
        {
          if (count == line)        //Zeile gefunden?
          {
            found = true;
            break;
          }
          else
            count++; //Index inkrementieren
        }
      }
      else
        break;
    }

    file.close();
    return found;
  }
  else
    return false;
}

Dann definierst du global einen Puffer, z.B.:

const unsigned int STRING_BUFFER_SIZE = 31;
char stringBuffer[STRING_BUFFER_SIZE];

Dann kannst du einfach sowas machen:

bool found = read_line_from_SD("data.txt", 0)

if(found)
{
}

Die Parse-Funktion hängt dann von den Daten ab. Wenn man weiß dass man z.B. immer 4 Integer hat:

void parse_data(int* data)
{ 
   data[0] = atoi(strtok(string_buffer, ",;")); 
   data[1] = atoi(strtok(NULL, ",;"));
   data[2] = atoi(strtok(NULL, ",;"));
   data[3] = atoi(strtok(NULL, ",;"));
}

Oder man liest gezielt einen bestimmten Wert aus:

int get_value_from_line(unsigned int index)
{
  char* ptr = strtok(stringBuffer, ",;"); //Zeiger auf erstes Token
  for (unsigned int i = 0; i < index; i++) //Tokens abtrennen bis gewünschte Wert erreicht ist
    ptr = strtok(NULL, ",;");

  return atoi(ptr);
}

D.h. 0 als Parameter liefert den 1. Wert, 1 den zweiten, etc.

Da gibt es einige Optionen

Der große Vorteil hierbei ist, dass Einlesen und Parsen getrennt sind! Dadurch ändert sich niemals die Einlese-Funktion für eine Zeile. Wenn man möchte kann man sich darüber noch eine Funktion legen die verschiedene Zeilen-Anordnungen verarbeiten (z.B. wenn man in der Text-Datei Titel für Zeilen hat, oder Beschreibungen für die Werte).

Und man kann sich ganz einfach verschiedene Parse-Funktionen für verschiedene Dateien schreiben ohne dass man groß was am Programm ändert.

Ach ja, noch ganz wichtig:
Wenn du Daten in der Datei laufend änderst, musst du unbedingt dafür sorgen, dass die auf eine konstante Breite formatiert werden! Sonst schlägt das katastrophal fehl wenn du z.B. mal “20” mit “100” oder “100” mit “20” überschreibst. Da formatiert man dann einfach die Zahlen je nach Anwendung auf 3 oder 4 Stellen breit.

Wenn man die Daten nur einmal schreibt und dann immer nur ausliest (für feste Konfigurationen) braucht man das nicht

Hier ist die Luxusversion des obigen Codes, mit unterschiedlichen Datei-Varianten, Datentypen und Serial Parser zum Testen:

Der Serial Parser funktioniert so:

iread,0,0
fread,2,3
iwrite,0,0,100
fwrite,0,1,10.5

i = integer, f = float. Das sind zwei verschiedene Dateien. Dann “Zeile,Wert-Index” und für das Schreiben als letzter Parameter der neue Wert

Es gibt auch noch eine Datei “alt.txt” für Zeiten. Die wird mit aread/awrite bearbeitet

Dem Poster ging es da darum, dass er in der Text-Datei auch Beschreibungen der Werte hatte, also z.B.:

Zeiten1: ;12:10;12:30
Zeiten2: ;15:30;18:45

Oder:

Set1
min;max;start;stop
0001;0100;1000;1111

Das kann man aber auch anders machen oder ganz weglassen. Man muss nur die read() Funktion umschreiben. Wenn man keinen Text sondern nur Zahlen will, kann man einfach die Zeilen alle direkt auslesen.

Hey,
@ Doc_Arduino :open_mouth: dennoch vielen dank

@ Serenifly hört sich sehr viel versprechend an aber wie gesagt ich habs echt nicht so drauf … ::slight_smile:

so ich habs fehler nummer 1 ist weg :stuck_out_tongue: bitte nicht hauen

void lesen(){
	setup_file = SD.open("SETUP.TXT",FILE_READ);
	if (setup_file) {
		Serial.print("Lese -> SETUP.TXT:");
		while (setup_file.available()) {
			array_einstell[array_zahl] = setup_file.parseInt();
			//array_einstell[array_zahl] = setup_file.read();
			array_zahl = array_zahl +1;
		}
		setup_file.close();
		Serial.println("Fertig.");
	}
	else {
		Serial.println("Lese fehler -> SETUP.TXT");
	}
	for (int kd = 0; kd <= 9; kd++){
		Serial.println(array_einstell[kd]);
	}
}

da lag der hund begraben … es fehlte was

array_zahl = 0;

ich hab nie den denn array zurück gesetzt …
Ja nur jetzt ist das problem das er nach dem ausführen von ->lesen ->schreiben ->lesen → schreiben also beim zweiten mal schreiben er neustartet :sob:
MfG Sven

edit: neustart fehler auch weg … Array anzahl +1 …
Großes Danke an alle die geholfen haben !

Tu dir einen Gefallen und frage bei sowas immer ab ob dein Index noch in den Array Grenzen liegt. Dann bekommst du keine Probleme wenn mal mehr Text in der Datei steht oder das Array zu klein ist.

Lesen ist eigentlich einfach. Beim Schreiben wird es kompliziert.

Der Code in dem verlinkten Post ist übrigens eher als Demo gedacht. Das ist weit mehr als man für sowas eigentlich braucht, da noch das serielle Interface drin ist und drei verschiedene Datei-Formate verarbeitet werden.

Was man wirklich braucht sind die zwei low-level Funktionen stream_read() und read_line_from_SD() um eine Zeile einzulesen. Dann read() als Einstiegs-Punkt um das Lesen zu starten und get_value_from_line() und eine Parse Funktion um die Zeile zu Parsen.

read() und get_value_from_line() kann man dabei auch zusammenfassen wenn man ein ganz einfaches Datenformat wie das hat:

1000;2000;3000
0100;0200;0300

Also ohne zusätzlichen Text für Überschriften oder Beschreibungen

Für das Schreiben braucht man dann noch write() und write_value() und eine Formatierungsfunktion für den jeweiligen Datentyp (z.B. format_integer() für int). Wobei write() und write_value() da auch Demo-Code ist und etwas einfacher geht wenn die Text Datei simpler ist. Da braucht man keine Dinge wie dass man nur in jede dritte Zeile schreibt, weil die ersten zwei Zeilen nur Überschriften/Text enthalten

Es geht aber auch anders. Man kann auch statt einen einzelnen Wert gezielt zu überschreiben, einfach jedesmal die komplette Zeile neu schreiben. So mache ich das bei mir auf einem TFT. Ich ändere eine Variable, dann erzeuge ich einen String der alle Variablen der Zeile enthält und schreibe den in die entsprechende Zeile. Auch da muss man alle Werte auf eine konstante Breite formatieren.
Das ist vor allem dann praktischer wenn man nur wenige Variablen hat. Wenn es viele sind, dann ist es wahrscheinlich besser man schreibt nur das neu was sich wirklich geändert hat.