Hatena::ブログ(Diary)

Okiraku Programming RSSフィード Twitter

2009-04-19 Arduinoで学習リモコン

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。セットアップが終わったら、USBArduinoを接続して、まずはチュートリアルを眺めながらLED点滅させたりして遊んでみれば、楽しめます。


リモコン作ってみる

以前、PCから制御できる赤外線学習リモコンをPICとアセンブラで作ってみたのですが、似たようなものをArduinoで作ってみました。

PIC/アセンブラのときは何日かかかってるはずですが、Arduinoならパーツさえあればわずか数時間で出来てしまう程度の簡単さです。

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

こんなかんじ。

f:id:NeoCat:20090420001600:image:w360

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 を選ぶと、ArduinoUSB経由で通信が始まります。

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;
}

parasporospaparasporospa 2013/05/22 00:12 はじめまして、こちらのコードをそのまま使わせていただいています。ありがとうございます。
ただ電子工作ど素人なもので、わからないところがあります。もしよろしければ、お答えいただければ幸いです。

LEDに抵抗をはさんでいませんが、38kHzのパルスだから必要ないということなのでしょうか?
1/38kHz = 26.3μsなので、delayMicroseconds()は13ずつが正しいと思うのですが、調整して8と7にしてあるのでしょうか?実際には受光機によって反応がいい周波数は多少異なるそうですが…
それと、私は照明のリモコンでやっているのですが、赤外線が弱すぎるのか指向性のせいなのか、受光機の真下50cmが限界です(トランジスタで増幅しても)。そんなものなのでしょうか?

NeoCatNeoCat 2013/05/22 09:53 本当は電流制限抵抗はあった方が良いのですが、ArduinoのI/Oピンから流れる電流はMAX40mA程度なので、簡単に実験するときは省いてしまうことも多いです。もちろんトランジスタを入れるような場合は必須でしょう。
delayについては他の処理時間の分を引く必要があります。8/7にしたのはオシロスコープで38kHzに近づくように調整した結果です。この辺りはIDEのバージョン等によっても変わってしまうかもしれません。
delayの数値をあわせ込むほかに、PWMを使う方法もあり、これなら正確に38kHzが作れます(ただし使えるピンが制限されます)。
ご参考までに、こちらの記事ではPWMを使って40kHzちょうどを作っています。→ http://d.hatena.ne.jp/NeoCat/20110328/1301256560

信号の到達距離は赤外線LEDの種類によっても多少変わるようですね。見えないので分かりにくいですが、あまり電流を流しすぎたりするとLEDがダメージを受けて輝度が下がることもあるようです。

NeoCatNeoCat 2013/05/22 09:55 ↑ご存知かもしれませんが、携帯電話などのカメラを通して見ると、赤外線LEDが発光しているのを確認できますので、簡単に輝度の比較がしたいときなどに使えます。

parasporospaparasporospa 2013/05/22 16:43 丁寧にありがとうございます。
whileの条件判定に時間がかかりそうな分、2個目のdelayMicrosecondsの時間を短くしないといけないんですね。
ATmega328は16MHzなので、1命令1クロックとすれば、16命令で1μsかかると。
デューティ比が1/3ということもわかっていなかったのですが、結果としてHIGHが8.8μs、LOWが17.5μs続くのが望ましいんですよね。

PWMの周波数を変更するというのはこちらと同じ方法ですよね。
http://www.nina.jp/server/arduino/PWM/PWM.html

赤外線の確認方法ですが、「 可視光(赤色)をわずかに残すことで動作確認が目視で行え便利です」というものを使っていまして、確かに便利です。
http://akizukidenshi.com/catalog/g/gI-04779/
出力が大きい赤外線LEDもあるようなので、試してみたいと思います。
http://akizukidenshi.com/catalog/g/gI-03261/

parasporospaparasporospa 2013/05/26 12:32 事後報告です。
赤外線LEDをOSI3CA5111AからOSI5LA5113Aに変えたら2m以上到達できました!
何が違うんでしょうね?波長の850nmと940nmでしょうか?

はてなユーザーのみコメントできます。はてなへログインもしくは新規登録をおこなってください。