Arduinoでリモコン

2010年1月3日 このエントリをはてなブックマークに登録

 Arduinoで、KURO-RS(PC-OP-RS1)とIRIO88(Electric House)の互換で動作するリモコンデバイスを作成してみました。

arduino_ir

arduino用のリモコン・ライブラリーをいくつか見つけましたが、どのライブラリーもKURO-RS互換で動作させるのは難しいようなので、今回はそれらのライブラリーを参考にしながら一から作成しました。

注意:KURO-RS互換ですが、実際にKURO-RS用のアプリが動作するのか確認した訳ではありません。

作り方

回路図通り配線を行い、ソースプログラムをarduinoに書き込めば完成です。
上の写真では、ブレッドボード上で配線を行っています。 一番右端のLEDは通電確認用の物で、実際の動作には必要の無いものです。

作成に必要な部品

Arduino Duemilanove 1
U1 リモコン受信モジュール 1
D1 赤外線LED 1
R1 抵抗 300Ω (橙黒茶金) 1
  • Arduinoとは、AVRを搭載したCPUボードです。 今回利用したのはArduino Duemilanoveと言う、現在最も標準的なArduinoです。
  • リモコン受信モジュールは、赤外線を受信する為のセンサーで、リモコンのキャリア周波数である38kHz前後の赤外線のみを受信出来るようにモジュール化されたものです。 秋月電子で100円程で売られています。 参考:PL-IRM0101 / PL-IRM2121
     
  • 赤外線LED。 秋月電子で100個パックで売られている物を今回は利用しました。
     
  • 抵抗は300Ω(橙黒茶金)の物を利用ました。もう少し小さい値の物を使えば、赤外線LEDが強く光ると思います。

配線

回路図

この回路図は、BScharduinoライブラリ を利用して作成しました。

ソース

#define USB_BAUD 115200UL    // USB Serial Baudrate

#define IR_OUT  3            // IR LED (Arduino 3pin)
#define IR_IN   2            // IR Receiver(Arduino 2pin)
#define L_LED   13

#define MAX_DATA_LENGTH  960

#ifndef cbi
#define cbi(PORT, BIT) (_SFR_BYTE(PORT) &= ~_BV(BIT))
#endif
#ifndef sbi
#define sbi(PORT, BIT) (_SFR_BYTE(PORT) |= _BV(BIT))
#endif

volatile struct
{
    uint8_t rec_check: 1;
    uint8_t rec_start: 1;
}
intflags;

volatile uint8_t  ir_bit;
volatile uint8_t  ir_data[MAX_DATA_LENGTH];
volatile uint16_t i16,send_max;
volatile uint8_t  rbuf;
volatile uint8_t  tcnt2;
volatile uint8_t  smp_rate;

unsigned int count;

ISR(TIMER2_OVF_vect)
{
    TCNT2 = tcnt2;
    
    if( intflags.rec_start )
    {
        // 受信処理中
        if( !(PIND & _BV(2)) )
        {
            ir_data[i16] |= ir_bit;
        }
        ir_bit <<= 1;
        if( ir_bit == 0 )
        {
            ir_bit = 1;
            i16++;
            if( i16 >= MAX_DATA_LENGTH )
            {
                intflags.rec_start = 0;
            }
        }
    }
    else if( intflags.rec_check )
    {
        if( !(PIND & _BV(2)) )
        {
            i16=0;
            ir_data[i16] |= 1;
            ir_bit = 2;
            
            intflags.rec_check = 0;
            intflags.rec_start = 1;
        }
    }
    
    // L-LED点滅
    digitalWrite(L_LED, (count&0x0800)?HIGH:LOW);
    count++;
}

