[Raspberry Pi] 物理ボタンを押してBluetoothデバイス（ゲームパッド）に接続できるようにする

本記事ではゲームパッドの接続を扱いますが、おそらく他のデバイスでも応用できると思います。

## 用意するもの

- Raspberry Pi
    - 4Bで動作確認済
- PS4用 DUALSHOCK4
    - [価格.comで見る限り6,000円台で入手できます](https://kakaku.com/game/game-accesory/itemlist.aspx?pdf_ma=1105)
- タクトスイッチ
    - [秋月で購入できる安価なもので十分です](https://akizukidenshi.com/catalog/c/ctactsw/)
    - もちろんモーメンタリな物ならどんなスイッチでも大丈夫です
- ブレッドボード等

## やりたいこと

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/185296/f66eb9e5-04b7-b31e-c446-378f833f0054.png)

画像は[SONY公式サイト](https://www.playstation.com/ja-jp/accessories/dualshock-4-wireless-controller/)および[秋月電子](https://akizukidenshi.com/catalog/g/gP-03649/)より引用・加工。

### 妥協点

- 初回はコマンドラインでペアリング済みのデバイスを接続する手順が必要
    - 長押ししたら新規デバイスを探してくれて自動で接続……みたいなのではない
- Bluetooth Device Addressは直書きしている
    - 本当は設定ファイルみたいなのを読みに行くように修正するほうがよさそう

## 配線

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/185296/0625271e-234c-e5d8-e069-94d29950d955.png)

## 事前準備

コマンドラインから Bluetooth 接続できるようにしましょう。
ジョイスティックのドライバをインストールしておきます。

```
sudo apt install joystick -y
sudo apt install jstest-gtk -y
```

Bluetoothをコマンドラインで叩くツールを起動します。

```sh
bluetoothctl
```

bluetoothctl 上でデバイスのスキャンを開始します。

```sh
scan on
```

DUALSHOCK4で PS ボタンと SHARE ボタンを同時に押します。ライトバーが白く点滅開始したらペアリングを開始できます。
以下のように `Wireless Controller` とアドレスが表示されたら、アドレスをメモしておきましょう。

```
[NEW] Device XX:XX:XX:XX:XX:XX Wireless Controller
```

DUALSHOCK4 が検知されたらスキャンを終了します。

```sh
scan off
```

先程のアドレスを使ってペアリングします。

```sh
pair XX:XX:XX:XX:XX:XX
```

`Attempting to pair with XX:XX:XX:XX:XX:XX` と表示され、`Pairing successful` と表示されたら接続完了です。
DUALSHOCK4 のライトバーは青く光ります。bluetoothctl を終了します。

```sh
quit
```

ゲームパッドの動作チェックをします。

```sh
jstest-gtk
```

コントローラを操作し、反応をチェックして `Ctrl` + `C` で終了します。
一旦シャットダウンし、電源（USB）を一旦抜きます。DUALSHOCK4のライトバーが消灯し、接続解除が確認できたら次のステップに進みましょう。

```sh
shutdown -h now
```

なお、ラズパイを含めた Linux 環境で DUALSHOCK4 を使う場合は ds4drv を使う手もありますが、後述するように現在は非推奨の模様です。

## Python スクリプト

Pythinファイルを作成します。

```sh
nano bt_button.py
```


今回は GPIO 27 を使用していますが、環境によって変えます。
ここでもアドレスは先程のものに揃えてください。

```python:bt_button.py
import RPi.GPIO as GPIO
import subprocess
import time

GPIO.setmode(GPIO.BCM)

# GPIO27 を入力として設定。プルダウン抵抗をON（接続していない状態で False）にする
GPIO.setup(27, GPIO.IN, pull_up_down=GPIO.PUD_UP)

bt_btn_status = 1
 
while True:
    bt_btn_status = GPIO.input(27)
    if bt_btn_status == 0:
        subprocess.run("""
        bluetoothctl << EOF
        connect XX:XX:XX:XX:XX:XX
        """, shell=True)
        break
    time.sleep(0.05)
```

まずは実行して試しましょう。

```sh
python3 ./bt_button.py
```

スクリプトを実行すると操作を待ち受ける状態になるので、DUALSHOCK4 の PSボタンと SHARE ボタンを長押しし、タクトスイッチを押しましょう。
ライトバーが青く点灯すれば成功です。

`Ctrl` + `C` で上記のスクリプトを停止させます。

## service 登録する

先ほど作成したスクリプトを `/opt` 以下に移動させます。

[Filesystem Hierarchy Standard](https://www.pathname.com/fhs/pub/fhs-2.3.pdf) によれば `/opt` がそういう目的であることに加え、"/opt は起動時にマウントされていることが保証されている" との情報を見かけたのですが、軽く調べた限りソースが見当たらず……
現状、`/opt` に置く理由は「そうやっている方がいらっしゃるので」くらいであまり望ましい感じではないので、もう少し掘り下げたいです。

```sh
sudo mkdir -p /opt/bt_button
sudo mv ./bt_button.py /opt/bt_button/bt_button.py
```
以下のコマンドを叩き、`bt_button.service` ファイルを所定の位置に作成します。

```sh
sudo nano /etc/systemd/system/bt_button.service
```

```ini:bt_button.service
[Unit]
Description=GPIO BT Button

[Service]
ExecStart=/usr/bin/python3 /opt/bt_button/bt_button.py

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

service に登録します。

```sh
sudo systemctl enable bt_button.service
```

シャットダウンします。

```
sudo shutdown -h now
```

シャットダウンしたら先ほどと同様に一旦ラズパイの給電（USB）を抜きましょう。DUALSHOCK4のライトバーが消灯したら、電源を再接続し、ラズパイを起動します。
その後、接続手順（DUALSHOCK4のペアリング操作→タクトスイッチ操作）を試し、ライトバーが青く点灯すれば成功です。

## 参考にした記事

- [Raspberry Pi で systemd を使ってプログラムを自動実行する](https://qiita.com/molchiro/items/ee32a11b81fa1dc2fd8d)
    - service化
- [RaspberryPiにshutdownボタンを付けよう](https://jellyware.jp/kurage/raspi/shutdown_button.html)
    - GPIOでボタンを扱う方法に関して