Suche Unterstützung für Modul zur Ansteuerung eines I2C OLED Displays

Begonnen von Standarduser, 13 September 2018, 20:38:00

Vorheriges Thema - Nächstes Thema

Standarduser

Hallo zusammen,

Ich versuche mich gerade daran, ein Modul für ein OLED-Display zu entwickeln, das per I2C an den Raspberry Pi angeschlossen wird. Aber leider will es nicht so richtig.

Kann mich vielleicht jemand dabei unterstützen?

yoda_gh


PeMue

Hallo Standarduser,

Zitat von: Standarduser am 13 September 2018, 20:38:00
Kann mich vielleicht jemand dabei unterstützen?
welches Display verwendest Du denn? Hier habe ich was gesehen.
Wenn Du ein FHEM Modul schreiben willst, helfen Dir die I2C Module ggf. weiter.

Oder Du nimmst einen WemosD1mini und packst ESPEasy mit drauf und schließst das Display daran an.

Gruß PeMue
RPi3Bv1.2 rpiaddon 1.66 6.0 1xHM-CC-RT-DN 1.4 1xHM-TC-IT-WM 1.1 2xHB-UW-Sen-THPL-O 0.15 1x-I 0.14OTAU  1xCUNO2 1.67 2xEM1000WZ 2xUniroll 1xASH2200 3xHMS100T(F) 1xRFXtrx 90 1xWT440H 3xTFA30.3150 5xFA21
RPi1Bv2 LCDCSM 1.63 5.8 2xMAX HKT 1xMAX RT V200KW1 Heizung Wasser

Standarduser

#3
Ich nutze ein OLED mit SSD1306 Chip, das direkt an den RPi angeschlossen wird und habe ganz unabhängig von FHEM ein Perl-Script geschrieben, das mir auf der Kommandozeile beliebigen Text aufs Display schreibt. Ich nutze dafür eine externe Bibliothek, die sich ganz einfach mittels apt-get oder cpan installieren lässt.

Im ersten Schritt wollte ich jetzt in einem FHEM-Modul einfach einen statischen Text auf das Display schreiben. Allerdings tut sich auf dem Display überhaupt nichts. Vermutlich wird die Bibliothek garnicht angesprochen, denn schon beim Init müsste das Display mal aufleuchten- tut es aber nicht.

Meine erste Frage wäre also: Wie geht man hier grundsätzlich vor, wenn man ein Script besitzt, das außerhalb von FHEM funktioniert und externe Bibliotheken benutzt?

Das ist das Script auf der Kommandozeile:
#! /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();


Und das ist der Code, den ich bisher habe (abgekupfert von 52_I2C_LCD):
#
#  52_I2C_OLED.pm
#

package main;

use strict;
use warnings;
use HiPi qw( :oled :rpi);
use HiPi::Interface::MonoOLED;

my %sets = (
  "text" => "",
  "clear" => "noArg",
);

my %gets = (
);

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

$hash->{DefFn}         = "I2C_OLED_Define";
$hash->{UndefFn}       = "I2C_OLED_Undef";
#$hash->{DeleteFn}      = "I2C_OLED_Delete";
#$hash->{InitFn}        = "I2C_OLED_Init";
$hash->{SetFn}         = "I2C_OLED_Set";
#$hash->{GetFn}         = "I2C_OLED_Get";
#$hash->{AttrFn}        = "I2C_OLED_Attr";
#$hash->{ReadFn}        = "I2C_OLED_Read";
#$hash->{ReadyFn}       = "I2C_OLED_Ready";
#$hash->{StateFn}       = "I2C_OLED_State";
#$hash->{NotifyFn}      = "I2C_OLED_Notify";
#$hash->{RenameFn}      = "I2C_OLED_Rename";
#$hash->{ShutdownFn}    = "I2C_OLED_Shutdown";
 
  $hash->{AttrList}  = "restoreOnReconnect:on,off restoreOnStartup:on,off IODev model pinMapping"
." customChar0 customChar1 customChar2 customChar3 customChar4 customChar5 customChar6 customChar7"
." backLight:on,off blink:on,off autoClear:on,off autoBreak:on,off replaceRegex $main::readingFnAttributes";
#  autoScroll:on,off direction:leftToRight,rightToLeft do not work reliably
 
 
}

sub I2C_OLED_Define($$) {
    my ($hash, $def) = @_;
    my @a = split('[ \t]+', $def);
   
    $hash->{STATE}="defined";

if ($main::init_done) {
eval {
I2C_OLED_Init($hash,[@a[2..scalar(@a)-1]]);
};
return I2C_OLED_Catch($@) if $@;
}

return undef;
}

