Raspberry-PI LED per Weboberfläche steuern mit Java Play und pi4j

Es gibt viele Anleitungen, um eine LED über die GPIO-Schnittstelle des Raspberry PI zu steuern. Heute kommt eine weitere dazu. Mit Hilfe des Play Frameworks soll eine kleine Web-App gebaut werden über die mit Hilfe der Java-GPIO-Bibilothek Pi4J eine LED ein- und ausgeschaltet werden kann.
pi-play-gpio-1
Damit das ganze dann auf dem Handy auch noch hübsch aussieht kommt dabei noch das CSS-Framework Bootstrap und das Javascript Framework jQuery zum Einsatz. Mit viel größeren Kanonen kann man wohl kaum noch auf Spatzen schießen. Also: an die Arbeit…

Play Framework installieren

  • Play-Framework herunterladen: https://www.playframework.com/download
  • Zip Datei z.B. in ~/workspace/ entpacken
  • App mit Hilfe von activator erstellen:
    $ cd ~/workspace
    $ ./activator new pi-play-gpio play-java
    

     

  • Die App starten
    $ cd pi-play-gpio
    $ ./activator # achtung, nun wird scheinbar das halbe Internet heruntergeladen
    [pi-play-gpio] $ run # Achtung, scheinbar folgt nun die zweite Hälfte...
    
  • Nach einiger Zeit kommt die Meldung
     [info] play - Listening for HTTP on /0:0:0:0:0:0:0:0:9000

    . Nun einfach im Browser http://localhost:9000 eingeben und die die erste Lauffähige App kann bewundert werden (Bisher wurde noch nicht eine Zeile Code geschrieben)

  • Jetzt kann man das ganze Projekt noch in ein Eclipse-Projekt verwandeln. Dazu einfach die App mit Strg-d beenden und in die play-console „eclipse“ eingeben. Danach lässt sich das Projekt einfach in Eclipse importieren.

Rest-API zum Steuern der LED vorbereiten

Die LED soll über eine REST-API gesteuert werden. Dazu sollen folgende Methoden implementiert werden:

  • /api/setLed?status=true // schaltet die LED an
  • /api/setLed?status=false // schaltet die LED aus
  • /api/toggleLed // schaltet die LED abwechselnd an und aus

Dazu wird eine neue Klasse mit dem Namen Api.java unter app/controllers/Api.java erstellt:

package controllers;

import play.Logger;
import play.mvc.Controller;
import play.mvc.Result;

public class Api extends Controller {

	public static Result setLed(boolean on) {
		Logger.info("Switching LED to " + on);
		return ok();
	}

	public static Result toggleLed() {
		Logger.info("Toggle LED");
		return ok();
	}
}

Damit die API-Methoden nutzbar sind, müssen sie noch das entsprechende Routing festgelegt weden. Dazu einfach die folgenden Zeilen in die Datei conf/routes hinzufügen:

GET     /api/setLed       controllers.Api.setLed(status: Boolean ?= true)
GET     /api/toggleLed    controllers.Api.toggleLed()

Die App kann jetzt wieder über „./activator run“ gestartet werden (falls sie nicht mehr läuft) und schon kann die API im Browser getestet werden:

  • http://localhost:9000/api/setLed?status=true
    Im Logfile erscheint: [info] application – Switching LED to true
  • http://localhost:9000/api/setLed?status=false
    Im Logfile erscheint: [info] application – Switching LED to false
  • http://localhost:9000/api/toggleLed
    Im Logfile erscheint: [info] application – Toggle LED

Web-App vorbereiten

Als nächstes soll eine kleine WebApp gebaut werden, um die drei API-Funktionen (an, aus, toggle) über einen Browser zu steuern. Da wir ja mit Kanonen auf Spatzen schießen wollen, kommen dabei JQuery und Bootstrap zum Einsatz. Die Installation ist mit dem Play-Framework denkbar einfach. In die Datei build.sbt werden einfach die folgenden Zeile hinzugefügt:

libraryDependencies += "org.webjars" % "bootstrap" % "3.0.0"

Anschließend activator neu starten und die App mit „run“ starten. Im Logfile erscheint der Hinweis, dass jQuery und Bootstrap heruntergeladen werden. Beides kann nun ab sofort genutzt werden. Dazu wird das main-Template angepasst (app/views/main.scala.html)

 @(title: String)(content: Html)

<!DOCTYPE html>

