From 9e17d085935e623856b5950d8ba341db41f65ea7 Mon Sep 17 00:00:00 2001 From: klauswitt <> Date: Sun, 6 Jul 2014 21:39:05 +0000 Subject: [PATCH] added attribute OnStartup added attribute OutputPorts as substitute for InputPorts updated commandref git-svn-id: https://svn.fhem.de/fhem/trunk@6211 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/FHEM/52_I2C_PCA9532.pm | 436 +++++++++++++++++++++++------------- 1 file changed, 279 insertions(+), 157 deletions(-) diff --git a/fhem/FHEM/52_I2C_PCA9532.pm b/fhem/FHEM/52_I2C_PCA9532.pm index d55da4230..00432a21d 100644 --- a/fhem/FHEM/52_I2C_PCA9532.pm +++ b/fhem/FHEM/52_I2C_PCA9532.pm @@ -1,6 +1,5 @@ ############################################################################## # $Id$ -# 52_I2C_PCA9532.pm # ############################################################################## # Modul for I2C PWM Driver PCA9532 @@ -11,18 +10,14 @@ # contributed by Klaus Wittstock (2014) email: klauswittstock bei gmail punkt com # ############################################################################## -#zu tun: -#bei Set sollten Input Values nicht aktualisiert werden -#$sendpackage{data} als Array ? -#$clientmsg->{received} als Array ? #Inhalte des Hashes: -#i2caddress 00-7F I2C-Adresse +#i2caddress 00-127(7F) I2C-Adresse #direction i2cread|i2cwrite Richtung -#reg 00-FF|"" Registeradresse (kann weggelassen werden fuer IC's ohne Registeradressierung) +#reg 00-255|"" Registeradresse (kann weggelassen werden fuer IC's ohne Registeradressierung) #nbyte Zahl Anzahl Register, die bearbeitet werden sollen (im mom 0-99) -#data 00-FF ... Daten die an I2C geschickt werden sollen (muessen, wenn nbyte benutzt wird immer ein Vielfaches Desselben sein) -#received 00-FF ... Daten die vom I2C empfangen wurden, durch Leerzeichen getrennt (bleibt leer wenn Daten geschrieben werden) +#data 00-255 ... Daten die an I2C geschickt werden sollen (muessen, wenn nbyte benutzt wird immer ein Vielfaches Desselben sein) +#received 00-255 ... Daten die vom I2C empfangen wurden, durch Leerzeichen getrennt (bleibt leer wenn Daten geschrieben werden) #pname_SENDSTAT Ok|error zeigt uebertragungserfolg an package main; @@ -31,7 +26,6 @@ use warnings; use SetExtensions; #use POSIX; use Scalar::Util qw(looks_like_number); -#use Error qw(:try); my $setdim = ":slider,0,1,255 "; @@ -43,26 +37,26 @@ my %setsP = ( ); ############################################################################### sub I2C_PCA9532_Initialize($) { - my ($hash) = @_; - $hash->{DefFn} = "I2C_PCA9532_Define"; - $hash->{InitFn} = 'I2C_PCA9532_Init'; - $hash->{UndefFn} = "I2C_PCA9532_Undefine"; - $hash->{AttrFn} = "I2C_PCA9532_Attr"; - #$hash->{StateFn} = "I2C_PCA9532_SetState"; - $hash->{SetFn} = "I2C_PCA9532_Set"; - $hash->{GetFn} = "I2C_PCA9532_Get"; - $hash->{I2CRecFn} = "I2C_PCA9532_I2CRec"; - $hash->{AttrList} = "IODev do_not_notify:1,0 ignore:1,0 showtime:1,0". - "poll_interval T0:slider,0,1,255 T1:slider,0,1,255 InputPorts ". + my ($hash) = @_; + $hash->{DefFn} = "I2C_PCA9532_Define"; + $hash->{InitFn} = 'I2C_PCA9532_Init'; + $hash->{UndefFn} = "I2C_PCA9532_Undefine"; + $hash->{AttrFn} = "I2C_PCA9532_Attr"; + $hash->{StateFn} = "I2C_PCA9532_State"; + $hash->{SetFn} = "I2C_PCA9532_Set"; + $hash->{GetFn} = "I2C_PCA9532_Get"; + $hash->{I2CRecFn} = "I2C_PCA9532_I2CRec"; + $hash->{AttrList} = "IODev do_not_notify:1,0 ignore:1,0 showtime:1,0". + "poll_interval T0:slider,0,1,255 T1:slider,0,1,255 OutputPorts OnStartup InputPorts ". "$readingFnAttributes"; } ############################################################################### sub I2C_PCA9532_SetState($$$$) { - my ($hash, $tim, $vt, $val) = @_; + my ($hash, $tim, $vt, $val) = @_; - $val = $1 if($val =~ m/^(.*) \d+$/); - #return "Undefined value $val" if(!defined($it_c2b{$val})); - return undef; + $val = $1 if($val =~ m/^(.*) \d+$/); + #return "Undefined value $val" if(!defined($it_c2b{$val})); + return undef; } ############################################################################### sub I2C_PCA9532_Define($$) { @@ -70,10 +64,10 @@ sub I2C_PCA9532_Define($$) { my @a = split("[ \t]+", $def); $hash->{STATE} = 'defined'; if ($main::init_done) { - eval { I2C_PCA9532_Init( $hash, [ @a[ 2 .. scalar(@a) - 1 ] ] ); }; - return I2C_PCA9532_Catch($@) if $@; - } - return undef; + eval { I2C_PCA9532_Init( $hash, [ @a[ 2 .. scalar(@a) - 1 ] ] ); }; + return I2C_PCA9532_Catch($@) if $@; + } + return undef; } ############################################################################### sub I2C_PCA9532_Init($$) { @@ -82,169 +76,289 @@ sub I2C_PCA9532_Init($$) { my $name = $hash->{NAME}; if (defined $args && int(@$args) != 1) { return "Define: Wrong syntax. Usage:\n" . - "define I2C_PCA9532 "; + "define I2C_PCA9532 "; } - #return "$name I2C Address not valid" unless ($a[0] =~ /^(0x|)([0-7]|)[0-9A-F]$/xi); + #return "$name I2C Address not valid" unless ($a[0] =~ /^(0x|)([0-7]|)[0-9A-F]$/xi); - if (defined (my $address = shift @$args)) { - $hash->{I2C_Address} = $address =~ /^0.*$/ ? oct($address) : $address; - } else { - return "$name I2C Address not valid"; - } - - #$hash->{I2C_Address} = hex($a[0]); - AssignIoPort($hash); - $hash->{STATE} = 'Initialized'; - return; + if (defined (my $address = shift @$args)) { + $hash->{I2C_Address} = $address =~ /^0.*$/ ? oct($address) : $address; + } else { + return "$name I2C Address not valid"; + } + AssignIoPort($hash); + #PWM Frequenzen wiederherstellen + I2C_PCA9532_I2CSet($hash,AttrVal($name, "T0", 0),2); + I2C_PCA9532_I2CSet($hash,AttrVal($name, "T1", 0),4); + #PWM Werte wiederherstellen + I2C_PCA9532_Set($hash, $name,"PWM0", ReadingsVal($name,"PWM0",0) ); + I2C_PCA9532_Set($hash, $name,"PWM1", ReadingsVal($name,"PWM1",0) ); + #Portzustände wiederherstellen + my @outports = sort(split(/,/,AttrVal($name, "OutputPorts", ""))); + foreach (0..15) { + I2C_PCA9532_Set($hash, $name,"Port".$_, ReadingsVal($name,"Port".$_,0) ) if ( $_ ~~ @outports); + } + $hash->{STATE} = 'Initialized'; + return; } ############################################################################### sub I2C_PCA9532_Catch($) { - my $exception = shift; - if ($exception) { - $exception =~ /^(.*)( at.*FHEM.*)$/; - return $1; - } - return undef; + my $exception = shift; + if ($exception) { + $exception =~ /^(.*)( at.*FHEM.*)$/; + return $1; + } + return undef; +} +############################################################################### +sub I2C_PCA9532_State($$$$) { #reload readings at FHEM start + my ($hash, $tim, $sname, $sval) = @_; + Log3 $hash, 4, "$hash->{NAME}: $sname kann auf $sval wiederhergestellt werden $tim"; + if ($sname =~ m/^Port([0-9]|1[0-5])$/i) { + my $po = $sname; #noch ändern + $po =~ tr/[a-zA-Z]//d; #Nummer aus String extrahieren + my @outports = sort(split(/,/,AttrVal($hash->{NAME}, "OutputPorts", ""))); + if ( $po ~~ @outports) { + my %onstart = split /[,=]/, AttrVal($hash->{NAME}, "OnStartup", ""); + if ( exists($onstart{$po}) && exists($setsP{$onstart{$po}})) { + Log3 $hash, 5, "$hash->{NAME}: $sname soll auf $onstart{$po} gesetzt werden"; + readingsSingleUpdate($hash,$sname, $onstart{$po}, 1); + } else { + Log3 $hash, 5, "$hash->{NAME}: $sname soll auf Altzustand: $sval gesetzt werden"; + $hash->{READINGS}{$sname}{VAL} = $sval; + $hash->{READINGS}{$sname}{TIME} = $tim; + } + + } else { + Log3 $hash, 5, "$hash->{NAME}: $sname ist Eingang"; + } + } elsif ($sname =~ m/^PWM[0-1]$/i) { + my %onstart = split /[,=]/, AttrVal($hash->{NAME}, "OnStartup", ""); + if ( exists($onstart{$sname}) && looks_like_number($onstart{$sname}) && $onstart{$sname} >= 0 && $onstart{$sname} < 256 ) { + Log3 $hash, 5, "$hash->{NAME}: $sname soll auf $onstart{$sname} gesetzt werden"; + readingsSingleUpdate($hash,$sname, $onstart{$sname}, 1); + } else { + Log3 $hash, 5, "$hash->{NAME}: $sname soll auf Altzustand: $sval gesetzt werden"; + $hash->{READINGS}{$sname}{VAL} = $sval; + $hash->{READINGS}{$sname}{TIME} = $tim; + } + } + return undef; } ############################################################################### sub I2C_PCA9532_Undefine($$) { - my ($hash, $arg) = @_; - if ( defined (AttrVal($hash->{NAME}, "poll_interval", undef)) ) { - RemoveInternalTimer($hash); - } + my ($hash, $arg) = @_; + if ( defined (AttrVal($hash->{NAME}, "poll_interval", undef)) ) { + RemoveInternalTimer($hash); + } } ############################################################################### sub I2C_PCA9532_Attr(@) { - my (undef, $name, $attr, $val) = @_; - my $hash = $defs{$name}; - my $msg = ''; - if ($attr && $attr eq 'poll_interval') { - #my $pollInterval = (defined($val) && looks_like_number($val) && $val > 0) ? $val : 0; + my ($command, $name, $attr, $val) = @_; + my $hash = $defs{$name}; + my $msg = ''; + if ($command && $command eq "set" && $attr && $attr eq "IODev") { + if ($main::init_done and (!defined ($hash->{IODev}) or $hash->{IODev}->{NAME} ne $val)) { + main::AssignIoPort($hash,$val); + my @def = split (' ',$hash->{DEF}); + I2C_PCA9532_Init($hash,\@def) if (defined ($hash->{IODev})); + } + } elsif ($attr && $attr eq 'poll_interval') { if (!defined($val) ) { RemoveInternalTimer($hash); - } elsif ($val > 0) { + } elsif ( looks_like_number($val) && $val > 0) { RemoveInternalTimer($hash); InternalTimer(1, 'I2C_PCA9532_Poll', $hash, 0); } else { $msg = 'Wrong poll intervall defined. poll_interval must be a number > 0'; } - } elsif ($attr && $attr =~ m/^T[0-1]$/i) { - return "wrong value: $val for \"set $name $attr\" use 0-255" - unless(looks_like_number($val) && $val >= 0 && $val < 256); - substr($attr,0,1,""); - my $regaddr = ($attr == 0 ? 2 : 4); - my %sendpackage = ( i2caddress => $hash->{I2C_Address}, direction => "i2cwrite" ); - #$sendpackage{data} = sprintf("%.2X", $val); - #$sendpackage{reg} = sprintf("%.2X", $regaddr); - $sendpackage{data} = $val; - $sendpackage{reg} = $regaddr; - return "$name: no IO device defined" unless ($hash->{IODev}); - my $phash = $hash->{IODev}; - my $pname = $phash->{NAME}; - CallFn($pname, "I2CWrtFn", $phash, \%sendpackage); + } elsif ($attr && $attr =~ m/^T[0-1]$/i) { + return "wrong value: $val for \"set $name $attr\" use 0-255" + unless(looks_like_number($val) && $val >= 0 && $val < 256); + substr($attr,0,1,""); + my $regaddr = ($attr == 0 ? 2 : 4); + $msg = I2C_PCA9532_I2CSet($hash,$val,$regaddr); +###################################################################irgendwann raus damit } elsif ($attr && $attr eq "InputPorts") { - my @inp = split(" ", $val); + my @inp = split(" ", $val) if defined($val); + my $oval = 65535; foreach (@inp) { - return "wrong value: $_ for \"set $name $attr\" use space separated numbers 0-15" unless ($_ >= 0 && $_ < 16); + return "wrong value: $_ for \"attr $name $attr\" use space separated numbers 0-15" unless ($_ >= 0 && $_ < 16); + $oval -= 1 << $_; } + my $outports = undef; + foreach (0..15) { + if ( ($oval & (1 << $_)) != 0 ) { + $outports .= "," if defined($outports); + $outports .= $_; + } + } + $msg .= CommandAttr(undef, $name . ' OutputPorts ' . $outports); + ########das noch auf 1 setzen!!!!!!!!!!1 + $val = "" unless defined($val); + Log3 $hash ,1 , "\"attr $name $attr $val\" will removed in further versions, generated: \"attr $name OutputPorts $outports\" please store it in your config and remove InputPorts"; +############################################################################################### + } elsif ($attr && $attr eq "OutputPorts") { + my @inp = split(/,/, $val) if defined($val); + foreach (@inp) { + return "wrong value: $_ for \"attr $name $attr\" use comma separated numbers 0-15" unless ($_ >= 0 && $_ < 16); + } + $msg = I2C_PCA9532_ResetInputs($hash, @inp) unless $msg; + } elsif ($attr && $attr eq "OnStartup") { + if (defined $val) { + foreach (split (/,/,$val)) { + my @pair = split (/=/,$_); + $msg = "wrong value: $_ for \"attr $hash->{NAME} $attr\" use comma separated =on|off|PWM0|PWM1|last or PWM0|PWM1=0..255|last where = 0 - 15 " + unless ( scalar(@pair) == 2 && + (($pair[0] =~ m/^[0-9]|1[0-5]$/i && ( $pair[1] eq "last" || exists($setsP{$pair[1]}) ) ) || + ( $pair[0] =~ m/^PWM(0|1)$/i && looks_like_number($pair[1]) && $pair[1] >=0 && $pair[1] < 256)) + ); + } + } } - return ($msg) ? $msg : undef; + return ($msg) ? $msg : undef; } + ############################################################################### sub I2C_PCA9532_Poll($) { - my ($hash) = @_; - my $name = $hash->{NAME}; - # Read values - I2C_PCA9532_Get($hash, $name); - my $pollInterval = AttrVal($hash->{NAME}, 'poll_interval', 0); - if ($pollInterval > 0) { - InternalTimer(gettimeofday() + ($pollInterval * 60), 'I2C_PCA9532_Poll', $hash, 0); - } + my ($hash) = @_; + my $name = $hash->{NAME}; + # Read values + I2C_PCA9532_Get($hash, $name); + my $pollInterval = AttrVal($hash->{NAME}, 'poll_interval', 0); + if ($pollInterval > 0) { + InternalTimer(gettimeofday() + ($pollInterval * 60), 'I2C_PCA9532_Poll', $hash, 0); + } } ############################################################################### +sub I2C_PCA9532_ResetInputs{ + my ($hash, @outports) = @_; + my $msg = undef; + foreach my $LSreg (0..3) { #Nummer des entspechenden LS Registers + my $regaddr = $LSreg + 6; #Adresse fuer Controlregister berechnen (LS0 = 0x06) + my $n = $LSreg * 4; #Erster Port in LSx + my $sbyte = 0; + foreach (reverse 0..3) { #ensprechendes Controlregister fuellen + my $portn = $_ + $n; + if ($portn ~~ @outports) { #output aus dem Reading holen + my $pval = ReadingsVal($hash->{NAME},'Port'.$portn,"off"); + $sbyte += $setsP{ ($pval =~ m/^(0|1)$/i ? "off" : $pval) } << (2 * $_); + } else { #wenn Input ist auf rezessiv (high) setzen + $sbyte += $setsP{"off"} << (2 * $_); + } + } + $msg = I2C_PCA9532_I2CSet($hash,$sbyte,$regaddr); + last if $msg; + } + return defined $msg ? $msg : undef +} +############################################################################### sub I2C_PCA9532_Set($@) { - my ($hash, @a) = @_; - my $name =$a[0]; - my $cmd = $a[1]; - my $val = $a[2]; - my @inports = sort(split( " ",AttrVal($name, "InputPorts", ""))); - unless (@a == 3) { - - } - my %sendpackage = ( i2caddress => $hash->{I2C_Address}, direction => "i2cwrite" ); - if ( $cmd && $cmd =~ m/^Port((0|)[0-9]|1[0-5])$/i) { - return "wrong value: $val for \"set $name $cmd\" use one of: " . + my ($hash, @a) = @_; + my $name =$a[0]; + my $cmd = $a[1]; + my $val = $a[2]; + my @outports = sort(split(/,/,AttrVal($name, "OutputPorts", ""))); + #my @inports = sort(split( " ",AttrVal($name, "InputPorts", ""))); + unless (@a == 3) { + + } + my $msg; + #my %sendpackage = ( i2caddress => $hash->{I2C_Address}, direction => "i2cwrite" ); + if ( $cmd && $cmd =~ m/^Port((0|)[0-9]|1[0-5])$/i) { + return "wrong value: $val for \"set $name $cmd\" use one of: " . join(',', (sort { $setsP{ $a } <=> $setsP{ $b } } keys %setsP) ) unless(exists($setsP{$val})); - substr($cmd,0,4,""); - return "$name error: Port$cmd is defined as input" if ( $cmd ~~ @inports ); #Pruefen ob entsprechender Port Input ist + substr($cmd,0,4,""); + return "$name error: Port$cmd is defined as input" unless ( $cmd ~~ @outports ); #Pruefen ob entsprechender Port Input ist my $LSreg = int($cmd / 4); #Nummer des entspechenden LS Registers my $regaddr = $LSreg + 6; #Adresse fuer Controlregister berechnen (LS0 = 0x06) my $n = $LSreg * 4; #Erster Port in LSx my $sbyte = 0; foreach (reverse 0..3) { #ensprechendes Controlregister fuellen - my $portn = $_ + $n; -#hier noch alle inputs auf rezessiv setzen - if (( $portn) == $cmd ) { #->wenn aktueller Port dann neuer Wert + my $portn = $_ + $n; + if (( $portn) == $cmd ) { #->wenn aktueller Port dann neuer Wert $sbyte += $setsP{$val} << (2 * $_); - next; + #next; + } elsif ($portn ~~ @outports) {#->sonst aus dem Reading holen + $sbyte += $setsP{ReadingsVal($name,'Port'.$portn,"off")} << (2 * $_); + } else { #wenn Input ist auf rezessiv (high) setzen + $sbyte += $setsP{"off"} << (2 * $_); } - $sbyte += $setsP{ReadingsVal($name,'Port'.$portn,"off")} << (2 * $_); #->sonst aus dem Reading holen } + $msg = I2C_PCA9532_I2CSet($hash,$sbyte,$regaddr); + #$sendpackage{data} = sprintf("%.2X",$sbyte); - $sendpackage{data} = $sbyte; + #$sendpackage{data} = $sbyte; #$sendpackage{reg} = sprintf("%.2X", $regaddr); - $sendpackage{reg} = $regaddr; + #$sendpackage{reg} = $regaddr; - } elsif ($cmd && $cmd =~ m/^PWM[0-1]$/i) { - return "wrong value: $val for \"set $name $cmd\" use 0-255" + } elsif ($cmd && $cmd =~ m/^PWM[0-1]$/i) { + return "wrong value: $val for \"set $name $cmd\" use 0-255" unless(looks_like_number($val) && $val >= 0 && $val < 256); - substr($cmd,0,3,""); + substr($cmd,0,3,""); my $regaddr = ($cmd == 0 ? 3 : 5); - #$sendpackage{data} = sprintf("%.2X", $val); - $sendpackage{data} = $val; - $sendpackage{reg} = sprintf("%.2X", $regaddr); + $msg = I2C_PCA9532_I2CSet($hash,$val,$regaddr); + #return $msg if $msg; - } else { - my $list = undef; - foreach (0..15) { - next if ( $_ ~~ @inports ); #Inputs ueberspringen + #$sendpackage{data} = sprintf("%.2X", $val); + #$sendpackage{data} = $val; + #$sendpackage{reg} = sprintf("%.2X", $regaddr); + + } else { + my $list = undef; + foreach (0..15) { + next unless ( $_ ~~ @outports ); #Inputs ueberspringen #$list .= "Port" . $_ . ":" . join(',', sort keys %setsP) . " "; $list .= "Port" . $_ . ":" . join(',', (sort { $setsP{ $a } <=> $setsP{ $b } } keys %setsP) ) . " "; - } - $list .= join($setdim, ("PWM0", "PWM1")) . $setdim; - return "Unknown argument $a[1], choose one of " . $list; + } + $list .= join($setdim, ("PWM0", "PWM1")) . $setdim; + $msg = "Unknown argument $a[1], choose one of " . $list; + } + #return "$name: no IO device defined" unless ($hash->{IODev}); + #my $phash = $hash->{IODev}; + #my $pname = $phash->{NAME}; + #CallFn($pname, "I2CWrtFn", $phash, \%sendpackage); + return defined $msg ? $msg : undef +} +############################################################################### +sub I2C_PCA9532_I2CSet { + my ($hash, $regval, $reg) = @_; + if (defined (my $iodev = $hash->{IODev})) { + CallFn($iodev->{NAME}, "I2CWrtFn", $iodev, { + direction => "i2cwrite", + i2caddress => $hash->{I2C_Address}, + reg => $reg, + data => $regval, + }) if (defined $hash->{I2C_Address}); + } else { + return "no IODev assigned to '$hash->{NAME}'"; } - return "$name: no IO device defined" unless ($hash->{IODev}); - my $phash = $hash->{IODev}; - my $pname = $phash->{NAME}; - CallFn($pname, "I2CWrtFn", $phash, \%sendpackage); } ############################################################################### sub I2C_PCA9532_Get($@) { - my ($hash, @a) = @_; - my $name =$a[0]; + my ($hash, @a) = @_; + my $name =$a[0]; my %sendpackage = ( i2caddress => $hash->{I2C_Address}, direction => "i2cread" ); $sendpackage{reg} = 0; #startadresse zum lesen $sendpackage{nbyte} = 10; return "$name: no IO device defined" unless ($hash->{IODev}); my $phash = $hash->{IODev}; - my $pname = $phash->{NAME}; + my $pname = $phash->{NAME}; CallFn($pname, "I2CWrtFn", $phash, \%sendpackage); } ############################################################################### sub I2C_PCA9532_I2CRec($@) { # vom physical aufgerufen my ($hash, $clientmsg) = @_; - my $name = $hash->{NAME}; - my $phash = $hash->{IODev}; - my $pname = $phash->{NAME}; - while ( my ( $k, $v ) = each %$clientmsg ) { #erzeugen von Internals fuer alle Keys in $clientmsg die mit dem physical Namen beginnen - $hash->{$k} = $v if $k =~ /^$pname/ ; - } + my $name = $hash->{NAME}; + my $phash = $hash->{IODev}; + my $pname = $phash->{NAME}; + while ( my ( $k, $v ) = each %$clientmsg ) { #erzeugen von Internals fuer alle Keys in $clientmsg die mit dem physical Namen beginnen + $hash->{$k} = $v if $k =~ /^$pname/ ; + } #hier noch ueberpruefen, ob Register und Daten ok - if ($clientmsg->{direction} && defined($clientmsg->{reg}) && $clientmsg->{$pname . "_SENDSTAT"} && $clientmsg->{$pname . "_SENDSTAT"} eq "Ok") { + if ($clientmsg->{direction} && defined($clientmsg->{reg}) && $clientmsg->{$pname . "_SENDSTAT"} && $clientmsg->{$pname . "_SENDSTAT"} eq "Ok") { if ( $clientmsg->{direction} eq "i2cread" && defined($clientmsg->{received}) ) { # =~ m/^[a-f0-9]{2}$/i) { #my @rec = @{$clientmsg->{received}}; #bei uebergabe im hash als array my @rec = split(" ",$clientmsg->{received}); #bei uebergabe im als skalar @@ -260,42 +374,42 @@ sub I2C_PCA9532_I2CRec($@) { # vom physical aufgerufen } else { readingsSingleUpdate($hash,"state", "transmission error", 1); Log3 $hash, 3, "$name: failure in message from $pname"; - Log3 $hash, 3,(defined($clientmsg->{direction}) ? "Direction: $clientmsg->{direction} " : "Direction: undef "). - (defined($clientmsg->{i2caddress}) ? "I2Caddress: $clientmsg->{i2caddress} " : "I2Caddress: undef "). - (defined($clientmsg->{reg}) ? "Register: $clientmsg->{reg} " : "Register: undef "). - (defined($clientmsg->{data}) ? "Data: $clientmsg->{data} " : "Data: undef "). - (defined($clientmsg->{received}) ? "received: $clientmsg->{received} " : "received: undef "); + Log3 $hash, 3,(defined($clientmsg->{direction}) ? "Direction: " . $clientmsg->{direction} : "Direction: undef"). + (defined($clientmsg->{i2caddress}) ? " I2Caddress: " . sprintf("0x%.2X", $clientmsg->{i2caddress}) : " I2Caddress: undef"). + (defined($clientmsg->{reg}) ? " Register: " . sprintf("0x%.2X", $clientmsg->{reg}) : " Register: undef"). + (defined($clientmsg->{data}) ? " Data: " . sprintf("0x%.2X", $clientmsg->{data}) : " Data: undef"). + (defined($clientmsg->{received}) ? " received: " . sprintf("0x%.2X", $clientmsg->{received}) : " received: undef"); } - } else { + } else { readingsSingleUpdate($hash,"state", "transmission error", 1); Log3 $hash, 3, "$name: failure in message from $pname"; - Log3 $hash, 3,(defined($clientmsg->{direction}) ? "Direction: $clientmsg->{direction} " : "Direction: undef "). - (defined($clientmsg->{i2caddress}) ? "I2Caddress: $clientmsg->{i2caddress} " : "I2Caddress: undef "). - (defined($clientmsg->{reg}) ? "Register: $clientmsg->{reg} " : "Register: undef "). - (defined($clientmsg->{data}) ? "Data: $clientmsg->{data} " : "Data: undef "). - (defined($clientmsg->{received}) ? "received: $clientmsg->{received} " : "received: undef "); + Log3 $hash, 3,(defined($clientmsg->{direction}) ? "Direction: " . $clientmsg->{direction} : "Direction: undef"). + (defined($clientmsg->{i2caddress}) ? " I2Caddress: " . sprintf("0x%.2X", $clientmsg->{i2caddress}) : " I2Caddress: undef"). + (defined($clientmsg->{reg}) ? " Register: " . sprintf("0x%.2X", $clientmsg->{reg}) : " Register: undef"). + (defined($clientmsg->{data}) ? " Data: " . sprintf("0x%.2X", $clientmsg->{data}) : " Data: undef"). + (defined($clientmsg->{received}) ? " received: " . sprintf("0x%.2X", $clientmsg->{received}) : " received: undef"); #my $cmsg = undef; #foreach my $av (keys %{$clientmsg}) { $cmsg .= "|" . $av . ": " . $clientmsg->{$av}; } #Log3 $hash, 3, $cmsg; } - #undef $clientmsg; + #undef $clientmsg; } ############################################################################### sub I2C_PCA9532_UpdReadings($$$) { my ($hash, $reg, $inh) = @_; my $name = $hash->{NAME}; #$inh = hex($inh); - Log3 $hash, 5, "$name UpdReadings Register: $reg, Inhalt: $inh"; + Log3 $hash, 5, "$name UpdReadings Register: " .sprintf("0x%.2X", $reg).", Inhalt: ".sprintf("0x%.2X", $inh); readingsBeginUpdate($hash); if ( $reg < 10 && $reg > 5) { #Wenn PortRegister - my %rsetsP = reverse %setsP; + my %rsetsP = reverse %setsP; my $LSreg = $reg - 6; #Nummer des entspechenden LS Registers my $n = $LSreg * 4; #Erster Port in LSx foreach (reverse 0..3) { #Ports aus Controlregister abarbeiten - my $pval = 3 & ( $inh >> ($_ * 2) ); + my $pval = 3 & ( $inh >> ($_ * 2) ); my $port = $_ + $n; readingsBulkUpdate($hash, 'Port'.$port , $rsetsP{$pval}) - if (ReadingsVal($name, 'Port'.$port,"nix") ne $rsetsP{$pval}); #nur wenn Wert geaendert + if (ReadingsVal($name, 'Port'.$port,"nix") ne $rsetsP{$pval}); #nur wenn Wert geaendert } } elsif ( $reg == 3) { #wenn PWM0 Register readingsBulkUpdate($hash, 'PWM0' , $inh); @@ -306,16 +420,14 @@ sub I2C_PCA9532_UpdReadings($$$) { } elsif ( $reg == 4) { #wenn Frequenz0 Register $hash->{Frequency_1} = sprintf( "%.3f", ( 152 / ($inh + 1) ) ) . " Hz"; } elsif ( $reg >= 0 && $reg < 2 ) { #Input Register - my $j = 8 * $reg; - Log3 $hash, 5, "Register $reg Inh: $inh"; - my @inports = sort(split( " ",AttrVal($name, "InputPorts", ""))); + my $j = 8 * $reg; + my @outports = sort(split( ",",AttrVal($name, "OutputPorts", ""))); for (my $i = 0; $i < 8; $i++) { - Log3 $hash, 5, "Register $reg Forschleife i: $i"; - if ( ($i + $j) ~~ @inports ) { #nur als Input definierte Ports aktualisieren + Log3 $hash, 5, "Register " .sprintf("0x%.2X", $reg)." Forschleife i: $i"; + unless ( ($i + $j) ~~ @outports ) { #nur als Input definierte Ports aktualisieren my $sval = $inh & (1 << $i); $sval = $sval == 0 ? "0" :"1"; readingsBulkUpdate($hash, 'Port'.($i + $j) , $sval) if (ReadingsVal($name,'Port'.($i + $j),2) ne $sval); - Log3 $hash, 5, "Register $reg wert: $sval"; } } } @@ -387,11 +499,16 @@ sub I2C_PCA9532_UpdReadings($$$) { Set the polling interval in minutes to query the GPIO's level
Default: -, valid values: decimal number

