Arduinoでリモコン(その2)

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

以前、Arduinoでリモコンで紹介したリモコンデバイスに、Bluetoothモジュールを繋いでBluetoothで赤外線リモコンの操作を出来るようにしましたのでご紹介。

BT-MOD100RArduinoリモコン2

BluetoothのSPP(シリアルプロファイル)に対応したモバイル端末やスマートフォン等でリモコン操作が行えるようになります。 ただし、まだ対応したアプリはまだ出来ていませんが・・・

必要な部品

Arduino Duemilanove 1
BT-MOD100R(Bluetoothモジュール) 1
80FG970(BT-MOD100R用アダプタ) 1
U1 リモコン受信モジュール 1
R1 抵抗 1KΩ 1
R2 抵抗 75Ω 1
R3 抵抗 1KΩ 1
Q1 2SC1815(NPNトランジスタ) 1
D1 赤外線LED 1
D2 LED 1
  • Arduino
    利用しているのは、Arduino Duemilanoveで 3200円程で売られています。
     
  • BT-MOD100R + 80FG970
    今回利用したBluetoothモジュールは、BT-MOD100Rと言うモジュールで、携帯電話(Android)で利用してみたレポートをこちらで紹介しています。
    僕が購入したのは、BT-MOD100R80FG970で、約1万円程でした。
     
  • リモコン受信モジュール
    秋月電子で100円程で売られている物を利用。
    参考:PL-IRM0101 / PL-IRM2121
     
  • 抵抗
    秋月電子で100本入り100円で売られています。
    参考:75Ω 1KΩ
     
  • 2SC1815
    秋月電子で20個入り100円で売られています。
    参考:2SC1815GR
     
  • 赤外線LED
    今回利用したのは、秋月電子で5個100円で売られている50mAまで流せる赤外線LED。

     

 配線

回路図

基本的には、前回の配線と同じで、4番・5番のピンにBluetoothモジュールを接続したものです。
出力アップ用のトランジスタや、動作確認用LEDの外付けは、必要に応じて実装してください。

Arduinoソース

#include <NewSoftSerial.h>

#define USB_BAUD 115200UL    // USB Serial Baudrate
#define BT_BAUD 9600UL       // Bluetooth Connection Baudrate

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

#define FROM_USB    0
#define FROM_BT     1

#define BT_TPIN     4
#define BT_RPIN     5

#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;
volatile uint8_t  dataFrom;

unsigned int count;

NewSoftSerial BTSerial(BT_RPIN,BT_TPIN);

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;
        }
    }
    
    // 動作確認用
    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));
    
    // COM2A = 00: disconnect OC2A
    // COM2B = 00: disconnect OC2B; to send signal set to 10: OC2B non-inverted
    // WGM2 = 101: phase-correct PWM with OCRA as top
    // CS2 = 000: no prescaling
    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 setup()
{
    pinMode(L_LED, OUTPUT);
    digitalWrite(L_LED, HIGH);
    
    // Serial setup
    Serial.begin(USB_BAUD);
    Serial.flush();
    
    // Bluetooth側
    BTSerial.begin(BT_BAUD);
    BTSerial.flush();
    
    count=0;
    smp_rate=50;
    digitalWrite(L_LED, LOW);
    
    dataFrom=0;
}

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

int readData()
{
    for(;;)
    {
        if(dataFrom==FROM_USB)
        {
            if(Serial.available()>0)
            {
                return Serial.read();
            }
        }
        else
        {
            if(BTSerial.available()>0)
            {
                return BTSerial.read();
            }
        }
    }
}

void writeData(uint8_t data)
{
    if(dataFrom==FROM_USB)  Serial.write(data);
    else                    BTSerial.print(data);
}

void writeIRData(int length)
{
    int i;
    for(i=0; i<length; i++ )
    {
        writeData(ir_data[i]);
    }
}

/////////////////////////////////////
// Receive IR (compatible KURO-RS)
void command_r()
{
    writeData('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();
                writeData('Y');
                break;
            }
        }
        if(BTSerial.available() > 0)
        {
            rbuf = BTSerial.read();
            if(rbuf=='c')
            {
                IRInStop();
                break;
            }
        }
        if( intflags.rec_check == 0 && intflags.rec_start == 0 )
        {
            IRInStop();
            
            writeData('S');
            writeIRData(240);
            writeData('E');
            break;
        }
        
    }
}

