############################################################################## # $Id$ ############################################################################## # Modul for I2C GPIO Extender MCP23008 # # contributed by Klaus Wittstock (2014) email: klauswittstock bei gmail ############################################################################## package main; use strict; use warnings; use SetExtensions; use Scalar::Util qw(looks_like_number); my %Registers = ( 'IODIRA' => 0x00, #1 = input; 0 = output (default 1) 'IODIRB' => 0x00, 'IPOLA' => 0x01, #1 inverts logic (default 0) 'IPOLB' => 0x01, 'GPINTENA' => 0x02, #1 enables the pin for interrupt-on-change (default 0) 'GPINTENB' => 0x02, 'DEFVALA' => 0x03, #The default comparison value for interrupt (opposite value will caues an interrupt) (default 0) 'DEFVALB' => 0x03, 'INTCONA' => 0x04, #If a bit is set, the corresponding I/O pin is compared against DEFVAL register. Otherwise against the previous value. 'INTCONB' => 0x04, 'IOCON' => 0x05, 'GPPUA' => 0x06, #100k pull up resistor for input 'GPPUB' => 0x06, 'INTFA' => 0x07, #shows which Pin caused the interrupt (ro) 'INTFB' => 0x07, 'INTCAPA' => 0x08, #status from all registers at the time the interrupt occured, remain unchanged until a read of INTCAP or GPIO. (ro) 'INTCAPB' => 0x08, 'GPIOA' => 0x09, #value on the ports (r/w) 'GPIOB' => 0x09, 'OLATA' => 0x0A, 'OLATB' => 0x0A, ); my %setsP = ( 'off' => 0, 'on' => 1, ); my %intout = ( 'separate_active-low' => 0x00, 'separate_active-high' => 0x02, 'separate_open-drain' => 0x04, ); ############################################################################### sub I2C_MCP23008_Initialize($) { my ($hash) = @_; $hash->{DefFn} = "I2C_MCP23008_Define"; $hash->{InitFn} = 'I2C_MCP23008_Init'; $hash->{UndefFn} = "I2C_MCP23008_Undefine"; $hash->{AttrFn} = "I2C_MCP23008_Attr"; $hash->{StateFn} = "I2C_MCP23008_State"; $hash->{SetFn} = "I2C_MCP23008_Set"; $hash->{GetFn} = "I2C_MCP23008_Get"; $hash->{I2CRecFn} = "I2C_MCP23008_I2CRec"; $hash->{AttrList} = "IODev do_not_notify:1,0 ignore:1,0 showtime:1,0 ". "poll_interval OnStartup ". "OutputPorts:multiple-strict,A0,A1,A2,A3,A4,A5,A6,A7 ". "Pullup:multiple-strict,A0,A1,A2,A3,A4,A5,A6,A7 ". "invert_input:multiple-strict,A0,A1,A2,A3,A4,A5,A6,A7 ". "Interrupt:multiple-strict,A0,A1,A2,A3,A4,A5,A6,A7 ". "InterruptOut:separate_active-low,separate_active-high,separate_open-drain ". "$readingFnAttributes"; } ############################################################################### sub I2C_MCP23008_Define($$) { my ($hash, $def) = @_; my @a = split("[ \t]+", $def); $hash->{STATE} = 'defined'; if ($main::init_done) { eval { I2C_MCP23008_Init( $hash, [ @a[ 2 .. scalar(@a) - 1 ] ] ); }; return I2C_MCP23008_Catch($@) if $@; } return undef; } ############################################################################### sub I2C_MCP23008_Init($$) { #Geraet beim anlegen/booten/nach Neuverbindung (wieder) initialisieren my ( $hash, $args ) = @_; if (defined $args && int(@$args) != 1) { return "Define: Wrong syntax. Usage:\n" . "define I2C_MCP23008 "; } if (defined (my $address = shift @$args)) { $hash->{I2C_Address} = $address =~ /^0.*$/ ? oct($address) : $address; } else { return "$hash->{NAME} I2C Address not valid"; } AssignIoPort($hash); my $msg = ''; #Output level wieder setzen my $sbyte = 0; foreach (reverse 0..7) { $sbyte += $setsP{ReadingsVal($hash->{NAME},"PortA".$_,"off")} << ($_); #Werte fuer PortA aus dem Reading holen #$sbyte += $setsP{ReadingsVal($hash->{NAME},"PortB".$_,"off")} << (8 + $_); } $msg = I2C_MCP23008_SetRegPair($hash, $sbyte, "GPIO") if $sbyte; #bei Init IC neu konfigurieren if ( defined ( my $val = AttrVal($hash->{NAME},"invert_input",undef)) ) { ($msg, my $regval) = I2C_MCP23008_CheckAttr($hash, "invert_input", $val); $msg = I2C_MCP23008_SetRegPair($hash, $regval, "IPOL") unless $msg; } if ( defined ( my $val = AttrVal($hash->{NAME},"OutputPorts",undef)) ) { ($msg, my $regval) = I2C_MCP23008_CheckAttr($hash, "OutputPorts", $val); $msg = I2C_MCP23008_SetRegPair($hash, ~$regval, "IODIR") unless $msg; } if ( defined ( my $val = AttrVal($hash->{NAME},"Pullup",undef)) ) { ($msg, my $regval) = I2C_MCP23008_CheckAttr($hash, "Pullup", $val); $msg = I2C_MCP23008_SetRegPair($hash, $regval, "GPPU") unless $msg; } if ( defined ( my $val = AttrVal($hash->{NAME},"Interrupt",undef)) ) { ($msg, my $regval) = I2C_MCP23008_CheckAttr($hash, "Interrupt", $val); $msg = I2C_MCP23008_SetRegPair($hash, $regval, "GPINTEN") unless $msg; } if ( defined ( my $val = AttrVal($hash->{NAME},"InterruptOut",undef)) ) { my $regval = 0; $regval = $intout{$val} if defined $val; if (defined (my $iodev = $hash->{IODev})) { CallFn($iodev->{NAME}, "I2CWrtFn", $iodev, { direction => "i2cwrite", i2caddress => $hash->{I2C_Address}, reg => $Registers{IOCON}, data => $regval, }) if (defined $hash->{I2C_Address}); } else { return "no IODev assigned to '$hash->{NAME}'"; } } #Output level wieder setzen die zweite #$sbyte = 0; #foreach (reverse 0..7) { # $sbyte += $setsP{ReadingsVal($hash->{NAME},"PortA".$_,"off")} << ($_); #Werte fuer PortA aus dem Reading holen # $sbyte += $setsP{ReadingsVal($hash->{NAME},"PortB".$_,"off")} << (8 + $_); #} #$msg = I2C_MCP23008_SetRegPair($hash, $sbyte, "GPIO") if $sbyte; I2C_MCP23008_Get($hash, $hash->{NAME}); $hash->{STATE} = 'Initialized'; return ($msg) ? $msg : undef; } ############################################################################### sub I2C_MCP23008_Catch($) { #Fehlermeldung von eval formattieren my $exception = shift; if ($exception) { $exception =~ /^(.*)( at.*FHEM.*)$/; return $1; } return undef; } ############################################################################### sub I2C_MCP23008_Undefine($$) { my ($hash, $arg) = @_; if ( defined (AttrVal($hash->{NAME}, "poll_interval", undef)) ) { RemoveInternalTimer($hash); } } ############################################################################### sub I2C_MCP23008_Attr(@) { 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_MCP23008_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_MCP23008_Poll', $hash, 0); } else { $msg = 'Wrong poll intervall defined. poll_interval must be a number > 0'; } } elsif ($attr && $attr eq "OutputPorts") { ($msg, my $regval) = I2C_MCP23008_CheckAttr($hash, $attr, $val); $msg = I2C_MCP23008_SetRegPair($hash, ~$regval, "IODIR") unless $msg; } elsif ($attr && $attr eq "Pullup") { ($msg, my $regval) = I2C_MCP23008_CheckAttr($hash, $attr, $val); $msg = I2C_MCP23008_SetRegPair($hash, $regval, "GPPU") unless $msg; } elsif ($attr && $attr eq "invert_input") { ($msg, my $regval) = I2C_MCP23008_CheckAttr($hash, $attr, $val); $msg = I2C_MCP23008_SetRegPair($hash, $regval, "IPOL") unless $msg; } elsif ($attr && $attr eq "Interrupt") { ($msg, my $regval) = I2C_MCP23008_CheckAttr($hash, $attr, $val); $msg = I2C_MCP23008_SetRegPair($hash, $regval, "GPINTEN") 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|last where = A0 - A7" unless ( scalar(@pair) == 2 && $pair[0] =~ m/^(A)(0|)[0-7]$/i && $pair[1] =~ m/^(on|off|last)$/i); } } } elsif ($attr && $attr eq "InterruptOut") { my $regval = 0; if (defined $val) { return "wrong value: $_ for \"attr $hash->{NAME} $attr\" use one of: " . join(',', (sort { $intout{ $a } <=> $intout{ $b } } keys %setsP) ) unless(exists($intout{$val})); $regval = $intout{$val}; } if (defined (my $iodev = $hash->{IODev})) { #Log3 $hash, 1, "schreibe raus: i2cwrite|$hash->{I2C_Address}|$Registers{$regtype . $reg}|$port{$reg}|"; CallFn($iodev->{NAME}, "I2CWrtFn", $iodev, { direction => "i2cwrite", i2caddress => $hash->{I2C_Address}, reg => $Registers{IOCON}, data => $regval, }) if (defined $hash->{I2C_Address}); } else { return "no IODev assigned to '$hash->{NAME}'"; } } return ($msg) ? $msg : undef; } ############################################################################### sub I2C_MCP23008_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(A)(0|)[0-7]$/i) { my $po = substr $sname, 4, 2; # Ax oder Bx Log3 $hash, 5, "$hash->{NAME}: Port = $po"; if ( index( AttrVal($hash->{NAME}, "OutputPorts", ""), $po, 0) >= 0 ) { if ( ( my $pos = index(AttrVal($hash->{NAME},"OnStartup", ""), $po ,0) ) >=0 ) { my $val = substr AttrVal($hash->{NAME},"OnStartup",undef), $pos + 3, 2; if ( $val eq "on" ) { Log3 $hash, 5, "$hash->{NAME}: $sname soll auf on gesetzt werden"; readingsSingleUpdate($hash,$sname, "on", 1); } elsif ( $val eq "of" ) { Log3 $hash, 5, "$hash->{NAME}: $sname soll auf off gesetzt werden"; readingsSingleUpdate($hash,$sname, "off", 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 wird auf Altzustand: $sval gesetzt (kein Eintrag in on Startup)"; $hash->{READINGS}{$sname}{VAL} = $sval; $hash->{READINGS}{$sname}{TIME} = $tim; } } else { Log3 $hash, 5, "$hash->{NAME}: $sname ist Eingang"; } } return undef; } ############################################################################### sub I2C_MCP23008_CheckAttr { my ($hash, $attr, $val) = @_; my $msg = undef; my ($regval) = 0; if (defined $val) { foreach (split (/,/,$val)) { $msg = "wrong value: $_ for \"attr $hash->{NAME} $attr\" use comma separated values from A0 - A7" unless ($_ =~ m/^(A)(0|)[0-7]$/i); #my $bank = ($_ =~ m/^A/) ? 0 : 8; # A oder B $_ =~ tr/[a-zA-Z]//d; #Nummer aus String extrahieren #$regval |= 1 << ($_ + $bank); $regval |= 1 << $_; } } return $msg, $regval; } ############################################################################### sub I2C_MCP23008_SetRegPair { #set register pair for PortA/B my ($hash, $regval, $regtype) = @_; my %port = (); $port{A} = $regval & 0xff; #$port{B} = ( $regval >> 8 ) & 0xff; if (defined (my $iodev = $hash->{IODev})) { foreach my $reg (keys %port) { #Log3 $hash, 1, "schreibe raus: i2cwrite|$hash->{I2C_Address}|$Registers{$regtype . $reg}|$port{$reg}|"; CallFn($iodev->{NAME}, "I2CWrtFn", $iodev, { direction => "i2cwrite", i2caddress => $hash->{I2C_Address}, reg => $Registers{$regtype . $reg}, data => $port{$reg}, }) if (defined $hash->{I2C_Address}); } I2C_MCP23008_Get($hash,$hash->{NAME}) if ( ($iodev->{TYPE} ne "RPII2C") && ($regtype eq "GPIO") ); } else { return "no IODev assigned to $hash->{NAME}"; } } ############################################################################### sub I2C_MCP23008_Poll($) { #function for refresh intervall my ($hash) = @_; my $name = $hash->{NAME}; # Read values I2C_MCP23008_Get($hash, $name); my $pollInterval = AttrVal($hash->{NAME}, 'poll_interval', 0); if ($pollInterval > 0) { InternalTimer(gettimeofday() + ($pollInterval * 60), 'I2C_MCP23008_Poll', $hash, 0); } } ############################################################################### sub I2C_MCP23008_Set($@) { my ($hash, @a) = @_; my $name =$a[0]; my $cmd = $a[1]; my $val = $a[2]; #my @outports = sort(split(/,/,AttrVal($name, "OutputPorts", ""))); unless (@a == 3) { } my $msg = undef; if ( $cmd && $cmd =~ m/^P(ort|)(A)((0|)[0-7])(,(P|)(ort|)(A|B)((0|)[0-7])){0,7}$/i) { return "wrong value: $val for \"set $name $cmd\" use one of: " . join(',', (sort { $setsP{ $a } <=> $setsP{ $b } } keys %setsP) ) unless(exists($setsP{$val})); my @scmd = split(",", $cmd); foreach (@scmd) { $_ =~ tr/P(ort|)//d; #Nummer aus String extrahieren #$msg .= (defined $msg ? "," : "") . "Port" . $_ unless ( ($_) ~~ @outports ); #Pruefen ob entsprechender Port Input ist $msg .= (defined $msg ? "," : "") . "Port" . $_ unless ( AttrVal($name, "OutputPorts", "") =~ /$_/ ); #Pruefen ob entsprechender Port Input ist } return "$name error: $msg is defined as input" if $msg; #Log3 $hash, 1, "$name: multitest gereinigt: @scmd"; my $regval = 0; foreach (reverse 0..7) { #foreach my $po ("A","B") { foreach my $po ("A") { my $bank = ($po eq "A") ? 0 : 8; # A oder B #if ( ($po.$_) ~~ @scmd ) { #->wenn aktueller Port in Liste dann neuer Wert if ( $cmd =~ /$po$_/ ) { #->wenn aktueller Port in Liste dann neuer Wert $regval += $setsP{$val} << ($bank + $_); } else { #->sonst aus dem Reading holen $regval += $setsP{ReadingsVal($name,"Port".$po.$_,"off")} << ($bank + $_); } } } #Log3 $hash, 1, "$name: endwert: $regval"; $msg = I2C_MCP23008_SetRegPair($hash, $regval, "GPIO") unless $msg; } else { my $list = ""; foreach (0..7) { #next unless ( ("A" . $_) ~~ @outports ); #Inputs ueberspringen next unless ( AttrVal($name, "OutputPorts", "") =~ /A$_/ ); #Inputs ueberspringen $list .= "PortA" . $_ . ":" . join(',', (sort { $setsP{ $a } <=> $setsP{ $b } } keys %setsP) ) . " "; } #foreach (0..7) { # next unless ( ("B" . $_) ~~ @outports ); #Inputs ueberspringen # $list .= "PortB" . $_ . ":" . join(',', (sort { $setsP{ $a } <=> $setsP{ $b } } keys %setsP) ) . " "; #} $msg = "Unknown argument $a[1], choose one of " . $list; } return ($msg) ? $msg : undef; } ############################################################################### sub I2C_MCP23008_Get($@) { my ($hash, @a) = @_; my $name =$a[0]; my %sendpackage = ( i2caddress => $hash->{I2C_Address}, direction => "i2cread" ); $sendpackage{reg} = $Registers{GPIOA}; #startadresse zum lesen $sendpackage{nbyte} = 1; return "$name: no IO device defined" unless ($hash->{IODev}); my $phash = $hash->{IODev}; my $pname = $phash->{NAME}; CallFn($pname, "I2CWrtFn", $phash, \%sendpackage); } ############################################################################### sub I2C_MCP23008_I2CRec($@) { #ueber CallFn 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/ ; } #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} 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 Log3 $hash, 3, "$name: wrong amount of registers transmitted from $pname" unless (@rec == $clientmsg->{nbyte}); foreach (reverse 0..$#rec) { #reverse, damit Inputs (Register 0 und 1 als letztes geschrieben werden) I2C_MCP23008_UpdReadings($hash, $_ + $clientmsg->{reg} , $rec[$_]); } readingsSingleUpdate($hash,"state", "Ok", 1); } elsif ($clientmsg->{direction} eq "i2cwrite" && defined $clientmsg->{data}) { # =~ m/^[a-f0-9]{2}$/i) {#readings aktualisieren wenn uebertragung ok I2C_MCP23008_UpdReadings($hash, $clientmsg->{reg} , $clientmsg->{data}) if ( ($clientmsg->{reg} == $Registers{GPIOA}) || ($clientmsg->{reg} == $Registers{GPIOB}) ); readingsSingleUpdate($hash,"state", "Ok", 1); } 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"); } } 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: " . 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; } } ############################################################################### sub I2C_MCP23008_UpdReadings($$$) { #nach Rueckmeldung readings updaten (ueber I2CRec aufgerufen) my ($hash, $reg, $inh) = @_; my $name = $hash->{NAME}; #$inh = hex($inh); #$reg = hex($reg); Log3 $hash, 5, "$name UpdReadings Register: $reg, Inhalt: $inh"; readingsBeginUpdate($hash); if ($reg == $Registers{GPIOA}) { my %rsetsP = reverse %setsP; foreach (0..7) { my $pval = 1 & ( $inh >> $_ ); readingsBulkUpdate($hash, 'PortA'.$_ , $rsetsP{$pval}) if (ReadingsVal($name, 'PortA'.$_,"nix") ne $rsetsP{$pval}); #nur wenn Wert geaendert } } #elsif ($reg == $Registers{GPIOB}) { #my %rsetsP = reverse %setsP; #foreach (0..7) { # my $pval = 1 & ( $inh >> $_ ); # readingsBulkUpdate($hash, 'PortB'.$_ , $rsetsP{$pval}) # if (ReadingsVal($name, 'PortB'.$_,"nix") ne $rsetsP{$pval}); #nur wenn Wert geaendert #} #} readingsEndUpdate($hash, 1); return; } 1; =pod =item device =item summary controls/reads GPIOs from an via I2C connected MCP23008 port extender =item summary_DE steuern/lesen der GPIOs eines über I2C angeschlossenen MCP23008 =begin html

