Serielle Schnittstelle zeilenweise auslesen

Hallo miteinander,

ich befasse mich gerade mit der Seriellen Schnittstelle, und versuche wenn etwas an das Arduino gesendet wird dies Zeilenweise auszulesen, doch bisher mit geringen erfolg.

ich habe mir ein Test Programm:

/* 
  Hier ist eine Kleine Übung für die Serielle Schnittstelle: 
  
      1. Variablen Definieren 
      2. Serielle Schnittstelle Starten 
      3. Bekannte Befehle über Serielle Schnittstelle ausgeben
    DO 
      4. Warten bis etwas über Serielle Schnittstelle kommt 
      5. Befehl auswerten und antworten
    LOOP  
  
  
  Ator: Muecke  
  Bord: Arduino Mega 2560
  BJ: 03/14
*/

int incomingByte = 0;   // for incoming serial data

void setup()
{
 Serial.begin(9600);                                                                              //  serial port starten mit der Baudrate 9600
 Serial.println("Arduino Mega 2560 meldet sich zum Dienst.");                                     //  Ausgabe auf dem Seriellen Monitor am PC 
 Serial.println("Ich kenne auch schon ein paar Befehle:");                                        //  Ausgabe auf dem Seriellen Monitor am PC 
 Serial.println("Befehle beginnt und endet mit einem : (Doppelpunkt)");                           //  Ausgabe auf dem Seriellen Monitor am PC 
 Serial.println("");                                                                              //  Ausgabe auf dem Seriellen Monitor am PC 
 Serial.println(":Name:    ich antworte mit meinem Namen");                                       //  Ausgabe auf dem Seriellen Monitor am PC 
 Serial.println(":Version:    Firmware Nr.");                                                     //  Ausgabe auf dem Seriellen Monitor am PC
 Serial.println("Leider kann ich noch nicht antworten");                                          //  Ausgabe auf dem Seriellen Monitor am PC
 
}

void loop()
{
 if (Serial.available() > 0)                                                                       // Wenn Serieller Datenpuffer größer 0 Byte ist dann mach weiter   
  {
   incomingByte = Serial.read();                                                                   // Serieller Datenpuffer in "incomingByte" schreiben !! immer nue 1 Byte 
                                                                                                   // Das ausgelesene Zeichen wird gelöscht aus dem Serieller Datenpuffer
   Serial.print("Empfangen erhielt: ");
   Serial.println(incomingByte);
  }
}

mir wird jedoch immer jedes einzelne Byte zurück gegeben, ich hätte gerne immer das die gesamte Zeile vorhanden ist. damit ich das dann mit den Befehlen von mir abgleichen kann um das Arduino etwas machen zu lassen.

kann mir da jemand etwas weiterhelfen? einen Tipp geben?

Gruß Muecke

Muecke:
kann mir da jemand etwas weiterhelfen? einen Tipp geben?

Das letzte mal habe ich dazu erst vor wenigen Tagen hier einen Test-Sketch gepostet:
http://forum.arduino.cc/index.php?topic=226573.msg1638763#msg1638763

Die Funktion, die zeilenweise empfängt, heißt in diesem Sketch "receiveBuffer()".

Der Sketch insgesamt macht aber mehr als eine Zeile empfangen, er wertet dabei auch drei gesendete Werte aus (X, Y, Z) und weist diese jeweils in dem Augenblick an Variablen zu, wenn eine Zeile komplett empfangen wurde.

danke für den Link, auch wen ich da irgend wie nicht mit klar komme :frowning:

kann es sein das wenn ich über den Seriellen Monitor vom Arduino arbeite beim Senden kein Zeilenumbruch geschieht?

Bildschirmfoto 2014-03-26 um 15.29.29.png
Bildschirmfoto 2014-03-26 um 15.29.29.png (26 KB)

Du kannst das Endzeichen einstellen. Auf keines, CR, LF oder CR+LF. Du willst hier nur CR (Wagenrücklauf).

Serenifly:
Du kannst das Endzeichen einstellen. Auf keines, CR, LF oder CR+LF. Du willst hier nur CR (Wagenrücklauf).

Mein Code funktioniert mit einem beliebigen Zeilenendezeichen.

Standardmäßig ist das CR.
Im seriellen Monitor funktionieren die Einstellungen "Sowohl NL als auch CR" oder "Zeilenumbruch CR".

Mit der Umstellung im Code auf:

#define ZEILENTRENNZEICHEN 10

funktionieren im seriellen Monitor die Einstellungen "Sowohl NL als auch CR" oder "Neue Zeile NL".

Mein Code funktioniert nicht, wenn überhaupt kein Zeilenende-Trennzeichen gesendet wird.

