gpio
Raspberrypi3

RPi3のGPIOにShutdownボタンを付ける

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を設定できるような気配ですが、良い方法がわかりませんでした。。。(directionhighまたはlowを書き込むとそれっぽいのですが、そうするとedgenoneしか選択できなくなる。。。)

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はdirectioninにします。

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

ちなみに、edgenone(defualt)にするとエッジ検出しなくなります。

チャタリング対策していないためRising Edgeも拾ってるようです。。。