物理ボタンを付けてRaspberry Piを安全にシャットダウンする方法

物理ボタンを付けてRaspberry Piを安全にシャットダウンする方法
物理ボタンを付けてRaspberry Piを安全にシャットダウンする方法

Raspberry Piの電源をオフする場合にUSB電源コードをそのまま引っこ抜くと、運が悪い場合はSDカード内のOSが壊れる可能性があります(私もその経験あります)。かと言って、SSHでログインして $ sudo shutdown -h now を毎回実行するのも非常に面倒です。 そこでRaspberry Piに物理ボタンを外付けして、安全にシャットダウンする方法をご紹介します。

はじめに

ここで紹介する手順は次のとおりです。

  1. Raspberry PiのGPIOピンに物理ボタンを設置
  2. ボタンを監視するPythonコードを作成
  3. ②をシステムサービスへ登録

記事の後半ではさらにLEDも追加して、Raspberry Piの電源オンオフ状態を分かりやすく工夫しました!ぜひ最後までご覧ください。

Raspberry PiのGPIOピンに物理ボタンを設置

押している間だけ導通するモーメンタリー型のスイッチを使用しました。

このスイッチをRaspberry PiのGPIO27(物理番号13)とGround(物理番号14)に繋ぎます。

Raspberry Piとスイッチの配線
Raspberry Piとスイッチの配線

物理ボタンをRaspberry Piに繋ぐ
物理ボタンをRaspberry Piに繋ぐ

ボタンを監視するPythonコード

次に、適当な場所に shutdown_by_button.py としてボタンを監視するPythonコードを作成します。

py
#!/usr/bin/env python

import RPi.GPIO as GPIO
import os, time

GPIO.setmode(GPIO.BCM)

GPIO.setup(27, GPIO.IN, pull_up_down = GPIO.PUD_UP)  # GPIO27--Button--GND

def shutdown(channel):
  os.system("sudo shutdown -h now")

GPIO.add_event_detect(27, GPIO.FALLING, callback = shutdown, bouncetime = 3000)

while 1:
  time.sleep(100)

上記プログラムの内容はGPIO.add_event_detect でGPIOのイベントを検知し、shutdown 関数を呼び出して sudo shutdown -h now を実行させています。

解説

このスクリプトは、Raspberry Pi上で動作し、物理ボタンの入力を使用してRaspberry Piを安全にシャットダウンするためのものです。以下、簡単な解説です。

GPIOモードの設定

GPIO.setmode(GPIO.BCM)は、GPIOピンを指定するための番号体系をBCM (Broadcom) ピン番号に設定しています。この設定により、プログラム内でGPIOピンを指定する際に、物理ピン番号ではなくBCM番号を使用します。

GPIOピンの設定

GPIO.setup(27, GPIO.IN, pull_up_down = GPIO.PUD_UP)は、BCM番号27のGPIOピンを入力モードに設定し、内部でプルアップ抵抗を有効にしています。プルアップ抵抗を有効にすることで、物理的なボタンが接続されていない時にピンが高電圧 (HIGH) 状態に保たれます。

シャットダウン関数の定義

shutdown関数は、GPIOピンからの入力信号を受け取り、システムコマンドsudo shutdown -h nowを実行してRaspberry Piをシャットダウンします。

イベント検出の設定

GPIO.add_event_detect(27, GPIO.FALLING, callback = shutdown, bouncetime = 3000)は、BCM番号27のGPIOピンに対するイベント検出を設定しています。GPIO.FALLINGは、ピンの状態がHIGHからLOWに変化する(つまり、ボタンが押される)ことを検出するためのトリガーです。callback = shutdownは、指定されたイベントが検出された際に実行される関数を指定しています。bouncetime = 3000は、連続してイベントが検出されることを防ぐためのデバウンス時間(ミリ秒)を設定しています。

無限ループ

最後のwhile 1:ループとtime.sleep(100)により、スクリプトは無限に実行され続け、この間ずっとGPIOピンの状態を監視します。time.sleep(100)はCPUの無駄な使用を避けるために追加されていますが、実際にはGPIOイベントによるコールバック関数の呼び出しで主な処理が行われるため、このスリープはそれほど重要ではありません。

