Rasperry Pi mit Arduino

Aus m-wiki
Zur Navigation springen Zur Suche springen

Einführung

Es ist möglich, (mehrere) Arduinos mit einem Rasperry Pi zu koppeln. Es gibt dafür mehrere Wege, die hier vorgestellt werden.

Arduino über Raspberry Pi programmieren

Man kann die Arduino IDE auch auf dem Raspi installieren. Je nach Version der unterstützten IDE und/oder Arduino-Board muss die Programmierung auch über einen externen Rechner geschehen.

$ sudo apt-get update
$ sudo apt-get install arduino

Damit der Raspi über das USB-Kabel mit dem Arduino "sprechen" kann, muss die serielle Konsole deaktiviert werden. Wenn man die serielle Konsole für andere Zwecke brauchen möchte, muss der Arduino extern programmiert werden. Die Kommunikation mit dem Arduino ist aber auch über I2C möglich.

Kommunikation zwischen Arduino und Raspi

serieller Monitor

In der Arduino-IDE kann man Nachrichten der seriellen Schnittstelle anzeigen. Dies geschieht über die Lupe auf der rechten Seite der Werkzeugleiste.

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

void loop() {
  Serial.println("Hallo Welt");
  delay(1000);
} // end loop

PyFirmata

Diese Bibliothek erlaubt die Ausführung von Programmen ohne eigenen Code für den Arduino zu entwickeln, sondern man sendet einfach den Code vom Raspi auf den Arduino. Dazu wird zuerst über die IDE des Arduino der entsprechende Sketch geladen (File -> Examples -> Firmata -> StandardFirmata). Anschliessend wartet der Arduino auf Befehle vom Raspi. Das Problem dabei ist nur, dass der Raspi immer mit dem Arduino verbunden sein muss. Für unabhängige Programme muss man hingegen eigene Sketche für den Arduino schreiben und diese dann laden.

Auf dem Raspi muss zuerst die PySerial-Bibliothek installiert werden. Danach kann PiFirmata von Git installiert werden.

$ sudo agt-get install git # Falls Git noch nicht installiert wurde
$ git clone https://github.com/tino/pyFirmata.git
$ cd pyFirmata
$ sudo python setup.py install

Test der eingebauten LED

Anschliessend kann man testen, ob das Programm auf dem Arduino die eingebaute LED aufblinken lässt:

import pyfirmata, time

board = pyfirmata.Arduino('dev/ttyACMo')  # Kommunikation über USB
pin13 = board.get_pin('d:13:o')           # Den digitalen Pin 13 als Output schalten

pin13.write(1)
sleep(2)
pin13.write(0)
board.exit()

externe LED ansteuern

Die Verwendung einer externen LED (mit Vorwiderstand am Pin 10) ist ähnlich

import pyfirmata, time

board = pyfirmata.Arduino('dev/ttyACMo')  # Kommunikation über USB
pin10 = board.get_pin('d:10:o')           # Den digitalen Pin 10 als Output schalten

while True:
  pin10.write(1)
  sleep(1)
  pin10.write(0)
  sleep(1)

Taster einlesen

Möchte man einen Taster einlesen, geht dies natürlich auch.

Taster am Arduino aus Raspi-Kochbuch
# https://github.com/simonmonk/raspberrypi_cookbook/blob/master/code/ardu_switch.py
import pyfirmata, time, sys

board = pyfirmata.Arduino('/dev/ttyACM0')
switch_pin = board.get_pin('d:4:i')
it = pyfirmata.util.Iterator(board)
it.start()
switch_pin.enable_reporting()

while True:
   input_state = switch_pin.read()
   if input_state == False:
       print('Button Pressed')
       time.sleep(0.2)

PyFirmata braucht fürs Einlesen der Inputs einen Iterator, welcher einen eigenen Thread eröffnet. Damit die Meldungen auch verarbeitet werden, muss das reporting aktiviert werden. Leider kann man den Iterator-Thread nicht mehr einfach beenden. Damit der nicht im Hintergrund weiterläuft bis man den Raspi rebootet, kann man Python mit $ sudo killall python beenden.

analoge Werte einlesen (Potentiometer)

Das Einlesen eines analogen Eingangs geschieht wie beim digitalen Eingang.

# https://github.com/simonmonk/raspberrypi_cookbook/blob/master/code/ardu_adc.py
import pyfirmata, time

board = pyfirmata.Arduino('/dev/ttyACM0')
analog_pin = board.get_pin('a:0:i')
it = pyfirmata.util.Iterator(board)
it.start()
analog_pin.enable_reporting()

while True:
   reading = analog_pin.read()
   if reading != None:          # Verhindern, dass ungültige Werte interpretiert werden, wenn der Eingang noch nicht initialisiert wurde
       voltage = reading * 5.0
       print("Reading=%f\tVoltage=%f" % (reading, voltage))
       time.sleep(1)

PWM einsetzen

Das Vorgehen ist analog der obigen Vorgänge. Programmbeispiele gibt es dazu auf der Webseite vom Kochbuch.

die serielle Schnittstelle anstatt USB nutzen

serielle Verbindung aus Raspi-Kochbuch

Hier ist es wichtig, dass man sich bewusst ist, dass der Arduino 5V benutzt. Wenn man die Pins also direkt anschliesst, so zerstört man den Eingang des Raspi. Für die Pegelumsetzung gibt es Pegelwandler-Module und sonst verwendet man einfach einen Spannungsteiler wie im Bild. Dieses habe ich im Gegensatz zum Buch angepasst, weil der Arduino eine 5V-Quelle braucht. Als Widerstände nimmt man die beim Spannungsteiler vorgeschlagenen.

Im Unterschied zur Verbindung mit USB muss man nur die Zeile mit der Schnittstelle anpassen:

