Linux NetMag #5
Titel: LCD-Display
URL: http://www.linuxnetmag.de/de/issue5/m5lcd1.html

LCD Wer einen Linux-PC ohne Monitor als Server laufen hat, aber trotzdem immer über den momentanen Zustand des Rechners informiert sein möchte, für den eignet sich der Einsatz eines LC-Displays.


LCD & Linux

Es wurde schon viel über den Anschluß eines LC-Displays an Linux-Rechner geschrieben. Wir wollen dieses Thema nicht zum endlosesten Mal aufgreifen.
Stattdessen präsentieren wir hier eine leicht andere Lösung, indem wir zeigen, wie man mittels geeignetem Zusammenspiel von LCD und jslaunch nicht nur eine Statusanzeige basteln kann, sondern mit dieser auch interaktiv den PC steuern kann.

Wir werden zeigen, wie man mit Hilfe des Programms lcd von Nils Färber ein LCD am Parallelport ansteuern kann und gleichzeitig über vier Tasten, die vom Programm jslaunch abgefragt werden, sich ein eigenes kleines Menü schaffen kann, mit dem man verschiedene Befehle ausführen kann; und das alles ohne einen nennenswerten Programmieraufwand.

Das Display

Das Programm lcd ist geeignet, um Displays mit dem Chip vom Typ Hitachi HD44780 anzusteuern.
Das Display ist z.B. bei Conrad-Elektronik erhältlich und kostet in der Ausführung 20 Zeilen und 4 Spalten ca 70,- DM ohne (Best.-Nr = 187283) und ca. 80,- DM mit Hintergrundbeleuchtung (Best.-Nr = 187291). Man sollte allerdings bei dunklen Räumen unbedingt ein Display mit Hintergrundbeleuchtung nehmen, da das Display ansonsten nur schlecht lesbar ist.

Display mit Hintergrundbeleuchtung

Das Display wird an den Parallelport angeschlossen und benötigt dafür folgende Verkabelung:

25 pin
ParPort-Kabel
LCD
Panel
LCD-Pin
1
EN
6
2
DB1
7
3
DB2
8
4
DB3
9
5
DB4
10
6
DB5
11
7
DB6
12
8
DB7
13
9
DB8
14
14
RS
4
18-25
GND
Erdung

Parport-Nummerierung

Zusätzlich müssen noch Pin 2 des LCD-Panels an +5V und die Pins 1 und 5 des Displays und die Pins 18 bis 25 des Parallelports an die Erdung geschlossen werden.
Eine gute 5-Volt-Quelle im PC ist das 2-adrige Floppy-Stromkabel (rotes Kabel für +5V, schwarz für Erdung -- vorher aber nochmal testen!).

Den Kontrast des Displays kann man mittels eines 100-Ohm-Potentiometers ein. Zumeist reicht es aber, den Pin 3 des Displays direkt an die Erdung anzuschließen.

 +5V ---+
         /
         \ <--+
         /    |
         \    |
  GND ---+    +--- VL (Pin 3 - driver input)

Die endgültige Verkabeleung sieht dann folgendermaßen aus:

       25 pin          LCD
        SUB D           Panel   Pin
        ---------------------------
                        GND     1  --+--- \ Connect to
                        +5V     2  --|--- / Gameport
                        R/W     5  --+    
        1               EN      6    |    
        2               DB1     7    |    
        3               DB2     8    |    
        4               DB3     9    |    
        5               DB4     10   |    
        6               DB5     11   |    
        7               DB6     12   |    
        8               DB7     13   |    
        9               DB8     14   |    
        14              RS      4    |    
        18-25           GND        --+    
                                         

Die Erdung der Pins 18-25 des Parallelportkabels ist äußerst wichtig, ansonsten sieht man auf dem Display nur flimmernde Zeichen und Kästchen.

Ist das Display hingegen korrekt angeschlossen, dann sieht man zwei Reihen mit schwarzen Kästchen:

Display ohne Informationsanzeige

Die Treiber

