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/fhem@6207 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
klauswitt 2014-07-06 21:38:05 +00:00
parent a62c4a9f77
commit b75d80f1df

View File

@ -8,7 +8,7 @@
# define <name> RPI_GPIO <Pin> # define <name> RPI_GPIO <Pin>
# where <Pin> is one of RPi's GPIO # where <Pin> 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,6 +20,8 @@ use Scalar::Util qw(looks_like_number);
use IO::File; use IO::File;
use SetExtensions; use SetExtensions;
sub RPI_GPIO_fileaccess($$;$);
sub RPI_GPIO_Initialize($) { sub RPI_GPIO_Initialize($) {
my ($hash) = @_; my ($hash) = @_;
$hash->{DefFn} = "RPI_GPIO_Define"; $hash->{DefFn} = "RPI_GPIO_Define";
@ -33,7 +35,7 @@ sub RPI_GPIO_Initialize($) {
" direction:input,output pud_resistor:off,up,down" . " direction:input,output pud_resistor:off,up,down" .
" interrupt:none,falling,rising,both" . " interrupt:none,falling,rising,both" .
" toggletostate:no,yes active_low:no,yes" . " toggletostate:no,yes active_low:no,yes" .
" debounce_in_ms restoreOnStartup:no,yes" . " debounce_in_ms restoreOnStartup:no,yes,on,off,last" .
" longpressinterval " . " longpressinterval " .
"$readingFnAttributes"; "$readingFnAttributes";
} }
@ -62,7 +64,7 @@ sub RPI_GPIO_Define($$) {
"define <name> RPI_GPIO <GPIO>"; "define <name> RPI_GPIO <GPIO>";
} }
#Prüfen, ob GPIO bereits verwendet #Pruefen, ob GPIO bereits verwendet
foreach my $dev (devspec2array("TYPE=$hash->{TYPE}")) { foreach my $dev (devspec2array("TYPE=$hash->{TYPE}")) {
if ($args[2] eq InternalVal($dev,"RPI_pin","")) { if ($args[2] eq InternalVal($dev,"RPI_pin","")) {
return "GPIO $args[2] already used by $dev"; return "GPIO $args[2] already used by $dev";
@ -71,17 +73,38 @@ sub RPI_GPIO_Define($$) {
my $name = $args[0]; my $name = $args[0];
$hash->{RPI_pin} = $args[2]; $hash->{RPI_pin} = $args[2];
$hash->{dir_not_set} = 1;
#export Pin alte Version -> wird jetzt über direction gemacht (WiringPi Programm GPIO) if(-e "$gpiodir/gpio$hash->{RPI_pin}" && -w "$gpiodir/gpio$hash->{RPI_pin}/value" && -w "$gpiodir/gpio$hash->{RPI_pin}/direction") { #GPIO bereits exportiert?
#my $exp = IO::File->new("> /sys/class/gpio/export"); Log3 $hash, 4, "$name: gpio$hash->{RPI_pin} already exists";
#print $exp "$hash->{RPI_pin}"; #nix tun...ist ja schon da
#$exp->close; } elsif (-w "$gpiodir/export") { #gpio export Datei mit schreibrechten?
#select(undef, undef, undef, 0.4); #kurz warten bis Verzeichnis angelegt 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}";
}
# create default attributes
my $msg = CommandAttr(undef, $name . ' direction input');
return $msg if ($msg);
select(undef, undef, undef, 0.4);
$hash->{fhem}{interfaces} = "switch"; $hash->{fhem}{interfaces} = "switch";
return undef; return undef;
} }
@ -142,7 +165,6 @@ if ($cmd eq 'on') {
if(!defined($setsinpt{$cmd})) { if(!defined($setsinpt{$cmd})) {
return 'Unknown argument ' . $cmd . ', choose one of ' . join(' ', keys %setsinpt) return 'Unknown argument ' . $cmd . ', choose one of ' . join(' ', keys %setsinpt)
} else { } else {
} }
} }
} }
@ -154,25 +176,33 @@ if ($cmd eq 'on') {
sub RPI_GPIO_State($$$$) { #reload readings at FHEM start sub RPI_GPIO_State($$$$) { #reload readings at FHEM start
my ($hash, $tim, $sname, $sval) = @_; my ($hash, $tim, $sname, $sval) = @_;
Log3 $hash, 4, "$hash->{NAME}: $sname kann auf $sval wiederhergestellt werden $tim"; 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},"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") { if (AttrVal($hash->{NAME},"direction","") eq "output") {
$hash->{READINGS}{$sname}{VAL} = $sval; $hash->{READINGS}{$sname}{VAL} = $sval;
$hash->{READINGS}{$sname}{TIME} = $tim; $hash->{READINGS}{$sname}{TIME} = $tim;
Log3 $hash, 4, "OUTPUT $hash->{NAME}: $sname wiederhergestellt auf $sval"; Log3 $hash, 4, "OUTPUT $hash->{NAME}: $sname wiederhergestellt auf $sval";
if ($sname eq "state") { 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},$sname,$sval);
RPI_GPIO_Set($hash,$hash->{NAME},$sval); RPI_GPIO_Set($hash,$hash->{NAME},$sval);
Log3 $hash, 4, "OUTPUT $hash->{NAME}: STATE wiederhergestellt auf $sval"; Log3 $hash, 4, "OUTPUT $hash->{NAME}: STATE wiederhergestellt auf $sval (restoreOnStartup=$rval)";
} }
} elsif ( (AttrVal($hash->{NAME},"direction","") eq "input") && (AttrVal($hash->{NAME},"toggletostate","") eq "yes")) { } elsif ( AttrVal($hash->{NAME},"direction","") eq "input") {
if ($sname eq "Toggle") { if ($sname eq "Toggle") {
$hash->{READINGS}{$sname}{VAL} = $sval; $hash->{READINGS}{$sname}{VAL} = $sval;
$hash->{READINGS}{$sname}{TIME} = $tim; $hash->{READINGS}{$sname}{TIME} = $tim;
Log3 $hash, 4, "INPUT $hash->{NAME}: $sname wiederhergestellt auf $sval";
#RPI_GPIO_Set($hash,$hash->{NAME},$sval); #RPI_GPIO_Set($hash,$hash->{NAME},$sval);
if ((AttrVal($hash->{NAME},"toggletostate","") eq "yes")) {
readingsBeginUpdate($hash); readingsBeginUpdate($hash);
readingsBulkUpdate($hash, 'state', $sval); readingsBulkUpdate($hash, 'state', $sval);
readingsEndUpdate($hash, 1); readingsEndUpdate($hash, 1);
Log3 $hash, 4, "INPUT $hash->{NAME}: $sname und STATE wiederhergestellt auf $sval"; Log3 $hash, 4, "INPUT $hash->{NAME}: STATE wiederhergestellt auf $sval";
}
} elsif ($sname eq "Counter") { } elsif ($sname eq "Counter") {
$hash->{READINGS}{$sname}{VAL} = $sval; $hash->{READINGS}{$sname}{VAL} = $sval;
$hash->{READINGS}{$sname}{TIME} = $tim; $hash->{READINGS}{$sname}{TIME} = $tim;
@ -200,7 +230,6 @@ sub RPI_GPIO_Attr(@) {
RemoveInternalTimer($hash); RemoveInternalTimer($hash);
} }
} }
if ($attr eq 'longpressinterval') { if ($attr eq 'longpressinterval') {
if ( defined($val) ) { if ( defined($val) ) {
unless ( looks_like_number($val) && $val >= 0.1 && $val <= 10 ) { unless ( looks_like_number($val) && $val >= 0.1 && $val <= 10 ) {
@ -208,44 +237,47 @@ sub RPI_GPIO_Attr(@) {
} }
} }
} }
if ($attr eq 'direction') { if ($attr eq 'direction') {
if (!$val) { #$val nicht definiert: Einstellungen löschen if (!$val) { #$val nicht definiert: Einstellungen loeschen
$msg = "$hash->{NAME}: no direction value. Use input output"; $msg = "$hash->{NAME}: no direction value. Use input output";
} elsif ($val eq "input") { } elsif ($val eq "input") {
#RPI_GPIO_fileaccess($hash, "direction", "in"); delete($hash->{dir_not_set});
RPI_GPIO_exuexpin($hash, "in"); RPI_GPIO_fileaccess($hash, "direction", "in");
Log3 $hash, 5, "$hash->{NAME}: direction: input"; #RPI_GPIO_exuexpin($hash, "in");
Log3 $hash, 5, "$hash->{NAME}: set attr direction: input";
} elsif( ( AttrVal($hash->{NAME}, "interrupt", "none") ) ne ( "none" ) ) { } elsif( ( AttrVal($hash->{NAME}, "interrupt", "none") ) ne ( "none" ) ) {
$msg = "$hash->{NAME}: Delete attribute interrupt or set it to none for output direction"; $msg = "$hash->{NAME}: Delete attribute interrupt or set it to none for output direction";
} elsif ($val eq "output") { } elsif ($val eq "output") {
#RPI_GPIO_fileaccess($hash, "direction", "out"); 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_exuexpin($hash, "out"); RPI_GPIO_fileaccess($hash, "direction", "out");
Log3 $hash, 5, "$hash->{NAME}: direction: output"; #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 { } else {
$msg = "$hash->{NAME}: Wrong $attr value. Use input output"; $msg = "$hash->{NAME}: Wrong $attr value. Use input output";
} }
} }
if ($attr eq 'interrupt') { if ($attr eq 'interrupt') {
if ( !$val || ($val eq "none") ) { if ( !$val || ($val eq "none") ) {
RPI_GPIO_fileaccess($hash, "edge", "none"); RPI_GPIO_fileaccess($hash, "edge", "none");
RPI_GPIO_inthandling($hash, "stop"); RPI_GPIO_inthandling($hash, "stop");
Log3 $hash, 5, "$hash->{NAME}: interrupt: none"; Log3 $hash, 5, "$hash->{NAME}: set attr interrupt: none";
} elsif (( AttrVal($hash->{NAME}, "direction", "output") ) eq ( "output" )) { } elsif (( AttrVal($hash->{NAME}, "direction", "output") ) eq ( "output" )) {
$msg = "$hash->{NAME}: Wrong direction value defined for interrupt. Use input"; $msg = "$hash->{NAME}: Wrong direction value defined for interrupt. Use input";
} elsif ($val eq "falling") { } elsif ($val eq "falling") {
RPI_GPIO_fileaccess($hash, "edge", "falling"); RPI_GPIO_fileaccess($hash, "edge", "falling");
RPI_GPIO_inthandling($hash, "start"); RPI_GPIO_inthandling($hash, "start");
Log3 $hash, 5, "$hash->{NAME}: interrupt: falling"; Log3 $hash, 5, "$hash->{NAME}: set attr interrupt: falling";
} elsif ($val eq "rising") { } elsif ($val eq "rising") {
RPI_GPIO_fileaccess($hash, "edge", "rising"); RPI_GPIO_fileaccess($hash, "edge", "rising");
RPI_GPIO_inthandling($hash, "start"); RPI_GPIO_inthandling($hash, "start");
Log3 $hash, 5, "$hash->{NAME}: interrupt: rising"; Log3 $hash, 5, "$hash->{NAME}: set attr interrupt: rising";
} elsif ($val eq "both") { } elsif ($val eq "both") {
RPI_GPIO_fileaccess($hash, "edge", "both"); RPI_GPIO_fileaccess($hash, "edge", "both");
RPI_GPIO_inthandling($hash, "start"); RPI_GPIO_inthandling($hash, "start");
Log3 $hash, 5, "$hash->{NAME}: interrupt: both"; Log3 $hash, 5, "$hash->{NAME}: set attr interrupt: both";
} else { } else {
$msg = "$hash->{NAME}: Wrong $attr value. Use none, falling, rising or both"; $msg = "$hash->{NAME}: Wrong $attr value. Use none, falling, rising or both";
} }
@ -260,9 +292,10 @@ sub RPI_GPIO_Attr(@) {
if ($attr eq 'active_low') { if ($attr eq 'active_low') {
if ( !$val || ($val eq "no" ) ) { if ( !$val || ($val eq "no" ) ) {
RPI_GPIO_fileaccess($hash, "active_low", "0"); RPI_GPIO_fileaccess($hash, "active_low", "0");
#Log 1, "$hash->{NAME}: interrupt: none"; Log3 $hash, 5, "$hash->{NAME}: set attr active_low: no";
} elsif ($val eq "yes") { } elsif ($val eq "yes") {
RPI_GPIO_fileaccess($hash, "active_low", "1"); RPI_GPIO_fileaccess($hash, "active_low", "1");
Log3 $hash, 5, "$hash->{NAME}: set attr active_low: yes";
} else { } else {
$msg = "$hash->{NAME}: Wrong $attr value. Use yes or no"; $msg = "$hash->{NAME}: Wrong $attr value. Use yes or no";
} }
@ -273,9 +306,12 @@ sub RPI_GPIO_Attr(@) {
$msg = "$hash->{NAME}: debounce_in_ms value to big. Use 0 to 250"; $msg = "$hash->{NAME}: debounce_in_ms value to big. Use 0 to 250";
} }
} }
if ($attr eq 'pud_resistor') {#nur fuer Raspberry (ueber gpio utility)
if ($attr eq 'pud_resistor') {
my $pud; 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 ) { if ( !$val ) {
} elsif ($val eq "off") { } elsif ($val eq "off") {
$pud = $gpioprg.' -g mode '.$hash->{RPI_pin}.' tri'; $pud = $gpioprg.' -g mode '.$hash->{RPI_pin}.' tri';
@ -290,6 +326,7 @@ sub RPI_GPIO_Attr(@) {
$msg = "$hash->{NAME}: Wrong $attr value. Use off, up or down"; $msg = "$hash->{NAME}: Wrong $attr value. Use off, up or down";
} }
} }
}
return ($msg) ? $msg : undef; return ($msg) ? $msg : undef;
} }
@ -313,18 +350,20 @@ sub RPI_GPIO_Undef($$) {
delete $selectlist{$hash->{NAME}}; delete $selectlist{$hash->{NAME}};
close($hash->{filehandle}); close($hash->{filehandle});
} }
#unexport Pin alte Version if (-w "$gpiodir/unexport") {#unexport Pin alte Version
#my $uexp = IO::File->new("> /sys/class/gpio/unexport"); my $uexp = IO::File->new("> $gpiodir/unexport");
#print $uexp "$hash->{RPI_pin}"; print $uexp "$hash->{RPI_pin}";
#$uexp->close; $uexp->close;
#alternative unexport Pin: } else {#alternative unexport Pin:
RPI_GPIO_exuexpin($hash, "unexport"); RPI_GPIO_exuexpin($hash, "unexport");
}
Log3 $hash, 1, "$hash->{NAME}: entfernt";
return undef; return undef;
} }
sub RPI_GPIO_Except($) { #called from main if an interrupt occured sub RPI_GPIO_Except($) { #called from main if an interrupt occured
my ($hash) = @_; my ($hash) = @_;
#seek($hash->{filehandle},0,0); #an Anfang der Datei springen (ist nötig falls vorher schon etwas gelesen wurde) #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 #chomp ( my $firstval = $hash->{filehandle}->getline ); #aktuelle Zeile auslesen und Endezeichen entfernen
#my $acttime = gettimeofday(); #my $acttime = gettimeofday();
my $eval = RPI_GPIO_fileaccess($hash, "edge"); #Eintstellung Flankensteuerung auslesen my $eval = RPI_GPIO_fileaccess($hash, "edge"); #Eintstellung Flankensteuerung auslesen
@ -336,7 +375,7 @@ sub RPI_GPIO_Except($) { #called from main if an interrupt occured
select(undef, undef, undef, $debounce_time); 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) 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 chomp ( my $val = $hash->{filehandle}->getline ); #aktuelle Zeile auslesen und Endezeichen entfernen
if ( ( $val == 1) && ( $eval ne ("falling") ) ) { if ( ( $val == 1) && ( $eval ne ("falling") ) ) {
@ -359,16 +398,16 @@ sub RPI_GPIO_Except($) { #called from main if an interrupt occured
$valto = "off"; $valto = "off";
} }
Log3 $hash, 5, "Toggle ist jetzt $valto"; 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 if (( AttrVal($hash->{NAME}, "toggletostate", "no") ) eq ( "yes" )) { #wenn Attr "toggletostate" gesetzt auch die Variable fuer den STATE wert setzen
$valst = $valto; $valst = $valto;
} }
#Zählfunktion #Zaehlfunktion
if (!defined($hash->{READINGS}{Counter}{VAL})) { #Zähler existiert nicht -> anlegen if (!defined($hash->{READINGS}{Counter}{VAL})) { #Zaehler existiert nicht -> anlegen
Log3 $hash, 5, "Zähler war nicht def"; Log3 $hash, 5, "Zaehler war nicht def";
$valcnt = "1"; $valcnt = "1";
} else { } else {
$valcnt = $hash->{READINGS}{Counter}{VAL} + 1; $valcnt = $hash->{READINGS}{Counter}{VAL} + 1;
Log3 $hash, 5, "Zähler ist jetzt $valcnt"; Log3 $hash, 5, "Zaehler ist jetzt $valcnt";
} }
#langer Testendruck #langer Testendruck
} elsif ($eval eq "both") { } elsif ($eval eq "both") {
@ -388,8 +427,8 @@ sub RPI_GPIO_Except($) { #called from main if an interrupt occured
} }
} }
delete ($hash->{READINGS}{Toggle}) if ($eval ne ("rising" || "falling")); #Reading Toggle löschen wenn Edge weder "rising" noch "falling" 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 löschen wenn edge nicht "both" delete ($hash->{READINGS}{Longpress}) if ($eval ne "both"); #Reading Longpress loeschen wenn edge nicht "both"
readingsBeginUpdate($hash); readingsBeginUpdate($hash);
readingsBulkUpdate($hash, 'Pinlevel', $valalt); readingsBulkUpdate($hash, 'Pinlevel', $valalt);
readingsBulkUpdate($hash, 'state', $valst); readingsBulkUpdate($hash, 'state', $valst);
@ -397,7 +436,7 @@ sub RPI_GPIO_Except($) { #called from main if an interrupt occured
readingsBulkUpdate($hash, 'Counter', $valcnt) if ($valcnt); readingsBulkUpdate($hash, 'Counter', $valcnt) if ($valcnt);
readingsBulkUpdate($hash, 'Longpress', $vallp) if ($vallp); readingsBulkUpdate($hash, 'Longpress', $vallp) if ($vallp);
readingsEndUpdate($hash, 1); readingsEndUpdate($hash, 1);
#Log3 $hash, 5, "RPIGPIO: Except ausgelöst: $hash->{NAME}, Wert: $val, edge: $eval,vt: $valto, $debounce_time s: $firstval"; #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 sub RPI_GPIO_longpress($) { #for reading longpress
@ -442,30 +481,55 @@ sub RPI_GPIO_fileaccess($$;$) { #Fileaccess for GPIO base directory
my $fname = $args[0]; my $fname = $args[0];
my $pinroot = qq($gpiodir/gpio$hash->{RPI_pin}); my $pinroot = qq($gpiodir/gpio$hash->{RPI_pin});
my $file =qq($pinroot/$fname); 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){ if (int(@args) < 2){
my $fh = IO::File->new("< $file"); my $fh = IO::File->new("< $file");
if (defined $fh) { if (defined $fh) {
chomp ( my $pinvalue = $fh->getline ); chomp ( my $pinvalue = $fh->getline );
$fh->close; $fh->close;
return $pinvalue; return $pinvalue;
} } else {
else {
Log3 $hash, 1, "Can't open file: $hash->{NAME}, $fname"; Log3 $hash, 1, "Can't open file: $hash->{NAME}, $fname";
} }
} else { } else {
my $value = $args[1]; 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"); my $fh = IO::File->new("> $file");
if (defined $fh) { if (defined $fh) {
print $fh "$value"; print $fh "$value";
$fh->close; $fh->close;
} } else {
else {
Log3 $hash, 1, "Can't open file: $hash->{NAME}, $fname"; Log3 $hash, 1, "Can't open file: $hash->{NAME}, $fname";
} }
} }
} }
}
sub RPI_GPIO_exuexpin($$) { #export and unexport Pin via GPIO utility sub RPI_GPIO_exuexpin($$) { #export, unexport and direction Pin via GPIO utility
my ($hash, $dir) = @_; my ($hash, $dir) = @_;
my $sw; my $sw;
if ($dir eq "unexport") { if ($dir eq "unexport") {
@ -475,30 +539,38 @@ sub RPI_GPIO_exuexpin($$) { #export and unexport Pin via GPIO utility
$sw = "export"; $sw = "export";
$dir = " ".$dir; $dir = " ".$dir;
} }
#alternative export Pin if ( defined(my $ret = RPI_GPIO_CHECK_GPIO_UTIL($gpioprg)) ) {
if(-e $gpioprg) { Log3 $hash, 1, "$hash->{NAME}: " . $ret;
if(-x $gpioprg) { } else {
if(-u $gpioprg) {
my $exp = $gpioprg.' '.$sw.' '.$hash->{RPI_pin}.$dir; my $exp = $gpioprg.' '.$sw.' '.$hash->{RPI_pin}.$dir;
$exp = `$exp`; $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 sub RPI_GPIO_inthandling($$) { #start/stop Interrupthandling
my ($hash, $arg) = @_; my ($hash, $arg) = @_;
my $msg = ''; my $msg = '';
if ( $arg eq "start") { if ( $arg eq "start") {
#FH für value-datei #FH fuer value-datei
my $pinroot = qq($gpiodir/gpio$hash->{RPI_pin}); my $pinroot = qq($gpiodir/gpio$hash->{RPI_pin});
my $valfile = qq($pinroot/value); my $valfile = qq($pinroot/value);
$hash->{filehandle} = IO::File->new("< $valfile"); $hash->{filehandle} = IO::File->new("< $valfile");
@ -517,6 +589,7 @@ sub RPI_GPIO_inthandling($$) { #start/stop Interrupthandling
} }
1; 1;
=pod =pod
=begin html =begin html
@ -524,25 +597,36 @@ sub RPI_GPIO_inthandling($$) { #start/stop Interrupthandling
<h3>RPI_GPIO</h3> <h3>RPI_GPIO</h3>
<ul> <ul>
<a name="RPI_GPIO"></a> <a name="RPI_GPIO"></a>
<p>
Raspberry Pi offers direct access to several GPIO via header P1 (and P5 on V2). The Pinout is shown in table under define. 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<br><br> 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<br>
In addition to the Raspberry Pi, also BBB, Cubie and almost every linux system which provides gpio access in userspace is supported.<br>
<b>Warning: Never apply any external voltage to an output configured pin! GPIO's internal logic operate with 3,3V. Don't exceed this Voltage!</b><br><br> <b>Warning: Never apply any external voltage to an output configured pin! GPIO's internal logic operate with 3,3V. Don't exceed this Voltage!</b><br><br>
<b>preliminary:</b><br> <b>preliminary:</b><br>
GPIO Pins accessed by sysfs. The files are located in folder /system/class/gpio which can be only accessed by root. GPIO Pins accessed by sysfs. The files are located in folder <code>/system/class/gpio</code> and belong to the gpio group (on actual Raspbian distributions since jan 2014).<br>
This module uses gpio utility from <a href="http://wiringpi.com/download-and-install/">WiringPi</a> library to export and change access rights of GPIO's<br> After execution of following commands, GPIO's are usable whithin PRI_GPIO:<br>
Install WiringPi: <ul><code>
<pre> sudo adduser fhem gpio<br>
sudo apt-get update sudo reboot
sudo apt-get upgrade </code></ul><br>
sudo apt-get install git-core On older Raspbian distributions and if attribute <code>pud_resistor</code> shall be used, aditionally gpio utility from <a href="http://wiringpi.com/download-and-install/">WiringPi</a>
git clone git://git.drogon.net/wiringPi library must be installed to export and change access rights of GPIO's, or to set the internal pullup/down resistor.<br>
Installation WiringPi:<br>
<ul><code>
sudo apt-get update<br>
sudo apt-get upgrade<br>
sudo apt-get install git-core<br>
git clone git://git.drogon.net/wiringPi<br>
cd wiringPi cd wiringPi
./build ./build
sudo adduser fhem gpio</pre> </code></ul><br>
Thats all<br><br> On Linux systeme where <code>/system/class/gpio</code> can only accessed as root, GPIO's must exported and their access rights changed before FHEM starts.<br>
This can be done in <code>/etc/rc.local</code> (Examole for GPIO22 and 23):<br>
<ul><code>
echo 22 > /sys/class/gpio/export<br>
echo 23 > /sys/class/gpio/export<br>
chown -R fhem:root /sys/devices/virtual/gpio/*<br>
chown -R fhem:root /sys/class/gpio/*<br>
</code></ul><br>
<a name="RPI_GPIODefine"></a> <a name="RPI_GPIODefine"></a>
<b>Define</b> <b>Define</b>
<ul> <ul>
@ -649,6 +733,10 @@ sub RPI_GPIO_inthandling($$) { #start/stop Interrupthandling
Sets the GPIO direction to input or output.<br> Sets the GPIO direction to input or output.<br>
Default: input, valid values: input, output<br><br> Default: input, valid values: input, output<br><br>
</li> </li>
<li>active_low<br>
Inverts logical value<br>
Default: off, valid values: on, off<br><br>
</li>
<li>interrupt<br> <li>interrupt<br>
<b>can only be used with GPIO configured as input</b><br> <b>can only be used with GPIO configured as input</b><br>
enables edge detection for GPIO pin<br> enables edge detection for GPIO pin<br>
@ -668,6 +756,7 @@ sub RPI_GPIO_inthandling($$) { #start/stop Interrupthandling
</li> </li>
<li>pud_resistor<br> <li>pud_resistor<br>
Sets the internal pullup/pulldown resistor<br> Sets the internal pullup/pulldown resistor<br>
<b>Works only with installed gpio urility from <a href="http://wiringpi.com/download-and-install/">WiringPi</a> Library.</b><br>
Default: -, valid values: off, up, down<br><br> Default: -, valid values: off, up, down<br><br>
</li> </li>
<li>debounce_in_ms<br> <li>debounce_in_ms<br>
@ -676,7 +765,7 @@ sub RPI_GPIO_inthandling($$) { #start/stop Interrupthandling
</li> </li>
<li>restoreOnStartup<br> <li>restoreOnStartup<br>
Restore Readings and sets after reboot<br> Restore Readings and sets after reboot<br>
Default: on, valid values: on, off<br><br> Default: last, valid values: last, on, off, no<br><br>
</li> </li>
<li>longpressinterval<br> <li>longpressinterval<br>
<b>works with interrupt set to both only</b><br> <b>works with interrupt set to both only</b><br>
@ -697,25 +786,36 @@ sub RPI_GPIO_inthandling($$) { #start/stop Interrupthandling
<h3>RPI_GPIO</h3> <h3>RPI_GPIO</h3>
<ul> <ul>
<a name="RPI_GPIO"></a> <a name="RPI_GPIO"></a>
<p>
Das Raspberry Pi erm&ouml;glicht direkten Zugriff zu einigen GPIO's &uuml;ber den Pfostenstecker P1 (und P5 bei V2). Die Steckerbelegung ist in den Tabellen unter Define zu finden. Das Raspberry Pi erm&ouml;glicht direkten Zugriff zu einigen GPIO's &uuml;ber den Pfostenstecker P1 (und P5 bei V2). Die Steckerbelegung ist in den Tabellen unter Define zu finden.
Dieses Modul erm&ouml;glicht es, die herausgef&uuml;hten GPIO's direkt als Ein- und Ausgang zu benutzen. Die Eing&auml;nge k&ouml;nnen zyklisch abgefragt werden oder auch sofort bei Pegelwechsel gesetzt werden.<br><br> Dieses Modul erm&ouml;glicht es, die herausgef&uuml;hrten GPIO's direkt als Ein- und Ausgang zu benutzen. Die Eing&auml;nge k&ouml;nnen zyklisch abgefragt werden oder auch sofort bei Pegelwechsel gesetzt werden.<br>
Neben dem Raspberry Pi k&ouml;nnen auch die GPIO's von BBB, Cubie und jedem Linuxsystem, das diese im Userspace zug&auml;gig macht, genutzt werden.<br>
<b>Wichtig: Niemals Spannung an einen GPIO anlegen, der als Ausgang eingestellt ist! Die interne Logik der GPIO's arbeitet mit 3,3V. Ein &uuml;berschreiten der 3,3V zerst&ouml;rt den GPIO und vielleicht auch den ganzen Prozessor!</b><br><br> <b>Wichtig: Niemals Spannung an einen GPIO anlegen, der als Ausgang eingestellt ist! Die interne Logik der GPIO's arbeitet mit 3,3V. Ein &uuml;berschreiten der 3,3V zerst&ouml;rt den GPIO und vielleicht auch den ganzen Prozessor!</b><br><br>
<b>Vorbereitung:</b><br> <b>Vorbereitung:</b><br>
Auf GPIO Pins wird im Modul &uuml;ber sysfs zugegriffen. Die Dateien befinden sich unter /system/class/gpio und k&ouml;nnen nur mit root erreicht werden. Auf GPIO Pins wird im Modul &uuml;ber sysfs zugegriffen. Die Dateien befinden sich unter <code>/system/class/gpio</code> und sind in der aktuellen Raspbian Distribution (ab Jan 2014) in der Gruppe gpio.<br>
Dieses Modul nutzt das gpio Tool von der <a href="http://wiringpi.com/download-and-install/">WiringPi</a>. Bibliothek um GPIS zu exportieren und die korrekten Nutzerrechte zu setzen. Nach dem ausf&uuml;hren folgender Befehle sind die GPIO's von PRI_GPIO aus nutzbar:<br>
Installation WiringPi: <ul><code>
<pre> sudo adduser fhem gpio<br>
sudo apt-get update sudo reboot
sudo apt-get upgrade </code></ul><br>
sudo apt-get install git-core F&uuml;r &auml;ltere Raspbian Distributionen und wenn das Attribut <code>pud_resistor</code> verwendet werden soll, muss zus&auml;tzlich das gpio Tool der <a href="http://wiringpi.com/download-and-install/">WiringPi</a>
git clone git://git.drogon.net/wiringPi Bibliothek installiert werden, um GPIO's zu exportieren und die korrekten Nutzerrechte zu setzen, bzw. den internen Pullup/down Widerstand zu aktivieren.<br>
Installation WiringPi:<br>
<ul><code>
sudo apt-get update<br>
sudo apt-get upgrade<br>
sudo apt-get install git-core<br>
git clone git://git.drogon.net/wiringPi<br>
cd wiringPi cd wiringPi
./build ./build
sudo adduser fhem gpio</pre> </code></ul><br>
Das wars!<br><br> F&uuml;r Linux Systeme bei denen der Zugriff auf <code>/system/class/gpio</code> nur mit root Rechten erfolgen kann, m&uuml;ssen die GPIO's vor FHEM start exportiert und von den Rechten her angepasst werden.<br>
Dazu in die <code>/etc/rc.local</code> folgendes einf&uuml;gen (Beispiel f&uuml;r GPIO22 und 23):<br>
<ul><code>
echo 22 > /sys/class/gpio/export<br>
echo 23 > /sys/class/gpio/export<br>
chown -R fhem:root /sys/devices/virtual/gpio/*<br>
chown -R fhem:root /sys/class/gpio/*<br>
</code></ul><br>
<a name="RPI_GPIODefine"></a> <a name="RPI_GPIODefine"></a>
<b>Define</b> <b>Define</b>
<ul> <ul>
@ -822,13 +922,17 @@ sub RPI_GPIO_inthandling($$) { #start/stop Interrupthandling
Setzt den GPIO auf Ein- oder Ausgang.<br> Setzt den GPIO auf Ein- oder Ausgang.<br>
Standard: input, g&uuml;ltige Werte: input, output<br><br> Standard: input, g&uuml;ltige Werte: input, output<br><br>
</li> </li>
<li>active_low<br>
Invertieren des logischen Wertes<br>
Standard: off, g&uuml;ltige Werte: on, off<br><br>
</li>
<li>interrupt<br> <li>interrupt<br>
<b>kann nur gew&auml;hlt werden, wenn der GPIO als Eingang konfiguriert ist</b><br> <b>kann nur gew&auml;hlt werden, wenn der GPIO als Eingang konfiguriert ist</b><br>
Aktiviert Flankenerkennung f&uuml;r den GPIO<br> Aktiviert Flankenerkennung f&uuml;r den GPIO<br>
bei jedem interrupt Ereignis werden die readings Pinlevel und state aktualisiert<br> bei jedem interrupt Ereignis werden die readings Pinlevel und state aktualisiert<br>
Standard: none, g&uuml;ltige Werte: none, falling, rising, both<br><br> Standard: none, g&uuml;ltige Werte: none, falling, rising, both<br><br>
Bei "both" wird ein reading Longpress angelegt, welches auf on gesetzt wird solange der Pin länger als 1s gedr&uuml;ckt wird<br> Bei "both" wird ein reading Longpress angelegt, welches auf on gesetzt wird solange der Pin l&auml;nger als 1s gedr&uuml;ckt wird<br>
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<br><br> 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&auml;hlt<br><br>
</li> </li>
<li>poll_interval<br> <li>poll_interval<br>
@ -842,6 +946,7 @@ sub RPI_GPIO_inthandling($$) { #start/stop Interrupthandling
</li> </li>
<li>pud_resistor<br> <li>pud_resistor<br>
Interner Pullup/down Widerstand<br> Interner Pullup/down Widerstand<br>
<b>Funktioniert ausslie&szlig;lich mit installiertem gpio Tool der <a href="http://wiringpi.com/download-and-install/">WiringPi</a> Bibliothek.</b><br>
Standard: -, g&uuml;ltige Werte: off, up, down<br><br> Standard: -, g&uuml;ltige Werte: off, up, down<br><br>
</li> </li>
<li>debounce_in_ms<br> <li>debounce_in_ms<br>
@ -849,8 +954,8 @@ sub RPI_GPIO_inthandling($$) { #start/stop Interrupthandling
Standard: 0, g&uuml;ltige Werte: Dezimalzahl<br><br> Standard: 0, g&uuml;ltige Werte: Dezimalzahl<br><br>
</li> </li>
<li>restoreOnStartup<br> <li>restoreOnStartup<br>
Wiederherstellen der Portzust&äuml;nde nach Neustart<br> Wiederherstellen der Portzust&auml;nde nach Neustart<br>
Standard: on, g&uuml;ltige Werte: on, off<br><br> Standard: last, g&uuml;ltige Werte: last, on, off, no<br><br>
</li> </li>
<li>longpressinterval<br> <li>longpressinterval<br>
<b>Funktioniert nur bei auf both gesetztem Attribut interrupt</b><br> <b>Funktioniert nur bei auf both gesetztem Attribut interrupt</b><br>