このスクリプトを実行することで、物理ボタンを押すだけでRaspberry Piを安全にシャットダウンできます。これは、Raspberry Piをヘッドレス(モニターやキーボードなし)で運用する際に特に便利です。

スクリプトの権限変更

さて、 shutdown_by_button.py の権限を変更して、rootユーザーがスクリプトを実行可能なようにしておきます。

bash
chmod 755 shutdown_by_button.py

プログラムをシステムサービスへ登録

先ほど作った shutdown_by_button.py をシステムサービスへ登録してデーモンとして実行させます。

$ sudo vi /usr/lib/systemd/system/shutdown_by_button.service を実行し、下記の内容を書き込みます。ExecStart=/home/pi/batch/shutdown_by_button.py の部分はプログラムが置いてあるパスに変更してください。
shutdown_by_button.service
[Unit]
Description=Shutdown/Reboot raspberry pi by GPIO button input
Wants=network.target

[Service]
ExecStart=/home/pi/batch/shutdown_by_button.py
Restart=on-failure
RestartSec=10s

[Install]
WantedBy=multi-user.target

続いてこの設定ファイルを反映させます。

bash
sudo systemctl daemon-reload

スクリプトを開始させます。

bash
sudo systemctl start shutdown_by_button

Raspberry Piの起動時にスクリプトが自動起動するように設定します。

bash
$ sudo systemctl enable shutdown_by_button

こちらのコマンドでサービスの状態が問題ないか調べておきます。active となっていれば正常です。

bash
$ sudo systemctl status shutdown_by_button
● shutdown_by_button.service - Shutdown/Reboot raspberry pi by GPIO button input
     Loaded: loaded (/lib/systemd/system/shutdown_by_button.service; enabled; v>
     Active: active (running) since Thu 2023-07-20 14:56:26 JST; 57s ago
     ...

これでサービスに登録されていることが確認できました。この画面は control + c で終了できます。以上ですべての設定が終わりました。

一度 $ sudo reboot してから物理ボタンでRaspberry Piの電源がオフされるか確かめてみましょう。しっかり反応させるためにもボタンスイッチを2、3秒押し続けてください。なお再び電源をオンにする場合は、USB-Cの電源コードを一度抜いてから再度挿してRaspberry Piを起動させてください。

【発展】LEDも付けてみる

Raspberry Piの基板に実装されている2つのLEDだけですと、電源が入っているのかどうか判断しずらいです。そこでGPIOを使ってLEDを取り付けてみました。Raspberry Piが起動するとLEDが点灯し、シャットダウンさせるとLEDが消灯します。先ほど作った shutdown_by_button.py に追記する形で簡単に実現できました。

下図のようにLEDを追加して配線します。電流制御用の抵抗は1kΩを使いました。

LEDを追加
LEDを追加

shutdown_by_button.py を次のように修正します。
py
#!/usr/bin/env python

import RPi.GPIO as GPIO
import os, time

# Select BCM(GPIO Number)
GPIO.setmode(GPIO.BCM)
GPIO.setup(27, GPIO.IN, pull_up_down = GPIO.PUD_UP)  # GPIO27--Button--GND

# LED Blink
GPIO.setup(17, GPIO.OUT)
GPIO.output(17, 1)

def shutdown(channel):
  os.system("sudo shutdown -h now")

GPIO.add_event_detect(27, GPIO.FALLING, callback = shutdown, bouncetime = 3000)

while 1:
  time.sleep(100)

こちらの動画のように、電源オンオフが目視で非常に分かりやすくなりました!とても便利なので、皆さんもぜひまねしてみてください。

Raspberry Piに物理ボタンとLED追加
Raspberry Piに物理ボタンとLED追加

関連記事

最後までご覧いただきありがとうございます!

▼ 記事に関するご質問やお仕事のご相談は以下よりお願いいたします。
お問い合わせフォーム

人気のRaspberry Pi
人気のRaspberry Pi周辺機器
Raspberry Piのオススメ入門書