sub I2C_OLED_Init($$) {
   
my ($hash,$args) = @_;
my $u = "wrong syntax: define <name> I2C_OLED <size-x> <size-y> [<address>]";

return $u if(int(@$args) < 2);
   
    $hash->{size_x} = shift @$args;
    $hash->{size_y} = shift @$args;
    $hash->{address} = shift @$args;
   
    my $name = $hash->{NAME};
   
    eval {
main::AssignIoPort($hash,AttrVal($hash->{NAME},"IODev",undef));
    require MonoOLED;
    my $oled = MonoOLED -> new(type => SSD1306_128_X_64_I2C, address => 0x3C);
    $oled -> clear_buffer;
$oled -> display_reset();
hash->{oled} = $oled;
    };

if (! (defined AttrVal($name,"stateFormat",undef))) {
$main::attr{$name}{"stateFormat"} = "text";
}

return undef;
}

sub I2C_OLED_Undef($$) {
    my ($hash, $arg) = @_;

# Display löschen


    return undef;
}

sub I2C_OLED_Set($@) {
my ($hash, @a) = @_;
return "Need at least one parameters" if(@a < 2);
my $command = $a[1];
my $value = $a[2];

if(!defined($sets{$command})) {
my @commands = ();
foreach my $key (sort keys %sets) {
push @commands, $sets{$key} ? $key.":".join(",",$sets{$key}) : $key;
}
return "Unknown argument $a[1], choose one of " . join(" ", @commands);
}

my $oled = $hash->{oled};
return unless defined $oled;

eval {
COMMAND_HANDLER: {
$command eq "text" and do {
shift @a;
shift @a;
$value = join(" ", @a);

if (AttrVal($hash->{NAME},"autoClear","on") eq "on") {
$oled -> clear_buffer;
$oled -> display_reset();
}

# set reading prior to regexp, could contain unprintable chars!
main::readingsSingleUpdate($hash,"text",$value,1);
$value = _i2c_lcd_replace($hash,$value);

$oled -> draw_text(20, 16, $value);
$oled -> display_update();
last;
};

$command eq "clear" and do {
$oled -> clear_buffer;
$oled -> display_reset();
last;
};

}
};
return I2C_LCD_Catch($@) if $@;
return undef;
}

sub I2C_OLED_Catch($) {
my $exception = shift;
if ($exception) {
$exception =~ /^(.*)( at.*FHEM.*)$/;
return $1;
}
return undef;
}

1;


# Beginn der Commandref

=pod
=item [helper|device|command]
=item summary Kurzbeschreibung in Englisch was MYMODULE steuert/unterstützt
=item summary_DE Kurzbeschreibung in Deutsch was MYMODULE steuert/unterstützt

=begin html
Englische Commandref in HTML
=end html

=begin html_DE
Deustche Commandref in HTML
=end html

# Ende der Commandref
=cut


Da fehlt noch einiges, allerdings müsste im Teil I2C_OLED_Init der Inhalt vom Display gelöscht werden, der eventuell noch vorhanden ist. Wird er aber nicht. Auch müsste es damit möglich sein, per Set-Befehl einen Text aufs Display zu schreiben. Zuletzt bekam ich beim Laden immer den Fehler im Bereich der Zeilen 132 ff., dass es ein Problem mit der globalen Variable $oled gibt. Dann habe ich ein wenig herumprobiert (Zeile 80). Nun erhalte ich die Fehlermeldung:

Can't use bareword ("hash") as a HASH ref while "strict refs" in use at ./FHEM/52_I2C_OLED.pm line 80.

Da fehlt mir noch einiges an Grundlagen, das weiß ich. Aber ich weiß im Moment auch nicht, woher ich die bekommen soll, bzw. wo ich das nachlesen könnte.

Standarduser

Hi, ich wollte das Theme gerne nochmal hochholen, weil ich nicht so richtig weiterkomme.

Ich ab meinen kompletten Code jetzt mal aufgeräumt und alles für den Moment unwesentliche entfernt. Das hier ist im Moment übrig:

#  52_I2C_OLED.pm
#
# benötigt:
# sudo dpkg --install libhipi-perl_0.72-1_armhf.deb
# sudo apt-get -y -f install

# define oled I2C_OLED 128 64 0x3C

package main;

use strict;
use warnings;
use HiPi qw( :oled :rpi);
use HiPi::Interface::MonoOLED;

