2016/10/08

RaspberryPiにshutdownボタンを付けよう

どうも、クラゲです。
ラスパイには電源をシャットダウンするボタンは一切付いていません。
毎回shutdownコマンド打つのが面倒だし、かと言ってラズパイの電源をいきなり切るのは不安です。
そこで、長押しでシャットダウンできるボタンを外付けします。


長押しにしている理由は、短押しだと間違って押してしまった場合などすぐに反応してしまうためです。PCと同じように長押しを採用しました。
順を追って丁寧に説明します。

【 大まかな流れ 】


回路作成

ラズパイ以外に必要な部品は以下3点です

回路は超シンプル
左下から3番目のGroundと6番目のピン(18pin)にジャンパー線を繋ぎます。
タクトスイッチは向きがあるので注意してください。
4つ足がありますが、2組の足は導通しています。

GPIO入力端子

まずはGPIO18pinのModeを確認しましょう

gpio readall

BCM18のModeはINになっていると思いますが、OUTになっている場合は以下のように打ち込んでください
(INになっている人も打ち込んで大丈夫です)

gpio -g mode 18 in

これでGPIO18pinは入力端子になりました
次にプルアップ設定を行います。
プルアップとは、電気回路上におけるデフォルト値をHigh(今回の場合は3.3V)に設定することです。
つまり、スイッチを押していないときは3.3Vで、スイッチを押すと0Vになります。
こちらがプルアップの設定コマンドです

gpio -g mode 18 up

本当にプルアップされているのか確認しましょう

gpio readall

BCM18のVが1になったと思います。
次にGPIO18pinの入力状態をコマンドで確認します。

gpio -g read 18

これを打つと"1"と表示されます。つまりHigh(3.3V)ということです。
次に、タクトスイッチを押した状態でコマンドを打ってみてください

gpio -g read 18

今度は0になったと思います。つまりLow(0.0V)ということです。
これでマニュアル操作での確認ができましたので、プログラムにしていきましょう!

プログラミング

タクトスイッチを押したら、"OK!"と表示されるようにしましょう
コードを書きます。
nanoを立ち上げます。ファイル名は"shutdown.py"としました。

nano shutdown.py

次のコードをコピペします

#!/usr/bin/python 
# coding:utf-8 
import time
import RPi.GPIO as GPIO
 
GPIO.setmode(GPIO.BCM)
 
#GPIO18pinを入力モードとし、pull up設定とします 
GPIO.setup(18,GPIO.IN,pull_up_down=GPIO.PUD_UP)
 
sw_status = 1
 
while True:
    sw_status = GPIO.input(18)
    if sw_status == 0:
        print("OK!")
        break
    time.sleep(0.03)
 
GPIO.cleanup()

保存して終了します
control + o で保存、ファイル名はそのままenter、control + x で終了。

今作ったファイルに実行許可を与えます

sudo chmod 755 Lchika.py

実行します

./Lchika.py

これでタクトスイッチを押すと"OK!"と表示されプログラムが終了します。
whileループにしているのは、GPIO.input()の箇所でずっと停止してる訳ではないためです。

割り込み入力待ち

先ほどのはループでぐるぐるしているので、ちょっと負荷がかかる可能性があります。
入力があるまで止まってもらうようにしたいと思います。
GPIO.wait_for_edge()を使うと、エッジ入力があるまで待ってくれます。
エッジというのは、LowからHighへの電圧変化(立ち上がりエッジ)もしくはHighからLowへの電圧変化(立ち下がりエッジ)です。
引数はGPIOピンと立ち上がりエッジ(GPIO.RISING)なのか立ち下がりエッジ(GPIO.FALLING)なのかを指定します。

#!/usr/bin/python 
# coding:utf-8 
import time
import RPi.GPIO as GPIO
 
GPIO.setmode(GPIO.BCM)
 
#GPIO18pinを入力モードとし、pull up設定とします 
GPIO.setup(18,GPIO.IN,pull_up_down=GPIO.PUD_UP)
 
GPIO.wait_for_edge(18GPIO.FALLING)
 
print("割り込みOK!")
 
GPIO.cleanup()

何度でも入力出来るようにする

先ほどのプログラムだと、1回押すとプログラムが終了してしまいます。
例えば、長押しを検出したら終了でいいけど、短押しを検出した時は引き続き長押しが検出されるまで待つ必要があります。

#!/usr/bin/python 
# coding:utf-8 
import time
import RPi.GPIO as GPIO
 
