SD karte echte floats schreiben und lesen?

Moin moin!

Ich habe mir einen CNC-Plotter aus 2 alten Laufwerken, nem Servo und nem Arduino Nano gebaut.
Dieser empfängt Seriell(USB) von meinem Laptop 5 einzelne Parameter als Float Zahlen und arbeitet die so empfangengen Gcode Befehle ab und holt sich dann neue Parameter.(Funktioniert bestens!)

Nun habe ich mir ein kleines SDCard shield geholt und möchte, dass mein Arduino am anfang des Vorganges alle Gcode-Befehle, sprich alle Float Variablen auf eine SD Karte schreibt und sich diese dann von Zeit zu Zeit abarbeitet.

Wie kann ich nun ECHTE Floats auf die SD Karte schreiben?
Und wie kann ich Sie zu beliebieger Zeit stück für stück nacheinander Auslesen?

versucht habe ich es wie folgt(nicht der komplette sketch):

//Globale Variablen
float G_empfangen = 8; // 8=kein gültiger code Frage Gcode ab!
float X_empfangen=0;
float Y_empfangen=0;
float I_empfangen=0;
float J_empfangen=0;

//SDCard
File GcodeFile;



void setup() 
{
  //... andere sachen

  GcodeFile = SD.open("gcode.txt", FILE_WRITE);// öffne gcode file bereit für lesen/schreiben
  writeSD(); // Schreibt alle Gcode Daten auf gcode.txt
  GcodeFile.close();
  GcodeFile = SD.open("gcode.txt", FILE_READ);// öffne gcode file bereit für lesen
  
  //... andere sachen
}




//SD Card beschreiben
void writeSD()
{
 int i=0;
  
 float Buffer_G = 0;  //inc float
 byte* Buffer_G_adr = (byte*) &Buffer_G; //adresse der float
 float Buffer_X = 0;  //inc float
 byte* Buffer_X_adr = (byte*) &Buffer_X; //adresse der float
 float Buffer_Y = 0;  //inc float
 byte* Buffer_Y_adr = (byte*) &Buffer_Y; //adresse der float
 float Buffer_I = 0;  //inc float
 byte* Buffer_I_adr = (byte*) &Buffer_I; //adresse der float
 float Buffer_J = 0;  //inc float
 byte* Buffer_J_adr = (byte*) &Buffer_J; //adresse der float

 

 while((int)Buffer_G != 9)
 {
  //G
  while(!Serial.available());
  while(Serial.available())
  {
    Serial.readBytes(Buffer_G_adr, 4);     
  }
  GcodeFile.write(Buffer_G_adr, 4);  //Float schreiben 
  //Quittieren mit dem gleichen Wert
  for(i=0; i<4; i++)
  {
  Serial.write(Buffer_G_adr[i]); 
  } 

  //X
  while(!Serial.available());
  while(Serial.available())
  {
    Serial.readBytes(Buffer_X_adr, 4);     
  }
  GcodeFile.write(Buffer_X_adr, 4);  //Float schreiben 
  //Quittieren mit dem gleichen Wert
  for(i=0; i<4; i++)
  {
  Serial.write(Buffer_X_adr[i]); 
  }  

  //Y
  while(!Serial.available());
  while(Serial.available())
  {
    Serial.readBytes(Buffer_Y_adr, 4);     
  }
  GcodeFile.write(Buffer_Y_adr, 4);  //Float schreiben 
  //Quittieren mit dem gleichen Wert
  for(i=0; i<4; i++)
  {
  Serial.write(Buffer_Y_adr[i]); 
  } 

  //I
  while(!Serial.available());
  while(Serial.available())
  {
    Serial.readBytes(Buffer_I_adr, 4);     
  }
  GcodeFile.write(Buffer_I_adr, 4);  //Float schreiben 
  //Quittieren mit dem gleichen Wert
  for(i=0; i<4; i++)
  {
  Serial.write(Buffer_I_adr[i]); 
  } 


  //J
  while(!Serial.available());
  while(Serial.available())
  {
    Serial.readBytes(Buffer_J_adr, 4);     
  }
  GcodeFile.write(Buffer_J_adr, 4);  //Float schreiben 
  //Quittieren mit dem gleichen Wert
  for(i=0; i<4; i++)
  {
  Serial.write(Buffer_J_adr[i]); 
  }
  
 } 
}



