From 766f77e8f344acd01f7c1baf4035330a4a287235 Mon Sep 17 00:00:00 2001 From: klauswitt <> Date: Sun, 6 Jul 2014 21:38:05 +0000 Subject: [PATCH] changed access to gpio via userspace by default (for BBB and Cubie) access via gpio utility as fallback updated commandref git-svn-id: https://svn.fhem.de/fhem/trunk@6207 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/FHEM/51_RPI_GPIO.pm | 1053 +++++++++++++++++++++----------------- 1 file changed, 579 insertions(+), 474 deletions(-) diff --git a/fhem/FHEM/51_RPI_GPIO.pm b/fhem/FHEM/51_RPI_GPIO.pm index 36aa0c6ed..d0bebbef7 100644 --- a/fhem/FHEM/51_RPI_GPIO.pm +++ b/fhem/FHEM/51_RPI_GPIO.pm @@ -8,7 +8,7 @@ # define RPI_GPIO # where is one of RPi's GPIO # -# contributed by Klaus Wittstock (2013) email: klauswittstock bei gmail punkt com +# contributed by Klaus Wittstock (2013) email: klauswittstock bei gmail # ############################################################################## @@ -20,22 +20,24 @@ use Scalar::Util qw(looks_like_number); use IO::File; use SetExtensions; +sub RPI_GPIO_fileaccess($$;$); + sub RPI_GPIO_Initialize($) { - my ($hash) = @_; - $hash->{DefFn} = "RPI_GPIO_Define"; - $hash->{GetFn} = "RPI_GPIO_Get"; - $hash->{SetFn} = "RPI_GPIO_Set"; - $hash->{StateFn} = "RPI_GPIO_State"; - $hash->{AttrFn} = "RPI_GPIO_Attr"; - $hash->{UndefFn} = "RPI_GPIO_Undef"; - $hash->{ExceptFn} = "RPI_GPIO_Except"; - $hash->{AttrList} = "poll_interval" . - " direction:input,output pud_resistor:off,up,down" . - " interrupt:none,falling,rising,both" . - " toggletostate:no,yes active_low:no,yes" . - " debounce_in_ms restoreOnStartup:no,yes" . - " longpressinterval " . - "$readingFnAttributes"; + my ($hash) = @_; + $hash->{DefFn} = "RPI_GPIO_Define"; + $hash->{GetFn} = "RPI_GPIO_Get"; + $hash->{SetFn} = "RPI_GPIO_Set"; + $hash->{StateFn} = "RPI_GPIO_State"; + $hash->{AttrFn} = "RPI_GPIO_Attr"; + $hash->{UndefFn} = "RPI_GPIO_Undef"; + $hash->{ExceptFn} = "RPI_GPIO_Except"; + $hash->{AttrList} = "poll_interval" . + " direction:input,output pud_resistor:off,up,down" . + " interrupt:none,falling,rising,both" . + " toggletostate:no,yes active_low:no,yes" . + " debounce_in_ms restoreOnStartup:no,yes,on,off,last" . + " longpressinterval " . + "$readingFnAttributes"; } my $gpiodir = "/sys/class/gpio"; #GPIO base directory @@ -49,7 +51,7 @@ my %setsoutp = ( my %setsinpt = ( 'readValue' => 0, -); +); sub RPI_GPIO_Define($$) { my ($hash, $def) = @_; @@ -58,357 +60,394 @@ sub RPI_GPIO_Define($$) { my $menge = int(@args); if (int(@args) < 3) { - return "Define: to less arguments. Usage:\n" . - "define RPI_GPIO "; + return "Define: to less arguments. Usage:\n" . + "define RPI_GPIO "; } - #Prüfen, ob GPIO bereits verwendet + #Pruefen, ob GPIO bereits verwendet foreach my $dev (devspec2array("TYPE=$hash->{TYPE}")) { - if ($args[2] eq InternalVal($dev,"RPI_pin","")) { - return "GPIO $args[2] already used by $dev"; - } + if ($args[2] eq InternalVal($dev,"RPI_pin","")) { + return "GPIO $args[2] already used by $dev"; + } } my $name = $args[0]; $hash->{RPI_pin} = $args[2]; - - #export Pin alte Version -> wird jetzt über direction gemacht (WiringPi Programm GPIO) - #my $exp = IO::File->new("> /sys/class/gpio/export"); - #print $exp "$hash->{RPI_pin}"; - #$exp->close; - #select(undef, undef, undef, 0.4); #kurz warten bis Verzeichnis angelegt + $hash->{dir_not_set} = 1; - # create default attributes - my $msg = CommandAttr(undef, $name . ' direction input'); - return $msg if ($msg); - select(undef, undef, undef, 0.4); + if(-e "$gpiodir/gpio$hash->{RPI_pin}" && -w "$gpiodir/gpio$hash->{RPI_pin}/value" && -w "$gpiodir/gpio$hash->{RPI_pin}/direction") { #GPIO bereits exportiert? + Log3 $hash, 4, "$name: gpio$hash->{RPI_pin} already exists"; + #nix tun...ist ja schon da + } elsif (-w "$gpiodir/export") { #gpio export Datei mit schreibrechten? + Log3 $hash, 4, "$name: write access to file $gpiodir/export, use it to export GPIO"; + my $exp = IO::File->new("> $gpiodir/export"); #gpio ueber export anlegen + print $exp "$hash->{RPI_pin}"; + $exp->close; + } else { + if ( defined(my $ret = RPI_GPIO_CHECK_GPIO_UTIL($gpioprg)) ) { #Abbbruch da kein gpio utility vorhanden + Log3 $hash, 1, "$name: can't export gpio$hash->{RPI_pin}, no write access to $gpiodir/export and " . $ret; + return "$name: can't export gpio$hash->{RPI_pin}, no write access to $gpiodir/export and " . $ret; + } else { #nutze GPIO Utility? + Log3 $hash, 4, "$name: using gpio utility to export pin"; + RPI_GPIO_exuexpin($hash, "in"); + } + } + + # wait for Pin export (max 5s) + my $checkpath = qq($gpiodir/gpio$hash->{RPI_pin}/value); + my $counter = 100; + while( $counter ){ + last if( -e $checkpath && -w $checkpath ); + Time::HiRes::sleep( 0.05 ); + $counter --; + } + unless( $counter ) { #abbrechen wenn export fehlgeschlagen + Log3 $hash, 1, "$name: failed to export pin gpio$hash->{RPI_pin}"; + } + $hash->{fhem}{interfaces} = "switch"; return undef; } sub RPI_GPIO_Get($) { - my ($hash) = @_; - my $name = $hash->{NAME}; - #my $dir = $attr{$hash->{NAME}}{direction} || "output"; - my $dir = ""; - my $zustand = undef; - my $val = RPI_GPIO_fileaccess($hash, "value"); - if ( defined ($val) ) { - if ( $val == 1) { - if ($dir eq "output") {$zustand = "on";} else {$zustand = "high";} - } elsif ( $val == 0 ) { - if ($dir eq "output") {$zustand = "off";} else {$zustand = "low";} - } - } else { - Log3 $hash, 1, "$hash->{NAME} GetFn: readout of Pinvalue fail"; - } - $hash->{READINGS}{Pinlevel}{VAL} = $zustand; - $hash->{READINGS}{Pinlevel}{TIME} = TimeNow(); - return "Current Value for $name: $zustand"; + my ($hash) = @_; + my $name = $hash->{NAME}; + #my $dir = $attr{$hash->{NAME}}{direction} || "output"; + my $dir = ""; + my $zustand = undef; + my $val = RPI_GPIO_fileaccess($hash, "value"); + if ( defined ($val) ) { + if ( $val == 1) { + if ($dir eq "output") {$zustand = "on";} else {$zustand = "high";} + } elsif ( $val == 0 ) { + if ($dir eq "output") {$zustand = "off";} else {$zustand = "low";} + } + } else { + Log3 $hash, 1, "$hash->{NAME} GetFn: readout of Pinvalue fail"; + } + $hash->{READINGS}{Pinlevel}{VAL} = $zustand; + $hash->{READINGS}{Pinlevel}{TIME} = TimeNow(); + return "Current Value for $name: $zustand"; } sub RPI_GPIO_Set($@) { - my ($hash, @a) = @_; - my $name =$a[0]; - my $cmd = $a[1]; - #my $val = $a[2]; - - if(defined($attr{$name}) && defined($attr{$name}{"direction"})) { - my $mt = $attr{$name}{"direction"}; - if($mt && $mt eq "output") { - if ($cmd eq 'toggle') { - my $val = RPI_GPIO_fileaccess($hash, "value"); #alten Wert des GPIO direkt auslesen - $cmd = $val eq "0" ? "on" :"off"; - } -if ($cmd eq 'on') { - RPI_GPIO_fileaccess($hash, "value", "1"); - #$hash->{STATE} = 'on'; - readingsBeginUpdate($hash); - #readingsBulkUpdate($hash, 'Pinlevel', $valalt); - readingsBulkUpdate($hash, 'state', "on"); - readingsEndUpdate($hash, 1); - } elsif ($cmd eq 'off') { - RPI_GPIO_fileaccess($hash, "value", "0"); - #$hash->{STATE} = 'off'; - readingsBeginUpdate($hash); - #readingsBulkUpdate($hash, 'Pinlevel', $valalt); - readingsBulkUpdate($hash, 'state', "off"); - readingsEndUpdate($hash, 1); - } else { - my $slist = join(' ', keys %setsoutp); - return SetExtensions($hash, $slist, @a); - } - } else { - if(!defined($setsinpt{$cmd})) { - return 'Unknown argument ' . $cmd . ', choose one of ' . join(' ', keys %setsinpt) - } else { - - } - } - } - if ($cmd eq 'readValue') { #noch bei input einpflegen - RPI_GPIO_updatevalue($hash); - } + my ($hash, @a) = @_; + my $name =$a[0]; + my $cmd = $a[1]; + #my $val = $a[2]; + + if(defined($attr{$name}) && defined($attr{$name}{"direction"})) { + my $mt = $attr{$name}{"direction"}; + if($mt && $mt eq "output") { + if ($cmd eq 'toggle') { + my $val = RPI_GPIO_fileaccess($hash, "value"); #alten Wert des GPIO direkt auslesen + $cmd = $val eq "0" ? "on" :"off"; + } + if ($cmd eq 'on') { + RPI_GPIO_fileaccess($hash, "value", "1"); + #$hash->{STATE} = 'on'; + readingsBeginUpdate($hash); + #readingsBulkUpdate($hash, 'Pinlevel', $valalt); + readingsBulkUpdate($hash, 'state', "on"); + readingsEndUpdate($hash, 1); + } elsif ($cmd eq 'off') { + RPI_GPIO_fileaccess($hash, "value", "0"); + #$hash->{STATE} = 'off'; + readingsBeginUpdate($hash); + #readingsBulkUpdate($hash, 'Pinlevel', $valalt); + readingsBulkUpdate($hash, 'state', "off"); + readingsEndUpdate($hash, 1); + } else { + my $slist = join(' ', keys %setsoutp); + return SetExtensions($hash, $slist, @a); + } + } else { + if(!defined($setsinpt{$cmd})) { + return 'Unknown argument ' . $cmd . ', choose one of ' . join(' ', keys %setsinpt) + } else { + } + } + } + if ($cmd eq 'readValue') { #noch bei input einpflegen + RPI_GPIO_updatevalue($hash); + } } sub RPI_GPIO_State($$$$) { #reload readings at FHEM start - my ($hash, $tim, $sname, $sval) = @_; - Log3 $hash, 4, "$hash->{NAME}: $sname kann auf $sval wiederhergestellt werden $tim"; - if ( (AttrVal($hash->{NAME},"restoreOnStartup","on") eq "on") && ($sname ne "STATE") ) { - if (AttrVal($hash->{NAME},"direction","") eq "output") { - $hash->{READINGS}{$sname}{VAL} = $sval; - $hash->{READINGS}{$sname}{TIME} = $tim; - Log3 $hash, 4, "OUTPUT $hash->{NAME}: $sname wiederhergestellt auf $sval"; - if ($sname eq "state") { - #RPI_GPIO_Set($hash,$hash->{NAME},$sname,$sval); - RPI_GPIO_Set($hash,$hash->{NAME},$sval); - Log3 $hash, 4, "OUTPUT $hash->{NAME}: STATE wiederhergestellt auf $sval"; - } - } elsif ( (AttrVal($hash->{NAME},"direction","") eq "input") && (AttrVal($hash->{NAME},"toggletostate","") eq "yes")) { - if ($sname eq "Toggle") { - $hash->{READINGS}{$sname}{VAL} = $sval; - $hash->{READINGS}{$sname}{TIME} = $tim; - #RPI_GPIO_Set($hash,$hash->{NAME},$sval); - readingsBeginUpdate($hash); - readingsBulkUpdate($hash, 'state', $sval); - readingsEndUpdate($hash, 1); - Log3 $hash, 4, "INPUT $hash->{NAME}: $sname und STATE wiederhergestellt auf $sval"; - } elsif ($sname eq "Counter") { - $hash->{READINGS}{$sname}{VAL} = $sval; - $hash->{READINGS}{$sname}{TIME} = $tim; - Log3 $hash, 4, "INPUT $hash->{NAME}: $sname wiederhergestellt auf $sval"; - } - } - } - return; + my ($hash, $tim, $sname, $sval) = @_; + Log3 $hash, 4, "$hash->{NAME}: $sname kann auf $sval wiederhergestellt werden $tim"; + + #if ( (AttrVal($hash->{NAME},"restoreOnStartup","yes") eq "yes") && ($sname ne "STATE") ) { + if ( $sname ne "STATE" && AttrVal($hash->{NAME},"restoreOnStartup","last") ne "no") { + if (AttrVal($hash->{NAME},"direction","") eq "output") { + $hash->{READINGS}{$sname}{VAL} = $sval; + $hash->{READINGS}{$sname}{TIME} = $tim; + Log3 $hash, 4, "OUTPUT $hash->{NAME}: $sname wiederhergestellt auf $sval"; + if ($sname eq "state") { + my $rval = AttrVal($hash->{NAME},"restoreOnStartup","last"); + $rval = "last" if ( $rval ne "on" && $rval ne "off" ); + $sval = $rval eq "last" ? $sval : $rval; + #RPI_GPIO_Set($hash,$hash->{NAME},$sname,$sval); + RPI_GPIO_Set($hash,$hash->{NAME},$sval); + Log3 $hash, 4, "OUTPUT $hash->{NAME}: STATE wiederhergestellt auf $sval (restoreOnStartup=$rval)"; + } + } elsif ( AttrVal($hash->{NAME},"direction","") eq "input") { + if ($sname eq "Toggle") { + $hash->{READINGS}{$sname}{VAL} = $sval; + $hash->{READINGS}{$sname}{TIME} = $tim; + Log3 $hash, 4, "INPUT $hash->{NAME}: $sname wiederhergestellt auf $sval"; + #RPI_GPIO_Set($hash,$hash->{NAME},$sval); + if ((AttrVal($hash->{NAME},"toggletostate","") eq "yes")) { + readingsBeginUpdate($hash); + readingsBulkUpdate($hash, 'state', $sval); + readingsEndUpdate($hash, 1); + Log3 $hash, 4, "INPUT $hash->{NAME}: STATE wiederhergestellt auf $sval"; + } + } elsif ($sname eq "Counter") { + $hash->{READINGS}{$sname}{VAL} = $sval; + $hash->{READINGS}{$sname}{TIME} = $tim; + Log3 $hash, 4, "INPUT $hash->{NAME}: $sname wiederhergestellt auf $sval"; + } + } + } + return; } sub RPI_GPIO_Attr(@) { - my (undef, $name, $attr, $val) = @_; - my $hash = $defs{$name}; - my $msg = ''; + my (undef, $name, $attr, $val) = @_; + my $hash = $defs{$name}; + my $msg = ''; - if ($attr eq 'poll_interval') { - if ( defined($val) ) { - if ( looks_like_number($val) && $val > 0) { - RemoveInternalTimer($hash); - InternalTimer(1, 'RPI_GPIO_Poll', $hash, 0); - } else { - $msg = "$hash->{NAME}: Wrong poll intervall defined. poll_interval must be a number > 0"; - } - } else { #wird auch aufgerufen wenn $val leer ist, aber der attribut wert wird auf 1 gesetzt - RemoveInternalTimer($hash); - } - } - - if ($attr eq 'longpressinterval') { - if ( defined($val) ) { - unless ( looks_like_number($val) && $val >= 0.1 && $val <= 10 ) { - $msg = "$hash->{NAME}: Wrong longpress time defined. Value must be a number between 0.1 and 10"; - } - } - } - - if ($attr eq 'direction') { - if (!$val) { #$val nicht definiert: Einstellungen löschen - $msg = "$hash->{NAME}: no direction value. Use input output"; - } elsif ($val eq "input") { - #RPI_GPIO_fileaccess($hash, "direction", "in"); - RPI_GPIO_exuexpin($hash, "in"); - Log3 $hash, 5, "$hash->{NAME}: direction: input"; - } elsif( ( AttrVal($hash->{NAME}, "interrupt", "none") ) ne ( "none" ) ) { - $msg = "$hash->{NAME}: Delete attribute interrupt or set it to none for output direction"; - } elsif ($val eq "output") { - #RPI_GPIO_fileaccess($hash, "direction", "out"); - RPI_GPIO_exuexpin($hash, "out"); - Log3 $hash, 5, "$hash->{NAME}: direction: output"; - } else { - $msg = "$hash->{NAME}: Wrong $attr value. Use input output"; - } - } - - if ($attr eq 'interrupt') { - if ( !$val || ($val eq "none") ) { - RPI_GPIO_fileaccess($hash, "edge", "none"); - RPI_GPIO_inthandling($hash, "stop"); - Log3 $hash, 5, "$hash->{NAME}: interrupt: none"; - } elsif (( AttrVal($hash->{NAME}, "direction", "output") ) eq ( "output" )) { - $msg = "$hash->{NAME}: Wrong direction value defined for interrupt. Use input"; - } elsif ($val eq "falling") { - RPI_GPIO_fileaccess($hash, "edge", "falling"); - RPI_GPIO_inthandling($hash, "start"); - Log3 $hash, 5, "$hash->{NAME}: interrupt: falling"; - } elsif ($val eq "rising") { - RPI_GPIO_fileaccess($hash, "edge", "rising"); - RPI_GPIO_inthandling($hash, "start"); - Log3 $hash, 5, "$hash->{NAME}: interrupt: rising"; - } elsif ($val eq "both") { - RPI_GPIO_fileaccess($hash, "edge", "both"); - RPI_GPIO_inthandling($hash, "start"); - Log3 $hash, 5, "$hash->{NAME}: interrupt: both"; - } else { - $msg = "$hash->{NAME}: Wrong $attr value. Use none, falling, rising or both"; - } - } - #Tastfunktion: bei jedem Tastendruck wird State invertiert - if ($attr eq 'toggletostate') { - unless ( !$val || ($val eq ("yes" || "no") ) ) { - $msg = "$hash->{NAME}: Wrong $attr value. Use yes or no"; - } - } + if ($attr eq 'poll_interval') { + if ( defined($val) ) { + if ( looks_like_number($val) && $val > 0) { + RemoveInternalTimer($hash); + InternalTimer(1, 'RPI_GPIO_Poll', $hash, 0); + } else { + $msg = "$hash->{NAME}: Wrong poll intervall defined. poll_interval must be a number > 0"; + } + } else { #wird auch aufgerufen wenn $val leer ist, aber der attribut wert wird auf 1 gesetzt + RemoveInternalTimer($hash); + } + } + if ($attr eq 'longpressinterval') { + if ( defined($val) ) { + unless ( looks_like_number($val) && $val >= 0.1 && $val <= 10 ) { + $msg = "$hash->{NAME}: Wrong longpress time defined. Value must be a number between 0.1 and 10"; + } + } + } + if ($attr eq 'direction') { + if (!$val) { #$val nicht definiert: Einstellungen loeschen + $msg = "$hash->{NAME}: no direction value. Use input output"; + } elsif ($val eq "input") { + delete($hash->{dir_not_set}); + RPI_GPIO_fileaccess($hash, "direction", "in"); + #RPI_GPIO_exuexpin($hash, "in"); + Log3 $hash, 5, "$hash->{NAME}: set attr direction: input"; + } elsif( ( AttrVal($hash->{NAME}, "interrupt", "none") ) ne ( "none" ) ) { + $msg = "$hash->{NAME}: Delete attribute interrupt or set it to none for output direction"; + } elsif ($val eq "output") { + unless ($hash->{dir_not_set}) { #direction bei output noch nicht setzten (erfolgt bei erstem schreiben vom Wert um kurzes umschalten beim fhem start zu unterbinden) + RPI_GPIO_fileaccess($hash, "direction", "out"); + #RPI_GPIO_exuexpin($hash, "out"); + Log3 $hash, 5, "$hash->{NAME}: set attr direction: output"; + } else { + Log3 $hash, 5, "$hash->{NAME}: set attr direction: output vorerst NICHT"; + } + } else { + $msg = "$hash->{NAME}: Wrong $attr value. Use input output"; + } + } + if ($attr eq 'interrupt') { + if ( !$val || ($val eq "none") ) { + RPI_GPIO_fileaccess($hash, "edge", "none"); + RPI_GPIO_inthandling($hash, "stop"); + Log3 $hash, 5, "$hash->{NAME}: set attr interrupt: none"; + } elsif (( AttrVal($hash->{NAME}, "direction", "output") ) eq ( "output" )) { + $msg = "$hash->{NAME}: Wrong direction value defined for interrupt. Use input"; + } elsif ($val eq "falling") { + RPI_GPIO_fileaccess($hash, "edge", "falling"); + RPI_GPIO_inthandling($hash, "start"); + Log3 $hash, 5, "$hash->{NAME}: set attr interrupt: falling"; + } elsif ($val eq "rising") { + RPI_GPIO_fileaccess($hash, "edge", "rising"); + RPI_GPIO_inthandling($hash, "start"); + Log3 $hash, 5, "$hash->{NAME}: set attr interrupt: rising"; + } elsif ($val eq "both") { + RPI_GPIO_fileaccess($hash, "edge", "both"); + RPI_GPIO_inthandling($hash, "start"); + Log3 $hash, 5, "$hash->{NAME}: set attr interrupt: both"; + } else { + $msg = "$hash->{NAME}: Wrong $attr value. Use none, falling, rising or both"; + } + } +#Tastfunktion: bei jedem Tastendruck wird State invertiert + if ($attr eq 'toggletostate') { + unless ( !$val || ($val eq ("yes" || "no") ) ) { + $msg = "$hash->{NAME}: Wrong $attr value. Use yes or no"; + } + } #invertierte Logik - if ($attr eq 'active_low') { - if ( !$val || ($val eq "no" ) ) { - RPI_GPIO_fileaccess($hash, "active_low", "0"); - #Log 1, "$hash->{NAME}: interrupt: none"; - } elsif ($val eq "yes") { - RPI_GPIO_fileaccess($hash, "active_low", "1"); - } else { - $msg = "$hash->{NAME}: Wrong $attr value. Use yes or no"; - } - } + if ($attr eq 'active_low') { + if ( !$val || ($val eq "no" ) ) { + RPI_GPIO_fileaccess($hash, "active_low", "0"); + Log3 $hash, 5, "$hash->{NAME}: set attr active_low: no"; + } elsif ($val eq "yes") { + RPI_GPIO_fileaccess($hash, "active_low", "1"); + Log3 $hash, 5, "$hash->{NAME}: set attr active_low: yes"; + } else { + $msg = "$hash->{NAME}: Wrong $attr value. Use yes or no"; + } + } #Entprellzeit - if ($attr eq 'debounce_in_ms') { - if ( $val && ( ($val > 250) || ($val < 0) ) ) { - $msg = "$hash->{NAME}: debounce_in_ms value to big. Use 0 to 250"; - } - } - - if ($attr eq 'pud_resistor') { - my $pud; - if ( !$val ) { - } elsif ($val eq "off") { - $pud = $gpioprg.' -g mode '.$hash->{RPI_pin}.' tri'; - $pud = `$pud`; - } elsif ($val eq "up") { - $pud = $gpioprg.' -g mode '.$hash->{RPI_pin}.' up'; - $pud = `$pud`; - } elsif ($val eq "down") { - $pud = $gpioprg.' -g mode '.$hash->{RPI_pin}.' down'; - $pud = `$pud`; - } else { - $msg = "$hash->{NAME}: Wrong $attr value. Use off, up or down"; - } - } - return ($msg) ? $msg : undef; - } + if ($attr eq 'debounce_in_ms') { + if ( $val && ( ($val > 250) || ($val < 0) ) ) { + $msg = "$hash->{NAME}: debounce_in_ms value to big. Use 0 to 250"; + } + } + if ($attr eq 'pud_resistor') {#nur fuer Raspberry (ueber gpio utility) + my $pud; + if ( defined(my $ret = RPI_GPIO_CHECK_GPIO_UTIL($gpioprg)) ) { + Log3 $hash, 1, "$hash->{NAME}: unable to change pud resistor:" . $ret; + return "$hash->{NAME}: " . $ret; + } else { + if ( !$val ) { + } elsif ($val eq "off") { + $pud = $gpioprg.' -g mode '.$hash->{RPI_pin}.' tri'; + $pud = `$pud`; + } elsif ($val eq "up") { + $pud = $gpioprg.' -g mode '.$hash->{RPI_pin}.' up'; + $pud = `$pud`; + } elsif ($val eq "down") { + $pud = $gpioprg.' -g mode '.$hash->{RPI_pin}.' down'; + $pud = `$pud`; + } else { + $msg = "$hash->{NAME}: Wrong $attr value. Use off, up or down"; + } + } + } + return ($msg) ? $msg : undef; +} sub RPI_GPIO_Poll($) { #for attr poll_intervall -> readout pin value - my ($hash) = @_; - my $name = $hash->{NAME}; - RPI_GPIO_updatevalue($hash); - my $pollInterval = AttrVal($hash->{NAME}, 'poll_interval', 0); - if ($pollInterval > 0) { - InternalTimer(gettimeofday() + ($pollInterval * 60), 'RPI_GPIO_Poll', $hash, 0); - } - return; + my ($hash) = @_; + my $name = $hash->{NAME}; + RPI_GPIO_updatevalue($hash); + my $pollInterval = AttrVal($hash->{NAME}, 'poll_interval', 0); + if ($pollInterval > 0) { + InternalTimer(gettimeofday() + ($pollInterval * 60), 'RPI_GPIO_Poll', $hash, 0); + } + return; } sub RPI_GPIO_Undef($$) { - my ($hash, $arg) = @_; - if ( defined (AttrVal($hash->{NAME}, "poll_interval", undef)) ) { - RemoveInternalTimer($hash); - } - if ( ( AttrVal($hash->{NAME}, "interrupt", "none") ) ne ( "none" ) ) { - delete $selectlist{$hash->{NAME}}; - close($hash->{filehandle}); - } - #unexport Pin alte Version - #my $uexp = IO::File->new("> /sys/class/gpio/unexport"); - #print $uexp "$hash->{RPI_pin}"; - #$uexp->close; - #alternative unexport Pin: - RPI_GPIO_exuexpin($hash, "unexport"); - return undef; + my ($hash, $arg) = @_; + if ( defined (AttrVal($hash->{NAME}, "poll_interval", undef)) ) { + RemoveInternalTimer($hash); + } + if ( ( AttrVal($hash->{NAME}, "interrupt", "none") ) ne ( "none" ) ) { + delete $selectlist{$hash->{NAME}}; + close($hash->{filehandle}); + } + if (-w "$gpiodir/unexport") {#unexport Pin alte Version + my $uexp = IO::File->new("> $gpiodir/unexport"); + print $uexp "$hash->{RPI_pin}"; + $uexp->close; + } else {#alternative unexport Pin: + RPI_GPIO_exuexpin($hash, "unexport"); + } + Log3 $hash, 1, "$hash->{NAME}: entfernt"; + return undef; } sub RPI_GPIO_Except($) { #called from main if an interrupt occured - my ($hash) = @_; - #seek($hash->{filehandle},0,0); #an Anfang der Datei springen (ist nötig falls vorher schon etwas gelesen wurde) - #chomp ( my $firstval = $hash->{filehandle}->getline ); #aktuelle Zeile auslesen und Endezeichen entfernen - #my $acttime = gettimeofday(); - my $eval = RPI_GPIO_fileaccess($hash, "edge"); #Eintstellung Flankensteuerung auslesen - my ($valst, $valalt, $valto, $valcnt, $vallp) = undef; - my $debounce_time = AttrVal($hash->{NAME}, "debounce_in_ms", "0"); #Wartezeit zum entprellen - if( $debounce_time ne "0" ) { - $debounce_time /= 1000; - Log3 $hash, 4, "Wartezeit: $debounce_time ms"; - select(undef, undef, undef, $debounce_time); - } - - seek($hash->{filehandle},0,0); #an Anfang der Datei springen (ist nötig falls vorher schon etwas gelesen wurde) - chomp ( my $val = $hash->{filehandle}->getline ); #aktuelle Zeile auslesen und Endezeichen entfernen - - if ( ( $val == 1) && ( $eval ne ("falling") ) ) { - $valst = "on"; - $valalt = "high"; - } elsif ( ( $val == 0 ) && ($eval ne "rising" ) ) { - $valst = "off"; - $valalt = "low"; - } - if ( ( ($eval eq "rising") && ( $val == 1 ) ) || ( ($eval eq "falling") && ( $val == 0 ) ) ) { #nur bei Trigger auf steigende / fallende Flanke -#Togglefunktion - if (!defined($hash->{READINGS}{Toggle}{VAL})) { #Togglewert existiert nicht -> anlegen - Log3 $hash, 5, "Toggle war nicht def"; - $valto = "on"; - } elsif ( $hash->{READINGS}{Toggle}{VAL} eq "off" ) { #Togglewert invertieren - Log3 $hash, 5, "Toggle war auf $hash->{READINGS}{Toggle}{VAL}"; - $valto = "on"; - } else { - Log3 $hash, 5, "Toggle war auf $hash->{READINGS}{Toggle}{VAL}"; - $valto = "off"; - } - Log3 $hash, 5, "Toggle ist jetzt $valto"; - if (( AttrVal($hash->{NAME}, "toggletostate", "no") ) eq ( "yes" )) { #wenn Attr "toggletostate" gesetzt auch die Variable für den STATE wert setzen - $valst = $valto; - } -#Zählfunktion - if (!defined($hash->{READINGS}{Counter}{VAL})) { #Zähler existiert nicht -> anlegen - Log3 $hash, 5, "Zähler war nicht def"; - $valcnt = "1"; - } else { - $valcnt = $hash->{READINGS}{Counter}{VAL} + 1; - Log3 $hash, 5, "Zähler ist jetzt $valcnt"; - } -#langer Testendruck - } elsif ($eval eq "both") { - if ( $val == 1 ) { - my $lngpressInterval = AttrVal($hash->{NAME}, "longpressinterval", "1"); - InternalTimer(gettimeofday() + $lngpressInterval, 'RPI_GPIO_longpress', $hash, 0); - #$hash->{Anzeit} = gettimeofday(); - } else { - RemoveInternalTimer('RPI_GPIO_longpress'); - $vallp = 'off'; - #my $zeit = $acttime; - #$zeit -= $hash->{Anzeit}; - #Log3 $hash, 5, "Anzeit: $zeit"; - #readingsBeginUpdate($hash); - #readingsBulkUpdate($hash, 'Anzeit', $zeit); - #readingsEndUpdate($hash, 1); + my ($hash) = @_; + #seek($hash->{filehandle},0,0); #an Anfang der Datei springen (ist noetig falls vorher schon etwas gelesen wurde) + #chomp ( my $firstval = $hash->{filehandle}->getline ); #aktuelle Zeile auslesen und Endezeichen entfernen + #my $acttime = gettimeofday(); + my $eval = RPI_GPIO_fileaccess($hash, "edge"); #Eintstellung Flankensteuerung auslesen + my ($valst, $valalt, $valto, $valcnt, $vallp) = undef; + my $debounce_time = AttrVal($hash->{NAME}, "debounce_in_ms", "0"); #Wartezeit zum entprellen + if( $debounce_time ne "0" ) { + $debounce_time /= 1000; + Log3 $hash, 4, "Wartezeit: $debounce_time ms"; + select(undef, undef, undef, $debounce_time); } - } - - delete ($hash->{READINGS}{Toggle}) if ($eval ne ("rising" || "falling")); #Reading Toggle löschen wenn Edge weder "rising" noch "falling" - delete ($hash->{READINGS}{Longpress}) if ($eval ne "both"); #Reading Longpress löschen wenn edge nicht "both" - readingsBeginUpdate($hash); - readingsBulkUpdate($hash, 'Pinlevel', $valalt); - readingsBulkUpdate($hash, 'state', $valst); - readingsBulkUpdate($hash, 'Toggle', $valto) if ($valto); - readingsBulkUpdate($hash, 'Counter', $valcnt) if ($valcnt); - readingsBulkUpdate($hash, 'Longpress', $vallp) if ($vallp); - readingsEndUpdate($hash, 1); - #Log3 $hash, 5, "RPIGPIO: Except ausgelöst: $hash->{NAME}, Wert: $val, edge: $eval,vt: $valto, $debounce_time s: $firstval"; + + seek($hash->{filehandle},0,0); #an Anfang der Datei springen (ist noetig falls vorher schon etwas gelesen wurde) + chomp ( my $val = $hash->{filehandle}->getline ); #aktuelle Zeile auslesen und Endezeichen entfernen + + if ( ( $val == 1) && ( $eval ne ("falling") ) ) { + $valst = "on"; + $valalt = "high"; + } elsif ( ( $val == 0 ) && ($eval ne "rising" ) ) { + $valst = "off"; + $valalt = "low"; + } + if ( ( ($eval eq "rising") && ( $val == 1 ) ) || ( ($eval eq "falling") && ( $val == 0 ) ) ) { #nur bei Trigger auf steigende / fallende Flanke +#Togglefunktion + if (!defined($hash->{READINGS}{Toggle}{VAL})) { #Togglewert existiert nicht -> anlegen + Log3 $hash, 5, "Toggle war nicht def"; + $valto = "on"; + } elsif ( $hash->{READINGS}{Toggle}{VAL} eq "off" ) { #Togglewert invertieren + Log3 $hash, 5, "Toggle war auf $hash->{READINGS}{Toggle}{VAL}"; + $valto = "on"; + } else { + Log3 $hash, 5, "Toggle war auf $hash->{READINGS}{Toggle}{VAL}"; + $valto = "off"; + } + Log3 $hash, 5, "Toggle ist jetzt $valto"; + if (( AttrVal($hash->{NAME}, "toggletostate", "no") ) eq ( "yes" )) { #wenn Attr "toggletostate" gesetzt auch die Variable fuer den STATE wert setzen + $valst = $valto; + } +#Zaehlfunktion + if (!defined($hash->{READINGS}{Counter}{VAL})) { #Zaehler existiert nicht -> anlegen + Log3 $hash, 5, "Zaehler war nicht def"; + $valcnt = "1"; + } else { + $valcnt = $hash->{READINGS}{Counter}{VAL} + 1; + Log3 $hash, 5, "Zaehler ist jetzt $valcnt"; + } +#langer Testendruck + } elsif ($eval eq "both") { + if ( $val == 1 ) { + my $lngpressInterval = AttrVal($hash->{NAME}, "longpressinterval", "1"); + InternalTimer(gettimeofday() + $lngpressInterval, 'RPI_GPIO_longpress', $hash, 0); + #$hash->{Anzeit} = gettimeofday(); + } else { + RemoveInternalTimer('RPI_GPIO_longpress'); + $vallp = 'off'; + #my $zeit = $acttime; + #$zeit -= $hash->{Anzeit}; + #Log3 $hash, 5, "Anzeit: $zeit"; + #readingsBeginUpdate($hash); + #readingsBulkUpdate($hash, 'Anzeit', $zeit); + #readingsEndUpdate($hash, 1); + } + } + + delete ($hash->{READINGS}{Toggle}) if ($eval ne ("rising" || "falling")); #Reading Toggle loeschen wenn Edge weder "rising" noch "falling" + delete ($hash->{READINGS}{Longpress}) if ($eval ne "both"); #Reading Longpress loeschen wenn edge nicht "both" + readingsBeginUpdate($hash); + readingsBulkUpdate($hash, 'Pinlevel', $valalt); + readingsBulkUpdate($hash, 'state', $valst); + readingsBulkUpdate($hash, 'Toggle', $valto) if ($valto); + readingsBulkUpdate($hash, 'Counter', $valcnt) if ($valcnt); + readingsBulkUpdate($hash, 'Longpress', $vallp) if ($vallp); + readingsEndUpdate($hash, 1); + #Log3 $hash, 5, "RPIGPIO: Except ausgeloest: $hash->{NAME}, Wert: $val, edge: $eval,vt: $valto, $debounce_time s: $firstval"; } sub RPI_GPIO_longpress($) { #for reading longpress - my ($hash) = @_; - my $name = $hash->{NAME}; - my $val = RPI_GPIO_fileaccess($hash, "value"); - if ($val == 1) { - readingsBeginUpdate($hash); - readingsBulkUpdate($hash, 'Longpress', 'on'); - readingsEndUpdate($hash, 1); - } + my ($hash) = @_; + my $name = $hash->{NAME}; + my $val = RPI_GPIO_fileaccess($hash, "value"); + if ($val == 1) { + readingsBeginUpdate($hash); + readingsBulkUpdate($hash, 'Longpress', 'on'); + readingsEndUpdate($hash, 1); + } } sub RPI_GPIO_dblclick($) { @@ -416,140 +455,185 @@ sub RPI_GPIO_dblclick($) { } sub RPI_GPIO_updatevalue($) { #update value for Input devices - my ($hash) = @_; - my $val = RPI_GPIO_fileaccess($hash, "value"); - if ( defined ($val) ) { - my ($valst, $valalt) = undef; - if ( $val == 1) { - $valst = "on"; - $valalt = "high"; - } elsif ( $val == 0 ) { - $valst = "off"; - $valalt = "low"; - } - readingsBeginUpdate($hash); - readingsBulkUpdate($hash, 'Pinlevel', $valalt); - readingsBulkUpdate($hash, 'state', $valst) if (( AttrVal($hash->{NAME}, "toggletostate", "no") ) eq ( "no" )); - readingsEndUpdate($hash, 1); - } else { - Log3 $hash, 1, "$hash->{NAME}: readout of Pinvalue fail"; - } + my ($hash) = @_; + my $val = RPI_GPIO_fileaccess($hash, "value"); + if ( defined ($val) ) { + my ($valst, $valalt) = undef; + if ( $val == 1) { + $valst = "on"; + $valalt = "high"; + } elsif ( $val == 0 ) { + $valst = "off"; + $valalt = "low"; + } + readingsBeginUpdate($hash); + readingsBulkUpdate($hash, 'Pinlevel', $valalt); + readingsBulkUpdate($hash, 'state', $valst) if (( AttrVal($hash->{NAME}, "toggletostate", "no") ) eq ( "no" )); + readingsEndUpdate($hash, 1); + } else { + Log3 $hash, 1, "$hash->{NAME}: readout of Pinvalue fail"; + } } -sub RPI_GPIO_fileaccess($$;$) { #Fileaccess for GPIO base directory +sub RPI_GPIO_fileaccess($$;$) { #Fileaccess for GPIO base directory #my ($hash, $fname, $value) = @_; - my ($hash, @args) = @_; - my $fname = $args[0]; - my $pinroot = qq($gpiodir/gpio$hash->{RPI_pin}); - my $file =qq($pinroot/$fname); - if (int(@args) < 2){ - my $fh = IO::File->new("< $file"); - if (defined $fh) { - chomp ( my $pinvalue = $fh->getline ); - $fh->close; - return $pinvalue; - } - else { - Log3 $hash, 1, "Can't open file: $hash->{NAME}, $fname"; - } - } else { - my $value = $args[1]; - my $fh = IO::File->new("> $file"); - if (defined $fh) { - print $fh "$value"; - $fh->close; - } - else { - Log3 $hash, 1, "Can't open file: $hash->{NAME}, $fname"; - } - } + my ($hash, @args) = @_; + my $fname = $args[0]; + my $pinroot = qq($gpiodir/gpio$hash->{RPI_pin}); + my $file =qq($pinroot/$fname); + Log3 $hash, 5, "$hash->{NAME}, in fileaccess: $fname " . (defined($args[1])?$args[1]:""); + + if ($hash->{dir_not_set} && $fname eq "value") { #direction setzen (bei output direkt status mit schreiben) + delete($hash->{dir_not_set}); + my $dir = AttrVal($hash->{NAME},"direction","input"); + $dir = $dir eq "input" ? "in" : "out"; + if ($dir eq "out" && $fname eq "value" && defined($args[1])) { + my $al = AttrVal($hash->{NAME},"active_low","off"); + my $lev = $al eq "on" ? 0 : 1; + $dir = ($args[1] == $lev ? "high" : "low") + } + #$dir = ($args[1] == 1 ? "high" : "low") if ($dir eq "out" && $fname eq "value" && defined($args[1])); + RPI_GPIO_fileaccess($hash, "direction", $dir); + Log3 $hash, 4, "$hash->{NAME}: direction gesetzt auf $dir"; + } + + if (int(@args) < 2){ + my $fh = IO::File->new("< $file"); + if (defined $fh) { + chomp ( my $pinvalue = $fh->getline ); + $fh->close; + return $pinvalue; + } else { + Log3 $hash, 1, "Can't open file: $hash->{NAME}, $fname"; + } + } else { + my $value = $args[1]; + if ($fname eq "direction" && (not -w $file)) { #wenn direction und diese nicht schreibbar mit gpio utility versuchen + Log3 $hash, 4, "$hash->{NAME}: direction ueber gpio utility einstellen"; + RPI_GPIO_exuexpin($hash, $value); + #if ( defined(my $ret = RPI_GPIO_CHECK_GPIO_UTIL($gpioprg)) ) { + # Log3 $hash, 1, "$hash->{NAME}: " . $ret; + #} else { + #my $exp = $gpioprg.' -g mode '.$hash->{RPI_pin}. ' '.$value; + #$exp = `$exp`; + #} + } else { + my $fh = IO::File->new("> $file"); + if (defined $fh) { + print $fh "$value"; + $fh->close; + } else { + Log3 $hash, 1, "Can't open file: $hash->{NAME}, $fname"; + } + } + } +} + +sub RPI_GPIO_exuexpin($$) { #export, unexport and direction Pin via GPIO utility + my ($hash, $dir) = @_; + my $sw; + if ($dir eq "unexport") { + $sw = $dir; + $dir = ""; + } else { + $sw = "export"; + $dir = " ".$dir; + } + if ( defined(my $ret = RPI_GPIO_CHECK_GPIO_UTIL($gpioprg)) ) { + Log3 $hash, 1, "$hash->{NAME}: " . $ret; + } else { + my $exp = $gpioprg.' '.$sw.' '.$hash->{RPI_pin}.$dir; + $exp = `$exp`; + } + ####################### } -sub RPI_GPIO_exuexpin($$) { #export and unexport Pin via GPIO utility - my ($hash, $dir) = @_; - my $sw; - if ($dir eq "unexport") { - $sw = $dir; - $dir = ""; - } else { - $sw = "export"; - $dir = " ".$dir; - } - #alternative export Pin - if(-e $gpioprg) { - if(-x $gpioprg) { - if(-u $gpioprg) { - my $exp = $gpioprg.' '.$sw.' '.$hash->{RPI_pin}.$dir; - $exp = `$exp`; - } else { - Log3 $hash, 1, "file $gpioprg is not setuid"; - } - } else { - Log3 $hash, 1, "file $gpioprg is not executable"; - } - } else { - Log3 $hash, 1, "file $gpioprg doesnt exist"; - } - ####################### - +sub RPI_GPIO_CHECK_GPIO_UTIL { + my ($gpioprg) = @_; + my $ret = undef; + #unless (defined($hash->{gpio_util_exists})) { + if(-e $gpioprg) { + if(-x $gpioprg) { + unless(-u $gpioprg) { + $ret = "file $gpioprg is not setuid"; + } + } else { + $ret = "file $gpioprg is not executable"; + } + } else { + $ret = "file $gpioprg doesnt exist"; + } + return $ret; } sub RPI_GPIO_inthandling($$) { #start/stop Interrupthandling - my ($hash, $arg) = @_; - my $msg = ''; - if ( $arg eq "start") { - #FH für value-datei - my $pinroot = qq($gpiodir/gpio$hash->{RPI_pin}); - my $valfile = qq($pinroot/value); - $hash->{filehandle} = IO::File->new("< $valfile"); - if (!defined $hash->{filehandle}) { - $msg = "Can't open file: $hash->{NAME}, $valfile"; - } else { - $selectlist{$hash->{NAME}} = $hash; - $hash->{EXCEPT_FD} = fileno($hash->{filehandle}); - my $pinvalue = $hash->{filehandle}->getline; - Log3 $hash, 5, "Datei: $valfile, FH: $hash->{filehandle}, EXCEPT_FD: $hash->{EXCEPT_FD}, akt. Wert: $pinvalue"; - } - } else { - delete $selectlist{$hash->{NAME}}; - close($hash->{filehandle}); - } + my ($hash, $arg) = @_; + my $msg = ''; + if ( $arg eq "start") { + #FH fuer value-datei + my $pinroot = qq($gpiodir/gpio$hash->{RPI_pin}); + my $valfile = qq($pinroot/value); + $hash->{filehandle} = IO::File->new("< $valfile"); + if (!defined $hash->{filehandle}) { + $msg = "Can't open file: $hash->{NAME}, $valfile"; + } else { + $selectlist{$hash->{NAME}} = $hash; + $hash->{EXCEPT_FD} = fileno($hash->{filehandle}); + my $pinvalue = $hash->{filehandle}->getline; + Log3 $hash, 5, "Datei: $valfile, FH: $hash->{filehandle}, EXCEPT_FD: $hash->{EXCEPT_FD}, akt. Wert: $pinvalue"; + } + } else { + delete $selectlist{$hash->{NAME}}; + close($hash->{filehandle}); + } } 1; + =pod =begin html

