IoT per Knopfdruck

Internet of Things mit Java 8 und TinkerForge, Teil 3

Sven Ruppert
© S&S Media

Heute werden wir mit dem IoT auf Tuchfühlung gehen. Im Fokus steht das DualButton-Bricklet von TinkerForge. Wie kann bidirektional kommuniziert werden? Welche Interaktionsmöglichkeiten mit der Elektronik gibt es? Das alles wird die Vorbereitung für weitere manuelle Elemente sein.

Wir sind es gewohnt, Hautkontakt zu unserer Elektronik zu haben. Um genau zu sein: Fingerkontakt. Das DualButton-Bricklet gibt uns die Möglichkeit, manuell Impulse zu senden, die dann auf der Java-Seite ausgewertet werden. Aber auch der Weg zurück zum Bricklet ist machbar. Wir werden uns das heute genauer ansehen.       

Das DualButton-Bricklet

Das DualButton-Bricklet kann wie die anderen verwendeten Elemente einfach per Kabel an den MasterBrick angeschlossen werden. Damit stehen zwei Drucktaster zur Verfügung. Beide Taster haben eine eingebaute blaue LED. Der aktuelle Zustand der Taster (gedrückt/nicht gedrückt) und der LEDs (an/aus) kann jederzeit ausgelesen werden. Programmatisch können die Zustände der LEDs natürlich auch von außen gesetzt werden.

Verwendung der Buttons

Die Anbindung eines DualButton-Bricklets erfolgt auch wieder über die Konfiguration einer kapselnden Instanz des Elements und der Implementierung eines ActionListeners. Innerhalb desselben bekommt man die Werte geliefert. Da es sich um zwei separate Taster auf dem Bricklet handelt, gibt es prinzipiell die Zustände „Pressed“ und „Released“ für jeden Taster. Dargestellt werden die beiden Zustände durch eine 0 für „Released“ und eine 1 für „Pressed“. Wie in den vorherigen Artikeln beschrieben, werden wir das Bricklet zusammen mit dem ActionListener wieder in eine eigene Klasse auslagern, die das Interface Runnable implementiert. Die Klasse nennen wir Worker.

public class Worker implements Runnable{
    @Override
    public void run() {
        try {
            ipcon.connect(host, port);
            final BrickletDualButton.StateChangedListener changedListener
                    = (buttonL, buttonR, ledL, ledR) -> runLater(() -> {

                if (buttonL == BrickletDualButton.BUTTON_STATE_PRESSED) {
                    setMsg("Left button pressed");
                    activateLeftButton();
                }
                if (buttonL == BrickletDualButton.BUTTON_STATE_RELEASED) {
                    setMsg("Left button released");
                }
                if (buttonR == BrickletDualButton.BUTTON_STATE_PRESSED) {
                    setMsg("Right button pressed");
                    activateRightButton();
                }
                if (buttonR == BrickletDualButton.BUTTON_STATE_RELEASED) {
                    setMsg("Right button released");
                }
            });

            db.addStateChangedListener(changedListener);
            db.setLEDState(LED_STATE_ON, LED_STATE_ON );

        } catch (IOException
                | AlreadyConnectedException
                | TimeoutException
                | NotConnectedException e) {
            e.printStackTrace();
        }
    }

    private void setMsg(String msg) {
        tx.setText(msg.concat(newline).concat(tx.getText()));
    }
}

Innerhalb des ActionListeners werden einem wieder die benötigten Werte übergeben. In unserem Fall werden immer die Zustände beider Taster geliefert. Wenn nur der Zustand des Drückens von Interesse ist, kann man einfach, wie im Listing 1 dargestellt, den Status einzeln prüfen und reagieren. Sollte von Interesse sein, wann der Taster wieder losgelassen wurde, so muss man sich zusätzlich den letzten Wechsel-Zustand merken, um über die Differenz den Zeitpunkt zu bestimmen. Zu Beginn setzen wir den Zustand der beiden LEDs einmalig auf aktiv/leuchtend. In unserem Beispiel wurden die beiden Methoden activateRightButton() und activateLeftButton() aus der Klasse Worker heraus in die Klasse ButtonMaster verschoben. Das hatte lediglich den Grund, dass die beiden Methoden (Listing 2) gleich an anderer Stelle nochmals verwendet werden.

private void activateRightButton() {
    try {
        db.setLEDState(LED_STATE_OFF, LED_STATE_ON);
        bL.setText("InActive");
        bR.setText("Active");
    } catch (TimeoutException | NotConnectedException e) {
        e.printStackTrace();
    }
}

private void activateLeftButton() {
    try {
        db.setLEDState(LED_STATE_ON, LED_STATE_OFF);
        bL.setText("Active");
        bR.setText("InActive");

    } catch (TimeoutException | NotConnectedException e) {
        e.printStackTrace();
    }
}

Innerhalb dieser Methoden wird zum einen der Zustand der jeweiligen LED, zum anderen der Text der beiden JavaFX Buttons gegenläufig (Acive/InActive) gesetzt. Damit wird das Drücken eines Tasters nicht nur durch die LEDs sichtbar, sondern auch durch die Beschriftung der beiden Buttons im JavaFX GUI. Damit haben wir den Weg von der Hardware zum GUI beschrieben. Wie aber geht es von JavaFX zum Taster?

Anbindung an JavaFX