Die Treiber zum Ansprechen des LCDs liegen momentan in der Verion 0.152 vor. Allerdings konnte diese Version von uns nicht zum Kompilieren gebracht werden, jedoch funktionierte die Vorgängerversion 0.151, die man auch auf dem FTP-Server finden kann.
ftp://ftp.unix-ag.uni-siegen.de/pub/os/linux/drivers/
(Bei der auf dem Server befindlichen Version 0.6.0 handelt es sich um ein gleichnamiges, aber anderes Programm.)
Probieren Sie zuerst, ob die Treiber kompilieren, bevor Sie sich das LC-Display kaufen. Erst nachdem Sie sicher sind, daß die Treiber auch kompiliert werden können, lohnt sich der Kauf des Displays!
Erfolg mit dem Kompilieren des Treibers hatten wir nur auf einem System mit 2.2.13-Kernel und einem älteren mit 2.0.36.
Beim aktuellen 2.2.16-Kernel brach LCD-0.151 mit Fehlermeldungen ab, aber vielleicht haben Sie ja mehr Erfolg.
Vor dem Kompilieren wählt man in der Datei driver/Makefile den zugehörigen Kernel (2.0.x oder 2.2.x) aus. Außerdem muß man in der Datei driver/hardware.h noch die Parallelportadresse einstellen:

driver/hardware.h
 [...]
    #define LCD_ADDRESS 0x0378     /* entspricht lpt1: auf den meisten PCs */
 /* #define LCD_ADDRESS 0x0278  */ /* entspricht lpt2: auf den meisten PCs */
 /* #define LCD_ADDRESS 0x3bc   */ /* auch moegliche paralleport-Adresse   */
 [...]

Entfernen Sie dafür die Kommentarzeichen /* und */.
Falls Sie nicht wissen, welche Adresse der Paralleport hat, schließen Sie den Drucker an, laden Sie das Druckermodul

>> inmod lp
und schauen Sie sich mittels
>> cat /proc/ioports 
die Adresse an.
Falls Sie ein Display mit einer anderen Größe als 20x4 besitzen, können Sie unter driver/hardware.h die Anzahl der Spalten und Zeilen einstellen:

driver/hardware.h
 [...]
 #define LCD_COLS 20
 #define LCD_LINES 4
 [...]

Zum Kompilieren der Treiber benötigt lcd die Datei /usr/src/linux/include/linux/modversions.h und /usr/src/linux-2.2.13/include/linux/modsetver.h, die allerdings beide beim 2.0.x-Kernel fehlen.
Es reicht allerdings diese beiden Dateien von einem 2.2.x-Kernel einfach in das entsprechende Verzeichnis zu kopieren(modsetver.h und modversions.h).

Ist alles für das Kompilieren vorbereitet, genügt ein

>> make
im Hauptverzeichnis von lcd.
Danach befindet sich im lcd-Unterverzeichnis driver/ das kompilierte Modul lcd.o, welches zum Ansteuern des LCDs benötigt wird.
Als letztes muß noch die Pseudo-Datei /dev/lcd angelegt werden, über die man dann nachher das LCD ansprechen kann. Dafür führt man das Skript mkdevice im Unterverzeichnis driver aus (als root).
Als nächstes muß dann die Hardware korrekt angeschlossen werden.

jslaunch

Wir haben bereits in einer früheren Ausgabe über das Jslaunch berichtet (Jslaunch -- PC per Joystick steuern).
In Verbindung mit einem LC-Display sind allerdings die Einsatzmöglichkeiten des Programms noch weitaus umfangreicher.

Das Programm jslaunch überprüft in einstellbaren Zeitintervallen, ob bei einem Joystick, der am Joystickport einer Soundkarte angeschlossen ist, ein oder mehrere Knöpfe gedrückt sind, und führt einstellbare Befehle aus.
Das komfortable am Programm ist, daß es zum Einsatz kein Kernelmodul benötigt um den Joystickport auszulesen, sondern diese Information direkt auslesen kann und somit sehr einfach zu installieren ist.
Wegen der hohen Stabilität des Programms eignet es sich als eine Art Notfall-Reset eingesetzt zu werden, falls einige wichtige Komponenten des Systems abgestürzt sind (z.B. Tastatur und/oder Netzwerkkarte), um den PC dann immer noch ohne Hard-Reset neu starten zu können.
Am einfachsten ist es, sich irgendwoher einen defekten Joystick zu beschaffen, und derart zu zerlegen, daß man nur noch das Kabel und die Taster übrig läßt.
Wenn das Display ein professionelles Aussehen haben sollte, empfiehlt es sich, die Feuerknöpfe gegen richtige Taster auszutauschen (In jedem Elektronikfachhandel erhältlich).
Beim Programm jslaunch selbst fällt keinerlei große Arbeit an, außer es zu kompilieren (>> make).

