Arduinoで遊ぶページ

Arduinoで遊んだ結果を残すページです。
garretlab
赤外線リモコンコード解析(NECフォーマット)

概要

赤外線センサを利用して、NECフォーマットの赤外線リモコンのコードを表示するプログラムを作成します。

PARA LIGHT ELECTRONICS社のPL-IRM2161-C438という赤外線センサを使います。

作るもの

赤外線リモコンが出しているコードを表示するプログラムを作成します。

赤外線リモコンでよく使われるデータフォーマットには、NECフォーマット、家電協フォーマット、SONYフォーマットの3種類があるようです。家にあるDVDレコーダはは東芝製で、このレコーダは、NECフォーマットを採用しているようです。この実験では、NECフォーマットのリモコンコードを読み取っていきます。

データフォーマット

NECフォーマットの概要は以下の通りです。詳細は、ルネサス エレクトロニクスのページを参照してください。

リーダコードは、9msのon、その後、4.5msのoffです。

カスタムコードとデータコードは、1と0との組み合わせです。

1は、0.56ミリ秒のonの後、1.69ミリ秒のoff(全体では、2.25ミリ秒)、0は、0.56ミリ秒のonの後、0.565ミリ秒のoff)全体では1.125ミリ秒)であらわされます。また、カスタムコードは、16ビット、データコードは8ビットです。それぞれのバイトを、下位ビットから送信していきます。

ストップビットについては、参照したページには明確な定義は見つけられませんでしたが、0.56ミリ秒のonの後、offになるのではと思います。

また、リピートコードというのもあります。これは、リモコンのキーを押し続けた時に出るコードのようで、リピートコードの後に、すぐに、ストップビットが来ます。

リピートコードは、9msのon、その後、2.25msのoffです。

余談ですが、テレビのリモコンボタンを一度だけ押したところ、そのボタンが押し続けられるような現象が発生しました。音量を一段階上げたつもりがどんどん上がり続け、チャンネルを一つ上げるとどんどんチャンネルが上がり続ける…リモコンが壊れたのかと思い、リモコンの電池を抜いても事態は変わらず、部屋を見渡してみると、別のリモコンの上に本が乗っていました。こちらのリモコンがリピートコードを出し続けて、別のリモコンで出したコードを繰り返し実行していたようです。

今までのことをまとめると、上記の数値と少し異なりますが、9/16ミリ秒(=0.5625ミリ秒)を1単位とすると、おおよそ以下のように表せます。

リーダコード
16単位のon、8単位のoff
データの1
1単位のon、3単位のoff
データの0
1単位のon、1単位のoff

このため、1回のリモコンデータの送信では、onが34回、offが34回となります。

プログラム

今回作成するプログラムは以下の通りです。

  • リーダコードの、onの時間とoffの時間を表示する。
  • カスタムコードのon/offをそれぞれ、0と1で表示するとともに、16進数でも表示します。
  • データコード・データコード(反転)についても、カスタムコード同様、on/offをそれぞれ、0と1で表示するともに、16進数でも表示します。
  • 表示先はシリアルコンソールとします。

用意するもの

以下のものを用意します。
  • Arduino
  • USBケーブル
  • PC
  • 赤外線センサ(PARA LIGHT ELECTRONICS社のPL-IRM2161-C438)
  • ブレッドボード
  • ジャンパーワイヤ

利用機器

今回利用した赤外線センサ(PARA LIGHT ELECTRONICS社のPL-IRM2161-C438)は、赤外線リモコンで利用される38kHzのキャリアで変調した赤外線を受信し、onのときにLOW、offのときにHIGHを出力します。

ノイズが大きい場所で利用する際は抵抗・コンデンサを接続してノイズの影響を抑えたほうがいいと思いますが、今回は特別なことは行わず、赤外線センサだけを接続しました。

基本的な考え方

状況把握

1回のループで、リモコンのコード1回分(34回のonと34回のoff)を配列に記録します。ただし、リピートコードだった場合は、2回のonと2回のoffだけを記録します。

処理決定

特にありません。

機器操作

読み取ったコードを解析し、リーダコードは、onとoffのそれぞれの時間、それ以外は、コードに変換し、1バイトずつ2進数と16進数とで、シリアルコンソールに表示します。

設計

ハードウェアの設計

赤外線センサをArduinoに接続します。

赤外線センサの出力は、デジタルの2番ピンに接続しました。他のピンに接続する場合は、プログラムを変更してください。

プログラムの設計

赤外線コードの読み取り

1回のloop()関数の呼び出しで、以下を行います。

  1. 赤外線センサの出力がLOW(=on)になるまで待つ(=HIGHの間は待つ)。
  2. 以降は、赤外線センサの出力がonになった時とoffになった時の時刻を、配列(timeという名前にしました)に記録していく。
  3. 通常は全部で68回読み取りを繰り返す。ただし、カスタムコードを読み取った後(4回目の記録を行ったとき)に、リーダーコードのoffの時間を調べて、3000ミリ秒以下だったら、読み取りは終了する。

時刻は、micros()を使って、マイクロ秒単位で記録していきます。

