Spezieller Serialmonitor gesucht

Guten morgen liebe Gemeinde.

Ich hab hier zwar schon viel gelesen aber jetzt bin ich auch Offizielles Mitglied geworden denn ich brauch Eure Hilfe.

Zum Problem:

Ich schreibe momentan mit dem Arduino einen G-Codeinterpreter für meinen Eigenbau 3D-Drucker.
probeweise habe ich eine STL Datei in einen G-Code slicen lassen und diese Textdatei über den Seriallport gesendet.

mit einzelnen Zeilen funktioniert das Ganse soweit auch recht gut.

Schicke ich aber die komplette Datei mit 1,2 MB ASCII text legt sich die Sache nach ner weile auf die Nase.

Abgefragt wird im Programm zeilenweise. Das heißt:
Erstmal wird Serial eingelesen bis zu einem NL
dann wird sie Zeile zerlegt in befehl und Koordinaten.
dann wird der Verfahrweg berechnet und die Motoren angesteuert bis die entsprechenden Koordinaten erreicht sind.
dann wird die Nächste zeile eingelesen.

Ich Rate das das Problem darin besteht das der Serialmonitor bzw der Befehl "cat Helper.gcode >> /dev/ttyUSB0" Immer weiter sendet und im Ardoino ein recht kleiner SerialCash drinn ist der dann überläuft.

Meine frage ist nun:
Gibt es eine Möglichkeit die Serielle Schnittstelle so zu konfigurieren das die Übertragung angehalten wird sobald der Cash vom Arduino voll ist oder
Kennt von euch einen Serialmonitor der Textdateien Zeilenweise sendet und auf ein Steuerzeichen des Arduinos wartet bis er eine Neue Zeile sendet. z.B. wenn die verarbeitung mit dem Arduino Abgeschlossen ist lass ich ihn via Serial.println(); einfach ein NL an den PC senden. dieser reagiert darrauf mit dem senden der nächsten Textzeile???

oder hat jemand noch ne andere Idee???

Ich bin bestimmt nicht der Erste bzw. der Letzte mit dem Problem gabe aber nichts gefunden was annähernd mein Problem behandelt.

Kennt von euch einen Serialmonitor der Textdateien Zeilenweise sendet und auf ein Steuerzeichen des Arduinos wartet bis er eine Neue Zeile sendet. z.B. wenn die verarbeitung mit dem Arduino Abgeschlossen ist lass ich ihn via Serial.println(); einfach ein NL an den PC senden. dieser reagiert darrauf mit dem senden der nächsten Textzeile???

Was du beschreibst im Prinzip Software Handshaking mit XON/XOFF:

Ansonsten kannst du selbst ein Programm schreiben. z.B. in C#, Java, Python oder mit was auch immer du dich auskennst.

Den Puffer den du, meinst bezeichnet man als Cache.

Im Gegensatz zu Bargeld, das im Englischen cash heisst.

Danke Serenify, Das wahr SEHR hilfreich!!!

aber so wirklich funktioniert das noch nicht. Ich bin nicht ganz so fit in Serial Kommunikation, um genau zu sein sind das meine ersten Gehversuche und ich bin mir unsicher ob ich den wikilink richtig verstanden habe.

Ich Poste den Protoype womit ich die Serialabfrage im Programm umgesetzt hab. der Ganze Code ist noch nicht fertig und hat mittlerweile über 600 Zeilen. ich glaub das würde den Ramen Sprengen.

String getline() {
  unsigned char c = 0;                         // Zählervariable
  char Zeichenkette[256];                      // Buffer CharArray für Eingelesene Zeile
  Serial.print((char)0x11);                    // DC1 X-ON
  for (c = 0; c != 255; c++) {                 // Zählerschleife zum Auslesen der Eingabezeile
    while (Serial.available() == '\0') {         // Solange kein Zeichen über Serial eingelesen wurde
      delay(1);                                    // Warte eine Millisecunde.
    };
    Zeichenkette[c] = Serial.read();             // Lese Ein Zeichen von der Serialen Schnittstelle 
    if ((Zeichenkette[c] == 13) || (Zeichenkette[c] == '\n') || (Zeichenkette[c] == '\r')) {  // Wenn das Ausgelesene zeichen ein Return oder Newline ist dann
      Zeichenkette[c] = '\0';                      // Setze Nullbyte  
      break;                                       // Dann breche zählerschleife ab.
    };
  };
  Serial.print((char)0x13);                    // DC3 X-OFF
  Zeichenkette[c] = '\0';                      // Überschreibe letztes Zeichen mit Nullbyte und defeniere Stringende
  String Buffer = Zeichenkette;                // Überspiele den CharArray mit der Ausgelesenen Zeile in ein Sring
  Buffer.trim();                               // Schneide alle überflüssigen Lerzeichen raus
  return Buffer;                               // Liefere den Sring als wertigkeit im Befel getline();
};

der Buffer läuft anscheinend immer noch über...

Verwendet wird das Programm CuteCom.
Handschake ist auf Software eingestellt.
Die Steuerzeichen tauchen auch nicht in dem Communikationsfenster auf. Also sollten sie teoretisch interpretiert werden.
(Nichtaskizeichen werden im "/0xFF" Format Dargestellt.)
Allerdings tauchen Halbe und abgeschnittene Zeilen auf.

Kann mir Jemand helfen???

Sorry, aber das ist Schrott. Unübersichtlich, unlogisch, blockierend und verschwenderisch.

Hier ist grundlegender Code wie man eine Zeile in ein char Array einliest. Ohne Fluss-Steuerung:

const int SERIAL_BUFFER_SIZE = 60;
char serialBuffer[SERIAL_BUFFER_SIZE];

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  if (readSerial(Serial))
  {
    Serial.print("Read: "); Serial.println(serialBuffer);
  }

}

bool readSerial(Stream& stream)
{
  static byte index;

  while (stream.available())
  {
    char c = stream.read();

    if (c >= 32 && index < SERIAL_BUFFER_SIZE - 1)
    {
      serialBuffer[index++] = c;
    }
    else if (c == '\n' && index > 0)
    {
      serialBuffer[index] = '\0';
      index = 0;
      return true;
    }
  }
  return false;
}