I2C_MCP23008

    Provides an interface to the MCP23008 16 channel port extender IC. On Raspberry Pi the Interrupt Pin's can be connected to an GPIO and RPI_GPIO can be used to get the port values if an interrupt occurs.
    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_MCP23008 <I2C Address>
      <I2C Address> may be an 2 digit hexadecimal value (0xnn) or an decimal value
      For example 0x40 (hexadecimal) = 64 (decimal). An I2C address are 7 MSB, the LSB is the R/W bit.
    Set
      set <name> <port[,port[...]]> <value>

      where <port> is one of PortA0 to PortA7 and <value> is one of:
        off
        on

      Example:
        set mod1 PortA4 on
        set mod1 PortA4,PortA6 off
        set mod1 PortA4,A6 on

    Get
      get <name>

      refreshes all readings

    Attributes
    • poll_interval
      Set the polling interval in minutes to query the GPIO's level
      Default: -, valid values: decimal number

    • OutputPorts
      Comma separated list of ports that are used as Output
      Ports not in this list can't be written
      Default: no, valid values: A0-A7

    • OnStartup
      Comma separated list of output ports and their desired state after start
      Without this atribut all output ports will set to last state
      Default: -, valid values: <port>=on|off|last where <port> = A0-A7

    • Pullup
      Comma separated list of input ports which switch on their internal 100k pullup
      Default: -, valid values: A0-A7

    • Interrupt
      Comma separated list of input ports which will trigger the IntA/B pin
      Default: -, valid values: A0-A7

    • invert_input
      Comma separated list of input ports which use inverted logic
      Default: -, valid values: A0-A7

    • InterruptOut
      Configuration options for INT output pin
      Values:
      • active-low (INT output is active low)
      • active-high (INT output is active high)
      • open-drain (INTA output is open drain)

    • IODev
    • ignore
    • do_not_notify
    • showtime

