以前、Arduinoでリモコンで紹介したリモコンデバイスに、Bluetoothモジュールを繋いでBluetoothで赤外線リモコンの操作を出来るようにしましたのでご紹介。
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-MOD100Rと80FG970で、約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用のアプリなんかが動く・・・はずです(未確認)
ユニットの購入!
自分では中々作成できないので、完成品を購入できますか?