Raspberry Piを使って、Apple HomeKit互換のHomebridgeを作ったのは、もう4年ほど前になります。作ってから停電以外では特に停止することもなく安定して稼働しています。夜電灯を消すのも布団に入ってから「Hey Siri 電気を消して」というだけでできますし、寒い冬の朝などは「Hey Siri エアコンをつけて」といえば、布団の中からエアコンをつけて部屋を暖める事ができます。もちろん、Apple Watch、iPhone、iPadからもコントロールすることが可能です。純正のHomeKitではないので、最初動かすまでは結構大変でしたが…
もちろん、所有している機器は、どれもHomeKit対応ではありません。では、HomeKit対応でない機器をどうやって制御しているかというと、HomeBridgeシステムから赤外線リモコンコードを出力して操作しています。最初は、Raspberry Pi のGPIOを直接操作してリモコン信号を送信するlircやpigpioも試してみましたが、サウンドボードとの相性が悪いのか安定して動作させることができず、ワンチップマイコンを外付して対応することにしました。リモコンコードを出力するためにSTM32F103を使いました。UARTまたはUSB経由でコマンドを送ってリモコンコードを出力します。リモコンコードのフォーマットは有名なNECフォーマット、家製協フォーマット、SONYフォーマット、三菱フォーマットなど様々なフォーマットが存在します。これらを個別に対応しても良いのですが、後から簡単にフォーマットを追加できるようにしました。Universal IR Blasterと名付けました。
いつもの通り、回路図も設計資料も残っていないのですが、少しでも記憶が残っている間に備忘録として残しておこうと思いました。なので、あやふやなところもありますし、間違っているところもあるでしょう。間違いを見つけたら随時修正して行きたいと思います。
Universal IR Bluster Protocol
- UARTの通信パラメータは、115200bps, 8bit, stop bit 1です。
USB CDCクラスに対応しているので、VCP(Virtual Com Port)を使うことも可能です。 - プロトコルという程のものでもありませんが、XML風の形式としました。あくまでも”風”です。いや、香り程度かな? 個人的にあまり好きではないので、JSONではありません…
大きく以下の3要素から成ります。
リモコンコードのフォーマットを設定するタグ<ir_parameter></ir_parameter>,
リモコンコードのデータを設定するタグ<ir_data></ir_data>,
リモコンコードを送信するタグ<ir_command></ir_command>で構成されます。 - <ir_parameter></ir_parameter>タグで、送信するフォーマット(パラメータ)を決めて、<ir_data></ir_data>タグで送信するリモコンコード(データ)、例えば、「電源ON」のリモコンコードを設定し、実際に送信するときに<ir_command></ir_command>タグを送るというシーケンスになります。
- パラメータ、データは、一度設定しておけば、電源が切られるまで保持します。データだけを更新することで、同じリモコンフォーマットで送信することができます。
- パラメータ、データ、コマンドは各々一行で送信します。デリミタはLFです。
リモコンコードのフォーマット設定
リモコンコードを構成する要素をパラメータとして設定します。
これは、家製協(AEHA)フォーマットを使用しているPanasonic照明用の一例です。以下を一行として送信します。
<ir_parameter>の例
1 2 3 4 5 6 7 8 9 10 |
<ir_parameter> <carrier><freq>38000</freq><duty>30</duty></carrier> <header><mark>3400</mark><space>1700</space></header> <zero><order>IR_ORDER_NORMAL</order><mark>425</mark><space>425</space></zero> <one><order>IR_ORDER_NORMAL</order><mark>425</mark><space>1275</space></one> <stopbit><order>IR_ORDER_NORMAL</order><mark>425</mark><space>0</space></stopbit> <repeatgap><mode>IR_GAP_ADD_ALL</mode><time>75175</time></repeatgap> <bitorder>IR_BIT_ORDER_LSB_FIRST</bitorder> <sequence>h88888pg</sequence> </ir_parameter> |
- リモコンコードの構造やフォーマットについては、昔の記事をご覧ください。
- <carrier><freq>38000</freq><duty>30</duty></carrier>
リモコン信号のキャリア周波数、Dutyの設定です。
<freq>38000</freq> は、キャリア周波数 38000[Hz]です。
<duty>30</duty>キャリアのDuty 30%です。 - <header><mark>3400</mark><space>1700</space></header>
<mark>3400</mark> は、ヘッダーのパルス(H)期間 が3400[us]、
<space>1700</space>は、ヘッダーの無信号(L)期間が1700[us]です。 - <zero><order>IR_OREDER_NORMAL</order><mark>425</mark><space>425</space></zero>
コード”0″のフォーマットを設定します。
<order>IR_OREDER_NORMAL</order>は、<mark>(H期間)→<space>(L期間)の順序で出力されます。逆に<space>(L期間)→<mark>(H期間)にする場合は、IR_OREDER_REVERSEを設定します。
<mark>425</mark>は、H期間が425[us]です。<space>425</space>は、L期間が425[us]です。
この例では、”0″の場合、H期間とL期間が同じということですね。 - <one><order>IR_OREDER_NORMAL</order><mark>425</mark><space>1275</space></one>
コード”1″のフォーマットを設定します。要素は、<zero></zero>の時と同じです。
この例では、”1″の場合、<mark>(H期間)が 425[us],<space>( L期間)が 1275 [us]になっています。Hに対してLの期間が3倍ですね。<order>にIR_ORDER_NORMALを設定していますので、<mark>→<space>の順序になります。 - <stopbit><order>IR_OREDER_NORMAL</order><mark>425</mark><space>0</space></stopbit>
データが終わったことを示すストップビットです。この例では、<mark>(H期間) 425[us]を出力します。
<space>には 0 を設定しているので、L期間はありません。 - <repeatgap><mode>IR_GAP_ADD_ALL</mode><time>75175</time></repeatgap>
次のリモコンコードを送信するまでの無信号期間です。これを守らないと、正しいリモコンコードと認識されないことがあります。
<mode>IR_GAP_ADD_ALL</mode>は、単純に所定の無信号期間を置きます。他に、IR_GAP_CONST_LENGHは、送信開始から次の送信開始までの期間を一定にします。IR_GAP_CONST_LENGH_FROM_DATAは、ヘッダーを除いたデータからの期間を一定します。
<time>75175</time>は、その期間に75175 [us]を設定しています。 - 例にはありませんが、データとデータの区切りにセパレータ(無信号期間)を挿入するフォーマットもありますので、<separator></separator>タグも用意しています。
<separator><order>IR_OREDER_NORMAL</order><mark>0</mark><space>4233</space></separator> - <sequence>h88888pg</sequence>
設定したパラメータを組み合わせて送信する順序(シーケンス)を設定します。
h: <header>タグで指定したヘッダー
8: 8bitデータ、この例では8bitデータを5個送信する
p: <stopbit>タグで指定したstop bit
g: <repeatgap>タグで指定した無信号期間(ギャップ)
この例では、ヘッダー、8ビットのデータ5個、ストップビット、ギャップの順で送出されます。
他にも以下の設定が可能です。
s: <separator>タグで指定した無信号期間(セパレータ)
r:<repeat>繰り返しの開始を指定します。要素で設定した<mark><space>も出力します。
e:<ext_repeatgap> ギャップをもう一つ設定することが可能です。ギャップ時間の開始ポイントはrで設定したところになります。
送信データの設定
実際に送信するデータを<ir_data></ir_data>タグで設定します。このタグは、設定するだけで、送信されません。
- <ir_parameter>タグの <sequence>要素で設定したデータ個数と同じ数のデータをセットします。
- 例では、<sequence>h88888pg</sequence>でしたので、88888の部分がデータになります。この例では、8ビットx5個のデータを表しています。1~8ビットを設定できます。
- <codes>の要素が順に送信されることになります。
- メーカコード(カスタムコード)や、チェックコード(パリティ)等もデータの一部として設定します。
- この例は、Panasonicの照明を点灯するコードです。
送信
実際にリモコン信号を送信します。
- <send>要素で送信回数を設定します。
- </ir_command>を受け取ると、<ir_parameter>,<ir_data>で設定したフォーマット、データに基づいたリモコン信号を<send>要素の回数繰り返し送信します。送信間隔は、<sequence>要素のg,eで決まります。
- <ir_parameter>,<ir_data>は最新のものが保持されます。
同じリモコンコードを再度送りたい場合は、<ir_command><send>5</send></ir_command>
だけでOKです。(送信回数は適宜変更してください) - 一般的に、受信機側では外乱によりリモコン信号が正常に受信できない場合を考慮し、複数回同じリモコン信号を受信したときのみ動作するなどの処理が入っていますので、複数回送信すのが良いと思います。
このように、<ir_command>の中でコードデータと、回数を一度に指定することもできます。
この例は、Panasonicの照明を消す(OFF)リモコンコードを5回繰り返し送信します。
その他の例
PCからPythonで送信するときの例をいくつか紹介します。
NECフォーマットの例
何を動作させるコードだったかは忘れました(笑)
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 |
import serial port = serial.Serial( port = "/dev/ttyS0", baudrate = 115200, parity = serial.PARITY_NONE, stopbits = serial.STOPBITS_ONE, bytesize = serial.EIGHTBITS, timeout = 3.0 ) #NEC FORMAT TEST command = "<ir_parameter>\n" command += "<carrier><freq>38000</freq><duty>30</duty></carrier>\n" command += "<header><mark>9000</mark><space>2250</space></header>\n" command += "<zero><order>IR_ORDER_NORMAL</order><mark>560</mark><space>560</space></zero>\n" command += "<one><order>IR_ORDER_NORMAL</order><mark>560</mark><space>1680</space></one>\n" command += "<separator><order>IR_ORDER_NORMAL</order><mark>0</mark><space>2250</space></separator>\n" command += "<stopbit><order>IR_ORDER_NORMAL</order><mark>560</mark><space>0</space></stopbit>\n" command += "<repeat><order>IR_ORDER_NORMAL</order><mark>300</mark><space>0</space></repeat>\n" command += "<repeatgap><mode>IR_GAP_CONST_LENGH</mode><time>108000</time></repeatgap>\n" command += "<ext_repeatgap><mode>IR_GAP_CONST_LENGH</mode><time>108000</time></ext_repeatgap>\n" command += "<bitorder>IR_BIT_ORDER_LSB_FIRST</bitorder>\n" command += "<sequence>hs8888grhpe</sequence>\n" command += "</ir_parameter>" command += "<ir_command><codes>0xA5,0x5A,0x48,0x7E</codes><send>5</send></ir_command>" port.write(command.encode('utf-8')) |
三菱電機エアコン
三菱電機製エアコン(霧ヶ峰)をON(?)するリモコンコードです。
このリモコンコードには、冷房/暖房、温度設定など色々な設定が含まれています。このコードは多分、暖房24℃だったと思います。長くて面倒くさかった…
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 |
import serial port = serial.Serial( port = "/dev/ttyS0", baudrate = 115200, parity = serial.PARITY_NONE, stopbits = serial.STOPBITS_ONE, bytesize = serial.EIGHTBITS, timeout = 3.0 ) #Mitsubishi Room Air Conditioner Power ON command = "<ir_parameter>\n" command += "<carrier><freq>38000</freq><duty>30</duty></carrier>\n" command += "<header><mark>3400</mark><space>1700</space></header>\n" command += "<zero><order>IR_ORDER_NORMAL</order><mark>425</mark><space>425</space></zero>\n" command += "<one><order>IR_ORDER_NORMAL</order><mark>425</mark><space>1275</space></one>\n" command += "<stopbit><order>IR_ORDER_NORMAL</order><mark>425</mark><space>0</space></stopbit>\n" command += "<repeatgap><mode>IR_GAP_CONST_ADD_ALL</mode><time>16000</time></repeatgap>\n" command += "<bitorder>IR_BIT_ORDER_LSB_FIRST</bitorder>\n" command += "<sequence>h888888888888888888pg</sequence>\n" command += "</ir_parameter>" command += "<ir_command><codes>0x23,0xCB,0x26,0x01,0x00,0x20,0x48,0x07,0xC0,0x42,0x00,0x00,0x00,0x00,0x94,0x40,0x40,0x9A</codes><send>2</send></ir_command>" port.write(command.encode('utf-8')) |
その他 C
Cでプログラムしたときのコードが見つかりました。とりあえず、いくつかのフォーマットを検討していたようですので、参考までに紹介します。
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 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
#ifndef _IR_PARAMETER_H_ #define _IR_PARAMETER_H_ //多分 三菱TV用 const char ir_parameter_mit_tv[] = "<ir_parameter>\ <carrier><freq>38000</freq><duty>30</duty></carrier>\ <header><mark>8447</mark><space>4223</space></header>\ <zero><order>IR_OREDER_NORMAL</order><mark>300</mark><space>920</space></zero>\ <one><order>IR_OREDER_NORMAL</order><mark>300</mark><space>2140</space></one>\ //<separator><order>IR_OREDER_NORMAL</order><mark>300</mark><space>2140</space></separator>\ <stopbit><order>IR_OREDER_NORMAL</order><mark>300</mark><space>0</space></stopbit>\ <repeatgap><mode>IR_GAP_CONST_LENGH</mode><time>53700</time></repeatgap>\ <bitorder>IR_BIT_ORDER_LSB_FIRST</bitorder>\ <sequence>88pg</sequence></ir_parameter>"; //多分 三菱レコーダー、TV内蔵BDレコーダーの操作にも使えるかも const char ir_parameter_mit_bdr[] = "<ir_parameter>\ <carrier><freq>38000</freq><duty>30</duty></carrier>\ <header><mark>8447</mark><space>4223</space></header>\ <zero><order>IR_ORDER_NORMAL</order><mark>517</mark><space>546</space></zero>\ <one><order>IR_ORDER_NORMAL</order><mark>517</mark><space>1581</space></one>\ <separator><order>IR_ORDER_NORMAL</order><mark>0</mark><space>4233</space></separator>\ <stopbit><order>IR_ORDER_NORMAL</order><mark>517</mark><space>0</space></stopbit>\ <repeatgap><mode>IR_GAP_CONST_LENGH</mode><time>63290</time></repeatgap>\ <bitorder>IR_BIT_ORDER_LSB_FIRST</bitorder>\ <sequence>h8ps8pg</sequence>\ </ir_parameter>"; //SONY BDレコーダー用 const char ir_parameter_sony_bdr[] = "<ir_parameter>\ <carrier><freq>40000</freq><duty>30</duty></carrier>\ <header><mark>2400</mark><space>600</space></header>\ <zero><order>IR_ORDER_NORMAL</order><mark>600</mark><space>600</space></zero>\ <one><order>IR_ORDER_NORMAL</order><mark>1200</mark><space>600</space></one>\ <stopbit><order>IR_ORDER_NORMAL</order><mark>300</mark><space>0</space></stopbit>\ <repeat><order>IR_ORDER_NORMAL</order><mark>300</mark><space>0</space></repeat>\ <repeatgap><mode>IR_GAP_CONST_LENGH</mode><time>45000</time></repeatgap>\ <bitorder>IR_BIT_ORDER_LSB_FIRST</bitorder>\ <sequence>h785g</sequence>\ </ir_parameter>"; //NEC const char ir_parameter_nec[] = "<ir_parameter>\ <carrier><freq>38000</freq><duty>30</duty></carrier>\ <header><mark>9000</mark><space>2250</space></header>\ <zero><order>IR_ORDER_NORMAL</order><mark>560</mark><space>560</space></zero>\ <one><order>IR_ORDER_NORMAL</order><mark>560</mark><space>1680</space></one>\ <separator><order>IR_ORDER_NORMAL</order><mark>0</mark><space>2250</space></separator>\ <stopbit><order>IR_ORDER_NORMAL</order><mark>560</mark><space>0</space></stopbit>\ <repeat><order>IR_ORDER_NORMAL</order><mark>300</mark><space>0</space></repeat>\ <repeatgap><mode>IR_GAP_CONST_LENGH</mode><time>108000</time></repeatgap>\ <ext_repeatgap><mode>IR_GAP_CONST_LENGH</mode><time>108000</time></ext_repeatgap>\ <bitorder>IR_BIT_ORDER_LSB_FIRST</bitorder>\ <sequence>hs8888grhpe</sequence>\ </ir_parameter>"; //家製協(AEHA)フォーマット データ5バイト、Panasonic照明用 const char ir_parameter_aeha05[] = "<ir_parameter>\ <carrier><freq>38000</freq><duty>30</duty></carrier>\ <header><mark>3400</mark><space>1700</space></header>\ <zero><order>IR_ORDER_NORMAL</order><mark>425</mark><space>425</space></zero>\ <one><order>IR_ORDER_NORMAL</order><mark>425</mark><space>1275</space></one>\ <stopbit><order>IR_ORDER_NORMAL</order><mark>425</mark><space>0</space></stopbit>\ <repeatgap><mode>IR_GAP_CONST_ADD_ALL</mode><time>75175</time></repeatgap>\ <bitorder>IR_BIT_ORDER_LSB_FIRST</bitorder>\ <sequence>h88888pg</sequence>\ </ir_parameter>"; //家製協(AEHA)フォーマット データ18バイト、三菱電機エアコン霧ヶ峰用 const char ir_parameter_aeha18[] = "<ir_parameter>\ <carrier><freq>38000</freq><duty>30</duty></carrier>\ <header><mark>3400</mark><space>1700</space></header>\ <zero><order>IR_ORDER_NORMAL</order><mark>425</mark><space>425</space></zero>\ <one><order>IR_ORDER_NORMAL</order><mark>425</mark><space>1275</space></one>\ <stopbit><order>IR_ORDER_NORMAL</order><mark>425</mark><space>0</space></stopbit>\ <repeatgap><mode>IR_GAP_CONST_ADD_ALL</mode><time>16000</time></repeatgap>\ <bitorder>IR_BIT_ORDER_LSB_FIRST</bitorder>\ <sequence>h888888888888888888pg</sequence></ir_parameter>"; #endif |
Sharpフォーマット、RC5フォーマット、RC6フォーマット等、連続送信の際にビットを反転させるフォーマットには対応できていません。