Hallo
Ich habe ein SD kartenmodul das ich auslesen und beschreiben will.
Das Modul ist da und alles wird richtig erkannt, das läuft also.
Nur mit dem lesen und beschreiben habe ich kein glück.
Ich will in die textdatei nur eine zahl schreiben und später wieder auslesen.
Kurzfassung:
Ich habe einen zähler (int drinkzaehler;) den ich nach einem durchlauf um einen erhöhe.
Meine idee:
Erste zeile der textdatei auslesen und an drinkzaehler übergeben.
Den drinkzaehler wert um einen erhöhen.
Den alten wert in der datei löschen.
Den neuen wert wieder zurück schreiben.
Ich kriege es nicht hin !
Das ist jetzt der sketch womit ich versuche das zu bewerkstelligen.
#include <SD.h>
#include <SPI.h>
#define SDdaten_Lenght 5 // Länge zeichenfolge von sd lesen
File dataFile;
char SDdaten[SDdaten_Lenght]; // SD Daten 5 zeichen lang
int drinkZaehler = 0; // Zähler für fertige drinks auf 0 setzen
byte SDdata_count = 0; // SD array zähler
const int chipSelect_Pin = 53; // Port an dem CS des kartenlesers angeschlossen ist
void setup() {
SD.begin(53);
pinMode(chipSelect_Pin, OUTPUT); // CS Port SD Kartenleser als ausgang festlegen
}
void loop() {
dataFile = SD.open("dzahl.txt", FILE_READ); // Datei öffnen auf SD Karte
if (dataFile)
{
while (dataFile.available())
{
Serial.write(dataFile.read());
SDdaten[SDdata_count] = dataFile.read(); // schreibt in das SDdaten array
SDdata_count++; // verschiebt SDdaten array um 1
}
dataFile.close(); // Datei schliessen
drinkZaehler = SDdaten; // SDdaten an drinkZaehler übergeben
drinkZaehler++; // drinkZaehler für die drinks einen hochzählen
Serial.println(drinkZaehler);
}
else
{
Serial.println("Fehler beim Lesen dzahl.txt");
}
//---------------------------------------------- // Schreiben auf SD Karte
dataFile = SD.open("dzahl.txt", FILE_WRITE);
if (dataFile) // ist die datei verfügbar dann...
{
dataFile.println(drinkZaehler); // schreib die daten in den file
dataFile.close(); // Datei wieder schliessen
}
else
{
Serial.println("Fehler beim schreiben in dzahl.txt");
}
}
Das wandeln von char , byte und int macht mir glaube ich die grössten probleme.
Und das löschen des wertes in der ersten zeile der datei.
Ich kann damit in die datei schreiben nur immer untereinander !
Und er schreibt immer das falsche was wohl am konvertieren liegt
Auch die begrenzung auf 5 zeichen wie ich sie oben angegeben habe muss nicht sein.
Er kann ruhig den inhalt der ersten zeile komplett einlesen solang wie sie ebend ist.
Wird eh nicht übermässig viel werden.
Wäre jemand bereit mir bei dem Problem zu helfen ?
Bei dir fehlen jegliche Kenntnisse zur Behandlung von Strings in C.
Achtung:
Diese Version geht davon aus dass die Zahl in der Text-Datei mit einem LF abgeschlossen ist!! Also wenn du die Datei per Hand anlegst unbedingt danach einen Zeilenumbruch machen! Man kann das auch so machen, dass das Ende der Datei abgefangen ist, aber da war ich zu faul dazu.
Und die Zahl muss in der ersten Zeile stehen.
In setup() ist kurzes Code Stück dass eine Datei korrekt erstellt. Wenn die einmal existiert darf man das natürlich nicht mehr machen. Sonst wird die alte überschrieben.
Um die Zahl dann richtig zu überschreiben ist es am einfachsten man formatiert sie auf eine konstante Breite und füllt vorne mit Nullen auf. Das wird in printNumber() gemacht. Wenn man wirklich immer nur inkrementiert bräuchte man es nicht unbedingt, aber sehr wohl wenn man eine Zahl auch mit kleineren Zahlen überschreiben kann (z.B. "10" mit "9"). Das ist aber nicht viel Code. Also kann man es auch gleich so machen.
readStream() liest einen String ein bis zu einem LF. Danach steht der String in stringBuffer.
Der Test-Sketch inkrementiert mit jedem eingelesenen Zeichen von Serial den Wert in der Datei. Was man eingibt ist egal. Du kannst auch mehrere Zeichen auf einmal eingeben. Bei "aaa" wird dann dreimal inkrementiert.
#include <SD.h>
#include <SPI.h>
const int NUMBER_WIDTH = 5;
const int STRING_BUFFER_SIZE = 10;
char stringBuffer[STRING_BUFFER_SIZE];
File dataFile;
void setup()
{
SD.begin();
Serial.begin(9600);
//Datei neu erstellen. Mit 0 anfangen
if (SD.exists("test.txt"))
SD.remove("test.txt");
dataFile = SD.open("test.txt", FILE_WRITE);
if (dataFile)
{
printNumber(dataFile, 0);
//Datei auslesen
dataFile.seek(0); //Dateizeiger auf Anfang setzen
readStream(dataFile);
Serial.print("read: "); Serial.println(stringBuffer);
Serial.println();
dataFile.close();
}
}
void loop()
{
if (Serial.available())
{
Serial.read();
dataFile = SD.open("test.txt", FILE_WRITE);
if (dataFile)
{
dataFile.seek(0);
//Datei lesen
readStream(dataFile);
Serial.print("read: "); Serial.println(stringBuffer);
//String konvertieren und inkrementieren
unsigned int value = atol(stringBuffer);
value++;
//Zahl zurückschreiben
dataFile.seek(0);
printNumber(dataFile, value);
//wieder auslesen
dataFile.seek(0);
readStream(dataFile);
Serial.print("new value: "); Serial.println(stringBuffer);
Serial.print("as integer: "); Serial.println(atol(stringBuffer));
Serial.println();
dataFile.close();
}
}
}
void printNumber(Stream& stream, unsigned int value)
{
unsigned long num = 10;
for (int i = 0; i < NUMBER_WIDTH - 1; i++)
{
if (value < num) stream.print('0');
num = num * 10;
}
stream.println(value);
}
bool readStream(Stream& stream)
{
static byte index;
while (stream.available())
{
char c = stream.read();
if (c >= 32 && index < STRING_BUFFER_SIZE - 1)
{
stringBuffer[index++] = c;
}
else if (c == '\n' && index > 0)
{
stringBuffer[index] = '\0';
index = 0;
return true;
}
}
return false;
}
WOW !!! :o
Da magst du recht haben das ich damit probleme habe.
Aber es ist auch das erste mal das ich was mit einem Arduino mache oder C.
Ich werde mal versuchen den code in meinen Sketch zu übernehmen.
Ich dachte wie du an meinem beispiel gesehen hast das es mit viel weniger code geht.
Das was du gepostet hast hätte ich mit sicherheit nicht hin bekommen.
Ich muss das erstmal Testen.
Bis dahin Danke ich dir für deine Hilfe.
Ich werde berichten !
bye
Serenifly ist unser String Guru, das macht der mit links und 40 Fieber.
Mußt nur genau lesen was er schreibt und testen und ausprobieren. Ggf. nachfragen. Du packst das.
Topper_Harley:
Ich dachte wie du an meinem beispiel gesehen hast das es mit viel weniger code geht.
Es geht auch mit weniger, aber der Code ist dann schlechter und weniger allgemein.
Wie gesagt bräuchte man in deinem Fall die Zahl nicht unbedingt auf eine konstante Breite zu formatieren. Aber irgendwann willst du dann vielleicht doch mal was anderes machen als nur zu Inkrementieren. Dann würdest du da schnell Probleme bekommen.
Man kann auch das Einlesen anders machen. Aber so ist es vollkommen allgemein. Mit readStream() kann man allen möglichen Text einlesen (nicht nur von SD, sondern auch von Serial oder Ethernet!). Du könntest damit auch mehr als eine Zahl einlesen. Ich verwende das gleiche Prinzip für Config Dateien mit Strings wie "100,200,300". Da liest man dann die entsprechende Zeile aus teilt den String in Teil-Strings auf und konvertiert diese. Beim Zurückschreiben kann man der Einfachheit halber alle Werte neu schreiben, auch wenn sich nur einer geändert hat.
Es ist auch kein Problem darüber eine weitere Funktion zu legen die eine bestimmte Zeile aus einer Datei ausliest. Dazu muss man nur mehrmals readStream() (ein besserer Name wäre wohl readLine() ) aufrufen und die Anzahl der Aufrufe zählen. Dann kann man Dateien mit mehreren Zeilen und mehreren Werten pro Zeile haben.
Das schöne ist dann, dass der Code um eine Zeile einzulesen gleich bleibt. Man muss also nicht alles neu schreiben nur weil man das Format der Datei geändert hat.
Ein paar deiner grundlegenden Fehler waren folgende:
1.) Dein eingelesener String ist nicht unbedingt NULL-terminiert. Sondern nur wenn du weniger als 5 Zeichen einliest (da das Array am Anfang alles auf NULL steht. Und das auch nur weil es global ist).
2.) Du weißt dann einfach den String an eine Integer Variable zu. Um Strings in Zahlen zu konvertieren braucht man Funktionen wie atoi(), atol() oder atof(). Das steht für "ascii to int/long/float"
3.) Wenn du eine Datei zum Schreiben öffnest steht der Dateizeiger am Ende der Datei. Deshalb steht in meinem Code immer wieder file.seek(0) um auf den Anfang zu gehen
Ansonsten war es nicht völlig falsch und ging schon stark in die richtige Richtung
Und wie gesagt ist das nur Test-Code! Der Teil in setup() ist nur um erst mal die Datei zu erstellen. Das kann man auch per Hand machen. In fertigem Code willst du da vielleicht nicht jedesmal die Datei löschen und bei Null anfangen, sondern eine bestehende Datei beibehalten. Kommt auf die Anwendung an.
Genauso lese ich nach dem Schreiben die Zeile wieder zur Kontrolle aus. Das dient nur dazu um es zu Veranschaulichen und dir zu zeigen dass es funktioniert. In deinem eigentlichen Code brauchst du das nicht.
Hi
Saubere Arbeit funktioniert prima.
mit einer kleinen einschränkung die von meinem aufbau kommen kann.
Ich habe ja 2 Mega2560 per sda/scl verbunden.
Schreiben soll der slave auf die SD.
Er macht alles sobald ich das hier auskommentiere:
void loop()
// if (Serial.available())
// {
// Serial.read();
Und natürlich auch die schliessende klammer weiter unten.
Wenn ich das weg lasse macht er alles wie gewollt.
Er bekommt aus irgendeinem grund wohl eine meldung das Serial nicht verfügbar ist und dann macht er nix.
Ist aber für mich jetzt nicht weiter schlimm weil ich oben weiter ja schon die sd abfrage und dort kriege ich immer ein OK das sd da ist und auch volumen und,und ,und.
Also ist das wohl eher ein schönheitsfehler der mit anderen umständen zusammen hängen wird.
Ich bin auf jeden fall Glücklich das dieses problem auch gelöst ist.
Noch ein paar kleine hürden und dann können die Fahrprogramme für die Motoren und Servo, geschrieben werden :-))
Das ist wie gesagt nur ein Test-Sketch. Du willst das Schreiben ja irgendwie anders auslösen. Ist mache das in Test-Sketches nur immer gerne mit Serial weil das sehr einfach geht und man keine Taster anschließen muss.
Der Quick and Dirty Weg um eine Zahl auszulesen ist übrigens parseInt()
Das hätte hier gereicht, aber ich habe mal bewusst etwas weiter ausgeholt. Ein Nachteil ist dass es blockierend ist, was bei Serial große Probleme bereitet. Bei SD wäre das kein Problem gewesen (da man nicht auf eintreffende Daten warten muss), aber wenn die Datei komplexer wird (mehrere Werte und vor allem mehrere Zeilen) sind andere Optionen besser.
Wenn der String nicht riesig lange ist (was bei Ethernet Anwendung manchmal der Fall ist), ist es in den meisten Fällen am besten man liest eine Zeile bis zum Ende ein und bearbeitet dann diese Zeile. Egal was die Datenquelle ist. Das ist nicht der kürzeste Code, aber man ist am flexibelsten. Der größte Vorteil dabei ist dass Einlesen und Parsen/Bearbeiten getrennt sind. Wenn sich das Format einer Zeile ändert, muss man nur die Auswertung der Zeile anders machen.
ich habe Deinen Code auch mal ausprobiert, nur leider ersetzt er nicht die erste Zeile, sondern holt sich den Wert aus der ersten Zeile, inkrementiert ihn und schreibt ihn in die zweite Zeile. Führe ich das Ganze nochmal aus, dann holt er sich wieder den Wert aus der ersten Zeile, inkrementiert und fügt es in die dritte Zeile ein.... usw.
Habe versucht ein wenig dran rum zu basteln, aber es ändert sich nichts.
Tja, das habe ich mir auch gedacht. Wahrscheinlich sehe ich den Wald vor lauter Bäumen wieder nicht.
Ich schicke gegen Abend mal den Code, die Ausgabe des Seriellen Monitors und den Inhalt der Textdatei.
Bis dann.
Würde mich freuen, wenn Du nachher noch mal vorbei schaust.
Wahrscheinlich sehe ich den Wald vor lauter Bäumen wieder nicht
Immer besser, solche Probleme erstmal mit "einzelnen Bäumen", also einem minimalen Testprogramm, anzusehen. Wenn dann der Fehler weg/anders ist, war in der Regel eine der anderen, aus Versehen ungeprüften, Annahmen falsch.
void printNumber(Stream& stream, unsigned int value) {
unsigned long num = 10;
for (int i = 0; i < NUMBER_WIDTH - 1; i++) {
if (value < num) stream.print('0');
num = num * 10;
}
stream.println(value);
}
bool readStream(Stream& stream) {
static byte index;
while (stream.available()) {
char c = stream.read();
if (c >= 32 && index < STRING_BUFFER_SIZE - 1) {
stringBuffer[index++] = c;
} else if (c == '\n' && index > 0 || c == '\r' && index > 0) {
stringBuffer[index] = '\0';
index = 0;
return true;
}
}
return false;
}
Hier die Ausgabe des Seriellen Monitors:
read: 00000
read: 00000
new value: 00000
as integer: 0
read: 00000
new value: 00000
as integer: 0
read: 00000
new value: 00000
as integer: 0
read: 00000
new value: 00000
as integer: 0
read: 00000
new value: 00000
as integer: 0
read: 00000
new value: 00001
as integer: 1
read: 00001
new value: 00002
as integer: 2
read: 00002
new value: 00003
as integer: 3
read: 00003
new value: 00004
as integer: 4
read: 00004
new value: 00005
as integer: 5
read: 00005
new value: 00006
as integer: 6
read: 00006
new value: 00007
as integer: 7
read: 00007
new value: 00008
as integer: 8
read: 00008
new value: 00009
as integer: 9
read: 00009
new value: 00010
as integer: 10
Und es steht dann auch entsprechend nur das in der Datei:
00010
Wie gesagt es ist wichtig dass auch diese einzelne Zeile mit einem Zeilenumbruch abgeschlossen ist. Extra abzufragen ob die Datei zu Ende ist spare ich mir