void setup() {
// MPU6050 인터페이스 초기화
Wire.begin();
Wire.beginTransmission(MPU);
Wire.write(0x6B);
Wire.write(0);
Wire.endTransmission(true);
lcd.begin(16, 2); // LCD 화면 인터페이스 초기화
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
pinMode(selectButton, INPUT_PULLUP);
}
메인 loop 섹션에서는 스위치 문을 사용해 장치의 여러 프로그램을 전환합니다. 첫 번째인 케이스 0에서는 측정 단위를 선택합니다. lcd.print() 함수로 LCD에 텍스트를 출력하고 if 문으로 네 가지 측정 단위를 전환합니다.
switch (program) {
// 서로 다른 프로그램 간 전환
case 0: // 측정 단위 선택
lcd.setCursor(0, 0); // LCD에 이후 텍스트가 표시될 위치 설정
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; }
// 버튼을 0.5초 이상 길게 누름 - 프로그램 변경
delay(500);
if (digitalRead(selectButton) == 0) {
program = 1;
lcd.clear();
delay(500);
}
}
break;
unitSelect 변수는 getDistance() 커스텀 함수에서 작동하는데, 초음파 센서에서 얻는 기본 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"; }
측정 단위를 선택하고 첫 번째 프로그램에 진입하려면 버튼을 0.5초 이상 길게 누르면 됩니다.
// 버튼을 0.5초 이상 길게 누름 - 프로그램 변경
delay(500);
if (digitalRead(selectButton) == 0) {
program = 1;
lcd.clear();
delay(500);
}
}
break;
케이스 1(거리 측정 프로그램)에서는 getDistance() 커스텀 함수로 가장 가까운 물체까지의 거리를 구합니다.
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);
}
// 버튼을 0.5초 이상 길게 누름 - 프로그램 변경
delay(500);
if (digitalRead(selectButton) == 0) {
program = 2;
d = 0;
lcd.clear();
delay(500);
}
}
break;
이 함수가 어떻게 작동하는지 봅시다.
//===== getDistance - 커스텀 함수
float getDistance() {
// trigPin 초기화
digitalWrite(trigPin, LOW);
// trigPin을 10마이크로초간 HIGH 상태로
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
// echoPin 읽기, 음파 왕복 시간(마이크로초) 반환
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;
}
트리거 핀으로 센서에 초음파 발생을 지시합니다.
에코 핀과 pulseIn() 함수로 음파가 센서에서 물체까지 왕복하는 시간을 측정합니다. 음속과 이동 시간을 알면 거리를 쉽게 계산할 수 있습니다. 측정된 거리와 텍스트를 LCD에 출력하고, 버튼을 누르면 마지막 두 측정값을 저장/출력합니다.
다음은 면적 계산 프로그램으로 비슷한 방식을 사용합니다. 두 번의 수직 측정을 하고 곱해 정사각형 면적을 구합니다.
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);
}
// 거리 1 저장
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);
}
// 거리 2 저장
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);
}
// 버튼을 0.5초 이상 길게 누름 - 프로그램 변경
delay(500);
if (digitalRead(selectButton) == 0) {
program = 3;
d = 0;
lcd.clear();
delay(500);
}
}
break;
마지막 케이스는 각도 측정 프로그램입니다. 가속도계 데이터(지구 중력장의 세기가 X, Y, Z 세 축으로 측정된 값)를 읽어야 합니다. 각 축 값은 2개 레지스터에 저장되므로 총 6개 레지스터를 읽고 결합해 올바른 값을 얻습니다.
case 3: // 각도 측정 프로그램
// 가속도계 데이터 읽기
Wire.beginTransmission(MPU);
Wire.write(0x3B); // 레지스터 0x3B(ACCEL_XOUT_H)부터 시작
Wire.endTransmission(false);
Wire.requestFrom(MPU, 6, true); // 총 6개 레지스터 읽기, 각 축 값은 2개 레지스터에 저장
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.5초 이상 길게 누름 - 프로그램 변경
delay(500);
if (digitalRead(selectButton) == 0) {
program = 0;
lcd.clear();
delay(500);
}
}
break;
X, Y, Z 가속도계 값을 얻으면 피치 각도(Y축 기준 회전)와 롤 각도(X축 기준 회전)를 두 수식으로 계산할 수 있습니다. 이 수식에 대한 자세한 내용은 Freescale Semiconductor 애플리케이션 노트 Tilt Sensing Using a Three-Axis Accelerometer를 참조하세요. 값을 텍스트와 함께 LCD에 출력하고, 버튼을 눌러 피치 또는 롤 각도 중 무엇을 표시할지 전환합니다.
이 아두이노 거리 측정기와 수평계 프로젝트의 전체 소스 코드:
/* DIY 디지털 거리 측정기 및 수평계 by Dejan Nedelkovski, www.HowToMechatronics.com */
#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;
float d1 = 0;
float d2 = 0;
float area = 0;
int axis = 0;
int 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;
}
}
//===== getDistance - 커스텀 함수
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;
}
이상으로 튜토리얼을 마칩니다. 이 아두이노 프로젝트를 즐기셨고 새로운 것을 배우셨기를 바랍니다. 아래 댓글 섹션에서 자유롭게 질문해 주세요.