秋月電子通商で売っているオプトサプライ赤外線リモコンと受信モジュールを使ってワイルドミニ四駆を動かす工作の作り方です。
まずは厚さ1mmのプラ板でシャーシを作成します。ワイルドミニ四駆のボディーとサーボモーター(SG92R)をねじ止めできるように穴を開けます。穴の位置は細かく計算するより現物合わせで開けました。
厚さ5mm、幅20mmのバルサ材を下記のように穴を開けて切り出し、木工ボンドでコの字型に組み立ててシャフトを通します。最終的にステアリング機構のタイロッド的な役割を果たします。タイヤの内側には内径3mmのパイプを5mm程度にカットしたスペーサーを入れます。
高さを調節するために3mmと5mmのバルサ材を重ねた上に、タミヤのミニモーター低速ギヤボックス(ギヤ比は71.4)を後部に両面テープで貼り付けます。同時に、サーボホーンに両面テープで前の手順で作ったものを貼り付けます。
ブレッドボードにArduino NANO互換品(ELEGOO製)とモータードライバー(DRV8835)を挿して配線します。Arduinoは必要なピンだけにハンダ付けをして、空いたスペースを有効活用します。ブレッドボードにもともとあった穴を利用して、2か所をプラ板シャーシにM3ネジで固定しました。
配線は下記のように接続しました。狭い場所ではジャンパーワイヤーでボードの空き部分を使えるように配線しました。この他に効果不明ですが、レギュレーター間とモーター間にコンデンサを挿しています。三端子レギュレーターは5V用です。
接続元 | 接続先 |
---|---|
9V電池プラス | 三端子レギュレータ、ArduinoVIN |
9V電池マイナス | 三端子レギュレータ、ArduinoGND |
三端子レギュレータ5V出力 | サーボモーター電源 |
Arduino5V | 受信モジュール電源 |
3番ピン | サーボモーター制御 |
5番ピン | モータードライバーAIN1 |
6番ピン | モータードライバーAIN2 |
11番ピン | 受信モジュールからの入力 |
GND | サーボモーター、受信モジュール |
3Vリチウム電池プラス | モータードライバー電源、モーター電源 |
3Vリチウム電池マイナス | モータードライバーGND |
モータードライバーAOUT1 | モーター |
モータードライバーAOUT2 | モーター |
10mmの角材と5mmのバルサ材でブレッドボードの配線部分をまたぐ台を作成します。この上に赤外線受信モジュールとモーター用電池を載せます。しかし、厚さ5mmのバルサ材では高くて電池がボディー天井に干渉することが最後の段階で判明し、彫刻刀で削りました。
モーター用電池は小型のリチウム電池CR2(3V)を使います。電池ボックスと受信モジュールを挿した小さなブロックのブレッドボードを台に両面テープで貼り付けて配線します。このとき、ボディー天井の穴から受信モジュールが出てくるように位置を合わせます。
Arduinoとサーボ用9V電池はシャーシの裏に両面テープで貼り付けます。後輪タイヤの内側に内径3mmのパイプを5mm程度にカットしたスペーサーを入れてボディーをかぶせれば完成です(写真では、ボディーの上につくライト?部品をつけ忘れてます)。
スケッチは下記のとおりです。
#include <Servo.h>
#define srvPin 3
#define motor1 5
#define motor2 6
#define infPin 11
Servo srv;
void setup() {
srv.attach(srvPin); // サーボモーター
pinMode(motor1, OUTPUT); // モーター出力1
pinMode(motor2, OUTPUT); // モーター出力2
pinMode(infPin, INPUT); // 赤外線受信
Serial.begin(115200);
Serial.println("Serial Start.");
}
void loop() {
int code, code_mae, angle;
// 最初に誤作動防止のため、サーボを切り離してモーターを停止状態にする
code_mae = 99;
angle = 85;
srv.detach();
delay(200);
analogWrite(motor1, 0);
analogWrite(motor2, 0);
while(1){
code = readData(code_mae);
delay(100); // 少し間を開ける
code_mae = code;
Serial.println(String(code, HEX));
switch(code){
case 0xa0: // ↑:前進
analogWrite(motor1, 250);
analogWrite(motor2, 0);
break;
case 0x0: // ↓:後退
analogWrite(motor1, 0);
analogWrite(motor2, 250);
break;
case 0x10: // ←:左に曲がる
if(angle != 70){
angle = 70;
srv.attach(srvPin);
srv.write(angle);
delay(200);
srv.detach();
}
break;
case 0x80: // →:右に曲がる
if(angle != 100){
angle = 100;
srv.attach(srvPin);
srv.write(angle);
delay(200);
srv.detach();
}
break;
case 0x20: // セレクト:サーボ真ん中へ
if(angle != 85){
angle = 85;
srv.attach(srvPin);
srv.write(angle);
delay(200);
srv.detach();
}
break;
case 0xd8: // 電源:停止
analogWrite(motor1, 0);
analogWrite(motor2, 0);
break;
}
}
}
// 赤外線データの読み取りとデータコード決定
int readData(int code_mae) {
int code;
unsigned long int start, ltime, htime;
while(digitalRead(infPin) == HIGH){;} // 未送信状態のHIGH。読み飛ばし
start = micros();
while(digitalRead(infPin) == LOW){;}
ltime = micros() - start;
if(ltime > 8000){
start = micros();
while(digitalRead(infPin) == HIGH){;}
htime = micros() - start;
if(htime > 4000){ // リーダーコードなら以降のデータを読み込み
code = getCode();
} else if(htime > 2000){ // リピートコードの場合
code = code_mae;
} else { // 読み取りエラーの可能性
Serial.println("先頭部分でリーダーコードでもなくリピートコードでもない");
}
} else { // 想定外の場合
Serial.print("ltime <= 8000 : ");
Serial.println(ltime);
}
while(digitalRead(infPin) == LOW){;} // ストップビットを読み取り
return(code);
}
// 赤外線データを読み取ってデータコード8ビット(反転ビット含まず)を返す
int getCode(){
int i, j, code;
int data[40];
unsigned long int start, len;
j = 0;
for(i=0; i<32; i++){
start = micros();
while(digitalRead(infPin) == LOW){;}
while(digitalRead(infPin) == HIGH){;}
len = micros() - start;
if(i > 15 && i < 24){
if(len > 2000){ // LOWとHIGHの合計時間で0と1を判断
bitSet(code, j);
}
j++;
}
}
return(code);
}