Top/Devel/電子工作/Arduino/赤外線学習リモコンR2-D2

赤外線学習リモコンR2-D2はてなブックマーク

IR_R2-D2_01.jpg

夏は暑い。


外が暑い中を帰ってきて、自宅も暑いとうんざりする。


帰宅途中にエアコンをオンにできたらいいのに。


…そうだ!R2-D2に頼もう!

ということで、赤外線で色々な機器を操作するR2-D2を作ることに。


以前、Devel/電子工作/トラ技付録78K0 USBマイコン基板/赤外線学習リモコンR2-D2で作ったものがハードウェア故障とソース紛失で動作しなくなってしまったので、そのリベンジです。


この後、Raspberry Pi+irMagicianで作り直しました!

IR_R2-D2_02.jpg

機能

http://【ArduinoのIPアドレス】/【IRパケットの10進数表現】 にGETリクエストを送ると、任意の赤外線信号を送信できます。

材料

ir_shield.jpg

手順

  1. Arduino Uno、イーサネットシールド、IRシールドを順に接続する。
  2. IRシールドの赤外線LEDに拡散キャップを装着する。
  3. IRシールドのジャンパをすべて外す。
  4. 「赤外線信号を10進表現IRパケットに変換するスケッチ」をArduinoに書き込む。
  5. シリアルモニタを表示する。
  6. IRシールドのジャンパを2ピンと8ピンにつける。
  7. リモコンをIRシールドに向けて、ボタンを押す。
  8. シリアルモニタに表示された10進表現IRパケットをメモしておく。これを学習させたい信号分繰り返す。
  9. IRシールドのジャンパをすべて外す。
  10. 「ウェブサーバとして10進表現IRパケットを受信し、赤外線信号を送信するスケッチ」をArduinoに書き込む。
  11. IRシールドのジャンパを2ピンと8ピンにつける。
  12. 【動作確認】ブラウザを開き、http://【ArduinoのIPアドレス】/【10進数表現IRパケット】にアクセスしてみる。
  13. 正常に動作することが確認できたら、外装の製作に入る。
  14. Star Wars Bank R2-D2を切断する。
  15. IRシールドの赤外線LEDが露出するように穴を空ける。
  16. Star Wars Bank R2-D2にArduinoを格納する。

スケッチ

赤外線信号を10進表現IRパケットに変換するスケッチ

赤外線信号を受信し、シリアルモニタにIRパケット情報を10進数表現で表示します。

Jun様のいじわるリモコンスケッチの改変版です。Jun様ありがとうございます!

fileir_receiver.ino
/*
 * ir_reciever
 *
 * Date: 2012-08-05 23:55:00 +0900 (Sun, 05  Aug 2012)
 * Author: diabah( http://cubic9.com/ )
 * Original: jun ( http://www.geocities.jp/jun930/ele/irshield.html )
 */

#define DELIMITER "."

// IR packet
int hh,hl;
byte d1h,d1l;
byte d0h,d0l;
byte th,tl;
int len;
byte dat[50];

// last receive time
unsigned long last = 0;

int read_byte(){
  while(Serial.available() == 0);
  return Serial.read();
}

boolean rcv_pkt(){
  int i;
  byte cs = 0;
  
  if(read_byte() != 0xff) return false;
  cs += (i = read_byte());  hh = i * 256;
  cs += (i = read_byte());  hh += i;
  cs += (i = read_byte());  hl = i * 256;  
  cs += (i = read_byte());  hl += i;
  cs += (d1h = read_byte());
  cs += (d1l = read_byte());
  cs += (d0h = read_byte());
  cs += (d0l = read_byte());
  cs += (th = read_byte());
  cs += (tl = read_byte());
  cs += (i = read_byte());  len = i * 256;
  cs += (i = read_byte());  len += i;
  if (len > 400 || len <= 0) return false;
  for(i = 0;i < bytes(); i++){
    cs += (dat[i] = read_byte());
  }
  if(read_byte() != cs) return false;
  
  return true;
}

int bytes(){
  if(len == 0) return 0;
  return (len - 1) / 8 + 1;
}

