Arduinoで学習リモコン

Aruduino Duemilanove 328買ってみた。

Arduino (アルデュイーノ)とは、AVRマイコンを搭載しデジタル/アナログ入出力を備えたイタリア発祥のマイコンボードです。C++ライクな言語での開発環境が整備されていて、非常に簡単に開発が出来るのが特徴。いろんなセンサをPCに繋ぐインターフェースとしても使いやすいし、もちろんスタンドアローンでもいろいろこなしてくれます。

中でも Aruduino Duemilanove 328 (読み方。イタリア語かっこいい)は、USB 1本でPC(Mac, Windows, Linux)と接続するだけで、すぐに開発が始められる簡単さが特徴です。Arduinoは「オープンソースハードウェア」であり、設計情報は全て公開されているため、自分で組み立てることも出来ます。が、まずは製品を買うのが簡単だと思って、Amazonのスイッチサイエンス社のを買ってみました。

追記

現在は、Duemilanoveの後継機のArduino Unoが発売されているので、そちらを購入しましょう。初心者の方は解説本やブレッドボードやタクトスイッチなどが付属したArduinoをはじめようキットも良いでしょう。


開発環境は公式サイトのガイドに従って、USB-シリアルのドライバとArduino IDEの最新版をセットアップすればOK。セットアップが終わったら、USBにArduinoを接続して、まずはチュートリアルを眺めながらLED点滅させたりして遊んでみれば、楽しめます。

リモコン作ってみる

以前、PCから制御できる赤外線学習リモコンをPICとアセンブラで作ってみたのですが、似たようなものをArduinoで作ってみました。
PIC/アセンブラのときは何日かかかってるはずですが、Arduinoならパーツさえあればわずか数時間で出来てしまう程度の簡単さです。

必要なモノ
パーツ 購入先
Arduino Duemilanove 328 Amazon / スイッチサイエンスで購入
赤外線リモコン受信モジュール 秋月で購入。
赤外線LED 秋月で購入。100個もいらないのだが。
配線

こんなかんじ。

DIGITAL 13pinとGNDに赤外線LEDを、DIGITAL 8pinに赤外線受光モジュールを繋ぐだけです。

そうしたら、USBポートでPCに接続し、IDEを起動します。IDEのToolsメニューからBoard→Arduino Duemilanove 328, Serial → /dev/cu.usbserial.A7006SXqを選んでおきましょう。

動かしてみる

本エントリ末尾のコードをIDEにコピペし、Upload to I/O Boardボタンをクリックすると、Arduinoにプログラムが転送され、動作し始めます。
Serial Monitorボタンを押し、通信速度として 57600 baud を選ぶと、ArduinoとUSB経由で通信が始まります。
Arduino IDE(シリアル通信中)

この状態で、赤外線リモコンを受光機に向けてボタンを押すと、「293459 930 459 64 161 62 57 52 64 47 67 50 174 64 ...」といった具合で信号の内容が表示されます。この数字は、10μs単位でリモコン信号のON/OFF状態が続いた時間を交互に表しています。
上の例ですと、最初の数字は無通信時間で、信号ではなくゴミですが、その後は、例えば「930 459」なら9300μs ON、4590μs OFF、を意味しています。


こんどはこの内容をArduinoから送信してみましょう。先ほどの数字をエディタなどにコピーし、最初の数字を削除、先頭に半角スペース(送信開始の意味)を追加します。最後に、「 0 」を前後にスペースを入れて追加します。これをIDEの欄にペーストし、Sendボタンを押すと、リモコン信号が送信されます!

長い信号だとシリアル通信が追いつかず途中で切れてしまう場合があるようなので、数回に分けて少しずつSendしてください。またエアコン等の複雑な信号(512個以上ON/OFFが続く信号)には対応できない場合があります。


実用的にするには、本体をケースに入れたり、適当なGUIなどを入れて使いやすくする必要がありますね。

コード

以下の通り。テキストで信号の内容をシリアル受信できるようにしてある分、ちょっと長い。

int ir_in = 8;
int ir_out = 13;
unsigned int data[512] = {};

int last = 0;
unsigned long us = micros();

// セットアップ
void setup() {
  Serial.begin(57600);        // シリアル通信速度の設定
  pinMode(ir_in, INPUT);  // 入出力ピンの設定
  pinMode(ir_out, OUTPUT);
}

// dataからリモコン信号を送信
void sendSignal() {
  for (int cnt = 0; cnt < 512; cnt++) {
    unsigned long len = data[cnt]*10;  // dataは10us単位でON/OFF時間を記録している
    if (len == 0) break;      // 0なら終端。
    unsigned long us = micros();
    do {
      digitalWrite(ir_out, 1 - (cnt&1)); // iが偶数なら赤外線ON、奇数なら0のOFFのまま
      delayMicroseconds(8);  // キャリア周波数38kHzでON/OFFするよう時間調整
      digitalWrite(ir_out, 0);
      delayMicroseconds(7);
    } while (long(us + len - micros()) > 0); // 送信時間に達するまでループ
  }
  Serial.print("OK\n");
}

// シリアルからの受信をチェック
void checkSerial() {
  if (Serial.available() > 0) {
    if (Serial.read() == ' ') {  // スペース受信で入力開始
      Serial.print(">");
      unsigned int x = 0;
      int i = 0;
      int len = 0;
      while (1) {
        while (Serial.available() == 0) {} // 次のバイトが来るまで無限ループで待つ
        int a = Serial.read();
        if (a >= '0' && a <= '9') {    // 0〜9を受信なら
          len++;
          x *= 10;
          x += a - '0';
        } else if (len) {    // 数値を受信済み、かつ数値以外が来たら
          data[i++] = x; // 受信した数値をdataに記憶
          Serial.print(i);
          Serial.print(":");
          Serial.print(x);
          Serial.print("\t");
          if (x == 0 || i >= 512) { // 0受信で赤外線送信開始
            sendSignal();
            break;
          }
          x = 0;
          len = 0;
        }
      }
    }
  }
}

void loop() {
  unsigned int val;
  unsigned int cnt = 0;
  
  // Wait for incoming IR signal
  while ((val = digitalRead(ir_in)) == last) {  // パルスが切り替わるまで待機
    if (++cnt >= 30000) {  // 30000回ループで信号が終了したとみなす
      if (cnt == 30000)  
        Serial.print("\n");
      else
        checkSerial();   // シリアル通信が来ているかチェック
      cnt = 30000;
    }
  }

  unsigned long us2 = micros();
  Serial.print((us2-us)/10, DEC);   // 赤外線のON/OFFが続いた時間を10us単位で送信
  Serial.print(" ");
  last = val;
  us = us2;
}