Raspberry Pi: OLED-Display 128×64 mit Perl ansteuern (I2C)

Ich hatte in diesem Beitrag schonmal darüber geschrieben, wie man ein kleines 0,96'' OLED-Display mit 128x64 Pixeln über die I2C-Schnittstelle ansteuern kann. Dort kam als Programmiersprache Python zum Einsatz. Dieser Beitrag ist im Prinzip genau der gleiche, nur dass wir das hier mit Perl machen.

Raspberry Pi mit angeschlossenem 0,96″-OLED-Display

Raspberry Pi vorbereiten

Voraussetzung für alles folgende ist, dass Euer Raspberry Pi betriebsbereit ist. Wie man das Image auf eine SD-Karte bekommt und den RPi einrichtet, hatte ich hier schonmal beschrieben.

Von Haus aus ist die I2C-Schnittstelle am Raspberry Pi deaktiviert. Um diese nun einzuschalten verbinden wir uns per SSH und starten die Konfigurationsoberfläche vom Raspberry Pi:

$ sudo raspi-config

Dort gehen wir zum Punkt 5 (Interfacing Options) -> P5 (I2C) -> ENTER und bestätigen die Abfrage, ob wir I2C aktivieren wollen, mit <Ja>. Anschließend verlassen wir raspi-config und starten den RPi neu:

$ sudo reboot

OLED-Display anschließen

Mit ein paar Steckbrücken verbinden wir den Raspberry Pi mit dem OLED-Display. Bei meinem Display ist es egal, ob man 5V oder 3,3V nutzt. Prüft das besser bei Eurem nochmal.

Raspberry Pi: OLED-Display über I2C anschließen

Raspberry Pi: OLED-Display über I2C anschließen

Adresse ermitteln

Da I2C ein Bus-System für mehrere Teilnehme ist, haben alle auch eine Busadresse. Bei diesen kleinen Displays hat sich so etwas wie ein Standard durchgesetzt. Bei fast allen lautet die Adresse 0x3C. Das lässt sich einfach überprüfen, indem wir folgenden Befehl absetzen:

$ i2cdetect -y 1

Als Ausgabe erhalten wir dann so eine Tabelle, der wir als Adresse des einzigen Busteilnehmers 3c entnehmen können:

pi@raspberrypi:~ $ i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- 3c -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- --

Perl-Code

Bibliothek einbinden

Für die Ansteuerung des Displays nutzen wir eine fertige Bibliothek, die wir einfach nur herunterladen und installieren müssen:

$ wget http://hipi.znix.com/download/libhipi-perl_0.72-1_armhf.deb
$ sudo dpkg --install libhipi-perl_0.72-1_armhf.deb
$ sudo apt-get -y -f install

Nach der Installation legen wir uns eine neue Script-Datei an:

$ touch display.pl
$ chmod 777 display.pl

Anschließend öffnen wir die soeben erstellte Datei in einem beliebigen Editor und fangen mit der Programmierung an:

$ nano display.pl

Beispiel 1: Hallo Welt

Raspberry Pi: OLED-Display-Beispiel "Hallo Welt"

Raspberry Pi: OLED-Display-Beispiel „Hallo Welt“

#! /usr/bin/perl

# Bibliotheken importieren
use HiPi qw( :oled :rpi);
use HiPi::Interface::MonoOLED;

# Display einrichten
my $oled = HiPi::Interface::MonoOLED -> new(
    type      => SSD1306_128_X_64_I2C,
    address   => 0x3C,
);

# Display zum Start löschen
$oled -> clear_buffer;
$oled -> display_reset();

# Hallo Welt
$oled -> draw_text(20, 16, "Hallo");
$oled -> draw_text(60, 40, "Welt!");
 
# Ausgaben auf Display schreiben
$oled -> display_update();

Der Klassiker in der Programmierung schreibt einfach ein paar Worte aufs Display. Die Zeilen 1-11 im Script liefern dabei die Grundfunktionalitäten. Interessant wird es ab Zeile 14, denn hier beginnt die echte Ausgabe aufs Display. Der hier verwendete Befehl $oled -> clear_buffer; löscht den kompletten Inhalt des Schreib-Puffers und $oled -> display_reset(); löscht alles, was auf dem Display angezeigt wird.

Auf den Zeilen 18 und 19 wird die eigentliche Textausgabe erzeugt. Die Syntax lautet $oled -> draw_text(<x>, <y>, <Inhalt>, [<Schriftart>]). Die Angaben <x>, <y> stehen hier für die Koordinaten auf dem Display, bezogen auf die obere linke Ecke. Das Wort „Hallo“ wird also 20 Pixel nach rechts und 16 Pixel nach unten verschoben. Das Wort „Welt“ dann demzufolge um 60 Pixel nach rechts und 40 Pixel nach unten.

