HowTo: Ardunio – Die serielle Schnittstelle verwenden

Über die serielle Schnittstelle wird normalerweise jedes Arduino Board programmiert.
Darüber lässt sich aber nicht nur der Programmcode auf den Arduino übertragen. Es lassen sich auch Steuerbefehle an den Arduino senden oder Werte vom Arduino empfangen.

Wie das geht und welche Möglichkeiten Ihr dabei habt beschreibe ich im folgenden Artikel.


Sicherheitshinweise

Ich weiß die folgenden Hinweise sind immer irgendwie lästig und wirken unnötig. Aber leider haben schon viele Menschen die es "besser" wussten aus Leichtsinnigkeit Augen, Finger oder anderes verloren bzw. sich verletzt. Im Vergleich dazu ist ein Datenverlust fast nicht der Rede Wert, aber auch diese können echt ärgerlich sein. Deswegen nehmt Euch bitte fünf Minuten Zeit um die Sicherheitshinweise zu lesen. Denn auch das coolste Projekt ist keine Verletzung oder anderen Ärger wert.
https://www.nerdiy.de/sicherheitshinweise/


Serielle Schnittstelle vorbereiten

Um die serielle Schnittstelle in Eurem Programm zu nutzen müsst Ihr diese initialisieren.
Dies geschieht indem Ihr in der „setup()“-Funktion die Funktion

Serial.begin(115200);

aufruft. Die Zahl „115200“ steht dabei für die Baudrate – also die Geschwindigkeit – mit der die Symbole Nicht Bits oder Bytes) über die serielle Schnittstelle übertragen werden. Diese Geschwindigkeit solltet ihr von der „Qualität“ der Verbindungsleitung abhängig machen.

Habt Ihr zum Beispiel ein abgeschirmtes und sehr kurzes Kabel so kann die Geschwindigkeit sehr hoch eingestellt werden.
Bei nicht abgeschirmten und/oder sehr langen Kabeln sollte diese Geschwindigkeit nicht so hoch gewählt werden.

Gängige Baudraten sind zum Beispiel die folgenden Werte:

50
110
150
300
1200
2400
4800
9600
19200
38400
57600
115200
230400
460800
500000

Egal welche Geschwindigkeit Ihr einstellt, dieser Wert muss auch im „Seriellen Monitor“ – also dem anderen Kommunikationspartner der seriellen Kommunikation – eingestellt werden. Nur dann werden die Zeichen korrekt empfangen und gesendet. Mehr dazu erfahrt Ihr im Laufe des Textes.

Falls Ihr Euch unsicher seid, welche Baudrate Ihr verwenden sollt empfehle ich Werte zwischen 9600 baud und 115200 baud. Diese und die dazwischen liegenden Werte sind auch bei vielen fertigen produkten wie Sensoren und Aktoren die gängigen Werte.


Daten vom Arduino Board an den verbundenen Computer senden

Damit Ihr Daten von eurem Arduino Board an den verbundenen Computer senden könnt müsst ihr folgende Funktion aufrufen.

Diese sendet den Text „Hallo Welt!“ inklusive Zeilenumbruch an den Computer.

Serial.println("Hallo Welt!");

Als Beispiel könnt Ihr dazu folgenden Code ausprobieren.

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

void loop() 
{
  Serial.println("Hallo Welt!");
  delay(1000);
}
Dadurch erhaltet Ihr die abgebildete Ausgabe. Nach jedem „Hallo Welt!“ wird automatisch ein Wechsel in die nächste Zeile vorgenommen.

In der folgenden Kombination sendet die Funktion den Text „Hallo Welt!“ ohne Zeilenumbruch an den Computer.

Serial.print("Hallo Welt!");

Dazu gibt es ein weiteres Beispiel.

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

void loop() 
{
  Serial.print("Hallo Welt!");
  delay(1000);
}
Da nun nach dem „Hallo Welt!“ kein Zeilenumbruch vorgenommen wird entsteht eine Lange Zeile aus mehreren „Hallo Welt!“-Abschnitten.

