ラズパイにシャットダウンと起動ボタンを
2020/07/01
24時間稼働はハードルが高い
  • 前回のレポートで「ラズパイでファイルサーバーを作ったがゆるく使っている」という話をしました。この理由にはクライアント側への影響を抑える以外に、自宅内で24時間稼働は避けたいという考えがあります。

  • ネットワーク関連の機器は、ある程度24時間稼働が想定にあると思いますが、さすがにラズパイが24時間稼働想定だとは思えません。そのため一日の中でスイッチをOFFにする時間が欲しいのです。

  • ですが我が家のラズパイはヘッドレス運用(*1)のためシャットダウンは微妙に面倒ですし、そもそも私以外の家族にシャットダウンを頼むこともできません。そんな理由からラズパイにシャットダウンと起動ボタンを付けることにしました。

LEDは載ってるものを使う
  • ボタン云々の前に、使っている方にはご理解いただけると思いますが、ラズパイ基板上の2個のLEDを見ても稼働状態とシャットダウン状態はかなり見分けにくいです。

  • ボタン追加時に判別のLEDも載せようかと思いましたが、CPUが止まるまでの制御ができないのと、追加回路に電源を引っ張りたくなかったことから他の方法を探したところ、基板上のLEDをハートビート設定にするというものを見つけました。

  • 下記のように /boot/config.txt の末尾に、LEDハートビート設定の1行を追加するだけです。私は緑側点滅を選びました。
  • # /bootへ移動
      $ cd /boot
    
    # 念のため config.txt バックアップ
      $ sudo cp -p config.txt config.txt.bak
    
    # config.txt の編集
      $ sudo nano config.txt
    
        # /boot/config.txt の末尾に下記の緑LED赤LEDか選んで追加 
        ------------------------------------------
        # Act LED(緑): heartbeat
        dtparam=act_led_trigger=heartbeat
        
        # Pwr LED(赤): heartbeat
        dtparam=pwr_led_trigger=heartbeat
        ------------------------------------------
    
    # 再起動
      $ sudo shutdown -r now

  • これでCPU稼働時はLED点滅、シャットダウン終了時にLEDが消えるので、状態が目視できるようになります。

保護抵抗は必要か?
  • 追加回路ではLEDを使用せず、基本はGNDとショートするボタンだけになりました。Pull-up設定した特定のGPIOに"0"が入力されたらシャットダウン動作させれば良いだけです。3.3V電源無しなので追加回路の接続ミス時に装置破壊する危険度はかなり下がりました。

  • ただ、Figure 1で示すように接続したGPIOが出力だった場合、下記の様に電源/GND間でのショートが起こるので、普通ならばGPIOドライバ出力電流のスペックで収まるように保護抵抗を入れます


  • Figure 1 : GPIO出力モード時にショートが起こる

  • しかし、ラズパイのGPIOで意図的な設定以外に出力ピンがあるかと言えば、実は無いんですね。下記は gpio readall コマンドで状態を見たものですが、3.3V/5Vの電源以外は全て入力モードです。
    $ gpio readall
     +-----+-----+---------+------+---+---Pi 3B+-+---+------+---------+-----+-----+
     | 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 |   IN | 0 | 13 || 14 |   |      | 0v      |     |     |
     |  22 |   3 | GPIO. 3 |   IN | 0 | 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 3B+-+---+------+---------+-----+-----+

  • よって3.3V/5V電源に相当する物理ピン(1,2,4,17番)にだけは接続しないように注意すれば、間違っても「思ったように動かない」だけで済みます。すでに現在ケースファンが3.3V(物理1番)と0V(物理6番)を使っているので、ピンの位置基準はわかりやすいです。

  • 結論としては、ラズパイGPIOとの信号接続時に電源ピンだけ注意すれば保護抵抗は必要無し(*2)と判断しました。

回路決定...とてもシンプル
  • さて実際の回路ですが、2本のGPIOと、それぞれペアになるGND(*3)を接続するだけなので、Figure 2のようになります。ちなみに赤文字の"X"は電源ピンで、そこに接続するのは絶対にNGという意味です。尚GPIOの配置はラズパイのサイトを参照しました。


  • Figure 2 : 信号接続図

  • Figure 2内のStart Button(起動ボタン)ですが、これはラズパイの機能そのものです。Shutdown状態でGPIO3に"0"入力すると再度起動します(*4)。よってボタンへの配線のみでソフト処理は何も必要ありません

  • Shutdown Button(シャットダウンボタン)はGPIO4に接続しています。このGPIO4にはPull-Up設定を行い、"0"入力されたらシャットダウン操作を行う(sudo shutdown -h now 発行)ように教え込みます(次のsectionにて)。

  • 小さなブレッドボード上で実際にFigure 2の配線接続を行い、ラズパイケースに貼り付けたのがFigure 3になります。左側がファイルサーバ、右側がネットワークプレーヤーです。青が起動ボタン、黒がシャットダウンボタンになります。


  • Figure 3 : 配線後の写真