Zeile 22: Der Befehl $oled -> display_update(); schreibt den Inhalt des Puffers auf das OLED-Display.

Alle Display-Informationen werden solange gesammelt, bis sie mit dem Befehl $oled -> display_update(); auf das Display übertragen werden.

Den Code kopieren wir einfach in den Editor, speichern und schließen diesen (Strg+X) und führen das Script aus:

$ ./display.pl

Beispiel 2: Schriften formatieren

Raspberry Pi: OLED-Display-Beispiel "Hallo Welt" mit formatierter Schrift

Raspberry Pi: OLED-Display-Beispiel „Hallo Welt“ mit formatierter Schrift

#! /usr/bin/perl

# Bibliotheken importieren
use HiPi qw( :oled :rpi);
use HiPi::Interface::MonoOLED;

# Display einrichten
my $oled = HiPi::Interface::MonoOLED -> new(
    type      => SSD1306_128_X_64_I2C,
    address   => 0x3C,
);

# Display zum Start löschen
$oled -> clear_buffer;
$oled -> display_reset();

# Hallo Welt
$oled -> draw_text(20, 16, "Hallo", "Sans12");
$oled -> draw_text(60, 40, "Welt!", "Sans20");
 
# Ausgaben auf Display schreiben
$oled -> display_update();

Wir gehen einen Schritt weiter und formatieren die ausgegebene Schrift. Dazu ergänzen wir die Textausgabe einfach um die Schriftart (Zeilen 18 und 19). Folgende Schriftarten stehen dabei zur Verfügung:

Standard-Schriften mit allen ASCII-Zeichen und dem Grad-Zeichen (° / U+00B0 ):

  • Sans10, Sans12, Sans14, Sans15, Sans19, Sans20, Sans26, Sans33
  • Mono10, Mono12, Mono14, Mono15, Mono19, Mono20, Mono26, Mono33
  • Serif10, Serif12, Serif14, Serif15, Serif19, Serif20, Serif26, Serif33

Zusätzlich gibt es noch erweiterte Schriftarten, die alle Latin-1-Zeichen enthalten:

  • SansExtended11, SansExtended13, SansExtended15, SansExtended17, SansExtended21, SansExtended23, SansExtended30, SansExtended38
  • MonoExtended11, MonoExtended13, MonoExtended15, MonoExtended17, MonoExtended21, MonoExtended23, MonoExtended30, MonoExtended38
  • SerifExtended11, SerifExtended13, SerifExtended15, SerifExtended17, SerifExtended21, SerifExtended23, SerifExtended30, SerifExtended38

EPD-Schriftarten, die mit einer höheren Auflösung erzeugt wurden, sind ebenfalls enthalten. Sie sind dafür gedacht, um auf E-Paper-Displays genutzt zu werden. Es können jedoch alle Bitmap-Fonts für OLED- und E-Paper-Displays verwendet werden. Die EDP-Fonts enthalten alle Latin-1-Zeichen.

  • SansEPD15, SansEPD19, SansEPD23, SansEPD28, SansEPD31, SansEPD38, SansEPD50, SansEPD76, SansEPD102
  • MonoEPD15, MonoEPD19, MonoEPD23, MonoEPD28, MonoEPD31, MonoEPD38, MonoEPD50, MonoEPD76, MonoEPD102
  • SerifEPD15, SerifEPD19, SerifEPD23, SerifEPD28, SerifEPD31, SerifEPD38, SerifEPD50, SerifEPD76, SansEPD102

Leider werden hier keine True Type Fonts unterstützt, sondern nur Bitmap-Schriftarten unterstützt. Die aufgeführten Namen sind eine Kombination aus der eigentlichen Schriftart und der Schriftgröße, die in Bitmap-Fonts fest vorgegeben ist. Wer andere Schriftarten nutzen möchte, muss sich diese selber erzeugen. Dazu gibt es hier eine Anleitung. Wir klammern dieses Thema hier erstmal aus und begnügen uns mit den mitgelieferten Schriftarten.

Beispiel 3: Formen zeichnen

Raspberry Pi: OLED-Display-Beispiel mit Formen

Raspberry Pi: OLED-Display-Beispiel mit Formen

#! /usr/bin/perl

# Bibliotheken importieren
use HiPi qw( :oled :rpi);
use HiPi::Interface::MonoOLED;

# Display einrichten
my $oled = HiPi::Interface::MonoOLED -> new(
    type      => SSD1306_128_X_64_I2C,
    address   => 0x3C,
);

