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ピンは適宜修正して下さい。

const int threshold = 120;

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(A0) < 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

出力される数字のうち、+は入力があった時間、-は入力がなかった時間です。一番最初の桁の大きいマイナスの数字は、前回の最後の入力からの時間ですので、その次のプラスの数字からがリモコンの信号です。

デコード

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な時間)の後にリーダ部分が再度現れますので、取得したデータを見ればわかるはずです。

なんとなくキレイにはなりましたが、まだわかりにくいです。ルールに則って、デジタルの0, 1に変換しましょう。といっても、単純にデータ部分の-1を0, -3を1に変換し、他の数字は無視すればOKです。

以下のようなスクリプトで簡単に変換できます。

dec = ""
for d in data.split("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"):
  if d == "-1":
    dec += "0"
  elif d == "-3":
    dec += "1"
print(dec)

この結果、上の例で示した信号は次のようなものでした。

1000000000001000000011000000101011110101000000001111111101000000101111110100001110111100

これで、リモコンの送信に必要な信号データは手に入りました。

これをさらに16進数表すと…なんてのは別にやる必要はありません。

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

作り方

Posted by T&H