Mehrere Arduinos verbinden – So klappt es!

Es kann vorkommen, dass man mehrere Arduinos verbinden will. Dazu gibt es mehrere Möglichkeiten. Eine sehr komfortable ist die Verbindung per I2C-Schnittstelle. Wie das geht, erfährst du hier.

I2C steht für Inter-Integrated Circuit und wird I-Quadrat-C oder I-To-C ausgesprochen. Dabei handelt es sich um einen ursprünglich von Philips entwickelten Standard zur Kommunikation zwischen verschiedenen ICs (Integrierten Schaltkreisen). Dabei sollte ein IC, der Master, die Steuerung über verschiedene Peripherie-ICs übernehmen. Dieses Verfahren wird als Master-Slave-Bus bezeichnet.

I2C benötigt nur zwei Datenleitungen: SCK, die serielle Uhr (Serial Clock) und SDA, die serielle Datenleitung. Das Arduino-Board ist glücklicher Weise bereits damit ausgestattet. Der SDA Pin ist der Analog Input 4, der SCK Pin ist der Analog Input 5. Zusätzlich sind die Pins beim Arduino UNO R3 auch auf der linken Seite der digitalen Pins herausgeführt. Es handelt sich aber um die gleichen Anschlüsse, wie der A4 und A5.

Wie erwähnt ist I2C ein Master-Slave-System. Das bedeutet, dass eines der Arduino-Boards ein Master werden muss. Alle anderen Arduino-Boards bekommen ihre eigene Adresse.

Mehrere Arduinos verbinden: Schaltplan

Im Grunde werden von allen Arduion-Boards die Pins A4 und A5, sowie der GND verbunden. Zusätzlich sollten zwei Widerstände als Pull-Ups für die SDA und SCL Leitungen verschaltet werden.

Die Arduinos können entweder alle über separate Netzteile oder USB-Strom versorgt werden, oder über ein kräftiges externes Netzteil. Ich rate davon ab, die Netzteil-Buchse eines Arduinos zu verwenden, da hier eine Diode den maximalen Stromfluss auf ein Ampere begrenzt. Bei vielen Arduinos kommt man schnell darüber. Allerdings ist bei dieser Art des Betriebes unbedingt auf richtige Polung zu achten!

Mehrere Arduinos verbinden mit Netzteil
Variante 1 mit gemeinsamem Netzteil
Mehrere Arduinos verbinden I2C USB
Variante 2 mit separaten Netzteilen oder USB-Stromversorgung

Mehrere Arduinos verbinden: Master Code

#include <Wire.h>

/* MASTER */
#define NODE_MAX_NUMBER 5
#define PAYLOAD_SIZE 2
int nodePayload[NODE_MAX_NUMBER][PAYLOAD_SIZE];

void setup() {
  Serial.begin(9600);
  Wire.begin();
}
void loop() {
  for (int i=2; i<=NODE_MAX_NUMBER; i++){
    sendeDaten(i,i*3);
    empfangeDaten(i);
  }    

  // Wartezeit
  delay(1000);
}

void sendeDaten(int empfaenger, int daten){
  // Senden
  Wire.beginTransmission(empfaenger); // sendet zu Node empfaenger
  Wire.write(daten);           // sendet daten
  Wire.endTransmission();    // Übermittlungsstop  
}

void empfangeDaten(int empfaenger){
  // Anfragen und Empfangen
  Wire.requestFrom(empfaenger, PAYLOAD_SIZE);      // Fragt Daten von Node empfaenger ab
  if (Wire.available() == PAYLOAD_SIZE) {          // wenn Daten empfangen werden
    for (int i = 0; i < PAYLOAD_SIZE; i++) {
      nodePayload[empfaenger][i] = Wire.read();                // lies Daten in nodePayload-Array
      Serial.println(nodePayload[empfaenger][i]);   // Zeige die Daten im seriellen Monitor an
    }
  }  
}

Um die I2C-Schnittstelle nutzen zu können, muss die Wire-Bibliothek eingebunden werden: include <Wire.h> Wire war der Vorgänger von Arduino und die I2C-Funktionalität ist in dieser Bibliothek enthalten. Der Code setzt sich im Grunde aus zwei Hauptfunktionen zusammen: dem Daten-Senden und dem Daten-Empfangen.