erster Test

Nachdem nun alles korrekt angeschlossen wurde, wollen wir das Display auch testen.
Dafür muß zuerst das Modul des LCD-Treibers geladen werden (als root im Unterverzeichnis driver/):

>> /sbin/insmod lcd.o 
Daraufhin sollte ein nichtblinkender Cursor in der linken oberen Ecke des Displays erscheinen. Wenn alles funktioniert, kann man das Modul auch in das Verzeichnis /lib/modules/2.2.*/misc/ kopieren, so daß man sich zum Laden des Treibers nicht immer in das driver/-Verzeichnis begeben muß.

Um nun auch etwas auf dem Display darzustellen, begibt man sich ins Unterverzeichnis tools/proclcd und kompiliert das dort liegende Programm mit einem

>> make
Das Programm wird gestartet und dessen Ausgabe an das /dev/lcd-Device umgelenkt:
>> ./proclcd > /dev/lcd
Daraufhin sollte man etwa folgende Ausgabe auf dem Display sehen:

Proclcd-Test

Entladen wird der Treiber übrigens mittels

>> /sbin/rmmod lcd
Dies funktioniert jedoch nur, wenn kein laufender Prozeß auf das LCD-Device zugreift.

Als nächstes geht es an das Testen des Joysticks.
Man starte jslaunch zuerst mit dem Befehl

>> jslaunch -r 1 "echo 1" -r 2 "echo 2" -r 3 "echo 3" -r 4 "echo 4"
Drückt man nun einen Knopf, zeigt einem jslaunch an, um welche Button-Nummer es sich dabei handelt.
Man sollte sich merken oder notieren, welcher Knopf welcher Nummer zugeordent wird, da wir hierüber später das Display steuern wollen.

Wenn nun alles funktioniert wie es sollte (LCD und Joystick-Knöpfe), kann man beides in die Verblendung eines PCs (oder auch in ein externes Gehäuse) einbauen.
Ein freier Einschubschacht im Gehäuse eignet sich ideal, um das Display und vier Taster daran zu befestigen. Die Kabel vom Parallelport und Joystickport müssen dann allerdings wieder zurück ins Gehäuse geführt werden. Wenn aber der Parallelport und/oder Joystickport nicht fest auf einer Slot-Karte sitzen, sondern mittels Kabel auf einer eigenen Blende an der Rückseite liegen, sollte man diese abschrauben und die Kabel gleich im Gehäuse lassen).

Das Menü

Im diesem Abschnitt basteln wir uns ein Menü, welches später über die Joystick-Knöpfe gesteuert werden soll. Es handelt sich hierbei um kein hochkompliziertes Programm, sondern um einige kleine Bash-Skripte, so daß auch ein ungeübter Programmierer die LCD-Ausgaben ohne größere Probleme an seine eigenen Bedürfnisse anpassen kann.

Zunächst leget man ein neues Verzeichnis an, in dem dann später die Skripte für das LCD gesammelt werden:

>> mkdir /usr/local/etc/display/
In diesem Verzeichnis legt man dann die Datei own an, die zusätzlich als ausführbar gekennzeichnet sein muß:
>> touch /usr/local/etc/display/own
>> chmod a+x /usr/local/etc/display/own
Der Inhalt des Skripts sollte folgendermaßen aussehen;