//nächsten 5 Parameter von SD Card auslesen
void Gcode()
{
  byte * G_adr = (byte*) &G_empfangen;
  byte * X_adr = (byte*) &X_empfangen;
  byte * Y_adr = (byte*) &Y_empfangen;
  byte * I_adr = (byte*) &I_empfangen;
  byte * J_adr = (byte*) &J_empfangen;
  

  //G
  GcodeFile.readBytes(G_adr, 4);
  
  //X
  GcodeFile.readBytes(X_adr, 4);

  //Y
  GcodeFile.readBytes(Y_adr, 4);

  //I
  GcodeFile.readBytes(I_adr, 4);

  //J
  GcodeFile.readBytes(J_adr, 4); 
  


  //Enable PIN
  digitalWrite(12, HIGH);

  

  


}

Ich habe alles mögliche versucht aber irgendwie keinen erfolg gehabt.
Im Setup() schließe und öffne ich die datei um wieder an den Anfang zu kommen und so nach und nach die 4Byte großen Parameter auszulesen.

es haut aber irgendwie vorne und hinten nicht hin. die erstellte datei ist leer. (0Byte groß)

Ich hoffe ihr könnt mir helfen, sitze da schon einige Tage drann.

Hinweis: Ich kann kein C++ lediglich C.

Vielen lieben dank im Vorraus!!

Hallo,
Drehbank oder Fräse? Ist aber egal, G-Code
Du hast nicht den ganzen SD-Card Code abgebildet, ich weiß also nicht,
wie Du Deine SD-Card ansprichst.
Dein Schreiben halte ich für zu umständlich.
Bei dem Schreiben würde ich es anders machen. Das ganze ist ja PAL-Programmierung.
Also ist es doch so, das Du die Programmdaten zu Sätzen zusammenfassen kannst.
Jeder Satz hat ein bestimmtes Muster.
N, G, X ,Y, Z,F S, T, M
N-Wort, G-Wort x, y, z, F-Wort, S-Wort, T-Wort M-Wort

Den Satz kannst Du doch unter dem N-Wort einwandfrei speichern und wieder
finden.
N120 G00 x45 y10 z50 F S T M09

 if (SdGCodeZl == 3)// nur ein Zähler
       {SdGCode = SD.open("gcode.txt", FILE_WRITE);
       if (SdGCode)
       {
SdGCode.print(N);
SdGCode.print(120);
SdGCode.print(" ");
SdGCode.print(G);
SdGCode.print(00);
SdGCode.print(" ");
SdGCode.print(X);
SdGCode.print(45);
SdGCode.print(" ");
SdGCode.print(Y);
SdGCode.print(10);
SdGCode.print(" ");
SdGCode.println(Z);
SdGCode.print(50);
SdGCode.print(" ");
SdGCode.print(F);
SdGCode.print(" ");
SdGCode.print(" ");
SdGCode.print(X);
SdGCode.print(S);
SdGCode.print(" ");
SdGCode.print(" ");
SdGCode.print(T);
SdGCode.print(" ");
SdGCode.print(" ");
SdGCode.print(" ");
SdGCode.print(M);
SdGCode.print(" ");
SdGCode.println(" ");
 
       SdGCode.close();
       }
       }

Deklarieren mußt Du Dir das selbst. Eingabemaske mußt Du Dir basteln.

Der Satz würde dann z. B. so aussehen:

N120 G00 X45 Y-10 Z50 M09.
Du speicherst immer einen ganzen Satz ab, oder nur das, was Du brauchst.
Lese Beispiel findest Du unter der Lib.
Gruß und Spaß
Andreas
P.S.
float X;
X=32.458
SdGCode.print(X,3);

Danke für deine Antwort. Es ist ein aus dvd Laufwerken gebauter Plotter also X Y und stift anheben absetzen.
Ja an der Übertragung kann man noch viel ändern.

Zu der Initialisierung der SD Karte

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