Das liest erst mal nur eine Zeile ein und gibt sie aus.

Das Prinzip ist einfach: readSerial() wird ständig aufgerufen und schaut nach oben Daten da sind. Wenn nicht kehrt die Funktion gleich zurück. Wenn was da ist wird es eingelesen und nachgeschaut was es ist. Wenn es das Endzeichen ist wird der String terminiert und die Funktion gibt true zurück. Wenn es ein anderes Zeichen ist wird es abgespeichert falls noch Platz im Array ist. Die Funktion gibt false zurück wenn das Ende noch nicht erreicht ist.
Man fragt also auf den Rückgabewert ab und wenn er true ist hat man eine Zeile. In der Zwischenzeit kann man dann noch andere Dinge tun.

Du würdest das dann so machen:

  if (readSerial(Serial))
  {
    Serial.write(0x13);
 
    //Auswertung

    Serial.write(0x11);
  }

Nach dem Block wird dann automatisch wieder readSerial() ständig aufgerufen und geht weiter

Lerne außerdem wie man direkt mit C Strings arbeitet. Da gibt es massig Funktionen um Strings zu splitten und in Integer zu konvertieren. Oder nach Zeichen und Teil-Strings zu suchen. Das alles speicherschonend und in situ.
Wenn es wirklich unbedingt die Arduino String Klasse sein muss, dann verwende sie wenigstens korrekt. Erstelle ein Objekt und reserviere mit reserve() Speicher. Dann ist es auch nicht so schlimm wenn man jedes Zeichen mit + anhängt, wobei man auch bei der String Klasse auf jeden Index direkt zugreifen kann.

Aber erstelle nicht ein char Array und kopiere das in ein String Objekt. Das ist völlig unnötig und verschwendet nur Speicher. Gerade bei so großen Arrays. Auf String Objekte kann man wie gesagt aber komplett verzichten. Das geht alles direkt mit null-terminierten char Arrays.

Mojens. Ersmal nochmal nen dickes Danke,

in gcc hatte ich den G-Code-Converter damals schon halb fertig. Wollte damals die Schrittmotorsteuerung über die LPT lösen was sich aus ättlichen Problemen auf die Nase gelegt hat.

Kurts um, ich bin dann vor die Wand gerannt das die verwendeten Bibliotheken nicht im Arduinopaket mit drin sind und sie nicht inklodiert werden konnten. Also hab ich mich darann gemacht und hab die Strings in Arryas zerlegt und die entsprechend verwurstet hab.

Das ich hier nen Array dreckt als String zurück geben kann war mir neu. Bin davon ausgegangen das ich erst die Variablen konvertieren muss.

Hab's umgebastelt und noch weitere Funktionen direkt eingebaut um das zuschnippeln aufs wesentliche direkt abzuarbeiten.

Kurze Erläuterungen zu G-Code;
Mit Semikolon werden Kommentare in G-Code definiert, (das heißt, alles was ab einem ";" kommt ist für den Programmablauf Überflüssig)
Lehrzeichen sind nur zur Menschlichen Lesbarkeit im G-Code, (Werden also beim Auslesen ignoriert)
Eine Zeile Besteht aus einem Befehl und eventuell Koordinaten. (Typische Zeile währe G1 X100.305213 Y100; Kommentar);
Groß und Kleinschreibung sollte ignoriert werden.

was hälst du von dem Folgendem Code zum auslesen einer Anweisungszeile???

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  String Line = getorder(Serial);
  if (Line != '\0')
  {
    Serial.print("Read: "); Serial.println(Line);
  }
//  Serial.print("#");
}

String getorder(Stream& stream)
{
  static byte index = 0;                                       // Statische Zählervariable für die Zeileposition mit 0 anlegen
  const byte BUFFER_SIZE = 255;                                // Größe des Arryas in einer variabele festlegen
  char Buffer[BUFFER_SIZE];                                    // Arrya für inhalt der Befelsleiste angeben
  char Zeichen ='\0';                                          // Zeichenvariabele Leer anlegen
  static boolean Kommi;                                        // Remanenter Blockiermerker 

  Serial.print((char)0x11);                                    // DC1 X-ON
  while (stream.available())                                   // Solange noch ein Zeichen im SerialCache
  {
    Zeichen = stream.read();                                   // Lese das Nächste Zeichen ein
    if (index == 0) { Kommi = false; };                        // Feststellen ob ein Kommentar anfängt und Blokiermerker setzen 
    if (isLowerCase(Zeichen)) { Zeichen = Zeichen - 32; };     // Aus Kleinbuchstaben Großbuchstaben machen  
    if (isGraph(Zeichen) && index < BUFFER_SIZE - 1)           // Ist das eingelesene Zeichen Druckbar und der Buffer nicht übergelaufen
    {
      if (Kommi == false) { Buffer[index++] = Zeichen; };      // Wenn noch nicht ein Kommentar gefunden wurde Übertrag das Zeichen in den Buffer und Zähl eins hoch
    }
    else if ((Zeichen == '\n' || Zeichen == '\r') && index > 0)// ist das zeichen nicht Druckbar und [( wird Newline oder Return erkannt) und es ist mindestens das 2. zeichen]
    {
      Buffer[index] = '\0';                                    // Dann setze Nullbyte als Stringende
      index = 0;                                               // Und setze Zähler auf 0
      Serial.print((char)0x13);                                // DC3 X-OFF
      return Buffer;                                           // und gebe die ausgewertete Zeile zurück
    };
    if (Zeichen == ';') { Kommi = true; };                     // Wird das Zeichen für Kommentar erkannt setze den Blockiermerker
  };
  return (String)'\0';                                         // Sind keine Zeichen im SerialCache dann Gebe NullByte zurück
};

Der Test mit meiner Beispiel-G-Codedatei läuft gerade.

fox00014:
was hälst du von dem Folgendem Code zum auslesen einer Anweisungszeile???

Überhaupt nichts.

  • es werden Strings benutzt -> späterer Crash vorprogrammiert

  • die Routine funktioniert nur, wenn die gesamte Zeile bereits empfangen ist,
    oder während ihrer Laufzeit vollständig wird, das ist regelmäßig nicht der Fall

  • die Rückgabe der Adresse eines lokalen Puffers geht selten gut,
    ob der implizite Cast zum String dies Problem umgeht will ich überhaupt nicht testen.