Wollt Ihr Variabel-Werte – in diesem Fall den Wert der Variable „lustige_variable“ –  senden könnt Ihr dies wie folgt tun:

uint8_t lustige_variable=5;
Serial.println(String(lustige_variable));

Als Beispiel sieht das Ganze wieder so aus.

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

void loop() 
{
  uint8_t lustige_variable=5; 
  Serial.println(String(lustige_variable));
  delay(1000);
}
Der Wert (5) der Variable wird inklusive Zeilenumbruch ausgegeben.

Senden eines Zeilenumbruchs bedeutet, dass zusätzlich zu dem eigentlichen Nachrichteninhalt noch ein Steuerzeichen mitgesendet wird, dass dem Empfänger signalisiert in die nächste Zeile zu wechseln.


Seriellen Monitor nutzen

Der Serielle Monitor ist das Gegenstück zum Arduino-Board. Es ist das Programm welches am Computer die Daten empfängt und für euch lesbar macht.
Ihr findet es als Teil der Arduino-IDE unter dem Menüpunkt „Werkzeuge/Serieller Monitor“. Tools wie dieser „Serielle Monitor“ werden auch Terminalprogramme genannt. Eine gute Alternative zu dem „Seriellen Monitor“ der Arduino-IDE ist auch das Programm HTERM.

Im Fenster des Seriellen Monitors lassen sich das Zeilenabschlusszeichen und die Baudrate einstellen. Durch einen Klick auf „Ausgabe löschen“ lässt sich außerdem der Inhalt des Ausgabefensters löschen.

In dem Fenster des Seriellen Monitors lässt sich die Baudrate einstellen. Hier muss der gleiche Wert eingestellt werden den ihr zuvor im Programmcode eingestellt habt.
Habt ihr mit dem Befehl

Serial.begin(115200);

also eine Baudrate von 115200 baud eingestellt so müsst Ihr auch im seriellen Monitor eine Baudrate von 115200 baud einstellen.

Neben der Baudrate lässt sich außerdem noch das Zeilenabschlusszeichen einstellen. Diese Einstellung benötigt Ihr aber nur, wenn Ihr Daten vom Computer an den Arduino senden wollt.

Hier lässt sich nämlich das Zeichen einstellen, welches nach jedem Absenden eines Wertes automatisch angehängt und mitgesendet wird.

Dies könnt Ihr in Eurem Code zum Beispiel sehr gut dazu nutzen um auf dem Arduino zu erkennen ob die Übertragung eines Befehls oder Werts abgeschlossen ist.

Im Bereich „Befehle und Parameter an den Arduino senden“ wird dies ausführlich erklärt.


Serieller Plotter nutzen

Der Serielle-Plotter ist ein Tool mit dem Ihr Zahlenwerte (z.B. Messwerte) direkt auf dem Computer als zeitlichen Verlauf darstellen könnt. Dazu müsst ihr den Arduino so programmieren, dass er die Zahlenwerte an den Computer sendet.
Dies geht zum Beispiel mit dem Befehl:

Serial.println(VariableMitEinemMesswert);

Die Variable „VariableMitEinemMesswert“ sollte natürlich euren Messwert als Zahl enthalten.
Auf dem Computer müsst ihr dann den Seriellen Plotter starten. Diesen findet ihr unter „Werkzeuge/Serieller Plotter“.

Ein simples Beispiel das auf dem seriellen Plotter eine Sinus-Kurve ausgibt ist das folgende. Probiert den Code einfach aus. Er sollte sich auf jedem Arduino ausführen lassen. Denkt daran Euren „Seriellen Plotter“ auf 115200 baud einzustellen.