board = pyfirmata.Arduino('dev/ttyAMAo')  # Kommunikation über die serielle Schnittstelle

Serielle Schnittstelle

Die serielle Schnittstelle kann natürlich auch direkt ohne PyFirmata genutzt werden. In diesem Fall muss man den Sketch auf Seiten des Arduino aber selber programmieren. Die Verkabelung geschieht aber gleich wie im obigen Abschnitt. In diesem Beispiel wird die Verkabelung umgestellt auf Pin 8 und 9, damit die USB-Verbindung nicht tagiert wird, da man sonst nicht mehr auf den Arduino zugreifen kann, ohne ihn zurückzusetzen.

Hier nun das Programm für den Raspi.

# https://github.com/simonmonk/raspberrypi_cookbook/blob/master/code/ardu_pi_serial.py
import serial

ser = serial.Serial('/dev/ttyAMA0', 9600)

while True:
  command = raw_input("Kommando: l = LED-Zustand wechseln, r = A0 einlesen, x = Abbruch ")
  if command == 'l' :
    ser.write('l')
  elif command == 'r' :
    ser.write('r')
    print(ser.readline())
  elif command == 'x' :
    break

Nun noch der Sketch für den Arduino:

// https://github.com/simonmonk/raspberrypi_cookbook/blob/master/code/ArduinoSerial/ArduinoSerial.ino
#include "SoftwareSerial.h"

int ledPin = 13;
int analogPin = A0;
SoftwareSerial ser(8, 9); // RX, TX nicht über Pin 0 und 1, damit über USB auf den Arduino zugegriffen werden kann
boolean ledOn = false;

void setup() {
 ser.begin(9600); 
 pinMode(ledPin, OUTPUT);
} // end setup()

void loop() {
 if (ser.available()) {
   char ch = ser.read();
   if (ch == 'l') {
     toggleLED();
   } // end if
   if (ch == 'r') {
     sendAnalogReading();
   } // end if
 } // end if 
} // end loop()

void toggleLED() {
 ledOn = ! ledOn;
 digitalWrite(ledPin, ledOn);
} // end toggleLED()

void sendAnalogReading() {
 int reading = analogRead(analogPin);
 ser.println(reading);
} // end sendAnalogReading()

I2C-Schnittstelle

I2C Verbindung Arduino-Raspi aus Raspi-Kochbuch

Die Anschlüsse für die I2C-Schnittstelle sind auf dem Arduino nicht direkt beschriftet. Sie befinden sich oberhalb der digitalen Eingänge. Auf dem Bild sieht man, welches der SDA und welches der SCL-Anschluss ist. Die Einspeisung des Arduino kann entweder über den Raspi oder extern erfolgen. Beim Arduino R3 sind die Pins direkt nutzbar. Bei früheren Revisionen wurde für I2C noch die Pins A4 und A5 verwendet.

Im Programm wird wie bei der ser. Schnittstelle eine LED gesteuert und ein analoger Eingang eingelesen. Der Datenaustausch geschieht nun aber über I2C. Ein Pegelwandler ist hier nicht notwendig, da die I2C-Pins auf dem Arduino keine Pull-Up-Widerstände verwenden und somit die 3.3V des Raspi benutzen. Dies kann bei anderen Geräten aber anders sein, so dass man dies beim Anschluss an den Raspi beachten muss.

Zuerst der Code für den Raspi:

# https://github.com/simonmonk/raspberrypi_cookbook/blob/master/code/ardu_pi_i2c.py 
import smbus
import time

# for RPI version 1, use "bus = smbus.SMBus(0)"
bus = smbus.SMBus(1)

# This must match in the Arduino Sketch
SLAVE_ADDRESS = 0x04

def request_reading():
   reading = int(bus.read_byte(SLAVE_ADDRESS))
   print(reading)

while True:
   command = raw_input("Enter command: l - toggle LED, r - read A0 ")
   if command == 'l' :
       bus.write_byte(SLAVE_ADDRESS, ord('l'))
   elif command == 'r' :
       request_reading()

Nun der Sketch für den Arduino:

// https://github.com/simonmonk/raspberrypi_cookbook/blob/master/code/ArduinoI2C/ArduinoI2C.ino
#include <Wire.h>

int SLAVE_ADDRESS = 0x04; // Muss mit der Adresse im Raspi übereinstimmen, kann aber frei betimmt werden.
int ledPin = 13;
int analogPin = A0;
boolean ledOn = false;

void setup() {
   pinMode(ledPin, OUTPUT);
   Wire.begin(SLAVE_ADDRESS);          // Instanz für I2C erstellen
   Wire.onReceive(processMessage);     // CallBack-Funktion einrichten. Dies ist wie ein Interrupt, der aufgerufen wird, wenn eine Meldung auf dem I2C-Bus eintrifft.
   Wire.onRequest(sendAnalogReading);  // Diese Funktion wird aufgerufen, wenn der Raspi einen Wert anfordert.
   Serial.begin(9600);
}

void loop() { }                        // Leer, da ja nur die zwei Interrupts warten

void processMessage(int n) {
 Serial.println("In processMessage");  // Meldung ausgeben
   char ch = Wire.read();              // Wert einlesen
   if (ch == 'l') {
     toggleLED();
   }
}

void toggleLED() {
 ledOn = ! ledOn;
 digitalWrite(ledPin, ledOn);
}

void sendAnalogReading() {
 Serial.println("In sendAnalogReading"); // Meldung ausgeben
 int reading = analogRead(analogPin);    // Wert vom Pin einlesen
 Wire.write(reading >> 2);               // Wert schreiben (der Wert wird um 2 Bit geschoben, was einer Teilung durch vier entspricht, damit man nur 1 Byte senden muss.
}