[52_I2C_OLED] Modul zu Ansteuerung von OLED-Displays am Raspberry Pi

Begonnen von Standarduser, 22 September 2018, 20:36:28

Vorheriges Thema - Nächstes Thema

Standarduser

Ich habe ein kleines Modul geschrieben, mit dem es möglich ist, direkt aus FHEM heraus Textinformationen auf ein OLED-Display, das per I2C an den RPi angeschlossen ist, zu schreiben.
Bei mir läuft es seit einigen Tagen und so dachte ich, ich stelle es Euch mal vor. Vielleicht kann ja der ein oder andere etwas damit anfangen.

Zur Installation einfach die Datei 52_I2C_OLED.pm in das Verzeichnis /opt/fhem/FHEM kopieren und anschließend ein reload 52_I2C_OLED.pm in FHEM absetzen.

Das Modul erklärt sich im Prinzip fast von selbst. Dennoch nachfolgend der Text für die Commandref (auch im Modul enthalten):

ZitatI2C_OLED

Das Modul I2C_OLED dient zur Ansteuerung von OLED-Displays, die über I2C an einen Raspberry Pi angeschlossen sind.
Unterstützt werden Displays mit SSD1306- oder SH1106-Chip, 128 Pixeln in der Breite und 32 oder 64 Pixel in der Höhe.

Funktionsweise:
Das Display wird in Zeilen aufgeteilt, dabei ist die Anzahl der Zeilen frei wählbar (1...6). Jede Zeile kann einzeln
mit beliebigem Text beschrieben werden. Um den Umgang mit Messwerten zu erleichtern, kann man in den
Attributen je Zeile ein Präfix und ein Suffix festlegen. Damit ist es beispielsweise möglich, per Notify nur einen
Zahlenwert auf das Display zu schreiben und dennoch einen sinnvollen Zusammenhang herstellen.
Nimmt man zum Beispiel einen einfachen Temperaturwert "10", so kann man mit dem Präfix "Temperatur:" und dem
Suffix "°C" folgende Ausgabe auf dem Display erzeugen: "Temperatur: 10 °C".

Vorraussetzungen:
Zur Ansteuerung des Displays wird die Bibliothek HiPi::Interface::MonoOLED benötigt, die sich mit folgenden
Kommandozeilen-Befehlen installieren lässt:

  • sudo dpkg --install libhipi-perl_0.72-1_armhf.deb
  • sudo apt-get -y -f install
Außerdem wird eine funktionierende Instanz von RPII2C benötigt, um Zugriff auf den I2C-Bus zu erhalten.

Define
define <name> I2C_OLED <type> <size-x> <size-y> <address>

Bei der Definition sind Displaytyp, Anzahl der Pixel in Breite und Höhe, sowie die I2C-Adresse anzugeben.
Die I2C-Adresse lässt sich mit folgendem Kommandozeilenbefehl ermitteln:
i2cdetect -y 1

Erlaubte Werte:

  • type: SSD1306 oder SH1106
  • size-x: 128
  • size-y: 32 oder 64
  • address: im Format 0x..

Beispiel: define oled SSD1306 I2C_OLED 128 64 0x3c

Set

  • OnOff: Schaltet das Display ein/aus.
  • contrast: Kontrasteinstellung für das Display. Erlaubt ist ein Zahlenbereich zwischen 0...255.
  • flip: Spiegelt die Darstellung auf dem Display. Mögliche Werte sind 1 (spiegeln) und 0 (normal).
  • invert: Invertiert das Display. Dabei wird der Hintergrund hell und die Schrift schwarz.
  • text_line1: Text, der in Zeile 1 geschrieben werden soll.
  • text_line2: Text, der in Zeile 2 geschrieben werden soll.
  • text_line3: Text, der in Zeile 3 geschrieben werden soll.
  • text_line4: Text, der in Zeile 4 geschrieben werden soll.
  • text_line5: Text, der in Zeile 5 geschrieben werden soll.
  • text_line6: Text, der in Zeile 6 geschrieben werden soll.
  • update: Erzwingt das aktualisieren des Displays (nur in Ausnahmefällen nötig).