byte checksum(){
  byte cs = 0;
  int i;
  
  cs += (byte)(hh >> 8) + (byte)(hh & 0xff);
  cs += (byte)(hl >> 8) + (byte)(hl & 0xff);
  cs += d1h + d1l + d0h + d0l + th + tl;
  cs += (byte)(len >> 8) + (byte)(len & 0xff);
  for(i = 0;i < bytes(); i++) cs += dat[i];

  return cs;
}

void snd_pkt(){
  Serial.write((byte)0xff);
  Serial.write((byte)(hh >> 8));
  Serial.write((byte)(hh & 0xff));
  Serial.write((byte)(hl >> 8));
  Serial.write((byte)(hl & 0xff));
  Serial.write(d1h);
  Serial.write(d1l);
  Serial.write(d0h);
  Serial.write(d0l);
  Serial.write(th);
  Serial.write(tl);
  Serial.write((byte)(len >> 8));
  Serial.write((byte)(len & 0xff));
  Serial.write(dat,bytes());
  Serial.write(checksum());
  read_byte();  // receive ACK
}

void setup(){
  pinMode(13,OUTPUT);
  Serial.begin(19200);
  len = 0;
}

void print_pkt(){
  Serial.println();
  Serial.print(int((byte)0xff));
  Serial.print(DELIMITER);
  Serial.print(int((byte)(hh >> 8)));
  Serial.print(DELIMITER);
  Serial.print(int((byte)(hh & 0xff)));
  Serial.print(DELIMITER);
  Serial.print(int((byte)(hl >> 8)));
  Serial.print(DELIMITER);
  Serial.print(int((byte)(hl & 0xff)));
  Serial.print(DELIMITER);
  Serial.print(int(d1h));
  Serial.print(DELIMITER);
  Serial.print(int(d1l));
  Serial.print(DELIMITER);
  Serial.print(int(d0h));
  Serial.print(DELIMITER);
  Serial.print(int(d0l));
  Serial.print(DELIMITER);
  Serial.print(int(th));
  Serial.print(DELIMITER);
  Serial.print(int(tl));
  Serial.print(DELIMITER);
  Serial.print(int((byte)(len >> 8)));
  Serial.print(DELIMITER);
  Serial.print(int((byte)(len & 0xff)));
  Serial.print(DELIMITER);
  int i;
  for(i = 0;i < bytes(); i++){
    Serial.print(int(dat[i]));
  Serial.print(DELIMITER);
  }
  Serial.print(int(checksum()));
  Serial.println();
}

void loop(){
  if (Serial.available() > 0){
    digitalWrite(13,HIGH);
    if(rcv_pkt()){
      last = millis();
    }
    digitalWrite(13,LOW);
  }
  
  if(millis() - last > 1000 && len > 0){
    digitalWrite(13,HIGH);
    snd_pkt();
    digitalWrite(13,LOW);
    Serial.end();

    delay(100);

    digitalWrite(13,HIGH);
    Serial.begin(9600);
    print_pkt();
    digitalWrite(13,LOW);
    Serial.end();

    delay(100);

    Serial.begin(19200);

    len = 0;
  }
}

ウェブサーバとして10進表現IRパケットを受信し、赤外線信号を送信するスケッチ

ウェブサーバとしてGETリクエストで10進数表現IRパケット情報を受信し、赤外線信号を送信します。

実際に使用する前にMACアドレスをご自身のArduinoイーサネットシールド貼付のMACアドレスに書き換えてください。IPアドレス、ポートも必要であれば変更してください。

ブロードバンドルータなどでポートマッピング設定(いわゆるポート開放)をするとインターネット経由でR2-D2を操作できるようになります。

fileir_sender.ino
/*
 * ir_sender
 *
 * Date: 2012-08-05 23:55:00 +0900 (Sun, 05  Aug 2012)
 * Author: diabah( http://cubic9.com/ )
 * Original: Arduino Ofiicial ( http://arduino.cc/en/Tutorial/WebServer )
 */

#include <SPI.h>
#include <Ethernet.h>

