Raspberry Pi にVolume Up、Down、Mute、Shutdown、Resetボタンを付ける

# はじめに

* Raspberry piのGPIOにタクトボタン3個を接続し、Volume Up、Down、Mute、Shutdown、Reset（電源ON）の機能を割り当てます。GPIOのライブラリは[gpiozero](https://gpiozero.readthedocs.io/en/stable/)を使用します。gpiozeroは、長押、短押制御が簡単に実装できるのでオススメです。
* gpiozeroには、タクトボタンをシミュレートするMock PinsというAPIがあります。タクトボタンがない方も今回のコードを確認できるようにこのAPIの使用方法も記載しました。
* 以前投稿した[「gpiozeroのAPIでボタンの長押、短押をシンプルにコーディングする。」](https://qiita.com/Aco-gt/items/40381ca72d98605bde82) も参考になると思いますので合わせてご覧ください。

# 1. スイッチ接続方法

* Mute、Shutdown、Reset用：btn_3 -> GPIO3
（ResetボタンはGPIO3に接続する必要があります。）
GPIO3をGroundに落とすことでOSを起動することができます。電源の抜き差しが不要になり便利です。

* Volume Up : btn_5 -> GPIO5（他のGPIOでも問題ありません。）

* Volume Down : btn_11 -> GPIO11（他のGPIOでも問題ありません。）

* 各スイッチのマイナス側はGPIOのGroundにつないでください。私は9番pinに接続しています。

# 2. スクリプト

* 長押、短押のときに呼び出す関数を作成し、さらに関数内でpin番号毎の処理を記載しています。
* sudo amixer scontrols コマンドで表示されたcontrolを指定するようにしてください。今回は3B+を使用していますが私の環境では「Headphone」と表示されました。以前、Pi Zeroで実装した時は、「PCM」でした。HDMIを出力先にしている場合は他のcontrolの指定が必要かもしれません。
* コマンドをsudoで起動していますがこれは自動起動する際、rootユーザで起動することになるのでそれを考慮したものです。ちなみにpiユーザでコマンドを実行すると「Master」が表示されます。「Master」を指定する場合は、「amixer sset Master 10%+」のように%で制御する必要があります。rootユーザの時と制御方法が異なるのでpiユーザのコマンド実行は問題なく実行できるものの、自動起動にするとうまくいかなくてずいぶん時間を使いました。なぜ制御方法が異なるのか？理由についてまだ解明できていません。ご存知の方いらっしゃったらコメントください。
* Muteボタンはtoggle型です。ボタンを押す毎にMute、Mute解除になります。
* Muteボタン長押の時には、shutdownするようにコマンドを記載しました。shutdown状態でこの同じMuteボタンを押すとOSが起動します。Reset処理はgpiozeroの機能ではなくRaspberry Piの仕様です。Reset（電源ON）ボタンだけ実装したい場合はプログラム無しでただGPIO3とGroundにスイッチをつなぐだけで大丈夫です。

```    python:pyhome/gpio_3button_sample.py 
#!/usr/bin/python3
# coding:utf-8

import subprocess
from gpiozero import Button
from signal import pause


Button.was_held = False

def held(btn):
    btn.was_held = True
    print("button", btn.pin.number, "長押検知")
    # Shutdown
    if btn.pin.number == 3:
        subprocess.run('sudo poweroff', shell=True)

def released(btn):
    if not btn.was_held:
        print("button", btn.pin.number, "短押検知")
        if btn.pin.number == 5:
            # sudo amixer scontrols コマンドで表示されたcontrolを指定してください
            subprocess.run('sudo amixer sset Headphone 4db+', shell=True)
        elif btn.pin.number == 11:
            subprocess.run('sudo amixer sset Headphone 4db-', shell=True)
        elif btn.pin.number == 3:
            # toggle 指定するとボタンを押す毎にMute、Mute解除が可能になります。
            subprocess.run('sudo amixer sset Headphone toggle', shell=True)
    btn.was_held = False

btn_3 = Button(3)
btn_11 = Button(11)
btn_5 = Button(5)

btn_3.when_held = held
btn_3.when_released = released
btn_11.when_released = released
btn_5.when_released = released

pause()

```

# 3. 動作確認
### 3.１タクトボタンを接続して確認する場合
``` 
$ python3 pyhome/gpio_3button_sample.py

# button11を短押したとき Volume Down
button 11 短押検知
Simple mixer control 'Headphone',0
  Capabilities: pvolume pvolume-joined pswitch pswitch-joined
  Playback channels: Mono
  Limits: Playback -10239 - 400
  Mono: Playback -2208 [75%] [-22.08dB] [on]

# button5を短押したとき Volume Up
button 5 短押検知
Simple mixer control 'Headphone',0
  Capabilities: pvolume pvolume-joined pswitch pswitch-joined
  Playback channels: Mono
  Limits: Playback -10239 - 400
  Mono: Playback -1808 [79%] [-18.08dB] [on]

# 状態が[on]の時にbutton3を短押したとき Mute
button 3 短押検知
Simple mixer control 'Headphone',0
  Capabilities: pvolume pvolume-joined pswitch pswitch-joined
  Playback channels: Mono
  Limits: Playback -10239 - 400
  Mono: Playback -1808 [79%] [-18.08dB] [off]

# 状態が[off]の時にbutton3を短押したとき Mute解除
button 3 短押検知
Simple mixer control 'Headphone',0
  Capabilities: pvolume pvolume-joined pswitch pswitch-joined
  Playback channels: Mono
  Limits: Playback -10239 - 400
  Mono: Playback -1808 [79%] [-18.08dB] [on]

# button3を長押したとき shutdown
```
### 3.2 Mock Pinsで確認する場合
* [Mock pinsというAPI](https://gpiozero.readthedocs.io/en/stable/api_pins.html#mock-pins)を使えば物理スイッチがなくてもスイッチ動作をシミュレートすることができます。
* スクリプト pyhome/gpio_3button_sample.py に Mock pinsを使うための宣言を追加します。
* 対話型実行環境として Ipythonを使用します。

```txt:IPythonの起動
 $ python3 -m IPython
Python 3.7.3 (default, Jul 25 2020, 13:03:44)
Type "copyright", "credits" or "license" for more information.

IPython 5.8.0 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: 
```

* IPythonのプロンプトが表示されたら以下ソースをペーストします。

```txt:Mockpinsを使用するためスクリプト

import subprocess
from gpiozero import Button
from signal import pause

### Mock pinsを使用するために追加するコード ### 
from gpiozero.pins.mock import MockFactory
from gpiozero import Device
Device.pin_factory = MockFactory()
############################################ 

Button.was_held = False

def held(btn):
    btn.was_held = True
    print("button", btn.pin.number, "長押検知")
    # Shutdown
    if btn.pin.number == 3:
        subprocess.run('sudo poweroff', shell=True)

def released(btn):
    if not btn.was_held:
        print("button", btn.pin.number, "短押検知")
        if btn.pin.number == 5:
            subprocess.run('sudo amixer sset Headphone 4db+', shell=True)
        elif btn.pin.number == 11:
            subprocess.run('sudo amixer sset Headphone 4db-', shell=True)
        elif btn.pin.number == 3:
            subprocess.run('sudo amixer sset Headphone toggle', shell=True)
    btn.was_held = False

btn_3 = Button(3)
btn_11 = Button(11)
btn_5 = Button(5)

btn_3.when_held = held
btn_3.when_released = released
btn_11.when_released = released
btn_5.when_released = released

### Mock pinsを使用するために除外するコード ###
# pause()
############################################ 
```
* 続いてIPythonのコンソールにbuttonの短押と長押しをシミュレートするコマンドを入力します。

短押：btn.pin.drive_low();btn.pin.drive_high()
長押：btn.pin.drive_low()
ボタンを元に戻す：btn.pin.drive_high()

短押は押し込んですぐ離すlow(押す)->high(離す)
長押は押したままlow(押す)
メソッドがタクトボタンの動きと連動してるのでわかりやすいと思います。

```
# button3 短押シミュレート Mute
In [2]: btn_3.pin.drive_low();btn_3.pin.drive_high()
button 3 短押検知
Simple mixer control 'Headphone',0
  Capabilities: pvolume pvolume-joined pswitch pswitch-joined
  Playback channels: Mono
  Limits: Playback -10239 - 400
  Mono: Playback 0 [96%] [0.00dB] [off]

# button3 短押シミュレート Mute解除
In [3]: btn_3.pin.drive_low();btn_3.pin.drive_high()
button 3 短押検知
Simple mixer control 'Headphone',0
  Capabilities: pvolume pvolume-joined pswitch pswitch-joined
  Playback channels: Mono
  Limits: Playback -10239 - 400
  Mono: Playback 0 [96%] [0.00dB] [on]

# button11 短押シミュレート Volume Down
In [4]: btn_11.pin.drive_low();btn_11.pin.drive_high()
button 11 短押検知
Simple mixer control 'Headphone',0
  Capabilities: pvolume pvolume-joined pswitch pswitch-joined
  Playback channels: Mono
  Limits: Playback -10239 - 400
  Mono: Playback -400 [92%] [-4.00dB] [on]

# button5 短押シミュレートVolume Up
In [5]: btn_5.pin.drive_low();btn_5.pin.drive_high()
button 5 短押検知
Simple mixer control 'Headphone',0
  Capabilities: pvolume pvolume-joined pswitch pswitch-joined
  Playback channels: Mono
  Limits: Playback -10239 - 400
  Mono: Playback 0 [96%] [0.00dB] [on]

# button3を元に戻す
In [6]: btn_3.pin.drive_high()

# button3 長押シミュレート shutdown
In [7]: btn_3.pin.drive_low()
```
# 4. 自動起動設定

``` 
# スクリプトに実行権を付与する
$ chmod +x pyhome/gpio_3button_sample.py

# 自動起動用サービスファイルを作成する
sudo vi /etc/systemd/system/gpio_3button_sample.service
``` 

```/etc/systemd/system/gpio_3button_sample.service 
[Unit]
Description=Pi Zero Gpio Service Daemon

[Service]
ExecStart =/home/pi/pyhome/gpio_3button_sample.py
Restart=always
Type=simple
[Install]
WantedBy=multi-user.target
``` 
``` 
# サービス自動起動を有効にする
$ sudo systemctl enable gpio_3button_sample
# サービスを起動する
$ sudo systemctl start gpio_3button_sample
# サービスの状態を確認する
$ sudo systemctl status gpio_3button_sample
● gpio_3button_sample.service - Pi Zero Gpio Service Daemon
   Loaded: loaded (/etc/systemd/system/gpio_3button_sample.service; enabled; vendor preset: enabled)
   Active: active (running)
```

# おわりに
Raspberry Piにスイッチつけたりスピーカつけたりするとぐっと使いやすくなると感じています。マウスやキーボードで操作するのとは違ったダイレクトな操作性は気持が良いです。
この記事が何かのお役にたてれば幸いです。

では、すてきなRaspberry Pi Lifeを！