Get
Keine Befehle verfügbar.

Attribute

  • OLED_Lines: Anzahl der Zeilen, die auf dem Display angezeigt werden sollen (1...6).
  • OLED_Font: Schriftart für den Text auf dem Display.
  • OLED_FontSize: Schriftgröße (sollte passend zur Anzahl der Zeilen gewählt werden).
  • OLED_text_line1_prefix: Zusätzlicher Text, der auf Zeile 1 vor einem Messwert angezeigt werden soll.
  • OLED_text_line1_suffix: Zusätzlicher Text, der auf Zeile 1 hinter einem Messwert angezeigt werden soll.
  • OLED_text_line2_prefix: Zusätzlicher Text, der auf Zeile 2 vor einem Messwert angezeigt werden soll.
  • OLED_text_line2_suffix: Zusätzlicher Text, der auf Zeile 2 hinter einem Messwert angezeigt werden soll.
  • OLED_text_line3_prefix: Zusätzlicher Text, der auf Zeile 3 vor einem Messwert angezeigt werden soll.
  • OLED_text_line3_suffix: Zusätzlicher Text, der auf Zeile 3 hinter einem Messwert angezeigt werden soll.
  • OLED_text_line4_prefix: Zusätzlicher Text, der auf Zeile 4 vor einem Messwert angezeigt werden soll.
  • OLED_text_line4_suffix: Zusätzlicher Text, der auf Zeile 4 hinter einem Messwert angezeigt werden soll.
  • OLED_text_line5_prefix: Zusätzlicher Text, der auf Zeile 5 vor einem Messwert angezeigt werden soll.
  • OLED_text_line5_suffix: Zusätzlicher Text, der auf Zeile 5 hinter einem Messwert angezeigt werden soll.
  • OLED_text_line6_prefix: Zusätzlicher Text, der auf Zeile 6 vor einem Messwert angezeigt werden soll.
  • OLED_text_line6_suffix: Zusätzlicher Text, der auf Zeile 6 hinter einem Messwert angezeigt werden soll.

Den Entwicklungsstand dieses Moduls würde ich im Moment irgendwo zwischen alpha und beta sehen. Die eingebauten Funktionen laufen bei mir einwandfrei, aber ganz zu Ende entwickelt ist es dann doch noch nicht. Eine englische Commandref werde ich später im Modul integrieren. Der nächste Entwicklungsschritt soll sein, dass ein Diagramm direkt auf dem Display angezeigt wird. Dafür hab ich auch schon eine Lösung, ich muss sie nur noch umsetzen.

Das angehängte Bild zeigt mein Display, konfiguriert für 5 Zeilen mit der Schriftgröße 15.

Standarduser

Ich habe das Modul ein wenig überarbeitet. Neue Version im ersten Post.

dman

Super Modul, funktioniert gut und ist sehr einfach zu handhaben. Danke!

dman

Vielleicht noch eine Anregung: Wenn ohne angeschlossenem Display definiert wird oder ein definiertes Display nicht mehr angeschlossen ist, dann läuft FHEM insgesamt nicht (stürzt ab im ersten Fall oder startet nicht im zweiten Fall). Vielleicht kann man den Fehler noch abfangen.

anschristian

 :) Super das du dir die Arbeit gemacht hast weiter so hat auf anhieb funktioniert und kann jetzt endlich die kleinen Displays benutzten.

Eine Frage hätte ich bevor ich weiter mach kann man eigentlich mehr als eines definieren ?
Ist vielleicht eine dumme frage aber einzeln angesteckt haben alle immer die gleiche Adresse die müsste man dann ändern geht das überhaupt?
Habs mit i2C nicht so um genau zu sein war das der erste Versuch mit dem Oled Display 128x64 und das hat ja dank dir gleich mal super funktioniert.

Standarduser

Hi. Theoretisch müsste das Modul mit zwei Instanzen auch zwei Displays steuern können. Aber das habe ich nie ausprobiert, da ich nur ein Display besitze.
Wenn du zwei Displays ansteuern willst, dann benötigen die auch zwei unterschiedliche Adressen.