#define BEGINNING_OF_LINE 0
#define PREFIX_LENGTH 4
#define DELIMITER "."

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {  0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
IPAddress ip(192, 168, 0, 40);

// Initialize the Ethernet server library
// with the IP address and port you want to use 
// (port 80 is default for HTTP):
EthernetServer server(3000);

void setup() {
 // Open serial communications and wait for port to open:
  Serial.begin(19200);


  // start the Ethernet connection and the server:
  Ethernet.begin(mac, ip);
  server.begin();
}


void loop() {
  char ir_code[255];
  int cusror = 0;
  int ir_cnt = 0;
  // listen for incoming clients
  EthernetClient client = server.available();
  if (client) {
    // an http request ends with a blank line
    boolean beginflag = false;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        //Serial.write(c);
        if (cusror == BEGINNING_OF_LINE && c == 'G') {
          // if beginning of line is start with "GET"
          beginflag = true;
        } else 
        if (cusror == PREFIX_LENGTH + 1 && c == 'f') {
          // if GET request is getting favicon
          beginflag = false;
        } else
        if (cusror == BEGINNING_OF_LINE && c == '\r') {
          // if beggining of line is CR
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connnection: close");
          client.println();
          client.println("<html>");
          client.println("<body>");
          client.println(ir_code);
          client.println("</body>");
          client.println("</html>");

          char *part;
          for(part = strtok(ir_code, DELIMITER); part; part = strtok(NULL, DELIMITER)) {
            Serial.write(byte(atoi(part)));
          }
          break;
        } else
        if (beginflag == true) {
          if (cusror == PREFIX_LENGTH) {
            ir_cnt = -1;
          } else if (cusror > PREFIX_LENGTH){
            if (c == ' ') {
              // if current character is the space before "HTTP/1.1"
              ir_code[ir_cnt] = '\0';
              beginflag = false;
            } else {
              ir_code[ir_cnt] = c;
            }
          }
          ir_cnt++;
        }
        cusror++;
        if (c == '\n') {
          cusror = BEGINNING_OF_LINE;
        }
      }
    }
    
    // give the web browser time to receive the data
    delay(1);
    // close the connection:
    client.stop();
  }
}

自宅のIRパケット

操作型番10進数表現IRパケット
エアコン電源オフ東京ガス DCグリル-35CEA255.1.69.0.157.23.57.23.17.23.255.0.48.86.232.111.1.68.43.190
エアコン電源オン東京ガス DCグリル-35CEA255.1.69.0.157.23.57.23.18.23.255.0.48.86.232.111.1.68.24.172
シーリングライト電源オフPanasonic HHFZ4290255.0.139.0.62.21.47.21.13.20.255.0.40.52.74.144.236.124.224
シーリングライト電源オンPanasonic HHFZ4290255.0.139.0.62.21.47.21.13.21.255.0.40.52.74.144.44.188.97
テレビ電源オン/オフPanasonic TH-36FG15255.0.138.0.63.21.46.22.13.21.255.0.48.64.4.1.0.188.189.49
テレビ音量増Panasonic TH-36FG15255.0.138.0.63.21.47.21.13.20.255.0.48.64.4.1.0.4.5.192
テレビ音量減Panasonic TH-36FG15255.0.138.0.63.21.45.23.12.21.255.0.48.64.4.1.0.132.133.192
MDコンポ電源オン/オフPanasonic SA-PM35MD255.0.138.0.62.21.46.21.13.21.255.0.48.64.4.5.56.188.129.47
コンポ音量減Panasonic SA-PM35MD255.0.138.0.62.21.45.21.13.21.255.0.48.64.4.5.0.132.129.190
コンポ音量増Panasonic SA-PM35MD255.0.137.0.63.21.47.21.13.21.255.0.48.64.4.5.0.4.1.192
コンポCD再生Panasonic SA-PM35MD255.0.138.0.62.21.45.21.13.21.255.0.48.64.4.5.80.80.5.94
コンポMD再生Panasonic SA-PM35MD255.0.138.0.62.21.45.21.13.21.255.0.48.64.4.5.4.80.81.94
コンポ停止Panasonic SA-PM35MD255.0.137.0.62.21.46.21.13.21.255.0.48.64.4.5.56.0.61.46

備考