///////////////////////////////////
// Receive IR (compatible IRIO88)
void command_R()
{
    writeData('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();
                break;
            }
        }
        if(BTSerial.available() > 0)
        {
            rbuf = BTSerial.read();
            if(rbuf=='c')
            {
                IRInStop();
                writeData('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;
                }
            }
            
            writeData('S');
            writeData(smp_rate);
            writeData((byte)(send_max>>8));
            writeData((byte)(send_max&0xFF));
            writeIRData(send_max);
            writeData('E');
            break;
        }
    }
}

/////////////////////////////////
// Send IR (compatible KURO-RS)
void command_t()
{
    // ch receive
    writeData('Y');
    rbuf = readData();
    
    // data receive
    writeData('Y');
    i16=0;
    while(i16<240)
    {
        ir_data[i16] = readData();
        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);
    writeData('E');
}

////////////////////////////////
// Send IR (compatible IRIO88)
void command_S()
{
    // ch receive
    writeData('Y');
    smp_rate = readData();
    send_max = (readData()<<8);
    send_max += readData();
    
    // data receive
    if(send_max>0 && send_max<=MAX_DATA_LENGTH)
    {
        i16=0;
        while(i16<send_max)
        {
            ir_data[i16] = readData();
            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);
    writeData('E');
}
////////////////////////////////
// Set Sampling Rate (compatible IRIO88)
void command_T()
{
    writeData('Y');
    rbuf = readData();
    
    if(rbuf>100)  rbuf=100;
    else if(rbuf<20)  rbuf=20;
    
    smp_rate=rbuf;
    writeData('E');
}

void loop()
{
    // 
    if(Serial.available() > 0)
    {
        digitalWrite(L_LED, HIGH);
        dataFrom=FROM_USB;
        
        rbuf = Serial.read();
        if(rbuf == 'r')       command_r();    // Receive IR (compatible KURO-RS)
        else if(rbuf == 'R')  command_R();    // Receive IR (compatible IRIO88)
        else if(rbuf == 'S')  command_S();    // Send IR (compatible IRIO88)
        else if(rbuf == 't')  command_t();    // Send IR (compatible KURO-RS)
        else if(rbuf == 'T')  command_T();    // Set Sampling Rate (compatible IRIO88)
        else if(rbuf == 'i')  writeData('O'); // LED (compatible KURO-RS)
        else if(rbuf == 'c')  writeData('Y'); // Cancel (compatible KURO-RS)
        else
        {
            delay(100);
            Serial.flush();
            BTSerial.flush();
        }
        digitalWrite(L_LED, LOW);
    }
    
    if(BTSerial.available() > 0)
    {
        digitalWrite(L_LED, HIGH);
        dataFrom=FROM_BT;
        
        rbuf = BTSerial.read();
        
        if(rbuf == 'r')       command_r();
        else if(rbuf == 'R')  command_R();
        else if(rbuf == 'S')  command_S();
        else if(rbuf == 't')  command_t();
        else if(rbuf == 'T')  command_T();
        else if(rbuf == 'i')  writeData('O');
        else if(rbuf == 'c')  writeData('Y');
        else
        {
            delay(100);
            Serial.flush();
            BTSerial.flush();
        }
        digitalWrite(L_LED, LOW);
    }
}

このプログラムではNewSoftSerial(Arduinoライブラリ)を利用しています。

内部の処理としては、シリアル入力を2系統に対応させているので、今回利用したBluetoothモジュール以外のUSART対応モジュールを接続することも可能です。
他のモジュールを接続する場合、ソースの冒頭の

#define BT_BAUD 9600UL

を変更して、通信速度を調整してください。

使い方

デバイスは、上の配線通り接続して、ソースをコンパイルしてArduinoに書き込めば出来上がりです。

PCがBluetoothに対応している場合、RFCOMMを設定してコマンドを送ると、リモコン操作が行えるようになります。このデバイスを操作する為のコマンドはこちらのページを ご覧下さい。

RFCOMMで接続できれば、KURO-RS用のアプリなんかが動く・・・はずです(未確認)

 

タグ: , ,



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

  1. sapporo kazz より:

    ユニットの購入!

     自分では中々作成できないので、完成品を購入できますか?