Zum Inhalt springen

Serielle Kommunikation

    In- und Outputs

    (Dieses Beispiel ist noch in Bearbeitung)

    Das Arduino-Board kann seriell mit einem angeschlossenen Computer kommunizieren. Dies ermöglicht z.B. die Steuerung von Software und ist damit ein wichtiger Bestandteil des Physical Computings.

    Im folgenden Beispiel werden exemplarisch fünf LEDs über Processing an- und abgeschaltet. Drei Taster und ein Potentiometer senden Daten an Processing zurück.


    Sieh dir jetzt meinen neuen Arduino-Videokurs an: Jetzt ansehen!


    Schritt 1: Daten von Arduino an Processing senden:

    Die Schaltung besteht aus einem Potentiometer, dass am AnalogIn 0 und drei Tastern, die an den digitalen Kanälen 3, 4 und 5 angeschlossen sind.

    Arduino-Code

    int potPin = 0;
    
    int button1 = 3;
    int button2 = 4;
    int button3 = 5;
    
    void setup(){
      Serial.begin(9600);
      pinMode(button1,INPUT);
      pinMode(button2,INPUT);
      pinMode(button3,INPUT);
    }
    
    void loop(){
      Serial.print(digitalRead(button1));
      Serial.print(",");
      Serial.print(digitalRead(button2));
      Serial.print(",");
      Serial.print(digitalRead(button3));
      Serial.print(",");
      Serial.println(analogRead(potPin));
      delay(10);
    }

    Im ersten Teil werden die angeschlossenen Pins deklariert:

    int potPin = 0;
    
    int button1 = 3;
    int button2 = 4;
    int button3 = 5;

    Im Setup wird die serielle Kommunikation mit dem Befehl Serial.begin(9600); gestartet. Das Parameter 9600 legt die Übertragungsgeschwindigkeit (Baud-Rate) fest. Des weiteren müssen die Button-Pins als Input deklariert werden:

    pinMode(button1,INPUT);
    pinMode(button2,INPUT);
    pinMode(button3,INPUT);

    In der Loop-Methode werden Daten an die serielle Schnittstelle des Arduino-Boards gesendet. Der Befehl Serial.print(X); sendet eine Zeichenkette (String), der Befehl Serial.println(X); dagegen sendet eine Zeichenkette und erzeugt einen Zeilenumbruch (Linefeed).

    Der Button-Zustand wird mit der Methode digitalRead(button1) ausgelesen und per Serial.print(…); direkt versendet. Er ist entweder 0 oder 1.

    void loop(){
      Serial.print(digitalRead(button1));
      Serial.print(",");
      Serial.print(digitalRead(button2));
      Serial.print(",");
      Serial.print(digitalRead(button3));
      Serial.print(",");
      Serial.println(analogRead(potPin));
      delay(10);
    }

    Pro Methodendurchlauf wird also eine Zeichenkette der Form 0,0,0,0 + Zeilenumbruch versendet.

    Processing-Code

    import processing.serial.*;
    Serial serialPort;
    
    int[] button = {0,0,0};
    float potWinkel;
    
    float[] data;
    
    void setup(){
      size(400,400);
      noStroke();
      println(Serial.list());
      serialPort = new Serial(this, Serial.list()[0], 9600); // Für Mac-User
      // seriellerPort = new Serial(this, Serial.list()[Serial.list().length-1], 9600); // Für PC-User
      serialPort.bufferUntil('\n');
    }
    
    void draw(){
      background(200);
    
      // Grafik Poti
      fill(255);
      arc(200,200,50,50,0,potWinkel);
    
      // Grafik Taster
      for (int i=1; i<4; i++) {
        if (button[i-1]==0) fill(0);
        else fill(255);
        rect(130+30*i,290,20,20);
      }
    }
    
    void serialEvent(Serial serialPort) {
      String dataString = serialPort.readStringUntil('\n');
      if (dataString != null) {
        float[] data = float(split(dataString, ","));
        if (data.length >=4){
          if (data[0]==0) button[0]=0;
          else button[0]=1;
          if (data[1]==0) button[1]=0;
          else button[1]=1;
          if (data[2]==0) button[2]=0;
          else button[2]=1;
          potWinkel=map(data[3],0,1023,0,PI*2);
        }
      }
    }

    In Processing wird zunächst die serielle Bibliothek serial.h eingebunden, danach wird ein serielles Objekt erstellt.

    Serial serialPort;

    Dann werden die Arrays button und data und die Fließkommazahl potWinkel deklariert.

    int[] button = {0,0,0};
    float potWinkel;
    float[] data;

    Im Setup wird der Serielle Port für die Kommunikation mit Arduino geöffnet. Der Befehl println(Serial.list()); listet alle verfügbaren seriellen Geräte auf. Am Mac ist das letzte angeschlossene serielle Gerät immer an Position 0.

    serialPort = new Serial(this, Serial.list()[0], 9600);

    Bei PC-Benutzern ist es immer die letzte Stelle:

    serialPort = new Serial(this, Serial.list()[Serial.list().length-1], 9600);

    Der Befehl serialPort.bufferUntil(‚\n‘); veranlasst Processing, alle einkommenden Signale bis zu einem Zeilenumbruch (Linefeed) zwischenzuspeichern.

    Die Methode serialEvent(serialPort) wird immer aufgerufen, wenn am seriellen Port Daten verfügbar sind. Die eingehenden Daten werden in die Variable dataString eingelesen:

    String dataString = serialPort.readStringUntil('\n');

    Der eingehende String wird in ein Array umgewandelt. Als Trennzeichen wird ein Komma verwendet:

    float[] data = float(split(dataString, ","));

    Nun werden die einzelnen Variablenfelder überprüft und ausgewertet:

    if (data[0]==0) button[0]=0;
    else button[0]=1;

    Das Parameter des Potentiometers wird in einen Winkel umgewandelt:

    potWinkel=map(data[3],0,1023,0,PI*2);

    Die grafische Ausgabe ist in der draw() Methode hinterlegt:

    void draw(){
      background(200);
    
      // Grafik Poti
      fill(255);
      arc(200,200,50,50,0,potWinkel);
    
      // Grafik Taster
      for (int i=1; i<4; i++) {
        if (button[i-1]==0) fill(0);
        else fill(255);
        rect(130+30*i,290,20,20);
      }
    }

    Nun wird das Skript um die Ausgabe erweitert:

    Arduino-Code

    int potPin = 0;
    
    int button1 = 3;
    int button2 = 4;
    int button3 = 5;
    
    int led1 = 12;
    int led2 = 11;
    int led3 = 10;
    int led4 = 9;
    int led5 = 8;
    
    int data[7] = {0,0,0,0,0,0,0};
    
    void setup(){
      Serial.begin(9600);
    
      pinMode(button1,INPUT);
      pinMode(button2,INPUT);
      pinMode(button3,INPUT);
    
      pinMode(led1,OUTPUT);
      pinMode(led2,OUTPUT);
      pinMode(led3,OUTPUT);
      pinMode(led4,OUTPUT);
      pinMode(led5,OUTPUT);
    }
    
    void loop(){
      if (Serial.available()>0){
        for (int i=0; i<6; i++){
          data[i]=data[i+1];
        }
        data[6]=Serial.read();
      }
    
      if ((data[0]=='*')&&(data[6]=='#')){
        if (data[1]=='1') digitalWrite(led1,HIGH);
        else digitalWrite(led1,LOW);
        if (data[2]=='1') digitalWrite(led2,HIGH);
        else digitalWrite(led2,LOW);
        if (data[3]=='1') digitalWrite(led3,HIGH);
        else digitalWrite(led3,LOW);
        if (data[4]=='1') digitalWrite(led4,HIGH);
        else digitalWrite(led4,LOW);
        if (data[5]=='1') digitalWrite(led5,HIGH);
        else digitalWrite(led5,LOW);
      }
    
      Serial.print(digitalRead(button1));
      Serial.print(",");
      Serial.print(digitalRead(button2));
      Serial.print(",");
      Serial.print(digitalRead(button3));
      Serial.print(",");
      Serial.println(analogRead(potPin));
      delay(10);
    }

    Die Pins der LEDs werden deklariert:

    int led1 = 12;
    int led2 = 11;
    int led3 = 10;
    int led4 = 9;
    int led5 = 8;

    Die LED-Pins werden im setup() als OUTPUT angelegt:

    pinMode(led1,OUTPUT);
    pinMode(led2,OUTPUT);
    pinMode(led3,OUTPUT);
    pinMode(led4,OUTPUT);
    pinMode(led5,OUTPUT);

    Im loop() wird der serielle Port nach eingehenden Daten überprüft. Diese Daten werden in die letzte Stelle eines Arrays eingelesen, alle anderen Stellen werden darin um eine Stelle nach vorn sortiert:

    if (Serial.available()>0){
      for (int i=0; i<6; i++){
        data[i]=data[i+1];
      }
      data[6]=Serial.read();
    }

    Das Processing-Übertragungsprotokoll sendet eine Zeichenkette der Form *00000#. Enthält das Datenarray an der ersten Stelle einen Stern und an der letzten Stelle ein Rautenzeichen, werden die zwischen stehenden Einträge an die LEDs übergeben. Enthält das Datenfeld eine Eins, wird die zugewiesene LED ein-, sonst wird sie abgeschaltet:

    if ((data[0]=='*')&&(data[6]=='#')){
      if (data[1]=='1') digitalWrite(led1,HIGH);
      else digitalWrite(led1,LOW);
      if (data[2]=='1') digitalWrite(led2,HIGH);
      else digitalWrite(led2,LOW);
      if (data[3]=='1') digitalWrite(led3,HIGH);
      else digitalWrite(led3,LOW);
      if (data[4]=='1') digitalWrite(led4,HIGH);
      else digitalWrite(led4,LOW);
      if (data[5]=='1') digitalWrite(led5,HIGH);
      else digitalWrite(led5,LOW);
    }

    Processing-Code

    import processing.serial.*;
    
    
    Serial serialPort;
    
    int[] led = {0,0,0,0,0};
    int[] button = {0,0,0};
    float potWinkel;
    float[] data;
    
    void setup(){
      size(400,400);
      noStroke();
      println(Serial.list());
      serialPort = new Serial(this, Serial.list()[0], 9600); // Für Mac-User
      // seriellerPort = new Serial(this, Serial.list()[Serial.list().length-1], 9600); // Für PC-User
      serialPort.bufferUntil('\n');
    }
    
    void draw(){
      background(200);
      // LEDs
      for (int i=1; i<6; i++) {
      if (led[i-1]==0) fill(0);
        else fill(255);
        ellipse(110+30*i,110,20,20);
      }
      // Poti
      fill(255);
      arc(200,200,50,50,0,potWinkel);
      // Taster
      for (int i=1; i<4; i++) {
        if (button[i-1]==0) fill(0);
        else fill(255);
        rect(130+30*i,290,20,20);
      }
    }
    
    void mousePressed() {
      if (mouseButton == LEFT) {
        if ((mouseY>100)&&(mouseY<120)){
          for (int i=1; i<6; i++){
            if ((mouseX>100+30*i)&&(mouseX<120+30*i)) {
              if (led[i-1]==0) led[i-1]=1;
              else led[i-1]=0;
            }
            sendSerial();
          }
        }
      }
    }
    
    void serialEvent(Serial serialPort) {
      String dataString = serialPort.readStringUntil('\n');
      if (dataString != null) {
        float[] data = float(split(dataString, ","));
        if (data.length >=4){
          if (data[0]==0) button[0]=0;
          else button[0]=1;
          if (data[1]==0) button[1]=0;
          else button[1]=1;
          if (data[2]==0) button[2]=0;
          else button[2]=1;
          potWinkel=map(data[3],0,1023,0,PI*2);
        }
      }
    }
    
    void sendSerial(){
      // Serielle Ausgabe
      serialPort.write('*');
      if (led[0]==1) serialPort.write('1');
      else serialPort.write('0');
      if (led[1]==1) serialPort.write('1');
      else serialPort.write('0');
      if (led[2]==1) serialPort.write('1');
      else serialPort.write('0');
      if (led[3]==1) serialPort.write('1');
      else serialPort.write('0');
      if (led[4]==1) serialPort.write('1');
      else serialPort.write('0');
      serialPort.write('#');
    }

    Unter anderem ist der Processing-Code mit der sendSerial() Methode erweitert:

    void sendSerial(){
      // Serielle Ausgabe
      serialPort.write('*');
      if (led[0]==1) serialPort.write('1');
      else serialPort.write('0');
      if (led[1]==1) serialPort.write('1');
      else serialPort.write('0');
      if (led[2]==1) serialPort.write('1');
      else serialPort.write('0');
      if (led[3]==1) serialPort.write('1');
      else serialPort.write('0');
      if (led[4]==1) serialPort.write('1');
      else serialPort.write('0');
      serialPort.write('#');
    }

    Diese Methode wird immer ausgeführt, wenn das mousePressed()-Event ausgelöst wird. Es sendet jeweils den Zustand aller LEDs über den seriellen Port serialPort. Die gesendete Zeichenkette hat die Form *00000#.

    10 Gedanken zu „Serielle Kommunikation“

    1. Pingback: Nachtlicht mit Arduino – Fotowiderstand und LED

    2. Hallo,
      Wenn ich an den Arduino einfach nur eine Zahl senden will die dann dort als Variable gespeichert wird , muss ich das dann über firmata und/ oder processing machen oder gibt es da einen einfacheren Weg? Vielleicht einfach über den seriellen Monitor mit der Arduino IDE ?

      habe das versucht kam aber zu keinem nennenswerten Ergebnis:

      int test = 0;

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

      void loop() {
      if Serial.available {

      test = Serial.read();
      delay(100);
      Serial.println(test);
      }
      }

      habe als im Seriellen monitor immer nur als Ausgabe 49 oder 50 usw. bekommen

      wo liegt mein Fehler ?
      danke schonmal im voraus , gruß

    3. Hallo!
      Für mein Projekt lese ich analoge Spannungen ein, die in Matlab weiterverarbeitet werden sollen.
      Wie sieht es denn beim Arduino Uno mit den anderen Übertragungseigenschaften aus? In allen Beispielen habe ich bisher nur die Baudrate, nicht aber die Parität, Anzahl Daten- und Stoppbits usw. Gefunden, die ich aber für die Übertragung benötige.
      Wo kann ich sie einstellen bzw. Herausfinden, wie der Uno arbeitet?

    4. void loop(){
      Serial.print(digitalRead(button1));
      Serial.print(„,“);
      Serial.print(digitalRead(button2));
      Serial.print(„,“);
      Serial.print(digitalRead(button3));
      Serial.print(„,“);
      Serial.println(analogRead(potPin));
      delay(10);
      }

      Pro Methodendurchlauf wird also eine Zeichenkette der Form 0,0,0,0 + Zeilenumbruch versendet.

      Frage wo Kommt denn der Zeilenumbruch ’n‘ her ?
      verstehe ich nicht!

    5. Hallo Stefan,
      Ich hab mal ne Frage und würde mich freuen wenn ich vielleicht eine Hilfe bekommen könnten,
      Ich arbeite mit dem Arduino Mega und möchte sehr gern des selbe Programm für 40 Digital I/O schreiben leider funktioniert es ab einer Stringlänge von sieben oder acht nicht mehr.
      Das mit der Firmata Bibliothek klingt sehr Verlockend aber ich möchte die Befehle nicht über die Processing Software schicken sondern über eine Serielle Schnittstelle mit einen selbst geschriebenen Programm gibt es da eine Möglichkeit?

    6. @Johannes und Nils: Ich habe gerade noch mal alles nachgebaut. Ich schätze, ihr meint die An- und Abführungszeichen? Da gab es eine blöde Funktion in dem WordPress (diese Website). Eigentlich ist die ganz cool, da sie falsche An- und Abführungszeichen ersetzt – für Code ist das natürlich blöd.

      Jetzt geht es, obwohl ich dieses Script nicht mehr empfehle, da die Verwendung von Firmata wesentlich einfacher ist Episode 12 stellt das mal vor:
      https://www.starthardware.org/2012/01/videoworkshop-arduino-lernen/

      Viel Spaß weiterhin und danke nochmal für den Hinweis :)

    7. Bitte überarbeitet das Programm nochmal, wir haben bereits 2 Stunden daran gesessen und versucht alle Fehler die wir fanden zu verbessern (hierbei handelt es sich nur um Fehler die wir beim Input endeckt haben, da wir beim Output noch gar nicht angefangen haben) und sehen noch kein Ende. Wobei wir dabei große Probleme haben da wir noch Anfänger auf dem Gebit sind und uns dieses Tutorial einfach zur Übung durchlesen und ausführen wollten.

      grüße

    Die Kommentarfunktion ist deaktiviert.