Teil 4: Programmierung von Servomotor und Display

Raspberry Pi 2 – Dachfenster mit Java steuern
Kommentare

In den ersten Teilen dieser Artikelserie haben wir uns intensiv mit den Grundlagen der GPIO beschäftigt. Jetzt beginnen wir direkt mit einem ersten Raspberry Pi Projekt: dem Bau einer Dachfenstersteuerung. In Teil 4 geht es um die Programmierung von Servomotor und Display.

Nachdem wir im ersten Teil die Details der GPIO beleuchtet und Java auf dem Raspi installiert haben, ging es in Teil 2 an das Programmieren mit Java. In Teil 3 haben wir ein echtes Projekt begonnen – den Bau einer Dachfenstersteuerung – und Taster sowie Temperatursensor programmiert. Im vierten Teil erläutern wir nun die Programmierung von Servomotor und Display.

Servomotor

Um das Dachfenster unseres Modellhäuschens zu öffnen, verwenden wir einen handelsüblichen Modellbauservo. Der Motor wird über das Tool Servoblaster angesteuert. Wie Sie im Schaltplan 1 (Abb. 1) gut sehen können, ist der Motor komplett galvanisch vom Raspberry Pi getrennt und wird, wie oben beschrieben, über eine separate Spannungsquelle betrieben.

Abb. 1: Der Schaltplan zeigt, wie alles zusammengebaut werden muss

Abb. 1: Der Schaltplan zeigt, wie alles zusammengebaut werden muss

Das Testen der einzelnen Komponenten ist ein wichtiger Bestandteil eines solchen Projekts. Leider lassen sich Servos nicht mit einem einfachen Logiksignal testen, da sie ein spezielles PWM-Signal benötigen. Der einfachste Weg, den Motor zu testen, ist daher das Tool Servoblaster. Mit ihm kann man bis zu 21 Servomotoren über die GPIO des Raspberrys bequem über die Kommandozeile steuern. Die Installation ist recht einfach. Um den Servoblaster für den Raspberry 1 herunterzuladen und zu installieren, sind folgende Schritte auszuführen:

git clone git://github.com/richardghirst/PiBits.git 
cd PiBits/ServoBlaster/user 
make 

Für den Raspberry Typ 2 ist eine gepatchte Version des Servoblasters nötig; sie kann Online heruntergeladen werden.

tar -xvzf ServoBlaster-20150219.tgz 
cd ServoBlaster
make

Als ob das nicht schon genug wäre, gibt es bei einigen Kernel-Firmware-Kombinationen immer noch einen Fehler (mknod /dev/servoblaster-mb c 100 0), sodass man den Patch noch einmal patchen muss, damit es geht. Falls Sie von diesem Problem betroffen sind, müssen Sie im servod folgende Zeile ändern:

