プロジェクト15:IRリモコンタンク

(1)説明:

赤外線リモコンは、電気モーター、電気ファン、その他多くの家電製品に最もよく使われるリモコンの一つです。このプロジェクトでは、これまでに学んだ知識を活用して、赤外線リモコンスマートカーを作成します。

第9レッスンでは、赤外線リモコンの各キーに対応するキー値をテストしました。このプロジェクトでは、コード(キー値)を設定して、対応するボタンでスマートカーの動きを制御し、8X16 LEDドットマトリクスに動作パターンを表示させることができます。

ライントラッキングスマートカーの具体的なロジックを以下の表に示します:

超音波キー

キー値

キーからの指示

image-20230427094710725

FF629D

前進(PWMを200に設定)
前進パターンを表示

image-20230427094716639

FFA857

後退(PWMを200に設定)
後退パターンを表示

image-20230427094720417

FF22DD

左折
「STOP」パターンを表示

image-20230427094725151

FFC23D

右折
左折パターンを表示

image-20230427094729839

FF02FD

停止
「STOP」パターンを表示

初期設定:8X16 LEDドットマトリクスに「」のパターンを表示します。

(2)フローチャート:

(3)接続図:

注意:

8x16 LEDパネルのGND、VCC、SDA、SCLは、拡張ボードのG(GND)、V(VCC)、SDA、SCLに接続されています。

8833ボードにはIR受信機が内蔵されているため、配線する必要はありません。IR受信機のピンはG(GND)、V(VCC)、D3です。

(4)テストコード:

注意:コードをアップロードする前にBluetoothモジュールを接続しないでください。コードのアップロードもシリアル通信を使用しており、Bluetoothシリアル通信と競合が発生し、アップロードが失敗する可能性があります。)

/*
 Keyestudio Mini Tank Robot V3 (Popular Edition)
 lesson 15
 IRremote Control Tank
 http://www.keyestudio.com
*/
#include <IRremote.h>
IRrecv irrecv(3);  //
decode_results results;
long ir_rec;  //受信した赤外線値を格納するために使用

