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#.