<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>@title</title>
        <link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/main.css")">
        <link rel="stylesheet" media="screen" href="@routes.Assets.at("lib/bootstrap/css/bootstrap.css")">
        <link rel="shortcut icon" type="image/png" href="@routes.Assets.at("images/favicon.png")">
        <script src="@routes.Assets.at("lib/jquery/jquery.js")" type="text/javascript"></script>
        <script src="@routes.Assets.at("lib/bootstrap/js/bootstrap.min.js")" type="text/javascript"></script>
        <script src="@routes.Assets.at("javascripts/hello.js")" type="text/javascript"></script>
    </head>
    <body>
    <nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
      <div class="container">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="#">pi-play-led-control</a>
        </div>
        <div id="navbar" class="collapse navbar-collapse">
          <ul class="nav navbar-nav">
            <li class="active"><a href="#">Home</a></li>
          </ul>
        </div>
      </div>
    </nav>

    <div class="container">
        @content
          </div>
    </body>
</html>

Nun noch ein wenig CSS in der Datei public/stylesheets/main.css ergänzen:

body {
  padding-top: 50px;
}
.starter-template {
  padding: 40px 15px;
  text-align: center;
}

.btn {
  width:150px;
}

Jetzt noch den Content der Startseite (app/views/index.scala.html)

 @(title: String)

@main(title) {
  <div class="starter-template">
  <h1>LED-Control</h1>
  <button type="button" class="btn btn-success" onclick="$.get('/api/setLed?status=true', function(){})" name = "ledOn" value = "LED on">LED on</button> <br/><br/>
  <button type="button" class="btn btn-danger" onclick="$.get('/api/setLed?status=false', function(){})" name = "ledOff" value = "LED off">LED off</button> <br/><br/>
  <button type="button" class="btn btn-primary" onclick="$.get('/api/toggleLed', function(){})" name = "toggleLED" value = "LED toggle">LED toggle</button> <br/><br/>
  </div>
}

Es gibt also 3 Buttons, die bei onclick über jQuery unsere API aufrufen. Auch das kann jetzt getestet werden. Einfach im Browser „http://localhost:9000“ aufrufen und die Web-App mit den 3 Knöpfen sollte erscheinen. Beim Klicken auf die Buttons sollten auch die entsprechenden Einträge im Logfile zu sehen sein.

pi-play-gpio-1

Vorbereiten der GPIO-Models

Das GPIO-Model wird folgende Aufgaben haben:

  • Speichern des aktuellen LED-Zustands
  • Entgegennehmen der Befehle zum Steuern der LED von der Api-Klasse
  • Steuern der LED, falls GPIO verfügbar ist
  • Wenn kein GPIO verfügbar ist, sollen die Steuerbefehle im Logfile ausgegeben werden. Damit lässt sich die App auch auf anderen Rechnern, als dem Raspberry-PI starten und testen, was vor allem beim Entwickeln sehr hilfreich ist.

Es wird also eine neue Klasse im package app.models erzeugt (Gpio.java)

package controllers;

import models.Gpio;
import play.mvc.Controller;
import play.mvc.Result;

public class Api extends Controller {

	public static Result setLed(boolean on) {
		Gpio.setLed(on);
		return ok();
	}

	public static Result toggleLed() {
		Gpio.toggleLed();
		return ok();
	}
}

Außerdem muss die Api.java nun geändert werden, damit das Gpio-Model auch genutzt wird:

package controllers;

import models.Gpio;

public class Api extends Controller {

	public static Result setLed(boolean on) {
		Gpio.setLed(on);
		return ok();
	}

	public static Result toggleLed() {
		Gpio.toggleLed();
		return ok();
	}
}

Das ganze kann nun wieder im Browser getestet werden. Viel verändert hat sich nicht. Der einzige Unterschied ist, dass im Logfile auch beim ToggleLed der richtige Status gezeigt wird.

Pi4J einbinden

Damit später auf dem Raspberry Pi das eigentliche Ziel (steuern einer LED) erreicht wird, wird nun die Pi4J-Bibliothek eingebunden. Das geschieht einfach wieder durch eine Zeile in der build.sbt:

libraryDependencies += "com.pi4j" % "pi4j-core" % "0.0.5"

Anschließend den activator neu starten und die App mit „run“ starten. Im Logfile sollte zu sehen sein, wie pi4j heruntergeladen wird.

Pi4J wird jetzt in dem Model Gpio.java genutzt:

package controllers;

