Nintendo Switch の Joy-Con で家電を操作する

[Life is Tech ! Advent Calendar 2018](https://qiita.com/advent-calendar/2018/life-is-tech) 23日目の記事です。

# はじめに

こんにちは、Life is Tech ! メンターのつっちーです。

突然ですが、僕はいま悩んでいることがあります。それは、「**リモコンがたくさんあるのをどうにかしたい問題**」についてです。

![1_cons.JPG](https://qiita-image-store.s3.amazonaws.com/0/120362/53766673-0f8f-a007-e652-44452bdee09f.jpeg)

ライトやエアコン、スピーカなどの家電を操作するリモコン。確かに必要ではあるのですが、たくさんの種類のリモコンが机に置いてある状態はイケてないですよね。別のもので代用して1つにまとめることができれば、とってもスマートになるはずです。

Google Homeなどのスマートスピーカを使って声で操作するという方法は最近よく見られるけど、やっぱり物理的なリモコンの操作性は捨てがたい。市販されているスマートリモコンを買って1つにまとめてもいいけど、それなら何か身近なもので代用できるんじゃないか・・・。

そんなことをぐるぐる考えていた僕の目に、ふと映ったものがありました。

![2_switch.JPG](https://qiita-image-store.s3.amazonaws.com/0/120362/773c2940-712a-bc0c-cea5-63cbf84a5e2a.jpeg)

![3_joycons.JPG](https://qiita-image-store.s3.amazonaws.com/0/120362/189610c5-bac8-cb4b-6c5f-81570fcdc111.jpeg)


そう、 Nintendo Switch の **Joy-Con** です。

手に取りやすいサイズと形で、適度な数のボタンも付いています。複数のリモコンのよく使うボタンを1つにまとめるのには十分ではないでしょうか。
なにより、Joy-Conで暖房をつけることなんかができたりしたらすごく楽しそうです。

というわけで、プログラミングと電子工作の力を使って、**Joy-Con をリモコン代わりにしてスマートに家電を操作**できるようにしていきたいと思います。

# 概要
 Joy-Con と Raspberry Pi を使い、以下のようなシステムを作っていきます。

![system.001.jpeg](https://qiita-image-store.s3.amazonaws.com/0/120362/32db55f4-c8e4-81f8-5a93-940ba94274c1.jpeg)


今回の記事では、部屋のシーリングライトを操作するこちらのリモコンをメインに扱っていきたいと思います。

![5_lightcon.JPG](https://qiita-image-store.s3.amazonaws.com/0/120362/2a8ed916-1558-c4be-0bc4-3df713954a2e.jpeg)


# 使用したもの

| 使用したもの | 備考 |
|:-----------|:-----------|
| Nintendo Switch Joy-Con (R) | 左右(LR)どちらでも大丈夫です。 |
| シーリングライト用リモコン |  |
| Raspberry Pi Zero WH | OS: Raspbian GNU/Linux 9.6 (stretch) |
| micro SDカード |  |
| 赤外線リモコン受信モジュール(ＯＳＲＢ３８Ｃ９ＡＡ) |  |
| ５ｍｍ赤外線ＬＥＤ(９４０ｎｍ ＯＳＩ５ＦＵ５１１１Ｃ－４０) |  |
| カーボン抵抗１Ｗ２００Ω |  |
| ジャンプワイヤ オスーメス |  |
| ミニブレッドボード(ＢＢ－６０１) ||



# 注意
ここから電子工作をしていきますが、部品やRaspberry Piはすぐ壊れてしまうため、実際に試す際は配線を間違えないよう注意して作業してください。自己責任でお願いします。

# 手順
## 0. Raspberry Pi のセットアップ
ここでは、Raspberry PiへのOSのインストールや初期設定については省略します。セットアップについては以下の記事が参考になるかと思います。

[Raspberry Pi Zero(W)のセットアップ](https://qiita.com/hishi/items/8bdfd9d72fa8fe2e7573)

## 1. Joy-Conのボタン入力を検知する
まずは、Joy-Conのボタン入力を検知する部分を実装していきます。

### Joy-ConとRaspberry Piを接続する
実は、 Joy-Con は普通に Bluetooth デバイスとして扱うことができます。なので、マウスやキーボードと同じようにペアリングするだけで簡単に接続することができます。

今回はコマンドライン上でペアリングしていきます。以下のコマンドで `bluetoothctl` を起動します。

```
$ sudo bluetoothctl
```

`bluetoothctl` の対話環境が起動するので、以下のように `scan on` と入力し実行します。 

```
[bluetooth]# scan on
```

デバイスのスキャンが始まったので、Joy-Conのシンクロボタンを長押ししてペアリングモードにします。
すると、スキャンされたデバイスが一覧で表示される中に、以下のようにJoy-Conが出てきます。

```
[bluetooth]# scan on
Discovery started
・・・
[NEW] Device XX:XX:XX:XX:XX:XX Joy-Con (R)
・・・
```

この `XX:XX:XX:XX:XX:XX` (Joy-Conによって異なります)はMACアドレスです。このMACアドレスを使って以下のようにコマンドを入力することでペアリング・接続を行います。

```
[bluetooth]# pair XX:XX:XX:XX:XX:XX
[bluetooth]# trust XX:XX:XX:XX:XX:XX
[bluetooth]# connect XX:XX:XX:XX:XX:XX
```

それぞれ `succeeded` または `successful` と表示されれば成功です。

余談ですが、MacやWindowsのPCでも接続できます。ゲームコントローラとして使うことができるのはもちろん、プレゼン用のリモコンとして使うことなんかもできます(参考記事：[【JavaScript】スイッチのJoy-Conをプレゼンリモコンにするワンライナー](https://qiita.com/mascii/items/ad17ffa962bc82b9c4f6))。余談終わり。


### pygameを使ったスクリプトを書く

ペアリングができたら、Joy-Conのボタン入力を検知するスクリプトを書いていきます。Pythonのライブラリである `pygame` を使います。
今回は `con.py` というファイルを作って以下のようなコードを書きました。

```con.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import time
import pygame


def main():
    pygame.init()
    joys = pygame.joystick.Joystick(0)
    joys.init()

    while True:
        events = pygame.event.get()
        for event in events:
            if event.type == pygame.JOYBUTTONDOWN or event.type == pygame.JOYHATMOTION:
                print(event)
        time.sleep(0.1)

        
if __name__ == '__main__':
    main()
```

#### ちょっと解説
まず `pygame` をimportします。

```con.py
import pygame
```

次に、pygameの初期化、Joystickの取得・初期化を行っています。

```con.py
pygame.init()
joys = pygame.joystick.Joystick(0)
joys.init()
```

以下の部分では、Joy-Con からの入力を `events` として取得し、それがボタン入力( `pygame.JOYBUTTONDOWN` )またはスティック入力( `pygame.JOYHATMOTION` )であればその内容を `print` しています。

```con.py
events = pygame.event.get()
        for event in events:
            if event.type == pygame.JOYBUTTONDOWN or event.type == pygame.JOYHATMOTION:
                print(event)
```

このスクリプトを実行し、Joy-Conのボタンを押したりスティックを倒したりしてみると、以下のように出力されるはずです。

```
$ python3 con.py
<Event(10-JoyButtonDown {'joy': 0, 'button': 0})>
<Event(10-JoyButtonDown {'joy': 0, 'button': 1})>
<Event(10-JoyButtonDown {'joy': 0, 'button': 3})>
<Event(10-JoyButtonDown {'joy': 0, 'button': 2})>
<Event(9-JoyHatMotion {'joy': 0, 'hat': 0, 'value': (0, -1)})>
<Event(9-JoyHatMotion {'joy': 0, 'hat': 0, 'value': (0, 0)})>
<Event(9-JoyHatMotion {'joy': 0, 'hat': 0, 'value': (0, 1)})>
<Event(9-JoyHatMotion {'joy': 0, 'hat': 0, 'value': (0, 0)})>
<Event(9-JoyHatMotion {'joy': 0, 'hat': 0, 'value': (0, -1)})>
```

何度か試していくと、ボタンとidの対応、スティックの向きとvalueの対応が分かってきますね。
終了する場合は` control+C` を入力します。
これで、Joy-Conの入力が検知できるようになりました。

## 2. 送信するリモコン信号を取得する
Joy-Con の入力が検知できるようになったので、次はその入力の際にリモコン信号を送信するようにすればいいですね。しかし、そのためにはどんなリモコン信号を送信すればいいのかを知らなければいけません。そこで、リモコンから電化製品に送られる赤外線信号を取得していきます。

### 赤外線を受信する回路を組む

まずは赤外線を受信する回路を組んでいきます。
赤外線受信モジュールの3本の足を、Raspberry Piのそれぞれ対応するピンとジャンプワイヤーでつなぎます。
今回はinputをgpio18としました。

![IMG_9868.JPG](https://qiita-image-store.s3.amazonaws.com/0/120362/473a852e-98ef-783e-1d5a-3b5e1e116794.jpeg)



<img width="864" alt=" 2018-12-23 20.01.16.png" src="https://qiita-image-store.s3.amazonaws.com/0/120362/13caa3d8-ba70-17ad-ae3e-49c6ae3d6c4c.png">


### pigpioで赤外線を受信する

赤外線の送受信をするために、今回は `pigpio` というライブラリを使っていきます。僕のRaspberry Piにははじめからインストールされていましたが、もしされていないようであれば以下のようにしてインストールします。

```
$ sudo apt install pigpio python3-pigpio
```

以下のコマンドでpigpioを起動しておきます。

```
$ sudo pigpiod
```

赤外線の送受信に使うスクリプトはpigpioのサイト上で公開されているもの( http://abyz.me.uk/rpi/pigpio/examples.html#Python_irrp_py )をそのまま使うことにします。

以下のコマンドでダウンロードします。 `con.py` と同じ場所においておきましょう。

```
$ curl http://abyz.me.uk/rpi/pigpio/code/irrp_py.zip | zcat > irrp.py
```

以下のようにこのスクリプトを実行すると、赤外線の受信・学習ができます。

```
$ python3 irrp.py -r -g18 -f codes light:on --no-confirm --post 100
```

オプションがたくさん付いていますが、それぞれ下の表のような意味を持っています。

| オプション | 意味 |
| :---------- | :---------- |
| -r | recordモード |
| -g18 | gpio18で受信する |
| -f codes light:on | codesというファイルの中にlight:onという名前で出力する |
| --no-confirm | 確認のためにもう一度信号を要求することをしない |
| --post 100 | 100msの間赤外線を受信しなかったら信号が終了したとみなす |

実行すると以下のように表示されます。

```
Recording
Press key for 'light:on'
```

このときにリモコンを赤外線受信モジュールに向けて「全灯」ボタンを押してみると、

```
Okay
```

と出力されて実行が終了します。どんな信号が受信されたのか確認してみると、

```
$ cat codes
{"light:on": [3456, 1653, 415, 1283, 415, 1283, 415, 408, 415, 408, 415, 1283, 415, 408, 415, 408, 415, 408, 415, 1283, 415, 1283, 415, 1283, 415, 408, 415, 1283, 415, 408, 415, 408, 415, 408, 415, 408, 415, 408, 415, 1283, 415, 408, 415, 408, 415, 408, 415, 408, 415, 408, 415, 1283, 415, 408, 415, 408, 415, 408, 415, 1283, 415, 1283, 415, 1283, 415, 1283, 415, 408, 415, 1283, 415, 1283, 415, 1283, 415, 59728, 3456, 1653, 415, 1283, 415, 1283, 415, 408, 415, 408, 415, 1283, 415, 408, 415, 408, 415, 408, 415, 1283, 415, 1283, 415, 1283, 415, 408, 415, 1283, 415, 408, 415, 408, 415, 408, 415, 408, 415, 408, 415, 1283, 415, 408, 415, 408, 415, 408, 415, 408, 415, 408, 415, 1283, 415, 408, 415, 408, 415, 408, 415, 1283, 415, 1283, 415, 1283, 415, 1283, 415, 408, 415, 1283, 415, 1283, 415, 1283, 415, 59728, 3456, 1653, 415, 1283, 415, 1283, 415, 408, 415, 408, 415, 1283, 415, 408, 415, 408, 415, 408, 415, 1283, 415, 1283, 415, 1283, 415, 408, 415, 1283, 415, 408, 415, 408, 415, 408, 415, 408, 415, 408, 415, 1283, 415, 408, 415, 408, 415, 408, 415, 408, 415, 408, 415, 1283, 415, 408, 415, 408, 415, 408, 415, 1283, 415, 1283, 415, 1283, 415, 1283, 415, 408, 415, 1283, 415, 1283, 415, 1283, 415]}
```

このように出力されています。ここから、同じ手順で `light:off` といった名前をつけて各ボタンの信号を受信していくと、このファイルにそれぞれの信号が記録されていきます。

これで、送信したい信号を取得することができました。


## 3. リモコン信号を送信する

それでは、いま取得した信号を送信する準備をしていきます。

### 赤外線を送信する回路を組む
まずは、赤外線を送信する回路を組んでいきます。
赤外線LEDと抵抗をジャンプワイヤーでつなぎます。
今回はoutputをgpio21としました。

![IMG_9876.JPG](https://qiita-image-store.s3.amazonaws.com/0/120362/d97ea036-6697-de08-28cc-92068bcce00b.jpeg)

<img width="864" alt=" 2018-12-23 20.01.37.png" src="https://qiita-image-store.s3.amazonaws.com/0/120362/a9a882e8-2a32-3980-1efb-4e0bf8152cb4.png">

ここまでで回路は完成です。実際の回路はこのような感じになりました。

![IMG_9859.JPG](https://qiita-image-store.s3.amazonaws.com/0/120362/662fe816-4e1c-a3e0-9f43-f9a3deb76b20.jpeg)


### pigpioで赤外線を送信する
赤外線信号の送信は、以下のコマンドで行うことができます。

```
$ python3 irrp.py -p -g21 -f codes light:on
```
オプションは以下の意味を持っています。

| オプション | 意味 |
| :---------- | :---------- |
| -p | playbackモード |
| -g21 | gpio21で送信する |
| -f codes light:on | codesというファイルの中にあるlight:onという名前の信号を出力する |

実行することで、赤外線信号がLEDから出力され、シーリングライトの灯りがつきます！わーい！


## 4. Joy-Conの入力で赤外線信号を送信するようにする
最後に、手順1で作成したJoy-Conの入力を検知するPythonスクリプトと連携させていきましょう。
以下のように書き換えます。

```con.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import time
import subprocess
import pygame


def main():
    pygame.init()
    joys = pygame.joystick.Joystick(0)
    joys.init()

    while True:
        events = pygame.event.get()
        for event in events:
        	if event.type == pygame.JOYBUTTONDOWN and event.button == 0:
                subprocess.run('python3 irrp.py -p -g21 -f codes light:on', shell=True)
                print('Button 0 was pressed.')
                
        time.sleep(0.1)

        
if __name__ == '__main__':
    main()
```

#### ちょっと解説

irr.py をスクリプト内で実行するために、`subprocess` をimportします。

```con.py
import subprocess
```

0番目のボタン(=B)が押されたときに、赤外線信号を送信するコマンドを実行するようにします。

```con.py
if event.type == pygame.JOYBUTTONDOWN and event.button == 0:
	subprocess.run('python3 irrp.py -p -g21 -f codes light:on', shell=True)
```


ここまできたら、以下のようにして実行して、Bボタンを押してみましょう。

```
$ python3 con.py
```

![IMG_9874.JPG](https://qiita-image-store.s3.amazonaws.com/0/120362/b57b1032-630b-089a-4f40-5cfe2cde35c9.jpeg)

Joy-Conのボタンを押すことで、シーリングライトを点灯させることができました！


# おわりに
少しのプログラミングと簡単な電子工作で、Joy-Conをスマートリモコンとして使えるようにすることができました。これを応用すれば、Joy-Conを使って様々なWebサービスを動かすことも簡単にできそうです。逆に、Joy-Conではない他のデバイスをリモコン代わりにすることもできるでしょう。

こういった開発をすることで身の回りの生活が便利になりますし、なによりも楽しいので、これからもいろいろ作っていきたいと思います。