こちらの方のページを参考にArduinoでリモコン作ってみた。
参考にしたページでは受信したリモコンデータを一旦テキストエディタ等で加工しなければ送信データとして使えなかったので、コピペで送信できるようにソースを都合よく改変。
使った部品は秋月電子で買える受光部と赤外線LED。
LEDは+の方をリモコン出力ピンにもう片方をGNDに接続。
受光部はOUTPUTをリモコン入力ピンに、あとGND、5Vにそれぞれ接続。
こんな感じ。
これに向かってリモコンのボタンを押してやるとシリアルモニタにデータが表示されるのでコロン以降の文字列をそのままコピーしてArduinoに送信してやるとそのままリモコンとして動作する。
※ただし、80bytesを超えるような長さの文字列を送信すると取りこぼすっぽいので分割して送信すること。
ソースはこんな感じ。
#define DLEN 512 // リモコン入出力ピン #define IR_IN A1 #define IR_OUT A0 // リモコンデータ保存配列 unsigned char data[DLEN]; // リモコン受信状態制御 0:Leader受診前 1:Leader受信後受信完了まで -1:赤外線受信停止(未実装) char recvStatus = 0; // リモコン入力ピンの値保持 char last = 0; // -1,0,1しか使わない。 char onOff = -1; // 赤外線基本の時間 (μsec) リモコンで違うため可変 int tlen; // 0:点灯時間 1:消灯時間保持 unsigned int prevMem[2]; unsigned long us = micros(); // 消灯、点灯時間比率テーブル // NECテーブル unsigned char nec[5][2] = { {1,1}, // '0':数字の並びはoff, on の時間の並び {3,1}, // '1' {4,16}, // repeat {8,16}, // Leader {0,1}, // END }; // 家製協テーブル unsigned char aeha[5][2] = { {1,1}, // '0' {3,1}, // '1' {8,8}, // repeat {4,8}, // Leader {0,1}, // END }; // セットアップ void setup() { Serial.begin(57600); // シリアル通信速度の設定 pinMode(IR_IN, INPUT); // 入出力ピンの設定 pinMode(IR_OUT, OUTPUT); } // dataからリモコン信号を送信 void sendSignal() { char onoff = 1; // 必ず点灯から始まるため。 unsigned char state = 0; unsigned int len; unsigned char (*p)[2]; // necとaehaのtの長さテーブル参照用ポインタ。 for (int cnt = 0; cnt < DLEN; cnt++) { // len に必要な点灯または消灯時間をセット if(cnt == 0){ // 点灯、消灯時間比率テーブルの決定 p = (data[cnt] == 'A') ? aeha : nec ; state = 3; }else{ // 0 or 1 or 2 or 4(END) state = data[cnt] - '0'; } for(onoff = 1; onoff >= 0; onoff--){ // テーブルから、点灯/消灯時間を決定 len = tlen * p[state][onoff]; // 長さ0のデータを見つけたら、リモコン信号送信終了 if(len == 0) break; unsigned long us = micros(); do { digitalWrite(IR_OUT, onoff); delayMicroseconds(8); // キャリア周波数38kHzでON/OFFするよう時間調整 digitalWrite(IR_OUT, 0); delayMicroseconds(7); } while (long(us + len - micros()) > 0); // 送信時間に達するまでループ } // 長さ0のデータを見つけたら、リモコン信号送信終了 if(len == 0) break; } Serial.print("OK\n"); } // シリアルからの受信をチェック // 入力エラーチェックなど全くしていないので注意。 void checkSerial() { unsigned char state = 0; unsigned int datacnt = 0; unsigned char c; if (Serial.available() > 0) { c = Serial.read(); if(c == 'R'){ sendSignal(); }else if (c == 'P') { // 入力開始 // 念のため受信バッファ初期化 for(datacnt = 0; datacnt < DLEN; datacnt++) data[datacnt] = 0; tlen = 0; datacnt = 0; Serial.print(">"); while (1) { while (Serial.available() == 0) { } // 次のバイトが来るまで無限ループで待つ int a = Serial.read(); switch(state){ case 0: // N(EC) or A(AEHA) data[datacnt] = a; datacnt++; state++; break; case 1: // tlen if('0' <= a && a <= '9'){ tlen = tlen * 10 + (a - '0'); }else{ state++; // ’S'受信 } break; case 2: // 0 or 1 or 2 or 4 if('0' <= a && a <= '2'){ data[datacnt] = a; datacnt++; }else{ // '4' データ終端 data[datacnt] = a; datacnt++; state++; } break; } if(state == 3){ Serial.print("data read end.[t="); Serial.print(tlen,DEC); Serial.print(",len="); Serial.print(datacnt,DEC); Serial.print("]\n"); Serial.write(data, datacnt); Serial.print("\n"); sendSignal(); state = 0; break; } } } } } // 点灯、消灯時間からどのデータ(CustomerCode or Data or Leader or Repeat)であるか判断する。 // 送信開始から終了までに複数回Leaderを送るものには対応していない。 void chkData(){ unsigned int t; if(prevMem[0] < 900){ // Customer Code or Data if(recvStatus == 1){ if(prevMem[0] * 2 < prevMem[1]){ Serial.print("1"); } else{ Serial.print("0"); } } } // 最初の点灯の長さでNEC/AEHAどっちのフォーマットかいい加減な感じで判断。 else{ if(recvStatus == 0){ if(prevMem[0] > 8000){ t = prevMem[0] / 16; Serial.print("NEC Leader:[t="); Serial.print(t, DEC); Serial.print("]:PN"); }else{ t = prevMem[0] / 8; Serial.print("AEHA Leader:[t="); Serial.print(t, DEC); Serial.print("]:PA"); } Serial.print(t, DEC); Serial.print("S"); recvStatus = 1; }else{ if(recvStatus == 1) Serial.print("2"); } } } 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 ){ if(flames > 0 && recvStatus == 1){ Serial.print("4\n"); } recvStatus = 0; onOff = -1; // 最初に赤外線ONを受信した時に赤外線off時間の値を無視するため flames = 0; } else{ checkSerial(); // シリアル通信が来ているかチェック } cnt = 30000; } } unsigned long us2 = micros(); unsigned int len = (us2-us); if(onOff >= 0){ // 最初に赤外線ONを受信した時に赤外線off時間の値を無視するため prevMem[onOff] = len; if(onOff == 1){ chkData(); flames++; onOff = -1; } } last = val; us = us2; onOff++; }