Arduino と 超音波測距センサー HC-SR04 を使ってラジコン用のラップタイマーを自作しました。
ラップタイマーを自作しようとおもったきっかけは、父親がラジコンサーキットを自作し、ラップタイムが測定したいと言っていたことです。

作成したラップタイマー

ラップタイムの確認はLCD(液晶ディスプレイ)で行います。
液晶ディスプレイの上段には、センサーで検出している対象物までの距離と、ラップタイムを検出するための閾値を表示しています(詳細はあとで書いています)。
液晶ディスプレイの下段には、ラップタイムを表示しています。
ミニカーでテスト
上記のプロトタイプを、家の机の上でミニカーを使ってテストしました。
ちゃんとラップを測定できています。
室内用ラジコンでテスト
家の廊下で室内用ラジコンを使ってテストしました。
動画ではわかりにくいですが、きちんとラップタイムを測定できました。
ラジコンサーキットでテスト
父親のラジコンサーキットで実際にテストしてみました。
動画ではわかりにくいですが、きちんとラップタイムを測定できました。
使った部品
今回使用した部品は以下です。
部品 | 用途・備考 |
---|---|
Arduino Uno | |
HC-SR04 超音波距離センサー | ラジコン通過を検出するセンサー。使い方はこちら。 |
LCD1602 ディスプレイ モジュール | ラップタイムを表示する液晶ディスプレイ。 |
ロータリーエンコーダ モジュール | 距離センサーの閾値を変更する。 種類は何でもいいですが、ここではKY-040を使用。 |
可変抵抗器 10KΩ | LCDのコントラスト調整用。 |
ブレッドボード | |
ジャンパー線 | |
9V形アルカリ乾電池 | |
バッテリースナップ | |
部品を固定する木材 | ホームセンターで購入。約 150円。 |
木ねじ | 部品を固定する。ホームセンターで購入。数十本で約 100円。 |
全て、ばらばらに購入すると、約10,000円ほどかかってしまいます。
(部品によっては、複数個で売っているため、部品単価はもう少し安くなります。)
そこでおすすめなのがArduinoのスターターキットです。
いろいろな部品が入って、約4000円~6000円で購入することができます。
私は下記のスターターキットを購入して、初めてArduinoを使いました。
入っているすべてのパーツの配線図や、スケッチ(コード)が書いている説明書も入っているので電子工作の入門キットとして本当におすすめです。
上記のラップタイマーを作るにあたって、スターターキットに入っているもの以外で購入したのは、ホームセンターで購入した木材と木ねじだけです。
ラップタイマーの仕様
ラジコンの検出は超音波距離センサーで行う
ラジコンの通過の検出には超音波距離センサHC-SR04を使っています。
HC-SR04については以下の記事で詳しく書いています。

例えば、コースの幅が100cmである場合、距離センサの検出距離が、
- ラジコンが通過していない時は測定距離は100cm(コースの端には距離を測定するための物体をおきます)
- ラジコンが通過中は測定距離が100cm未満になる
ということを利用して、ラジコンの通過を検出しています。
LCD(液晶ディスプレイ)に表示する内容

液晶ディスプレイには、
- 検出距離(dis)
- 閾値(th)
- ラップタイム(raptime)
を表示しています。
検出距離(dis)は距離センサーで現在検出している距離を表示しています。
閾値(th)は、ラップタイムを測定するための閾値(距離)です。
この閾値よりも、検出距離(dis)が短い場合に、ラップタイムを計測します。
この閾値は、ロータリーエンコーダーで変更できるようにしています。

配線
配線はジャンパー線を使っているので、ごちゃごちゃに見えますが、内容はとてもシンプルです。
各部品のデータシート通りに、部品とArduinoを接続しています。
今回のラップタイマーはそれぞれの部品を別々にArduinoに接続して使えるので、下記ではそれぞれの部品とArduinoの接続について書いています。
HC-SR04(超音波距離センサ)
HC-SR04との配線の詳細はこちらに書いています。
上記の記事とは少し、配線を変更しています(他の部品を配線しやすくするため)。
HC-SR04 | Arduino UNO |
---|---|
Vcc | 5V |
Trig | D5 |
Echo | D6 |
GND | GND |


LCD(液晶ディスプレイ)
データシートはこちらです。
液晶のコントラスト調整用に10kΩの可変抵抗も接続しています。


ロータリーエンコーダー
距離の閾値を変更するために使用しています。


すべての部品を載せた場合の配線
上記のすべての部品をブレッドボードに載せた場合の配線は以下です。