GPIO.setmode(GPIO.BCM)
 
#GPIO18pinを入力モードとし、pull up設定とします 
GPIO.setup(18,GPIO.IN,pull_up_down=GPIO.PUD_UP)
 
while True:
    GPIO.wait_for_edge(18GPIO.FALLING)
    print("割り込みOK!")
 
GPIO.cleanup()

これで、何度もスイッチを押すことが可能となりました。
終了する時はキーボードにてcontrol + c を押してください
ちなみに、while Ture:を使わず、以下のような記述を最後に書くことで自分自身を再び呼んで実行させる方法もありますがここでは説明省略します。

os.system(__file__)

例外処理について

上記のプログラムだと最後のGPIO.cleanup()には到達しません。
例外(control + c)が起きた時にtry〜exceptionを使ってGPIO.cleanup()させてから終わらせたかったのですが、どうやらバグがあるらしく、GPIO.wait_for_edgeの割り込み中にcontrol + c を押しても例外処理されませんでした。
詳しくはこちらを参照してください。
https://www.raspberrypi.org/forums/viewtopic.php?t=151401&p=994597

長押し対応

500ms以上押下していたら "長押し検知"と表示するようにして、500ms未満の場合は"短押し"と表示させます

#!/usr/bin/python 
# coding:utf-8 
import time
import RPi.GPIO as GPIO
 
GPIO.setmode(GPIO.BCM)
 
#GPIO18pinを入力モードとし、pull up設定とします 
GPIO.setup(18,GPIO.IN,pull_up_down=GPIO.PUD_UP)
 
while True:
    GPIO.wait_for_edge(18GPIO.FALLING)
    sw_counter = 0
 
    while True:
        sw_status = GPIO.input(18)
        if sw_status == 0:
            sw_counter = sw_counter + 1
            if sw_counter >= 50:
                print("長押し検知!")
                break
        else:
            print("短押し検知")
            break
 
        time.sleep(0.01)
 
    print(sw_counter)

最後の print(sw_counter)はデバッグ用です。

シャットダウン命令

これで、長押し検知が上手くできたので、print文ではなく実際のシャットダウン命令に置き換えます。
os.system("sudo shutdown -h now")を長押し検知のところに記述します。
os.systemを使うためにimport osを最初の方で記述します。

#!/usr/bin/python 
# coding:utf-8 
import time
import RPi.GPIO as GPIO
import os
 
GPIO.setmode(GPIO.BCM)
 
#GPIO18pinを入力モードとし、pull up設定とします 
GPIO.setup(18,GPIO.IN,pull_up_down=GPIO.PUD_UP)
 
while True:
    GPIO.wait_for_edge(18GPIO.FALLING)
    sw_counter = 0
 
    while True:
        sw_status = GPIO.input(18)
        if sw_status == 0:
            sw_counter = sw_counter + 1
            if sw_counter >= 50:
                print("長押し検知!")
                os.system("sudo shutdown -h now")
                break
        else:
            print("短押し検知")
            break
 
        time.sleep(0.01)
 
    print(sw_counter)

これで長押しするとシャットダウンできました!

ちなみに最初の動画では”シャットダウンします”と音声合成がしゃべっていますが、シャットダウン命令の前に音声合成のスクリプトを1行追記するだけです

os.system('/home/pi/jtalk.sh "シャットダウンします!"')

音声合成をインストールしていない場合はこちらを参照してください。
http://jellyware.jp/kurage/raspi/raspi_speech_synthesis.html

起動時に自動でプログラム実行

このスクリプトを毎回手で実行するのは当然面倒なので、起動したら自動的に実行されるようにします。
etcの下にあるrc.localを編集します

sudo nano /etc/rc.local

"exit 0" の前に以下のように記述してください

...
/home/pi/shutdown.py
exit 0

これで上書き保存してリブートすれば、長押しシャットダウンの完成です!

3.2inch LCDの場合

3.2inch LCDを使っている場合ですが、回路図をみるとスイッチの割り当てが以下のようになっています。
http://www.waveshare.com/w/upload/a/ad/3.2inch-RPi-LCD-B-Schematic.pdf
上のSW KEY0: 12pin GPIO18
中央SW KEY1: 16pin GPIO23
下のSW KEY2: 18pin GPIO24


つまり、上のスイッチがGPIO18なので、これまでのスクリプトがそのまま適応できます!

他にも短押しのときにはrebootを割り当てるなど色々応用が効きそうです。
以上、RaspberryPiにshutdownボタンを付けようでした!