プリントサーバー用のRaspberryPI 3B+にシャットダウンボタン・再起動ボタンを追加した。
私の3DプリンターにはRaspberryPI 3B+(以下PI)を搭載しています。
このPIですが現状、プリンター本体と電源を共用し、プリンター本体の電源断と同時にPIも電源供給が止まるようになっています。しかしながらこの場合PIにセットアップされているMicroSDカードの破損の可能性が低確率ですが存在します。今回、このリスク対処を行いました。
なぜMicroSDが破損するの?
PIはセットアップされたMicroSDカードに記録されているOSに常時アクセスをしているため、電源断のタイミングによってはMicroSDカードにアクセス中に電源が落ちる可能性があります。アクセス中に電源が落ちると最悪カードが破壊されることがあります。
ではどうやって破損を回避するのか。
意図的にMicroSDカードにアクセスしていない状況を作り出したのちに電源を切断すればほぼ問題ありません。具体的にはOSをシャットダウン後に電源を落とすわけです。
OSをシャットダウンするにはGUI画面からシャットダウンを実行するかbashからシャットダウンコマンドを実行することになります。すが毎回キーボードもしくはマウスをつないで(もしくはつないだまま)シャットダウンを実行するのは手間ですので3Dプリンターにシャットダウンボタンを搭載することにしました。
ボタン追加の概要
今回は、PIに標準搭載されているGPIOピンに押しボタンスイッチを配線し、OS上でボタンのON/OFFを監視。2秒以上ボタンが押されたときにシャットダウンコマンドが実行させるようにしました。
工程としては、
1.ボタンの設置
2.ボタンON/OFF検出プログラムの作成
3.起動・再起動時に自動的に2.のプログラムが起動するよう設定
4.ボタンを3Dプリンターに設置する取り付け具のデザイン・設置
以上4工程になります。
追加で、あったほうが運用上便利ということで再起動ボタンも実装しました。
まず使用するGPIOポートを決定する。
PIにはGPIOポートを使ってOSをシャットダウンする機能は搭載されていないため自作する必要があります。今回は、I2C通信を将来別な目的で使いたいということもあり、GPIO.2と3以外にスイッチを付けることを検討しましたが再起動用ボタンがGPIO.3固定のため断念。結果、GPIO.23とGPIO.GNDの間にシャットダウン用のスイッチの接続し、スイッチを押したらGPIO.23にGNDが入力されるようにしました。
再起動についてはPIの標準機能でシャットダウンモードの時にGPIO3にGNDを入力すると再起動するという機能がある為GPIO.3とGNDの間にスイッチを接続し、スイッチを押したらGPIO.3にGNDが入力されるようにしました。結果的にI2Cポートを利用するしかないため、将来I2Cを使うときは分岐する必要が出てしまいました。参考用の回路図を以下に示します。
シャットダウン用プログラムを作成する。
ボタンが2秒以上押されたらOSをシャットダウンするコードを作成します。PIにはpythonが最初からインストールされていますのでpythonで記述しました。プログラムはroot権限で動くようにしたかったため、/rootフォルダにshutBtn.pyというファイル名で作成しました。
※改行コードはLFにしないと実行時にエラーとなります。Windows等別環境で記述してPIに転送するときは注意が必要です。
#!/usr/lib/env python import RPi.GPIO as GPIO #GPIOモジュールを読み込み import os, time #os,timeの両モジュールを読み込み GPIO.setmode(GPIO.BCM) #GPIOはBCMモードで指定 GPIO.setup(23, GPIO.IN, pull_up_down = GPIO.PUD_UP) #GPIO23ピンを入力モード(GPIO.IN),プルアップ(pull_up_down = GPIO.PUD_UP)として定義 def shutdown(): #シャットダウンを関数化 OS.system("sudo shutdown -h now") #シャットダウン後システムを停止としてシャットダウン実行 try: while True: #Trueループなのでbreakされるまで繰り返す(ループ1) GPIO.wait_for_edge(23, GPIO.FALLING) #立ち去がりエッジ検出に指定 sw_cnt = 0 #sw_cntに0を代入する while True: #Trueループなのでbreakされるまで繰り返す(ループ2) sw_stat = GPIO.input(23) #GPIO.23の状態を代入 if sw_stat == 0: #GPIO.23がGND(0)ならば・・・ sw_cnt = sw_cnt + 1 #sw_cntに1を加算する if sw_cnt >= 200: #もし、sw_cntが200以上であれば・・・ shutdown() #シャットダウン関数を呼び出す break else #GPIO.23がGND(0)以外ならば break #ループ2を終了するのでループ1に戻る time.sleep(0.01) #GPIO.23がGNDならここに来るので0.01s待ループ2を繰り返す except KeyboardInterrupt: #CTRL-Cによる終了割込みがあれば pass finally: GPIO.cleanup() #GPIO終了処理を行う
動作確認する
まず、shutBtn.pyに実行権限を付与します。
pi@raspberrypi:~ $ sudo chmod +x /root/shutBtn.py
2.shutBtn.pyを実行
pi@raspberrypi:~ $ sudo /root/shutBtn.py
3.ボタンを2秒以上押してみてシャットダウンが開始されれば成功です。
再起動時に上記のプログラムが自動的に実行されるように設定する。
ケース1:rc.localに追記する方法
rc.localファイルの末尾にあるexit 0の上に以下の分を追記して保存。
/root/shutBtn.py exit 0
ケース2:デーモンとして実行する。
デーモンの設定ファイルを作るためにsystemdフォルダへ移動
pi@raspberrypi:~ $ cd /usr/lib/systemd/system/
nanoエディタ等で設定ファイルを記述します。
pi@raspberrypi:~ $ sudo nano shutBtn.service
内容は以下の通り。
[Unit] Description=Shutdown raspberry pi by GPIO Wants=network.target [Service] ExecStart=/root/shutBtn.py # 先程作成したコードの絶対パス Restart=on-failure RestartSec=10s [Install] WantedBy=multi-user.target
保存して終了します。
デーモンを起動し、OS起動時に自動起動するようにします。
pi@raspberrypi:~ $ sudo systemctl daemon-reload pi@raspberrypi:~ $ sudo systemctl start shutBtn pi@raspberrypi:~ $ sudo systemctl enable shutBtn
起動状態を確認
pi@raspberrypi:~ $ sudo systemctl status shutBtn
緑色の●で、Active:がactive(running)になっていれば成功です。
ボタンをプリンターに取り付ける取り付け具を作る。
3D CADなどを利用して用意したスイッチに合う取り付け具を作り、3Dプリンターで出力。
取り付けを行いました。
最終確認
一旦OSを再起動しデーモンが正常に動作しているか確認したのちに、シャットダウン用のスイッチを2秒以上押してみてシャットダウンが実行されること、シャットダウンが実行されたのちに再起動用のスイッチをおしてみてOSが起動することを確認。ここまで問題なければ終了です。
※シャットダウン後に主電源を落としてしまうと、そもそも通電していないので再起動用のボタンでは起動できません。あくまでも通電はしたままで、シャットダウンされた状態の時に押すと再起動するというものです。
補足.押しボタンスイッチに保護抵抗は必要ないのか?
保護抵抗の有り無しは最終的には設計者の考え次第だと思いますが、今回は省略しました。
理由は使用したGPIOピンは押しボタンスイッチ専用としたこと、PIは初期状態では入力モードで起動するため自分で出力モードに変更する予定が無い為です。以上より、将来的にポートを兼用する機能を実装する必要が出たときに保護抵抗も検討することとしました。
※ご要望があればこれについては詳しく別記事にしたいと思います。