Ich meine, dass man bei einigen Displays die Adresse durch Umlöten eines Widerstandes ändern kann. Aber sicher bin ich mir da nicht und ich kann auch nicht sagen, bei welchem das der Fall sein könnte.

dman

noch was zu meiner Anregung der Fehlerbehandlung. Mit folgendem Zusatz kann man prüfen, ob an einer erwarteten I2C-Adresse ($address) ein device im bus ist:

use HiPi qw( :i2c );
use HiPi::Device::I2C;
my $dev = HiPi::Device::I2C->new;
my $present = $dev->check_address($address);

man kann dann im Definitionsteil ggf. mit folgendem aussteigen:

if ($present != 1) {return "kein Device an der angegebenen Adresse";}

Standarduser

Hi d-man,

ich habe mir das gerade mal angeschaut, allerdings funktioniert der von dir vorgeschlagene Check bei mir nicht. Das Ergebnis ist immer negativ, auch wenn das Display angeschlossen ist und die Adresse stimmt.
Hast du das selber schonmal ausprobiert?

dman

Hm, ja, bei mir funktioniert es. Ich habe es in Dein Modul eingebaut. Habe das zuerst etwas anders versucht und dann ein nicht nachvollziehbares merkwürdiges Verhalten gehabt. Schließlich habe ich $dev global definiert. 

Also die ersten 3 Zeilen am Anfang und die beiden letzten Zeilen innerhalb I2C_OLED_Define nach den Checks bzgl. x und y.

use List::Util qw(min max);

use HiPi qw( :i2c );
use HiPi::Device::I2C;
my $dev = HiPi::Device::I2C->new;

my %sets = (
"flip" => "1,0",
"displayOnOff" => "on,off",
"invert" => "normal,inverted",
"contrast" => "",
"diagram_values" => "",
"text_line1" => "",
"text_line2" => "",
"text_line3" => "",
"text_line4" => "",
"text_line5" => "",
"text_line6" => "",
"update" => "noArg",
);

my %gets = (
);

my %repl = (
"«" => "\xAB",
"°" => "\xB0",
"±" => "\xB1",
"²" => "\xB2",
"³" => "\xB3",
"µ" => "\xB5",
"·" => "\xB7",
"¹" => "\xB9",
"º" => "\xBA",
"»" => "\xBB",
"¼" => "\xBC",
"½" => "\xBD",
"¾" => "\xBE",
"Ã,," => "\xC4",
"Ö" => "\xD6",
"×" => "\xD7",
"Ø" => "\xD8",
"Ü" => "\xDC",
"ß" => "\xDF",
"ä" => "\xE4",
"ö" => "\xF6",
"÷" => "\xF7",
"ø" => "\xF8",
"ü" => "\xFC",
);

sub I2C_OLED_Initialize($) {
my ($hash) = @_;

$hash->{DefFn}         = "I2C_OLED_Define";
$hash->{UndefFn}       = "I2C_OLED_Undef";
$hash->{SetFn}         = "I2C_OLED_Set";
$hash->{AttrFn}        = "I2C_OLED_Attr";
 
  $hash->{AttrList}  =
  "OLED_Lines " .
  "OLED_Font:SansExtended,MonoExtended,SerifExtended " .
  "OLED_FontSize:11,13,15,17,21,23,30,38 " .
  "OLED_DiagramLastValueFirst:true,false " .
  "OLED_DiagramMaxValues " .
  "OLED_DiagramOnOff:on,off " .
  "OLED_DiagramStyle:Bars,BarsFilled,Lines,Peaks " .
  "OLED_DiagramTitle ".
  "OLED_DiagramTitleSuffix ".
  "OLED_SwitchDisplay:true,false " .
  "OLED_SwitchDisplayInterval " .
  "OLED_text_line1_prefix " .
  "OLED_text_line2_prefix " .
  "OLED_text_line3_prefix " .
  "OLED_text_line4_prefix " .
  "OLED_text_line5_prefix " .
  "OLED_text_line6_prefix " .
  "OLED_text_line1_suffix " .
  "OLED_text_line2_suffix " .
  "OLED_text_line3_suffix " .
  "OLED_text_line4_suffix " .
  "OLED_text_line5_suffix " .
  "OLED_text_line6_suffix ";
}