/*                                _   _                 _  _               _
                                 | \ | |               | |(_)             | |
  __      ____      ____      __ |  \| |  ___  _ __  __| | _  _   _     __| |  ___
  \ \ /\ / /\ \ /\ / /\ \ /\ / / | . ` | / _ \| '__|/ _` || || | | |   / _` | / _ \
   \ V  V /  \ V  V /  \ V  V /_ | |\  ||  __/| |  | (_| || || |_| | _| (_| ||  __/
    \_/\_/    \_/\_/    \_/\_/(_)|_| \_| \___||_|   \__,_||_| \__, |(_)\__,_| \___|
                                                               __/ |
                                                              |___/
     sinusTest by Fabian Steppat
     Infos on https://www.nerdiy.de/ardunio-die-serielle-schnittstelle/

     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
     the Free Software Foundation, either version 3 of the License, or
     (at your option) any later version.

     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.

     You should have received a copy of the GNU General Public License
     along with this program.  If not, see .

     You will find additional information to this project at the following address:
     
HowTo: Ardunio – Die serielle Schnittstelle verwenden
This codesnippet prints a sine wave via serial console or serial plotter */ //variables and constants float degreeAngle = 0; float sinusValue = 0; const float pi = 3.1415; float radiantAngle = 0; const uint8_t sinusFactor = 50; void setup() { Serial.begin(115200); } void loop() { radiantAngle = degreeAngle * pi / 180; //making deg in radians sinusValue = sin(radiantAngle); //calculate sine from radiant angle degreeAngle += 1.0; //increase the angle delay(5); //make output a bit slower if (degreeAngle > 360) //if degree boundary is reached reset it to zero { degreeAngle = 0; } Serial.println(sinusFactor * sinusValue); //output to serial monitor/plotter }
Das oben angegeben Beispiel sollte die abgebildete Sinuskurve auf dem seriellen Plotter anzeigen.

Der Plotter funktioniert natürlich nicht nur mit Sinus- sondern auch mit allen anderen Zahlen-werten.


Befehle und Parameter an den Arduino senden

Nun wisst Ihr also wie man verschiedene Status-Nachrichten oder Werte vom Arduino an den Computer sendet.
Es geht aber auch andersherum. Dies ist besonders dann sinnvoll oder praktisch, wenn ihr den Arduino von außen steuern und keine Eingabegeräte wie Taster und Co. an den Arduino anschließen wollt (oder mangels GPIOs könnt).

So könnt Ihr zum Beispiel die Helligkeit oder Farbe einer LED oder auch eine Motordrehzahl eines angeschlossenen Motors alleine über die serielle Schnittstelle einstellen.