my %sets = (
  #"text" => "",
  #"clear" => "noArg",
);

my %gets = (
);

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

$hash->{DefFn}         = "I2C_OLED_Define";
$hash->{UndefFn}       = "I2C_OLED_Undef";
$hash->{SetFn}         = "I2C_OLED_Set";
 
  $hash->{AttrList}  = "IODev autoClear:on,off";
}


sub I2C_OLED_Define($$) {
    my ($hash, $def) = @_;
    my @args = split("[ \t][ \t]*", $def);
   
    if(int(@args) < 5) {
        return "too few parameters: define <name> I2C_OLED <size-x> <size-y> [<address>]";
    }
   
    $hash->{size_x} = $args[2];
    $hash->{size_y} = $args[3];
    $hash->{address} = $args[4];
       
my $oled = HiPi::Interface::MonoOLED -> new(type => SSD1306_128_X_64_I2C, address => 0x3C);
    #my $oled -> clear_buffer;
#my $oled -> display_reset();

return undef;
}

sub I2C_OLED_Undef($$) {
    my ($hash, $arg) = @_;
    # nothing to do
    return undef;
}

sub I2C_OLED_Set($@) {
my ($hash, @a) = @_;
#
return undef;
}

1;


Auf den Zeilen 13 und 14 binde ich die externe Bibliothek ein und auf Zeile 47 wird dann damit die Display-Instanz erzeugt (alle Parameter im Moment noch fest einprogrammiert).

Leider stürzt FHEM mit dem Befehl define oled I2C_OLED 128 64 0x3C komplett ab. Im Log erscheint dann folgender Eintrag:
open error on /dev/i2c-1: Permission denied
at /usr/local/lib/arm-linux-gnueabihf/perl/5.24.1/HiPi/Interface/MonoOLED.pm line 235.


Offensichtlich hat der Benutzer "fhem" nicht die nötigen Rechte, auf das I2C-Device zuzugreifen.
Jetzt ist mir nicht so ganz klar, wie fhem denn Zugriff auf I2C erlangen könnte. Ich hab ja schon einige FHEM-Module ausprobiert, aber an den Rechten musste ich bisher noch nie etwas drehen. Gibt es also einen anderen Weg, fhem das Display ansteuern zu lassen, bzw. Zugriff auf I2C zu gewähren?

yoda_gh

Zitat von: Standarduser am 16 September 2018, 13:27:57
Leider stürzt FHEM mit dem Befehl define oled I2C_OLED 128 64 0x3C komplett ab. Im Log erscheint dann folgender Eintrag:
open error on /dev/i2c-1: Permission denied
at /usr/local/lib/arm-linux-gnueabihf/perl/5.24.1/HiPi/Interface/MonoOLED.pm line 235.


Offensichtlich hat der Benutzer "fhem" nicht die nötigen Rechte, auf das I2C-Device zuzugreifen.
Jetzt ist mir nicht so ganz klar, wie fhem denn Zugriff auf I2C erlangen könnte. Ich hab ja schon einige FHEM-Module ausprobiert, aber an den Rechten musste ich bisher noch nie etwas drehen. Gibt es also einen anderen Weg, fhem das Display ansteuern zu lassen, bzw. Zugriff auf I2C zu gewähren?

Siehe die Doku zu RPII2C, dort stehen verschiedene Möglichkeiten, dem User FHEM die Rechte zu geben. Ich habe den User zur Gruppe "i2c" hinzugefügt. Schau Dir mal /dev/i2c-1 an, ob dort die Gruppe "i2c" Schreibrechte hat, wenn ja, ist das vermutlich die einfachste Möglichkeit. Neu starten nicht vergessen, damit der User die Gruppenzugehörigkeit bekommt.

Standarduser

Danke. Ich bin mittlerweile auch schon auf diese Stelle gestoßen und mein Modul läuft auch schon recht passabel, sodass ich mich nun auf das wesentliche konzentrieren kann.

Im Moment probiere ich verschiedene Möglichkeiten aus, wie ich das Display mit Leben befüllen kann. Zum Beispiel ob es Sinn macht, Zeilen zu erzeugen und diese einzeln zu beschreiben oder mir die Inhalte lieber extern zusammenbaue und insgesamt aufs Display schreibe. Da das Display eine Matrix von 128x64 Pixeln besitzt hat man da ja ziemlich viele Freiheitsgrade.

Falls da jemand Ideen hat würde ich sie gerne hier lesen.