Arduinoを使った赤外線リモコンの作り方~信号解析編

2019年4月6日

目的

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進数表すと…なんてのは別にやる必要はありません。

次回は送信機を作ります。

作り方

Posted by T&H