Raspberry Pi 3にシャットダウンボタンをつける(2)
前回の続き。C言語やPythonでRaspberry PiのGPIO制御を行うためのライブラリWiringPiを使用してみる。
WiringPiのインストール
次の記事を参考にした。
tool-lab.com
まずI2Cライブラリをapt-getでインストールする。
その後、WiringPiのソースコードを置きたいディレクトリに移動し、
$ cd wiringPi
$ ./build
を実行する。*1
インストールに成功すると、下記のコマンドでバージョン情報が出力されるハズ。
gpio version: 2.38
Copyright (c) 2012-2017 Gordon Henderson
This is free software with ABSOLUTELY NO WARRANTY.
For details type: gpio -warranty
Raspberry Pi Details:
Type: Pi 3, Revision: 02, Memory: 1024MB, Maker: Embest
* Device tree is enabled.
*--> Raspberry Pi 3 Model B Rev 1.2
* This Raspberry Pi supports user-level GPIO access.
WiringPiをコマンド上で実行してみる
コマンドで、
を実行すると、下記のようにGPIOの状態が表示される。
+-----+-----+---------+------+---+---Pi 3---+---+------+---------+-----+-----+ | BCM | wPi | Name | Mode | V | Physical | V | Mode | Name | wPi | BCM | +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+ | | | 3.3v | | | 1 || 2 | | | 5v | | | | 2 | 8 | SDA.1 | IN | 1 | 3 || 4 | | | 5v | | | | 3 | 9 | SCL.1 | IN | 1 | 5 || 6 | | | 0v | | | | 4 | 7 | GPIO. 7 | IN | 1 | 7 || 8 | 0 | IN | TxD | 15 | 14 | | | | 0v | | | 9 || 10 | 1 | IN | RxD | 16 | 15 | | 17 | 0 | GPIO. 0 | IN | 0 | 11 || 12 | 0 | IN | GPIO. 1 | 1 | 18 | | 27 | 2 | GPIO. 2 | OUT | 1 | 13 || 14 | | | 0v | | | | 22 | 3 | GPIO. 3 | IN | 1 | 15 || 16 | 0 | IN | GPIO. 4 | 4 | 23 | | | | 3.3v | | | 17 || 18 | 0 | IN | GPIO. 5 | 5 | 24 | | 10 | 12 | MOSI | IN | 0 | 19 || 20 | | | 0v | | | | 9 | 13 | MISO | IN | 0 | 21 || 22 | 0 | IN | GPIO. 6 | 6 | 25 | | 11 | 14 | SCLK | IN | 0 | 23 || 24 | 1 | IN | CE0 | 10 | 8 | | | | 0v | | | 25 || 26 | 1 | IN | CE1 | 11 | 7 | | 0 | 30 | SDA.0 | IN | 1 | 27 || 28 | 1 | IN | SCL.0 | 31 | 1 | | 5 | 21 | GPIO.21 | IN | 1 | 29 || 30 | | | 0v | | | | 6 | 22 | GPIO.22 | IN | 1 | 31 || 32 | 0 | IN | GPIO.26 | 26 | 12 | | 13 | 23 | GPIO.23 | IN | 0 | 33 || 34 | | | 0v | | | | 19 | 24 | GPIO.24 | IN | 0 | 35 || 36 | 0 | IN | GPIO.27 | 27 | 16 | | 26 | 25 | GPIO.25 | IN | 0 | 37 || 38 | 0 | IN | GPIO.28 | 28 | 20 | | | | 0v | | | 39 || 40 | 0 | IN | GPIO.29 | 29 | 21 | +-----+-----+---------+------+---+----++----+---+------+---------+-----+-----+ | BCM | wPi | Name | Mode | V | Physical | V | Mode | Name | wPi | BCM | +-----+-----+---------+------+---+---Pi 3---+---+------+---------+-----+-----+
ここで、
を実行すると、BCM(GPIOの番号で、≠物理ピン番号)の欄における22のピンのModeがOUTになる。この状態で、
$ gpio -g write 22 0
のように値を書き込むと、LEDが光ったり消えたりする。
同様に、
$ gpio -g mode 27 up
のように設定すると、GPIO27のMODEをINにし、プルアップ抵抗が有効になる。
この状態でボタンを押したり離したりしながら
を実行すると、1や0が返ってくる。
WiringPiを使ってGPIOを制御するPythonサンプルコード
下記の記事のPythonサンプルコードを引用する。
http://www.neko.ne.jp/~freewing/raspberry_pi/raspberry_pi_3_gpio_led_sw/
#!/usr/bin/env python import RPi.GPIO as GPIO import time # GPIO22 = Pin15 GPIO_OUT = 22 # GPIO27 = Pin13 GPIO_INP = 27 GPIO.setmode(GPIO.BCM) GPIO.setup(GPIO_OUT, GPIO.OUT, initial=False) GPIO.setup(GPIO_INP, GPIO.IN, pull_up_down=GPIO.PUD_UP) for i in range(5): print GPIO.input(GPIO_INP) GPIO.output(GPIO_OUT, True) time.sleep(0.5) GPIO.output(GPIO_OUT, False) time.sleep(0.5)
はじめにWiringPiを使用するために、RPi.GPIOをインポートする。
次にGPIO22を出力に、GPIO27を入力に(+プルアップ抵抗を有効に)設定する。その後ループに入ると、LEDは0.5秒おきに点滅し、スイッチの状態が1秒おきにターミナルに出力される、というもの。
ボタン押下でシャットダウンを行うコードを実装してみた
http://denshikousaku.net/put-shutdown-and-reboot-button-on-raspberry-pi
上記のページを参考にシャットダウンを行うコードを実装してみたのがこちら。
#!/usr/bin/env python # -*- encoding:utf-8 -*- import RPi.GPIO as GPIO import time import subprocess # GPIO22 = Pin15 GPIO_OUT = 22 # GPIO27 = Pin13 GPIO_IN = 27 GPIO.setmode(GPIO.BCM) GPIO.setup(GPIO_IN, GPIO.IN, pull_up_down = GPIO.PUD_UP) GPIO.setup(GPIO_OUT, GPIO.OUT, initial = GPIO.LOW) button_previous = 0 button_current = 0 release = 0 count = 0 while True: button_current = not GPIO.input(GPIO_IN) release = (not button_current) and (button_previous) if button_current: blink = not((count / 10) % 2) GPIO.output(GPIO_OUT, blink) if count % 20 == 0: print(count / 20) count += 1 if (release): if (count >= 60): print(u'Reboot now...') command = u'sudo shutdown -r now' subprocess.call(command, shell = True) GPIO.output(GPIO_OUT, GPIO.LOW) count = 0 if (button_current) and (count >= 100): print(u'Shutdown now...') command = u'sudo shutdown -h now' subprocess.call(command, shell = True) break button_previous = button_current time.sleep(0.05) GPIO.cleanup()
簡単に説明すると、1/20秒ごとにボタンの状態をポーリングし、100回連続でボタンが押されたとき(5秒経過時)にシャットダウンを実行するというもの。60回以上100回未満でボタンが離されたときは再起動を実行し、60回未満の場合は特に何もしない。
また、ボタンを押し続けている間、LEDが0.5秒おきに点滅するよう記述している。
続いて、先程のページでもう一つ紹介されているサンプルを参考に上記のスクリプトを改良してみる。
先程のページではGPIOからの入力による割込み発生までプロセスをスリープさせる関数wait_for_edge()を使用したサンプルが紹介されている。これを参考にスクリプトを改良してみた。
#!/usr/bin/env python # -*- encoding:utf-8 -*- import sys import time import subprocess import RPi.GPIO as GPIO def waitShutdownButton(): # GPIO22 = Pin15 GPIO_OUT = 22 # GPIO27 = Pin13 GPIO_IN = 27 LOOPS_PER_SEC = 20 BLINK_SEC = 0.5 MAX_PRESS_SEC = 5 SHUTDOWN_SEC = 5 REBOOT_SEC = 3 count = 0 GPIO.setmode(GPIO.BCM) GPIO.setup(GPIO_IN, GPIO.IN, pull_up_down = GPIO.PUD_UP) GPIO.setup(GPIO_OUT, GPIO.OUT, initial = GPIO.LOW) try: while True: GPIO.wait_for_edge(GPIO_IN, GPIO.FALLING) while not GPIO.input(GPIO_IN): blink = not(int(count / (LOOPS_PER_SEC * BLINK_SEC)) % 2) GPIO.output(GPIO_OUT, blink) count += 1 if count % LOOPS_PER_SEC == 0: print(count / LOOPS_PER_SEC) if (count >= LOOPS_PER_SEC * MAX_PRESS_SEC): break time.sleep(1.0 / LOOPS_PER_SEC) if (count >= LOOPS_PER_SEC * SHUTDOWN_SEC): print(u'Shutdown now...') GPIO.cleanup() command = u'sudo shutdown -h now' subprocess.call(command, shell = True) break elif (count >= LOOPS_PER_SEC * REBOOT_SEC): print(u'Reboot now...') GPIO.cleanup() command = u'sudo shutdown -r now' subprocess.call(command, shell = True) break GPIO.output(GPIO_OUT, GPIO.LOW) count = 0 except KeyboardInterrupt: GPIO.cleanup() if __name__ == '__main__': waitShutdownButton() sys.exit(0)
先程と異なり、スイッチが押されるまではスリープしているので、CPU資源を無駄遣いせずに済む。また、キーボードでCtrl+Cが入力されるとスクリプトを終了する...はずなのだが、スリープ中はCtrl+Cを押しても反応せず、ボタンを押してプロセスが再開されると同時にGPIO.cleanup()が実行され、スクリプトが終了する。WiringPiの不具合なのか、はたまた仕様なのか...。
GPIOモジュールについては下記の記事も読みやすかった。
ag.hatenablog.com
*1:2022/02/26追記:
現在では公式によるサポートが終了しており、GitHubからソースコードを取得する必要がある。