sub I2C_OLED_Define($$) {
    my ($hash, $def) = @_;
    my @args = split("[ \t][ \t]*", $def);
   
    #prüfen, ob Define mit ausreichend Argumenten angegeben wurde
    if(int(@args) < 5) {
        return "too few parameters: define <name> I2C_OLED <size-x> <size-y> [<address>]";
    }
   
    # Status auf "defined" setzen
    $hash->{STATE} = "defined";


    # Argumente
    my $sizex = $args[2];
    my $sizey = $args[3];
    my $address = $args[4];
   
    # Prüfen, ob Werte der Argumente ok sind
    if ($sizex != 128) {
    return "size-x must be 128";
    }
    if ($sizey != 32 and $sizey != 64) {
    return "size-y must be 32 or 64";
    }

    my $present = $dev->check_address($address);
    if ($present != 1) {return "kein Device an der angegebenen Adresse";}

# Neue Display-Instanz starten
       
        my $oled = HiPi::Interface::MonoOLED
                  -> new("type => SSD1306_.$sizex._X_.$sizey._I2C,
                     address => $address,
                     skip_reset => 1");


Standarduser

Meine Variante war exakt die selbe, funktioniert hat es trotzdem nicht.

Ich schau mir das morgen nochmal an.

anschristian

Anke für die rasche Antwort, konnte mir inzwischen bezüglich mehrerer displays selbst schon eine Antwort geben.
Habs einfach mit zwei Displays ausprobiert die haben die gleiche Adresse und funktionieren für meine zwecke mit einer Definition einwandfrei.
denn ich möchte ja auch das auf allen displays das gleiche steht und wenn man keine unterschiedlichen Texte am Display ausgeben möchte kann man das 2. 3. usw. einfach dazuhängen. Übrigens der Define Check wie von d-man angegeben hat auch bei mir nicht funktioniert. Aber trotzdem Danke für eure Hilfe.

dman

ah, ich sehe, was das Problem ist. es geht um die Adresse im hexadezimalen oder dezimalen Format. wenn man 60 dezimal statt 0x3c hexadezimal eingibt, dann funktioniert es (auch sonst). irgendwie bin ich automatisch dezimal unterwegs gewesen, weil ich Probleme mit der Umwandlung hatte... Vielleicht bekommt das ja jemand in den Griff

Peter21

Hallo, bin gerade dabei ein SH1106 Display mit FHEM anzusteuern. Ich habe versucht dementsprechend den Zugriff von SSD1306 auf SH1106 zu ändern. Leider ohne Erfolg. Mit diesem Modul werden maximal Bruchteile des Textes am oberen und unteren Rand angezeigt sobald ich Zeile 6 befülle. Bei Befüllung der Zeilen 1-5 erscheint nichts auf dem Display. Wenn ich mit PERL direkt auf das Display schreibe erscheint alles wie geplant an Ort und Stelle (Hello World).
Da ich blutiger Anfänger bin (aber lernfähig) bräuchte ich noch einen Tip welchen Parameter ich in 52_I2C_OLED ändern könnte, damit es funktioniert.
Danke, Peter.

Standarduser

Kannst du dein Display mit der HiPi-Bibliothek ansteuern? Dann Zeig doch mal deinen Code.

Peter21

Hallo,
Der Text auf dem Display funktioniert mit diesem Code:
#! /usr/bin/perl
# Bibliotheken importieren
use HiPi qw( :oled :rpi);
use HiPi::Interface::MonoOLED;
# Display einrichten
my $oled = HiPi::Interface::MonoOLED -> new(
type => SH1106_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();

Er nutzt also auch die installierte HiPi Bibliothek. Was mich stutzig macht ist, dass der Text stehen bleibt obwohl über FHEM der Text Test geschrieben wird. Es scheint also auch kein komplettes Reset des Displays von FHEM zu geben.