void IROutSetup(int kHz,int duty)
{
    pinMode(IR_OUT, OUTPUT);
    digitalWrite(IR_OUT, LOW);
    
    TCCR2A &= ~(_BV(COM2B1));
    
    TCCR2A = _BV(WGM20);
    TCCR2B = _BV(WGM22) | _BV(CS20);

    OCR2A = F_CPU / 2 / kHz / 100;
    OCR2B = OCR2A / duty;
}
void  IRInSetup(int smp_rate)
{
    TCCR2A = 0;  // normal mode
    cbi(TCCR2B,CS22);
    sbi(TCCR2B,CS21);
    cbi(TCCR2B,CS20);
  
    sbi(TIMSK2,TOIE2);
  
    tcnt2 = 256 - ((F_CPU/8/10000*smp_rate)/100) +5;
    TCNT2 = tcnt2;
  
    sei();

    pinMode(IR_IN, INPUT);
    PORTD|=_BV(2);
}

void IRInStop()
{
    cbi(TIMSK2,TOIE2);
}

void crearBuf()
{
    for(i16=0; i16<MAX_DATA_LENGTH; i16++ )
    {
        ir_data[i16] = 0x00;
    }
}

void waitSerialAvailable()
{
    while(Serial.available() <= 0)
    {
        __asm("nop");
    }
}

void setup()
{
    pinMode(L_LED, OUTPUT);
    digitalWrite(L_LED, HIGH);
    
    // Serial setup
    Serial.begin(USB_BAUD);
    Serial.flush();
    
    count=0;
    smp_rate=50;
    digitalWrite(L_LED, LOW);
}

