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 und deren Richtung. 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);
Client
Den Client habe ich relativ schnell (und etwas schlampig) in JavaScript heruntergerissen.
Wer etwas JavaScript und die vielen d.ebi, d.ac, d.ce und d.ctn irritiert - das sind Abkürzungen für häufig verwendete Befehle, die ich in common.js definiert habe:
var d = {
d : document,
w : window,
ebi : function(i) { return document.getElementById(i); },
ebn : function(n) { return document.getElementsByName(n); },
febn : function(n) { return document.getElementsByName(n)[0]; },
ce : function(n) { return document.createElement(n); },
ctn : function(n) { return document.createTextNode(n); },
ac : function(p, c) { return p.appendChild(c); },
ac1 : function(p, c) { p.appendChild(c); return p; }
}
d.ebi steht zum Beispiel für document.getElementById
d.ac für document.appendChild
usw...
In diesem Sinne auch eine Entschuldigung an alle, die bei dem Klammer-Massaker einen Knoten im Hirn bekommen ;-)
Dadurch, dass nahezu alles in JavaScript gemacht wird, ist der HTML-Body gähnend leer. Erst beim Laden wird der Seite Leben eingehaucht:
Der Code erzeugt zunächst eine Tabelle, die dann durch Objekte von GpioPin gefüllt wird. Durch Klick auf die span-Elemente bzw. Bilder wird der Inhalt umgeschaltet und per XmlHttpRequest ein Kommando an den Server geschickt.
Beim Klick auf Lesen wird ebenfalls per XmlHttpRequest eine Anfrage an den Server gesendet, wie oben erwähnt, mit einem JSON-String antwortet. Dies sieht dann z. B. wie folgt aus:
[{"bit":17,"val":0,"dir":"o"},{"bit":18,"val":1,"dir":"o"},{"bit":21,"val":0,"dir":"o"},{"bit":22,"val":0,"dir":"i"},{"bit":23,"val":1,"dir":"i"},{"bit":24,"val":0,"dir":"o"},{"bit":25,"val":0,"dir":"o"}]
Aufgedröselt wird die Struktur deutlicher:
[
{
"bit" : 17,
"val" : 0,
"dir" : "o"
},{
"bit" : 18,
"val" : 1,
"dir" : "o"
},{
"bit" : 21,
"val" : 0,
"dir" : "o"
},{
...
}
]
Diese wird durch den JSON-parser in ein Objekt umgewandelt, per Schleife zugeordnet und durch die Funktion GpioPin::SetVals() gesetzt.
Die Richtung und Wert kann man einfach durch Klick auf das jeweilige Element verändern. Durch einen Klick auf "Refresh" kann man die tatsächlichen Werte vom Server ermitteln, nach dem Aktivieren der Checkbox geschieht dies automatisch im Sekundentakt.
Getestet wurde die die Anwendung bis jetzt im Firefox, "Standard" Android-Browser und dem aktuellen IE, wobei in letzterem die Aktualisierung nicht richtig funktioniert.
Download
Hinweise
Der Inhalt der ZIP-Datei kann relativ einfach mit FileZilla auf den Raspberry (per SSH-Filetransfer) geschoben werden. Der Order IO muss unter /var/www/ liegen, gpio.sh an einem nahezu beliebigen Ort. Vor dem Ausführen der gpio.sh nicht vergessen, die Ausführungsrechte zu setzen!