Raspberry Pi にシャットダウンボタンをつける

コンソールを使わずに運用する場合でも自力でシャットダウンができないと何かと不便なので、スイッチでシャットダウンできるようにしました。

参考にしたサイト
http://raspi.tv/2013/how-to-use-interrupts-with-python-on-the-raspberry-pi-and-rpi-gpio

準備

まず、以下のように対話モードでpythonを実行し、RPi.GPIOのバージョンが0.5.1以上であることを確認します。

% sudo python
Python 2.7.3 (default, Jan 13 2013, 11:20:46) 
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import RPi.GPIO as GPIO
>>> GPIO.VERSION
'0.5.2a'

配線

Raspberry Piにスイッチを接続します。

P1ピンヘッダの 14ピン(GND) と 16ピン(GPIO23) から、スイッチに配線します。

サンプルの動作確認

まずは割り込み動作の確認のため、参考サイトのサンプルをそのまま試してみます。

% wget http://raspi.tv/download/interrupt1.py.gz
gunzip interrupt1.py.gz

interrupt1.py

#!/usr/bin/env python2.7  
# script by Alex Eames http://RasPi.tv/  
# http://raspi.tv/2013/how-to-use-interrupts-with-python-on-the-raspberry-pi-and-rpi-gpio  
import RPi.GPIO as GPIO  
GPIO.setmode(GPIO.BCM)  

# GPIO 23 set up as input. It is pulled up to stop false signals  
GPIO.setup(23, GPIO.IN, pull_up_down=GPIO.PUD_UP)  

print "Make sure you have a button connected so that when pressed"  
print "it will connect GPIO port 23 (pin 16) to GND (pin 6)\n"   
raw_input("Press Enter when ready\n>")  

print "Waiting for falling edge on port 23"  
# now the program will do nothing until the signal on port 23   
# starts to fall towards zero. This is why we used the pullup  
# to keep the signal high and prevent a false interrupt  

print "During this waiting time, your computer is not"    
print "wasting resources by polling for a button press.\n"  
print "Press your button when ready to initiate a falling edge interrupt."  
try:  
        GPIO.wait_for_edge(23, GPIO.FALLING)  
        print "\nFalling edge detected. Now your program can continue with"   
        print "whatever was waiting for a button press."  
except KeyboardInterrupt:  
        GPIO.cleanup()  # clean up GPIO on CTRL+C exit  
GPIO.cleanup()          # clean up GPIO on normal exit 

このスクリプトを、rootで起動します。
(pin16とpin6をボタンに繋げと書いてありますが、pin6の代わりにpin14を使用しました。
 14と16が隣同士なのでこちらの方がわかりやすいかと)

% sudo ./interrupt1.py
Make sure you have a button connected so that when pressed
it will connect GPIO port 23 (pin 16) to GND (pin 6)

Press Enter when ready
>
Waiting for falling edge on port 23
During this waiting time, your computer is not
wasting resources by polling for a button press.

Press your button when ready to initiate a falling edge interrupt.

(ここでスイッチを押す)

Falling edge detected. Now your program can continue with
whatever was waiting for a button press.

スイッチに反応してコマンドが終了します。

スクリプト改造

上記のサンプルを元に、スイッチが押されたらシャットダウンを行うコマンドを作ります。
/usr/local/sbin/shutdown-btn.py

#!/usr/bin/env python2.7  
import RPi.GPIO as GPIO  
import os
GPIO.setmode(GPIO.BCM)  

GPIO.setup(23, GPIO.IN, pull_up_down=GPIO.PUD_UP)  

try:  
        GPIO.wait_for_edge(23, GPIO.FALLING)  
except KeyboardInterrupt:  
        GPIO.cleanup()  # clean up GPIO on CTRL+C exit  
GPIO.cleanup()          # clean up GPIO on normal exit  
os.system("/sbin/shutdown -h now")

これをrootで起動し、スイッチを押せばシャットダウンが行われます

sudo /usr/local/sbin/shutdown-btn.py

(スイッチを押す)

Broadcast message from root@pi2 (pts/1) (Thu Sep 26 01:27:26 2013):

The system is going down for system halt NOW!

init script作成

自動で起動したいので、サービス化します。
下記内容で、/etc/init.d/shutdown-button を作成します。

### BEGIN INIT INFO
# Provides: shutdown-button
# Required-Start:
# Required-Stop:
# Default-Start: 1 2 3 4 5 6
# Default-Stop: 0
# Short-Description: Shutdown Button
# Description: wait Shutdown Button
### END INIT INFO
#! /bin/sh
# /etc/init.d/shutdown-button
PIDFILE=/var/run/shutdown-btn.pid
case "$1" in
        start)
                if [ -f $PIDFILE ]; then
                        echo $PIDFILE exists.
                        exit 1
                fi
                start-stop-daemon -S -x /usr/local/sbin/shutdown-btn.py -b -m -p $PIDFILE
                ;;
        stop)
                if [ ! -f $PIDFILE ]; then
                        echo $PIDFILE not found.
                        exit 1
                fi
                start-stop-daemon -K -p $PIDFILE
                rm $PIDFILE
                ;;
        *)
                echo "Usage: /etc/init.d/shutdown-button {start|stop}"
                exit 1
                ;;
esac
exit 0

update-rc.dでinit scriptを登録します。デフォルトのrunlevel設定と違うと警告が出ますが、これでOKです。

% sudo update-rc.d shutdown-button defaults
update-rc.d: using dependency based boot sequencing
update-rc.d: warning: default start runlevel arguments (2 3 4 5) do not match shutdown-button Default-Start values (1 2 3 4 5 6)
update-rc.d: warning: default stop runlevel arguments (0 1 6) do not match shutdown-button Default-Stop values (0)

これで /usr/local/sbin/shutdown-btn.py が自動で起動されるようになりました。

まとめ

とりあえずシャットダウンスイッチが動作するようになりました。
が、これだけのために専用のプロセスがずっと待ち受けているのはちょっと不格好な気がしますし、pythonのモジュールを使ってちょいちょいとスクリプトを書いただけで中身がよくわかりません。
GPIOがらみはもう少し掘り下げて、応用がきく状態にしたいと思っています。