const byte XOFF = 0x13;
//Serial.print((char)0x13);
Serial.write(XOFF);

Kurts um, ich bin dann vor die Wand gerannt das die verwendeten Bibliotheken nicht im Arduinopaket mit drin sind und sie nicht inklodiert werden konnten. Also hab ich mich darann gemacht und hab die Strings in Arryas zerlegt und die entsprechend verwurstet hab.

Was du hast ist die AVR libc:
http://www.nongnu.org/avr-libc/user-manual/modules.html

Das sind größtenteils Standard C Funktionen mit ein paar GNU Erweiterungen. Hier sind besonders die zwei wichtig:
http://www.nongnu.org/avr-libc/user-manual/group__avr__string.html
http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html

In stdlib.h sind Konvertierungs-Funktionen nach Integer und Float und umgekehrt

Inkludieren musst du die nicht. Die sind schon mit der Arduino Software drin

Ich würde dir auch raten dich was die Rückgabe-Werte betrifft an meinen Code zu halten

Wenn du was gegen globale Variablen hast, dann mach es so:

char* getorder(Stream& stream)
{
  static char buffer[BUFFER_SIZE];     //muss static sein!!

  

  return NULL;         //NULL zurückgeben wenn noch nicht fertig
}

Und return buffer; wenn fertig

Und dann auf NULL abfragen

char* buffer =  getorder(Serial);

if (buffer != NULL)
{
}

Also wenn der Rückgabe-Wert ungleich Null ist man fertig.

Das Puffer muss dann aber unbedingt static sein, da du keine Zeiger auf lokale, nicht-statische Variablen zurückgegeben kannst.

Wenn du dann ein String Objekt willst, kannst du das immer noch an der Stelle kopieren. Aber dazu eine Warnung: Das ist eben eine Kopie! Du hast einen 256 Byte Puffer. Mit dem Objekt sind dann zumindest kurzzeitig 500 Bytes belegt. Ich nehme an, du hast vielleicht einen Mega. Dann wäre das wahrscheinlich nicht so schlimm. Aber auf kleineren Prozessoren sollte man das nicht tun.

Groß und Kleinschreibung sollte ignoriert werden.

Wenn du willst kannst du das auch noch später in einem Rutsch konvertieren. Da gibt es strlwr() (string lower) dazu:
http://www.nongnu.org/avr-libc/user-manual/group__avr__string.html#ga119342b34031ba4ee28f4e38a22c5f0d
Oder strupr() für Großbuchstaben

Du kannst es aber auch beim Einlesen machen. Geschmackssache

@Whandall

die Rückgabe der Adresse eines lokalen Puffers geht selten gut,
ob der implizite Cast zum String dies Problem umgeht will ich überhaupt nicht testen.

Er gibt keine Adresse zurück. Sondern ein Objekt. Welchen der Compiler dank return value optimiziation sogar wegoptimieren kann. Eine gute Idee ist das aber trotzdem nicht.

Übrigens will er indirekt Zeichen von einer SD-Karte lesen. Das heißt es sollte irgendwann man ein CR/LF ankommen, auch wenn zwischendurch Zeichen verloren gehen.
Die Ausnahme ist vielleicht am Ende, wenn die letzte Zeile nicht richtig ankommt. Es wäre aber auch kein Problem hier zusätzlich ein Timeout einzubauen und das Einlesen abzubrechen wenn eine gewisse Zeit lang nichts ankommt.

Also, ich binn grad dabei eure Vorschläge umzusetzen und nachzulesen.

In der Theorie wollt ich alles mit String umsetzen grade wegen der Speicherbelastung. String gegenüber char Array hat den vorteil das man sich nicht darum scheren muss ob man in einen nicht definierten Speicherbereich schreibt und der String verbraucht im Speicher immer nur soviel wie auch drin steht. Und muss nicht dauerhaft 255 Zeichen haben um sicher zu stellen das auch Mörderkommentare nicht das einlesen von "new line" verhindert...
Daher auch die Idee die Kommentare beim einlesen gleich zu identifizieren und das schreiben in die char Array dabei sofort zu unterbinden und die Lehrzeichen rauszuwerfen.

Auf der anderen Seite hab ich auch ein wenig Angst davor Bibliotheken zu inklodieren da diese das Programm aufpumpt da ja alle dort enthaltenen Prototypen mit Komeliert werden.
Zudem hab ich hir mal irgendwo gelesen das String nicht string ist. Soll heißen das der String Arduino nicht das gleiche wie string c++ sein soll sondern irgendwie ne abgespeckte Version ist oder so. Genau hab ich es damals nicht gelesen da ich eigentlich was anderes gesucht hatte und nur durch Zufall drauf gestoßen bin.

Zu den Früchten eurer Arbeit bis jetzt:

Im bereich Serialkommunikation scheint das ersetzen von Serial.prit((char)0x13); durch Serial.write(0x13); für XOFF des Rätzels Lösung gewesen zu sein. ich teste aber noch. Warum das so ist hab ich allerdings noch nicht verstanden. Das heist doch nicht ohne Grund SteuerZEICHEN???

Was ich oben vergessen hatte zu erwähnen ist das "new line" bzw "Return" in G-Code die Befehlszeile beendet.
das Semikolon wird dort verwirrender weise als Satzende bezeichnet. aber sowas wie (G91; G30 X0 Y0 Z0; Nullpunkt fahrt\n) geht nicht.
das wird dann (G30G91 X0 Y0 Z0; Nullpunkt fahrt\n) geschrieben.
Also ist "New Line" unbedingt ervorderlich.
G-Code ist auch nicht wirklich eine Programiersprache. eher sowas in die Richtung html. beschreibt was zu tuen ist.

ich Poste hier mal ne kleine Brocke von ner G-Codedatei zum besseren Verständnis wie son ding aufgebaut ist.

; diese datei hat 41346 Zeilen
; generated by Slic3r 1.0.0RC2 on 2014-03-16 at 00:48:36

