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.
Inhalt
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.
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
#! /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.
$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
#! /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
#! /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 Koordinatenx1, 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 Koordinatenx1, 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.
- Proxmox: „Failed to connect to Server“ mit Safari auf MacOS - 28. Januar 2023
- Loxone: Benachrichtigung per Telegram - 15. Januar 2022
- Telegram: Nachrichten per Bot von der Heimautomation aufs Handy - 2. Januar 2022
1 Comment