# Display zum Start löschen
$oled -> clear_buffer;
$oled -> display_reset();

# Formen zeichnen
$oled -> draw_line(4, 2, 20, 63);                       # diagonale Linie
$oled -> draw_rectangle(22, 2, 30, 63);                 # Rechteck
$oled -> draw_rectangle((32, 2, 40, 63), 1);            # Rechteck, ausgefüllt
$oled -> draw_ellipse(51, 32, 9, 31);                   # Ellipse
#$oled -> draw_line(76, 2, 76, 63);                     # vertikale Linie (wird nicht gezeichnet)
$oled -> draw_rectangle((76, 2, 76, 63));               # vertikale Linie
$oled -> draw_arc((76, 32, 14, 31), 270, 90);           # Bogen
$oled -> draw_polygon([[92, 63], [110, 63], [101, 2]]); # Polygon (Dreieck)
 
# Ausgaben auf Display schreiben
$oled -> display_update();

Für das Zeichnen von Formen haben wir bereits alles an Bord, was benötigt wird. Zur Verfügung stehen verschiedene Grundformen. In nachfolgender Auflistung stehen x1 , y1 für die Startkoordinaten, x2, y2 für die Endkoordinaten, rh für den vertikalen Radius und rw für den horizontalen Radius:

  • Linien: $oled -> draw -> line(x1, y1, x2, y2);
  • Rechteck (nur Rahmen): $oled -> draw_rectangle(x1, y1, x2, y2);
  • Rechteck (ausgefüllt): $oled -> draw_rectangle((x1, y1, x2, y2), 1);
  • Ellipse: $oled -> draw_ellipse(x1, y1, rh, rw);
    Die Koordinaten x1, y1 sind als Mittelpunkt der Ellipse anzugeben.
  • Polygon: $oled -> draw_polygon([[x1, y1], [x2, y2], [x3, y3]]);
    Die Eckpunkte werden als Array angegeben. [x1, y1] erste Ecke, [x2, y2] zweite Ecke, [x3, y3] dritte Ecke, …
  • Bogen: $oled -> draw_arc((x1, y1, rh, rw), α1, α2);
    Die Koordinaten x1, y1 sind als Mittelpunkt der Ellipse anzugeben.
    α1, α2 stehen für den Startwinkel und den Endwinkel, gerechnet im Uhrzeigersinn, begonnen bei 3:00 Uhr. Der Platzbedarf eines Bogens ist der selbe, wie der einer Ellipse, jedoch wird nur der angegebene Winkel gezeichnet.

Leider scheint es noch ein paar Bugs in der Bibliothek zu geben. So wird eine senkrechte Linie, wenn also x1 = x2 ist, nicht gezeichnet. Hier kann man sich jedoch mit einem Rechteck behelfen. Außerdem wird das Polygon nicht so gezeichnet, wie es die Koordinaten vorgeben (zumindest bei mir). Laut Dokumentation soll ein Polygon automatisch geschlossen werden, tut es bei mir jedoch nicht. Selbst das manuelle Schließen funktioniert nicht. Schade, aber damit kann ich trotzdem leben.

Wer in das Zeichnen von Formen tiefer einsteigen will, sollte sich nochmal die offizielle Dokumentation zu Gemüte führen. Da steht ganz genau beschrieben, wie man einzelne Formen zeichnen kann, welche Formen sich wie füllen lassen, wie man einzelne Pixel ansteuern kann, wie man abgerundete Rechtecke zeichnet, usw.

Beispiel 4: Bilddateien

Leider ist es mit dieser Bibliothek nicht möglich, Bilddateien auf direktem Weg, also durch Laden einer BMP-Datei, auf das Display zu bringen. Stattdessen bleibt nur der Weg über ein Bit-Array. Es muss also Pixel für Pixel angegeben werden, ob es eingeschaltet sein soll, oder nicht. Aus der Gesamtsumme ergibt sich dann das Bild.

#! /usr/bin/perl

# Bibliotheken importieren
use HiPi qw( :oled :rpi);
use HiPi::Interface::MonoOLED;

# Display einrichten
my $oled = HiPi::Interface::MonoOLED -> new(
    type      => SSD1306_128_X_64_I2C,
    address   => 0x3C,
);

# Display zum Start löschen
$oled -> clear_buffer;
$oled -> display_reset();

