RPi3のGPIOにShutdownボタンを付ける
Pre-conditions
- RPi3は2018-03-13-raspbian-stretch-lite.imgからセットアップ済み
GPIO実験
GPIOの仕様は公式ページを参照しました。
GPIO5, GPIO6にそれぞれLED, プッシュSWを繋いで実験します。
[GPIO5]---[LED+抵抗]---[GND]
[GPIO6]---[プッシュSW]---[GND]
GPIO6はpull-upされてます。
MEMO: RPi3はpull-up/downを設定できるような気配ですが、良い方法がわかりませんでした。。。(direction
にhigh
またはlow
を書き込むとそれっぽいのですが、そうするとedge
がnone
しか選択できなくなる。。。)
Test LED
GPIOはデバイスファイルでアクセスできます。
pi $ ls /sys/class/gpio/
export gpiochip0 gpiochip100 gpiochip128 unexport
pi $ echo 5 > /sys/class/gpio/export
pi $ ls /sys/class/gpio/
export gpio5 gpiochip0 gpiochip100 gpiochip128 unexport
pi $ ls /sys/class/gpio/gpio5
active_low device direction edge power subsystem uevent value
export
に利用するGPIOの番号を書き込むと該当するGPIOのデバイスファイルが生成されます。
pi $ echo out > /sys/class/gpio/gpio5/direction
pi $ echo 1 > /sys/class/gpio/gpio5/value
これでLEDが点灯して、
pi $ echo 0 > /sys/class/gpio/gpio5/value
これでLEDが消灯します。unexport
でGPIOの利用をやめることができます。
pi $ echo 5 > /sys/class/gpio/unexport
Test switch
プッシュSWはdirection
をin
にします。
pi $ echo 6 > /sys/class/gpio/export
pi $ echo in > /sys/class/gpio/gpio6/direction
value
がプッシュSWの状態で変化します。
pi $ cat /sys/class/gpio/gpio6/value
? (1 or 0)
Polling switch
プッシュSWの状態をポーリングによって監視し、プッシュ検出でLEDを点滅させます。
trigger-polling.sh
#!/bin/bash
echo Start Button Trigger
echo 6 > /sys/class/gpio/export 2> /dev/null
echo 5 > /sys/class/gpio/export 2> /dev/null
sleep 0.1
echo in > /sys/class/gpio/gpio6/direction
echo out > /sys/class/gpio/gpio5/direction
gpio_in=/sys/class/gpio/gpio6/value
led_out=/sys/class/gpio/gpio5/value
while :
do
echo 1 > $led_out
while [ `cat $gpio_in` = "0" ]
do
sleep 0.01
done
while [ `cat $gpio_in` = "1" ]
do
sleep 0.01
done
echo Falling Edge Detected
echo 0 > $led_out
sleep 0.5
echo 1 > $led_out
sleep 0.1
echo 0 > $led_out
sleep 0.1
echo 1 > $led_out
sleep 0.1
echo 0 > $led_out
sleep 0.1
echo 1 > $led_out
sleep 0.1
echo 0 > $led_out
done
Shutdownスクリプト
ほぼtrigger-polling.shですが、最後にshutdown
します。
shutdown-button.sh
#!/bin/bash
echo 6 > /sys/class/gpio/export 2> /dev/null
echo 5 > /sys/class/gpio/export 2> /dev/null
sleep 0.1
echo in > /sys/class/gpio/gpio6/direction
echo out > /sys/class/gpio/gpio5/direction
gpio_in=/sys/class/gpio/gpio6/value
led_out=/sys/class/gpio/gpio5/value
echo 1 > $led_out
while [ `cat $gpio_in` = "0" ]
do
sleep 0.01
done
while [ `cat $gpio_in` = "1" ]
do
sleep 0.01
done
echo 0 > $led_out
sleep 0.5
echo 1 > $led_out
sleep 0.1
echo 0 > $led_out
sleep 0.1
echo 1 > $led_out
sleep 0.1
echo 0 > $led_out
sleep 0.1
echo 1 > $led_out
sleep 0.1
echo 0 > $led_out
shutdown -h now
これを起動時に実行されるようサービスとして登録します。とりあえず動く設定です。
pi:/etc/systemd/system/shutdown-button.service
[Unit]
Description=Shutdown Button
After=local-fs.target
ConditionPathExists=/home/pi
[Service]
ExecStart=/home/pi/shutdown-button.sh
Restart=no
Type=simple
User=root
Group=root
[Install]
WantedBy=multi-user.target
pi $ ls /home/pi
shutdown-button.sh trigger_edge trigger_edge.c trigger-polling.sh
pi $ sudo systemctl daemon-reload
pi $ sudo systemctl enable shutdown-button
pi $ sudo shutdown -r now
これで起動時にshutdown-button.shが起動されます。
Appendix
RPi3はGPIOのエッジトリガーの検出が可能で、ポーリングよりリソースに優しいです。Shellscriptで書く方法がわからなかったため今回は不採用としましたが、C言語のコードを書き残しておきます。
trigger_edge.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <poll.h>
static char const * PATH = "/sys/class/gpio/gpio6/value";
int main(void) {
int fd;
puts("Start Button Trigger");
fd = open(PATH, O_RDONLY);
if (fd == -1) {
fprintf(stderr, "Error Failed to the file: %s\n", PATH);
return -1;
}
{
struct pollfd polls;
char ch;
polls.fd = fd;
polls.events = POLLPRI;
read(fd, &ch, 1);
lseek(fd, 0, SEEK_SET);
poll(&polls, 1, -1);
}
puts("Falling Edge Detected");
close(fd);
return 0;
}
$ echo 6 > /sys/class/gpio/export 2> /dev/null
$ echo in > /sys/class/gpio/gpio6/direction
$ echo falling > /sys/class/gpio/gpio6/edge
$ gcc trigger_edge.c -o trigger_edge
$ ./trigger_edge
Start Button Trigger
Falling Edge Detected
ちなみに、edge
をnone
(defualt)にするとエッジ検出しなくなります。
チャタリング対策していないためRising Edgeも拾ってるようです。。。