if (mknod(filename, S_IFCHR|0600, makedev(100, 0)) < 0) {

Die 100 muss durch eine 249 ersetzt werden. Danach wird noch einmal ein make ausgeführt, um die Änderungen zu kompilieren.

Um den Userspace-Treiber für einen Servomotor zu laden, müssen wir die folgende Zeile verwenden:

sudo ./servod --p1pins=11 --pcm

Die Option –pcm verhindert, dass der Servoblaster den internen PWM-Generator des Raspberry Pi verwendet, der normalerweise für die Soundausgabe verwendet wird. Eine weitere wichtige Option, die man kennen sollte, ist — invert, mit der das Ausgangssignal invertiert wird. Das ergibt Sinn, falls man einen einstufigen Transistorverstärker zur Pegelanpassung des Servosignals verwenden möchte.

Sollten wir in die Verlegenheit kommen, den Servoblaster beenden zu müssen, killen wir einfach alle servod-Prozesse mit sudo killall servod.

Der servod erstellt uns einen Gerätetreiber, der recht einfach zu bedienen ist. Mit der folgenden Anweisung wird dem Servo 0 der Wert 140 zugewiesen, was normalerweise in etwa der Mittelposition des Motors entspricht:

echo 0=140 > /dev/servoblaster 

Jetzt können wir einfach testen, ob unser Aufbau soweit funktioniert. Unterschiedliche Servomotor-Modelle verhalten sich auch leicht unterschiedlich. Man muss immer etwas herumprobieren, um die Werte für die gewünschten Positionen zu ermitteln.

Display

Als Display verwenden wir ein normales, wenn auch etwas altmodisches Nokia-5110-Display. Es hat eine Auflösung von 84 x 48 Pixel. Um das Nokia-Display anzusteuern, gibt es verschiedene C-Bibliotheken im Internet; leider hat sich keine einzige Java-Bibliothek finden lassen. Das soll uns hier aber nicht aufhalten. Wir verwenden einfach die vorhandene freie C-Bibliothek aus dem Internet und übersetzen sie in Java. Die teilweise übersetzte Bibliothek liegt übrigens ebenfalls auf der Heft-DVD und auf der Website zum Magazin. Die Bibliothek ist nur so weit übersetzt, wie wir es für unser kleines Projekt benötigen; das C-Original bietet einiges mehr an Funktionen. Der nicht übersetzte Sourcecode ist auskommentiert, aber in der Bibliothek noch vorhanden. Falls jemand einer dieser Funktionen verwenden möchte, kann man sie einfach noch in Java übersetzen.

Damit die übersetzte Bibliothek funktioniert, muss erst einmal der passende Treiber aktiviert werden. Das erledigen wir wie schon beim I2C über die Raspi-Config (sudo raspi-config). Dort gehen wir wieder in die „8 Advanced Options“, wo wir dann „A6 SPI“ auswählen und dort alles aktivieren. Danach müssen wir beim Beenden wie gewohnt rebooten. Auch beim SPI-Treiber werden wir noch einmal selbst Hand anlegen, damit es funktioniert. In der Datei /etc/modules (sudo nano /etc/modules) benötigen wir noch zwei zusätzliche Einträge:

spi_bcm2835 
spidev

Auch ist es ist nötig, in der /boot/config.txt (sudo nano /boot/config.txt) noch zwei zusätzliche Einträge zu hinterlassen:

dtparam=spi=on 
dtoverlay=spi-bcm2835 

Damit diese Änderungen wirksam werden, müssen wir noch einen Reboot durchführen.

Die Hardware zur Ansteuerung des Displays ist auf der einen Seite leicht aufzubauen, weil das Display mit den 3,3 Volt der GPIO der Raspberrys ohne Pegelwandlung zurechtkommt. Auf der anderen Seite gibt es verschiedene Versionen des Nokia-Displays, die – wie sollte es auch anders sein – unterschiedliche Pinbelegungen haben. Daher ist es wichtig, sich das Datenblatt des Displays genau anzusehen und sich beim Aufbau an den Namen der Pins zu orientieren und nicht an den Nummern. Im Schaltplan ist zu sehen, wie das Display angeschlossen werden muss. Um die Funktion des Displays zu testen, müssen wir ein erstes kleines Programm schreiben. Es kann einfach, wie im ersten Teil beschrieben gebaut und gestartet werden. Bitte achten Sie darauf, dass sich die Java-Datei PCD8544.java im gleichen Verzeichnis befindet (Listing 2). Die PCD8544.java ist die angepasste Bibliothek, die auf der Heft-DVD und auf der Website zu dieser Ausgabe zu finden ist.

import com.pi4j.io.gpio.Pin;
import com.pi4j.io.gpio.RaspiPin;


public class DisplayTest {
  // Pinbelegung des Displays
  final static Pin dc = RaspiPin.GPIO_04;
  final static Pin rst = RaspiPin.GPIO_05;
  static byte contrast = (byte)0xff;
  public static void main(String[] args) throws Exception {
    // init and clear lcd
    PCD8544 pcd = new PCD8544();
    pcd.LCDInit(dc,rst,contrast);
    pcd.LCDclear();
    pcd.LCDdrawstring(0, 0, "Hallo Pi");
    pcd.LCDdisplay();
  }

}// class

Endlich Java

Nachdem wir nun alle Einzelkomponenten getestet haben, können wir jetzt damit starten, alles zusammen zum Laufen zu bringen. Nachdem unser Dachfenstersteuerprogramm alle nötigen Initialisierungsschritte erledigt hat, fragt das Programm jede Sekunde den Temperatursensor ab und vergleicht den Messwert mit dem Vorgabewert. Ist die Temperatur über dem eingestellten Wert, wird das Fenster geöffnet. Sinkt sie unter den eingestellten Wert, wird es wieder geschlossen.

Im realen Einsatz sollte man darüber nachdenken, den Sensor seltener abzufragen, weil er sich dadurch erwärmt und die Messwerte verfälscht. In einem realen Projekt reicht es sicherlich, den Sensor einmal in der Minute abzufragen. Nach jeder Messung wird das Display aktualisiert.

Der Vorgabewert kann über die zwei Taster verändert werden. Dies ist mit zwei Event Listenern, die an die IO-Ports gebunden sind, realisiert. Sobald ein Taster gedrückt wurde, wird das Display aktualisiert. Das war auch schon das Programm für unser Dachfensterprojekt (Listing 3).

import com.pi4j.io.gpio.*;
import com.pi4j.io.gpio.event.*;
import com.pi4j.io.i2c.*;
public class OpenWindows {
  final static Pin dc = RaspiPin.GPIO_04;
  final static Pin rst = RaspiPin.GPIO_05;
  private static final int i2cBus = 1;
  private static final int address = 0x5c;
  static double value=22.0;
  static byte contrast = (byte)0xff;
  static boolean running=false;
  static double hum=0;
  static double temp=0;
  static PCD8544 pcd=null;
  public static void main(String[] args) throws Exception {
    final GpioController gpio = GpioFactory.getInstance();
    final GpioPinDigitalInput upButton = gpio.provisionDigitalInputPin(RaspiPin.GPIO_01, PinPullResistance.PULL_DOWN);
    final GpioPinDigitalInput downButton = gpio.provisionDigitalInputPin(RaspiPin.GPIO_02, PinPullResistance.PULL_DOWN);
    upButton.addListener(new GpioPinListenerDigital() {
      @Override
      public void handleGpioPinDigitalStateChangeEvent(GpioPinDigitalStateChangeEvent event) {
        if ((event.getState() == PinState.HIGH) && !running) {
          running = true;
          OpenWindows.value = OpenWindows.value + 1;
          System.out.println("Up Button : " + OpenWindows.value);
          OpenWindows.show();
          running = false;
        }
      }
    });
    downButton.addListener(new GpioPinListenerDigital() {
      @Override
      public void handleGpioPinDigitalStateChangeEvent(GpioPinDigitalStateChangeEvent event) {
        if ((event.getState() == PinState.HIGH) && !running) {
          running = true;
          OpenWindows.value = OpenWindows.value - 1;
          System.out.println("Down Button : " + OpenWindows.value);
          OpenWindows.show();
          running = false;
        }
      }
    });
    pcd = new PCD8544();
    pcd.LCDInit(dc, rst, (byte)contrast);
    byte[] wb = new byte[]{(byte) 0x03, (byte) 0x00, (byte) 0x04};
    byte[] rb = new byte[8];
    I2CBus bus = I2CFactory.getInstance(I2CBus.BUS_1);
    I2CDevice dev = bus.getDevice(address);
    while (true) {
      try {
        // wecken
        try {
          dev.read();
        } catch (Exception e) {
          Thread.sleep(100);
        };
        // messen
        dev.read(wb, 0, 3, rb, 0, 8);
        int crc = ((rb[6] & 0xff) << 8) | rb[7] & 0xff;
        hum = (((rb[2] & 0xff) << 8) | rb[3] & 0xff);
        temp = (((rb[4] & 0xff) << 8) | rb[5] & 0xff); hum = hum / 10; temp = temp / 10; } catch (Exception e) {//System.out.println(e); } show(); if (temp>value){
        System.out.println("open");
        String[] cmd = {"/bin/bash","-c","echo 0=180 > /dev/servoblaster"  };
        Process p=Runtime.getRuntime().exec(cmd);
      }
      else {
        System.out.println("close");
        String[] cmd = {"/bin/bash","-c","echo 0=90 > /dev/servoblaster"  };
        Process p=Runtime.getRuntime().exec(cmd);
      }
      Thread.sleep(1000); 
    }//while
  }//main
  private static void show(){
    pcd.LCDclear();
    pcd.LCDdrawstring(0, 0, "Temp :" + temp);
    pcd.LCDdrawstring(0, 9, "Hum  :" + hum);
    pcd.LCDdrawstring(0, 17, "Value:" + value);
    System.out.println("Temp :" + temp);
    System.out.println("Hum  :" + hum);
    System.out.println("Value:" + value);
    pcd.LCDdisplay();
  }
}//class

Fazit

Dieses Projekt zeigt, was man alles aus unserem kleinen Freund – dem Raspberry Pi – herausholen kann. Das Programm beweist, dass man mit der Bibliothek Pi4J sehr elegant auf Hardware zugreifen kann. Wir haben uns alle wichtigen Teile der GPIO einmal angesehen und haben damit den Grundstein für eigene Experimente gelegt. An dieser Stelle sollte auch erwähnt werden, dass bei Raspberry-Projekten normalerweise nicht so viele manuelle Eingriffe und Patches nötig sind. Das Problem liegt darin begründet, dass viele Komponenten noch nicht richtig mit der leicht geänderten Hardware des Raspberry Pi 2 umgehen können; sowohl die Firmware als auch die Kernelversion spielen hier eine große Rolle. Der Servoblaster zum Beispiel lässt sich unter dem Pi 1 einfach installieren und läuft super. Um die SPI- und I2C-Treiber zu laden, ist normalerweise auch nicht so viel Aufwand nötig.

Ach, und falls sie sich gewundert haben, warum aus einem Java-Programm heraus Shell-Kommandos abgesetzt werden: Das liegt schlicht und einfach daran, dass die SoftPWM-Funktionen der Bibliothek Pi4J beim Raspberry Pi 2 nicht funktionieren. Obwohl die Hardwareunterstützung für den Pi Typ 2 etwas hakelig ist, wünsche ich ihnen viel Spaß bei eigenen Experimenten!

Entwickler Magazin

Entwickler Magazin abonnierenDieser Artikel ist im Entwickler Magazin erschienen.

Natürlich können Sie das Entwickler Magazin über den entwickler.kiosk auch digital im Browser oder auf Ihren Android- und iOS-Devices lesen. In unserem Shop ist das Entwickler Magazin ferner im Abonnement oder als Einzelheft erhältlich.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -