Arduino測距儀とデジタル水準器の製作─ わかりやすいステップバイステップガイド
このチュートリアルでは、Arduino測距儀とデジタル水準器の作り方を学びます。以下の動画をご覧いただくか、書面によるチュートリアルをお読みください。 本デバイスは、超音波センサーで最寄り物体までの距離を測定し、加速度計で地面に対する角度を測定、LCDディスプレイで結果を表示、すべての部品をカスタム設計PCBに実装しています。 単一ボタンで操作可能。電源投入後、まず測定単位を選択します。 ボタン押下で単位を切り替え、長押し(約0.5秒)で最初のプログラム(距離測定)に入ります。ここでは距離測定と直前の2回分の測定値保存が可能です。 再度長押しで2番目のプログラム(面積測定)に入り、直交する2つの距離を測って正方形の面積を算出します。 3番目のプログラムはデジタル水準器で、地面に対する角度を測定します。 ボタンで2軸(ピッチ/ロール)を切り替えて測定可能です。 このArduinoプロジェクトの回路図は以下の通り。 各モジュールの詳細チュートリアルは以下をご覧ください:超音波センサーチュートリアル、LCDチュートリアル、MEMS加速度計チュートリアル。 必要なコンポーネント: 回路図に基づきEasyEDAでカスタムPCBを設計。 設計完了後、ガーバーファイルをエクスポート。このプロジェクトのEasyEDAファイルはこちらで確認できます。 JLCPCB(このプロジェクトのスポンサー)でPCBを注文。 ガーバーファイルをドラッグ&ドロップでアップロード、ビューアーで確認。問題なければ仕様を選んで注文。初回なら最大10枚まで2ドル。 数日後、PCB到着。品質は素晴らしく、設計通りの仕上がり。 電子部品の組み立て開始。ピンヘッダーをPCBにはんだ付け(着脱容易にするため)。 3つの抵抗を実装。2つはLCDコントラスト用分圧抵抗(R1に1kΩ、R2に220Ω)、1つはボタン用プルアップ抵抗。 ケースは透明アクリルで製作(電子部品の美しさを可視化)。手元にあった5mm厚アクリルを丸ノコでカット。 上面パネル(4mm厚、LCDにフィットしやすいため)にLCD用開口部を加工。ドリルで2穴を開け、糸ノコで大まかに切り出し、ヤスリで整えてLCDがピタリとはまるようにしました。 フォースナービットで電源スイッチ、操作ボタン、超音波センサー用の開口部を加工。 5分硬化型エポキシでケース組み立て。上面はボルト2本を挿入・接着し、上面パネルをナットで固定できるようにしました。 LCD、電源スイッチ、ボタン、バッテリーコネクターにピンヘッダー/ジャンパーワイヤーをはんだ付け。 最終組み立て:超音波センサーをPCBと側面パネルの穴に挿入、Arduinoボード、加速度計モジュール、バッテリーコネクターを実装。 上面パネルにLCD、電源スイッチ、ボタンを固定してPCBに接続。最後に9V電池をコネクターに接続し、上面パネルをナットで固定。 ハードウェア完成。あとはプログラムの仕組みを解説します。 コードが長いため、セクションごとに説明を加え、最後に完全なソースコードを掲載します。 まずWire.h(加速度計のI2C通信用)とLiquidCrystal.h(LCD制御用)をインクルード。LCDオブジェクト、MPU6050のI2Cアドレス、超音波センサーピン、プログラム用変数を定義。 setup()では加速度計のI2CインターフェースとLCDを初期化、超音波センサーのトリガー/エコーピンとボタンピンのモードを設定。 メインループではswitch文でプログラムを切り替え。case 0で測定単位を選択。lcd.print()でテキスト表示、if文で4つの単位を切り替え。 unitSelect変数はgetDistance()カスタム関数で機能し、超音波センサーから得られる基本単位(cm)をどの単位に変換するかを指示します。 ボタンを0.5秒以上長押しするとプログラム1(距離測定)に移行。 case 1(距離測定プログラム)ではgetDistance()カスタム関数で最寄り物体までの距離を取得。 getDistance()関数の仕組み: トリガーピンで超音波パルスを発信、エコーピンとpulseIn()で往復時間を測定、音速から距離を算出。LCDに距離とテキストを表示、ボタン押下で直前2回の測定値を保存・表示。 case 2(面積測定プログラム)では同様の方法で直交する2距離を測定し、乗算で面積を算出。 case 3(角度測定プログラム)。加速度計データ(地球重力場のX,Y,Z各軸成分)を読み取り。各軸の値は2レジスタに格納されるため、計6レジスタを読み取って結合。 X,Y,Z値からピッチ角(Y軸周り回転)とロール角(X軸周り回転)を算出。計算式の詳細はFreescale Semiconductorアプリケーションノート「3軸加速度計を使用した傾斜検知」を参照。LCDに値とテキストを表示、ボタンでピッチ/ロールを切り替え。 このArduinoプロジェクトをお楽しみいただけたら幸いです。ご質問はコメント欄でお気軽にお尋ねください。概要