Zur Kommunikation über die serielle Schnittstelle stell die Arduino-IDE dazu ein paar nützliche Funktionen bereit. (Die Dokumentation mit mehr Details dazu findet Ihr hier: https://www.arduino.cc/reference/en/language/functions/communication/serial/)

Damit der Empfang und die Auswertung von Befeheln klappt gibt es nun drei Aufgaben zu lösen:
1. Wie erkennt der Arduino empfangene Zeichen?
2. Wie werden aus den empfangenen Zeichen komplette Befehle zusammengesetzt?
3. Wie können diese empfangenen Befehle ausgewertet werden?

1. Wie erkennt der Arduino empfangene Zeichen

Um nun auf eingehende Zeichen zu reagieren gibt es die Funktion

Serial.available()

Diese prüft ob neue Zeichen im Empfangspuffer des Arduino enthalten sind. Der Empfangspuffer ist dabei eine Art zwischenspeicher in dem empfangene Zeichen solange gespeichert werden bis der Mikrocontroller des Arduinos Zeit hat sich um die empfangegenen Zeichen zu „kümmern“.

In dem Fall, dass neue Zeichen empfangen, aber noch nicht aus dem Empfangspuffer gelesen wurden gibt die Funktion „Serial.available()“ „true“ zurück ansonsten „false“.

Diese Funktion eignet sich also perfekt dazu um zu überprüfen ob aktuell Zeichen emfpangen wurden, welche nun ausgewertet werden müssen. Dabei sagt uns der Befehl nicht wieviele Zeichen empfangen wurden. Er sagt nur, dass mindestens ein Zeichen im Empfangspuffer ist.

2. Wie werden aus den empfangenen Zeichen komplette Befehle

Gut, nun haben wir dank der vorherigen Funktion erkannt, dass mindestens ein Zeichen im Empfangspuffer verfügbar ist. Aber wie kommen wir nun an die empfangenen Zeichen?

Zeichen aus dem Empfangspuffer lassen sich mit dem Befehl

Serial.read()

auslesen. Dabei gibt die Funktion das erste im Empfangspuffer enthaltene Zeichen aus und entfernt es automatisch aus dem Empfangspuffer.
Um nun alle Zeichen auszulesen und in eine Variable zu schreiben, lesen wir nun solange aus dem Empfangspuffer bis dieser leer ist und merken uns dabei jedes ausgelesene Zeichen.
Dies kann man mit einer Schleife zum Beispiel so umsetzen:

void check_seriellen_empfang() 
{ 
   String seriellerPuffer; 
   while (Serial.available()) 
   {
      char aktuellesZeichen = (char)Serial.read(); 
      if (aktuellesZeichen == 13)   //prüfen ob das eingelesen Zeichen das Zeilenabschlusszeichen ist
      { 
         empfangene_zeichen_auswerten(seriellerPuffer); 
      } else 
      { 
         seriellerPuffer += aktuellesZeichen; 
      } 
   }
}

Wie Ihr sehen könnt besteht der Code aus einer while-schleife die solange läuft bis der Empfangspuffer leer ist (dann ist Serial.available()==false).
Innerhalb der while-Schleife werden währenddessen zeichenweise die auf dem Emfpangspuffer enthaltenen Zeichen ausgelesen (Serial.read()), der Variable „aktuellesZeichen“ zugewiesen und an den String „seriellerPuffer“ angehängt.

Hier kommt nun auch das im Abschnitt „Serieller Monitor“ erwähnte Zeilenabschlusszeichen ins Spiel. In diesem (und jedem anderen Terminalprogramm) lässt sich nämlich ein Zeichen einstellen, welches nach jedem Absenden eines Zeichens oder einer Zeichenkette automatisch angehängt und mitgesendet wird.

Dieses Zeilenabschlusszeichen lässt sich nun nutzen um zu erkennen ob die zuvor eingelesene Zeichenkette abgeschlossen ist und somit ausgewertet werden kann.
Dies bedeutet, dass jedes empfangene Zeichen darauf geprüft werden muss ob es das Zeilenabschlusszeichen ist. Im oben gezeigten Code geschieht dies durch

if (aktuellesZeichen == 13)...

Lasst euch von der 13 nicht verwirren. Das Problem bei Zeilenabschlusszeichen ist nämlich, dass diese auf einer Tastatur nicht aufgeführt sind. Man kann sie also nicht als Vergleichszeichen in den Code „eintippen“.
Um das empfangene Zeichen trotzdem auf das Zeichenabschlusszeichen zu prüfen bedienen wir uns hier eines Tricks.

Dank der Ascii-Tabelle wissen wir nämlich, dass jedes Zeichen im Computer einem Byte-Wert(also einem Zahlenwert zwischen 0 und 255) entspricht.
Wenn ihr im Arduino also zum Beispiel ein „A“ speichert, so speichert dieser eigentlich den Zahlenwert „65“.
Würde man nun prüfen wollen ob ein empfangenes Zeichen einem „A“ entspricht könnte man entweder

if (aktuellesZeichen == "A")

oder aber auch

if (aktuellesZeichen == 65)

schreiben. Beide Vergleiche würden zum gleichen Ergebnis führen.

Diesen Trick machen wir uns nun zu Nutze. Um zu prüfen, ob das aktuell empfangene Zeichen ein Zeilenabschlusszeichen wie zum Beispiel das CR (=CarriageReturn) ist, prüfen wir nicht auf das Zeichen selber sondern auf den Wert des Zeichens in der Ascii-Tabelle: Also 13.

Sobald dieses Zeichen empfangen wurde wissen wir also, dass alle zuvor abgesendeten Zeichen einen Befehl darstellen sollen und nun ausgewertet werden müssen.

3. Wie können diese empfangenen Befehle ausgewertet werden

Wir bzw. der Arduino hat nun also einen Befehl empfangen. Dieser ist in der String-Variable „seriellerPuffer“ gespeichert und wird als Funktionsparameter an die Funktion „empfangene_zeichen_auswerten()“ übergeben.

Diese Funktion könnte zum Beispiel so aussehen:

void empfangene_zeichen_auswerten(String seriellerPuffer)
{
   Serial.println("\"" + String(seriellerPuffer) + "\" empfangen.");   //ausgabe des soeben empfangenen Befehls
   
   if (seriellerPuffer == "simplerBefehlDerWasTut")
   {
      simpler_befehl_der_was_tut();
   } else if (seriellerPuffer.indexOf('=') != -1)
   {
      uint16_t empfangenerWert = seriellerPuffer.substring(seriellerPuffer.indexOf('=') + 1).toInt();
      String empfangenerParameter = seriellerPuffer.substring(0, seriellerPuffer.indexOf('='));
      
      if (empfangenerParameter == "befehlDerEinenWertSetzt")
      {
         if (empfangenerWert <= 23 && empfangenerWert >= 0)
         {
            befehl_der_einen_wert_setzt(empfangenerWert );
         } else
         {
            Serial.println(F("Wert ausserhalb des erlaubten Wertebereichs."));
         }
      } else
      {
         Serial.print(F("Der Befehl \""));
         Serial.print(seriellerPuffer);
         Serial.print(F("\" wurde nicht erkannt."));
      }
   } else
   {
      Serial.print(F("Der Befehl \""));
      Serial.print(seriellerPuffer);
      Serial.print(F("\" wurde nicht erkannt."));
   }
}

Nach dem Empfang eines Befehls wird dieser nochmal ausgegeben, dies ist gerade für Debugging-Zwecke sehr praktisch. So könnt Ihr überprüfen welche Zeichenkette denn letztendlich im Arduino angekommen ist.

Danach kann mit einer einfachen IF-Abfrage geprüft werden welcher Zeichenkette der empfangene Befehl entspricht. Hier wird zum Beispiel geprüft ob der Befehl der Zeichenkette „simplerBefehlDerWasTut“ entspricht.
Ist dies der Fall wird die Funktion „simpler_befehl_der_was_tut();“ ausgeführt.

Mithilfe dieser „absoluten“ Befehle könntet ihr nun einfache Befehle absetzen. Zum Beispiel um ein Licht ein- oder auszuschalten.

Um nun aber auch Werte übergeben zu können müssen auch Befehle der Form „helligkeitSetzenAuf=20“ ausgewertet werden können.
Es muss also der Befehlsname (in diesem Fall „helligkeitSetzenAuf“) und der Wert (in diesem Fall „20“) erkannt werden.

Dies geschieht im weiteren Teil des oben gezeigten Codes.
Sollte nämlich keiner der „absoluten“ Befehle zu der empfangenen Zeichenkette passen wird geprüft ob in der empfangenen Zeichenkette ein „=“-Zeichen Vorhanden ist.

Solltet Ihr also zum Beispiel den Befehl „setzeHelligkeit=100“ an den Arduino gesendet haben wird er die gesamte Zeichenkette „setzeHelligkeit=100“ keinem „absoluten“ Befehl zuordnen können.

In diesem Fall wird die empfangene Zeichenkette also auf ein Gleichheitszeichen untersucht und dieses auch gefunden.

Daraufhin wird die empfangene Zeichenkette mit folgenden Befehlen in den Teil vor und hinter dem Gleichheitszeichen zerlegt.

uint16_t empfangenerWert = seriellerPuffer.substring(seriellerPuffer.indexOf('=') + 1).toInt(); 
String empfangenerParameter = seriellerPuffer.substring(0, seriellerPuffer.indexOf('='));

Nun ist in der Variable „empfangenerWert“ die „100“ und in der Variable „empfangenerParameter“ der Befehl „setzeHelligkeit“ gespeichert.

Einmal aus der empfangenen Zeichenkette extrahiert, können die beiden Werte dann weiterverarbeitet und darauf reagiert werden.

Probiert es einfach aus. 🙂
Den gesamten Code zum ausprobieren findet ihr nochmal hier:

/*                                _   _                 _  _               _
                                 | \ | |               | |(_)             | |
  __      ____      ____      __ |  \| |  ___  _ __  __| | _  _   _     __| |  ___
  \ \ /\ / /\ \ /\ / /\ \ /\ / / | . ` | / _ \| '__|/ _` || || | | |   / _` | / _ \
   \ V  V /  \ V  V /  \ V  V /_ | |\  ||  __/| |  | (_| || || |_| | _| (_| ||  __/
    \_/\_/    \_/\_/    \_/\_/(_)|_| \_| \___||_|   \__,_||_| \__, |(_)\__,_| \___|
                                                               __/ |
                                                              |___/
     serialTest by Fabian Steppat
     Infos on https://www.nerdiy.de/ardunio-die-serielle-schnittstelle/

     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
     the Free Software Foundation, either version 3 of the License, or
     (at your option) any later version.

     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.

     You should have received a copy of the GNU General Public License
     along with this program.  If not, see .

     You will find additional information to this project at the following address:
     
HowTo: Ardunio – Die serielle Schnittstelle verwenden
This codesnippet is a basic conept of a serial communication to control an arduino via serial commands */ { Serial.begin(115200); } void loop() { check_seriellen_empfang(); } void check_seriellen_empfang() { String seriellerPuffer; while (Serial.available()) { char aktuellesZeichen = (char)Serial.read(); if (aktuellesZeichen == 13) { empfangene_zeichen_auswerten(seriellerPuffer); } else { seriellerPuffer += aktuellesZeichen; } } } void empfangene_zeichen_auswerten(String seriellerPuffer) { Serial.println("\"" + String(seriellerPuffer) + "\" empfangen."); if (seriellerPuffer == "simplerBefehlDerWasTut") { simpler_befehl_der_was_tut(); } else if (seriellerPuffer.indexOf('=') != -1) { uint16_t empfangenerWert = seriellerPuffer.substring(seriellerPuffer.indexOf('=') + 1).toInt(); String empfangenerParameter = seriellerPuffer.substring(0, seriellerPuffer.indexOf('=')); if (empfangenerParameter == "befehlDerEinenWertSetzt") { if (empfangenerWert & lt; = 23 & amp; & empfangenerWert & gt; = 0) { befehl_der_einen_wert_setzt(empfangenerWert ); } else { Serial.println(F("Wert ausserhalb des erlaubten Wertebereichs.")); } } else { Serial.print(F("Der Befehl \"")); Serial.print(seriellerPuffer); Serial.print(F("\" wurde nicht erkannt.")); } } else { Serial.print(F("Der Befehl \"")); Serial.print(seriellerPuffer); Serial.print(F("\" wurde nicht erkannt.")); } }

Ich hoffe bei euch hat alles wie beschrieben funktioniert. Falls nicht oder ihr Fragen oder Anregungen habt lasst es mich in den Kommentaren bitte wissen. Ich trage dies dann ggf. in den Artikel nach.
Auch Ideen für neue Projekte sind immer gerne willkommen. 🙂

Fab

P.S. Viele dieser Projekte - besonders die Hardwareprojekte - kosten viel Zeit und Geld. Natürlich mache ich das weil ich Spaß daran habe, aber wenn Du es cool findest, dass ich die Infos dazu mit Euch teile, würde ich mich über eine kleine Spende an die Kaffeekasse freuen. 🙂

Buy Me a Coffee at ko-fi.com

Kommentar hinterlassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.