Skip to content

Raspberry Pi GPIO mit XMPP

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.

Pin Belegung Raspberry (CC 3.0)

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.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.