Arduino測距儀の回路図

カスタム設計PCB



デバイスの組み立て









Arduino測距儀とデジタル水準器のソースコード
#include <Wire.h> // I2C通信ライブラリ
#include <LiquidCrystal.h> // LiquidCrystalライブラリ
LiquidCrystal lcd(7, 6, 5, 4, 3, 2); // LCDオブジェクト (rs, enable, d4, d5, d6, d7)
const int MPU = 0x68; // MPU6050加速度計のI2Cアドレス
#define trigPin 8
#define echoPin 9
#define selectButton 10
int16_t AcX, AcY, AcZ;
long duration;
float distance;
int program = 0;
float d = 0, d1 = 0, d2 = 0, area = 0;
int axis = 0, angle = 0;
int unitSelect = 0;
String unit = "cm";void setup() {
Wire.begin();
Wire.beginTransmission(MPU);
Wire.write(0x6B);
Wire.write(0);
Wire.endTransmission(true);
lcd.begin(16, 2);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
pinMode(selectButton, INPUT_PULLUP);
}switch (program) {
case 0: // 測定単位選択
lcd.setCursor(0, 0);
lcd.print("Select Unit: ");
lcd.setCursor(13, 0);
lcd.print(unit);
lcd.print(" ");
delay(10);
if (digitalRead(selectButton) == 0) {
if (unitSelect == 0) { unit = "in"; unitSelect = 1; }
else if (unitSelect == 1) { unit = "m"; unitSelect = 2; }
else if (unitSelect == 2) { unit = "ft"; unitSelect = 3; }
else if (unitSelect == 3) { unit = "cm"; unitSelect = 0; }
// 長押しでプログラム切り替え
delay(500);
if (digitalRead(selectButton) == 0) {
program = 1;
lcd.clear();
delay(500);
}
}
break;// 単位変換
if (unitSelect == 1) { distance = distance; unit = "cm"; } // cm→cm
else if (unitSelect == 2) { distance = distance * 0.393701; unit = "in"; } // cm→inch
else if (unitSelect == 3) { distance = distance * 0.01; unit = "m"; } // cm→m
else if (unitSelect == 0) { distance = distance * 0.0328; unit = "ft"; } // cm→ftdelay(500);
if (digitalRead(selectButton) == 0) {
program = 1;
lcd.clear();
delay(500);
}
break;case 1: // 距離測定プログラム
distance = getDistance();
lcd.setCursor(0, 0);
lcd.print("Dist: ");
lcd.print(distance);
lcd.print(" ");
lcd.setCursor(14, 0);
lcd.print(unit);
delay(10);
lcd.setCursor(0, 1);
lcd.print("d:");
lcd.setCursor(8, 1);
lcd.print("d:");
delay(200);
// 測定値1を保存
if (digitalRead(selectButton) == 0) {
if (d == 0) {
lcd.setCursor(0, 1);
lcd.print("d: ");
lcd.setCursor(2, 1);
lcd.print(distance);
d = 1;
delay(100);
}
// 測定値2を保存
else if (d == 1) {
lcd.setCursor(8, 1);
lcd.print("d: ");
lcd.setCursor(10, 1);
lcd.print(distance);
d = 0;
delay(100);
}
// 長押しでプログラム切り替え
delay(500);
if (digitalRead(selectButton) == 0) {
program = 2;
d = 0;
lcd.clear();
delay(500);
}
}
break;//===== getDistance - カスタム関数
float getDistance() {
digitalWrite(trigPin, LOW);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
duration = pulseIn(echoPin, HIGH);
distance = duration * 0.034 / 2; // 距離(cm)
// 単位変換
if (unitSelect == 1) { distance = distance; unit = "cm"; }
else if (unitSelect == 2) { distance = distance * 0.393701; unit = "in"; }
else if (unitSelect == 3) { distance = distance * 0.01; unit = "m"; }
else if (unitSelect == 0) { distance = distance * 0.0328; unit = "ft"; }
return distance;
}
case 2: // 面積測定プログラム
distance = getDistance();
lcd.setCursor(0, 0);
lcd.print("Area: ");
lcd.print(area);
lcd.setCursor(12, 0);
lcd.print(unit);
lcd.print("^2");
delay(200);
if (d == 0) { lcd.setCursor(0, 1); lcd.print("d1: "); lcd.setCursor(3, 1); lcd.print(distance); delay(200); }
else if (d == 1) { lcd.setCursor(9, 1); lcd.print("d2: "); lcd.setCursor(12, 1); lcd.print(distance); delay(200); }
else if (d == 2) { lcd.setCursor(6, 0); lcd.print(area); delay(200); }
if (digitalRead(selectButton) == 0) {
if (d == 0) { lcd.setCursor(0, 1); lcd.print("d1: "); lcd.setCursor(3, 1); lcd.print(distance); d = 1; d1 = distance; delay(100); }
else if (d == 1) { lcd.setCursor(9, 1); lcd.print("d2: "); lcd.setCursor(12, 1); lcd.print(distance); d = 2; d2 = distance; area = d1 * d2; delay(100); }
else if (d == 2) { lcd.clear(); d = 0; area = 0; delay(100); }
delay(500);
if (digitalRead(selectButton) == 0) { program = 3; d = 0; lcd.clear(); delay(500); }
}
break;case 3: // 角度測定プログラム
Wire.beginTransmission(MPU);
Wire.write(0x3B); // レジスタ0x3B (ACCEL_XOUT_H) から開始
Wire.endTransmission(false);
Wire.requestFrom(MPU, 6, true); // 計6レジスタ読取
AcX = Wire.read() << 8 | Wire.read(); // X軸
AcY = Wire.read() << 8 | Wire.read(); // Y軸
AcZ = Wire.read() << 8 | Wire.read(); // Z軸
if (axis == 0) {
// ピッチ角計算(Y軸周りの回転)
angle = atan(-1 * AcX / sqrt(pow(AcY, 2) + pow(AcZ, 2))) * 180 / PI;
lcd.setCursor(0, 0);
lcd.print("Pitch");
} else if (axis == 1) {
// ロール角計算(X軸周りの回転)
angle = atan(-1 * AcY / sqrt(pow(AcX, 2) + pow(AcZ, 2))) * 180 / PI;
lcd.setCursor(0, 0);
lcd.print("Roll ");
}
lcd.setCursor(0, 1);
lcd.print("Angle: ");
lcd.print(abs(angle));
lcd.print(" ");
lcd.setCursor(10, 1);
lcd.print("deg");
delay(200);
// 軸切り替え
if (digitalRead(selectButton) == 0) {
if (axis == 0) { axis = 1; delay(100); }
else if (axis == 1) { axis = 0; delay(100); }
// 長押しでプログラム0に戻る
delay(500);
if (digitalRead(selectButton) == 0) { program = 0; lcd.clear(); delay(500); }
}
break;
}完全なソースコード
/* DIY Digital Range Measurer and Spirit Level
by Dejan Nedelkovski, www.HowToMechatronics.com */
#include <Wire.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 6, 5, 4, 3, 2);
const int MPU = 0x68;
#define trigPin 8
#define echoPin 9
#define selectButton 10
int16_t AcX, AcY, AcZ;
long duration;
float distance;
int program = 0;
float d = 0, d1 = 0, d2 = 0, area = 0;
int axis = 0, angle = 0;
int unitSelect = 0;
String unit = "cm";
void setup() {
Wire.begin();
Wire.beginTransmission(MPU);
Wire.write(0x6B);
Wire.write(0);
Wire.endTransmission(true);
lcd.begin(16, 2);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
pinMode(selectButton, INPUT_PULLUP);
}
void loop() {
switch (program) {
case 0:
lcd.setCursor(0, 0);
lcd.print("Select Unit: ");
lcd.setCursor(13, 0);
lcd.print(unit);
lcd.print(" ");
delay(10);
if (digitalRead(selectButton) == 0) {
if (unitSelect == 0) { unit = "in"; unitSelect = 1; }
else if (unitSelect == 1) { unit = "m"; unitSelect = 2; }
else if (unitSelect == 2) { unit = "ft"; unitSelect = 3; }
else if (unitSelect == 3) { unit = "cm"; unitSelect = 0; }
delay(500);
if (digitalRead(selectButton) == 0) { program = 1; lcd.clear(); delay(500); }
}
break;
case 1:
distance = getDistance();
lcd.setCursor(0, 0);
lcd.print("Dist: ");
lcd.print(distance);
lcd.print(" ");
lcd.setCursor(14, 0);
lcd.print(unit);
delay(10);
lcd.setCursor(0, 1);
lcd.print("d:");
lcd.setCursor(8, 1);
lcd.print("d:");
delay(200);
if (digitalRead(selectButton) == 0) {
if (d == 0) { lcd.setCursor(0, 1); lcd.print("d: "); lcd.setCursor(2, 1); lcd.print(distance); d = 1; delay(100); }
else if (d == 1) { lcd.setCursor(8, 1); lcd.print("d: "); lcd.setCursor(10, 1); lcd.print(distance); d = 0; delay(100); }
delay(500);
if (digitalRead(selectButton) == 0) { program = 2; d = 0; lcd.clear(); delay(500); }
}
break;
case 2:
distance = getDistance();
lcd.setCursor(0, 0);
lcd.print("Area: ");
lcd.print(area);
lcd.setCursor(12, 0);
lcd.print(unit);
lcd.print("^2");
delay(200);
if (d == 0) { lcd.setCursor(0, 1); lcd.print("d1: "); lcd.setCursor(3, 1); lcd.print(distance); delay(200); }
else if (d == 1) { lcd.setCursor(9, 1); lcd.print("d2: "); lcd.setCursor(12, 1); lcd.print(distance); delay(200); }
else if (d == 2) { lcd.setCursor(6, 0); lcd.print(area); delay(200); }
if (digitalRead(selectButton) == 0) {
if (d == 0) { lcd.setCursor(0, 1); lcd.print("d1: "); lcd.setCursor(3, 1); lcd.print(distance); d = 1; d1 = distance; delay(100); }
else if (d == 1) { lcd.setCursor(9, 1); lcd.print("d2: "); lcd.setCursor(12, 1); lcd.print(distance); d = 2; d2 = distance; area = d1 * d2; delay(100); }
else if (d == 2) { lcd.clear(); d = 0; area = 0; delay(100); }
delay(500);
if (digitalRead(selectButton) == 0) { program = 3; d = 0; lcd.clear(); delay(500); }
}
break;
case 3:
Wire.beginTransmission(MPU);
Wire.write(0x3B);
Wire.endTransmission(false);
Wire.requestFrom(MPU, 6, true);
AcX = Wire.read() << 8 | Wire.read();
AcY = Wire.read() << 8 | Wire.read();
AcZ = Wire.read() << 8 | Wire.read();
if (axis == 0) {
angle = atan(-1 * AcX / sqrt(pow(AcY, 2) + pow(AcZ, 2))) * 180 / PI;
lcd.setCursor(0, 0);
lcd.print("Pitch");
} else if (axis == 1) {
angle = atan(-1 * AcY / sqrt(pow(AcX, 2) + pow(AcZ, 2))) * 180 / PI;
lcd.setCursor(0, 0);
lcd.print("Roll ");
}
lcd.setCursor(0, 1);
lcd.print("Angle: ");
lcd.print(abs(angle));
lcd.print(" ");
lcd.setCursor(10, 1);
lcd.print("deg");
delay(200);
if (digitalRead(selectButton) == 0) {
if (axis == 0) { axis = 1; delay(100); }
else if (axis == 1) { axis = 0; delay(100); }
delay(500);
if (digitalRead(selectButton) == 0) { program = 0; lcd.clear(); delay(500); }
}
break;
}
}
float getDistance() {
digitalWrite(trigPin, LOW);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
duration = pulseIn(echoPin, HIGH);
distance = duration * 0.034 / 2;
if (unitSelect == 1) { distance = distance; unit = "cm"; }
else if (unitSelect == 2) { distance = distance * 0.393701; unit = "in"; }
else if (unitSelect == 3) { distance = distance * 0.01; unit = "m"; }
else if (unitSelect == 0) { distance = distance * 0.0328; unit = "ft"; }
return distance;
}
製造プロセス
- Arduinoで作る模型鉄道DCCコマンドステーション 〜Bluetooth制御・無料アプリ付き〜
- Arduinoデジタルダイス: LCD表示の乱数ジェネレーター自作
- Arduino UNOで作るLCDアニメーション&ゲーム入門
- Arduinoオーディオ周波数検出器 – 高感度サウンドモジュールで最大音量ピークの周波数を測定
- Arduinoで作る自作デジタルペット「Tamaguino」─ 3Dプリンター製ケース付き完全ガイド
- Arduino三目並べ:MAX7219 LEDマトリクスと段ボールケースで自作
- ArduinoとNokia 5110ディスプレイで自作電圧計:ステップバイステップガイド
- SparkFun 7セグメントディスプレイで作る信頼性の高いArduinoカウントダウンタイマー
- Python 3からArduino UNOを制御 – シリアル通信でLED点滅デモ
- Arduino NanoとRDA5807Mで作るコンパクトFMラジオ
- DIY集水槽水位モニター - Arduino Nano、MPXV5010DP、OLEDディスプレイ