diff --git a/fhem/CHANGED b/fhem/CHANGED index 8f5c7fbf7..f5adcc4fb 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,6 @@ # Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Do not insert empty lines here, update check depends on it. + - feature: new module 52_I2C_MCP342x.pm added (klausw) - feature: SYSMON: read cpu temp on FritzBox - feature: ios7smallscreenstyle.css: table width based on screen width, new header, links colored in detail view diff --git a/fhem/FHEM/00_RPII2C.pm b/fhem/FHEM/00_RPII2C.pm index 8fcc94b89..da728985a 100644 --- a/fhem/FHEM/00_RPII2C.pm +++ b/fhem/FHEM/00_RPII2C.pm @@ -13,7 +13,7 @@ my @clients = qw( I2C_LCD I2C_DS1307 I2C_PC.* -I2C_MCP23017 +I2C_MCP.* I2C_BMP180 I2C_SHT21 I2C_TSL2561 @@ -172,7 +172,7 @@ sub RPII2C_Set($@) { #writeBlock noch nicht fertig my ($hash, @a) = @_; my $name = shift @a; my $type = shift @a; - my @sets = ('writeByte', 'writeByteReg', 'writeBlock'); #, 'writeNBlock'); + my @sets = ('writeByte', 'writeByteReg', 'writeBlock', 'writeBlockReg'); #, 'writeNBlock'); return "Unknown argument $type, choose one of " . join(" ", @sets) if @a < 2; foreach (@a) { #Hexwerte pruefen und in Dezimalwerte wandeln @@ -182,7 +182,7 @@ sub RPII2C_Set($@) { #writeBlock noch nicht fertig my $i2ca = shift @a; return "$name: I2C Address not valid" unless ($i2ca > 3 && $i2ca < 128); #pruefe auf Hexzahl zwischen 4 und 7F - my $i2chash = { i2caddress => $i2ca, direction => "i2cwrite" }; + my $i2chash = { i2caddress => $i2ca, direction => "i2cbytewrite", test => "local" }; my ($reg, $nbyte, $data) = undef; if ($type eq "writeByte") { $data = join(" ", @a); @@ -190,21 +190,17 @@ sub RPII2C_Set($@) { #writeBlock noch nicht fertig $reg = shift @a; $data = join(" ", @a); } elsif ($type eq "writeBlock") { + $nbyte = int(@a); + return "$name maximal blocksize (32byte) exeeded" if $nbyte > 32; + $data = join(" ", @a); + $i2chash->{direction} = "i2cwrite"; + } elsif ($type eq "writeBlockReg") { $reg = shift @a; $nbyte = int(@a); return "$name maximal blocksize (32byte) exeeded" if $nbyte > 32; $data = join(" ", @a); - $i2chash->{direction} = "i2cblockwrite"; -#####kommt weg da sinnlos??!!! Achtung $nbyte stimmt derzeit nicht -# } elsif ($type eq "writeNBlock") { -# $reg = shift @a; -# return "$name register address must be a hexvalue" if (!defined($reg) || $reg !~ /^(0x|)[0-9A-F]{1,4}$/xi); -# $nbyte = shift @a; -# return "$name number of bytes must be decimal value" if (!defined($nbyte) || $nbyte !~ /^[0-9]{1,2}$/); -# return "$name data values must be n times number of bytes" if (int(@a) % $nbyte != 0); -# $data = join(" ", @a); -######################################################################### - }else { + $i2chash->{direction} = "i2cwrite"; + } else { return "Unknown argument $type, choose one of " . join(" ", @sets); } @@ -215,12 +211,12 @@ sub RPII2C_Set($@) { #writeBlock noch nicht fertig undef $i2chash; #Hash loeschen return undef; } -##################################### fertig? +##################################### nicht fertig! sub RPII2C_Get($@) { # my ($hash, @a) = @_; my $nargs = int(@a); my $name = $hash->{NAME}; - my @gets = ('read'); + my @gets = ('read','readblock','readblockreg'); unless ( exists($a[1]) && $a[1] ne "?" && grep {/^$a[1]$/} @gets ) { return "Unknown argument $a[1], choose one of " . join(" ", @gets); } @@ -229,7 +225,7 @@ sub RPII2C_Get($@) { # return "$name: I2C Address not valid" unless ( $a[2] =~ /^(0x|)([0-7]|)[0-9A-F]$/xi); return "$name register address must be a hexvalue" if (defined($a[3]) && $a[3] !~ /^(0x|)[0-9A-F]{1,4}$/xi); return "$name number of bytes must be decimal value" if (defined($a[4]) && $a[4] !~ /^[0-9]{1,2}$/); - my $i2chash = { i2caddress => hex($a[2]), direction => "i2cread" }; + my $i2chash = { i2caddress => hex($a[2]), direction => "i2cbyteread" }; $i2chash->{reg} = hex($a[3]) if defined($a[3]); #startadresse zum lesen $i2chash->{nbyte} = $a[4] if defined($a[4]); #Log3 $hash, 1, "Reg: ". $i2chash->{reg}; @@ -239,7 +235,29 @@ sub RPII2C_Get($@) { # my $received = $i2chash->{received}; #als Scalar undef $i2chash; #Hash loeschen return (defined($received) ? "received : " . $received ." | " : "" ) . " transmission: $status"; - } + } elsif ($a[1] eq "readblock") { + return "use: \"get $name $a[1] []\"" if(@a < 3); + return "$name: I2C Address not valid" unless ( $a[2] =~ /^(0x|)([0-7]|)[0-9A-F]$/xi); + return "$name number of bytes must be decimal value" if (defined($a[3]) && $a[3] !~ /^[0-9]{1,2}$/); + my $i2chash = { i2caddress => hex($a[2]), direction => "i2cread" }; + $i2chash->{nbyte} = $a[3] if defined($a[3]); + my $status = &{$hash->{hwfn}}($hash, $i2chash); + my $received = $i2chash->{received}; #als Scalar + undef $i2chash; #Hash loeschen + return (defined($received) ? "received : " . $received ." | " : "" ) . " transmission: $status"; + } elsif ($a[1] eq "readblockreg") { + return "use: \"get $name $a[1] []\"" if(@a < 2); + return "$name: I2C Address not valid" unless ( $a[2] =~ /^(0x|)([0-7]|)[0-9A-F]$/xi); + return "$name register address must be a hexvalue" if (defined($a[3]) && $a[3] !~ /^(0x|)[0-9A-F]{1,4}$/xi); + return "$name number of bytes must be decimal value" if (defined($a[4]) && $a[4] !~ /^[0-9]{1,2}$/); + my $i2chash = { i2caddress => hex($a[2]), direction => "i2cread" }; + $i2chash->{reg} = hex($a[3]) if defined($a[3]); + $i2chash->{nbyte} = $a[4] if defined($a[4]); + my $status = &{$hash->{hwfn}}($hash, $i2chash); + my $received = $i2chash->{received}; #als Scalar + undef $i2chash; #Hash loeschen + return (defined($received) ? "received : " . $received ." | " : "" ) . " transmission: $status"; + } return undef; } ##################################### @@ -262,7 +280,7 @@ sub RPII2C_Write($$) { #wird vom Client aufgerufen && defined( $main::defs{$d}{I2C_Address} ) && defined($clientmsg->{i2caddress}) && $main::defs{$d}{I2C_Address} eq $clientmsg->{i2caddress} ) { my $chash = $main::defs{$d}; - Log3 $hash, 5, "$name ->Client gefunden: $d". ($main::defs{$d}{I2C_Address} ? ", I2Caddress: $main::defs{$d}{I2C_Address}":"") . ($clientmsg->{data} ? " Data: $clientmsg->{data}" : ""); + Log3 $hash, 5, "$name ->Client gefunden: $d". ($main::defs{$d}{I2C_Address} ? ", I2Caddress: $main::defs{$d}{I2C_Address}":"") . ($clientmsg->{data} ? " Data: $clientmsg->{data}" : "") . ($clientmsg->{received} ? " Gelesen: $clientmsg->{received}" : ""); CallFn($d, "I2CRecFn", $chash, $clientmsg); undef $clientmsg #Hash loeschen nachdem Daten verteilt wurden } @@ -347,25 +365,16 @@ sub RPII2C_HWACCESS($$) { I2CBusDevicePath => $hash->{DeviceName}, I2CDeviceAddress => hex( sprintf("%.2X", $clientmsg->{i2caddress}) ), ); - if (defined($clientmsg->{nbyte}) && defined($clientmsg->{reg}) && defined($clientmsg->{data}) && $clientmsg->{direction} eq "i2cblockwrite") { #Registerblock beschreiben +# if (defined($clientmsg->{nbyte}) && defined($clientmsg->{reg}) && defined($clientmsg->{data}) && $clientmsg->{direction} eq "i2cblockwrite") { #blockweise beschreiben (Register) + if ( defined($clientmsg->{reg}) && defined($clientmsg->{data}) && $clientmsg->{direction} eq "i2cwrite") { #blockweise beschreiben (Register) my @data = split(" ", $clientmsg->{data}); my $dataref = \@data; $inh = $dev->writeBlockData( $clientmsg->{reg} , $dataref ); my $wr = join(" ", @{$dataref}); Log3 $hash, 5, "$hash->{NAME}: Block schreiben Register: " . sprintf("0x%.2X", $clientmsg->{reg}) . " Inhalt: " . $wr . " N: ". int(@data) ." Returnvar.: $inh"; $status = "Ok" if $inh == 0; -#kommt wieder weg################# -# } elsif (defined($clientmsg->{nbyte}) && defined($clientmsg->{reg}) && defined($clientmsg->{data}) && $clientmsg->{direction} eq "i2cwrite") { #Registerbereich (mehrfach) beschreiben -# my @data = split(" ", $clientmsg->{data}); -# foreach (0..$#data) { -# my $i = $_ -( int($_ / $clientmsg->{nbyte}) * $clientmsg->{nbyte} ); -# $inh = $dev->writeByteData( ($clientmsg->{reg} + $i ) ,$data[$_]); -# Log3 $hash, 5, "$hash->{NAME} NReg schreiben; Reg: " . ($clientmsg->{reg} + $i) . " Inh: " . $data[$_] . " Returnvar.: $inh"; -# last if $inh != 0; -# $status = "Ok" if $inh == 0; -# } -#hier Mehrfachbeschreibung eines Registers noch entfernen und dafuer Bereich mit Registeroperationen beschreiben - } elsif (defined($clientmsg->{reg}) && defined($clientmsg->{data}) && $clientmsg->{direction} eq "i2cwrite") { #Register beschreiben +# } elsif (defined($clientmsg->{reg}) && defined($clientmsg->{data}) && $clientmsg->{direction} eq "i2cwrite") { #byteweise beschreiben (Register) + } elsif (defined($clientmsg->{reg}) && defined($clientmsg->{data}) && $clientmsg->{direction} eq "i2cbytewrite") { #byteweise beschreiben (Register) my @data = split(" ", $clientmsg->{data}); foreach (@data) { $inh = $dev->writeByteData($clientmsg->{reg},$_); @@ -373,7 +382,7 @@ sub RPII2C_HWACCESS($$) { last if $inh != 0; $status = "Ok" if $inh == 0; } - } elsif (defined($clientmsg->{data}) && $clientmsg->{direction} eq "i2cwrite") { #Byte(s) schreiben + } elsif (defined($clientmsg->{data}) && ( $clientmsg->{direction} eq "i2cwrite" || $clientmsg->{direction} eq "i2cbytewrite" ) ) { #Byte(s) schreiben my @data = split(" ", $clientmsg->{data}); foreach (@data) { $inh = $dev->writeByte($_); @@ -381,7 +390,7 @@ sub RPII2C_HWACCESS($$) { last if $inh != 0; $status = "Ok" if $inh == 0; } - } elsif (defined($clientmsg->{reg}) && $clientmsg->{direction} eq "i2cread") { #Register lesen + } elsif (defined($clientmsg->{reg}) && ( $clientmsg->{direction} eq "i2cread" || $clientmsg->{direction} eq "i2cbyteread" ) ) { #byteweise lesen (Register) my $nbyte = defined($clientmsg->{nbyte}) ? $clientmsg->{nbyte} : 1; my $rmsg = ""; for (my $n = 0; $n < $nbyte; $n++) { @@ -395,7 +404,7 @@ sub RPII2C_HWACCESS($$) { } #@{$clientmsg->{received}} = split(" ", $rmsg) if($rmsg); #Daten als Array uebertragen $clientmsg->{received} = $rmsg if($rmsg); #Daten als Scalar uebertragen - } elsif ($clientmsg->{direction} eq "i2cread") { #Byte lesen #Byte lesen + } elsif ($clientmsg->{direction} eq "i2cread"|| $clientmsg->{direction} eq "i2cbyteread") { #Byte lesen my $nbyte = defined($clientmsg->{nbyte}) ? $clientmsg->{nbyte} : 1; my $rmsg = ""; for (my $n = 0; $n < $nbyte; $n++) { @@ -425,43 +434,46 @@ sub RPII2C_HWACCESS_ioctl($$) { my $ankommen = "$hash->{NAME}: vom client empfangen"; foreach my $av (keys %{$clientmsg}) { $ankommen .= "|" . $av . ": " . $clientmsg->{$av}; } Log3 $hash, 5, $ankommen; + #Log3 $hash, 1, $ankommen if $clientmsg->{test} eq "local"; my $i2caddr = hex(sprintf "%x", $clientmsg->{i2caddress}); - if ( sysopen(my $fh, $hash->{DeviceName}, O_RDWR) != 1) { #Datei oeffnen + if ( sysopen(my $fh, $hash->{DeviceName}, O_RDWR) != 1) { #Datei oeffnen Log3 $hash, 3, "$hash->{NAME}: HWaccess sysopen failure: $!" - } elsif( not defined( ioctl($fh,$I2C_SLAVE,$i2caddr) ) ) { #I2C Adresse per ioctl setzen + } elsif( not defined( ioctl($fh,$I2C_SLAVE,$i2caddr) ) ) { #I2C Adresse per ioctl setzen Log3 $hash, 3, "$hash->{NAME}: HWaccess (0x".unpack( "H2",pack "C", $clientmsg->{i2caddress}).") ioctl failure: $!" - } elsif (defined($clientmsg->{nbyte}) && defined($clientmsg->{reg}) && defined($clientmsg->{data}) && $clientmsg->{direction} eq "i2cblockwrite") { #Registerblock beschreiben - my $data = chr($clientmsg->{reg}); +# } elsif (defined($clientmsg->{nbyte}) && defined($clientmsg->{reg}) && defined($clientmsg->{data}) && $clientmsg->{direction} eq "i2cblockwrite") { #blockweise schreiben + } elsif ( defined($clientmsg->{data}) && $clientmsg->{direction} eq "i2cwrite") { #blockweise schreiben + my $data = defined($clientmsg->{reg}) ? chr($clientmsg->{reg}) : undef; foreach (split(" ", $clientmsg->{data})) { $data .= chr($_); } my $retval = syswrite($fh, $data, length($data)); unless (defined($retval) && $retval == length($data)) { - Log3 $hash, 3, "$hash->{NAME}: HWaccess blockweise nach 0x".unpack( "H2",pack "C", $clientmsg->{i2caddress})." schreiben, Reg: 0x". unpack( "H2",pack "C", $clientmsg->{reg}) . " Inh: $clientmsg->{data}, laenge: ".length($data)."| -> syswrite failure: $!"; + Log3 $hash, 3, "$hash->{NAME}: HWaccess blockweise nach 0x".unpack( "H2",pack "C", $clientmsg->{i2caddress})." schreiben, " . (defined($clientmsg->{reg}) ? "Reg: 0x". unpack( "H2",pack "C", $clientmsg->{reg}) : "") . " Inh: $clientmsg->{data}, laenge: ".length($data)."| -> syswrite failure: $!"; } else { $status = "Ok"; - Log3 $hash, 5, "$hash->{NAME}: HWaccess block schreiben, Reg: 0x". unpack( "H2",pack "C", $clientmsg->{reg}) . " Inh(dec):|$clientmsg->{data}|, laenge: |".length($data)."|"; + Log3 $hash, 5, "$hash->{NAME}: HWaccess block schreiben, " . (defined($clientmsg->{reg}) ? "Reg: 0x". unpack( "H2",pack "C", $clientmsg->{reg}) : "") . " Inh(dec):|$clientmsg->{data}|, laenge: |".length($data)."|"; } - #(my $datah = $data) =~ s/(.|\n)/sprintf("%.2X ",ord($1))/eg; - #Log3 $hash, 1, "$hash->{NAME}: HWaccess block schreiben data:|$clientmsg->{data}|, laenge: |".length($data)."|"; - #$status = "Ok" if $resulw == length($data); - } elsif (defined($clientmsg->{data}) && $clientmsg->{direction} eq "i2cwrite") { #byteweise beschreiben - my $reg = undef; +# } elsif (defined($clientmsg->{data}) && $clientmsg->{direction} eq "i2cwrite") { #byteweise schreiben + } elsif (defined($clientmsg->{data}) && $clientmsg->{direction} eq "i2cbytewrite") { #byteweise schreiben + my $reg = undef; $reg = $clientmsg->{reg} if (defined($clientmsg->{reg})); $status = "Ok"; foreach (split(" ", $clientmsg->{data})) { my $data = (defined($reg) ? chr($reg++) : "") . chr($_); my $retval = syswrite($fh, $data, length($data)); + #Log3 $hash, 1, "retval= $retval" if $clientmsg->{test} eq "local"; unless (defined($retval) && $retval == length($data)) { Log3 $hash, 3, "$hash->{NAME}: HWaccess byteweise nach 0x".unpack( "H2",pack "C", $clientmsg->{i2caddress})." schreiben, ". (defined($reg) ? "Reg: 0x". unpack( "H2",pack "C", ($reg - 1)) . " " : "")."Inh: 0x" . unpack( "H2",pack "C", $_) .", laenge: ".length($data)."| -> syswrite failure: $!"; $status = "error"; last; } - Log3 $hash, 5, "$hash->{NAME}: HWaccess byteweise schreiben, ". (defined($reg) ? "Reg: 0x". unpack( "H2",pack "C", ($reg - 1)) . " " : "")."Inh: 0x" . unpack( "H2",pack "C", $_) .", laenge: ".length($data); + Log3 $hash, 5, "$hash->{NAME}: HWaccess byteweise schreiben, ". (defined($reg) ? "Reg: 0x". unpack( "H2",pack "C", ($reg - 1)) . " " : "")."Inh: 0x" . unpack( "H2",pack "C", $_) .", laenge: ".length($data); + #Log3 $hash, 1, "$hash->{NAME}: HWaccess byteweise zu 0x".unpack( "H2",pack "C", $clientmsg->{i2caddress})." schreiben, ". (defined($reg) ? "Reg: 0x". unpack( "H2",pack "C", ($reg - 1)) . " " : "")."Inh: 0x" . unpack( "H2",pack "C", $_) .", laenge: ".length($data) if $clientmsg->{test} eq "local"; } - } elsif ($clientmsg->{direction} eq "i2cread") { #vom I2C lesen +# } elsif ($clientmsg->{direction} eq "i2cread") { #byteweise lesen + } elsif ($clientmsg->{direction} eq "i2cbyteread") { #byteweise lesen my $nbyte = defined($clientmsg->{nbyte}) ? $clientmsg->{nbyte} : 1; my $rmsg = ""; foreach (my $n = 0; $n < $nbyte; $n++) { @@ -483,7 +495,34 @@ sub RPII2C_HWACCESS_ioctl($$) { $rmsg .= " " if $n <= $nbyte; $status = "Ok" if ($n + 1) == $nbyte; } - $clientmsg->{received} = $rmsg if($rmsg); #Daten als Scalar uebertragen + $clientmsg->{received} = $rmsg if($rmsg); #Daten als Scalar uebertragen +# } elsif ($clientmsg->{direction} eq "i2cblockread") { #blockweise lesen + } elsif ($clientmsg->{direction} eq "i2cread") { #blockweise lesen + my $nbyte = defined($clientmsg->{nbyte}) ? $clientmsg->{nbyte} : 1; + #Log3 $hash, 1, "test Blockweise lese menge: |$nbyte|, reg: |". $clientmsg->{reg} ."|"; + my $rmsg = ""; + if ( defined($clientmsg->{reg}) ) { + Log3 $hash, 4, "$hash->{NAME}: HWaccess blockweise lesen setze Registerpointer auf " . ($clientmsg->{reg}); + my $retval = syswrite($fh, chr($clientmsg->{reg}), 1); + unless (defined($retval) && $retval == 1) { + Log3 $hash, 3, "$hash->{NAME}: HWaccess blockweise von 0x".unpack( "H2",pack "C", $clientmsg->{i2caddress})." lesen,". (defined($clientmsg->{reg}) ? " Reg: 0x". unpack( "H2",pack "C", ($clientmsg->{reg})) : "") . " -> syswrite failure: $!" if $!; + last; + } + } + my $buf = undef; + my $retval = sysread($fh, $buf, $nbyte); + #Log3 $hash, 1, "test Blockweise lesen menge: |$nbyte|, return: |$retval|, inh: |$buf|"; + unless (defined($retval) && $retval == $nbyte) { + Log3 $hash, 3, "$hash->{NAME}: HWaccess blockweise von 0x".unpack( "H2",pack "C", $clientmsg->{i2caddress})." lesen,". (defined($clientmsg->{reg}) ? " Reg: 0x". unpack( "H2",pack "C", ($clientmsg->{reg})) : "") . " -> sysread failure: $!" if $!; + last; + } else { + $status = "Ok" + } + #Log3 $hash, 1, "test Blockweise lesen menge: |$nbyte|, inh: $buf"; + $rmsg = $buf; + $rmsg =~ s/(.|\n)/sprintf("%u ",ord($1))/eg; + #Log3 $hash, 1, "test Blockweise lesen ergebnis: |$rmsg|"; + $clientmsg->{received} = $rmsg if($rmsg); #Daten als Scalar uebertragen } $hash->{STATE} = $status; $hash->{ERRORCNT} = defined($hash->{ERRORCNT}) ? $hash->{ERRORCNT} += 1 : 1 if $status ne "Ok"; @@ -540,7 +579,7 @@ sub RPII2C_HWACCESS_ioctl($$) {
  • - Optional: access via IOCTL will be used if Device::SMBus is not present.
    + Optional: access via IOCTL will be used (RECOMMENDED) if Device::SMBus is not present.
    To access the I2C-Bus via the Device::SMBus module, following steps are necessary:
      sudo apt-get install libmoose-perl
      sudo cpan Device::SMBus

    @@ -567,11 +606,15 @@ sub RPII2C_HWACCESS_ioctl($$) {
  • Write n-bytes to an register range (as an series of single register write operations), beginning at the specified register:
    - set <name> writeByteReg <I2C Address> <Register Address> <value>

    + set <name> writeByteReg <I2C Address> <Register Address> <value> [<value> [..]]

    +
  • +
  • + Write n-bytes directly to an I2C device (as an block write operation):
    + set <name> writeBlock <I2C Address> <Register Address> <value> [<value> [..]]

  • Write n-bytes to an register range (as an block write operation), beginning at the specified register:
    - set <name> writeBlock <I2C Address> <Register Address> <value>

    + set <name> writeBlockReg <I2C Address> <Register Address> <value> [<value> [..]]


  • Examples:
      @@ -590,13 +633,22 @@ sub RPII2C_HWACCESS_ioctl($$) { Get
        - get <name> read <I2C Address> [<Register Address> [<number of registers>]] -
        - gets value of I2C device's registers

        +
      • + Gets value of I2C device's registers:
        + get <name> read <I2C Address> [<Register Address> [<number of registers>]]

        +
      • +
      • + Gets value of I2C device in blockwise mode:
        + get <name> readblock <I2C Address> [<number of registers>]

        +
      • +
      • + Gets value of I2C device's registers in blockwise mode:
        + get <name> readblockreg <I2C Address> <Register Address> [<number of registers>]

        +

      • Examples:
          Reads byte from device with I2C address 0x60
          - get test1 writeByte 60
          + get test1 read 60
          Reads register 0x01 of device with I2C address 0x6E.
          get test1 read 6E 01 AA 55
          Reads register 0x03 to 0x06 of device with I2C address 0x60.
          @@ -672,7 +724,7 @@ sub RPII2C_HWACCESS_ioctl($$) {

      • - Optional: Hardwarezugriff via IOCTL wird standardmäßig genutzt, wenn Device::SMBus nicht installiert ist
        + Optional: Hardwarezugriff via IOCTL wird standardmäßig genutzt (EMPFOHLEN), wenn Device::SMBus nicht installiert ist
        Soll der Hardwarezugriff über das Perl Modul Device::SMBus erfolgen sind diese Schritte notwendig:
          sudo apt-get install libmoose-perl
          sudo cpan Device::SMBus

        @@ -699,11 +751,15 @@ sub RPII2C_HWACCESS_ioctl($$) {
      • Schreibe n-bytes auf einen Registerbereich (als Folge von Einzelbefehlen), beginnend mit dem angegebenen Register:
        - set <name> writeByteReg <I2C Address> <Register Address> <value>

        + set <name> writeByteReg <I2C Address> <Register Address> <value> [<value> [..]]

      • +
      • + Schreibe n-bytes auf ein I2C device (als Blockoperation):
        + set <name> writeBlock <I2C Address> <value> [<value> [..]]

        +
      • Schreibe n-bytes auf einen Registerbereich (als Blockoperation), beginnend mit dem angegebenen Register:
        - set <name> writeBlock <I2C Address> <Register Address> <value>

        + set <name> writeBlockReg <I2C Address> <Register Address> <value> [<value> [..]]


      • Beispiele:
          @@ -714,20 +770,29 @@ sub RPII2C_HWACCESS_ioctl($$) { Schreibe 0xAA zu Register 0x01 des Moduls mit der I2C Adresse 0x6E, schreibe danach 0x55 in das Register 0x02 als einzelne Befehle
          set test1 writeByteReg 6E 01 AA 55
          Schreibe 0xA4 zu Register 0x03, 0x00 zu Register 0x04 und 0xDA zu Register 0x05 des Moduls mit der I2C Adresse 0x60 zusammen als ein Blockbefehl
          - set test1 writeBlock 60 03 A4 00 DA
          + set test1 writeBlockReg 60 03 A4 00 DA

      Get
        - get <name> read <I2C Address> [<Register Address> [<number of registers>]] -
        - Auslesen der Registerinhalte des I2C Moduls

        - Examples: +
      • + Auslesen der Registerinhalte des I2C Moduls:
        + get <name> read <I2C Address> [<Register Address> [<number of registers>]]

        +
      • +
      • + Blockweises Auslesen des I2C Moduls (ohne separate Register):
        + get <name> readblock <I2C Address> [<number of registers>]

        +
      • +
      • + Blockweises Auslesen der Registerinhalte des I2C Moduls:
        + get <name> readblockreg <I2C Address> <Register Address> [<number of registers>]

        +

      • + Beispiele:
          Lese Byte vom Modul mit der I2C Adresse 0x60
          - get test1 writeByte 60
          + get test1 read 60
          Lese den Inhalt des Registers 0x01 vom Modul mit der I2C Adresse 0x6E.
          get test1 read 6E 01 AA 55
          Lese den Inhalt des Registerbereichs 0x03 bis 0x06 vom Modul mit der I2C Adresse 0x60.
          diff --git a/fhem/FHEM/52_I2C_MCP342x.pm b/fhem/FHEM/52_I2C_MCP342x.pm new file mode 100644 index 000000000..8f12f068d --- /dev/null +++ b/fhem/FHEM/52_I2C_MCP342x.pm @@ -0,0 +1,448 @@ +############################################## +# $Id: 52_MCP342x.pm 5865 2014-05-14 23:00:12Z klauswitt $ + +package main; + +use strict; +use warnings; + +use Time::HiRes qw(usleep); +use Scalar::Util qw(looks_like_number); +#use Error qw(:try); + +use constant { + MCP3422_I2C_ADDRESS => '0x68', +}; + +################################################## +# Forward declarations +# +sub I2C_MCP342x_Initialize($); +sub I2C_MCP342x_Define($$); +sub I2C_MCP342x_Attr(@); +sub I2C_MCP342x_Poll($); +sub I2C_MCP342x_Set($@); +sub I2C_MCP342x_Undef($$); + + +my %resols = ( +'12' => { + code => 0b00000000, + delay => 5690, + lsb => 1000, + }, +'14' => { + code => 0b00000100, + delay => 22730, + lsb => 250, + }, +'16' => { + code => 0b00001000, + delay => 90910, + lsb => 62.5, + }, +'18' => { + code => 0b00001100, + delay => 363640, + lsb => 15.625, + }, +); + +my %gains = ( +'1' => 0b00000000, +'2' => 0b00000001, +'4' => 0b00000010, +'8' => 0b00000011, +); + +sub I2C_MCP342x_Initialize($) { + my ($hash) = @_; + + $hash->{DefFn} = 'I2C_MCP342x_Define'; + $hash->{InitFn} = 'I2C_MCP342x_Init'; + $hash->{AttrFn} = 'I2C_MCP342x_Attr'; + $hash->{GetFn} = 'I2C_MCP342x_Get'; + $hash->{UndefFn} = 'I2C_MCP342x_Undef'; + $hash->{I2CRecFn} = 'I2C_MCP342x_I2CRec'; + + $hash->{AttrList} = 'IODev do_not_notify:0,1 showtime:0,1 poll_interval:1,2,5,10,20,30 ' . + 'ch1roundDecimal:0,1,2,3 ch1gain:1,2,4,8 ch1resolution:12,14,16,18 ch1factor '. + 'ch2roundDecimal:0,1,2,3 ch2gain:1,2,4,8 ch2resolution:12,14,16,18 ch2factor '. + 'ch3roundDecimal:0,1,2,3 ch3gain:1,2,4,8 ch3resolution:12,14,16,18 ch3factor '. + 'ch4roundDecimal:0,1,2,3 ch4gain:1,2,4,8 ch4resolution:12,14,16,18 ch4factor '. + $readingFnAttributes; +} + +sub I2C_MCP342x_Define($$) { + my ($hash, $def) = @_; + my @a = split('[ \t][ \t]*', $def); + + $hash->{STATE} = "defined"; + + if ($main::init_done) { + eval { I2C_MCP342x_Init( $hash, [ @a[ 2 .. scalar(@a) - 1 ] ] ); }; + return I2C_MCP342x_Catch($@) if $@; + } + return undef; +} + +sub I2C_MCP342x_Init($$) { + my ( $hash, $args ) = @_; + + my $name = $hash->{NAME}; + + if (defined $args && int(@$args) > 1) + { + return "Define: Wrong syntax. Usage:\n" . + "define MCP342x [] []"; + } + + if (defined (my $address = shift @$args)) { + $hash->{I2C_Address} = $address =~ /^0.*$/ ? oct($address) : $address; + return "$name I2C Address not valid" unless ($address < 128 && $address > 3); + } else { + $hash->{I2C_Address} = hex(MCP3422_I2C_ADDRESS); + } + + if (defined (my $channels = shift @$args)) { + $hash->{channels} = $channels if $channels == 2 || $channels == 4; + } else { + $hash->{channels} = 2; + } + + + my $msg = ''; + # create default attributes + #$msg = CommandAttr(undef, $name . ' poll_interval 5'); + if ($msg) { + Log3 ($hash, 1, $msg); + return $msg; + } + AssignIoPort($hash); + $hash->{STATE} = 'Initialized'; + +# my %sendpackage = ( i2caddress => $hash->{I2C_Address}, direction => "i2cread" ); +# $sendpackage{reg} = hex("AA"); +# $sendpackage{nbyte} = 22; +# return "$name: no IO device defined" unless ($hash->{IODev}); +# my $phash = $hash->{IODev}; +# my $pname = $phash->{NAME}; +# CallFn($pname, "I2CWrtFn", $phash, \%sendpackage); + + return undef; +} + +sub I2C_MCP342x_Catch($) { + my $exception = shift; + if ($exception) { + $exception =~ /^(.*)( at.*FHEM.*)$/; + return $1; + } + return undef; +} + +sub I2C_MCP342x_Attr (@) {# hier noch Werteueberpruefung einfuegen + 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_MCP342x_Init($hash,\@def) if (defined ($hash->{IODev})); + } + } + if ($attr && $attr eq 'poll_interval') { + #my $pollInterval = (defined($val) && looks_like_number($val) && $val > 0) ? $val : 0; + if (!defined($val) ) { + RemoveInternalTimer($hash); + } elsif ($val > 0) { + RemoveInternalTimer($hash); + InternalTimer(1, 'I2C_MCP23017_Poll', $hash, 0); + } else { + $msg = 'Wrong poll intervall defined. poll_interval must be a number > 0'; + } + } elsif ($attr eq 'roundDecimal') { + $msg = 'Wrong $attr defined. Use one of 0, 1, 2' if defined($val) && $val <= 0 && $val >= 3 ; + } elsif ($attr eq 'gain') { + foreach (split (/,/,$val)) { + my @pair = split (/=/,$_); + $msg = "wrong value: $_ for \"attr $hash->{NAME} $attr\" use comma separated =1|2|4|8 where = 1-$hash->{channels}" + unless ( ( scalar(@pair) == 2 && + $pair[0] =~ m/^[1-4]$/i && $pair[0] <= $hash->{channels} && + $pair[1] =~ m/^(1|2|4|8)$/i ) || + $val =~ m/^(1|2|4|8)$/i); + } + } elsif ($attr eq 'resolution') { + foreach (split (/,/,$val)) { + my @pair = split (/=/,$_); + $msg = "wrong value: $_ for \"attr $hash->{NAME} $attr\" use comma separated =12|14|16|18 where = 1-$hash->{channels}" + unless ( ( scalar(@pair) == 2 && + $pair[0] =~ m/^[1-4]$/i && + $pair[1] =~ m/^1(2|4|6|8)$/i ) && + $val =~ m/^1(2|4|6|8)$/i ); + } + } + + return ($msg) ? $msg : undef; +} + +sub I2C_MCP342x_Poll($) { + my ($hash) = @_; + my $name = $hash->{NAME}; + + # Read values + I2C_MCP342x_Get($hash, $name); + + my $pollInterval = AttrVal($hash->{NAME}, 'poll_interval', 0); + if ($pollInterval > 0) { + InternalTimer(gettimeofday() + ($pollInterval * 60), 'I2C_MCP342x_Poll', $hash, 0); + } +} + +sub I2C_MCP342x_Get($@) { + my ($hash, @a) = @_; + my $name = $a[0]; + my $cmd = $a[1]; + + my $rex = "^[1-" . $hash->{channels} . "]\$"; + if ($cmd =~ m/$rex/i) { + my $resol = defined $a[2] ? $a[2] : AttrVal($hash->{NAME},("ch" . $cmd . "resolution"),"12"); + return "Wrong resolution, use 12, 14, 16 or 18" unless $resol =~ m/^1(2|4|6|8)$/i; + my $gain = defined $a[3] ? $a[3] : AttrVal($hash->{NAME},("ch" . $cmd . "gain"),"1"); + return "Wrong gain, use 1, 2, 4 or 8" unless $gain =~ m/^(1|2|4|8)$/i; + my $ts = ReadingsTimestamp($hash->{NAME},("Channel".$cmd),0); + I2C_MCP342x_readvoltage($hash,$cmd,$resol,$gain); + foreach (1..400) { #max 2s warten + usleep 5000; + return ReadingsVal($hash->{NAME},("Channel".$cmd),undef) if $ts ne ReadingsTimestamp($hash->{NAME},("Channel".$cmd),0); + } + } else { + foreach (1..$hash->{channels}) { + my $resol = defined $a[3] ? $a[3] : AttrVal($hash->{NAME},("ch" . $_ . "resolution"),"12"); + return "Wrong resolution, use 12, 14, 16 or 18" unless $resol =~ m/^1(2|4|6|8)$/i; + my $gain = defined $a[4] ? $a[4] : AttrVal($hash->{NAME},("ch" . $_ . "gain"),"1"); + return "Wrong gain, use 1, 2, 4 or 8" unless $gain =~ m/^(1|2|4|8)$/i; + I2C_MCP342x_readvoltage($hash,$_,$resol,$gain); + } + my @gets = ('1', '2'); + push(@gets,('3', '4')) if $hash->{channels} == 4; + return 'Unknown argument ' . $cmd . ', choose one of ' . join(' ', @gets) + } +} + +sub I2C_MCP342x_Undef($$) { + my ($hash, $arg) = @_; + + RemoveInternalTimer($hash); + return undef; +} + +sub I2C_MCP342x_I2CRec ($$) { + 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 $ankommen = "$hash->{NAME}: vom physical empfangen"; + # foreach my $av (keys %{$clientmsg}) { $ankommen .= "|" . $av . ": " . $clientmsg->{$av}; } + #Log3 $hash, 1, $ankommen; + if ($clientmsg->{direction} && $clientmsg->{$pname . "_SENDSTAT"} && $clientmsg->{$pname . "_SENDSTAT"} eq "Ok") { + readingsSingleUpdate($hash,"state", "Ok", 1); + if ( $clientmsg->{direction} eq "i2cread" && defined($clientmsg->{received}) ) {###hier noch normal read rein,wenn alles wieder ok + #Log3 $hash, 1, "empfangen: $clientmsg->{received}"; + I2C_MCP342x_GetVoltage ($hash, $clientmsg->{received}); # if $clientmsg->{type} eq "temp" && $clientmsg->{nbyte} == 2; + } + } else { + readingsSingleUpdate($hash,"state", "transmission error", 1); + Log3 $hash, 3, "$name: failurei in message from $pname"; + 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"); + } +} + +sub I2C_MCP342x_GetVoltage ($$) { + my ($hash, $rawdata) = @_; + my @raw = split(" ",$rawdata); + if ( defined($raw[4]) ) { + if ( ($raw[4] & 0b10000000) == 0 ) { + my $channel = 1 + (($raw[4] & 0b01100000) >> 5 ); + my $resol = 2 * (($raw[4] & 0b00001100) >> 2 ) + 12; + my $gain = 2 ** ($raw[4] & 0b00000011); + my $rawvolt; + if ($resol == 18) { + $rawvolt = ($raw[0] & 0b00000011) << 16 | $raw[1] << 8 | $raw[2]; + } else { + $rawvolt = $raw[0] << 8 | $raw[1]; + } + #Log3 $hash, 1, "Kanal: $channel, rawvolt: $rawvolt, Aufloesung: $resol, Gain: $gain, LSB: $resols{$resol}{lsb}"; + $rawvolt -= (1 << $resol) if $rawvolt >= (1 << ($resol - 1)); + #Log3 $hash, 1, "Kanal: $channel, Unsignedrawvolt: $rawvolt"; + + my $voltage = ( $rawvolt * $resols{$resol}{lsb} ) / $gain ; + #$voltage /= 1000000; + $voltage *= AttrVal($hash->{NAME},("ch" . $channel . "factor"),"1"); + $voltage = sprintf( + '%.' . AttrVal($hash->{NAME}, ('ch' . $channel . 'roundDecimal'), 3) . 'f', + $voltage + ); + $voltage .= " overflow" if ( $rawvolt == ( (1<<($resol-1)) - 1) || $rawvolt == (1<<($resol-1)) ); + readingsSingleUpdate($hash,"Channel$channel", $voltage, 1); + #Log3 $hash, 1, "Kanal: $channel, Fertig: $voltage"; + } else { + Log3 $hash, 3, $hash->{NAME} . " error, output conversion not finished"; + } + } +} + +sub I2C_MCP342x_readvoltage($@) { + my ($hash, $channel, $resol, $gain) = @_; + my $name = $hash->{NAME}; + return "$name: no IO device defined" unless ($hash->{IODev}); + my $phash = $hash->{IODev}; + my $pname = $phash->{NAME}; + #0b10010000 + my $confreg = 1 << 7; # 1|| |||| Initiate a new conversion + $confreg |= ($channel - 1) << 5; # 11 |||| Channel Selection Bits + $confreg |= $resols{$resol}{code}; # 11|| Sample Rate Selection Bit + $confreg |= $gains{$gain}; # 11 PGA Gain Selection Bits + #Log3 $hash, 1, "confinhalt: " . sprintf ('0b%08b', $confreg); + + # Write CONFIGURATION REGISTER to device. This requests a conversion process + my $i2creq = { i2caddress => $hash->{I2C_Address}, direction => "i2cwrite" }; + $i2creq->{data} = $confreg; + CallFn($pname, "I2CWrtFn", $phash, $i2creq); + usleep($resols{$resol}{delay}); #Verzoegerung + + # Read the result from device + my $i2cread = { i2caddress => $hash->{I2C_Address}, direction => "i2cread" }; + $i2cread->{nbyte} = 5; + #$i2cread->{type} = "temp"; + CallFn($pname, "I2CWrtFn", $phash, $i2cread); + + return; +} + + +1; + +=pod +=begin html + + +

          I2C_MCP342x

          +
            + + Provides an interface to the MCP3422/3/4 A/D converter. + The I2C messages are send through an I2C interface module like RPII2C, FRM + or NetzerI2C so this device must be defined first.
            + attribute IODev must be set
            +
            + Define +
              + define <name> I2C_MCP342x [[<I2C Address>] <n channels>]
              + where <I2C Address> is without direction bit and <n channels> is the number of A/D channels

              +
            + + Get +
              + get <name> [[[<channel>] <resolution> ] <gain>]
              + Returns the level on specific <channel>. <resolution> and <gain> will override attibutes for actual operation. + Without attributes only the readings will be refreshed.

              +
            + + Attributes +
              +
            • poll_interval
              + Set the polling interval in minutes to query data from sensor
              + Default: 5, valid values: 1,2,5,10,20,30

              +
            • + Following attributes are separate for all channels.

              +
            • ch1resolution
              + resolution settings
              + the bigger the resolution the longer the conversion time.
              + Default: 12, valid values: 12,14,16,18

              +
            • +
            • ch1gain
              + gain setting
              + Important: the gain setting will reduce the measurement range an may produce an overflow. In this case "overflow" will be added to reading
              + Default: 1, valid values: 1,2,4,8

              +
            • +
            • ch1factor
              + correction factor (will be mutiplied to channel value)
              + Default: 1, valid values: number

              +
            • +
            • ch1roundDecimal
              + Number of decimal places for value
              + Default: 3, valid values: 0,1,2,3

              +
            • +
            • IODev
            • +
            • do_not_notify
            • +
            • showtime
            • +

            +
          + +=end html + +=begin html_DE + + +

          I2C_MCP342x

          +
            + + Ermöglicht die Verwendung eines MCP3422/3/4 I2C A/D Wandler. + I2C-Botschaften werden über ein I2C Interface Modul wie beispielsweise das RPII2C, FRM + oder NetzerI2C gesendet. Daher muss dieses vorher definiert werden.
            + Das Attribut IODev muss definiert sein.
            +
            + Define +
              + define <name> I2C_MCP342x [[<I2C Address>] <n channels>]
              + Der Wert <I2C Address> ist die I2C Adresse ohne Richtungsbit und <n channels> die Anzahl der A/D Kanäle.
              +
            + + Get +
              + get <name> [[[<channel>] <resolution> ] <gain>]
              + Aktuelle Werte vom entstrechenden <channel> lesen. <resolution> und <gain> überschreiben die entsprechenden Attribute für diesen Lesevorgang

              +
            + + Attribute +
              +
            • poll_interval
              + Aktualisierungsintervall aller Werte in Minuten.
              + Standard: 5, gültige Werte: 1,2,5,10,20,30

              +
            • + Folgende Attribute existieren separat für alle Kanäle.

              +
            • ch1resolution
              + Auflösung des Kanals
              + Je größer die Auflösung desto länger die Lesezeit.
              + Standard: 12, gültige Werte: 12,14,16,18

              +
            • +
            • ch1gain
              + Verstärkungsfaktor
              + Wichtig: Der Verstärkungsfaktor verringert den Messbereich entsprechend und kann zu einem Überlauf führen. In diesem Fall wird "overflow" an das reading angehängt.
              + Standard: 1, gültige Werte: 1,2,4,8

              +
            • +
            • ch1factor
              + Korrekturfaktor (Wird zum Kanalwert multipliziert.)
              + Standard: 1, gültige Werte: Zahl

              +
            • +
            • ch1roundDecimal
              + Anzahl Dezimalstellen für den Messwert
              + Standard: 3, gültige Werte: 0,1,2,3

              +
            • +
            • IODev
            • +
            • do_not_notify
            • +
            • showtime
            • +

            +
          + +=end html_DE + +=cut \ No newline at end of file diff --git a/fhem/MAINTAINER.txt b/fhem/MAINTAINER.txt index 48a251fb7..60efbc07f 100644 --- a/fhem/MAINTAINER.txt +++ b/fhem/MAINTAINER.txt @@ -151,6 +151,7 @@ FHEM/52_I2C_EEPROM.pm klausw http://forum.fhem.de Sonstige FHEM/52_I2C_LCD ntruchsess http://forum.fhem.de Sonstige Systeme FHEM/52_I2C_MCP23008 klausw http://forum.fhem.de Sonstige Systeme FHEM/52_I2C_MCP23017 klausw http://forum.fhem.de Sonstige Systeme +FHEM/52_I2C_MCP342x klausw http://forum.fhem.de Sonstige Systeme FHEM/52_I2C_PCA9532 klausw http://forum.fhem.de Sonstige Systeme FHEM/52_I2C_PCF8574 klausw http://forum.fhem.de Sonstige Systeme FHEM/52_I2C_SHT21 klausw http://forum.fhem.de Sonstige Systeme