RaspberryPiでスマートホーム 〜赤外線リモコンでエアコン操作 その1〜
ラズパイとUSB赤外線リモコンアドバンスでエアコンの操作に対応させたいと思います。エアコン対応はちょっと面倒くさいです。
エアコンのリモコンは全ての情報を毎回送信しています。どういうことかというと”温度をあげる”ボタンを押すと、例えば今の設定が25℃なら温度26℃の命令と同時に電源ON、冷房、風量1、風向きはスイング、タイマーセットなし…など全機能の情報が同時に送信されています。
なので単純に学習リモコンのように覚えるだけだと、組み合わせが膨大になります。
- 冷房、温度28℃、風量1、風向きスイング…
- 冷房、温度28℃、風量2、風向きスイング…
- 冷房、温度28℃、風量1、風向き固定…
- 冷房、温度28℃、風量2、風向き固定…
- …
風量2つと風向き変えるだけで4つ。温度を5種類変えたければさらに×5。暖房、冷房、ドライで×3。タイマー使うならさらに…
使う機能をグッと絞って少ないパターンを記憶するのも手ですが、今回は送信するコマンドを解析して自分でコマンドを生成するようにしました。
ちなみにエアコンはDAIKINです。リモコンには”ARC446A3”と書かれています。
USB赤外線リモコンアドバンスのコマンドの解析
USB赤外線リモコンアドバンスには有志の方がメーカー提供のWindows版をLinuxに移植したbto_advanced_USBIR_cmdというプログラムがあり、それを使ってリモコンのコマンドを学習したり、赤外線を送信したりしています。
コマンドを生成するためにはまずこのbto_advanced_USBIR_cmdが学習した内容のファイルがどういう形式なのか調べないといけません。bto_advanced_USBIR_cmdのソースを参考に進めていきます。
このファイルには0xで始まる16進数の数値がカンマ区切りのテキストで並んでいます。
0x00,0x13,0x00,0x10,0x00,0x12,0x00,0x11,0x00,0x12,0x00,0x11,0x00,0x12,0x00,0x11,0x00,0x12,0x00,0x11,0x00,0x12,0x03,0xc0,0x00,0x86,0x00,0x41,0x00,0x13,0x00,0x31,0x00,0x12,0x00,0x11,0x00,0x12,0x00,0x11,0x00,0x12,0x00,0x11,0x00,0x12,0x00,0x31,0x00,0x13,0x00,0x10,0x00,0x13,0x00,0x11,0x00,0x11,0x00,0x11,0x00,0x12,0x00,0x11,0x00,0x12,0x00,0x32,0x00,0x12,0x00,0x11,0x00,0x12,0x00,0x31,0x00,0x13,0x00,0x31,0x00,0x12,0x00,0x11,0x00,0x12,0x00,0x32,0x00,0x12,0x00,0x31,0x00,0x13,0x00,(省略)
ソースと照らし合わせながら見て分かったことは…
- 4バイトで1セット。
- その4バイトのうち、最初の2バイトで赤外線をONにする長さ、後ろの2バイトで赤外線をOFFにする長さを表している
ということでした。例えば最初の4バイト、”0x00,0x13,0x00,0x10”が意味するところは、まず上位2バイトと下位2バイトをまとめて0x0013と0x0010、それぞれを10進数に直すと19と16。つまりLEDのONが19、LEDのOFFが16。この19,16という数字は実際の長さを周期(38khzの場合は26μs)で割ったもののようです。
赤外線リモコンのフォーマットにはいろいろあるようですが、私が使っているDAIKINのエアコンは家製協フォーマットをベースにしているようです。
赤外線のオンとオフの時間の長さでビットの0か1かが決まります。さらにいうとオンの時間は常に一定でオフの時間が短いと0、長いと1になります。また0か1以外にLEADERコードやSTOPコードと呼ばれる特殊な信号もあります。
上記の情報を元にこのファイルをビットにして表示するプログラムを作りました。Python3です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
import sys BIT_0_START = 15 BIT_0_END = 20 BIT_1_START = 45 BIT_1_END = 55 def get_bits(data): bits = [] for i in range(0, len(data), 4): byte1 = int(data[i + 0], 16) byte2 = int(data[i + 1], 16) byte3 = int(data[i + 2], 16) byte4 = int(data[i + 3], 16) on = (byte1 << 8) | byte2 off = (byte3 << 8) | byte4 if (off >= BIT_1_START and off <= BIT_1_END): bit = "1" elif (off >= BIT_0_START and off <= BIT_0_END): bit = "0" else: bit = "?" hex_on = data[i] + data[i + 1][-2:] hex_off = data[i + 2] + data[i + 3][-2:] item = {"on":on, "off":off, "bit":bit, "hex_on":hex_on, "hex_off":hex_off} bits.append(item) return bits def dump(bits): hist = {} cnt = 0 for i in range(len(bits)): cnt += 1 hist[bits[i]["hex_on"]] = 1 if (not bits[i]["hex_on"] in hist) else hist[bits[i]["hex_on"]] + 1 hist[bits[i]["hex_off"]] = 1 if (not bits[i]["hex_off"] in hist) else hist[bits[i]["hex_off"]] + 1 print(format(cnt, "03") + " offset:" + format(i, "03") + " on:" + bits[i]["hex_on"] + "(" + str(bits[i]["on"]) + ") off:" + bits[i]["hex_off"] + "(" + str(bits[i]["off"]) + ") bit:" + str(bits[i]["bit"]) ) print("") for k, v in hist.items(): print(k + "(" + str(int(k, 16)) + "): " + str(v)) if __name__ == "__main__": data = sys.stdin.readline().split(",") bits = get_bits(data) dump(bits) |
dump_bits.pyという名前です。USB赤外線リモコンアドバンで記録したファイルを標準入力から流し込んで使います。
cat aircon_cold_28_wind0_swing.txt | python dump_bits.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
001 offset:000 on:0x0012(18) off:0x0011(17) bit:0 002 offset:001 on:0x0012(18) off:0x0011(17) bit:0 003 offset:002 on:0x0012(18) off:0x0010(16) bit:0 004 offset:003 on:0x0013(19) off:0x0010(16) bit:0 005 offset:004 on:0x0013(19) off:0x0010(16) bit:0 006 offset:005 on:0x0012(18) off:0x03c1(961) bit:? <--- STOP CODE 007 offset:006 on:0x0085(133) off:0x0042(66) bit:? <--- LEADER CODE 008 offset:007 on:0x0012(18) off:0x0032(50) bit:1 009 offset:008 on:0x0012(18) off:0x0011(17) bit:0 (略) 070 offset:069 on:0x0012(18) off:0x0031(49) bit:1 071 offset:070 on:0x0013(19) off:0x0031(49) bit:1 072 offset:071 on:0x0013(19) off:0x0522(1314) bit:? <--- STOP CODE 073 offset:072 on:0x0086(134) off:0x0041(65) bit:? <--- LEADER CODE 074 offset:073 on:0x0013(19) off:0x0031(49) bit:1 075 offset:074 on:0x0012(18) off:0x0011(17) bit:0 (略) 136 offset:135 on:0x0012(18) off:0x0031(49) bit:1 137 offset:136 on:0x0013(19) off:0x0010(16) bit:0 138 offset:137 on:0x0013(19) off:0x0522(1314) bit:? <--- STOP CODE 139 offset:138 on:0x0086(134) off:0x0041(65) bit:? <--- LEADER CODE 140 offset:139 on:0x0013(19) off:0x0031(49) bit:1 141 offset:140 on:0x0013(19) off:0x0010(16) bit:0 (略) 290 offset:289 on:0x0012(18) off:0x0032(50) bit:1 291 offset:290 on:0x0012(18) off:0x0011(17) bit:0 292 offset:291 on:0x0012(18) off:0x1e0d(7693) bit:? <--- STOP CODE . 0x0012(18): 198 0x0011(17): 128 0x0010(16): 86 0x0013(19): 89 0x03c1(961): 1 0x0085(133): 1 0x0042(66): 1 0x0032(50): 24 0x0031(49): 49 0x0522(1314): 2 0x0086(134): 2 0x0041(65): 2 0x1e0d(7693): 1 |
オンとオフの長さは多少ばらつきがありました。最後にそれぞれの数値が何回出現したか表示しています。なお0か1かはオフの長さで判断しますが、以下の基準を使ってます。
- 45〜55…長い
- 15〜20…短い
これは実際に記録したコマンドを何個か調べて、この範囲だったからという安直な理由です。
以下のサイトにDAIKINのコマンドの構成がありました。
最初は0が5回続いてSTOPコード、LEADERコードがきて64bitの後にSTOPコード…とサイトの説明通りになっています。LEADERコードは(多少の誤差はあるが)全て同じようです。STOPコードに関しては中2つ以外は誤差とは呼べないくらいの差があるので、3種類あるのでしょうか。
次はデータ部分を解析して見ます。
続く…