スケッチ(コード)
今回使用したスケッチは以下です。
#include <LiquidCrystal.h>
//LCDピン
// BS E D4 D5 D6 D7
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
//HC-SR04
const int TRIG_PIN = 5; //HC-SR04 Trig
const int ECHO_PIN = 6; //HC-SR04 Echo
const unsigned int MAX_DIST = 23200; // Anything over 400 dis (23200 us pulse) is "out of range"
//ロータリーエンコーダー
#define PIN_A 2
#define PIN_B 3
const int8_t ENCODER_TABLE[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
volatile bool StatePinA = 1;
volatile bool StatePinB = 1;
volatile uint8_t State = 0;
volatile long Count = 0;
int i = 0; // ラップタイムの計測初回無視用
int dis_th; // タップタイマ測定用距離の閾値
float rap = 0; // ラップタイム
unsigned long prev, interval, curr; // タイマー用
void setup() {
lcd.begin(16, 2);
//測定距離が閾値以下の場合にLED13を点灯する
pinMode(13, OUTPUT);
//HC-SR04
pinMode(TRIG_PIN, OUTPUT);
digitalWrite(TRIG_PIN, LOW);
pinMode(ECHO_PIN, INPUT);
//ロータリーエンコーダー
pinMode(PIN_A, INPUT_PULLUP);
pinMode(PIN_B, INPUT_PULLUP);
attachInterrupt(0, ChangePinAB, CHANGE);
attachInterrupt(1, ChangePinAB, CHANGE);
prev = 0; // 前回実行時刻を初期化
interval = 5000; // ラップ測定 無視時間(msec)
dis_th = 100; // 距離の閾値(初期値)
}
void loop() {
unsigned long t1;
unsigned long t2;
unsigned long t3;
unsigned long pulse_width;
int dis;
//距離の閾値をLCDに表示
lcd.setCursor(10, 0);
lcd.print(" ");
lcd.setCursor(10, 0);
lcd.print("th:");
lcd.print(dis_th);
// Hold the trigger pin high for at least 10 us
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
// Wait for pulse on echo pin
while ( digitalRead(ECHO_PIN) == 0 );
// Measure how long the echo pin was held high (pulse width)
// Note: the micros() counter will overflow after ~70 min
t1 = micros();
while ( digitalRead(ECHO_PIN) == 1);
t2 = micros();
pulse_width = t2 - t1;
t3 = millis(); //前回のパルスを出力してから60ms以降にパルスを出すため。
// Calculate distance in centimeters and inches. The constants
// are found in the datasheet, and calculated from the assumed speed
//of sound in air at sea level (~340 m/s).
dis = pulse_width / 58.0;
// Print out results
if ( pulse_width > MAX_DIST ) {
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(0, 0);
lcd.print("out range");
} else {
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(0, 0);
lcd.print("dis:");
lcd.print(dis);
lcd.print("cm");
}
if(dis < dis_th) {
digitalWrite(13, HIGH);
curr = millis(); // 現在時刻を取得
if ((curr - prev) >= interval && i > 0) { // 前回実行時刻から実行周期以上経過していたら
rap = (curr - prev) / 1000.00; // ラップタイム(秒)算出。1000.00で割ると小数点以下が出る。
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print("raptime:");
lcd.print(rap);
lcd.print("sec");
prev = curr; // 前回実行時刻を現在時刻で更新
} else if (i == 0) { // 初回はラップタイムを算出しない
prev = curr;
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print("rap start!!");
i++;
} else {
// 何もしない
}
} else {
digitalWrite(13, LOW);
}
delay(10);
// Wait at least 60ms before next measurement
// while( millis() - t3 < 60 ){
// HC-SR04のデータシート記載の待ち時間
// }
}
//ロータリーエンコーダーの関数。距離の閾値を更新する。
void ChangePinAB(){
StatePinA = PIND & 0b00000100;
StatePinB = PIND & 0b00001000;
State = (State<<1) + StatePinA;
State = (State<<1) + StatePinB;
State = State & 0b00001111;
dis_th += ENCODER_TABLE[State]*2.5;
}
HC-SR04
HC-SR04に関するコードの詳細は、こちらの記事を参考にしてください。
LCD
LCDはLiquidCrystal
というライブラリを使用して、簡単に使えます。
使い方はArduinoの公式ドキュメント、もしくは日本語リファレンスを参考にしてください。
ロータリーエンコーダー
ロータリーエンコーダーは、距離の閾値を変更するために付けています。
コースの幅や、環境によって調整可能です。
ロータリーエンコーダーのコードは、こちらの記事を参考にさせていただきました。
測定無視時間
上記のコードの44行目では、ラジコンの通過を検出してから、次の検出をスタートするまでの検出停止時間を定数で入れています。
このようにしておかないと、ラジコンが通過中に何度もラップを測定するような不具合が起きてしまいます。
今回は 5秒 で設定しています。
interval = 5000; // ラップ測定 無視時間(msec)
問題点
超音波距離センサを仕様通りに使っていない
HC-SR04は一度距離を測定してから、次に距離を測定するまでに 60msec 間隔を空けるようにデータシートに書いています。
最初は仕様通りにコードを書いていたのですが(上記のコードにはコメントアウトして残しています、128 – 130 行目)、ラップが検出できる場合と、できない場合がありました。
原因は60msecが経過するのを待つ間はラップタイマを検出できないタイミングになっているので、その間にラジコンが通過すると、ラップが検出できないということになっていました。
たとえば、ラジコンの車体の長さが 30cm、時速が 15km であるとすると、車体はセンサ部分を 72msec で通過することになります。
ラジコンの車体の長さが 30cm、時速が 30km であるとすると、車体はセンサ部分を 36msec で通過することになります。
ラップタイムを測定できない 60msec の間に、ラジコンが通過してしまうことは十分に考えられます。
対策として、データシートは無視してしまいますが、ループの最後で 10msec だけdelay
でプログラムを止めています。
何回もテストしましたが、ラップタイムの測定は正常にできました。
ラップは1台しか測定できない
私の使い方では、1台しか測定しないのでこれで十分ですが、2台以上測定が必要な場合は仕様の再検討が必要です。
2台以上測定するためには、それぞれの車体の識別を出来るようにしなければいけません。
次やること : 音声出力
父親からの初めのリクエストでは、ラップタイムは大きめの 7seg に表示してほしいということだったのですが、ラジコンの運転中は目が離せないため、音声でラップが分かるようにしてほしいと言われました。
次は、ラップの音声出力にチャレンジします。