; layer_height = 0.4
; perimeters = 3
; top_solid_layers = 3
; bottom_solid_layers = 3
; fill_density = 0.4
; perimeter_speed = 30
; infill_speed = 60
; travel_speed = 130
; nozzle_diameter = 0.5
; filament_diameter = 3
; extrusion_multiplier = 1
; perimeters extrusion width = 0.50mm
; infill extrusion width = 0.53mm
; solid infill extrusion width = 0.53mm
; top infill extrusion width = 0.53mm
; first layer extrusion width = 0.70mm

G21 ; set units to millimeters
M107
M104 S200 ; set temperature
G28 ; home all axes
G1 Z5 F5000 ; lift nozzle

M109 S200 ; wait for temperature to be reached
G90 ; use absolute coordinates
G92 E0
M82 ; use absolute distances for extrusion
G1 F1800.000 E-1.00000
G92 E0
;G92 E0
G1 Z0.350 F7800.000
G1 X86.978 Y85.612 F7800.000
G1 E1.00000 F1800.000
G1 X87.569 Y85.122 E1.02495 F540.000
G1 X89.728 Y83.562 E1.11164
G1 X90.309 Y83.192 E1.13402
G1 X90.928 Y82.882 E1.15658
G1 X93.359 Y81.802 E1.24309
G1 X93.999 Y81.552 E1.26545
G1 X94.659 Y81.372 E1.28771
G1 X97.258 Y80.812 E1.37424
G1 X97.959 Y80.702 E1.39729
G1 X98.659 Y80.662 E1.42010
G1 X101.329 Y80.662 E1.50697
G1 X102.029 Y80.702 E1.52978
G1 X102.728 Y80.812 E1.55284
G1 X105.329 Y81.372 E1.63937
G1 X105.989 Y81.552 E1.66163
G1 X106.619 Y81.792 E1.68356
G1 X109.059 Y82.872 E1.77038
G1 X109.689 Y83.192 E1.79337
G1 X110.279 Y83.572 E1.81620
U.S.W.

fox00014:
String gegenüber char Array hat den vorteil das man sich nicht darum scheren muss ob man in einen nicht definierten Speicherbereich schreibt

Wird beim Einlesen hier berücksichtigt

 if (index < SERIAL_BUFFER_SIZE - 1)
  • 1 damit noch Platz für den Terminator ist

und der String verbraucht im Speicher immer nur soviel wie auch drin steht.

Beim ursprünglichen Einlesen ja. Wobei es auch bei Strings nicht verkehrt ist reserve() zu machen, damit nicht ständig realloc() ausgeführt wird wenn man den String größer machen muss.

Aber es wird u.a. ein neues Objekt erstellt wenn du Dinge wie substring() machst. C Strings kann man dagegen in situ bearbeiten! Eine Such-Funktion liefert einfach einen Zeiger im bestehenden String zurück.

Daher auch die Idee die Kommentare beim einlesen gleich zu identifizieren und das schreiben in die char Array dabei sofort zu unterbinden und die Lehrzeichen rauszuwerfen.

Kommentare gleich zu entfernen ist sicherlich eine sehr gute Idee.

Das mit den Leerzeichen würde ich mir aber überlegen. Die könnte man nämlich beim Splitten des Strings sehr schön als Delimiter verwenden :slight_smile:
strtok() mit dem Leerzeichen machen und dann die Teil-Strings analysieren

Im bereich Serialkommunikation scheint das ersetzen von Serial.prit((char)0x13); durch Serial.write(0x13); für XOFF des Rätzels Lösung gewesen zu sein. ich teste aber noch. Warum das so ist hab ich allerdings noch nicht verstanden. Das heist doch nicht ohne Grund SteuerZEICHEN??

Ich dachte eigentlich auch dass es letztlich das Gleiche tun sollte.

Auf der anderen Seite hab ich auch ein wenig Angst davor Bibliotheken zu inklodieren da diese das Programm aufpumpt da ja alle dort enthaltenen Prototypen mit Komeliert werden.

Es werden nur die Funktionen kompiliert die auch verwendet werden. Man holt sich am Anfang recht viel rein, aber nicht alles. Aber generell frisst die String Klasse schon mehr Speicher und ist viel langsamer.

Zudem hab ich hir mal irgendwo gelesen das String nicht string ist. Soll heißen das der String Arduino nicht das gleiche wie string c++ sein soll sondern irgendwie ne abgespeckte Version ist oder so.

Die Arduino String Klasse ist eine Art Wrapper für einen Teil AVR lib c Funktionen. Ein Problem ist dass man nicht viel damit machen kann. Gerade an Konvertierungs- und Formatierungs-Funktionen mangelt es deutlich.

Und sowas wie strtok() gibt es auch nicht. Es gab mal split(), aber das war auch nicht so toll, das es ein Array aus neuen String Objekten geliefert hat. Auf einem PC ist das kein Problem. Aber auf einem µC frisst das einfach viel Speicher. Vielleicht wurde es deshalb entfernt. kA.

fox00014:
In der Theorie wollt ich alles mit String umsetzen grade wegen der Speicherbelastung. String gegenüber char Array hat den vorteil das man sich nicht darum scheren muss ob man in einen nicht definierten Speicherbereich schreibt und der String verbraucht im Speicher immer nur soviel wie auch drin steht. Und muss nicht dauerhaft 255 Zeichen haben um sicher zu stellen das auch Mörderkommentare nicht das einlesen von "new line" verhindert...

Einen Puffer maximaler Größe dauerhaft zur Verfügung zu stellen, ist in deinem Fall ganz sicher sinnvoll, da das Programm ja die ganze Zeit zeilenweise das Script abarbeitet. Wo die Daten herkommen ist ja erst einmal nebensächlich. Die Hoffnung jederzeit einen String von 255 Zeichen dynamisch anlegen zu können ist sehr optimistisch. Bei Programmen würde ich mich auch weniger auf Hoffnung verlassen wollen.

Strings und strings sind natürlich nicht das Gleiche. Strings sind eine C++ Abstraktion von strings, die in beiden Varianten letztlich die Daten beinhalten. Wegen der geringen Menge an Speicher (2/8/16k) in die alle statischen Variablen, der Stack, alle gleichzeitig aktiven lokalen Variablen und noch etwas Platz für Interrupts passen müssen, sollte man diesen Platz sehr kontrolliert verwenden.

