- Offizieller Beitrag
Java InfoBox (JInfoBox)
Hier wird eine klein Informationskiste zusammen gebaut, welche mich über aktuelle Nachrichten im Socialweb informieren soll.
Zusätzlich wird es auch möglich sein, einen kleinen Timer via Webseite zu stellen.
Die Idee von meinem Projekt findet ihr in meinem Blog.
Vorbereitung
Hardware
Als erstes werde ich schauen was ich wo anschließen muss.
Ich habe 3 LED´s, einen Buzzer, ein 4 Segement Display und 2 Taster.
Die LEDs, beide Taster sowie den Buzzer können direkt an ein GPIO Port angehangen werden.
Dazu gibt es auf dem LinkerKit Baseboard die Anschlüsse JP4 bis JP8. Wobei der JP4 Anschluss im Betriebssystem noch umkonfiguriert werden muss,
da es sich da um einen Seriellenport handelt.
Die 4 Segement Anzeige besitzt einen TM1637 Treiber, welcher über I2C betrieben wird.
Daher muss die Anzeige an den Anschluss JP3 geklemmt werden.
Software
Die Software werde ich in Java schreiben. Einfach weil ich beruflich damit zu tun habe und
immer gerne neue Bibliotheken ausprobieren will
Von meiner Projektidee ergeben sich folgende Funktionen:
- einen Timer der in X Minuten ein Event wirft
- einen Timer der um eine Uhrzeit ein Event wirft
- Steuerung des Buzzer´s
- Eingabe des Alarms über eine Webseite (für Smartphones)
- Steuerung der LED´s
- Email Postfach abfragen
- Facebook API abfragen
- Twitter API abfragen
- Taster auslesen um die LED´s zurück zusetzen
Jede einzelne Funktion wird ein eigenes Modul und wird später durch das Hauptprogramm vereinfacht
abgefragt oder gesteuert.
Die Entwicklung
Alarm
Zunächst habe ich erstmal gemeinsamkeiten von dem Alarm in Minuten und der Uhrzeit gesucht.
Jeden Alarm muss ich in einem eigenem Thread halten und ablaufen lassen.
Außerdem soll es möglich sein das sich jemand "anmelden" kann,
um ein Event zu bekommen wenn der Alarm ausgelöst wurde (siehe Observer Pattern).
Zusätzlich wird sich der Zeitpunkt gemerkt, wann der Alarm eingestellt wurde.
Das wären alle gemeinsamkeiten
Jetzt kann ich von dieser Klasse aus jeweils eine Ableitung erstellen für die jeweiligen Alarm Typen.
Alarm in X Minuten
Für diese Ableitung muss ich eine Minutenanzahl im Konstruktor übergeben.
Im Konstruktor berechne ich mir auch die Uhrzeit wann der Alarm ausgelöst werden soll und speicher diese in einem Klassenattribut weg.
Wenn nun der Thread gestartet wird, wird immer die aktuelle Uhrzeit vom System ausgelesen und mit der Zieluhrzeit verglichen. Wenn die Zieluhrzeit dabei hinter der aktuellen Uhrzeit liegt, wird der Thread interrupted und alle Klassen, welche sich für dieses Event eingetragen haben, werden benachrichtigt.
System.out.println("Alarmiere mich in 5 Minuten");
Alarm a1 = new AlarmIn(5);
a1.addListener(new AlarmListener() {
@Override
public void alarm() {
System.out.println("!!Alarm Alarm Alarm!!");
}
});
Alarm um Uhrzeit
Im gegensatz zu der Alarm in X Minuten Funktion brauche ich hier nur den Timestamp übergeben.
Anhand diesem Timestamp kann ich dann meine Zieluhrzeit setzten und auch hier ein Alarm auslösen.
System.out.println("Alarmiere mich um 12:00Uhr");
Calendar cal = Calendar.getInstance();
cal.set(Calendar.HOUR_OF_DAY, 12);
cal.set(Calendar.MINUTE, 00);
Alarm a2 = new AlarmAt(cal.getTimeInMillis());
a1.addListener(new AlarmListener() {
@Override
public void alarm() {
System.out.println("!!Alarm2 Alarm2 Alarm2!!");
}
});
Alles anzeigen
Steuerung der LEDs
Die LED´s gestaltet sich relativ einfach: In dem Datenblatt schauen an welchem GPIO Pin der jeweilige Anschluss beim baseboard geht, diesen Pin dann auf High setzen und schon leuchtet die LED.
Eine Funktion um die LED´s zum blinken zu bringen folgt später
LEDControl led = new LEDControl();
led.gelb(); // gelbe LED an
led.gelb(); // gelbe LED aus
led.gelb(true); // gelbe LED an
led.blau(); // blaue LED an
led.resetAll(); // alle LEDs aus
Steuerung des Buzzers
Ein Buzzer braucht ein wenig mehr als einfach den Pin auf High zu setzen. Um einen Ton rauszubekommen, muss der Buzzer Pin immer wieder von High auf Low und andersrum gesetzt werden. Dabei ist die zeitliche Abstand zwischen den Signalen ausschlaggebend für den Ton. Ohne Dokumentation war es sau schwer die Zeitabstände herraus zukriegen.
Ich hab echt lange rumprobiert welche Zeiten am besten passen könnten aber zufrieden bin ich mit dem Ton nicht.
Zusätzlich war ich mir nicht sicher ob Java viel zu langsam wäre, um den Buzzer richtig anzusteuern. Deshalb habe ich mir noch ein C Programm geschrieben aber leider führte das auch nicht zum Erfolg...
BuzzerControl buzzer = new BuzzerControl();
buzzer.piep(); // Buzzer macht geraeusche
Thread.sleep(5000); // wartet 5 Sekunden
buzzer.stopBeeping(); // Buzzer ist still
Die API Abfragen
Ich möchte zu unterschiedlichen Diensten eine Abfrage starten. Zum Beispiel möchte ich wissen ob ich neue EMails im Postfach habe oder ich eine Nachricht auf Facebook bekommen habe. Jede dieser Abfragen lässt sich wieder verallgemeinern. Ich möchte eine check() Methode für jede Abfrage haben und ich möchte auch dass die Abfrage mir ein Event liefert, wenn es etwas neues gibt. Zusätzlich sollen diese Abfragen in einem eigenen Thread laufen, um das Hauptprogramm nicht damit zu belasten.
Abfrage eines EMail Postfaches
Die Abfrage eines EMail Postfaches gestaltete sich einfacher als ich erst gedacht habe.
Java bietet dazu eine Allround Bibliothek - Javax. Mit Javax erstellt man zunächst eine Session, verbindet sich mit dem Server und öffnet dann ein Ordner auf dem Konto.
Die einzige Logik die implementiert werden musste ist, die Abfrage ob es neue EMails gibt. Von Haus aus gibt es sowas nicht (oder ich habs nicht gefunden ). Daher merke ich mir die Uhrzeit / Datum wann ich das Postfach zuletzt abgefragt habe. Wenn ich danach eine neue Abfrage starte und da sind Mails bei, welche aktueller sind als das gemerkte Datum, dann handelt es sich dabei um eine neue Mail.
EMailChecker email = new EMailChecker();
email.addListener(new CheckerListener() {
@Override
public void notifyNew(NotifyType type) {
System.out.println("Neue Email bekommen");
// Schalte LED an...
}
});
email.start(); // Startet die Abfrage in einem eigenem Thread
Alles anzeigen
Steuerung des 4 Segementen Display
Zunächst muss im System die I2C Modul eingeschaltet werden.
Als erstes wird das Modul von der Blacklist entfernt durch setzen einer # am Anfang:
$ cat /etc/modprobe.d/raspi-blacklist.conf
# blacklist spi and i2c by default (many users don't need them)
# blacklist spi-bcm2708
# blacklist i2c-bcm2708
Danach muss noch i2c_dev und i2c-bcm2708 in die /etc/modules eingetragen werden.
Zum Schluss den Pi neustarten oooooder für faule Socken reicht es das Modul per Hand zu laden
Damit wäre I2C einsatzbereit
Das 4 Segement Modul habe ich an dem JP3 vom Baseboard angeschlossen - der einzige Port mit I2C. Dank der freundlichen Hilfe von sd582 und seinem Beispiel Script, konnte ich zum ersten mal etwas auf dem Display erkennen. Ohne das Beispiel wäre es mir sicherlich nicht so leicht gefallen - dafür vielen Dank!
Das Script habe ich dann quasi 1:1 in Java übersetzt und in meine Alarm Klasse eingearbeitet. Letztlich sieht der Aufruf der Methode so aus:
//diff ist die Differenz zwischen Start und End Zeit des Alarms.
Date n = new Date(diff);
show(n);
und schon zeigt das Display die Uhrzeit an
Wenn die Zeit (die angezeigt werden soll) weniger als eine Minute beträgt dann werden die letzten Sekunden ausgegeben - um das Display ein wenig zu füllen.
Steuerung über eine Webseite
Zur Steuerung der Alarmzeiten über eine Webseite wollte ich das Dropwizard Framework ausprobieren. Dieses Framework besitzt viele kleine (und große) Tools für Entwickler um das Programmieren zu erleichtern. Allerdings ist das für mein Projekt ein wenig wie mit einer riesen Kanone auf Spatzen zu schießen - aber wie ich Anfangs schon schrieb: ich mag ein paar Bibliotheken ausprobieren
Dropwizard besitzt einen Jetty Webserver, welcher super simple zu benutzen ist.
Es muss eine kleine ftl Datei angelegt werden (quasi eine normale html Seite). Um nun auch auf irgendwelche Formulare zu reagieren, muss zusätzlich eine Ressource Klasse eingebunden werden. Diese Klasse wird über diverse Annotationen eingestellt und ruft letztlich eine Methode über eine URL auf.
Spoiler anzeigen
@Path("/alarmIn/")
@Produces(MediaType.TEXT_HTML)
public class AlarmResource {
@GET
public AlarmView startAlarm(@QueryParam("minutes") String text) {
if (text != null) {
try {
Alarm a = new AlarmIn(Integer.parseInt(text));
a.addListener(JInfoBox.getInstance());
a.start();
} catch (Exception e) {
System.out.println(e);
}
} else {
text = "unbekannt";
}
return new AlarmView(text);
}
}
Alles anzeigen
Die Methode startAlarm würde dann mit folgender URL aufgerufen:
IP-PI:8080/alarmIn/?minutes=1
Derzeit bin ich echt begeistert von diesem Framework. Es ist super einfach ein REST Service zu programmieren oder wie ich jetzt rausgefunden habe, eine Webseite mit Java Funktionen. Und alles wohlgemerkt in einer Jar verpackt
Hauptprogramm
Dank der einfachen Module, ist das Hauptprogramm super simpel
Das Hauptprogramm implementiert alle Listener die ich im Vorfeld definiert habe (AlarmListener, TasterListener, CheckerListener). Dort werden dann die unterschiedlichen Aktionen abgearbeitet.
Spoiler anzeigen
/***
* AlarmListener
*/
@Override
public void alarm() {
log.debug("ALARM!");
logMessages.add("ALARM!!!!");
buzzer.piep();
}
/***
* TasterListener
*/
@Override
public void taster1(boolean state) {
System.out.println("TASTE 1");
if (state) {
this.buzzer.stopBeeping();
}
}
/***
* TasterListener
*/
@Override
public void taster2(boolean state) {
System.out.println("TASTE 2");
if (state) {
this.led.resetAll();
}
}
/***
* CheckerListener
*/
@Override
public void notifyNew(NotifyType type) {
switch (type) {
case email:
this.led.gelb(true);
break;
default:
break;
}
}
Alles anzeigen
Um nun auch die Benachrichtigungen zu erhalten, muss sich das Hauptprogramm bei den jeweiligen Klassen "anmelden".
/***
* Konstruktor
*/
private JInfoBox() {
// Singleton
logMessages = new ArrayList<String>();
this.buzzer = new BuzzerControl();
this.taster = new TasterControl();
this.taster.addListener(this);
this.led = new LEDControl();
this.emailChecker = new EMailChecker();
this.emailChecker.addListener(this);
this.emailChecker.start();
}
Alles anzeigen
Mehr muss das Hauptprogramm nicht machen
Durch Dropwizard wird das Programm nicht beendet - deswegen kann auf eine Endlos-Schleife verzichtet werden.
Schluss
Das Projekt hat mir super viel spaß gemacht - vor allem am Ende das Hauptprogramm zusammen zubauen. Da merkt man erst ob man einen Designfehler begangen hat und gar nichts klappt Leider hat an verschiedenen Stellen an Dokumentation der Linksprite Bauteile gehapert... Wie schon geschrieben ohne das Beispiel Script für das 4 Segement Display wäre ein großer Teil des Projekts nicht realisierbar gewesen.
Letztenendes haben die Linkspire Bauteile irgendwie das erreicht, was der Hersteller wollte - einfache Module, welche sich super simpel zusammenstecken lassen und mit denen man sehr schöne Projekte aufbauen kann (als Neuling).
Java / Maven Projekt als Download: hier