Die Anbindung der beiden Taster an JavaFX ist vom Prinzip her genauso wie bei den Sensoren in den letzten Artikeln. Zur Erinnerung: Das Grundprinzip ist in der Form, dass aus der JavaFX-Anwendung heraus ein Thread gestartet wird. Dieser konfiguriert das Element und fügt in der run() – Methode den ActionListener hinzu. Wichtig hierbei ist, das die Anteile, die JavaFX-GUI-Elemente aktualisieren, wiederum in ein Platform.runLater() gekapselt werden. Der Aufruf ist dann sehr einfach:

Platform. runLater(new Worker());

Nun gehen wir noch den Weg von den JavaFX-Buttons zu der Hardware. Durch Drücken auf einen der beiden Softwarebuttons wird die jeweilige korrespondierende LED aktiviert. Das einzige, was hierzu notwendig ist, ist der Aufruf der dafür vorgesehenen Methode activate(Left/Right)Button(). Schon haben wir eine Steuerung der GUI durch die Hardware und umgekehrt. Anzumerken ist hier natürlich, dass durch das Drücken auf den Softwarebutton der Taster mechanisch NICHT bewegt wird. In Listing 3 ist das vollständige JavaFX-Programm zu sehen.

public class ButtonMaster extends Application {
    private static final String host = "localhost";
    private static final int port = 4223;
    private static final String UID = "j5K";
    public static String newline = System.getProperty("line.separator");

    private final IPConnection ipcon = new IPConnection();
    private final BrickletDualButton db 
        = new BrickletDualButton(UID, ipcon);

    public static void main(String[] args) {
        launch(args);
    }

    public Button bL;
    public Button bR;

    public TextArea tx;

    @Override
    public void start(Stage stage) {
        stage.setTitle("Button TinkerForge Sample");

        final VBox vBox = new VBox();
        setAnchorZero(vBox);
        final HBox hBox = new HBox();
        setAnchorZero(hBox);
        bL = new Button("LED links");
        bR = new Button("LED rechts");

        setAnchorZero(bL);
        setAnchorZero(bR);

        bL.setOnAction(actionEvent -> activateLeftButton());
        bR.setOnAction(actionEvent -> activateRightButton());

        hBox.getChildren().add(bL);
        hBox.getChildren().add(bR);

        vBox.getChildren().add(hBox);

        tx = new TextArea();
        vBox.getChildren().add(tx);


        Scene scene = new Scene(vBox);
        stage.setScene(scene);

        runLater(new Worker());

        stage.show();

    }

    private void setAnchorZero(final Node node) {
        AnchorPane.setBottomAnchor(node, 0.0);
        AnchorPane.setLeftAnchor(node, 0.0);
        AnchorPane.setRightAnchor(node, 0.0);
        AnchorPane.setTopAnchor(node, 0.0);
    }


    public class Worker implements Runnable {
        @Override
        public void run() {
            try {
                ipcon.connect(host, port);

                db.addStateChangedListener(
                  (buttonL, buttonR, ledL, ledR) -> runLater(() -> {
                    if (buttonL == BUTTON_STATE_PRESSED) {
                        setMsg("Left button pressed");
                        activateLeftButton();
                    }
                    if (buttonL == BUTTON_STATE_RELEASED) {
                        setMsg("Left button released");
                    }
                    if (buttonR == BUTTON_STATE_PRESSED) {
                        setMsg("Right button pressed");
                        activateRightButton();
                    }
                    if (buttonR == BUTTON_STATE_RELEASED) {
                        setMsg("Right button released");
                    }
                }));
                db.setLEDState(LED_STATE_ON, LED_STATE_ON);
            } catch (IOException
                    | AlreadyConnectedException
                    | TimeoutException
                    | NotConnectedException e) {
                e.printStackTrace();
            }
        }
        private void setMsg(String msg) {
            tx.setText(msg.concat(newline).concat(tx.getText()));
        }
    }

    private void activateRightButton() {
        try {
            db.setLEDState(LED_STATE_OFF, LED_STATE_ON);
            bL.setText("InActive");
            bR.setText("Active");
        } catch (TimeoutException | NotConnectedException e) {
            e.printStackTrace();
        }
    }

    private void activateLeftButton() {
        try {
            db.setLEDState(LED_STATE_ON, LED_STATE_OFF);
            bL.setText("Active");
            bR.setText("InActive");
        } catch (TimeoutException | NotConnectedException e) {
            e.printStackTrace();
        }
    }
}

Fazit

Mit dem DualButton-Bricklet haben wir zum ersten Mal die Möglichkeit, manuell per mechanischem Taster Software-Prozesse in Gang zu setzen. Was man damit alles realisieren kann, werden wir uns in einem der nächsten Teile genauer ansehen. Bis dahin: Stay tuned und Happy coding!

Die Quelltexte zu diesem Text sind unter [1] zu finden. Wer umfangreichere Beispiele zu diesem Thema sehen möchte, dem empfehle einen Blick auf [2].

Geschrieben von
Sven Ruppert
Sven Ruppert
Sven Ruppert arbeitet seit 1996 mit Java und ist Developer Advocate bei Vaadin. In seiner Freizeit spricht er auf internationalen und nationalen Konferenzen, schreibt für IT-Magazine und für Tech-Portale. Twitter: @SvenRuppert
Kommentare

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
400
  Subscribe  
Benachrichtige mich zu: