Autor Thema: [52_I2C_OLED] Modul zu Ansteuerung von OLED-Displays am Raspberry Pi  (Gelesen 507 mal)

Offline Standarduser

  • Full Member
  • ***
  • Beiträge: 499
    • indiBit.de
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):

Zitat
I2C_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-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 <size-x> <size-y> <address>

Bei der Definition sind 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:
  • size-x: 128
  • size-y: 32 oder 64
  • address: im Format 0x..

Beispiel: define oled 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.
« Letzte Änderung: 31 Januar 2019, 19:27:44 von Standarduser »

Offline Standarduser

  • Full Member
  • ***
  • Beiträge: 499
    • indiBit.de
Ich habe das Modul ein wenig überarbeitet. Neue Version im ersten Post.

Offline d-man

  • New Member
  • *
  • Beiträge: 28
Super Modul, funktioniert gut und ist sehr einfach zu handhaben. Danke!

Offline d-man

  • New Member
  • *
  • Beiträge: 28
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.

Online anschristian

  • Newbie
  • Beiträge: 2
 :) 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.

Offline Standarduser

  • Full Member
  • ***
  • Beiträge: 499
    • indiBit.de
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.

Offline d-man

  • New Member
  • *
  • Beiträge: 28
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";}

Offline Standarduser

  • Full Member
  • ***
  • Beiträge: 499
    • indiBit.de
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?

Offline d-man

  • New Member
  • *
  • Beiträge: 28
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");

« Letzte Änderung: Gestern um 19:59:09 von d-man »

Offline Standarduser

  • Full Member
  • ***
  • Beiträge: 499
    • indiBit.de
Meine Variante war exakt die selbe, funktioniert hat es trotzdem nicht.

Ich schau mir das morgen nochmal an.

Online anschristian

  • Newbie
  • Beiträge: 2
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.