Nabend,
ich komme leider nicht weiter und finde auch nichts was mir das Brett vom Kopft nimmt
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
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
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.
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"));
}
Hey,
erst mal danke für die antwort
ich sehe jetzt nicht wo ich
setup_file;
nicht nutze 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
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 aber versuche so weit wie möglich alleine klar zukommen nur jetzt bin ich an einem punkt wo ich nicht weiter komme
MfG Sven
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
Bei der serielen ausgabe geht das aber wenn ichs mit read in den array schreiben nicht daher das parseInt.
MfG Sven
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.
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:
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:
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.
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
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.