Im Loop befindet sich eine For-Schleife. Bei jedem Durchlauf wird mit einem anderen angeschlossenen Arduino-Board kommuniziert. In der For-Schleife wird zuerst die Funktion sendeDaten aufgerufen. Sie erhält als Parameter die Zieladresse, der Empfänger-Boards und einen Wert zwischen 0 und 255. Hierbei handelt es sich um Beispieldaten, die vom Empfänger ausgewertet werden können.

Dann wird die Funktion empfangeDaten aufgerufen. Wieder wird die Zieladresse übergeben. In der Funktion selbst wird zuerst eine Anfrage an das Empfänger-Board geschickt. (Der Master muss diese Anfragen an die angeschlossenen Arduinos schicken, damit Daten zurück gesendet werden dürfen.) Nun werden zwei Datenpakete abgefragt. Jedes muss im Wertebereich 0 bis 255 (also ein BYTE) liegen. Sie werden an ein mehrdimensionales Array übergeben, mit dem man im späteren Programmverlauf arbeiten könnte. Hier ist es wieder nur exemplarisch zu verstehen. Beide Datenpakete werden noch an die Serielle Schnittstelle übertragen und können im Seriellen Monitor überprüft werden.

Sehen wir uns nun an, wie es auf der anderen Seite aussieht: In den Slave-Modulen.

Mehrere Arduinos verbinden: Slave Code

#include <Wire.h>

int LED = 13;
int x = 0;

#define NODE_ADDRESS 2
#define PAYLOAD_SIZE 2
byte nodePayload[PAYLOAD_SIZE];

void setup() {
  Serial.begin(9600);
  pinMode (LED, OUTPUT);
  Wire.begin(NODE_ADDRESS);
  Wire.onReceive(receiveEvent); // erstelle ein Empfangen-Ereignis
  Wire.onRequest(requestEvent); // erstelle ein Anfrage-Ereignis
}

void receiveEvent(int bytes) {
  x = Wire.read();              // lies die gesendeten Daten aus
  // blinke 
  for (int i=0; i<x; i++){
    digitalWrite(LED, HIGH);
    delay(50);
    digitalWrite(LED, LOW);
    delay(50);
  }
  Serial.println(x);
}

void loop() {
  delay(10);
}

void requestEvent()
{
  nodePayload[0] = NODE_ADDRESS;
  nodePayload[1] = analogRead(A0) / 4;
  Wire.write(nodePayload, PAYLOAD_SIZE);
}

Dieser Code ist wie zu erwarten war eher passiv und wartet auf Nachricht vom Master. Im Setup werden zwei Events verknüpft:

Wire.onReceive(receiveEvent); // erstelle ein Empfangen-Ereignis
Wire.onRequest(requestEvent); // erstelle ein Anfrage-Ereignis

Die Funktion receiveEvent wird aufgerufen, wenn Daten empfangen werden. In ihr werden die empfangen Daten einfach in die Variable x geschrieben. Die LED am Pin 13 blinkt dann genau so oft, wie die Variable x groß ist. Wieder nur exemplarisch.

Die Funktion requestEvent wird ausgeführt, wenn der Master nach Daten verlangt. Sie schreibt zwei Daten in ein Array: die eigene Adresse und den momentanen Wert am Analogen Pin 0. Da dieser Wert ja im Bereich von 0 bis 1023 wird er durch vier geteilt um in den Zielbereich von 0 bis 255 überführt zu werden (BYTE).

Wichtig ist, dass jedes Slave-Arduino-Board eine eigene Adresse bekommt. Ich habe hier vier Slave-Arduinos angeschlossen und sie von 2 bis 5 nummeriert.

#define NODE_ADDRESS 2

Übrigens sollten sich so locker 112 Arduino-Boards anschließen und adressieren lassen.

Und jetzt würde mich brennend interessieren, was du damit vorhast. Schreib mir das gern in die Kommentare :-)

Abschließend bemerkt gibt es tatsächlich eine ganz spannende Diskussion darüber, ob die Begriffe Master und Slave noch angebracht sind. Ich persönlich finde das sogar nachvollziehbar und sehr unterhaltsam, die teilweise stark polarisierenden Kommentare zu lesen.

Referenzen

Hast du weitere Fragen? Probier doch mal unser brandneues Forum zu Arduino aus!

Schreibe einen Kommentar

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

 

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.