fox00014:
Im bereich Serialkommunikation scheint das ersetzen von Serial.prit((char)0x13); durch Serial.write(0x13); für XOFF des Rätzels Lösung gewesen zu sein. ich teste aber noch. Warum das so ist hab ich allerdings noch nicht verstanden. Das heist doch nicht ohne Grund SteuerZEICHEN???

Serial.print(0x13) druckt '1' und '9' (den Wert als Zahl in dezimaler Darstellung)
Serial.write(0x13) druckt ein einzelnes Zeichen mit dem Wert 19 bzw. 0x13 (ein Zeichen mit dem Wert)
Deshalb geht das mit dem Steuerzeichen besser.

Hier mal ein sehr grobes Beispiel wie man das einfach machen könnte:

char str[] = "G1 X90.928 Y82.882 E1.15658";

void setup()
{
  Serial.begin(9600);

  splitString(str);
}

void loop()
{
}

void splitString(char* str)
{
  char* ptr = strtok(str, " ");

  while (ptr != NULL)
  {
    analyzeToken(ptr);
    ptr = strtok(NULL, " ");
  }
}

void analyzeToken(char* str)
{
  if (strcmp(str, "G1") == 0)
  {
    Serial.println(F("Vorlauf"));
  }
  else
  {
    switch (str[0])
    {
      case 'X':
        Serial.print(F("X: "));
        Serial.println(atof(str + 1), 3);
        break;
      case 'Y':
        Serial.print(F("Y: "));
        Serial.println(atof(str + 1), 3);
        break;
      case 'E':
        Serial.print(F("E: "));
        Serial.println(atof(str + 1), 5);
        break;
    }
  }
}

Wie gesagt nur grob. Letztlich muss man hier ja mehrere Varianten unterscheiden:

G1 Z0.350 F7800.000
G1 X86.978 Y85.612 F7800.000
G1 E1.00000 F1800.000
G1 X87.569 Y85.122 E1.02495 F540.000
G1 X89.728 Y83.562 E1.11164

Alles G1 aber mit verschiedenen Parametern

Wirklich beschäftigt habe ich mich dem Thema G Code aber nicht. rudirabbit hat glaube ich einen G Code Parser geschrieben. Mir geht es eher eher um die Verwendung von C Strings. Einfacher wird das mit der String Klasse sicherlich nicht

Eine Option ist es den Parser als Zustands-Automaten auszuführen. So dass man wenn der erste Parameter nach G1 mit 'Z' anfängt in einem anderen Zustand ist als wenn ein 'X' oder 'E' kommt. Dann weiß man auch welche Teil-Strings danach zu erwarten sind. Das ist wahrscheinlich das Vernünftigste.

Befor ich es Vergessen. Jain: G1 ist am häufigsten verwendet.
G1 = Liniere Symmetrische Bewegung.
G0 = Schnellstmögliche Bewegung ohne Symmetrie.
G28 = Nullpunktfahrt
usw.

Gelöst ist es bei mir das ich erst nach G oder M suche.
G = Befehl
M = MaschienenMackro wie Werkzeugwechsel Temeratur setzen und warten bis temp. erreicht ist usw.

Danach wir Nach XYZES gesucht und die Passenden Variablen gesetzt.
zusammen wird das dann an den passenden Prototype für G1 G0 ... Übergeben.

Der berechnet dann und steuert die Motoren an.

Uff. Also erstmal das Ergebnis des ersten Testes.

Read: G1X84.995Y106.804F7800.0
Read: G1X85.956Y100.782F7800.0
Read: G1X85.Y101.038E16.63409F2443.9621X85.019Y101.271E16.660561X84.848Y101.909E16.67998
Read: G1X85.102Y103.558E16.72906
Read: G1X85.189Y103.646E16.73270
Read: G116.73573
Read: G1X86.396Y103.592E.77227
Read: G1X85.691Y103.780E169372
Read: G1X85.Y104.653E16.82028
Read: G1X87.24Y106.007E16.88626
Read: G1X87.23Y106.057E16.88778
Read: G1X86.4Y106.290E16.91424
Read: G1X86.Y106.719E16.92731
Read: G1X87.39Y108.704E16.99469
Read: G1X87.90Y108.567E17.01025
Read: G1X88.Y107.598E17.03974
Read: G1X88.5Y108.235E17.06135
Read: G1X88.40108.701E17.07555
Read: G1X89.04Y109.338E17.10202
Read: G1X88.7Y110.350E17.13283
Read: G1X89.6Y109.455E17.16884
Read: G1X89.5Y109.472E17.17077
Read: G1X89.1Y110.810E17.21150
Read: G1X90.43Y111.924E17.26068
Read: G1X91.0Y111.749E17.28055
Read: G1X91.2Y111.030E17.30244
Read: G1X91.73111.357E17.31881
Read: G1X91.59111.883E17.33484
Read: G1X92.22112.520E17.36131
Read: G1X92.06113.110E17.37928
Read: G1X92.53Y113.388E17.39517
Read: G1X92.7112.654E17.41752
Read: G1X93.44Y112.461E17.43946
Read: G1X94.45112.907E17.47171
Read: G1X94.7Y113.192E17.48356
Read: G1X94.45114.244E17.51559
Read: G1X94.93114.458E17.53106
Read: G1X95.23113.327E17.56551
Read: G1X95.33113.301E17.56848
Read: G1X97.13Y113.752F7800.000
Read: G1X97.2Y113.865E17.57319F2443.91X96.951Y114.962E17.606581X97.460Y115.070E17.62189
Read: G1X97.747Y113.999E17.65448
Read: G1X97.989Y113.935E17.66184
Read: G17800.000
Read: G1X99.755Y114.537E.68218F2443.962
Read: G1X99.559Y1.269E17.704X100.097Y115.269E17.72027
Read: G1X100.257Y114.672E17.73845
Read: G1X101.126Y114.439E17.76491
Read: G1X101.231Y114.048E17.77679
Read: G1X102.198Y115.143E17.81973