ソニーのブルーレイレコーダBDZ-AT950Wの信号は学習できませんでした (^^;

これはソニー製品の信号フォーマットは、家電協フォーマットやNECフォーマットとは違い、明滅の時間の長さによってビットを表現しており、IRレシーバで読み取れる方式ではないためです。

もちろん自前で実装すれば解決できます。特にビットを判定せず、読み取った波形をそのまま再現するようにすればよいのです。

しゃべらせる

しゃべらせたい場合、音声再生部品を別途調達する必要があります。

一般的なのはWaveシールドだと思うのですが、私が秋葉原に行ったときは見つけられませんでした。

そこで後述の通り、APLUS社の録音再生IC APR9600を利用した秋月電子通商のキットを使いました。

機能

http://【ArduinoのIPアドレス】/【IRパケットの10進数表現】 にGETリクエストを送ると、任意の赤外線信号を送信できます。

また、

http://【ArduinoのIPアドレス】/sound にGETリクエストを送ると、APR9600に録音された音声を再生します。

材料

手順

  1. K01-Aとアンプ基盤を組み立てて動作確認する。
  2. K01-Aの音質アップのためにオシレーターの周波数を決める抵抗(回路図のR7)を24kΩに差し替える。
  3. K01-Aの電源端子(基盤のJ3)の線を除去する。
  4. ArduinoのGNDをK01-Aの電源端子(基盤のJ3)に接続する。
  5. ST-20Aの白線をK01-Aのオーディオ出力(回路図のJ2)の+に接続する。
  6. ST-20Aの緑線をK01-Aのオーディオ出力(回路図のJ2)の-に接続する。
  7. ST-20Aの赤線をアンプ基盤のオーディオ入力(回路図のA)に接続する。
  8. ST-20Aの黒線をアンプ基盤のオーディオ入力(回路図のG)に接続する。
  9. スピーカーの+をアンプ基盤のオーディオ出力(回路図のP)に接続する。
  10. スピーカーの-をアンプ基盤のオーディオ出力(回路図のN)に接続する。
  11. ArduinoのDIGITAL2をK01-AのAPR9600の(回路図のM1)に接続する。
  12. Arduinoの5VをK01-Aの電源端子(基盤のJ3)に接続する。
  13. ArduinoのGNDをK01-Aの電源端子(基盤のJ3)に接続する。
  14. Arduinoの5Vをアンプ基盤の電源端子(回路図のV)に接続する。
  15. ArduinoのGNDをアンプ基盤の電源端子(回路図のG)に接続する。
  16. IRシールドのジャンパをすべて外す。
  17. 「赤外線信号を送信したり音声出力したりするスケッチ」をArduinoに書き込む。
  18. IRシールドのジャンパを2ピンと8ピンにつける。

※とりあえず組む場合、Arduinoのピンと何かを接続するにはICテストリードが便利です。

赤外線信号を送信したり音声出力したりするスケッチ

下記は前述の「ウェブサーバとして10進表現IRパケットを受信し、赤外線信号を送信するスケッチ」からの差分です。

fileir_sender_apr9600.diff
--- ir_sender.ino    Sun Sep 22 00:50:32 2013
+++ ir_sender_apr9600.ino    Sun Sep 22 00:52:44 2013
@@ -13,6 +13,8 @@
 #define PREFIX_LENGTH 4
 #define DELIMITER "."
 
+#define APR9600 2
+
 // Enter a MAC address and IP address for your controller below.
 // The IP address will be dependent on your local network:
 byte mac[] = {  0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
@@ -24,7 +26,12 @@
 EthernetServer server(3000);
 
 void setup() {
- // Open serial communications and wait for port to open:
+  // initialize the digital pin as an output.
+  pinMode(APR9600, OUTPUT);     
+  // reset digital pin connected to APR9600: 
+  digitalWrite(APR9600, HIGH);
+
+  // Open serial communications and wait for port to open:
   Serial.begin(19200);
 
 
@@ -55,6 +62,23 @@
           // if GET request is getting favicon
           beginflag = false;
         } else
+        if (cusror == PREFIX_LENGTH + 1 && c == 's') {
+          // if GET request is requsting sound
+          beginflag = false;
+          digitalWrite(APR9600, LOW);
+          delay(500);
+          digitalWrite(APR9600, HIGH);
+          client.println("HTTP/1.1 200 OK");
+          client.println("Content-Type: text/html");
+          client.println("Connnection: close");
+          client.println();
+          client.println("<html>");
+          client.println("<body>");
+          client.println("sound");
+          client.println("</body>");
+          client.println("</html>");
+          break;
+        } else
         if (cusror == BEGINNING_OF_LINE && c == '\r') {
           // if beggining of line is CR
           client.println("HTTP/1.1 200 OK");

コメント等ありましたらどうぞ :)

name:

参考

関連

Amazon

差分 一覧