/usr/local/etc/display/own
 #!/bin/sh

 # Just a dumb script copying some nice system information values
 # to our LCD device
 #
 
 export SLEEP=10
 
 #Bei 1,2,4 Menu -- Bei 3 reboot
 echo -n -e "\33c" > /dev/lcd
 echo -n  -e "\33h" > /dev/lcd
 echo -e "    " > /dev/lcd
 echo -e "  Bitte warten ..." > /dev/lcd
 echo -e "  ==============" > /dev/lcd

 killall jslaunch
 sleep 4
 /usr/local/jslaunch-2.0/jslaunch  -r 1 "killall own && /usr/local/etc/display/menu1"\ 
 -r 3 "killall own && /usr/local/etc/display/reboot"\ 
 -r 4 "killall own && /usr/local/etc/display/menu1"\ 
 -r 2 "killall own && /usr/local/etc/display/menu1"&
 #Die Knoepfe 1,2 und 4 starten das Menue. 
 #Knopf 3 faehrt den Rechner runter
 
 echo -n -e "\33c" > /dev/lcd
 
 while true; do
  echo -n -e "\33c" 
  echo -n -e "\33h"

 #Die folgenden Zeilen sammeln einige Informationen ueber
 #den PC die dann hintereinander angezeigt werden

 #-- Anzahl der Apache-Benutzer
 export WEB=$(apachectl status | grep reques | cut -d " " -f 4) .
 #-- Anzahl der FTP-Benutzer
 export FTP=$(ftpcount | grep local | cut -d " " -f 22)
 #--Groeße des freien Speichers
 export MEM=$(cat /proc/meminfo |fgrep "MemFree"|cut -b 13-24)
 #-- Wie lange ist der PC schon an (uptime)
 export UPT=$(uptime | cut -d "," -f 1)
 #-- Anzahl der eingeloggten User 
 export USER=$(uptime | cut -d "," -f 2)
 #-- Prozessorauslastung 
 export LOAD=$(cat /proc/loadavg | cut -d " " -f 1)
 #-- Die folgenden Zeilen geben Informationen
 #   ueber einige gemountete Verzeichnisse
 #   (Zip-Laufwerk, cdrom, local-partiotion...)
 export ZIP=$(df | grep "zip" | cut -d "%" -f 2,2)
 export LOCAL=$(df | grep "local" | cut -d "%" -f 2,2)
 export CDROM=$(df | grep "cdrom" | cut -d "%" -f 2,2)
 export ZIP_P=$(df | grep "zip" | cut -b 50-53)
 export LOCAL_P=$(df | grep "local" | cut -b 50-53)
 export CDROM_P=$(df | grep "cdrom" | cut -b 50-53)

 # ***** SCREEN 1 *******
 # Hier werden die oben gesammelten Informationen ausgegeben
 # Uptime, Memomry, HTTP/FTP-User und Load
   echo -e $UPT
   echo -e "MEM "$MEM
   echo -e "HTTP "$WEB" --  FTP" $FTP" "
   echo -e  $USER " load:" $LOAD
   sleep $SLEEP
   # Die Sleepzeit gibt an, wie lange die Info angezeigt wird
 
 # ***** SCREEN 2 *******
 # Auflisten der gemounteten Verzeichnisse
   echo -n -e "\33c"
  echo -n -e "\33h"
  echo -e  $ZIP_P $ZIP
  echo -e  $LOCAL_P $LOCAL
  echo -e  $CDROM_P $CDROM
  sleep $SLEEP

 # ***** SCREEN 3 *******
 # Auflisten aller Eintraege im Druckerspool
  echo -n -e "\33c"
  echo -n -e "\33h"

  export DRUCK=$(lpq | grep bytes | cut -b 8-18 | tail -n 6)
  echo -e "Druckaufträge:"
  echo -e $DRUCK
  sleep $SLEEP

 # ***** SCREEN 4 *******
 # Ein paar Infos über die Netzwerkdevices
  echo -n -e "\33c"
  echo -n -e "\33h"

 export ETH_R=$(cat /proc/net/dev | grep eth0 | cut -b 8-14)
 export ETH_S=$(cat /proc/net/dev | grep eth0 | cut -b 36-43)
 export IPP_R=$(cat /proc/net/dev | grep ippp0 | cut -b 8-14)
 export IPP_S=$(cat /proc/net/dev | grep ippp0 | cut -b 36-43)
 export IPP_E=$(cat /proc/net/dev | grep ippp0 | cut -b 44-48)
 export ETH_E=$(cat /proc/net/dev | grep eth0 | cut -b 44-48)
   echo -e "      eth0   ippp0"
   echo -e "rec " $ETH_R" "$IPP_R
   echo -e "sen " $ETH_S" "$IPP_S
   echo -e "err      " $ETH_E"    "$IPP_E
  sleep $SLEEP
 done > /dev/lcd

