Seit einiger Zeit liegen in meiner Schublade zwei Raspberry Pi herum. Die Zeit war reif, endlich etwas damit zu machen. Zunächst wollte ich etwas mit den General-Purpose-Input-Output (GPIO) spielen. Danach dachte ich jedoch, dass es doch mehr Spaß machen würde, wenn ich das Ganze mit XMPP verbinden kann. Dies ist dabei heraus gekommen.
Elektronische Verschaltung
Für die Nutzung der GPIO Pins am Raspberry sollte man sich zunächst einen Überblick über die PIN Belegung machen.
Simple LED
Der einfachste Schritt ist eine LED über einen GPIO ein- und auszuschalten. Ich habe dazu eine LED verwendet, die bei 3.0V (3.6V max.) arbeitet, daher ohne weitere Wiederstände verwendbar ist. Diese habe ich mit dem Ground PIN P1-06 und dem GPIO-4 (Pin1-07) verbunden.
Um die Schaltung per Konsole zu testen ist Python ein bequemer Weg.
pi@raspberrypi:~$ sudo python Python 2.7.3 (default, Jan 13 2013, 11:20:46) [GCC 4.6.3] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import RPi.GPIO as GPIO >>> GPIO.cleanup() >>> GPIO.setmode(GPIO.BCM) >>> GPIO.setup(4, GPIO.OUT) >>> GPIO.output(4, GPIO.HIGH)
Und schon leuchtet die LED.
Taster
Um den Taster anzuschließen, wird die Schaltung etwas schwieriger. Hierzu bin ich nach dieser Anleitung vorgegangen und habe die Pull Down Schaltung gewählt. Die Schaltung habe ich dann mit GPIO 17 (Pin1-11) verbunden. Ein einfacher Test wie auch in der Anleitung beschrieben, lässt es einfach prüfen:
import RPi.GPIO as GPIO ## Import GPIO library import time ## Import 'time' library. Allows us to use 'sleep' GPIO.cleanup() GPIO.setmode(GPIO.BCM) ## Use board pin numbering GPIO.setup(17, GPIO.IN) ## Setup GPIO Pin 7 to OUT prev_input = 0 while True: #take a reading input = GPIO.input(17) #if the last reading was low and this one high, print if (((not prev_input) and input)): print("Button pressed") #update previous input prev_input = input #slight pause to debounce time.sleep(0.05)
ergibt…
pi@raspberrypi:~$ sudo python input.py Button pressed Button pressed Button pressed
Schalter
Den Schalter habe ich mit der gleichen Schaltung an GPIO 22 (Pin1-15) angeschlossen.
Java GPIO
Mein nächster Schritt ist ein Java Programm, dass zunächst die GPIO Funktionen erledigen soll. Um Java auf dem PI ausführen zu können, habe ich zunächst Java 8 für ARM heruntergeladen und entpackt. Danach habe ich unter /opt/javalibs einen Ordner für Bibliotheken eingerichtet. Danach habe ich die Umgebungsvariablen JAVA_HOME und CLASSPATH eingerichtet und die PATH Variable entsprechend um das JAVA_HOME/bin Verzeichnis erweitert.
Schnell über die IDE ein Maven Projekt erstellt und die Dependency pi4j hinzugefügt.
<dependency> <groupId>com.pi4j</groupId> <artifactId>pi4j-core</artifactId> <version>0.0.5</version> </dependency> <dependency> <groupId>com.pi4j</groupId> <artifactId>pi4j-gpio-extension</artifactId> <version>0.0.5</version> </dependency>
Dann geht es weiter mit einer einfachen Klasse inkl. Main-Methode. Es ist zu beachten, dass PI4J eine eigene Pin Belegung
package de.patrickbreucking.m2m.example; import com.pi4j.io.gpio.*; import com.pi4j.io.gpio.event.GpioPinDigitalStateChangeEvent; import com.pi4j.io.gpio.event.GpioPinListenerDigital; import de.patrickbreucking.m2m.vipclient.SystemController; public class Main { private GpioController gpio; static void main(String[] args) { Main main = new Main(); main.init(); main.blinkLed(); //Run until termination while(true) { Thread.sleep(100); } } private void blinkLed() { final GpioPinDigitalOutput led1 = gpio.provisionDigitalOutputPin(RaspiPin.GPIO_07); led1.blink(200); } private void init() { gpio = GpioFactory.getInstance(); GpioPinDigitalInput myButton = gpio.provisionDigitalInputPin(RaspiPin.GPIO_00, PinPullResistance.PULL_DOWN); myButton.addListener(new GpioPinListenerDigital() { @Override public void handleGpioPinDigitalStateChangeEvent(GpioPinDigitalStateChangeEvent event) { if (event.getState().isHigh()) { System.out.println("Button was pressed!"); } } }); } }
Um das Programm einfach zu paketieren und per java -jar ausführbar zu machen, habe ich noch das Maven-Manifest Plugin entsprechend instruiert.
<packaging>jar</packaging> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <manifest> <addClasspath>true</addClasspath> <classpathPrefix>/opt/javalibs/</classpathPrefix> <mainClass>de.patrickbreucking.m2m.Main</mainClass> </manifest> </archive> </configuration> </plugin> </plugins> </build>
Nun die Libs auf dem PI unter /opt/javalibs kopieren, mit Maven das Jar erstellen und per SCP auf den PI bringen.
Java XMPP
Der letzte Schritt ist die Verwendung von XMPP um die LED von einem Chat-Client ein- und auszuschalten, bzw. per Knopfdruck eine Nachricht an den Chatpartner zu senden. Für XMPP habe ich die Smack Libs eingebunden.
<dependency> <groupId>org.igniterealtime.smack</groupId> <artifactId>smack</artifactId> <version>3.2.1</version> </dependency> <dependency> <groupId>org.igniterealtime.smack</groupId> <artifactId>smackx</artifactId> <version>3.2.1</version> </dependency>
In der Klasse XmppClient werden die XMPP Funktionen implementiert.
package de.patrickbreucking.m2m.example; import org.jivesoftware.smack.*; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.sasl.SASLDigestMD5Mechanism; public class XmppClient { private Connection conn1; public XmppClient() { } public void sendMessage(String desitination, String message) { ChatManager chatmanager = conn1.getChatManager(); Chat chat = chatmanager.createChat(desitination, new MessageListener() { @Override public void processMessage(Chat chat, Message message) { if (message.getBody().equals("pong")) { try { chat.sendMessage("pong received"); chat.removeMessageListener(this); } catch (XMPPException e) { e.printStackTrace(); } } } }); try { chat.sendMessage(message); } catch (XMPPException e) { e.printStackTrace(); } } public void connectToXmpp() throws XMPPException { SmackConfiguration.setPacketReplyTimeout(10000); boolean oldSsl = false; // Create a connection to the jabber.org server. ConnectionConfiguration config = new ConnectionConfiguration("xxx.xxxx.xx", 5222, "domain"); if (oldSsl) { config.setSecurityMode(ConnectionConfiguration.SecurityMode.enabled); config.setSocketFactory(new DummySSLSocketFactory()); config.setSASLAuthenticationEnabled(false); } else { SASLAuthentication.registerSASLMechanism("DIGEST-MD5", SASLDigestMD5Mechanism.class); SASLAuthentication.supportSASLMechanism("DIGEST-MD5", 0); config.setSASLAuthenticationEnabled(true); } config.setSelfSignedCertificateEnabled(true); config.setVerifyRootCAEnabled(false); config.setExpiredCertificatesCheckEnabled(false); config.setNotMatchingDomainCheckEnabled(false); config.setVerifyChainEnabled(false); conn1 = new XMPPConnection(config); conn1.connect(); conn1.login("xxx", "xxx", "client"); ChatManager chatmanager = conn1.getChatManager(); chatmanager.addChatListener(new ChatListener()); System.out.println("Xmpp Connect completed"); } }
Nun muss in der Base Klasse noch XMPP mit dem GPIO verbunden. Das Prozedere mit dem XMPP einrichten habe ich hier nicht beschrieben.
package de.patrickbreucking.m2m.vipclient; import org.jivesoftware.smack.Chat; import org.jivesoftware.smack.ChatManagerListener; import org.jivesoftware.smack.MessageListener; import org.jivesoftware.smack.packet.Message; import java.util.GregorianCalendar; public class ChatListener implements ChatManagerListener, MessageListener { @Override public void chatCreated(Chat chat, boolean b) { chat.addMessageListener(this); } @Override public void processMessage(Chat chat, Message message) { String body = message.getBody(); GregorianCalendar cal = new GregorianCalendar(); try { if (body == null || body.isEmpty()) { chat.sendMessage("nok - could not parse message"); return; } try { System.out.println("Got message from " + message.getFrom() + ", Content: " + body.toString()); Main.getInstance().handleCommunicationEvent(message.getBody()); } catch (Exception e) { chat.sendMessage("nok - could not parse message"); return; } } catch (Exception e) { System.out.println("Error in processint Message"); } } }
Die empfangene Nachricht wird an die Main Klasse weitergeleitet. Ich habe dazu die Main Klasse zu einem Singleton ausgebaut (böses Pattern – ich weiß). Nun habe ich noch die Klasse um diese beiden Methoden erweitert:
private void init() throws XMPPException { final XmppClient client = new XmppClient(); client.connectToXmpp(); gpio = GpioFactory.getInstance(); GpioPinDigitalInput myButton = gpio.provisionDigitalInputPin(RaspiPin.GPIO_00, PinPullResistance.PULL_DOWN); myButton.addListener(new GpioPinListenerDigital() { @Override public void handleGpioPinDigitalStateChangeEvent(GpioPinDigitalStateChangeEvent event) { if (event.getState().isHigh()) { System.out.println("Button was pressed!"); client.sendMessage("xxx", "Button was pressed!"); } } }); } public void handleCommunicationEvent(String body) { if (body.toString().toLowerCase().contains("blink_on")) { blinkLed(); } else if (body.toString().toLowerCase().contains("blink_off")) { ledOff(); } } private void ledOff() { final GpioPinDigitalOutput led1 = gpio.provisionDigitalOutputPin(RaspiPin.GPIO_07); led1.low(); }
Et voilà, wir können per Chat die LED steuern und bekommen Auskunft über Tastendrücke am PI.
(Urheberrecht)
Das Bild zur PIN Belegung stammt vom http://elinux.org/ Wiki und ist lizensiert durch die Creative Commons Attribution-ShareAlike 3.0 Unported License.