シャットダウンに使うGPIOの設定をしよう
  • 物理配線は終了したので後は設定です。設定方法はこちらのサイトを参考にさせて頂きました。最初はPythonのスクリプトからGPIOを操作するためのライブラリ(python-rpi.gpio)をインストールします。
  • $ sudo apt-get update
    $ sudo apt-get -y install python-rpi.gpio

  • 次は、以下の設定を行うPythonスクリプトを作成します。
    • GPIO4 を 入力モード/Pull-up に設定にする
    • GPIO4 が 2秒間(2000ms) "0" になったら shutdownを行う
    • GPIO4 を 100msのサンプリング間隔で監視する

  • 具体的には下記のように編集を行います。アカウント名が pi ならば作成されたスクリプトは
      /home/pi/scripts/shutdown_by_button.py
    になりますが、scripts/ ディレクトリの下に置くかどうかは好みで決めて問題ありません。
  • $ cd
    $ mkdir scripts # scripts ディレクトリを作ってそこに置くかは
    $ cd scripts    # 好みで決めてOKです
    
    $ sudo nano shutdown_by_button.py  # このファイル名で編集。内容は下記。
    
    #!/usr/bin/env python import RPi.GPIO as GPIO import os, time GPIO.setmode(GPIO.BCM) # BCM番号で指定することを宣言。 # GPIO4 : shutdown button GPIO.setup(4, GPIO.IN, pull_up_down = GPIO.PUD_UP) # GPIO4は入力(IN)でpull-up(PDP_UP) def shutdown(channel): os.system("sudo shutdown -h now") # shutdownのshellコマンドを持つ関数の定義 GPIO.add_event_detect(4, GPIO.FALLING, callback = shutdown, bouncetime = 2000) # ↑GPIO4が2秒(2000ms)以上"0"ならば、shutdown関数を呼ぶ while 1: time.sleep(100) # GPIO4の監視は100ms間隔で行う
    $ sudo chmod +x shutdown_by_button.py # 実行権の付与

  • 作成したPythonスクリプトを systemctl に登録するためのserviceファイルを作成します。下記ではservice名をshutdown_by_buttonにしています。
  • $ cd /usr/lib/systemd
    $ sudo mkdir system # /usr/lib/systemd の下に system ディレクトリを作成
    $ cd system
    
    $ sudo nano shutdown_by_button.service # system ディレクトリの中でファイル作成。内容は下記。
    
    [Unit] Description=Shutdown/Reboot raspberry pi by GPIO button input Wants=network.target [Service] ExecStart=/home/pi/shutdown_by_button.py # 先程作成したPythonスクリプトの絶対パス Restart=on-failure RestartSec=10s [Install] WantedBy=multi-user.target

  • serviceファイル作成後、systemctlへの登録を行い、動作チェックします。
  • # 設定反映
      $ sudo systemctl daemon-reload
    # スクリプトスタート
      $ sudo systemctl start shutdown_by_button
    # 自動起動
      $ sudo systemctl enable shutdown_by_button
    # ステータスチェック
      $ sudo systemctl status shutdown_by_button

  • 最後のステータスチェックで下記のように緑丸●active (running)が出ればOKです。赤丸●が出てしまった場合は、Pythonスクリプトのtypo等が原因なのでチェックしてみて下さい。
  • $ sudo systemctl status shutdown_by_button
     shutdown_by_button.service - Shutdown/Reboot raspberry pi ...
       Loaded: loaded (/usr/lib/systemd/system/shutdown_by_button.service; enabled; ...
       Active: active (running) since Tue 2020-06-30 21:10:04 JST; 22h ago
     Main PID: 373 (python)
        Tasks: 2 (limit: 2200)
       Memory: 5.6M
       CGroup: /system.slice/shutdown_by_button.service
               mq373 python /home/pi/scripts/shutdown_by_button.py
    
     6月 30 21:10:04 pifs systemd[1]: Started Shutdown/Reboot raspberry pi by GPIO button input.

最後に
  • 上記を経て、我が家で運用中のラズパイはボタンシャットダウンができるようになりました。


  • Figure 4 : 運用中のラズパイ

  • ミニサイズのブレッドボードや、配線/ボタンは予備があるので、ラズパイを買い足すことがあれば同じようにくっつけようかなと思っています。
Notes
  • ディスプレイを接続せずネットワークアクセスだけで操作することです。ターミナル/VNC/ブラウザでアクセス。
  • 追加回路上で3.3V/5V電源を使わない場合です。
  • ミニブレッドボード上端子間で配線する良い具合のワイヤが無かったため、GND共有しませんでした。
  • こちらのサイトを参考にしました。GPIO 3は最初からデフォルトでpull-upですね。
Copyright(C) 2020 Altmo
本HPについて