Raspberry Piに電源ボタンと動作中インジケーターを付ける

以前、天体写真撮影でRaspberry Piを使うために「ヘッドレスのRPiにAndroid端末からVNCで接続して操作する」という話題を取り上げた([[これ]](https://qiita.com/tomoto335/items/c71b87730db413a8edbb)と[[これ]](https://qiita.com/tomoto335/items/7d087060d8a563e94bbd))。ただ、本格的にこの使い方をしていると、RPiに **(1)安全な電源ボタン** と **(2)動作中インジケーター**が欲しくなる。というわけでそれぞれ作ってみたが、あちこち情報を集める必要があったので、せっかくなのでここにまとめておく。

# 解決したい課題

* 通信に不具合があって操作ができなくなったときなど、安全に再起動したい。今は通信が途切れてしまうと**SDカードが壊れないよう祈りながら電源を抜くしかない**。ハードウェア的なボタンを押したらソフトウェア的にシャットダウンコマンドが投入されて安全に再起動できる、みたいなのが欲しい。
* 撮影終了後に簡単にシャットダウンしたい。望遠鏡の片付けなどやることがいろいろあるのに、**Android端末からGUIやコマンドでぽちぽちシャットダウンするのはめんどくさい**。本体側のハードウェア的なボタンを押したら(以下同文)。
* シャットダウンコマンド投入後、終了処理が完了し、電源を抜いてもよい状態になったことを知りたい。VNCはシャットダウンが始まった途端に切断されてしまうし、RPi自体には状態が一目でわかる仕組みがなく、**アホみたいにじっと待ってから祈りながら電源を抜くしかない**。シャットダウンが完了したことがわかるLEDか何かが欲しい。(欲を言えば通信の状態や各種サーバの起動状態なども知りたいのだが、今回はスコープ外とする。)

# 今回のソリューション

ハードウェア側は、下の写真と回路図に示すように、GPIOに抵抗入りLEDとボタンスイッチを取り付ける。オスメスのピンヘッダに部品を直接はんだ付けし、それを挿しただけの雑なもので問題ない。これに設定ファイルやスクリプト等、ソフトウェア側の変更を若干加え、次のような動きを実現する。

* RPi動作中にボタンを長押しすると、シャットダウンコマンドが投入され、HALT状態になる
* HALT状態でボタンを押すと、再起動する
* LEDは動作中は点灯、HALT状態では消灯する

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/465152/94dc9c2e-4190-9313-c876-c5fbf683c693.jpeg" width=640>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/465152/2be32568-8ce0-a69b-54b2-1fadfd1429d6.png" width=640>

# 電源ボタンの作り方

やり方はいろいろあるが、1ボタン・1配線でON/OFF両方やろうと思ったら、「GPIO3とGNDの間にスイッチを挟む」方法が最も手っ取り早い。GPIO3をLOWに落とすとHALT状態から復帰するという機能がRPiにもともと備わっているので、電源ONはそれをそのまま利用できる。電源OFFの方は、GPIO3を監視してシャットダウンコマンドを投入する設定をソフトウェア側で行う必要がある。

電源OFFの実装はこの記事↓を参考にさせてもらった。GPIOの監視とシャットダウンコマンドの投入を行なうスクリプトをデーモンとして仕込むものである。

https://howchoo.com/g/mwnlytk3zmm/how-to-add-a-power-button-to-your-raspberry-pi

筆者はこれを次のように書き換えて使うことにした。

* 撮影中にうっかりボタンに触って電源が切れると困るので、3秒程度の長押しで初めて動作するようにする。
* VNCの画面の方に何か痕跡を残したいので、シャットダウン前に開かれている全ターミナルにメッセージを送る。なおshutdownコマンドやwallコマンドによるメッセージは期待通りに出なかったので、[[このあたり]](https://askubuntu.com/questions/789510/wall-doesnt-broadcast-to-gui-terminals-on-16-04)を参考に俺様wallコマンドを作る羽目になった。

変更後のコードはこちら:

https://gist.github.com/tomoto/2c301abb34f7603e286a141b74993a4d

または、[gpio-shutdown](https://github.com/raspberrypi/firmware/blob/827fdd073638fa7b7292d1148fe0af7465111eae/boot/overlays/README#L1200)というデバイスツリーオーバーレイを使うと、/boot/config.txtに次の一行を書くだけでほぼ同じことが実現できる。

```
dtoverlay=gpio-shutdown,gpio_pin=3,active_low=1,debounce=3000
```

debounce というパラメタ名はわかりにくいが、これが長押しの要求になるようだ。ただ、シャットダウン前にメッセージを送るような処理を追加することはできない。筆者は自分でスクリプティングする方が早いので前述の方法を用いたが、柔軟性はなくてもありものを使う方がいいという人はこちらがお勧めである。

# 動作中インジケーターの作り方

/boot/config.txtに次の一行を書くと、起動後にGPIO14がHIGHになり、動作中それが保たれる。(筆者は成り行きによりGPIO14を使っているが、本来はどのピンを使っても構わない。) そのピンにLEDをつなげば動作中を表すインジケーターのできあがりである。

```
gpio=14=op,dh
```

このインジケーターはシャットダウン処理が完了する少し前に消灯することに注意。インジケーター消灯後に本体側の緑LEDの点滅が2～3秒残るので、それが黙るのを待ってから電源を抜いた方がよい。

別の方法として、[gpio-poweroff]((https://github.com/raspberrypi/firmware/blob/827fdd073638fa7b7292d1148fe0af7465111eae/boot/overlays/README#L1167))というデバイスツリーオーバーレイを使うこともできる。次の一行を/boot/config.txtに書くと、指定したピンが起動後にHIGHになり、シャットダウン完了後にLOWに落ちる。

```
dtoverlay=gpio-poweroff,gpiopin=14,active_low=1
```

これはもともと本体と同期して外部電源をON/OFFするための機能らしく、内部動作が先の方法とは若干異なる。完全に電源を切れる状態になってから信号が変化するというメリットはあるが、GPIO3による再起動ができなくなる(別の端子に出ているGLOBAL_ENを使う必要がある)、信号が変化した後一定時間内に電源をカットしないとカーネルパニックを起こす(システム異常と解釈される)など、インジケーターとして用いるには望ましくない副作用がある。

また、オンボードの赤/緑のLEDを操作することもできるようだ([[ここ]](https://mlagerberg.gitbooks.io/raspberry-pi/content/5.2-leds.html)や[[ここ]](https://raspi.maruzonet.com/archives/189))。筆者も試してみたが、もともとの機能(電圧やSDカードアクセスのチェック)が失われるのはそれはそれで困りそうなので却下した。それでもいいという人、あるいは両立させる方法が思いつく人は検討の余地があるだろう。

# 参考資料

* この話題が意外とトリッキーなのは、見た目は電源ON/OFF/その状態表示というシンプルなユースケースなのに、実際には3つの異なる技術の組み合わせであり、且つそれぞれ3つやそこらの複数の選択肢があるということだ。そのため、ネットを検索して得られる how-to は(この記事も含めて)人によって言うことが少しずつ違い、その理由や自分にとって最適な選択肢をちゃんと理解するには合計10個くらいの技術を理解する必要がある。この境地に自力で達するのが結構大変だったのだが、これらの技術が網羅的かつきれいに整理されている素晴らしいgist([Shutdown Raspberry Pi with a Single Button](https://gist.github.com/lbussy/9e81cbcc617952f1250e353bd42e7775))を見つけた。これから作ろうという人は是非一読をお勧めする。

* この仕掛けをパッケージして売ったら商売になるんじゃないか、既にあるんじゃないか、と調べたところPimoroniが出していた。ただ少々古い製品で、RPi4が要求する3Aには対応していない。あまり売れなかったのだろうか。

https://shop.pimoroni.com/products/onoff-shim