//配列、画像データを保存するために使用。自分で計算するかモジュールツールから取得できます
unsigned char start01[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
unsigned char front[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x12, 0x09, 0x12, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
unsigned char back[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x48, 0x90, 0x48, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
unsigned char left[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x28, 0x10, 0x44, 0x28, 0x10, 0x44, 0x28, 0x10, 0x00};
unsigned char right[] = {0x00, 0x10, 0x28, 0x44, 0x10, 0x28, 0x44, 0x10, 0x28, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
unsigned char STOP01[] = {0x2E, 0x2A, 0x3A, 0x00, 0x02, 0x3E, 0x02, 0x00, 0x3E, 0x22, 0x3E, 0x00, 0x3E, 0x0A, 0x0E, 0x00};
unsigned char clear[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
#define SCL_Pin  A5  //クロックピンをA5に設定
#define SDA_Pin  A4  //データピンをA4に設定

#define ML_Ctrl 4  //左モーターの方向制御ピンを定義
#define ML_PWM 6   //左モーターのPWM制御ピンを定義
#define MR_Ctrl 2  //右モーターの方向制御ピンを定義
#define MR_PWM 5    //右モーターのPWM制御ピンを定義

void setup() 
{
  Serial.begin(9600);
  irrecv.enableIRIn();  //赤外線受信ライブラリを初期化

  pinMode(ML_Ctrl, OUTPUT);
  pinMode(ML_PWM, OUTPUT);
  pinMode(MR_Ctrl, OUTPUT);
  pinMode(MR_PWM, OUTPUT);

  pinMode(SCL_Pin, OUTPUT);
  pinMode(SDA_Pin, OUTPUT);
  matrix_display(clear); //画面をクリア
  matrix_display(start01);  //開始時の画像を表示
}

void loop() 
{
  if (irrecv.decode(&results))  //赤外線リモコン値を受信
  {
    ir_rec = results.value;
    String type = "UNKNOWN";
    String typelist[14] = {"UNKNOWN", "NEC", "SONY", "RC5", "RC6", "DISH", "SHARP", "PANASONIC", "JVC", "SANYO", "MITSUBISHI", "SAMSUNG", "LG", "WHYNTER"};
    if (results.decode_type >= 1 && results.decode_type <= 13) 
    {
      type = typelist[results.decode_type];
    }
    Serial.print("IR TYPE:" + type + "  ");
    Serial.println(ir_rec, HEX);
    irrecv.resume();
  }

  switch (ir_rec) 
  {
    case 0xFF629D: Car_front();     break;   //前進コマンド
    case 0xFFA857: Car_back();      break;   //後退コマンド
    case 0xFF22DD: Car_T_left();    break;   //左折コマンド
    case 0xFFC23D: Car_T_right();   break;   //右折コマンド
    case 0xFF02FD: Car_Stop();      break;   //停止コマンド
    case 0xFF30CF: Car_left();      break;   //左回転コマンド
    case 0xFF7A85: Car_right();     break;   //右回転コマンド
    default: break;
  }
}

/***************モーターを動かす関数***************/
void Car_back() 
{
  digitalWrite(MR_Ctrl, LOW);
  analogWrite(MR_PWM, 200);
  digitalWrite(ML_Ctrl, LOW);
  analogWrite(ML_PWM, 200);
  matrix_display(back);  //後退
}

void Car_front() 
{
  digitalWrite(MR_Ctrl, HIGH);
  analogWrite(MR_PWM, 55);
  digitalWrite(ML_Ctrl, HIGH);
  analogWrite(ML_PWM, 55);
  matrix_display(front);  //前進画像を表示
}

void Car_left() 
{
  digitalWrite(MR_Ctrl, HIGH);
  analogWrite(MR_PWM, 55);
  digitalWrite(ML_Ctrl, LOW);
  analogWrite(ML_PWM, 200);
  matrix_display(left);  //左折画像を表示
}

void Car_right() 
{
  digitalWrite(MR_Ctrl, LOW);
  analogWrite(MR_PWM, 200);
  digitalWrite(ML_Ctrl, HIGH);
  analogWrite(ML_PWM, 55);
  matrix_display(right);  //右折画像を表示
}

void Car_Stop() 
{
  digitalWrite(MR_Ctrl, LOW);
  analogWrite(MR_PWM, 0);
  digitalWrite(ML_Ctrl, LOW);
  analogWrite(ML_PWM, 0);
  matrix_display(STOP01);  //停止画像を表示
}

void Car_T_left() 
{
  digitalWrite(MR_Ctrl, HIGH);
  analogWrite(MR_PWM, 0);
  digitalWrite(ML_Ctrl, HIGH);
  analogWrite(ML_PWM, 100);
  matrix_display(left);  //左折画像を表示
}

void Car_T_right() 
{
  digitalWrite(MR_Ctrl, HIGH);
  analogWrite(MR_PWM, 100);
  digitalWrite(ML_Ctrl, HIGH);
  analogWrite(ML_PWM, 0);
  matrix_display(right);  //右折画像を表示
}

//この関数はドットマトリクス画面の表示に使用
void matrix_display(unsigned char matrix_value[])
{
  IIC_start();  //データ転送開始条件の関数を呼び出す
  IIC_send(0xc0);  //アドレスを選択
  for (int i = 0; i < 16; i++) //パターンデータは16バイト
  {
    IIC_send(matrix_value[i]); //パターンデータを転送
  }
  IIC_end();   //パターンデータ転送を終了
  IIC_start();
  IIC_send(0x8A);  //表示制御、パルス幅を4/16に選択
  IIC_end();
}

//データ転送開始の条件
void IIC_start()
{
  digitalWrite(SDA_Pin, HIGH);
  digitalWrite(SCL_Pin, HIGH);
  delayMicroseconds(3);
  digitalWrite(SDA_Pin, LOW);
  delayMicroseconds(3);
  digitalWrite(SCL_Pin, LOW);
}

//データ送信終了のサイン
void IIC_end()
{
  digitalWrite(SCL_Pin, LOW);
  digitalWrite(SDA_Pin, LOW);
  delayMicroseconds(3);
  digitalWrite(SCL_Pin, HIGH);
  delayMicroseconds(3);
  digitalWrite(SDA_Pin, HIGH);
  delayMicroseconds(3);
}

//データを転送
void IIC_send(unsigned char send_data)
{
  for (byte mask = 0x01; mask != 0; mask <<= 1) //各文字は8桁あり、1つずつ検出される
  {
    if (send_data & mask)  //各ビット(0または1)に応じてHIGHまたはLOWレベルを設定
    {
      digitalWrite(SDA_Pin, HIGH);
    } 
    else 
    {
      digitalWrite(SDA_Pin, LOW);
    }
    delayMicroseconds(3);
    digitalWrite(SCL_Pin, HIGH); //クロックピンSCL_PinをHIGHに引き上げてデータ送信を停止
    delayMicroseconds(3);
    digitalWrite(SCL_Pin, LOW); //クロックピンSCL_Pinを引き下げてSDAの信号を変更
  }
}

(5)テスト結果:

コードをアップロードした後、モータードライブシールドの電源スイッチをオンにします。ロボットを床に置き、上の表を参照して異なるボタンを押すと、ロボットは対応するプリセットの方向に動きます。