Wie ihr seht haut etwas bei der Datenübertragung nicht hin. Teile des Textes verschwinden einfach. Interessanter weise gibt es weniger Crashs bei geringerer Datenübertragungsrate. Könnt nen Bufferproblem sein was heißen würde das die Handshake Geschichte nicht funktioniert.
Oder es ist ein Problem mit dem Timing des Serial Signals wo ich jetzt echt nicht wüsste was ich da noch machen könnte.
Eventuell kommt es auch PC seitig her. vom Programm CuteCom, vom Betribsystem SuSE Linux, vom Serial Treiber.
Ich werde erstmal den Handshake testen. Nach dem XOFF Signal bau ich nen delay(3000); ein oder so um optisch an den LED's auf dem Board sehen zu können ob das überhaupt funktioniert.

Mensch meier, ich will doch nur ne ASCII Datei an den Arduino schiken. Dat kann doch nit so schwer sein.... Dachte ich.... Denken is wohl glücksache.

Eure Artikel hab ich übrigens schon überflogen und bin wirklich von eurer Hilfsbereitschaft und den Informationen begeistert.
Danke, So fängt es wieder an spaß zu machen.

Ich füge die oben genannte bremse rein und füttere den Arduino wieder. in der Zeit wie das läuft Schau ich mir eure Ausführungen genau an.

Kurtsfassung. Der Software Handshake funktioniert nicht...

aktuelles Programm sieht noch so aus:

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  String Line = getorder(Serial);
  if (Line != NULL)
  {
    Serial.print("Read: "); Serial.println(Line);
  }
//  Serial.print("#");
}

char* getorder(Stream& stream)
{
  const byte XOFF = 0x13;
  const byte XON = 0x11;
  static byte index = 0;                                       // Statische Zählervariable für die Zeileposition mit 0 anlegen
  static const byte BUFFER_SIZE = 255;                                // Größe des Arryas in einer variabele festlegen
  char Buffer[BUFFER_SIZE];                                    // Arrya für inhalt der Befelsleiste angeben
  char Zeichen ='\0';                                          // Zeichenvariabele Leer anlegen
  static boolean Kommi;                                        // Remanenter Blockiermerker 

  if (stream.available() == false) stream.write(XON);          // Ist der SerialBuffer Leer dann sende DC1 X-ON
  while (stream.available())                                   // Solange noch ein Zeichen im SerialCache
  {
    Zeichen = stream.read();                                   // Lese das Nächste Zeichen ein
    if (index == 0) { Kommi = false; };                        // Feststellen ob ein Kommentar anfängt und Blokiermerker setzen 
    if (isLowerCase(Zeichen)) { Zeichen = Zeichen - 32; };     // Aus Kleinbuchstaben Großbuchstaben machen  
    if (isGraph(Zeichen) && index < BUFFER_SIZE - 1)           // Ist das eingelesene Zeichen Druckbar und der Buffer nicht übergelaufen
    {
      if (Kommi == false) { Buffer[index++] = Zeichen; };      // Wenn noch nicht ein Kommentar gefunden wurde Übertrag das Zeichen in den Buffer und Zähl eins hoch
    }
    else if ((Zeichen == '\n' || Zeichen == '\r') && index > 0)// ist das zeichen nicht Druckbar und [( wird Newline oder Return erkannt) und es ist mindestens das 2. zeichen]
    {
      Buffer[index] = '\0';                                    // Dann setze Nullbyte als Stringende
      index = 0;                                               // Und setze Zähler auf 0
      stream.write(XOFF);                                      // Sende DC3 X-OFF
      delay(5000);
      return Buffer;                                           // und gebe die ausgewertete Zeile zurück
    };
    if (Zeichen == ';') { Kommi = true; };                     // Wird das Zeichen für Kommentar erkannt setze den Blockiermerker
  };
  return NULL;                                         // Sind keine Zeichen im SerialCache dann Gebe NullByte zurück
};

Egebnis ist das Die Komplette Datei durchgesendet wird ohne bei XOFF anzuhalten.
den Delay sieht man ledeglich an der Ausgabe des Arduinos im Fenster. Read: Kommt da mit der Verzögerrung an.
Emfangs LED ist schon lange dunkel während noch Meldungen ankommen und die SendeLED noch blitzt.
Die LED'S sind auf dem Arduinobord Vorinstalliert.
Die EmpfangsLED ist durchgänig am Anfang am leuchten. das heist XOFF wird komplett ignoriert...

Ideen??? :cry:

Ich habe kein Linux, aber ich habe mal mit RealTerm probiert. Normal nehme ich HTerm, aber das macht kein Software Handshaking (nur Hardware).

Was ich da habe, ist dass das nach ein paar Zeilen aufhört :frowning:
Aber es liegt nicht am Arduino! Wenn per Hand eine Zeile schickt reagiert er darauf! Also läuft das Programm und hängt nicht irgendwo

Aber davor macht er XON/XOFF (glaube ich) korrekt. Mit diesem Code:

void loop()
{
  if (readSerial(Serial))
  {
    Serial.write(XOFF);
 
    Serial.println("ack");
 
    delay(1000);

    Serial.write(XON);
  }
}

Ich glaube zumindest dass die RX LED nur alle Sekunde blinkt...

Wieso das nicht durchläuft kann ich aber nicht sagen :frowning:

Vielleicht liegt es bei dir auch eher am Terminal Programm

Nachtrag: CoolTerm zeigt genau das gleiche Verhalten :frowning:

Es sieht doch danach aus dass das Terminal Programm nicht auf XOFF reagiert. Wenn ich auf dem Arduino kein Delay mache empfange ich alles. Mit dem Delay kann es sein dass dann der Eingangspuffer zu schnell gefüllt wird. Und der ist nach 64 Zeichen voll.

nur wo Klemmt es. Ich mein.

Terminalprogramm Problem ist sehr wahrscheinlich.

Aber eventuell hängt es daran das wir für den Software Handshake Was im Protokoll vergessen haben was dieses erst überhabt aktiviert oder sowas.

Oder Werden die Steuerzeichen immer noch irgendwie falsch gesendet das sie nicht Interpretiert werden.
Mit anderen Betriebssystemen zu testen finde ich jetzt gar nicht so schlecht. Gibt zumindest hinweise ob es ein OS-BUG ist oder eher an Programm und oder Terminalprogramm Liegt.

----------------------- Nachtrag ------------------------

hab noch ein weiteres Teminalprogramm gefunden. moserial macht auch nen ganz guten eindruck.
Serialport ist eingestellt auf:

