2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-04-20 07:16:03 +00:00

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
This commit is contained in:
klauswitt 2014-07-06 21:39:05 +00:00
parent 95de4abcf3
commit 9e17d08593

View File

@ -1,6 +1,5 @@
############################################################################## ##############################################################################
# $Id$ # $Id$
# 52_I2C_PCA9532.pm
# #
############################################################################## ##############################################################################
# Modul for I2C PWM Driver PCA9532 # Modul for I2C PWM Driver PCA9532
@ -11,18 +10,14 @@
# contributed by Klaus Wittstock (2014) email: klauswittstock bei gmail punkt com # 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: #Inhalte des Hashes:
#i2caddress 00-7F I2C-Adresse #i2caddress 00-127(7F) I2C-Adresse
#direction i2cread|i2cwrite Richtung #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) #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) #data 00-255 ... 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) #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 #pname_SENDSTAT Ok|error zeigt uebertragungserfolg an
package main; package main;
@ -31,7 +26,6 @@ use warnings;
use SetExtensions; use SetExtensions;
#use POSIX; #use POSIX;
use Scalar::Util qw(looks_like_number); use Scalar::Util qw(looks_like_number);
#use Error qw(:try);
my $setdim = ":slider,0,1,255 "; my $setdim = ":slider,0,1,255 ";
@ -43,26 +37,26 @@ my %setsP = (
); );
############################################################################### ###############################################################################
sub I2C_PCA9532_Initialize($) { sub I2C_PCA9532_Initialize($) {
my ($hash) = @_; my ($hash) = @_;
$hash->{DefFn} = "I2C_PCA9532_Define"; $hash->{DefFn} = "I2C_PCA9532_Define";
$hash->{InitFn} = 'I2C_PCA9532_Init'; $hash->{InitFn} = 'I2C_PCA9532_Init';
$hash->{UndefFn} = "I2C_PCA9532_Undefine"; $hash->{UndefFn} = "I2C_PCA9532_Undefine";
$hash->{AttrFn} = "I2C_PCA9532_Attr"; $hash->{AttrFn} = "I2C_PCA9532_Attr";
#$hash->{StateFn} = "I2C_PCA9532_SetState"; $hash->{StateFn} = "I2C_PCA9532_State";
$hash->{SetFn} = "I2C_PCA9532_Set"; $hash->{SetFn} = "I2C_PCA9532_Set";
$hash->{GetFn} = "I2C_PCA9532_Get"; $hash->{GetFn} = "I2C_PCA9532_Get";
$hash->{I2CRecFn} = "I2C_PCA9532_I2CRec"; $hash->{I2CRecFn} = "I2C_PCA9532_I2CRec";
$hash->{AttrList} = "IODev do_not_notify:1,0 ignore:1,0 showtime:1,0". $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 ". "poll_interval T0:slider,0,1,255 T1:slider,0,1,255 OutputPorts OnStartup InputPorts ".
"$readingFnAttributes"; "$readingFnAttributes";
} }
############################################################################### ###############################################################################
sub I2C_PCA9532_SetState($$$$) { sub I2C_PCA9532_SetState($$$$) {
my ($hash, $tim, $vt, $val) = @_; my ($hash, $tim, $vt, $val) = @_;
$val = $1 if($val =~ m/^(.*) \d+$/); $val = $1 if($val =~ m/^(.*) \d+$/);
#return "Undefined value $val" if(!defined($it_c2b{$val})); #return "Undefined value $val" if(!defined($it_c2b{$val}));
return undef; return undef;
} }
############################################################################### ###############################################################################
sub I2C_PCA9532_Define($$) { sub I2C_PCA9532_Define($$) {
@ -70,10 +64,10 @@ sub I2C_PCA9532_Define($$) {
my @a = split("[ \t]+", $def); my @a = split("[ \t]+", $def);
$hash->{STATE} = 'defined'; $hash->{STATE} = 'defined';
if ($main::init_done) { if ($main::init_done) {
eval { I2C_PCA9532_Init( $hash, [ @a[ 2 .. scalar(@a) - 1 ] ] ); }; eval { I2C_PCA9532_Init( $hash, [ @a[ 2 .. scalar(@a) - 1 ] ] ); };
return I2C_PCA9532_Catch($@) if $@; return I2C_PCA9532_Catch($@) if $@;
} }
return undef; return undef;
} }
############################################################################### ###############################################################################
sub I2C_PCA9532_Init($$) { sub I2C_PCA9532_Init($$) {
@ -82,169 +76,289 @@ sub I2C_PCA9532_Init($$) {
my $name = $hash->{NAME}; my $name = $hash->{NAME};
if (defined $args && int(@$args) != 1) { if (defined $args && int(@$args) != 1) {
return "Define: Wrong syntax. Usage:\n" . return "Define: Wrong syntax. Usage:\n" .
"define <name> I2C_PCA9532 <i2caddress>"; "define <name> I2C_PCA9532 <i2caddress>";
} }
#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)) { if (defined (my $address = shift @$args)) {
$hash->{I2C_Address} = $address =~ /^0.*$/ ? oct($address) : $address; $hash->{I2C_Address} = $address =~ /^0.*$/ ? oct($address) : $address;
} else { } else {
return "$name I2C Address not valid"; return "$name I2C Address not valid";
} }
AssignIoPort($hash);
#$hash->{I2C_Address} = hex($a[0]); #PWM Frequenzen wiederherstellen
AssignIoPort($hash); I2C_PCA9532_I2CSet($hash,AttrVal($name, "T0", 0),2);
$hash->{STATE} = 'Initialized'; I2C_PCA9532_I2CSet($hash,AttrVal($name, "T1", 0),4);
return; #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($) { sub I2C_PCA9532_Catch($) {
my $exception = shift; my $exception = shift;
if ($exception) { if ($exception) {
$exception =~ /^(.*)( at.*FHEM.*)$/; $exception =~ /^(.*)( at.*FHEM.*)$/;
return $1; return $1;
} }
return undef; 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($$) { sub I2C_PCA9532_Undefine($$) {
my ($hash, $arg) = @_; my ($hash, $arg) = @_;
if ( defined (AttrVal($hash->{NAME}, "poll_interval", undef)) ) { if ( defined (AttrVal($hash->{NAME}, "poll_interval", undef)) ) {
RemoveInternalTimer($hash); RemoveInternalTimer($hash);
} }
} }
############################################################################### ###############################################################################
sub I2C_PCA9532_Attr(@) { sub I2C_PCA9532_Attr(@) {
my (undef, $name, $attr, $val) = @_; my ($command, $name, $attr, $val) = @_;
my $hash = $defs{$name}; my $hash = $defs{$name};
my $msg = ''; my $msg = '';
if ($attr && $attr eq 'poll_interval') { if ($command && $command eq "set" && $attr && $attr eq "IODev") {
#my $pollInterval = (defined($val) && looks_like_number($val) && $val > 0) ? $val : 0; 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) ) { if (!defined($val) ) {
RemoveInternalTimer($hash); RemoveInternalTimer($hash);
} elsif ($val > 0) { } elsif ( looks_like_number($val) && $val > 0) {
RemoveInternalTimer($hash); RemoveInternalTimer($hash);
InternalTimer(1, 'I2C_PCA9532_Poll', $hash, 0); InternalTimer(1, 'I2C_PCA9532_Poll', $hash, 0);
} else { } else {
$msg = 'Wrong poll intervall defined. poll_interval must be a number > 0'; $msg = 'Wrong poll intervall defined. poll_interval must be a number > 0';
} }
} elsif ($attr && $attr =~ m/^T[0-1]$/i) { } elsif ($attr && $attr =~ m/^T[0-1]$/i) {
return "wrong value: $val for \"set $name $attr\" use 0-255" return "wrong value: $val for \"set $name $attr\" use 0-255"
unless(looks_like_number($val) && $val >= 0 && $val < 256); unless(looks_like_number($val) && $val >= 0 && $val < 256);
substr($attr,0,1,""); substr($attr,0,1,"");
my $regaddr = ($attr == 0 ? 2 : 4); my $regaddr = ($attr == 0 ? 2 : 4);
my %sendpackage = ( i2caddress => $hash->{I2C_Address}, direction => "i2cwrite" ); $msg = I2C_PCA9532_I2CSet($hash,$val,$regaddr);
#$sendpackage{data} = sprintf("%.2X", $val); ###################################################################irgendwann raus damit
#$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 eq "InputPorts") { } elsif ($attr && $attr eq "InputPorts") {
my @inp = split(" ", $val); my @inp = split(" ", $val) if defined($val);
my $oval = 65535;
foreach (@inp) { 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 <port>=on|off|PWM0|PWM1|last or PWM0|PWM1=0..255|last where <port> = 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($) { sub I2C_PCA9532_Poll($) {
my ($hash) = @_; my ($hash) = @_;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
# Read values # Read values
I2C_PCA9532_Get($hash, $name); I2C_PCA9532_Get($hash, $name);
my $pollInterval = AttrVal($hash->{NAME}, 'poll_interval', 0); my $pollInterval = AttrVal($hash->{NAME}, 'poll_interval', 0);
if ($pollInterval > 0) { if ($pollInterval > 0) {
InternalTimer(gettimeofday() + ($pollInterval * 60), 'I2C_PCA9532_Poll', $hash, 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($@) { sub I2C_PCA9532_Set($@) {
my ($hash, @a) = @_; my ($hash, @a) = @_;
my $name =$a[0]; my $name =$a[0];
my $cmd = $a[1]; my $cmd = $a[1];
my $val = $a[2]; my $val = $a[2];
my @inports = sort(split( " ",AttrVal($name, "InputPorts", ""))); my @outports = sort(split(/,/,AttrVal($name, "OutputPorts", "")));
unless (@a == 3) { #my @inports = sort(split( " ",AttrVal($name, "InputPorts", "")));
unless (@a == 3) {
} }
my %sendpackage = ( i2caddress => $hash->{I2C_Address}, direction => "i2cwrite" ); my $msg;
if ( $cmd && $cmd =~ m/^Port((0|)[0-9]|1[0-5])$/i) { #my %sendpackage = ( i2caddress => $hash->{I2C_Address}, direction => "i2cwrite" );
return "wrong value: $val for \"set $name $cmd\" use one of: " . 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) ) join(',', (sort { $setsP{ $a } <=> $setsP{ $b } } keys %setsP) )
unless(exists($setsP{$val})); unless(exists($setsP{$val}));
substr($cmd,0,4,""); substr($cmd,0,4,"");
return "$name error: Port$cmd is defined as input" if ( $cmd ~~ @inports ); #Pruefen ob entsprechender Port Input ist 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 $LSreg = int($cmd / 4); #Nummer des entspechenden LS Registers
my $regaddr = $LSreg + 6; #Adresse fuer Controlregister berechnen (LS0 = 0x06) my $regaddr = $LSreg + 6; #Adresse fuer Controlregister berechnen (LS0 = 0x06)
my $n = $LSreg * 4; #Erster Port in LSx my $n = $LSreg * 4; #Erster Port in LSx
my $sbyte = 0; my $sbyte = 0;
foreach (reverse 0..3) { #ensprechendes Controlregister fuellen foreach (reverse 0..3) { #ensprechendes Controlregister fuellen
my $portn = $_ + $n; my $portn = $_ + $n;
#hier noch alle inputs auf rezessiv setzen if (( $portn) == $cmd ) { #->wenn aktueller Port dann neuer Wert
if (( $portn) == $cmd ) { #->wenn aktueller Port dann neuer Wert
$sbyte += $setsP{$val} << (2 * $_); $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} = sprintf("%.2X",$sbyte);
$sendpackage{data} = $sbyte; #$sendpackage{data} = $sbyte;
#$sendpackage{reg} = sprintf("%.2X", $regaddr); #$sendpackage{reg} = sprintf("%.2X", $regaddr);
$sendpackage{reg} = $regaddr; #$sendpackage{reg} = $regaddr;
} elsif ($cmd && $cmd =~ m/^PWM[0-1]$/i) { } elsif ($cmd && $cmd =~ m/^PWM[0-1]$/i) {
return "wrong value: $val for \"set $name $cmd\" use 0-255" return "wrong value: $val for \"set $name $cmd\" use 0-255"
unless(looks_like_number($val) && $val >= 0 && $val < 256); unless(looks_like_number($val) && $val >= 0 && $val < 256);
substr($cmd,0,3,""); substr($cmd,0,3,"");
my $regaddr = ($cmd == 0 ? 3 : 5); my $regaddr = ($cmd == 0 ? 3 : 5);
#$sendpackage{data} = sprintf("%.2X", $val); $msg = I2C_PCA9532_I2CSet($hash,$val,$regaddr);
$sendpackage{data} = $val; #return $msg if $msg;
$sendpackage{reg} = sprintf("%.2X", $regaddr);
} else { #$sendpackage{data} = sprintf("%.2X", $val);
my $list = undef; #$sendpackage{data} = $val;
foreach (0..15) { #$sendpackage{reg} = sprintf("%.2X", $regaddr);
next if ( $_ ~~ @inports ); #Inputs ueberspringen
} else {
my $list = undef;
foreach (0..15) {
next unless ( $_ ~~ @outports ); #Inputs ueberspringen
#$list .= "Port" . $_ . ":" . join(',', sort keys %setsP) . " "; #$list .= "Port" . $_ . ":" . join(',', sort keys %setsP) . " ";
$list .= "Port" . $_ . ":" . join(',', (sort { $setsP{ $a } <=> $setsP{ $b } } keys %setsP) ) . " "; $list .= "Port" . $_ . ":" . join(',', (sort { $setsP{ $a } <=> $setsP{ $b } } keys %setsP) ) . " ";
} }
$list .= join($setdim, ("PWM0", "PWM1")) . $setdim; $list .= join($setdim, ("PWM0", "PWM1")) . $setdim;
return "Unknown argument $a[1], choose one of " . $list; $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($@) { sub I2C_PCA9532_Get($@) {
my ($hash, @a) = @_; my ($hash, @a) = @_;
my $name =$a[0]; my $name =$a[0];
my %sendpackage = ( i2caddress => $hash->{I2C_Address}, direction => "i2cread" ); my %sendpackage = ( i2caddress => $hash->{I2C_Address}, direction => "i2cread" );
$sendpackage{reg} = 0; #startadresse zum lesen $sendpackage{reg} = 0; #startadresse zum lesen
$sendpackage{nbyte} = 10; $sendpackage{nbyte} = 10;
return "$name: no IO device defined" unless ($hash->{IODev}); return "$name: no IO device defined" unless ($hash->{IODev});
my $phash = $hash->{IODev}; my $phash = $hash->{IODev};
my $pname = $phash->{NAME}; my $pname = $phash->{NAME};
CallFn($pname, "I2CWrtFn", $phash, \%sendpackage); CallFn($pname, "I2CWrtFn", $phash, \%sendpackage);
} }
############################################################################### ###############################################################################
sub I2C_PCA9532_I2CRec($@) { # vom physical aufgerufen sub I2C_PCA9532_I2CRec($@) { # vom physical aufgerufen
my ($hash, $clientmsg) = @_; my ($hash, $clientmsg) = @_;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
my $phash = $hash->{IODev}; my $phash = $hash->{IODev};
my $pname = $phash->{NAME}; my $pname = $phash->{NAME};
while ( my ( $k, $v ) = each %$clientmsg ) { #erzeugen von Internals fuer alle Keys in $clientmsg die mit dem physical Namen beginnen 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/ ; $hash->{$k} = $v if $k =~ /^$pname/ ;
} }
#hier noch ueberpruefen, ob Register und Daten ok #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) { 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 = @{$clientmsg->{received}}; #bei uebergabe im hash als array
my @rec = split(" ",$clientmsg->{received}); #bei uebergabe im als skalar my @rec = split(" ",$clientmsg->{received}); #bei uebergabe im als skalar
@ -260,42 +374,42 @@ sub I2C_PCA9532_I2CRec($@) { # vom physical aufgerufen
} else { } else {
readingsSingleUpdate($hash,"state", "transmission error", 1); readingsSingleUpdate($hash,"state", "transmission error", 1);
Log3 $hash, 3, "$name: failure in message from $pname"; Log3 $hash, 3, "$name: failure in message from $pname";
Log3 $hash, 3,(defined($clientmsg->{direction}) ? "Direction: $clientmsg->{direction} " : "Direction: undef "). Log3 $hash, 3,(defined($clientmsg->{direction}) ? "Direction: " . $clientmsg->{direction} : "Direction: undef").
(defined($clientmsg->{i2caddress}) ? "I2Caddress: $clientmsg->{i2caddress} " : "I2Caddress: undef "). (defined($clientmsg->{i2caddress}) ? " I2Caddress: " . sprintf("0x%.2X", $clientmsg->{i2caddress}) : " I2Caddress: undef").
(defined($clientmsg->{reg}) ? "Register: $clientmsg->{reg} " : "Register: undef "). (defined($clientmsg->{reg}) ? " Register: " . sprintf("0x%.2X", $clientmsg->{reg}) : " Register: undef").
(defined($clientmsg->{data}) ? "Data: $clientmsg->{data} " : "Data: undef "). (defined($clientmsg->{data}) ? " Data: " . sprintf("0x%.2X", $clientmsg->{data}) : " Data: undef").
(defined($clientmsg->{received}) ? "received: $clientmsg->{received} " : "received: undef "); (defined($clientmsg->{received}) ? " received: " . sprintf("0x%.2X", $clientmsg->{received}) : " received: undef");
} }
} else { } else {
readingsSingleUpdate($hash,"state", "transmission error", 1); readingsSingleUpdate($hash,"state", "transmission error", 1);
Log3 $hash, 3, "$name: failure in message from $pname"; Log3 $hash, 3, "$name: failure in message from $pname";
Log3 $hash, 3,(defined($clientmsg->{direction}) ? "Direction: $clientmsg->{direction} " : "Direction: undef "). Log3 $hash, 3,(defined($clientmsg->{direction}) ? "Direction: " . $clientmsg->{direction} : "Direction: undef").
(defined($clientmsg->{i2caddress}) ? "I2Caddress: $clientmsg->{i2caddress} " : "I2Caddress: undef "). (defined($clientmsg->{i2caddress}) ? " I2Caddress: " . sprintf("0x%.2X", $clientmsg->{i2caddress}) : " I2Caddress: undef").
(defined($clientmsg->{reg}) ? "Register: $clientmsg->{reg} " : "Register: undef "). (defined($clientmsg->{reg}) ? " Register: " . sprintf("0x%.2X", $clientmsg->{reg}) : " Register: undef").
(defined($clientmsg->{data}) ? "Data: $clientmsg->{data} " : "Data: undef "). (defined($clientmsg->{data}) ? " Data: " . sprintf("0x%.2X", $clientmsg->{data}) : " Data: undef").
(defined($clientmsg->{received}) ? "received: $clientmsg->{received} " : "received: undef "); (defined($clientmsg->{received}) ? " received: " . sprintf("0x%.2X", $clientmsg->{received}) : " received: undef");
#my $cmsg = undef; #my $cmsg = undef;
#foreach my $av (keys %{$clientmsg}) { $cmsg .= "|" . $av . ": " . $clientmsg->{$av}; } #foreach my $av (keys %{$clientmsg}) { $cmsg .= "|" . $av . ": " . $clientmsg->{$av}; }
#Log3 $hash, 3, $cmsg; #Log3 $hash, 3, $cmsg;
} }
#undef $clientmsg; #undef $clientmsg;
} }
############################################################################### ###############################################################################
sub I2C_PCA9532_UpdReadings($$$) { sub I2C_PCA9532_UpdReadings($$$) {
my ($hash, $reg, $inh) = @_; my ($hash, $reg, $inh) = @_;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
#$inh = hex($inh); #$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); readingsBeginUpdate($hash);
if ( $reg < 10 && $reg > 5) { #Wenn PortRegister 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 $LSreg = $reg - 6; #Nummer des entspechenden LS Registers
my $n = $LSreg * 4; #Erster Port in LSx my $n = $LSreg * 4; #Erster Port in LSx
foreach (reverse 0..3) { #Ports aus Controlregister abarbeiten foreach (reverse 0..3) { #Ports aus Controlregister abarbeiten
my $pval = 3 & ( $inh >> ($_ * 2) ); my $pval = 3 & ( $inh >> ($_ * 2) );
my $port = $_ + $n; my $port = $_ + $n;
readingsBulkUpdate($hash, 'Port'.$port , $rsetsP{$pval}) 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 } elsif ( $reg == 3) { #wenn PWM0 Register
readingsBulkUpdate($hash, 'PWM0' , $inh); readingsBulkUpdate($hash, 'PWM0' , $inh);
@ -306,16 +420,14 @@ sub I2C_PCA9532_UpdReadings($$$) {
} elsif ( $reg == 4) { #wenn Frequenz0 Register } elsif ( $reg == 4) { #wenn Frequenz0 Register
$hash->{Frequency_1} = sprintf( "%.3f", ( 152 / ($inh + 1) ) ) . " Hz"; $hash->{Frequency_1} = sprintf( "%.3f", ( 152 / ($inh + 1) ) ) . " Hz";
} elsif ( $reg >= 0 && $reg < 2 ) { #Input Register } elsif ( $reg >= 0 && $reg < 2 ) { #Input Register
my $j = 8 * $reg; my $j = 8 * $reg;
Log3 $hash, 5, "Register $reg Inh: $inh"; my @outports = sort(split( ",",AttrVal($name, "OutputPorts", "")));
my @inports = sort(split( " ",AttrVal($name, "InputPorts", "")));
for (my $i = 0; $i < 8; $i++) { for (my $i = 0; $i < 8; $i++) {
Log3 $hash, 5, "Register $reg Forschleife i: $i"; Log3 $hash, 5, "Register " .sprintf("0x%.2X", $reg)." Forschleife i: $i";
if ( ($i + $j) ~~ @inports ) { #nur als Input definierte Ports aktualisieren unless ( ($i + $j) ~~ @outports ) { #nur als Input definierte Ports aktualisieren
my $sval = $inh & (1 << $i); my $sval = $inh & (1 << $i);
$sval = $sval == 0 ? "0" :"1"; $sval = $sval == 0 ? "0" :"1";
readingsBulkUpdate($hash, 'Port'.($i + $j) , $sval) if (ReadingsVal($name,'Port'.($i + $j),2) ne $sval); 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<br> Set the polling interval in minutes to query the GPIO's level<br>
Default: -, valid values: decimal number<br><br> Default: -, valid values: decimal number<br><br>
</li> </li>
<li>InputPorts<br> <li>OutputPorts<br>
Space separated list of Portnumers that are used as Inputs<br> Comma separated list of Portnumers that are used as Outputs<br>
Ports in this list can't be written<br> Only ports in this list can be written<br>
Default: no, valid values: 0 1 2 .. 15<br><br> Default: no, valid values: 0 1 2 .. 15<br><br>
</li> </li>
<li>OnStartup<br>
Comma separated list of output ports/PWM registers and their desired state after start<br>
Without this atribut all output ports will set to last state<br>
Default: -, valid values: <port>=on|off|PWM0|PWM1|last or PWM0|PWM1=0..255|last where <port> = 0 - 15<br><br>
</li>
<li>T0/T1<br> <li>T0/T1<br>
Sets PWM0/PWM1 to another Frequency. The Formula is: Fx = 152/(Tx + 1) The corresponding frequency value is shown under internals.<br> Sets PWM0/PWM1 to another Frequency. The Formula is: Fx = 152/(Tx + 1) The corresponding frequency value is shown under internals.<br>
Default: 0 (152Hz), valid values: 0-255<br><br> Default: 0 (152Hz), valid values: 0-255<br><br>
@ -466,11 +583,16 @@ sub I2C_PCA9532_UpdReadings($$$) {
Aktualisierungsintervall aller Werte in Minuten.<br> Aktualisierungsintervall aller Werte in Minuten.<br>
Standard: -, g&uuml;ltige Werte: Dezimalzahl<br><br> Standard: -, g&uuml;ltige Werte: Dezimalzahl<br><br>
</li> </li>
<li>InputPorts<br> <li>OutputPorts<br>
Durch Leerzeichen getrennte Portnummern die als Inputs genutzt werden.<br> Durch Komma getrennte Portnummern die als Outputs genutzt werden.<br>
Ports in dieser Liste k&ouml;nnen nicht geschrieben werden.<br> Nur Ports in dieser Liste k&ouml;nnen geschrieben werden.<br>
Standard: no, g&uuml;ltige Werte: 0 1 2 .. 15<br><br> Standard: no, g&uuml;ltige Werte: 0 1 2 .. 15<br><br>
</li> </li>
<li>OnStartup<br>
Durch Komma getrennte Output Ports/PWM Register und ihr gew&uuml;nschter Status nach dem Start.<br>
Ohne dieses Attribut werden alle Ausg&auml;nge nach dem Start auf den letzten Status gesetzt.<br>
Standard: -, g&uuml;ltige Werte: <port>=on|off|PWM0|PWM1|last oder PWM0|PWM1=0..255|last wobei <port> = 0 - 15<br><br>
</li>
<li>T0/T1<br> <li>T0/T1<br>
&Auml;nderung der Frequenzwerte von PWM0/PWM1 nach der Formel: Fx = 152/(Tx + 1). Der entsprechende Frequenzwert wird unter Internals angezeigt.<br> &Auml;nderung der Frequenzwerte von PWM0/PWM1 nach der Formel: Fx = 152/(Tx + 1). Der entsprechende Frequenzwert wird unter Internals angezeigt.<br>
Standard: 0 (152Hz), g&uuml;ltige Werte: 0-255<br><br> Standard: 0 (152Hz), g&uuml;ltige Werte: 0-255<br><br>