Raspberry Pi 3 ModelBにシャットダウン/起動ボタンをつける

## 目的
Raspberry Pi 3 ModelBに(物理的な)シャットダウン/起動ボタンをつける

## 環境
Raspberry Pi 3 ModelB
gpio制御言語：C言語[^1]
[^1]: 内容的にはいまさらだが、CでGPIO制御を書いているサイトがあまりなかったので参考に残しておく

## 作業メモ

### 構成図

具体的な作業内容に入る前に構成図について説明する

![shutdown_start.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248487/e81d30c2-19db-36cb-ef2a-7f198d6a5579.png)

+ 画像の左側がシャットダウンボタン/右側が起動ボタンになる
+ ざっくり動きの流れを説明する
    + GPIO制御によって端子をプルアップ(High)設定
    + ボタンを押下するとGND(Low)に落ちる
    + それを検知してシャットダウンを実現する(起動ボタンは違う仕組みで動くので後述)

### GPIO制御環境を整える

<b>参考サイト</b>
https://tool-lab.com/raspi-gpio-controlling-command-2/
http://wiringpi.com/examples/gertboard-and-wiringpi/blink/

+ WiringPi(GPIO制御環境)を導入

[参考サイト](https://tool-lab.com/raspi-gpio-controlling-command-2/)の通り導入しようと思ったのだが、自分の環境ではデフォルトで入っていた
そのため、改めて導入はしていない
参考に`gpio -v`を実行したときの情報を載せておく

```
$ gpio -v
gpio version: 2.46
Copyright (c) 2012-2018 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と言えばということで、Lチカで動作確認する
ここでは起動ボタンで使うGPIO端子(pin16/GPIO23)を制御する
GPIO端子とWiringPiの対応は以下のコマンドで確認できる
pin16はWiringPiだと"4"になる(wPiの列で確認可能)

```
$ gpio readall
 +-----+-----+---------+------+---+---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 |   IN | 0 | 13 || 14 |   |      | 0v      |     |     |
 |  22 |   3 | GPIO. 3 |   IN | 0 | 15 || 16 | 1 | 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---+---+------+---------+-----+-----+
```

上記を元に、[WiringPiのサイト](http://wiringpi.com/examples/gertboard-and-wiringpi/blink/)に載っているサンプルコードほぼそのまま使う
変更したのは`#define LED     4`のところ、先ほど確認した"4"を入れた

```
#include <stdio.h>
#include <wiringPi.h>

// LED Pin - wiringPi pin 0 is BCM_GPIO 17.

#define LED     4

int main (void)
{
  printf ("Raspberry Pi - Gertboard Blink\n") ;

  wiringPiSetup () ;

  pinMode (LED, OUTPUT) ;

  for (;;)
  {
    digitalWrite (LED, 1) ;     // On
    delay (500) ;               // mS
    digitalWrite (LED, 0) ;     // Off
    delay (500) ;
  }
  return 0 ;
}
```

### シャットダウンボタンを作成

<b>参考サイト</b>
http://gurochoro.blogspot.com/2017/08/debounce-in-a-microcomputer.html
https://qiita.com/marumen/items/78f65ad946a69603382e

+ シャットダウンを検知するプログラムを作成

<b>作ったのは、2秒ボタンを押すとシャットダウンするプログラム</b>
先に作ったLチカプログラムをベースに必要なところを変更する
`pullUpDnControl(SHUTDOWNPIN, PUD_UP);` でGPIO端子(pin16/GPIO23)をプルアップ(High)にする
[参考サイト](http://gurochoro.blogspot.com/2017/08/debounce-in-a-microcomputer.html)の"ディレイ方式"を参考にチャタリング除去処理
チャタリング除去した上でLowを検知したらシャットダウン処理をする

```shutdownbutton.c
include <stdlib.h>
#include <wiringPi.h>

#define SHUTDOWNPIN     4
#define SHUTDOWNTIME    2000

int main(void) {

    wiringPiSetup();

    /* setting INPUT/PULLUP */
    pinMode(SHUTDOWNPIN, INPUT);
    pullUpDnControl(SHUTDOWNPIN, PUD_UP);

    /* for shutdown proc */
    while(1){
        if( digitalRead(SHUTDOWNPIN) == LOW ){
            delay(SHUTDOWNTIME);
            if( digitalRead(SHUTDOWNPIN) == LOW ){
                system("sudo shutdown -h now");
            }
        }
    }
  return 0;
}
```

+ シャットダウンプログラムをサービス化

[以前自分で作ったもの](https://qiita.com/marumen/items/78f65ad946a69603382e)を参考にプログラムを常駐させる(サービス化)

serviceファイルを作る
`WantedBy=multi-user.target` で起動後常駐する
`Type=simple` は1度動作する = シャットダウンなので、`simple`にしている

```shutdownbutton.service
[Unit]
Description=shutdown button
DefaultDependencies=no

[Service]
Type=simple
ExecStart=/usr/bin/shutdownbutton

[Install]
WantedBy=multi-user.target
```

各種プログラムを必要なところへ配置

```
$ sudo chown root:root *
$ sudo cp shutdownbutton /usr/bin/
$ sudo cp shutdownbutton.service /etc/systemd/system/
$ sudo systemctl list-unit-files --type=service | grep shutdown
shutdownbutton.service                 disabled
```

サービスとして動作しているか確認[^2]

[^2]: `sudo systemctl start [service名]`としないでも動いていた。`WantedBy=multi-user.target`の設定だから？

```
$ sudo systemctl status shutdownbutton
● shutdownbutton.service - shutdown button
   Loaded: loaded (/etc/systemd/system/shutdownbutton.service; disabled; vendor
   Active: active (running) since Fri 2021-01-22 08:22:56 JST; 4s ago
 Main PID: 2905 (sample_chatter_)
   CGroup: /system.slice/shutdownbutton.service
           |-2905 /usr/bin/sample_chatter_remove

 1月 22 08:22:56 raspberrypi systemd[1]: Started shutdown button.
```

動作したので、サービスを常駐させる

```
$ sudo systemctl enable shutdownbutton
Created symlink /etc/systemd/system/multi-user.target.wants/shutdownbutton.service → /etc/systemd/system/shutdownbutton.service.
```

再起動して起動時に有効になることを確認

```
$ sudo reboot
$ sudo systemctl status shutdownbutton
● shutdownbutton.service - shutdown button
   Loaded: loaded (/etc/systemd/system/shutdownbutton.service; enabled; vendor preset: enabled)
   Active: active (running) since Fri 2016-11-04 02:16:42 JST; 4 years 2 months ago
 Main PID: 83 (sample_chatter_)
   CGroup: /system.slice/shutdownbutton.service
          |-83 /usr/bin/sample_chatter_remove

Warning: Journal has been rotated since unit was started. Log output is incomplete or unavailable.
```

<b>これで起動後、2秒ボタンを押すとシャットダウンするようにできた</b>

### 起動ボタンを作成

<b>参考サイト</b>
https://qiita.com/clses/items/e701c1cb6490751a6040

こちらは[参考サイト](https://qiita.com/clses/items/e701c1cb6490751a6040)でも書かれているが、<b>最初に構成図で触れた通りGPIO3(pin5)をGNDに落とせばいいだけである</b>
元々OS側にシャットダウンからの起動が機能として備わっているらしい
そのため、特にソフトウェアでの処理は何もいらない

## まとめ

目的を達成！

>Raspberry Pi 3 ModelBに(物理的な)シャットダウン/起動ボタンをつける

今はスイッチをブレットボード上に実装しているが、すっきりさせるために[このサイト](http://hammmm.hatenablog.com/entry/2016/11/14/231337)で使われているようなスイッチでスリム化もしたい