# $Id$ # An FHEM Perl module to control RaspberryPi extension board PiFace Digital # The PiFace Digital is an add-on board for the Raspberry Pi featuring 8 open-collector outputs, # with 2 relays and 8 inputs (with 4 on-board buttons). # These functions are fairly well fixed in the hardware, # so only the read, write and internal pull-up commands are implemented. package main; use strict; use warnings; sub PIFACE_Define($$); sub PIFACE_Undefine($$); sub PIFACE_Set($@); sub PIFACE_Get($@); sub PIFACE_Notify(@); sub PIFACE_Attr(@); my $base = 200; my $gpioCmd; if (-e '/usr/local/bin/gpio') { $gpioCmd = '/usr/local/bin/gpio'; } else { $gpioCmd = 'gpio'; } sub PIFACE_Initialize($){ my ($hash) = @_; $hash->{DefFn} = "PIFACE_Define"; $hash->{UndefFn} = "PIFACE_Undefine"; $hash->{SetFn} = "PIFACE_Set"; $hash->{GetFn} = "PIFACE_Get"; $hash->{NotifyFn} = "PIFACE_Notify"; $hash->{ShutdownFn} = "PIFACE_Shutdown"; $hash->{AttrFn} = "PIFACE_Attr"; $hash->{AttrList} = $readingFnAttributes . " defaultState:0,1,last,off pollInterval:0.5,0.75,1,1.5,2,3,4,5,6,7,8,9,10,off" . " disable:0,1 disabledForIntervals" . " portMode0:tri,up" . " portMode1:tri,up" . " portMode2:tri,up" . " portMode3:tri,up" . " portMode4:tri,up" . " portMode5:tri,up" . " portMode6:tri,up" . " portMode7:tri,up" . " shutdownClearIO:no,yes" . " watchdog:on,off,silent watchdogInterval"; } sub PIFACE_Define($$) { my ($hash, $def) = @_; my $name = $hash->{NAME}; $hash->{NOTIFYDEV} = "global"; Log3($name, 3, "PIFACE $name active"); readingsSingleUpdate($hash, "state", "active",1); return; } sub PIFACE_Undefine($$) { my($hash, $name) = @_; RemoveInternalTimer($hash); return; } sub PIFACE_Set($@) { my ($hash, @a) = @_; my $name = $hash->{NAME}; if (IsDisabled($name)) { Log3 $name, 4, "PIFACE $name set commands disabled."; return; } my $port = $a[1]; my $val = $a[2]; my ($adr, $cmd, $i, $j, $k); my $usage = "Unknown argument $port, choose one of all 0:0,1 1:0,1 2:0,1 3:0,1 4:0,1 5:0,1 6:0,1 7:0,1 "; return $usage if ($port eq "?"); if ($port ne "all") { $adr = $base + $port; Log3($name, 3, "PIFACE $name set port $port $val"); $cmd = "$gpioCmd -x mcp23s17:$base:0:0 write $adr $val"; $cmd = `$cmd`; readingsSingleUpdate($hash, 'out'.$port, $val, 1); } else { Log3($name, 3, "PIFACE $name set ports $val"); readingsBeginUpdate($hash); for($i = 0; $i < 8; $i ++) { $j = 2**$i; $k = $val & $j; $k = $k ? 1 : 0; Log3($name, 3, "PIFACE $name set port $i $k"); $adr = $base + $i; $cmd = "$gpioCmd -x mcp23s17:$base:0:0 write $adr $k"; $cmd = `$cmd`; readingsBulkUpdate($hash, 'out'.$i, $k); } readingsEndUpdate($hash, 1); } return; } sub PIFACE_Get($@) { my ($hash, @a) = @_; my $name = $hash->{NAME}; return undef if (IsDisabled($name)); my $port = $a[1]; my ($adr, $cmd, $pin, $portMode, $val); my $usage = "Unknown argument $port, choose one of all:noArg in:noArg out:noArg ". "0:noArg 1:noArg 2:noArg 3:noArg 4:noArg ". "5:noArg 6:noArg 7:noArg"; return $usage if $port eq "?"; if ($port eq "all") { PIFACE_Read_Inports(1, $hash); PIFACE_Read_Outports(1, $hash); Log3($name, 3, "PIFACE $name get port $port"); } elsif ($port eq "in") { PIFACE_Read_Inports(1, $hash); Log3($name, 3, "PIFACE $name get port $port"); } elsif ($port eq "out") { PIFACE_Read_Outports(1, $hash); Log3($name, 3, "PIFACE $name get port $port"); } else { # get state of in port $adr = $base + 8 + $port; $cmd = "$gpioCmd -x mcp23s17:$base:0:0 read $adr"; $val = `$cmd`; $val =~ s/\n//g; $val =~ s/\r//g; readingsSingleUpdate($hash, 'in' . $port, $val, 1); Log3($name, 3, "PIFACE $name get port in$port"); } return; } sub PIFACE_Attr(@) { my ($cmd, $name, $attrName, $attrVal) = @_; my $hash = $defs{$name}; return undef if (!$init_done); if ($attrName eq "pollInterval") { if (!defined $attrVal) { #RemoveInternalTimer($hash); } elsif ($attrVal eq "off" || ($attrVal =~ m/^\d+(\.\d+)?$/ && $attrVal > 0.5 && $attrVal <= 10)) { PIFACE_GetUpdate($hash); } else { #RemoveInternalTimer($hash); Log3($name, 3, "PIFACE $name attribute-value [$attrName] = $attrVal wrong"); CommandDeleteAttr(undef, "$name pollInterval"); } } elsif ($attrName eq "defaultState") { if (!defined $attrVal){ } elsif ($attrVal !~ m/^last|off|[01]$/) { Log3($name, 3, "PIFACE $name attribute-value [$attrName] = $attrVal wrong"); CommandDeleteAttr(undef, "$name defaultState"); } } elsif ($attrName =~ m/^portMode(.)/) { my $port = $1; #my $port = substr($attrName, 8, 1); my $adr = $base + 8 + $port; my $portMode = $attrVal; my $val; $portMode = "tri" if (!defined $attrVal); if ($attrVal !~ m/^tri|up$/) { $portMode = "tri" ; Log3($name, 3, "PIFACE $name attribute-value [$attrName] = $attrVal wrong"); CommandDeleteAttr(undef, "$name portMode$port"); } $cmd = "$gpioCmd -x mcp23s17:$base:0:0 mode $adr $portMode"; $val = `$cmd`; $cmd = "$gpioCmd -x mcp23s17:$base:0:0 read $adr"; $val = `$cmd`; $val =~ s/\n//g; $val =~ s/\r//g; readingsSingleUpdate($hash, 'in' . $port, $val, 1); } elsif ($attrName eq "shutdownClearIO") { if (!defined $attrVal){ } elsif ($attrVal !~ m/^no|yes$/) { Log3($name, 3, "PIFACE $name attribute-value [$attrName] = $attrVal wrong"); CommandDeleteAttr(undef, "$name defaultState"); } } elsif ($attrName eq "watchdog") { if (!defined $attrVal) { $attrVal = "off" ; CommandDeleteReading(undef, "$name watchdog"); } if ($attrVal !~ m/^on|off|silent$/) { Log3($name, 3, "PIFACE $name attribute-value [$attrName] = $attrVal wrong"); CommandDeleteAttr(undef, "$name watchdog"); } if ($attrVal =~ m/^on|silent$/) { readingsSingleUpdate($hash, 'watchdog', 'start', 1); $attr{$name}{$attrName} = $attrVal; PIFACE_Watchdog($hash); } } elsif ($attrName eq "watchdogInterval") { if ($attrVal !~ m/^\d+$/ || $attrVal < 10) { Log3($name, 3, "PIFACE $name attribute-value [$attrName] = $attrVal wrong"); CommandDeleteAttr(undef, "$name watchdogInterval"); } } return; } sub PIFACE_Notify(@) { my ($hash, $dev) = @_; my $name = $hash->{NAME}; return undef if (IsDisabled($name)); if ($dev->{NAME} eq "global" && grep (m/^INITIALIZED|REREADCFG$/,@{$dev->{CHANGED}})){ PIFACE_Restore_Inports_Mode($hash); PIFACE_Restore_Outports_State($hash); PIFACE_GetUpdate($hash); PIFACE_Watchdog($hash); Log3($name, 3, "PIFACE $name initialized"); } return undef; } sub PIFACE_Read_Outports($$) { my ($updateMode, $hash) = @_; my $name = $hash->{NAME}; my ($cmd, $i, $port, $val); readingsBeginUpdate($hash); for($i = 0; $i < 8; $i++){ $port = $base + $i; $cmd = "$gpioCmd -x mcp23s17:$base:0:0 read $port"; $val = `$cmd`; $val =~ s/\n//g; $val =~ s/\r//g; if ($updateMode == 1){ readingsBulkUpdate($hash, 'out'.$i, $val); } else { readingsBulkUpdateIfChanged($hash, 'out'.$i, $val); } } readingsEndUpdate($hash, 1); return; } sub PIFACE_Read_Inports($$) { my ($updateMode, $hash) = @_; my $name = $hash->{NAME}; my ($cmd, $i, $j, $port, $portMode, $val); readingsBeginUpdate($hash); for($i = 0; $i < 8; $i++){ $port = $base + 8 + $i; $cmd = "$gpioCmd -x mcp23s17:$base:0:0 read $port"; $val = `$cmd`; $val =~ s/\n//g; $val =~ s/\r//g; if ($updateMode == 1) { readingsBulkUpdate($hash, 'in'.$i, $val); } else { readingsBulkUpdateIfChanged($hash, 'in'.$i, $val); } } readingsEndUpdate($hash, 1); return; } sub PIFACE_Restore_Outports_State($) { my ($hash) = @_; my $name = $hash->{NAME}; my @cmd = ($name, 0, 0); my $defaultState = AttrVal($name, "defaultState", "off"); my ($adr, $cmd, $valOut); if ($defaultState ne "off") { for(my $port = 0; $port < 8; $port ++){ $cmd[1] = $port; $adr = $base + $port; $cmd = "$gpioCmd -x mcp23s17:$base:0:0 mode $adr out"; $valOut = `$cmd`; if ($defaultState eq "last") { $cmd[2] = ReadingsVal($name, "out" . $port, 0); } elsif ($defaultState == 1) { $cmd[2] = 1; } else { $cmd[2] = 0; } PIFACE_Set($hash, @cmd); } } PIFACE_Read_Outports(1, $hash); return; } sub PIFACE_Restore_Inports_Mode($) { my ($hash) = @_; my $name = $hash->{NAME}; my ($cmd, $port, $portMode, $valIn); for (my $i = 0; $i <= 7; $i++) { $port = $base + 8 + $i; $portMode = AttrVal($name, "portMode" . $i, "tri"); $cmd = "$gpioCmd -x mcp23s17:$base:0:0 mode $port in"; $valIn = `$cmd`; $cmd = "$gpioCmd -x mcp23s17:$base:0:0 mode $port $portMode"; $valIn = `$cmd`; } PIFACE_Read_Inports(1, $hash); return; } sub PIFACE_GetUpdate($) { my ($hash) = @_; my $name = $hash->{NAME}; my $pollInterval = AttrVal($name, "pollInterval", "off"); if ($pollInterval ne "off") { InternalTimer(gettimeofday() + $pollInterval, "PIFACE_GetUpdate", $hash, 1); PIFACE_Read_Inports(0, $hash); PIFACE_Read_Outports(0, $hash); } return; } sub PIFACE_Watchdog($) { my ($hash) = @_; my $name = $hash->{NAME}; return undef if (IsDisabled($name)); my ($cmd, $port, $portMode, $valIn, $valOut0, $valOut1); my $watchdog = AttrVal($name, "watchdog", undef); my $watchdogInterval = AttrVal($name, "watchdogInterval", 60); $watchdogInterval = 10 if ($watchdogInterval !~ m/^\d+$/ || $watchdogInterval < 10); if (!defined $watchdog) { CommandDeleteReading(undef, "$name watchdog"); } elsif ($watchdog =~ m/^on|silent$/) { InternalTimer(gettimeofday() + $watchdogInterval, "PIFACE_Watchdog", $hash, 1); for (my $i = 0; $i < 7; $i++) { $port = $base + 8 + $i; $portMode = AttrVal($name, "portMode" . $i, "tri"); $cmd = "$gpioCmd -x mcp23s17:$base:0:0 mode $port $portMode"; $valIn = `$cmd`; } $cmd = "$gpioCmd -x mcp23s17:$base:0:0 mode 215 up"; $valIn = `$cmd`; $cmd = "$gpioCmd -x mcp23s17:$base:0:0 read 215"; $valIn = `$cmd`; $valIn =~ s/\n//g; $valIn =~ s/\r//g; $cmd = "$gpioCmd -x mcp23s17:$base:0:0 write 207 0"; $cmd = `$cmd`; $cmd = "$gpioCmd -x mcp23s17:$base:0:0 read 207"; $valOut0 = `$cmd`; $valOut0 =~ s/\n//g; $valOut0 =~ s/\r//g; $cmd = "$gpioCmd -x mcp23s17:$base:0:0 write 207 1"; $cmd = `$cmd`; $cmd = "$gpioCmd -x mcp23s17:$base:0:0 read 207"; $valOut1 = `$cmd`; $valOut1 =~ s/\n//g; $valOut1 =~ s/\r//g; #Log3($name, 3, "PIFACE $name Watchdog in7: $valIn out7: $valOut0 $valOut1"); if ($valIn == 0 && $valOut0 == 0 && $valOut1 == 1) { readingsSingleUpdate($hash, "state", "active", 1) if (ReadingsVal($name, "state", '') ne "active"); readingsSingleUpdate($hash, ".watchdogRestart", 0, 1); if ($watchdog eq "on") { Log3($name, 3, "PIFACE $name Watchdog active"); readingsSingleUpdate($hash, "watchdog", "ok", 1); } elsif ($watchdog eq "silent") { readingsSingleUpdate($hash, "watchdog", "ok", 1) if (ReadingsVal($name, "watchdog", '') ne "ok"); } } else { if ($watchdog eq "on") { Log3($name, 3, "PIFACE $name Watchdog error"); readingsSingleUpdate($hash, "watchdog", "error", 1); } elsif ($watchdog eq "silent") { my $watchdogRestart = ReadingsVal($name, ".watchdogRestart", undef); if (!defined($watchdogRestart) || $watchdogRestart == 0) { Log3($name, 3, "PIFACE $name Watchdog Fhem restart"); readingsSingleUpdate($hash, "watchdog", "restart", 1); readingsSingleUpdate($hash, ".watchdogRestart", 1, 1); CommandSave(undef, undef); CommandShutdown(undef, "restart"); } elsif ($watchdogRestart == 1) { Log3($name, 3, "PIFACE $name Watchdog OS restart"); readingsSingleUpdate($hash, "watchdog", "restart", 1); readingsSingleUpdate($hash, ".watchdogRestart", 2, 1); CommandSave(undef, undef); #$cmd = 'shutdown -r now'; $cmd = 'sudo /sbin/shutdown -r now'; #$cmd = 'sudo /sbin/shutdown -r now > /dev/null 2>&1'; $cmd = `$cmd`; } elsif ($watchdogRestart == 2) { $attr{$name}{watchdog} = "off"; Log3($name, 3, "PIFACE $name Watchdog error"); Log3($name, 3, "PIFACE $name Watchdog deactivated"); CommandDeleteReading(undef, "$name .watchdogRestart"); readingsSingleUpdate($hash, "watchdog", "error", 1); readingsSingleUpdate($hash, "state", "error",1); CommandSave(undef, undef); } } } } else { Log3($name, 3, "PIFACE $name Watchdog off"); readingsSingleUpdate($hash, "watchdog", "off", 1); } return; } sub PIFACE_Shutdown($) { my ($hash) = @_; my $name = $hash->{NAME}; return undef if (AttrVal($name, "shutdownClearIO", 'no') eq 'no'); my ($cmd, $port); for (my $i = 0; $i <= 7; $i++) { $port = $base + $i; $cmd = "$gpioCmd -x mcp23s17:$base:0:0 write $port 0"; $cmd = `$cmd`; $port = $base + $i + 8; $cmd = "$gpioCmd -x mcp23s17:$base:0:0 mode $port tri"; $cmd = `$cmd`; } return undef; } 1; =pod =item summary Raspberry PiFace Digital Controler =item summary_DE Raspberry PiFace Digital Schnittstellenerweiterung =begin html

PIFACE

=end html =cut