void loop()
{
    // 
    if(Serial.available() > 0)
    {
        digitalWrite(L_LED, HIGH);
        
        rbuf = Serial.read();
        if(rbuf == 'r')  // Receive IR (compatible KURO-RS)
        {
            Serial.write('Y');
            intflags.rec_start = 0;
            intflags.rec_check  = 0;
            crearBuf();
            intflags.rec_check  = 1;
            IRInSetup(100);
            for(;;)
            {
                if(Serial.available() > 0)
                {
                    rbuf = Serial.read();
                    if(rbuf=='c')
                    {
                        IRInStop();
                        Serial.write('Y');
                        break;
                    }
                }
                if( intflags.rec_check == 0 && intflags.rec_start == 0 )
                {
                    IRInStop();
                    Serial.write('S');
                    for(i16=0; i16<240; i16++ )
                    {
                        Serial.write(ir_data[i16]);
                    }
                    Serial.write('E');
                    break;
                }
            }
        }
        else if(rbuf == 'R')  // Receive IR (compatible IRIO88)
        {
            Serial.write('Y');
            intflags.rec_start = 0;
            intflags.rec_check  = 0;
            crearBuf();
            intflags.rec_check  = 1;
            IRInSetup(smp_rate);
            for(;;)
            {
                if(Serial.available() > 0)
                {
                    rbuf = Serial.read();
                    if(rbuf=='c')
                    {
                        IRInStop();
                        Serial.write('Y');
                        break;
                    }
                }
                if( intflags.rec_check == 0 && intflags.rec_start == 0 )
                {
                    IRInStop();
                    // 最大格納データ長計算
                    for(send_max=(MAX_DATA_LENGTH-1); send_max>=0; send_max--)
                    {
                        if( ir_data[send_max] )
                        {
                            send_max++;
                            break;
                        }
                    }
                    
                    Serial.write('S');
                    Serial.write(smp_rate);
                    Serial.write((byte)(send_max>>8));
                    Serial.write((byte)(send_max&0xFF));
                    
                    for(i16=0; i16<send_max; i16++ )
                    {
                        Serial.write(ir_data[i16]);
                    }
                    Serial.write('E');
                    break;
                }
            }
            
        }
        else if(rbuf == 'S')  // Send IR (compatible IRIO88)
        {
            // smp_rate receive
            Serial.write('Y');
            waitSerialAvailable();
            smp_rate = Serial.read();
            
            waitSerialAvailable();
            send_max = (Serial.read()<<8);
            waitSerialAvailable();
            send_max += Serial.read();
            
            if(send_max>0 && send_max<=MAX_DATA_LENGTH)
            {
                // data receive
                i16=0;
                while(i16<send_max)
                {
                    waitSerialAvailable();
                    ir_data[i16] = Serial.read();
                    i16++;
                }
                
                // send
                byte sbuf;
                byte c;
                IROutSetup(382,3);  // 38.2kHz 1/3duty
                for(i16=0; i16<send_max; i16++ )
                {
                    sbuf=ir_data[i16];
                    for(c=0;c<8;c++)
                    {
                        if(sbuf&1)
                        {
                            digitalWrite(L_LED, HIGH);
                            sbi(TCCR2A,COM2B1);
                        }
                        else
                        {
                            digitalWrite(L_LED, LOW);
                            cbi(TCCR2A,COM2B1);
                        }
                        sbuf>>=1;
                        delayMicroseconds(smp_rate);
                    }
                }
                cbi(TCCR2A,COM2B1);
            }
            Serial.write('E');
        }
        else if(rbuf == 't')  // Send IR (compatible KURO-RS)
        {
            // ch receive
            Serial.write('Y');
            waitSerialAvailable();
            rbuf = Serial.read();
            
            // data receive
            Serial.write('Y');
            i16=0;
            while(i16<240)
            {
                waitSerialAvailable();
                ir_data[i16] = Serial.read();
                i16++;
            }
            
            // send
            byte sbuf;
            byte c;
            IROutSetup(382,3);  // 38.2kHz 1/3duty
            for(i16=0; i16<240; i16++ )
            {
                sbuf=ir_data[i16];
                for(c=0;c<8;c++)
                {
                    if(sbuf&1)
                    {
                        digitalWrite(L_LED, HIGH);
                        sbi(TCCR2A,COM2B1);
                    }
                    else
                    {
                        digitalWrite(L_LED, LOW);
                        cbi(TCCR2A,COM2B1);
                    }
                    sbuf>>=1;
                    delayMicroseconds(100);
                }
            }
            cbi(TCCR2A,COM2B1);
            Serial.write('E');
        }
        else if(rbuf == 'T')  // Set Sampling Rate (compatible IRIO88)
        {
            Serial.write('Y');
            waitSerialAvailable();
            rbuf = Serial.read();
            
            if(rbuf>100)  rbuf=100;
            else if(rbuf<20)  rbuf=20;
            
            smp_rate=rbuf;
            Serial.write('E');
        }
        else if(rbuf == 'i')  // LED (compatible KURO-RS)
        {
            Serial.write('O');
        }
        else if(rbuf == 'c')  // Cancel (compatible KURO-RS)
        {
            Serial.write('Y');
        }
        digitalWrite(L_LED, LOW);
    }
}

 

※2009/1/13 受信待機中のキャンセルの応答が’E’になっていたのを、’Y’に修正しました。

使い方

こちらのページをどうぞ

 

タグ: ,



コメント / トラックバック5件

  1. P より:

    winlirc用のpioservrmtggf1381.cfをコピペしてwinlircで使おうとしたところ、初期化に失敗してしまいます。XPや、2000、7でも試してみましたが、いずれもダメです。winlirc に最初から入っているsample.cfは問題なく使用できますが・・・どこが問題でしょうか?

  2. Futaba より:

    コメントありがとうございます。
    普通に考えて、pioservrmtggf1381.cfが問題なんじゃ無いかと思いますが・・・

  3. k より:

    Arduinoのキット販売で学習リモコンを計画しています。そこでこのライブラリを利用させていただきたいのですが、何か問題がありますでしょうか、宜しくお願いいたします。

  4. Futaba より:

    コメント有り難うございます。
    返信遅れてもうしわけありません。
    ソースの利用はご自由にどうぞ。
    ただし、回路の方が、このままだと動作が不安定になるので注意が必要です。

  5. halt より:

    Arduino Unoでも動作しました!素晴らしいコードの公開ありがとうございます!