# $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"; $hash->{helper}{timer}{poll} = {hash => $hash, param => 'PIFACE_GetUpdate'}; $hash->{helper}{timer}{watchdog} = {hash => $hash, param => 'PIFACE_Watchdog'}; Log3($name, 2, "PIFACE $name active"); readingsSingleUpdate($hash, "state", "active",1); return; } sub PIFACE_Undefine($$) { my($hash, $name) = @_; RemoveInternalTimer($hash->{helper}{timer}{poll}); RemoveInternalTimer($hash->{helper}{timer}{watchdog}); return; } sub PIFACE_Set($@) { my ($hash, @a) = @_; my $name = $hash->{NAME}; if (IsDisabled($name)) { Log3 $name, 4, "PIFACE $name set commands disabled."; return; } shift @a; my $port = ''; $port = shift @a if (defined $a[0]); my $val = shift @a if (defined $a[0]); my $all = ReadingsVal($name, 'all', 0); my ($adr, $cmd, $i, $j, $k); my $usage = "Unknown argument $port, choose one of all:bitfield,8,255 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 '' || $port eq "?" || !defined($val)); readingsBeginUpdate($hash); 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`; readingsBulkUpdate($hash, 'out' . $port, $val); $all = $val == 1 ? $all | 2 ** $port : $all & (2 ** $port ^ 255); readingsBulkUpdate($hash, 'all', $all); } else { readingsBulkUpdateIfChanged($hash, 'all', $val); 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)); shift @a; my $port = ''; $port = shift @a if (defined $a[0]); 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 '' || $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->{helper}{timer}{poll}, $hash->{helper}{timer}{poll}{param}); } elsif ($attrVal eq "off" || ($attrVal =~ m/^\d+(\.\d+)?$/ && $attrVal > 0.5 && $attrVal <= 10)) { PIFACE_GetUpdate($hash->{helper}{timer}{poll}); } else { RemoveInternalTimer($hash->{helper}{timer}{poll}, $hash->{helper}{timer}{poll}{param}); Log3($name, 2, "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, 2, "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, 2, "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, 2, "PIFACE $name attribute-value [$attrName] = $attrVal wrong"); CommandDeleteAttr(undef, "$name defaultState"); } } elsif ($attrName eq "watchdog") { if (!defined $attrVal) { $attrVal = "off" ; readingsDelete($hash, "watchdog"); } if ($attrVal !~ m/^on|off|silent$/) { Log3($name, 2, "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; $hash->{helper}{timer}{watchdog} = {hash => $hash, param => 'PIFACE_Watchdog'}; PIFACE_Watchdog($hash->{helper}{timer}{watchdog}); } } elsif ($attrName eq "watchdogInterval") { if ($attrVal !~ m/^\d+$/ || $attrVal < 10) { Log3($name, 2, "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->{helper}{timer}{poll}); PIFACE_Watchdog($hash->{helper}{timer}{watchdog}); Log3($name, 2, "PIFACE $name initialized"); } return undef; } sub PIFACE_Read_Outports($$) { my ($updateMode, $hash) = @_; my $name = $hash->{NAME}; my ($all, $cmd, $i, $j, $port, $val); $all = 0; 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; $j = 2**$i; $all |= $j if ($val == 1); if ($updateMode == 1){ readingsBulkUpdate($hash, 'out'.$i, $val); } else { readingsBulkUpdateIfChanged($hash, 'out'.$i, $val); } } readingsBulkUpdateIfChanged($hash, 'all', $all); 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 ($functionHash) = @_; my $hash = $functionHash->{hash}; my $param = $functionHash->{param}; my $name = $hash->{NAME}; my $pollInterval = AttrVal($name, "pollInterval", "off"); if ($pollInterval ne "off") { RemoveInternalTimer($hash->{helper}{timer}{poll}, $param); InternalTimer(gettimeofday() + $pollInterval, $param, $hash->{helper}{timer}{poll}, 0); PIFACE_Read_Inports(0, $hash); PIFACE_Read_Outports(0, $hash); } return; } sub PIFACE_Watchdog($) { #my ($hash) = @_; my ($functionHash) = @_; my $hash = $functionHash->{hash}; my $param = $functionHash->{param}; 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) { readingsDelete($hash, "watchdog"); } elsif ($watchdog =~ m/^on|silent$/) { RemoveInternalTimer($hash->{helper}{timer}{watchdog}, $param); InternalTimer(gettimeofday() + $watchdogInterval, $param, $hash->{helper}{timer}{watchdog}, 0); 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, 2, "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, 2, "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, 2, "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, 2, "PIFACE $name Watchdog OS restart"); readingsSingleUpdate($hash, "watchdog", "restart", 1); readingsSingleUpdate($hash, ".watchdogRestart", 2, 1); CommandSave(undef, undef); $cmd = '/sbin/shutdown -a -r now'; #$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, 2, "PIFACE $name Watchdog error"); Log3($name, 2, "PIFACE $name Watchdog deactivated"); readingsDelete($hash, ".watchdogRestart"); readingsSingleUpdate($hash, "watchdog", "error", 1); readingsSingleUpdate($hash, "state", "error",1); CommandSave(undef, undef); } } } } else { Log3($name, 2, "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
git clone git://git.drogon.net/wiringPi
cd wiringPi
./build
sudo raspi-config
, select Advanced Options
and set the A5 SPI
option to "Yes".
gpio -x mcp23s17:200:0:0 readall
gpio -x mcp23s17:200:0:0 read 200
gpio -x mcp23s17:200:0:0 write 201 0
or gpio -x mcp23s17:200:0:0 write 201 1
chmod +s /sbin/shutdown
or sudo chmod +s /sbin/shutdown
.sudo apt-get install wiringpi
sudo raspi-config
, select 9 Advanced Options
and set the A6 SPI
option to "Yes".
gpio -x mcp23s17:200:0:0 readall
gpio -x mcp23s17:200:0:0 read 200
gpio -x mcp23s17:200:0:0 write 201 0
or gpio -x mcp23s17:200:0:0 write 201 1
/etc/shutdown.allow
and insert the authorized user fhem
there (each the login name per line).
Now Fhem can reboot the operating system with /sbin/shutdown -a -r now
.define <name> PIFACE
set <name> <port> <value>
port 76543210
bit 10101010
get <name> <port>