Arduinoを使った赤外線リモコンの作り方~信号解析編
目的
Arduinoを使ってリモコンを作ります。
最終的には、スマートスピーカーと連携させて、
第一段として、Arduinoでリモコンの信号を読み取ってみます。
リモコンの信号について
一般的なリモコンは、赤外線LEDを使って不可視光による光の通信を行います。LEDを決まったルールに則って点滅させると、受信側がその意味を解読(デコード)し、その命令に従って機械を動かします。
ガラパゴス化が大好きな日本では、LEDの光らせ方のルールも複数あります。海外製の家電だと、さらに違う方式の可能性があります。ここでは、国内家電によく使われる家電協(AEHA)方式とNEC方式について必要最低限な情報を押さえておきます。ちなみに、この他にはSony方式なんかがあります。
共通事項
LEDが点滅することによって送る信号は、リーダー、データ、ストップビットに分けることが出来ます。(本当はもう少し複雑です)
リーダーは、LEDのONとOFFを組み合わせ、これからデータが来ますよという合図を送ります。
データ部分では、LEDのONとOFFを組み合わせて、デジタルの「0」と「1」を表現します。単位時間をTとすると、「0」は1TだけONした直後に1TだけOFF、「1」は1TだけONにした直後に3TだけOFFで表します。
ストップビットは、データを全部送った後に、1TだけONにして、これが最後ですよという合図を送ります。
実際には、LEDの発光は38kHzで変調されていますが、Arduinoで信号を受信する分には無視できるので割愛。信号を送信するときに改めて説明します。
家電協(AEHA)方式
単位時間 T = 425μsを使います。リーダはONが8T (3400μs), OFFが4T (1700μs)です。
NEC方式
単位時間 T = 562μsを使います。リーダはONが8T (8992μs), OFFが4T (4496μs)です。
Arduinoで信号を解析する
Arduinoを使ってリモコンの送信機を作る前に、送信する信号を読み取りましょう。
基本的な流れは、Arduinoにつけたセンサーで、リモコンから発せられる赤外線を受光し、赤外線のON, OFFのタイミングを調べ、一連のタイミングから信号を解読します。
必要なもの
- Arduino (UNO等)
- 赤外線LED (λ = 950nm付近) 1個
巷では、リモコンの信号解析をするにあたって赤外線センサーや赤外線モジュールが勧められていますが、リモコンの送信機が目的なので、センサーは後々不要になります。と言うより、端から用意する必要はありません。赤外線LEDをセンサーとして使用します。
LEDは適切な電圧をかけると光を出しますが、実は光を受けると電圧が生じます。ArduinoにはADCがありますので、電圧を読み取り、適当にしきい値を設けて、受光を判断します。
LEDはそのまま送信機に流用することを考えると、高輝度広角のものが良いです。私は10個入り100円で手に入れたYSL-R531FR1C-F1を使っています。
回路
Arduinoのアナログピン(上の図ではA0)に赤外線LEDのアノード(足の長い方)を、GNDにカソードを直接挿します。以上です。
ただし、ピンから電流を出力(digitalWriteでHIGHに)しないで下さい。LEDが焼き切れます。
スケッチ
単純に、赤外線が入力された時間、されなかった時間をシリアル通信で出力してみます。thresholdとanalogReadの入力ピン(READ_PIN)は適宜修正して下さい。(2020/6/27 微修正しました。)
const int threshold = 120; const int READ_PIN = 0; void setup(){ Serial.begin(115200UL); pinMode(READ_PIN,INPUT); Serial.println("Ready to receive"); } unsigned long now = micros(); unsigned long last = micros(); int state = -1; int b; void loop() { if (analogRead(READ_PIN) < threshold) { b = -1; } else { b = 1; } if (state * b < 0) { now = micros(); if (state == -1) { Serial.print("-"); } else { Serial.print("+"); } Serial.print((now-last), DEC); Serial.print("\t"); last = now; state *= -1; } }
一例として、こんなものが出力されます。
-5443392 3360 -1680 440 -1304 440 -408 424 -416 424 -416 416 -408 416 -416 536 -408 416 -408 424 -416 424 -416 416 -408 424 -416 528 -1192 440 -416 436 -412 424 -416 536 -400 416 -408 416 -400 536 -280 536 -1176 552 -1184 440 -408 424 -416 536 -288 536 -400 420 -404 420 -412 424 -1312 448 -408 424 -1208 552 -400 400 -1296 448 -1192 440 -1312 440 -1192 448 -408 536 -1184 440 -408 424 -1312 448 -408 424 -416 424 -424 424 -408 528 -288 536 -400 424 -408 424 -416 424 -1320 440 -1192 448 -1304 440 -1208 440 -1304 448 -1208 440 -1320 440 -1192 552 -392 400 -1296 440 -408 424 -416 424 -416 416 -408 528 -288 544 -408 420 -1196 560 -280 528 -1176 552 -1184 440 -1312 448 -1200 440 -1320 440 -1192 552 -392 400 -1296 440 -408 424 -416 424 -416 416 -408 416 -1312 440 -1192 560 -1176 440 -416 424 -1312 440 -1192 448 -1312 440 -1208 552 -392 408 -400 416
出力される数字のうち、正の数は入力があった時間、負の数は入力がなかった時間です。一番最初の桁の大きい負の数字は、前回の最後の入力からの時間ですので、その次の正の数字からがリモコンの信号です。(2020/10/5 微修正)
デコード
Arduinoで読み取ったON, OFFの時間は、本来はキレイなTの整数倍でいてほしいですが、結構バラツキがあります。とは言え、AEHA方式であれば、リーダのONの長さは3400μs、NEC方式であれば約9000μsですので、最初のONの時間を見ればどちらか判断できます。それぞれ、単位時間T = 425μs, 562μsですので、一つ一つのON, OFFの時間をTで割って丸めると、T何個分のON, OFFかわかります。
表計算ソフト(エクセル等)を使えば、一連のタイミングを一つずつセルに割り振った後に「=ROUND(対象のセル, 425)」みたいに計算すると一発で計算出来ます。
上の例で示した信号は、次のようになりました。
8 -4 1 -3 1 -1 1 -1 1 -1 1 -1 1 -1 1 -1 1 -1 1 -1 1 -1 1 -1 1 -1 1 -3 1 -1 1 -1 1 -1 1 -1 1 -1 1 -1 1 -1 1 -3 1 -3 1 -1 1 -1 1 -1 1 -1 1 -1 1 -1 1 -3 1 -1 1 -3 1 -1 1 -3 1 -3 1 -3 1 -3 1 -1 1 -3 1 -1 1 -3 1 -1 1 -1 1 -1 1 -1 1 -1 1 -1 1 -1 1 -1 1 -3 1 -3 1 -3 1 -3 1 -3 1 -3 1 -3 1 -3 1 -1 1 -3 1 -1 1 -1 1 -1 1 -1 1 -1 1 -1 1 -3 1 -1 1 -3 1 -3 1 -3 1 -3 1 -3 1 -3 1 -1 1 -3 1 -1 1 -1 1 -1 1 -1 1 -3 1 -3 1 -3 1 -1 1 -3 1 -3 1 -3 1 -3 1 -1 1 -1 1
注意1:ここで、データ部分に+1, -1, -3以外があるようだと、受信がうまく出来ていません。何回か試してみてください。
注意2:リモコンによっては、少し間を開けて同じ信号を何度か繰り返します。桁の大きいマイナスの値(LEDが長くOFFな時間)の後にリーダ部分が再度現れますので、取得したデータを見ればわかるはずです。
注意3:AEHA方式でもT=425ではない製品(SHARP製 LEDシーリングライト)がありました。とりあえずT=425で計算しても途中で端数を丸めてしまうので問題ありませんが、送信時は製品に合った値を使わないと上手く動作しませんので注意が必要です。シリアル通信で得られたデータの、正の値の平均値を取れば、Tが求まります。(2020/10/5 追記)
なんとなくキレイにはなりましたが、まだわかりにくいです。ルールに則って、デジタルの0, 1に変換しましょう。といっても、単純にデータ部分の-1を0, -3を1に変換し、他の数字は無視すればOKです。
適当なスクリプトでいいのですが、例えばPythonでは以下のようなスクリプトを用意して、dataに上記の数値をコピペすると簡単に変換できます。(2020/6/27 微修正しました)
dec = "" data = "8 -4 1 -3 1 -1 1 -1 1 -1 1 -1 1 -1 1 -1 1 -1 1 -1 1 -1 1 -1 1 -1 1 -3 1 -1 1 -1 1 -1 1 -1 1 -1 1 -1 1 -1 1 -3 1 -3 1 -1 1 -1 1 -1 1 -1 1 -1 1 -1 1 -3 1 -1 1 -3 1 -1 1 -3 1 -3 1 -3 1 -3 1 -1 1 -3 1 -1 1 -3 1 -1 1 -1 1 -1 1 -1 1 -1 1 -1 1 -1 1 -1 1 -3 1 -3 1 -3 1 -3 1 -3 1 -3 1 -3 1 -3 1 -1 1 -3 1 -1 1 -1 1 -1 1 -1 1 -1 1 -1 1 -3 1 -1 1 -3 1 -3 1 -3 1 -3 1 -3 1 -3 1 -1 1 -3 1 -1 1 -1 1 -1 1 -1 1 -3 1 -3 1 -3 1 -1 1 -3 1 -3 1 -3 1 -3 1 -1 1 -1 1" for d in data.split(): if d == "-1": dec += "0" elif d == "-3": dec += "1" print(dec)
この結果、上の例で示した信号は次のようなものでした。
1000000000001000000011000000101011110101000000001111111101000000101111110100001110111100
これで、リモコンの送信に必要な信号データは手に入りました。
これをさらに16進数表すと…なんてのは別にやる必要はありません。
次回は送信機を作ります。