Gerät: /dev/ttyUSB0
Bautrate: 9600
DatenBits: 8
Stopbits: 1
Parität: None
Handshake: Software
Zugifsmudus: Read and Write
Lokales Echo: AUS

Erstmal bis hier her richtig???

Oben kann ich verfolgen das ich nach dem Verbindungsaufbau kontinuierlich 0x11 XON empfange.
nach dem Senden eines Befehls z.B.: "G0 X10000" erhalte ich 0x13 XOFF. und das delay lauft. nach ablauf erhalte ich dann nach der Ausgabe wieder 0X11. Soweit scheint es zu laufen.

Sende ich zwei Befehle schnell Hintereinander über die Eingabezeile werden diese sofort gesendet und nicht auf 0x13 reagiert. OK. kann an dem manuellen Senden liegen.

Nun will ich eine Datei Senden. Nach dem ich die Datei ausgewählt habe erhalte ich die meldung: "Warte auf Gegenstelle".
Oben kann ich sehen das weiterhin 0x11 XON entgangen wird.
das Heist doch das wir im Übertragungsprotokoll irgendwas vergessen haben oder???

-------------------------- Nachtrag die 2. ---------------------------------
nach Wikipedia Datenflusssteuerung – Wikipedia gibt es DC1 bis DC4.
XON ist DC1 ist 0x11
XOFF ist DC3 ist 0x13
aber was ist DC2 und DC4. und welche Zeichen entsprechen denen? 0x12 und 0x14?

Kopie aus Wiki:
Software-Flusssteuerung, Software-Handshake, Software-Protokoll oder X-ON/X-OFF[Bearbeiten]
Eine Software-Flusssteuerung wird durch in die Datenübertragung eingefügte Zeichen gesteuert.
Im ASCII-Zeichensatz (ITU-T-Empfehlung T.50) sind die ersten 32 Zeichen für Steuerungsaufgaben reserviert. Vier davon, DC1 bis DC4 (Device Control), sind Gerätesteuerzeichen.
Die Software-Flusssteuerung sollte davon die folgenden Zeichen benutzen:
DC1 (oft als X-ON bezeichnet, engl. für Transmission ON, Zeichencodierung 11hex bzw. 17dez, PC-Tastatur: Strg-Q) und
DC3 (oft als X-OFF bezeichnet, engl. für Transmission OFF, Zeichencodierung 13hex bzw. 19dez,PC-Tastatur: Strg-S).
Diese Zeichen sind sowohl in Richtung Endeinrichtung zum Übertragungsgerät als auch umgedreht nutzbar.
In der Datenübertragung mit Modems gibt es oft die Möglichkeit, diese Zeichen durch Konfiguration umzustellen.
Anwendung[Bearbeiten]
Ist der Sendespeicher des lokalen Modems fast gefüllt, wird das X-OFF-Steuerzeichen in die Empfangsdaten zur eigenen Endeinrichtung eingefügt. Sobald dieser Speicher zur Gegenstelle gesendet wurde und damit wieder leer ist, wird das X-ON-Steuerzeichen eingefügt und damit die Blockierung der Endeinrichtung aufgehoben. Die Übertragungsleitung ist hierdurch vor Datenverlusten gesichert.
Probleme[Bearbeiten]
Beim Versand von Binärdaten dürfen die beiden Steuerzeichen nicht in den Daten auftauchen, da sonst die Datenübertragung unterbrochen wird. Die Zeichen müssen maskiert werden, z. B. dadurch, dass die ganze Datenübertragung so umkodiert wird, dass die Daten als ASCII-Werte der hexadezimalen Zahlen gesendet werden. Ein vor Jahren oft genutztes Format war der Hex-Record von Intel. Dadurch wurde das zu übertragene Datenvolumen aber verdoppelt. Obwohl durch die Umkodierung innerhalb der zu übertragenen Dateien die X-ON/X-OFF-Steuerzeichen nicht mehr vorkommen, war eine Übertragung oft nicht möglich. Das Protokoll X-Modem beinhaltet zum Beispiel einen fortlaufenden Blockzähler von 00hex bis FFhex, so dass unabhängig von den zu übertragenen Daten jedes Datenbyte auftritt.
Die Software-Flusssteuerung sollte nur genutzt werden, wenn es keine Alternative gibt.

--------------------------------------- Nachtrag die 3. ----------------------------------------------------------

Mikrocontrollernet hat eine sehr gute allgemeine Erklärung. aber wirklich weitergebracht hats mich auch nicht...

http://www.mikrocontroller.net/articles/RS-232

Oder Werden die Steuerzeichen immer noch irgendwie falsch gesendet das sie nicht Interpretiert werden.

Bei mir werden die richtig gesendet und empfangen. Aber die Terminal Programme reagieren irgendwie nicht drauf. Getestet mit Realterm und CoolCom. In RealTerm werden sie sogar als DC1 und DC3 angezeigt! In CoolTerm sieht man es wenn man auf Hex Ansicht geht.

Was da schief läuft weiß ich nicht. Es kann ja nicht sein, dass das gerade bei "Datei Senden" ignoriert wird, oder?

Oben kann ich verfolgen das ich nach dem Verbindungsaufbau kontinuierlich 0x11 XON empfange.
nach dem Senden eines Befehls z.B.: "G0 X10000" erhalte ich 0x13 XOFF. und das delay lauft. nach ablauf erhalte ich dann nach der Ausgabe wieder 0X11. Soweit scheint es zu laufen.

Ich würde XON nur senden nachdem man XOFF gemacht hat und es weiter gehen kann. Das behebt aber das Problem nicht.

Für mich sieht es so aus als ob er kontinuierlich weiter sendet und der Eingangspuffer des Arduino voll läuft, da er während des delay() natürlich nicht geleert wird

Was hilfreich wäre wenn man den gesendeten Text irgendwie anzeigen könnte, so dass man sieht wann was gesendet wird. Aber das erklärt dann immer noch nicht wieso er nicht aufhört.

aber was ist DC2 und DC4. und welche Zeichen entsprechen denen? 0x12 und 0x14?