RPI_GPIO

    - -

    - Raspberry Pi offers direct access to several GPIO via header P1 (and P5 on V2). The Pinout is shown in table under define. - With this module you are able to access these GPIO's directly as output or input. For input you can use either polling or interrupt mode

    - Warning: Never apply any external voltage to an output configured pin! GPIO's internal logic operate with 3,3V. Don't exceed this Voltage!

    - - preliminary:
    - GPIO Pins accessed by sysfs. The files are located in folder /system/class/gpio which can be only accessed by root. - This module uses gpio utility from WiringPi library to export and change access rights of GPIO's
    - Install WiringPi: -

    -  sudo apt-get update
    -  sudo apt-get upgrade
    -  sudo apt-get install git-core
    -  git clone git://git.drogon.net/wiringPi
    -  cd wiringPi
    -  ./build
    -  sudo adduser fhem gpio
    - Thats all

    - - - Define -
      - define RPI_GPIO <GPIO number>

      - all usable GPIO number are in the following tables

      - - + + Raspberry Pi offers direct access to several GPIO via header P1 (and P5 on V2). The Pinout is shown in table under define. + With this module you are able to access these GPIO's directly as output or input. For input you can use either polling or interrupt mode
      + In addition to the Raspberry Pi, also BBB, Cubie and almost every linux system which provides gpio access in userspace is supported.
      + Warning: Never apply any external voltage to an output configured pin! GPIO's internal logic operate with 3,3V. Don't exceed this Voltage!

      + preliminary:
      + GPIO Pins accessed by sysfs. The files are located in folder /system/class/gpio and belong to the gpio group (on actual Raspbian distributions since jan 2014).
      + After execution of following commands, GPIO's are usable whithin PRI_GPIO:
      +
        + sudo adduser fhem gpio
        + sudo reboot +

      + On older Raspbian distributions and if attribute pud_resistor shall be used, aditionally gpio utility from WiringPi + library must be installed to export and change access rights of GPIO's, or to set the internal pullup/down resistor.
      + Installation WiringPi:
      +
        + sudo apt-get update
        + sudo apt-get upgrade
        + sudo apt-get install git-core
        + git clone git://git.drogon.net/wiringPi
        + cd wiringPi + ./build +

      + On Linux systeme where /system/class/gpio can only accessed as root, GPIO's must exported and their access rights changed before FHEM starts.
      + This can be done in /etc/rc.local (Examole for GPIO22 and 23):
      +
        + echo 22 > /sys/class/gpio/export
        + echo 23 > /sys/class/gpio/export
        + chown -R fhem:root /sys/devices/virtual/gpio/*
        + chown -R fhem:root /sys/class/gpio/*
        +

      + + Define +
        + define RPI_GPIO <GPIO number>

        + all usable GPIO number are in the following tables

        + +
      PCB Revision 1 P1 pin header @@ -649,6 +733,10 @@ sub RPI_GPIO_inthandling($$) { #start/stop Interrupthandling Sets the GPIO direction to input or output.
      Default: input, valid values: input, output

      +
    • active_low
      + Inverts logical value
      + Default: off, valid values: on, off

      +
    • interrupt
      can only be used with GPIO configured as input
      enables edge detection for GPIO pin
      @@ -668,6 +756,7 @@ sub RPI_GPIO_inthandling($$) { #start/stop Interrupthandling
    • pud_resistor
      Sets the internal pullup/pulldown resistor
      + Works only with installed gpio urility from WiringPi Library.
      Default: -, valid values: off, up, down

    • debounce_in_ms
      @@ -676,7 +765,7 @@ sub RPI_GPIO_inthandling($$) { #start/stop Interrupthandling
    • restoreOnStartup
      Restore Readings and sets after reboot
      - Default: on, valid values: on, off

      + Default: last, valid values: last, on, off, no

    • longpressinterval
      works with interrupt set to both only
      @@ -697,35 +786,46 @@ sub RPI_GPIO_inthandling($$) { #start/stop Interrupthandling

      RPI_GPIO

        -

        Das Raspberry Pi ermöglicht direkten Zugriff zu einigen GPIO's über den Pfostenstecker P1 (und P5 bei V2). Die Steckerbelegung ist in den Tabellen unter Define zu finden. - Dieses Modul ermöglicht es, die herausgefühten GPIO's direkt als Ein- und Ausgang zu benutzen. Die Eingänge können zyklisch abgefragt werden oder auch sofort bei Pegelwechsel gesetzt werden.

        + Dieses Modul ermöglicht es, die herausgeführten GPIO's direkt als Ein- und Ausgang zu benutzen. Die Eingänge können zyklisch abgefragt werden oder auch sofort bei Pegelwechsel gesetzt werden.
        + Neben dem Raspberry Pi können auch die GPIO's von BBB, Cubie und jedem Linuxsystem, das diese im Userspace zugägig macht, genutzt werden.
        Wichtig: Niemals Spannung an einen GPIO anlegen, der als Ausgang eingestellt ist! Die interne Logik der GPIO's arbeitet mit 3,3V. Ein überschreiten der 3,3V zerstört den GPIO und vielleicht auch den ganzen Prozessor!

        - Vorbereitung:
        - Auf GPIO Pins wird im Modul über sysfs zugegriffen. Die Dateien befinden sich unter /system/class/gpio und können nur mit root erreicht werden. - Dieses Modul nutzt das gpio Tool von der WiringPi. Bibliothek um GPIS zu exportieren und die korrekten Nutzerrechte zu setzen. - Installation WiringPi: -

        -  sudo apt-get update
        -  sudo apt-get upgrade
        -  sudo apt-get install git-core
        -  git clone git://git.drogon.net/wiringPi
        -  cd wiringPi
        -  ./build
        -  sudo adduser fhem gpio
        - Das wars!

        - - + Auf GPIO Pins wird im Modul über sysfs zugegriffen. Die Dateien befinden sich unter /system/class/gpio und sind in der aktuellen Raspbian Distribution (ab Jan 2014) in der Gruppe gpio.
        + Nach dem ausführen folgender Befehle sind die GPIO's von PRI_GPIO aus nutzbar:
        +
          + sudo adduser fhem gpio
          + sudo reboot +

        + Für ältere Raspbian Distributionen und wenn das Attribut pud_resistor verwendet werden soll, muss zusätzlich das gpio Tool der WiringPi + Bibliothek installiert werden, um GPIO's zu exportieren und die korrekten Nutzerrechte zu setzen, bzw. den internen Pullup/down Widerstand zu aktivieren.
        + Installation WiringPi:
        +
          + sudo apt-get update
          + sudo apt-get upgrade
          + sudo apt-get install git-core
          + git clone git://git.drogon.net/wiringPi
          + cd wiringPi + ./build +

        + Für Linux Systeme bei denen der Zugriff auf /system/class/gpio nur mit root Rechten erfolgen kann, müssen die GPIO's vor FHEM start exportiert und von den Rechten her angepasst werden.
        + Dazu in die /etc/rc.local folgendes einfügen (Beispiel für GPIO22 und 23):
        +
          + echo 22 > /sys/class/gpio/export
          + echo 23 > /sys/class/gpio/export
          + chown -R fhem:root /sys/devices/virtual/gpio/*
          + chown -R fhem:root /sys/class/gpio/*
          +

        + Define
          define <name> RPI_GPIO <GPIO number>

          Alle verfügbaren GPIO number sind in den folgenden Tabellen zu finden

    • -
      - PCB Revision 1 P1 pin header - +
      + PCB Revision 1 P1 pin header + @@ -822,13 +922,17 @@ sub RPI_GPIO_inthandling($$) { #start/stop Interrupthandling Setzt den GPIO auf Ein- oder Ausgang.
      Standard: input, gültige Werte: input, output

      +
    • active_low
      + Invertieren des logischen Wertes
      + Standard: off, gültige Werte: on, off

      +
    • interrupt
      kann nur gewählt werden, wenn der GPIO als Eingang konfiguriert ist
      Aktiviert Flankenerkennung für den GPIO
      bei jedem interrupt Ereignis werden die readings Pinlevel und state aktualisiert
      Standard: none, gültige Werte: none, falling, rising, both

      - Bei "both" wird ein reading Longpress angelegt, welches auf on gesetzt wird solange der Pin länger als 1s gedrückt wird
      - Bei "falling" und "rising" wird ein reading Toggle angelegt, das bei jedem Interruptereignis toggelt und das Reading Counter, das bei jedem Ereignis um 1 hochzählt

      + Bei "both" wird ein reading Longpress angelegt, welches auf on gesetzt wird solange der Pin länger als 1s gedrückt wird
      + Bei "falling" und "rising" wird ein reading Toggle angelegt, das bei jedem Interruptereignis toggelt und das Reading Counter, das bei jedem Ereignis um 1 hochzählt

    • poll_interval
      @@ -842,6 +946,7 @@ sub RPI_GPIO_inthandling($$) { #start/stop Interrupthandling
    • pud_resistor
      Interner Pullup/down Widerstand
      + Funktioniert aussließlich mit installiertem gpio Tool der WiringPi Bibliothek.
      Standard: -, gültige Werte: off, up, down

    • debounce_in_ms
      @@ -849,8 +954,8 @@ sub RPI_GPIO_inthandling($$) { #start/stop Interrupthandling Standard: 0, gültige Werte: Dezimalzahl

    • restoreOnStartup
      - Wiederherstellen der Portzust&äuml;nde nach Neustart
      - Standard: on, gültige Werte: on, off

      + Wiederherstellen der Portzustände nach Neustart
      + Standard: last, gültige Werte: last, on, off, no

    • longpressinterval
      Funktioniert nur bei auf both gesetztem Attribut interrupt
      @@ -864,4 +969,4 @@ sub RPI_GPIO_inthandling($$) { #start/stop Interrupthandling =end html_DE -=cut +=cut \ No newline at end of file
    • Function PinPin Function
      3,3V 1 2 5V
      GPIO 0 (SDA0)3 4