エアコンのリモコン信号を解析する ~デコード編~

# なぜエアコンのリモコン信号を解析するのか

Raspberry PiのGPIOに赤外線LEDを繋ぎ、TVや部屋の照明等のリモコン信号を記録して操作するハックは多くの人が挑戦しています。例えば、以下の記事のようなものです。

[格安スマートリモコンの作り方](https://qiita.com/takjg/items/e6b8af53421be54b62c9)

また、エアコンの操作を行うハックも同様に行われています。例えば、以下の記事のようなものです。

[Slack経由で家の外からエアコンをon, offできる装置を、Raspberry Piで作ってみた。（しかも御坂美琴ちゃんが応答してくれる）](https://qiita.com/KAKY/items/55e6c54fa2073cdc0bbe)

ただし、このようなエアコンの操作を行うハックでは多くの場合、任意の温度やタイマーを設定することができません。これは、TVや照明のリモコンが「どのボタンを押されたのか」という情報だけを赤外線信号で送っているのに対し、エアコンのリモコンは、以下の画像のような「リモコンの液晶に表示されている全ての情報」を毎回送っているためです。つまり、同じ「温度を下げる」ボタンを押した場合でも、それまでの操作によって送信される信号が異なります。

<img src="https://qiita-image-store.s3.amazonaws.com/0/131110/189e9bde-6140-39e8-dd4f-311e3057a266.png" width="300px">

このような仕様のため、多くの人は特定のモード・設定温度・タイマー等を設定した状態での赤外線信号のみを記録し、Slack等を経由してその信号を送信するようにしているのです。

でも、せっかくSlack等でエアコンを操作できるようにするなら、あらゆる操作を行えるようにしたいところです。そこで、エアコンのリモコンが送信している赤外線信号を解析することにしました。

注意：この記事は特定の機種のリモコン信号を解析した結果であって、あらゆる機器が同じ仕様で作られているとは限りません。（むしろ、各機種固有の機能等に対応するため、異なる仕様である可能性が高いです。）ご自宅のエアコンで同じようなことをしたい方は、ぜひご自身の手でご使用中の機種のリモコン信号を解析してみてください。

今回は我が家にある三菱 MSZ-GV225-Wのリモコン信号を解析します。リモコン送信機型名 RH151と書いてあったので、同じ型名の機種であれば同じ信号を使っているはずです。

# 赤外線信号を受信して記録する

## Raspberry Piに赤外線受光モジュールを接続する

赤外線信号を受信するため、Raspberry PiのGPIOに赤外線受光モジュールを接続します。使用した素子とその接続方法は以下の記事と同様です。

[格安スマートリモコンの作り方](https://qiita.com/takjg/items/e6b8af53421be54b62c9#%E8%B5%A4%E5%A4%96%E7%B7%9A%E3%82%92%E5%8F%97%E4%BF%A1%E3%81%99%E3%82%8B%E5%9B%9E%E8%B7%AF)

## pigpioをインストールする

赤外線信号を記録するためにpigpioを使用するため、以下のコマンドでインストールしデーモンを起動します。

```sh
$ sudo apt update
$ sudo apt install pigpio python-pigpio -y
$ sudo systemctl start pigpiod
```

## 赤外線信号送受信プログラムをダウンロードする

pigpioの作者さんが公開している赤外線信号送受信用のPythonスクリプトを以下のようにダウンロードします。

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

## 赤外線信号を記録する

以下のようにダウンロードしたスクリプトを使って、赤外線信号を受信し記録します。

```sh
$ python3 irrp.py -r -g18 -f codes --no-confirm --post 50 ac:cool27
```

それぞれのオプションの意味は以下のとおりです。

|オプション|意味|
|----|----|
|-r|受信した信号を記録するモード|
|-g18|GPIO18を使って信号を受信する|
|-f codes|受信した信号を出力するファイル名を指定する|
|--no-confirm|確認のために同じ信号を2回要求しない|
|--post 50|50ms無信号の場合に信号が終わったと判断する|
|ac:cool27|信号の識別子。わかりやすい名前をつける|

試しに冷房27度の信号を記録してみると、以下のようなちょっと長〜い結果が出力されます。この数字の並びは、「赤外線がONの時間, OFFの時間, ONの時間, OFFの時間,...」という意味です。

```json
{"ac:cool27": [3452, 1665, 449, 1271, 449, 1271, 449, 411, 449, 411, 449, 411, 449, 1271, 449, 411, 449, 411, 449, 1271, 449, 1271, 449, 411, 449, 1271, 449, 411, 449, 411, 449, 1271, 449, 1271, 449, 411, 449, 1271, 449, 1271, 449, 411, 449, 411, 449, 1271, 449, 411, 449, 411, 449, 1271, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 1271, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 1271, 449, 1271, 449, 411, 449, 411, 449, 411, 449, 1271, 449, 1271, 449, 411, 449, 1271, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 1271, 449, 1271, 449, 411, 449, 1271, 449, 1271, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 1271, 449, 411, 449, 411, 449, 1271, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 1271, 449, 1271, 449, 411, 449, 1271, 449, 411, 449, 1271, 449, 1271, 449, 13342, 3452, 1665, 449, 1271, 449, 1271, 449, 411, 449, 411, 449, 411, 449, 1271, 449, 411, 449, 411, 449, 1271, 449, 1271, 449, 411, 449, 1271, 449, 411, 449, 411, 449, 1271, 449, 1271, 449, 411, 449, 1271, 449, 1271, 449, 411, 449, 411, 449, 1271, 449, 411, 449, 411, 449, 1271, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 1271, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 1271, 449, 1271, 449, 411, 449, 411, 449, 411, 449, 1271, 449, 1271, 449, 411, 449, 1271, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 1271, 449, 1271, 449, 411, 449, 1271, 449, 1271, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 1271, 449, 411, 449, 411, 449, 1271, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 411, 449, 1271, 449, 1271, 449, 411, 449, 1271, 449, 411, 449, 1271, 449, 1271, 449]}
```

# 信号を解析する

## リモコン信号の規格を見分ける

以下の記事からわかる通り、日本で使われているリモコン信号の規格は「NECフォーマット」「家製協(AEHA)フォーマット」「SONYフォーマット」の主に3つです。SONYフォーマットはSONY製品でしか使われていないので、今回使われている可能性があるのは「NECフォーマット」か「家製協(AEHA)フォーマット」です。

[赤外線リモコンの通信フォーマット](http://elm-chan.org/docs/ir_format.html)

データ部での'0'と'1'の表し方は、以下の通りNECフォーマットと家製協(AEHA)フォーマットで共通です。

<img width="367" alt="スクリーンショット 2018-09-04 16.48.51.png" src="https://qiita-image-store.s3.amazonaws.com/0/131110/bcf942e1-9849-f885-8682-4d9888777854.png">

NECフォーマットと家製協(AEHA)フォーマットの違いは、フォーマットの開始を表し、1Tの長さを定義するリーダー部にあります。

NECフォーマットでは、ONが16T、OFFが8T続いた後、上記のデータ部が始まります。一方、家製協(AEHA)フォーマットでは、ONが8T、OFFが4T続いた後、上記のデータ部が始まります。

<img width="673" alt="スクリーンショット 2018-09-04 21.23.04.png" src="https://qiita-image-store.s3.amazonaws.com/0/131110/273b53ba-ec07-0b05-2a7a-e1060771ee31.png">

先程記録したリモコン信号の中盤〜終盤は「449, 411」や「449, 1271」という値が多く並んでいることがわかります。よって、430ぐらいが1Tの長さだと考えられます。
これを踏まえて最初の「3452, 1665」を見ると、概ね「8T, 4T」ぐらいになっていることがわかります。どうやら我が家のエアコンのリモコン信号は家製協(AEHA)フォーマットが使われているようです。

## 信号をデコードする

使われているフォーマットがわかったので、それに従って信号をデコードしてみます。Pythonでデコードを行うためのプログラムを書いてみました。なお、このプログラムでは複数回リーダー部が現れる可能性が考慮されています。また、ファイルと信号の指定方法は上記の記録用プログラムと同じ形式にしました。

```decode.py
import json
import argparse

p = argparse.ArgumentParser()
p.add_argument("-f", "--file", help="Filename", required=True)
p.add_argument("id", nargs="+", type=str, help="IR codes")

args = p.parse_args()

with open(args.file, "r") as f:
  records = json.load(f)

for arg in args.id:
  if arg in records:
    code = records[arg]

    t = 0.0
    data = []
    for i in range(0, len(code) - 1, 2):
      if t * 7.0 < code[i]:
        t = (code[i] + code[i + 1]) / 12.0
        data.append("")
      elif code[i + 1] < t * 2.0:
        data[-1] += "0"
      elif code[i + 1] < t * 6.0:
        data[-1] += "1"

    for d in data:
      print(format(int(d, 2), "x"))
```

先程記録した信号をこのプログラムでデコードすると、以下のような結果が得られます。

```sh
$ python3 decode.py -f codes ac:cool27
c4d36480000418d06c12000000000000006b
c4d36480000418d06c12000000000000006b
```

だいぶ人間が読めそうな形になってきましたね。どうやら、確認のため同じ信号を2回送信しているようです。

# 解析編に続く

エアコンのリモコン信号をデコードし、16進数表記するところまでできました。ちょっと長くなったので一旦ここで切って、次回にデコードした値のどの部分がモードや設定温度等を表しているのか解析します。

# 参考文献

- [Rasberry Pi 3でリモコン信号を解析する](http://kagemomiji.hateblo.jp/entry/2016/09/03/102809)
- [Raspberry Piで三菱のエアコンのリモコンをスキャン・解析・送信してみる
](https://qiita.com/Hiroki_Kawakami/items/37cdb412a4e511a58103)
- [格安スマートリモコンの作り方](https://qiita.com/takjg/items/e6b8af53421be54b62c9#%E8%B5%A4%E5%A4%96%E7%B7%9A%E3%82%92%E5%8F%97%E4%BF%A1%E3%81%99%E3%82%8B%E5%9B%9E%E8%B7%AF)
- [赤外線リモコンの通信フォーマット](http://elm-chan.org/docs/ir_format.html)