上記のセンサの出力がonになったときや、offになったときというのは、デジタルピンの入力を読み以前の状態と同じ状態のときは入力を再度読むというビジーウエイトを使います。マルチタスクの場合はビジーウエイトは勧められませんが、今回のような場合は他に動かしているプログラムもないので問題はないでしょう。pulseIn()を使うほうが簡単かもしれません。

ループが終わった後は、配列には以下のようなデータが入っています。

配列 説明 備考
time[0] リーダーコードの開始時刻
time[1] ON→OFFになった時刻 time[2]-time[1]が4.5msだったらリーダーコード、2.25msだったらリピートコード
time[2] カスタムコード(1バイト目)の開始時刻
time[18] カスタムコード(2バイト目)の開始時刻
time[34] データコードの開始時刻 データコードの1ビット目がonかoffかは、time[36]-time[35]の値によってきまる。
time[50] データコード(反転)の開始時刻 データコード(反転)の1ビット目がonかoffかは、time[52]-time[51]の値によってきまる。
time[66] ストップビットの開始時刻
time[67] ストップビットの終了時刻 onがoffになった時刻。

なお、本来であれば、リーダコードの正当性をもう少し真面目に確認したほうがいいですが、実験目的なので今回はそこまでは考慮しないこととしました。

コードの出力

読み取ったコードを人間が読める形に変換して出力します。

  1. リーダーコードは、onになっていた時間と、offになっていた時間をそれぞれ、ミリ秒単位で表示します。
  2. カスタムコードは2バイトを1バイトずつにわけて表示、データコードとデータコード(反転)は、それぞれ1バイトを表示します。
  3. 1バイトを表示する際は、ビット列と16進表記の2種類を表示します。ビット列については受信した順序で表示し、16進表記は、データの値として表示します。下位ビットから受信しているため、表示したビット列は左が下位ビット、右が上位ビットとなること注意してください。

スケッチ

#define COUNT_NUM 68

const int pin = 2;              /* 赤外線センサの出力を接続するデジタルピン */
unsigned long time[COUNT_NUM];  /* 時刻を格納する配列 */

void setup () {
  pinMode(pin, INPUT);
  Serial.begin(9600);
}

void loop () {
  int repeat = 0;
  int mode; /* 読み取るデータを決める */
  char str[64];
  
  /* 68回データを読み取る */
  mode = HIGH; /* 最初はHIGH */
  for (int i = 0; i < COUNT_NUM; i++) {
    while(digitalRead(pin) == mode) { /* 状態が変わるまで待つ */
        ;
    }
    time[i] = micros(); /* 時刻を記録する */
    
    if (mode == HIGH) { /* onとoffが交互に来るので、待つべき状態を変える */
      mode = LOW;
    } else {
      mode = HIGH;
    }
    
    /* リーダコードのoffが2.25msだと次にストップビットが来て終了 */
    /* 3000ms以下かどうかで判定 */
    if ((i == 3) && ((time[2] - time[1])) < 3000)  { 
      repeat = 1;
      break;
    }
  }
  
  /* データの出力 */
  Serial.print("--------- BEGIN ---------\n");
  
  sprintf(str, "Leader:\n  HIGH %dms\n", (time[1] - time[0]));
  Serial.print(str);
  sprintf(str, "  LOW  %4dms\n\n", (time[2] - time[1]));
  Serial.print(str);
  
  if(repeat) {
    Serial.println("Repeat");
  } else {
    Serial.println("Custom Code:");
    print_data(2);
    print_data(18);

    Serial.println("");
  
    Serial.println("Data:");
    print_data(34);
    print_data(50);
    
    Serial.print("---------  END  ---------\n\n");
  }
}

void print_data(int start) {
  int code = 0;
  char str[32];
  
  sprintf(str, "  ");
  for (int i = 0; i < 8; i++) {
    if ((time[2 * i + start + 2] - time[2 * i + start]) > 1500) {
      code |= 1 << i;
      sprintf(&str[2 * (i + 1)], "1 ");
    } else {
      sprintf(&str[2 * (i + 1)], "0 ");
    }
  }
  
  sprintf(&str[17], ", %02x", code);
  Serial.println(str);
}

組立

赤外線センサに5Vの電源を接続します。また、データ出力端子をArduinoの2番ピンに接続します。他のピンに接続するときは、スケッチ中の pin に代入している数字を変更してください。

動作している様子

以下に動作している様子を示します。リモコンの0、1、2を順に押した時の様子です。

カスタムコードが、45 bc、データコードがそれぞれ、00、01、02であることがわかります。また、データコードのビットを反転したものがデータコード(反転)に入っている様子もわかります。

その他

データが1のときもデータが0のときも、リモコン信号のonの長さは同じなので、データが0か1かを知るだけなら、実は、デジタルピンがHIGHからLOWになる部分だけを読めば十分です。これだけであれば、割り込みを使って簡単に実現できますが、今回は、リーダ部分のonとoffの長さを表示したかったので、すべての信号を記録しました。

赤外線リモコンを操作する実験のページはこちらを参照してください。

バージョン

Arduino 0022



メニューを表示するためにJavaScriptを有効にしてください。

Arduinoで遊ぶページ
Copyright © 2011,2012 garretlab all rights reserved.
inserted by FC2 system