Raspberry Pi IO
Ich war einer der glücklichen, die einen der ersten Raspberry Pis ergattern konnte. Zu meiner Schande muss ich gestehen, dass ich relativ lange gezögert habe, mich intensiver mit dem Computerchen zu beschäftigen.
Interessant für Bastler ist auf jeden Fall die anschließbare Peripherie. Neben UART, SPI und I²C (für letztere existieren noch keine Treiber und ich bin leider noch zu blöd welche zu schreiben) existieren auch frei belegbare GPIOs auf dem Board.
Inhaltsverzeichnis
Erster Test
Das Beispielprogramm erfüllt seinen Zweck sehr gut. Nach ein wenig Herumspielen wollte ich wissen, wie schnell man denn die Bits wackeln lassen kann.
Eine kleinere Änderung im Source lässt GPIO17 (bzw. "GPIO 0" an der Steckerbelegung - ich möchte nicht wissen, wer auf diese missverständliche Namensgebung gekommen ist) mit nahezu maximaler Frequenz 1000 ein- und ausschalten.
for (rep=0; rep<1000; rep++)
{
GPIO_SET = 1<<17;
GPIO_CLR = 1<<17;
}
Mit den Shellbefehlen
gcc io.c sudo ./a.out
Lässt sich der Code kompilieren und anschließend ausführen.
Der Logic-Analyzer misst bei dem Progrämmchen knapp 13MHz, wobei ab und zu geringfügig größere Pausen entstehen, die die Frequenz auf 11MHz absinken lassen. Das ist ordentlich!
Das Problem mit root
Das Beispielprogramm funktioniert also super, solange man es als root ausführt.
Hinsichtlich der Sicherheit ist das jedoch etwas ungeschickt - wer möchte seine Programme dauerhaft als root ausführen?
Besonders für mein Vorhaben eine ganz schlechte Idee: ich möchte als kleine Demonstration LEDs per Browser schalten lassen. Gleichzeitig verbietet der gesunde Menschenverstand, einen Webserver oder gar PHP als root-User laufen zu lassen (besonders in Hinblick auf die schlechte Presse in den letzten Tagen).
Auch das /dev/mem-Interface möchte man nicht unbedingt fürs Userland freigeben (was zudem auch nicht funktioniert).
Glücklicherweise kann man auf die GPIOs einzeln über virtuelle Dateien zugreifen. Das ist zwar nicht ganz so schnell, dafür kann man die virtuellen Dateien "normalen" Benutzern zugänglich machen.
Dieses Zugänglichmachen findet für jeden Pin in 4 Schritten statt (hier am Beispiel von GPIO17):
sudo echo "17" > /sys/class/gpio/export sudo echo "out" > /sys/class/gpio/gpio17/direction sudo chmod 555 /sys/class/gpio/gpio17/value sudo chmod 555 /sys/class/gpio/gpio17/direction
Die letzten beiden Zeilen erlauben Lese- und Schreibzugriff für Besitzer (root), Gruppe (root) und jeden anderen. Alternativ kann man auch mit chown den Besitzer bzw. die Gruppe ändern, sodass man keinen Zugriff für jeden geben muss. In meinem Fall ist es aber (noch) egal.
Um alle Ports auf einmal "freizugeben" habe ich eine kleines Shell-Script geschrieben, die dies erledigt:
#!/bin/sh echo "17" > /sys/class/gpio/export echo "18" > /sys/class/gpio/export echo "21" > /sys/class/gpio/export echo "22" > /sys/class/gpio/export echo "23" > /sys/class/gpio/export echo "24" > /sys/class/gpio/export echo "25" > /sys/class/gpio/export echo "out" > /sys/class/gpio/gpio17/direction echo "out" > /sys/class/gpio/gpio18/direction echo "out" > /sys/class/gpio/gpio21/direction echo "out" > /sys/class/gpio/gpio22/direction echo "out" > /sys/class/gpio/gpio23/direction echo "out" > /sys/class/gpio/gpio24/direction echo "out" > /sys/class/gpio/gpio25/direction chmod 555 /sys/class/gpio/gpio17/value chmod 555 /sys/class/gpio/gpio18/value chmod 555 /sys/class/gpio/gpio21/value chmod 555 /sys/class/gpio/gpio22/value chmod 555 /sys/class/gpio/gpio23/value chmod 555 /sys/class/gpio/gpio24/value chmod 555 /sys/class/gpio/gpio25/value chmod 555 /sys/class/gpio/gpio17/direction chmod 555 /sys/class/gpio/gpio18/direction chmod 555 /sys/class/gpio/gpio21/direction chmod 555 /sys/class/gpio/gpio22/direction chmod 555 /sys/class/gpio/gpio23/direction chmod 555 /sys/class/gpio/gpio24/direction chmod 555 /sys/class/gpio/gpio25/direction
Für alle, die Linux nicht kennen: das "#!/bin/sh" am Anfang ist kein gewöhnlicher Kommentar, sondern ein Shebang, das der Bash (also der Konsole) sagt, mit was die Datei ausgeführt werden möchte. Wichtig ist auch, dass die Zeilenenden auf Linefeed (\n) und nicht auf Carrier Return+Linefeed eingestellt sind. Jeder gute Editor (z. B. Nodepad++ oder SciTE) kann das.
Wichtig ist anschließend, dass die Datei vor dem Start Ausführungsrechte bekommt:
chmod 770 gpio.sh
Anschließend kann man sie mit dem Befehl folgendem Befehl ausführen:
sudo ./gpio
Achtung: Die Freigabe der GPIOs ist nicht persistent. Nach einem Neustart muss das Script erneut ausgeführt werden!
Webserver
Ein grundlegendes Element fehlt noch: der Webserver, was wahrscheinlich am einfachsten mit PuTTY geht.
Hier die Befehle, die ich zur Installation von lighttpd mit PHP5 verwendet habe (keine Garantie auf Funktionalität, Vollständigkeit und Korrektheit):
sudo groupadd www-data sudo aptitude install lighttpd sudo aptitude install php5-cgi sudo lighty-enable-mod fastcgi sudo adduser pi www-data sudo chown -R www-data:www-data /var/www sudo chmod -R 775 /var/www
Damit PHP funktioniert, muss in der Datei /etc/lighttpd/lighttpd.conf (wenn noch nicht vorhanden) durch das Ausführen von
sudo nano /etc/lighttpd/lighttpd.conf
folgender Text hinzugefügt werden:
fastcgi.server = ( ".php" => (( "bin-path" => "/usr/bin/php5-cgi", "socket" => "/tmp/php.socket" )))
Nach einem Server-Neustart mit
sudo /etc/init.d/lighttpd force-reload
sollte der Raspberry Pi per HTTP erreichbar sein.
Software
Server
Wie schon erwähnt läuft auf dem Server (oder sollte zumindest) PHP laufen. Da ich es nicht mag, Seiten wegen Kleinigkeiten neu zu laden, kommt ein Ajax/JSON-Interface zum Einsatz.
Dementsprechend fällt die Serversoftware minimalistisch aus.
Beispielsweise übernimmt read.php das Lesen der Ports. Die Informationen werden in ein assoziatives (Name => Wert) Array geschrieben, ins JSON-Format umgewandelt und an den Client ausgegeben:
<?php
$bits = array(17, 18, 21, 22, 23, 24, 25);
$retval = array();
for($x = 0; $x < count($bits); $x++) {
$bit = $bits[$x];
$val = trim(@shell_exec("cat /sys/class/gpio/gpio".$bit."/value"));
$dir = trim(@shell_exec("cat /sys/class/gpio/gpio".$bit."/direction"));
$val = $val == "1" ? 1 : 0;
$dir = $dir == "out" ? "o" : "i";
$retval[] = array("bit" => $bit, "val" => $val, "dir" => $dir);
}
echo json_encode($retval);