# Himbeere mit dem Startpunkt x = 10, y = 15 zeichnen:
my $bitarray =  [ 
    [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
    [ 0, 0, 0, 1, 1, 1, 1, 0, 0, 0,   0, 0, 0, 1, 1, 1, 1, 0, 0, 0 ],
    [ 0, 0, 1, 0, 0, 0, 0, 1, 0, 0,   0, 0, 1, 0, 0, 0, 0, 1, 0, 0 ],
    [ 0, 0, 1, 0, 1, 1, 0, 0, 1, 0,   0, 1, 0, 0, 1, 1, 0, 1, 0, 0 ],
    [ 0, 0, 1, 0, 0, 0, 1, 0, 1, 0,   0, 1, 0, 1, 0, 0, 0, 1, 0, 0 ],
    
    [ 0, 0, 0, 1, 0, 0, 0, 0, 0, 1,   1, 0, 0, 0, 0, 0, 1, 0, 0, 0 ],
    [ 0, 0, 0, 0, 1, 1, 1, 0, 0, 0,   0, 0, 0, 1, 1, 1, 0, 0, 0, 0 ],
    [ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,   0, 0, 0, 1, 0, 0, 0, 0, 0, 0 ],
    [ 0, 0, 0, 0, 1, 1, 0, 0, 0, 1,   1, 0, 0, 0, 1, 1, 0, 0, 0, 0 ],
    [ 0, 0, 0, 1, 0, 0, 0, 0, 1, 0,   0, 1, 0, 0, 0, 0, 1, 0, 0, 0 ],
    
    [ 0, 0, 1, 0, 0, 1, 1, 0, 0, 1,   1, 0, 0, 1, 1, 0, 0, 1, 0, 0 ],
    [ 0, 0, 1, 0, 1, 0, 0, 1, 0, 0,   0, 0, 1, 0, 0, 1, 0, 1, 0, 0 ],
    [ 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,   0, 0, 1, 0, 0, 1, 0, 0, 1, 0 ],
    [ 0, 1, 0, 0, 0, 1, 1, 0, 0, 1,   1, 0, 0, 1, 1, 0, 0, 0, 1, 0 ],
    [ 0, 1, 0, 0, 0, 0, 0, 0, 1, 0,   0, 1, 0, 0, 0, 0, 0, 0, 1, 0 ],
    
    [ 0, 1, 0, 0, 1, 1, 0, 0, 1, 0,   0, 1, 0, 0, 1, 1, 0, 0, 1, 0 ],
    [ 0, 0, 1, 0, 1, 0, 1, 0, 0, 1,   1, 0, 0, 1, 0, 1, 0, 1, 0, 0 ],
    [ 0, 0, 1, 0, 0, 1, 1, 0, 0, 0,   0, 0, 0, 1, 1, 0, 0, 1, 0, 0 ],
    [ 0, 0, 0, 1, 0, 0, 0, 0, 0, 1,   1, 0, 0, 0, 0, 0, 1, 0, 0, 0 ],
    [ 0, 0, 0, 0, 1, 1, 0, 0, 1, 0,   0, 1, 0, 0, 1, 1, 0, 0, 0, 0 ],
    
    [ 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,   0, 0, 1, 1, 0, 0, 0, 0, 0, 0 ],
    [ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,   1, 1, 0, 0, 0, 0, 0, 0, 0, 0 ],
    [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,   0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
];

$oled -> draw_bit_array( 10, 15, $bitarray );
 
# Ausgaben auf Display schreiben
$oled -> display_update();

Sicher kann man sich auch ein Script schreiben, oder eine Bibliothek importieren, die Bilder in Bit-Arrays umwandelt. Aber ich benötige das eigentlich nicht, deswegen gehe ich hier auch nicht weiter darauf ein.

Sonstiges

Im Prinzip haben wir nun alles, was wir benötigen, um unser Display mit Leben zu befüllen. Ich hatte hier auch schonmal beschrieben, wie man die CPU-Auslastung des Raspberry Pi in einem Diagramm darstellen kann – allerdings in Python. Wer möchte, kann das ja mal in Perl übersetzen und das dann gerne mit uns teilen 🙂

Ein Hinweis zu den Bildern: Die habe ich aus dem Beitrag übernommen, wo wir das Gleiche mit Python gemacht haben. Da dort eine andere Bibliothek zum Einsatz kam, kann es leichte Abweichungen in der Darstellung geben. Aber als Veranschaulichung, was in dem Beispiel gezeigt wird, sollte es wohl reichen.

Sebastian

Sebastian

...ist staatlich geprüfter Techniker für Elektrotechnik, Schwerpunkt Prozessautomatisierung und Energietechnik. Die Themen Automatisierung und Programmierung haben es ihm besonders angetan.
Außerdem ist er für jede technische Spielerei zu haben 😉
Sebastian

Hinterlasse eine Antwort

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

Translate »