um eine Antwort von deiner Code zu bekommen muss ich doch 3 Zahlen hintereinander schreiben z.B.
10 10 10
und dann auf Senden gehen.

mit den unteren Teil für das Zeilenende habe ich alles schon ausprobiert,
ich Heb auch schon die Zahlen mit Komma (,) Komma Punkt (:wink: Doppelpunkt (:slight_smile: getrennt.

irgend wie verstehe ich den Code nicht.

Muecke:
um eine Antwort von deiner Code zu bekommen muss ich doch 3 Zahlen hintereinander schreiben z.B.
10 10 10
und dann auf Senden gehen.

Nö.

Das ist zwar eine Zeile, die ausgewertet wird, aber keine Zeile, mit denen die Variablen neu gesetzt werden.

Ich glaube, Du hast in dem Thread den Beitrag mit den Vorgaben des Themenstarters nicht gelesen. Er schrieb:
Ich möchte dort eine Eingabe machen(X20 Y30 Z10)

D.h. um die Werte neu zu setzen, müßtest Du eingeben:

X20 Y30 Z10

ah Ok,
dann schaue ich mal ob ich nun weiter komme.
Danke.

Wenn ich einen Text empfange sagen wir mal: Hallo
dann bekomme ich immer als Rückantwort Zahlen :frowning: ich gehe mal davon aus das das ascii Zeichen sind,
wie kann ich den Arduino sagen das das Buchstaben sein sollen?
bzw. ich meine das er die Zeichen wie auf der Tastatur erkennt Zahlen als Zahlen und Buchstaben als Buchstaben.

Muecke:
Wenn ich einen Text empfange sagen wir mal: Hallo
dann bekomme ich immer als Rückantwort Zahlen :frowning: ich gehe mal davon aus das das ascii Zeichen sind,
wie kann ich den Arduino sagen das das Buchstaben sein sollen?

Ob ein empfangenes Zeichen als "char" (ASCII-Zeichen) oder als "byte" (Zahl zwischen 0 und 255) interpretiert wird, ist Dir vollkommen selbst überlassen. Du kannst Dir ein empfangenes Zeichen als alles ausgeben lassen: ASCII-Zeichen, Dezimalzahl, Hexadezimalzahl, Binärzahl.

Beispiel-Sketch zum "Hallo" oder was auch immer Senden:

void setup() {
  Serial.begin(9600);
  Serial.println("Char\tDezimal\tHex\tBin");
}

void loop() {
  char c;
  byte b;
  if (Serial.available())
  {
    char c=Serial.read();
    b=c;
    Serial.print(c);
    Serial.print('\t'); // Horizontaler Tabulator (Steuerzeichen)
    Serial.print(b);
    Serial.print('\t'); // Horizontaler Tabulator (Steuerzeichen)
    Serial.print(b,HEX);
    Serial.print('\t'); // Horizontaler Tabulator (Steuerzeichen)
    Serial.print(b,BIN);
    Serial.println();
  }
}

OK soweit ist mir das hoffe ich klar.

habe jetzt mein Programm mal etwas erweitert :slight_smile:

/* 
  Hier ist eine Kleine Übung für die Serielle Schnittstelle: 
  
      1. Variablen Definieren 
      2. Serielle Schnittstelle Starten 
      3. Bekannte Befehle über Serielle Schnittstelle ausgeben
    DO 
      4. Warten bis etwas über Serielle Schnittstelle kommt 
      5. Befehl auswerten und antworten
    LOOP  
  
  
  Ator: Muecke  
  Bord: Arduino Mega 2560
  BJ: 03/14
*/

char Befehl;                                                                                     // Hier kommt der Befehl hinein der zusammen gesätzt wird.
char c;                                                                                          // Hier wird der Datenpuffer zwischen gespeichert aus der Seriellen schnitstelle 

void setup()
{
 Serial.begin(9600);                                                                              //  serial port starten mit der Baudrate 9600
}

void loop()
{
 if (Serial.available() > 0)                                                                       // Wenn Serieller Datenpuffer größer 0 Byte ist dann mach weiter   
 {
 c=Serial.read();                                                                                  // Serieller Datenpuffer in c schreiben 

   if (c==10) 
    {
     Serial.println("Arduino Mega 2560 meldet sich zum Dienst.");                                  //  Ausgabe auf dem Seriellen Monitor am PC 
     Serial.println("Ich kenne auch schon ein paar Befehle:");                                     //  Ausgabe auf dem Seriellen Monitor am PC 
     Serial.println("Befehle beginnt und endet mit einem : (Doppelpunkt)");                        //  Ausgabe auf dem Seriellen Monitor am PC 
     Serial.println("");                                                                           //  Ausgabe auf dem Seriellen Monitor am PC 
     Serial.println(":Name:    ich antworte mit meinem Namen");                                    //  Ausgabe auf dem Seriellen Monitor am PC 
     Serial.println(":Version:    Firmware Nr.");                                                  //  Ausgabe auf dem Seriellen Monitor am PC
     Serial.println("Leider kann ich noch nicht antworten");                                       //  Ausgabe auf dem Seriellen Monitor am PC
     Serial.println("");                                                                           //  Ausgabe auf dem Seriellen Monitor am PC 
     Serial.println("");                                                                           //  Ausgabe auf dem Seriellen Monitor am PC 
     Serial.println("");                                                                           //  Ausgabe auf dem Seriellen Monitor am PC 
     Serial.print("Empfangen: ");
     Serial.println(Befehl);                                                                       //  Zusammen gesätzten Befehl ausgeben
     Serial.println("");                                                                           //  Ausgabe auf dem Seriellen Monitor am PC 
     Serial.println("");                                                                           //  Ausgabe auf dem Seriellen Monitor am PC 
     Serial.println("");                                                                           //  Ausgabe auf dem Seriellen Monitor am PC 
     Serial.println("");                                                                           //  Ausgabe auf dem Seriellen Monitor am PC 
     // Befehl="";                                                                                  //  Löschen 
    // Warum kann ich hier den Inhalt meiner Variable nicht Löschen? 
    
    }
   else
    {
     Befehl = Befehl + c;                                                                           // Einzellne zeichen zusammen sätzen in einen String.  
    }

  }

}

Warum bekomme ich wenn ich meinen Befehl ausgeben nicht den Text sonder Zahlen und Zeichen?
und warum kann ich den Inhalt meiner Variable Befehl nicht löschen?

wo habe ich den Denkfehler?

Muecke:
Warum bekomme ich wenn ich meinen Befehl ausgeben nicht den Text sonder Zahlen und Zeichen?
und warum kann ich den Inhalt meiner Variable Befehl nicht löschen?

Ich weiß nicht, was das werden soll.

char Befehl;
char c;
...
Befehl = Befehl + c;

Du empfängst Beispielsweise ein "H", Dezimal 72 und addierst ein "a", Dezimal 97.
Macht zusammen ein Zeichen mit dem Zeichencode 72+97 = 169.

Alle Zeichencodes über 127 sind nicht genormt, was da für ein Zeichen mit ASCII-169 ausgegeben wird, keine Ahnung. Ist aber auch völlig egal. Weil es bestimmt nicht das ist, was Du machen möchtest: Zeichencodes addieren und daraus neue Zeichencodes bilden.

In Jurs' Code steht doch wie man aus einzelnen chars einen String bastelt.

  • zum aneinanderhängen von Zeichen zu verwenden geht nur mit der Arduino String Klasse (da diese den + Operator überläd), welche du nicht verwenden solltest, da sie hochgradig ineffizient und funktional stark beschränkt ist.
    Lerne mit C-Strings/Nullterminierte char Arrays umzugehen. Das bringt dich weiter. Da ist aber auch ein Unterschied zwischen char (ein Zeichen) und char[] (ein Array aus Char)

ich dachte, wenn ich alle Zeichen einzeln bekomme dann Sätze ich die zusammen bis ich einen Zeilen Umbruch bekomme.

also wollte ich eigentlich das es so ausschaut:

ich sende ein „Hallo (Zeilenumbruch)“

dann bekomme ich alle Zeichen einzeln in „c“ da mir das wenig bring zum auswerten würde ich gerne das Hallo wider zusammen Sätzen.

Befehl = Befehl + c
Befehl = „“ + H
Befehl = H + a
Befehl = Ha + l
Befehl = Hal + l
Befehl = Hall + o

da jetzt der Zeilenumbruch kommt soll mir das Word ausgegeben werden bzw. der Befehl :slight_smile:

doch ich glaube das ich da einen Denkfehler drin habe.

Das geht mit char nicht. Du addierst da die ASCII Zahlen Werte! Konkatenieren mit + geht nur mit Klassen, die diesen Operator überladen.

Du musst dir ein Array anlegen, das groß genug für den String ist. Dann schreibst du den eingelesenen char jedesmal in den aktuellen Index und inkrementierst diesen. Wenn der Trennzeichen kommst schreibst du '\0' in den aktuellen Index des Arrays und setzt die Index-Variable auf 0 zurück (Jurs setzt am Anfang das ganze Array mit memset() auf 0, da er abfragt ob das der Zeiger NULL ist. Das geht auch, ist aber bei der Version unten nicht nötig).

So geschrieben ist es vielleicht etwas einfacher für dich:

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

bool checkSerial()
{
	static byte index;

	if(Serial.available() > 0)
	{		
		char c = Serial.read();
		
		if(c != '\n' && index < SERIAL_BUFFER_SIZE - 1)
		{
			serialBuffer[index++] = c;
		}
		else
		{
			serialBuffer[index] = '\0';
			index = 0;
			return true;
		}
	}
	return false;
}

Hier ist der Eingangspuffer global. Die Funktion gibt true zurück wenn ein String fertig eingelesen wurde. Das Zeilentrennzeichen ist ein Linefeed. Kannst du aber genauso auf '\r' für CR umstellen

Allgemein würde ich dir mal empfehlen ein Buch über C zu kaufen, oder vielleicht mal das hier durchzulesen:

Besonders der Teil zu Arrays und string.h. Dir ist überhaupt nicht klar was ein char oder ein String in C überhaupt sind. Das ist in C ganz primitiv und nicht so wie in C++ oder anderen höheren Sprachen wo man da Klassen dafür hat.

Muecke:
also wollte ich eigentlich das es so ausschaut:

ich sende ein „Hallo (Zeilenumbruch)“

dann bekomme ich alle Zeichen einzeln in „c“ da mir das wenig bring zum auswerten würde ich gerne das Hallo wider zusammen Sätzen.

Ich habe Dir doch den funktionierenden Code mit der Funktion receiveBuffer(); verlinkt.

Die Funktion receiveBuffer(); liefert in dem Fall, dass eine komplette Zeile empfangen wurde, einen Zeiger auf den kompletten String zurück, ansonsten den Nullzeiger NULL. Aller Code zum Empfangen einer Zeile ist in receiveBuffer(); drin. Im Sinne einer "modularen Programmierung" kann diese Funktion völlig unverändert in x-beliebigen Programmen verwendet werden.

Wenn Du "die volle Zeile" bekommen und womöglich auf Serial ausgeben möchtest, dann machst Du in einer eigenen loop-Funktion zum Beispiel das:

 char* text=receiveBuffer();
 if (text!=NULL) Serial.println(text);

das ist richtig das du mir das verlinkt hast, und ich versuche das auch zu verstehen, leider bisher hapert es noch am verstehen.

was dein Code wann wie und warum macht.
ich dachte wenn ich ein wenig mit den Sachen herum spiele und mir eigene dinge zusammen baue dann verstehe ich villeicht das eine oder andere später in deinem Code.

Bisher hast du mir schon extrem viel geholfen und auch den Code für meine Tropfen Box geschrieben, wofür ich echt dankbar bin, und ich möchte nicht unhöflich sein und alles erfragen und andere machen lassen, sondern das gesamte auch zumindest verstehen können was da dann gemacht wird.

Daher versuche ich mich heran zu tasten und wie man sicher bemerkt hat bin ich in Sachen Programmiren nicht gerade ein As :wink:

ps. ich bin noch nicht mal richtig dahinter gekommen was da eine „Funktion“ ist oder auch nicht denn nicht alles in in der Loop drin dennoch wird drauf zugegriffen.

Funktionen:

Serenifly:
Funktionen:
Rheinwerk Computing :: C von A bis Z – 9 Funktionen

mal eine Doofe frage ist C und das was auf dem Arduino als Code hinterlegt wird identisch?

Im Hintergrund ja. Arduino ist C mit Teilen von C++ (standardmäßig kein new/delete. Keine Standard Template Library (wobei es eine Implementierung davon gibt)).

Da wir hier aber auf einem µC und nicht auf dem PC sind ist die Standard Bibliothek etwas anders, d.h. Libs wie string.h oder math.h sind nicht 100%ig identisch.

Die Arduino IDE stellt eine weitere Abstraktionsebene dar. Die versteckt z.B. die main() Funktion vor dir und bietet statt dessen loop() und setup() an. Sie inkludiert automatisch viele Header. Oder sie übernimmt das Erstellen von Funktions-Prototypen (was bei Galileo unter 9.5 erklärt ist). Dinge wie printf() zur direkten Ausgabe geht auch nicht direkt (über einen kleinen Umweg ja). Deshalb ist der Code den du da auf der Seite findest so nicht auf dem Arduino lauffähig. Das Prinzip ist aber das gleiche.

ja dann ist ja gut das ich überhaupt noch kein Programmieren gelernt habe, und erstrecht nicht C oder C++.
habe Anfang der 90`im Lätzen Jahrtausend mit ungefähr 10 - 12 Jahren in Qbasic etwas programmiert doch seither dann nie wider.