//im setup steht natürlich noch 
SD.begin(10);

Mehr hab ich mit der garnicht gemacht.

Aber Sinn der Sache ist eigentlich das ich nicht mehr irgendwelche strings und chars verwursten muss sondern direkt mit den floats arbeite.

SkobyMobil:
P.S.
float X;
X=32.458
SdGCode.print(X,3);

Aber die Funktion print() schreibt diese float ja als char Buchstaben in meine Datei!?
Das wiederum müsste ich ja erst wieder beim lesen zurück übersetzen.
Warum also ein Umweg gehen wenn mein PC mir die fertigen floats schon senden kann?

Ich möchte das der uC direkte 4Byte große floats von der SD Karte schreiben / auslesen kann.
Das muss doch irgendwie gehen :slight_smile:

Beste grüße, Perma

Wenn du unbedingt willst: Das geht, entweder mit gecasteten Zeigern, so wie du das gemacht hast

float x = 12.345;
byte * pxb = (byte*) &x; 

for ( byte i = 0; i < sizeof (x); i++) myfile.write(pxb[i]);

oder mit einer union

typedef union {float f; byte b[4];} fbytes;

fbytes x;
x.f = 12.345;  // x als float behandeln
for ( byte i = 0; i < 4; i++) myfile.write(x.b[i]); // x als byte array

Aber warum? Die Größe der Datei spielt keine Rolle, die Geschwindigkeit nicht, auch ein evtl.Genauigkeitsverlust sollte egal sein.
Aber die Datei ist nicht mehr für andere lesbar. Kann nicht editiert werden.

Das Format für Gleitkommazahlen hängt vom jeweiligen Prozessor bzw. FPU ab. Deshalb kommt für Datenaustausch praktisch nur eine textuelle Darstellung in Frage. Der SD Karte und Software ist es egal, ob darauf Zahlen binär oder textuell gespeichert werden, sie müssen nur entsprechend (als variable Strings oder Bitmuster fester Länge) geschrieben und gelesen werden.

Das Format für Gleitkommazahlen hängt vom jeweiligen Prozessor bzw. FPU ab.

Theoretisch ja. Praktisch hat sich (zumindest bei avr-gcc und intel/amd basierten Prozessoren) IEEE 754 LittleEndian durchgesetzt, so dass du, wenn es sein müsste, auf einem PC ein Progrämmchen mit dem gleichen Verfahren schreiben könntest, das die Gleitkommazahl dann auch so lesen kann wie du sie geschrieben hast oder umgekehrt.

Aber, wie gesagt: Wozu ?

Also mein Gedanke dahinter ist, warum den µC aufbürden die Gcode Textdatei zu analysieren und verarbeiten, wenn mein PC das zich mal schneller kann.

Mein Plotter funktioniert ja jetzt schon einwandfrei! Nur bekommt er im moment die Floatvariablen von einer Zeile geliefert und diese arbeitet er ab und fordert sich neue an. Das ganze passiert schon jetzt mit Gleitkommazahlen ohne probleme Seriell!

Nun will ich eigentlich nur am Anfang des Programms einmalig die komplette Gcode-Datei als floats übermitteln, so dass mein Laptop nicht die gesamte Zeit am Arduino gestöpselt bleiben muss. :slight_smile:
Hoffe das hat meine Intention etwas näher gebracht.

Ich habe jedoch probleme die Karte nun vorallem richtig auszulesen!

Ich habe es geschafft die Floats komplett in eine TXT zu schreiben.

ABER

Wenn ich nun die Datei schließe und danach direkt wieder öffne müsste ich ja am Anfang dieser Datei stehen. Der Plan ist nun mir immer die nächsten fünf Floats auszulesen. Das ist dann praktisch so wie er es im moment Seriell bekommt.
Es klapt aber irgendwie nicht wenn ich eigene unterfunktionen aufrufe und dann mal dort fünf floats auslese diese Funktion verlasse, die Floats abarbeite und ich wieder in die unterfunktion springe um die nächsten Floats zu lesen.
Ich hab den eindruck, dass er dann wieder zum anfang springt, oder garnicht lesen kann obwohl die Datei ja die ganze Zeit geöffnet bleibt.