-
  • InputPorts
    - Space separated list of Portnumers that are used as Inputs
    - Ports in this list can't be written
    +
  • OutputPorts
    + Comma separated list of Portnumers that are used as Outputs
    + Only ports in this list can be written
    Default: no, valid values: 0 1 2 .. 15

  • +
  • OnStartup
    + Comma separated list of output ports/PWM registers and their desired state after start
    + Without this atribut all output ports will set to last state
    + Default: -, valid values: =on|off|PWM0|PWM1|last or PWM0|PWM1=0..255|last where = 0 - 15

    +
  • T0/T1
    Sets PWM0/PWM1 to another Frequency. The Formula is: Fx = 152/(Tx + 1) The corresponding frequency value is shown under internals.
    Default: 0 (152Hz), valid values: 0-255

    @@ -466,11 +583,16 @@ sub I2C_PCA9532_UpdReadings($$$) { Aktualisierungsintervall aller Werte in Minuten.
    Standard: -, gültige Werte: Dezimalzahl

  • -
  • InputPorts
    - Durch Leerzeichen getrennte Portnummern die als Inputs genutzt werden.
    - Ports in dieser Liste können nicht geschrieben werden.
    +
  • OutputPorts
    + Durch Komma getrennte Portnummern die als Outputs genutzt werden.
    + Nur Ports in dieser Liste können geschrieben werden.
    Standard: no, gültige Werte: 0 1 2 .. 15

  • +
  • OnStartup
    + Durch Komma getrennte Output Ports/PWM Register und ihr gewünschter Status nach dem Start.
    + Ohne dieses Attribut werden alle Ausgänge nach dem Start auf den letzten Status gesetzt.
    + Standard: -, gültige Werte: =on|off|PWM0|PWM1|last oder PWM0|PWM1=0..255|last wobei = 0 - 15

    +
  • T0/T1
    Änderung der Frequenzwerte von PWM0/PWM1 nach der Formel: Fx = 152/(Tx + 1). Der entsprechende Frequenzwert wird unter Internals angezeigt.
    Standard: 0 (152Hz), gültige Werte: 0-255