import play.Logger;

import com.pi4j.io.gpio.GpioController;
import com.pi4j.io.gpio.GpioFactory;
import com.pi4j.io.gpio.GpioPinDigitalOutput;
import com.pi4j.io.gpio.PinState;
import com.pi4j.io.gpio.RaspiPin;

public class Gpio {
    static GpioController gpio = null;
    static GpioPinDigitalOutput myLed = null;
    static boolean led = false;

    static {
        try {
            gpio = GpioFactory.getInstance();
            myLed = gpio.provisionDigitalOutputPin(RaspiPin.GPIO_15,"My LED",PinState.LOW);
        } catch (Error e) {
            Logger.info("No GPIO available");
        }
    }

    public static void setLed(boolean on) {
        led = on;
        if (gpio != null) {
            PinState pinState = on?PinState.HIGH:PinState.LOW;
            myLed.setState(pinState);
        } else {
            Logger.info("No gpio available - LED now should be " + on);
        }
    }

    public static void toggleLed() {
        led =!led;
        setLed(led);
    }
}

Damit ist die Software fertig. Aber wie bekommen wir sie jetzt auf den Raspberry Pi? Auch das ist mit dem Play-Framework ganz einfach. Dazu wird die ganze App von dem Tool „activator“ zunächst zu einer Distribution zusammengepackt:

$ ./activator dist
[...]
Your package is ready in ~/workspace/pi-play-gpio/target/universal/pi-play-gpio-1.0-SNAPSHOT.zip

Diese Zip-Datei wird jetzt z.B. mit scp auf den Raspberry PI kopiert:

scp ~/workspace/pi-play-gpio/target/universal/pi-play-gpio-1.0-SNAPSHOT.zip pi@192.168.xxx.xxx:

Nun loggt man sich per SSH auf dem Raspberry Pi ein und entpackt die Software:

ssh pi@192.169.xxx.xxx
pi@raspberry ~ $ unzip pi-play-gpio-1.0-SNAPSHOT.zip

Die App kann nun gestartet werden. Dabei sind zwei Dinge zu beachten:

  • Da die GPIO-Schnittstelle benutzt wird, muss die Software als root ausgeführt werden
  • Standardmäßig verwenden Play-Apps 1GB Arbeitsspeicher. Viel zu viel für den Raspberry Pi. Deshalb muss das Programm mit angepassten Speicher-Eintsellungen gestartet werden. Das geschieht über die Option -mem 64, die dem Prozess 64MB Arbeitsspeicher zuweist.
sudo ./pi-play-gpio-1.0-SNAPSHOT/bin/pi-play-gpio -mem 64

Der Hardware-Teil

Die App läuft jetzt auf dem Raspberry Pi und kann über den Browser über die IP-Adresse des Raspberry Pi aufgerufen werden: „http://192.168.0.xxx:9000“. Beim Klick auf die Buttons passiert natürlich noch nichts, schließlich ist ja noch keine LED angeschlossen, aber immerhin bleibt auch das Logfile leer, was darauf hindeutet, dass die GPIO-Schnittstelle korrekt angesprochen werden kann. Also fix nochmal den Raspberry runterfahren („sudo shutdown -h now“) und die LED anschließen. Dazu werden die LED und ein passender Vorwiderstand auf ein Breadboard gesteckt und mit Hilfe von Jumperkabeln mit dem Raspberry PI verbunden. Wie in der Klasse Gpio.java festgelegt, wird in diesem Beispiel der GPIO-Port 15 für die LED verwendet. Welcher Pin auf dem Board das ist, muss der jeweiligen Dokumentation der unterschiedlichen Raspberry Pi – Modelle entnommen werden, Hier nochmal der Anschluss in Stichworden:

  • Breadboard mit LED und Vorwiderstand
  • Jumperkabel vom GPIO-Pin 15 zur Anode (plus) der LED
  • Vorwiderstand an die Kathode (minus) der LED
  • Jumperkabel vom Vorwidersand zu einem der Masse-Pins auf dem Raspberry

Jetzt den Raspberry wieder einschalten, per SSH einloggen und das Programm starten. (sudo ./pi-play-gpio-1.0-SNAPSHOT/bin/pi-play-gpio -mem 64).

Ab sofort lässt sich die LED über die Weboberfläche steuern.
pi-play-gpio-1

Den ganzen Code des Projektes gibt es auch auf Github:

https://github.com/lnitram/pi-play-gpio

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.