Die Skripte sind sehr simpel aufgebaut. Die gewünschte Information wird mittels eines kleinen Befehls ausgegeben und mit Hilfe der Programme grep und cut ausgeschnitten. Leider kann dies bei verschiedenen Distributionen zu Problemen führen, da die einzelnen Programme etwas andere Formatierungen in der Ausgabe benutzen, und deshalb die Spaltenangaben beim cut-Befehl nicht korrekt sind.
Sollte etwa eine Information auf Ihrem Display nicht erscheinen, sollten Sie die einzelnen Befehle per Hand ausführen und die Zahlenwerte per Hand testen.

Die Information wird dann immer in eine Variable geschrieben (z.B. $ETH_E für die Anzahl aller feherhaften Pakete der Netzwerkübertragung) und dann mittels echo ausgegeben.
Anstatt der hier benutzten eigenen Informationen könnte man auch ein beliebiges anderes Programm aufrufen, wie etwa procload.

Wie man in der 18. Zeile erkennt, wird beim Drücken der Knöpfe 1, 2 und 4 das Menü (own) gekillt und das jeweils dem Knopf zugeordnete Menü (hier bei allen menu1) gestartet.
Dieses Skript muß sich auch im Verzeichnis /usr/local/etc/display befinden und als ausführbar gekennzeichnet sein.
Der Inhalt der Datei könnte etwa folgendermaßen aussehen:

/usr/local/etc/display/menu1
 #!/bin/sh       
 #  zuerst muss der alte jslaunch gestoppt werden, da er noch die
 #  alte Konfiguration bestizt
 killall jslaunch

 #  Falls das vorherige Menue im Sleep-modus war, muß auch dieses
 #  gestoppt werden
 killall sleep
 
 #  Diese Sleepzeit ist leider notwendig, da der User genug Zeit haben muß,
 #  die Taste wieder loszulassen. Ansonsten weigert sich
 #  jslaunch erneut zu starten
 echo -n -e "\33c" > /dev/lcd
 echo -n -e "\33h" > /dev/lcd
 echo -e "    " > /dev/lcd
 echo -e "  Bitte warten ..." >/dev/lcd
 echo -e "  ==============" > /dev/lcd
 sleep 4


 #  Hier etwas informationen, welcher Knopf welche Option
 #  besitzt
 export VERZ=/usr/local/etc/display 
 echo -n -e "\33c" > /dev/lcd
 echo -n -e "\33h" > /dev/lcd
 echo "   --== Menu ==--" > /dev/lcd
 echo " 1 Zip-Modul laden" > /dev/lcd
 echo " 2 Zip  entladen" > /dev/lcd
 echo " 3 weiter" > /dev/lcd
 
 #  Nun starten wir den jslaunch mit den neuen Optionen
 #  1 == Zip mounten
 #  2 == Zip unmounten
 #  3 == z.B. Menue zum CD-Rom mounten -- ist aber auskommentiert
 #  4 == Reboot

 /usr/local/jslaunch-2.0/jslaunch\ 
  -r 1 "killall menu1 && /usr/local/etc/display/zip_on"\ 
  -r 3 "killall menu1 && /usr/local/etc/display/reboot"\ 
 #  -r 4 "killall menu1 && /usr/local/etc/display/cdrom"\ 
  -r 2 "killall menu1 && /usr/local/etc/display/zip_off" &

 sleep 15
 /usr/local/etc/display/own

Das Skript zum Reboot sieht folgendermaßen aus (hier könnte man z.B. noch eine Sicherungsfrage "Wirklich reboot?" eingebaut werden)

/usr/local/etc/display/reboot
 #!/bin/sh       
 killall jslaunch
 killall sleep
 
 echo -n -e "\33c" > /dev/lcd
 echo -n -e "\33h" > /dev/lcd
 echo -e "    " > /dev/lcd
 echo -e "  Reboot now ..." >/dev/lcd
 echo -e "  ==============" > /dev/lcd
 echo -e "    Bis bald ;-) "> /dev/lcd
 sleep 4
 reboot