Kann es sein, dass es dort irgendwie in Konflikt mit unterfunktionen kommt? Das er dieses FILE nicht richtig zuordnen kann??

Ob floats als Text oder binär ist das gleiche bei Serial wie bei Dateien, binär ist nur etwas mühsamer ohne Vorteil, aber egal.

So wie du es beschreibst, stimme ich dir zu:

  • es sollte möglich sein, zunächst eine Datei zu schreiben, mit Daten die per Serial eingelesen werden, und diese dann Stück für Stück auszulesen.
  • nach close und open bist du nicht mehr da wo dein Lesezeiger vorher war, sondern am Anfang -- ausser bei   open(name, O_APPEND);

Es klapt aber irgendwie nicht wenn ich eigene unterfunktionen aufrufe und dann mal dort fünf floats auslese diese Funktion verlasse, die Floats abarbeite und ich wieder in die unterfunktion springe um die nächsten Floats zu lesen.
Ich hab den eindruck, dass er dann wieder zum anfang springt, oder garnicht lesen kann obwohl die Datei ja die ganze Zeit geöffnet bleibt.

Wenn irgend was komplett merkwürdig ist, sind gerne mal Variable im RAM überschrieben. oder du arbeitest mit Zeigern ins Nirwana (z.B. auf ungültigen Stack). Insbesondere wenn eine kleine Demo die den Fehler zeigen soll, sich anders verhält als der eigentliche Sketch ...
Aber das (Demo-Programm) würde ich zunächst mal machen: Wenn das den Fehler auch zeigt, findet man das Problem dort leichter.

Vielleicht verwendest Du ein lokales Objekt für die Datei? Das wird dann beim Verlassen des Unterprogramms gelöscht, d.h. die Datei wird geschlossen, und beim nächsten Aufruf wieder geöffnet.

Hallo,
von dieser Bit&Byte-Schieberei habe ich keine Ahnung. Aber ich weiß, das man
damit alles erschlagen kann.

//Globale Variablen
float G_empfangen = 8; // 8=kein gültiger code Frage Gcode ab!
float X_empfangen=0;
float Y_empfangen=0;
float I_empfangen=0;
float J_empfangen=0;

Das kannst Du bis jetzt auf den Arduino schieben, aber es nicht nacheinander
abarbeiten?

Nennen wir das ganze Ding da oben mal Datensatz mit 5 Feldern.

Was ist jetzt abarbeiten?
Datensatz für Datensatz oder Feld für Feld.
Wahrscheinlich ist es so:
wähle ersten Datensatz
ok
wähle aus ersten Datensatz erstet Feld
ok
wenn fertig
wähle aus ersten Datensatz zweites Feld
ok
wenn fertig
u.s.w
wenn letztes Feld im ersten Datensatz fertig ist-
dann zweiten Datensatz abarbeiten wie ersten?

Dann brauchst Du doch nur zwei Zähler, einen DatensatzZähler und einen
FeldZähler. Einfach in einer Schleife.
Oder raff ich da etwas nicht?
Gruß und Spaß
Andreas

Hallo,

verwendest du die Arduino Standard SD Karten Library?
Wenn ja, die hat einen kleinen Bug, der wohl nie komplett behoben wird.
Es gibt aber einen kleinen Workaround für das öffnen und schließen einer Datei.
Vielleicht hilft das ja. Wenn nicht ist das auch kein Fehler, die Modifikation.

C:\Program Files (x86)\Arduino\libraries\SD\src

Datei: SD.cpp

Funktion ab Zeile 337 muß abgeändert werden in

boolean SDClass::begin(uint8_t csPin) {
  /*

    Performs the initialisation required by the sdfatlib library.

    Return true if initialization succeeds, false otherwise.

    Erster Block ergänzt, root wird geschlossen falls offen, damit kann jederzeit erneut initialisiert werden, zum Bsp. nach Kartenwechsel    
    */
    if (root.isOpen()) root.close();      // allows repeated calls
    return card.init(SPI_HALF_SPEED, csPin) &&
         volume.init(card) &&
         root.openRoot(volume);
}