=end html =begin html_DE

I2C_MCP23008

    Ermöglicht die Verwendung eines MCP23008 I2C 8 Bit Portexenders. Auf einem Raspberry Pi kann der Interrupt Pin des MCP23008 mit einem GPIO verbunden werden und über die Interrupt Funktionen von RPI_GPIO lässt sich dann ein get für den MCP23008 bei Pegeländerung auslösen.
    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_MCP23008 <I2C Address>
      <I2C Address> kann ein zweistelliger Hex-Wert (0xnn) oder ein Dezimalwert sein
      Beispielsweise 0x40 (hexadezimal) = 64 (dezimal). Als I2C Adresse verstehen sich die 7 MSB, das LSB ist das R/W Bit.
    Set
      set <name> <port[,port[...]]> <value>

      <port> kann PortA0 bis PortA7 annehmen und <value> folgende Werte:
        off
        on

      Beispiel:
        set mod1 PortA4 on
        set mod1 PortA4,PortA6 off
        set mod1 PortA4,A6 on

    Get
      get <name>

      Aktualisierung aller Werte

    Attribute
    • poll_interval
      Aktualisierungsintervall aller Werte in Minuten.
      Standard: -, gültige Werte: Dezimalzahl

    • OutputPorts
      Durch Komma getrennte Ports die als Ausgänge genutzt werden sollen.
      Nur Ports in dieser Liste können gesetzt werden.
      Standard: -, gültige Werte: A0-A7

    • OnStartup
      Durch Komma getrennte Output Ports 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: <port>=on|off|last wobei <port> = A0-A7

    • Pullup
      Durch Komma getrennte Input Ports, bei denen der interne 100k pullup aktiviert werden soll.
      Standard: -, gültige Werte: A0-A7

    • Interrupt
      Durch Komma getrennte Input Ports, die einen Interrupt auf IntA/B auslösen.
      Standard: -, gültige Werte: A0-A7

    • invert_input
      Durch Komma getrennte Input Ports, die reverse Logik nutzen.
      Standard: -, gültige Werte: A0-A7

    • InterruptOut
      Einstellungen für den INT Pin
      gültige Werte:
      • active-low (INT ist active low)
      • active-high (INT ist active high)
      • open-drain (INT ist open drain)

    • IODev
    • ignore
    • do_not_notify
    • showtime

=end html_DE =cut