RasPi Seriell Arduino

In Teil 2 wurde das python-Skript für die Erstellung der Senderliste vorgestellt. Für die Übertragung der Senderliste an den Arduino eignet sich die serielle Schnittstelle perfekt. Sie ist ja für die Kommunikation von lesbaren Zeichenfolgen geschaffen, daher sollte die Übermittlung einer Liste mit Sendernamen und Frequenzen kein Problem sein. Der Teufel ist jedoch ein Eichhörnchen und die Herausforderung einer seriellen Schnittstelle findet sich in den Details.

Über die Nutzung der seriellen Schnittstelle beim RasPi habe ich schon einige Male hier im Blog berichtet (siehe Die serielle Schnittstelle (UART) in Skripten programmieren). Außerdem ist gerade im Raspberry Pi Geek Magazin mein Artikel zur seriellen Kopplung von RasPi und Arduino erschienen. In aller Kürze an dieser Stelle noch einmal die Grundlagen: Grundsätzlich gibt es zwei Wege für eine serielle Verbindung zwischen dem RasPi und dem Arduino. Die erste ist die Verbindung über die GPIO-Pins des RasPI an die IO-Pins des Arduino. Da die Pins des RasPi jedoch maximal 3,3V vertragen, muss ein Spannungsregulierer (Level Shifter) zwischen RasPi und Arduino geschaltet werden. Diese Verbindung ist „Quell-Code-robust“, da die Pins im Arduino vom Programm immer gleich angesprochen werden.

Im Gegensatz dazu unterscheidet sich die Programmierung der seriellen Verbindung über die USB-Schnittstelle von Arduino Modell zu Arduino Modell. So ist zwar bei allen Arduino-Modellen das Objekt „Serial“ immer mit den Pins 0 und 1 verbunden. Während beim Arduino Uno „Serial“ aber auch für die serielle Verbindung über den USB-Port nutzbar ist, muss beim Arduino Leonardo das Objekt „Serial1“ dafür verwendet werden. Der Arduino Due hat sogar 4 serielle Ports. Da allerdings die Verbindung mit einem USB-Kabel besonders einfach ist, und keine zusätzlichen Bauteile wie einen Level-Shifter braucht, habe ich für das Projekt die USB-Schnittstelle genutzt. Man muss nur eben das korrekte Objekt im Programm verwenden („Serial1“ beim Arduino Leonardo).

Für die Übertragung der Senderliste vom RasPi zum Arduino kommt wieder ein python-Skript zum Einsatz. Die Senderliste ist eine CSV-Datei. Eine Zeile der Senderliste für den Standort Berlin sieht so aus:

95.8;radio1;RBB Radio Eins;1km;112

In der ersten Zeile sind die Spaltenüberschriften und danach folgenden in den Zeilen die Frequenz, der RDS-Kurzname, der vollständige Sendername, die Entfernung des Sendemast zum gewählten Standort (hier Berlin Stadtmitte) und die Sendestärke in dB. Die Liste für Berlin hat 90 Einträge. Es sollen jedoch nur die ersten 16 Sender übertragen werden. Praktischerweise hat das Abfrage-Skript die Liste schon sortiert, so dass die Übermittlung an den Arduino nach den ersten 16 Zeilen der Datei beendet werden kann.

Das python-Skript zum Übertragen der Senderliste an den Arduino ist unten nur als relevante Auszug aufgeführt. Das komplette Script ist etwas umfangreicher und auf Github abgelegt. Die folgenden Zeilennummern beziehen sich auf das komplette Script auf Github. Es ist umfangreicher als vielleicht nötig, denn es prüft die USB-Ports des RasPi auf einen angeschlossenen Arduino (Zeile 25 – 42). Ich habe meine Arduino-Modelle in Form von Vendor- und Produkt-ID hinterlegt, es kann aber sein, dass es hier Abweichungen zu anderen Modellen bzw. Versionen gibt. Falls Ihr einen Arduino angeschlossen habt, aber die Meldung kommt, es würde kein Arduino gefunden, dann kann das mit diesen IDs zusammenhängen. Testweise kann man sich die Werte über die Variable ports ausgeben lassen.

Doch der Reihe nach: Zunächst werden die Bibliotheken für die serielle Kommunikation geladen. Die Bibliothek ist im python-Paket pySerial enthalten. In Zeile 12 bis 18 wird die Datei mit der Senderliste eingelesen. Nach 16 gefundenen Sendern ist Schluss bzw. wenn vorher das Ende der Datei erreicht ist. Eine Zeile wird als Sendereintrag akzeptiert, wenn das erste Zeichen eine Ziffer ist. Ab Zeile 44 werden die Sender übertragen. Zunächst wird der serielle Port mit einer Baudrate von 38400 Baud geöffnet und das timeout deaktiviert. Dann wird die Zeichenkette „knock“ zum Arduino geschickt und damit „angeklopft“. Wenn der Arduino damit etwas anfangen kann, wird er mit „OK“ antworten. Und falls er das tut, beginnt der RasPi, Sender für Sender über das Kabel zu schicken. Jedes Mal sollte der Arduino mit einem „OK“ antworten. Da der Arduino eine Weile braucht, um die Sender abzuspeichern, legt das Skript nach jedem Sender eine kleine Pause ein.

#!/usr/bin/env python
# -*- codepage: utf-8 -*-
import serial
import re
import serial.tools.list_ports
import time

with open('stations.txt') as station_file:
  top15_sender=station_file.readlines[1:16]

ser = serial.Serial('/dev/ttyUSB0', 38400, timeout=None)
ser.write('knock')
line = ser.readline().rstrip()
if line == 'OK':
  print line
  for sender in top15_sender:
    to_send = ';'.join(sender.rstrip().split(';')[0:2])
    if re.match(r'^\d+',to_send):
      ser.write(to_send)
      print to_send
      line = ser.readline().rstrip()
      if line != 'OK':
        print "Da ging was schief"
        exit()
      time.sleep(3)

Und hier das passende C-Programm auf dem Arduino, das auf die Datenübertragung vom RasPi hört:

#include <Wire.h>
#include <EEPROM.h>

boolean knock = false;

void init_eeprom(void) {
  if (EEPROM.read(0)!='I') {
    EEPROM.write(0,'I');
    EEPROM.write(1,0);
  }
};
void setup() {
  init_eeprom();
  Serial.begin(38400);
  Serial.println("RadioXZ");
};
void loop() {
  char radio_station[23];
  if(Serial.available()>0) {
    String line = String(Serial.readString());
    switch(line.charAt(0)) {
      case 'k': Serial.println("OK");
        knock = true; break;
      default:
        if(knock==true) {
          String f = String(line);
          f.toCharArray(radio_station,23);
          radio_station[f.length()] = 0;
          byte counter = EEPROM.read(1);
          EEPROM.put(2+(counter*sizeof(radio_station)), radio_station);
          delay(500);
          counter = (counter % 16) + 1;
          EEPROM.write(1,counter);
          Serial.println("OK");
        };
        break;
    }
  }
}

Etwas ausführlicher beschreibe ich die Programmierung eines EPROM im Artikel „Langzeitgedächtnis“, der im Raspberry Pi Geek Magazin 06/2015 erschienen ist und inzwischen online freigeschalten wurde.