Das sind allgemeine Steuerzeichen. Bedenke dass dieser Kram mal für Drucker, Telex u.ä. entwickelt wurde. Dann wurde später verschiedene Übertragungsprotokolle für Computer entwickelt, die mit den Zeichen zum Teil anderen Dinge gemacht haben.

Eine andere Option wäre Hardware Handshaking mit CTS (Clear to Send). Dazu brauchst du aber einen externen TTL/USB Wandler, da du auf den Wandler auf dem Arduino keinen direkten Zugriff hast. Den kann man dann auch nicht an der normalen 0/1 Schnittstelle betreiben, sondern muss sich entweder eine Schnittstelle in Software emulieren (per AltSoftSerial) oder einen Controller mit mehreren Hardware Schnittstellen nehmen.

EDIT:
Ich hatte hier ein paar Tests gemacht und dann angenommen dass er auf XON/XOFF korrekt reagiert. Das war aber Blödsinn. Man sieht nur dass der Arduino die Pause macht. Man sieht nicht wann wirklich gesendet wird. Wahrscheinlich geht das an einem Stück.

Dass es mit kurzen Texten geht, kann eigentlich nur bedeuten dass der PC ständig sendet und der Eingangspuffer des Arduinos voll läuft. Der ist nach 64 Bytes voll. Den größer zu machen bringt auch nichts. Damit verschiebt man das Problem nur.

Man müsste wie gesagt mal angezeigt kommen was gerade gesendet wird. Dann könnte man genau vergleichen was wann abläuft. Erklärt aber immer noch nicht wieso er nicht aufhört zu senden.

Unter Windows gibt es wohl nen Freewareprogramm Namens SerialSniffer. Hersteller Download
Damit sollte man den Stream Visoalisieren können.

Bohr artet das aus.

Also zusammen gefasst haben wir festgestellt das mehrere Terminalprogramme auf XOFF während der Dateiübertragung nicht reagieren. Also Schließe ich mal nen Bug in den Programmen aus.

Wir haben festgestellt das der Arduino Serial Buffer überläuft da das Programm nicht auf XOFF Reagiert.
Bei allen Terminalprogrammen war Softwarehandshake ausgewählt.

Hardwarehandshake geht nicht on Board nur 3 Drat Verdratung zu der RS232 vorliegt. Also sind die Protokolleintragungen Kurzgeschlossen.

Also gibt es nur 2 Möglichkeiten.
Bei der normalen Dateiübertragung existiert kein Softwarehandshake.
Oder etwas fehlt damit das Protokoll aktiviert wird.

zu dem Zwek hab ich nach der Bedeutung der Steuerzeichen gegooglelt
Wiki eintrag.
Dummerweise nicht vollständig und bis jetzt hab ich auch noch nichts über das Softwarehandshake gefunden was über unsere bis jetzt spärliche informationen hinaus geht.

Währ schon mal gut zu wissen ob ich da Geister jage oder nicht...

Andere Protokolle hab ich auch schon versucht zu verstehen.
Xmodem übertägt immer in 127 Bit Blöcken. Doof für Zeilenweise Übertragung.
Serialdrucker nur Text eingerichtet. und getestet. Nix keinen Brumm. Und keine infos über das Druckprotokoll.
Erst die Komplette datei einlesen. Das wird wohl den Speicher des Arduinos volkommen sprengen.
Daten auf ner SD-Karte zwischen Speichern. macht ca. 1000 Zyklen danach ist die Durchgeritten.
Alten Ram. Viel zu fiele Pins.
Floppy, ist zu langsam und nicht groß genug. und zu fiele pins.
Festplatte. ist mit Kanonen auf Spatzen. Teuer. und keine Ahnung über SATA.
SSD oder USBStik hat auch Zyklenbeschrenkungen.
Flashspeicher sind einfach nit für dauerhaftes schreiben gemacht.

Also fällt das komplette übertragen der Daten wohl auch raus. Oder???

fox00014:
Hardwarehandshake geht nicht on Board nur 3 Drat Verdratung zu der RS232 vorliegt. Also sind die Protokolleintragungen Kurzgeschlossen.

Es geht mit einem externen USB/TTL Wandler. Da kann man CTS über den Konverter führen

Sich ein eigenes Programm zu schreiben, dass die Datei ausliest und entsprechend pausiert wäre eine Option. Das ist etwas Arbeit, aber nicht allzu kompliziert. Die Einzel-Aufgaben (Datei auslesen. Serial Kommunikation) sind recht trivial.

Es wäre aber nett rauszufinden wieso die Programme einfach munter weitersenden. Das kann ja nicht sein wenn man XON/XOFF aktivieren kann.

Externen USB -RS232 Konverter währe eine Option. aber mir wiederstrebt einfach die Tatsache etwas anzubauen was ja Eigendlich schon da ist. mal Ehrlich. das kann es doch nicht sein oder. Wozu Kauft man dann nen Board wenn ma eigendlich doch nur den IC gebraucht hätte und mit Pegelwandlern und allem doch anfängt.
Ne menge Mehrarbeit ohne wirklichen Zugewinn und es sind mindestens 4 IO's weg. Dazu kann man 1 und 2 auch nicht benutzen.
macht dann schon 6 IO's die weg sind. Das muss aderst gehen.

Warum die Programme nicht reagieren ist die große Frage. Ich versteh es auch nit wirklich. das einzige was ich mir vorstellen kann ist das dieses Softwarehandschaking erst noch mit einem anderen Steuerzeichen Aktiviert werden muss oder die Datei Binär gesendet wird was Die ASCII Zeichen umkodieren würde. Und das Terminalprogramm das wieder zurückcodiert.

Eigenes Sendeprogramm zu schreiben wiederstrebt mir eigentlich nur weil ich am Schluss für alle Betriebssysteme was basteln sollte. Und ich das Rad damit Neu erfinden muss.

Extrem cool währe eine Lösung mit einem Standarttreiber oder so. Modemtreiber oder Generec. Nur Text Drucker an RS232. Oder nen Programm was es schon gibt auf allen OS wie z.B. Putty. das ist aber Wiederum als Fernwartungs Konsole gedacht und sprengt wieder den Ramen.

Ideen was man noch testen könnte oder wie man z.B. das mit dem Druckertreiber oder dem Modemtreiber weiter kommt???
halt was benutzen womit XON und XOFF schon früher funktioniert hat?