概要
ルームライトのリモコンでラジコンを操作します。
厳密には、Arduinoと赤外線リモコンでモーターの制御を出来るようにします。
記事の内容としては、リモコンから送信されるデータの解析の後、リモコンのボタンを押すと動作するモノの製作を行うといった具合です。
リモコンを解析する
まずはリモコンの赤外線をArduinoに学習させましょう。
必要なもの
- 赤外線受光モジュール
- Arduino
- エアコンのリモコン本体「TLR-002」
の3つを使います。
ここで使用するArduinoは互換品で、
赤外線受光モジュールは「1838B」です。
引用元:https://www.robodukkan.com/38-KHz-IR-Kizilotesi-Alici-Goz-VS-1838B,PR-344.html
これは私の部屋のルームライトに付属してきたリモコンです。
配線
信号のピンには、別のピンを使用してもらっても構いませんが、
後ほど使うサンプルプログラムの関係で、信号ピンには11番を使用するのが楽だと思います。
IRremoteライブラリをインストール
IRremote.hとは、リモコンから送信される情報を確認したり、逆に赤外線LEDから情報を送信する場合に使える非常に便利なライブラリです。今回はこのライブラリを使用します。
Arduinoのライブラリマネージャを開き検索ボックスに「IRremote」と入力し、該当のライブラリをインストールします。
リモコンから送信される情報を取得する
IRrecvDumpV2.inoをArduinoに書き込む
ArduinoIDEでファイル>スケッチ例>IRremote>IRrecvDumpV2を選択し、開きます。
すると新しいウィンドウで、サンプルプログラムが開かれるはずです。
サンプル用のプログラムソースは以下の通りです。
//------------------------------------------------------------------------------
// Include the IRremote library header
//
#include <IRremote.h>
//------------------------------------------------------------------------------
// Tell IRremote which Arduino pin is connected to the IR Receiver (TSOP4838)
//
int recvPin = 11;
IRrecv irrecv(recvPin);
//+=============================================================================
// Configure the Arduino
//
void setup ( )
{
Serial.begin(9600); // Status message will be sent to PC at 9600 baud
irrecv.enableIRIn(); // Start the receiver
}
//+=============================================================================
// Display IR code
//
void ircode (decode_results *results)
{
// Panasonic has an Address
if (results->decode_type == PANASONIC) {
Serial.print(results->address, HEX);
Serial.print(":");
}
// Print Code
Serial.print(results->value, HEX);
}
//+=============================================================================
// Display encoding type
//
void encoding (decode_results *results)
{
switch (results->decode_type) {
default:
case UNKNOWN: Serial.print("UNKNOWN"); break ;
case NEC: Serial.print("NEC"); break ;
case SONY: Serial.print("SONY"); break ;
case RC5: Serial.print("RC5"); break ;
case RC6: Serial.print("RC6"); break ;
case DISH: Serial.print("DISH"); break ;
case SHARP: Serial.print("SHARP"); break ;
case JVC: Serial.print("JVC"); break ;
case SANYO: Serial.print("SANYO"); break ;
case MITSUBISHI: Serial.print("MITSUBISHI"); break ;
case SAMSUNG: Serial.print("SAMSUNG"); break ;
case LG: Serial.print("LG"); break ;
case WHYNTER: Serial.print("WHYNTER"); break ;
case AIWA_RC_T501: Serial.print("AIWA_RC_T501"); break ;
case PANASONIC: Serial.print("PANASONIC"); break ;
case DENON: Serial.print("Denon"); break ;
}
}
//+=============================================================================
// Dump out the decode_results structure.
//
void dumpInfo (decode_results *results)
{
// Check if the buffer overflowed
if (results->overflow) {
Serial.println("IR code too long. Edit IRremoteInt.h and increase RAWLEN");
return;
}
// Show Encoding standard
Serial.print("Encoding : ");
encoding(results);
Serial.println("");
// Show Code & length
Serial.print("Code : ");
ircode(results);
Serial.print(" (");
Serial.print(results->bits, DEC);
Serial.println(" bits)");
}
//+=============================================================================
// Dump out the decode_results structure.
//
void dumpRaw (decode_results *results)
{
// Print Raw data
Serial.print("Timing[");
Serial.print(results->rawlen-1, DEC);
Serial.println("]: ");
for (int i = 1; i < results->rawlen; i++) {
unsigned long x = results->rawbuf[i] * USECPERTICK;
if (!(i & 1)) { // even
Serial.print("-");
if (x < 1000) Serial.print(" ") ;
if (x < 100) Serial.print(" ") ;
Serial.print(x, DEC);
} else { // odd
Serial.print(" ");
Serial.print("+");
if (x < 1000) Serial.print(" ") ;
if (x < 100) Serial.print(" ") ;
Serial.print(x, DEC);
if (i < results->rawlen-1) Serial.print(", "); //',' not needed for last one
}
if (!(i % 8)) Serial.println("");
}
Serial.println(""); // Newline
}
//+=============================================================================
// Dump out the decode_results structure.
//
void dumpCode (decode_results *results)
{
// Start declaration
Serial.print("unsigned int "); // variable type
Serial.print("rawData["); // array name
Serial.print(results->rawlen - 1, DEC); // array size
Serial.print("] = {"); // Start declaration
// Dump data
for (int i = 1; i < results->rawlen; i++) {
Serial.print(results->rawbuf[i] * USECPERTICK, DEC);
if ( i < results->rawlen-1 ) Serial.print(","); // ',' not needed on last one
if (!(i & 1)) Serial.print(" ");
}
// End declaration
Serial.print("};"); //
// Comment
Serial.print(" // ");
encoding(results);
Serial.print(" ");
ircode(results);
// Newline
Serial.println("");
// Now dump "known" codes
if (results->decode_type != UNKNOWN) {
// Some protocols have an address
if (results->decode_type == PANASONIC) {
Serial.print("unsigned int addr = 0x");
Serial.print(results->address, HEX);
Serial.println(";");
}
// All protocols have data
Serial.print("unsigned int data = 0x");
Serial.print(results->value, HEX);
Serial.println(";");
}
}
//+=============================================================================
// The repeating section of the code
//
void loop ( )
{
decode_results results; // Somewhere to store the results
if (irrecv.decode(&results)) { // Grab an IR code
dumpInfo(&results); // Output the results
dumpRaw(&results); // Output the results in RAW format
dumpCode(&results); // Output the results as source code
Serial.println(""); // Blank line between entries
irrecv.resume(); // Prepare for the next value
}
}
シリアルモニタを確認する
このコードをArduinoに書き込み、シリアルモニタを開きましょう。
シリアルモニタは、IDEの右上のアイコンを押すことで起動します。
シリアルモニタを開いたら、赤外線モジュールに向けてリモコンのボタンを押しましょう。
するとリモコンから受信した情報が表示されるはずです。
様々な情報が出てきましたが、
今回使用するのは”0x214AD629″の部分になります。
この場合だと、「全灯」のボタンを押すことで、
リモコンから”0x214AD629″という情報が送信されているということが分かりました。
同様にして「消灯」のボタンを押した際の情報も収集します。
私の場合以下のようになりました。
ボタン | バイトコード |
全灯 | 0x214AD629 |
消灯 | 0x214ABE41 |
赤外線リモコンでLEDを制御してみる
前章で得たリモコンの情報を元に、
「全灯」ボタンを押してLEDをONに、「消灯」ボタンを押してLEDをOFFにするモノをプロトタイプを製作しましょう。
必要なもの
- 前章で配線したブレッドボート
- 適当なLED
- 330[Ω]の抵抗
LEDと抵抗は適当なもので構いません。なんなら豆電球でもOKです。
配線
下図のように配線をします。
前章の回路にLEDを加えただけの回路です。
コードを書く
「全灯」ボタンを押してLEDをON、「消灯」ボタンを押してLEDをOFFにするプログラムを組みます。
#include<IRremote.h>
#define ON 0x214A56A9 //「全灯ボタン」
#define OFF 0x214A3EC1 //「消灯ボタン」
int RECV_PIN = 11;
int LED_PIN = 3;
IRrecv irrecv(RECV_PIN);
decode_results results;
void setup() {
Serial.begin(9600);
irrecv.enableIRIn();
pinMode(LED_PIN,OUTPUT);
}
void loop() {
if (irrecv.decode(&results)) {
if (results.value == ON) {
Serial.println("ON");
digitalWrite(LED_PIN,HIGH) ;
}
if (results.value == OFF) {
Serial.println("OFF");
digitalWrite(LED_PIN,LOW);
}
irrecv.resume();
}
}
赤外線リモコンで、LEDを操作するこができました。
また、シリアルコンソールからも動作の確認ができるかと思います。
赤外線リモコンでモーターを制御する
いよいよ最後の章です。
前章の内容を元にモーターを制御しましょう!
必要なもの
- モータードライバ「TA7291P」2個
- 1.5Vの乾電池 2個
- 電池ボックス(モーター用の電源)
- モバイルバッテリー(Arduino用の電源)
- Arduino
- 赤外線受信モジュール
- タミヤのダブルギアボックス https://www.tamiya.com/japan/products/70168/index.html
モータードライバについて
ものすごく簡単に(雑に)まとめると、
Arduinoのピンの電圧のHIGH、LOWの組み合わせでモーターの「前進」、「後進」、「ブレーキ」のアクションを生み出すことが出来ます。
「TA7291の使い方」などで調べれば、
他記事に詳しい情報があると思いますので、割愛させていただきます。
外観
遊びで障害物見つけると自動停止するようにしていたので赤外線測距センサがついていますが、あまり関係ないです。
モーターの駆動電源は裏側の単三乾電池になります。
ソースコード
#include <IRremote.h>
int speed = 250;
int RecvPin = 11;
IRrecv irrecv(RecvPin);
decode_results results ;
#define Right_IN1 7
#define Right_IN2 8
#define Right_VREF 9
#define Left_IN1 3
#define Left_IN2 4
#define Left_VREF 5
#define FD 0x214A56A9 //forward
#define BC 0x214A3EC1 //back
#define LF 0x214A22DD //reft forward
#define LB 0x214A06F9 //reft back
#define RF 0x214A0AF5 //right forward
#define RB 0x214A2ED1 //right back
#define BRAKE 0x214A2CD3 //bBrake
void forward() {
Serial.println("前進");
digitalWrite(Right_IN1,HIGH);
digitalWrite(Right_IN2,LOW);
digitalWrite(Left_IN1,HIGH);
digitalWrite(Left_IN2,LOW);
}
void back() {
Serial.println("後進");
digitalWrite(Right_IN1,LOW);
digitalWrite(Right_IN2,HIGH);
digitalWrite(Left_IN1,LOW);
digitalWrite(Left_IN2,HIGH);
}
void left_forward() {
Serial.println("左前進");
digitalWrite(Right_IN1,LOW);
digitalWrite(Right_IN2,LOW);
digitalWrite(Left_IN1,HIGH);
digitalWrite(Left_IN2,LOW);
}
void left_back() {
Serial.println("左後進");
digitalWrite(Right_IN1,LOW);
digitalWrite(Right_IN2,LOW);
digitalWrite(Left_IN1,LOW);
digitalWrite(Left_IN2,HIGH);
}
void right_forward() {
Serial.println("右前進");
digitalWrite(Right_IN1,HIGH);
digitalWrite(Right_IN2,LOW);
digitalWrite(Left_IN1,LOW);
digitalWrite(Left_IN2,LOW);
}
void right_back() {
Serial.println("右後進");
digitalWrite(Right_IN1,LOW);
digitalWrite(Right_IN2,HIGH);
digitalWrite(Left_IN1,LOW);
digitalWrite(Left_IN2,LOW);
}
void Brake() {
Serial.println("停止");
digitalWrite(Right_IN1,HIGH);
digitalWrite(Right_IN2,HIGH);
digitalWrite(Left_IN1,HIGH);
digitalWrite(Left_IN2,HIGH);
}
void setup() {
Serial.begin(9600);
irrecv.enableIRIn();
pinMode(Right_IN1,OUTPUT);
pinMode(Right_IN2,OUTPUT);
pinMode(Right_VREF,OUTPUT);
pinMode(Left_IN1,OUTPUT);
pinMode(Left_IN2,OUTPUT);
pinMode(Left_VREF,OUTPUT);
analogWrite(Right_VREF,speed);
analogWrite(Left_VREF,speed);
}
void loop() {
if (irrecv.decode(&results)) {
if (results.value == FD ) {
forward();
}
if (results.value == BC ) {
back();
}
if (results.value == LF ) {
left_forward();
}
if (results.value == LB ) {
left_back();
}
if (results.value == RF ) {
right_forward();
}
if (results.value == RB ) {
right_back();
}
if (results.value == BRAKE) {
Brake();
}
irrecv.resume();
}
delay(1);
}
前章でLEDの点灯/消灯を制御した時と、基本的にやっていることに変わりはありません。
モータードライバの仕様に合わせて、どのピンをHIGHに、またはLOWにするかの組み合わせによって、前進、後進、右旋回、左旋回、ブレーキの動作を実現しています。
ハマったところ
今回、私が使用した赤外線受信モジュールは、Amazonに出回っている中華製のコピー品で、
赤外線が正常に読み取れないものがいくつか混ざっていました。
はずれのセンサを実装した際に、正常にラジコンが動作しなかったため、コードと2時間もにらめっこをしてしまいました。
リモコンの動作をうまく受け取れないなどの不具合があるときは、コードよりもセンサーを疑った方がいいかもしれません(笑)
その後
照明のリモコンでラジコンを操作すると、
部屋の電気が点いたり消えたりで鬱陶しいため、
ジャンク品のリモコンで操作できるように作り直したりなどしました。