Beim Menu1 habe ich den Knopf 3 auskommentiert, da nun einigermaßen klar sein sollte, wie man eigene Skripte zu basteln hat. Mit dem Knopf 3 könnte man beispielsweise zu einem Menü kommen, in dem man das CD-Rom-Laufwerk mounten und unmounten kann.
Aber auch jede andere Anzeige wäre denkbar, wie etwa spezielle Statusanzeigen.
Für jedes Menü muß dann ein neues Skript unter /usr/local/etc/display/ erstellt werden, das in etwa folgenden Aufbau besitzt

Grundstruktur
 #!/bin/sh       
 killall jslaunch
 killall sleep
 echo -n -e "\33c" > /dev/lcd
 echo -n -e "\33h" > /dev/lcd
 echo -e "    " > /dev/lcd
 echo -e "  Bitte warten ..." >/dev/lcd
 echo -e "  ==============" > /dev/lcd
 sleep 4

 #  Hier etwas informationen, welcher Knopf welche Option
 #  besitzt
 export VERZ=/usr/local/etc/display 
 echo -n -e "\33c" > /dev/lcd
 echo -n -e "\33h" > /dev/lcd
 echo "   --== Menu ==--" > /dev/lcd
 echo " 1 Knopf 1 macht..." > /dev/lcd
 echo " 2 Knopf 2 macht..." > /dev/lcd
 echo " 3 weiter" > /dev/lcd

 #  Jedem Skript wird ein Befehl oder Menü zugeordnet
 /usr/local/jslaunch-2.0/jslaunch\ 
  -r 1 "killall diese_menu && /usr/local/etc/display/Skript1"\ 
  -r 3 "killall diese_menu && /usr/local/etc/display/Skript2"\ 
 #  -r 4 "killall diese_menu && /usr/local/etc/display/Skript3"\ 
  -r 2 "killall diese_menu && /usr/local/etc/display/Skript4" &

 sleep 15
 /usr/local/etc/display/own

Mit dieser Methode kann man sich ohne großen Aufwand seine eigene Menüstruktur zusammenbasteln, ohne irgendwelche hochgestochenen Programmierkenntnisse besitzen zu müssen.

automatisch starten

Da das Display in Verbindung mit den Knöpfen sich ideal für einen PC ohne Monitor und Tastatur eigenet, muß das Menü auch automatisch beim Booten mitgestartet werden, da man sich schließlich nicht lokal eingloggen kann.

Dafür gibt es bekanntlich zwei Optionen. Entweder man trägt die Befehle zum Starten des Services in die boot.local, die sich unter /sbin/init.d befindet, oder man schreibt ein kleines init-Skript und legt diese unter /sbin/init.d (bei SuSE) oder unter etc/rc.d/init.d(bei RedHat) ab.

Die Befehle, die jeweils ausgeführt werden müssen, sind das Laden des LCD-Moduls und danach das Starten des Menü-Skriptes:

/sbin/insmod lcd
sleep 2
/usr/local/etc/display/own &
Wenn das LCD-Modul nicht in ein Unterverzeichnis von /lib/modules (siehe oben) kopiert wurde, muß der gesamte Pfad zum Modul angegeben werden.

So, das war's erstmal. Das LC-Display sollte nun eigentlich wie gewünscht funktionieren. Ach ja, wen es interessiert, hier sieht das ganze im eingebauten Zustand folgendermaßen aus:

Eingebautes LCD


http://www.home.unix-ag.org/nils/lcd.html Homepage des Programms lcd
http://www.linux-magazin.de/ausgabe/1999/08/LCD/lcd.html Artikel des Programmierers von lcd im Linux-Magazin
ftp://sunsite.org.uk/Mirrors/contrib.redhat.com/libc6/i386/ Leider hat jslaunch keine Homepage
http://lcdproc.omnipotent.net/ Treiber zum Ansteuern einer großen Anzahl von Displays über den serielle Port
http://www.mv.com/ipusers/cdwalker/lpt_driver.html Weitere Treiber zum Anschliessen eines Displays an den Druckerport