Rasperry Pi mit Arduino
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.
# 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
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()