4WD Bluetooth

4WD Bluetooth

지금까지 배운 RC카, 블루투스 모듈 사용법, 컨트롤러 사용법을 종합하여 Bluetooth로 조종하는 4WD를 만들어 보겠습니다. IR리모트를 사용한 RC카를 베이스로 하여 IR수신부를 빼고, 블루투스 모듈인 HC-06만 연결하면 됩니다.


schematic


Bluetooth Serial Controller 설정

여기서는 4WD를 블루투스로 조종하기 위한 콘트롤러를 만들어 봅니다.


  1. 먼저 Bluetooth Serial Controller를 실행시키세요. (블루투스 페어링이나 앱 설치방법은 이전의 글을 참고하세요.) 아래화면은 TERMINAL Mode인 상태입니다. 여기서 스페너 모양 아이콘을 눌러 PREFERCE로 진입하세요.

  1. PREFERENCE 화면에서 아무곳이나 누른 뒤, 위로 스크롤하여

  1. 9 BUTTON MODE를 활성화 시키고, TERMINAL MODE를 꺼주세요.

  1. 그러면 이런 화면이 나타납니다. 이제 각각의 버튼을 정의해보겠습니다.

  1. 다시 스페너 모양 아이콘을 눌러 PREFERENCE로 들어간 뒤, BUTTON-Name을 선택합니다.

  1. button2, button4, button5, button6, button8을 각각 누른 뒤, 각 버튼 별로 ‘전진’, ‘왼쪽’, ‘정지’, ‘오른쪽’, ‘후진’이라고 이름을 붙여줍니다.

  1. 다시 PREFERENCE에서 Command를 누르세요.

  1. Command에서 정의해주는 값이 제일 중요한데요. 각 버튼을 누를때, 아두이노로 전달되는 값을 정의해주는 것이기 때문입니다. button2, button4, button5, button6, button8을 각각 누른 뒤, 각 버튼 별로 ‘F’, ‘L’, ‘O’, ‘R’, ‘B’이라고 입력값을 넣어주세요. 이 값은 스케치 작성시 사용해야 하므로 꼭 기억해두어야 합니다.

  1. 이제 Visibility로 갑니다.

  1. 여기서는 필요없는 버튼을 지워줄 수 있어요. 실제 사용되는 버튼에만 체크하고, 나머지 버튼은 체크 해제합니다.

  1. 그러면, 아래와 같은 멋진 블루투스 컨트롤러가 나타납니다.

  1. 이 상태에서 돋보기 모양 아이콘을 눌러 HC-06에 접속해 봅니다. 여기서부터는 블루투스 페어링 과정에서 설명했던 부분과 똑같습니다.

  1. 모듈ID인 SKS100을 누르면 페어링을 시도하고요.

  1. 만일 페어링할 모듈이 나타나지 않으면, Scan for devices로 모듈을 찾아 페어링을 해봅니다.

  1. 페어링을 시도하는 중이네요.

  1. 페어링 완료! 이제 컨트롤러 사용을 위한 모든 준비과정이 완료되었습니다.


4WD 블루투스 초음파센서 자동차

위 회로에 초음파 센서 하나만 덧붙여봅니다. 50cm 이내에 물체가 접근하면 후진했다가 정지하도록 되어있습니다. (실제로는 50cm보다 훨씬 짧은 거리내에서 후진을 시작하네요.)


schematic


sketch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
//Initialize Bluetooth
#include <SoftwareSerial.h>
SoftwareSerial BTSerial(7, 3); // HC-06 TX,RX

// Initialize TB6612FNG Motor drive
int STBY = 10; // STBY pin on TB6612FNG. Must be HIGH to enable motor
int A_PWM = 5; // Left motor speed control using analogWrite() function. Value between 0 - 255
int A_IN1 = 9; // Left motor - LOW should go forward
int A_IN2 = 8; // Left motor - HIGH should go forward
int B_PWM = 6; // Right motor speed control using analogWrite() function. Value between 0 - 255
int B_IN1 = 11; // Right motor - LOW should go forward
int B_IN2 = 12; // Right motor - HIGH should go forward

int L_MaxSpeed = 255; //set motor speed to max speed
int R_MaxSpeed = 255; //set motor speed to max speed
int L_TurnSpeed = 128; //set motor speed to max speed
int R_TurnSpeed = 128; //set motor speed to max speed

int LR_Direct = 0; //for Direction (0:clockwise, 1:count-clockwise)

// Initialize Ultrasonic Sensor
#define TRIG 2
#define ECHO 4

char val;
long val_distance;

void setup() {
pinMode(A_PWM, OUTPUT); // Motor
pinMode(A_IN1, OUTPUT);
pinMode(A_IN2, OUTPUT);
pinMode(B_PWM, OUTPUT);
pinMode(B_IN1, OUTPUT);
pinMode(B_IN2, OUTPUT);
pinMode(STBY, OUTPUT);

pinMode(TRIG, OUTPUT); // Ultrasonic Sensor
pinMode(ECHO, INPUT);

Serial.begin(9600);
BTSerial.begin(9600); // Start Bluetooth

while (!BTSerial.available()) { // 처음 입력이 있을때까지
stop(); // 정지
}
}

void loop() {
distance(); // 거리 측정
Serial.println(val_distance); // 거리 출력

if (BTSerial.available()) { // Check for Bluetooth input
val = BTSerial.read(); // 입력값 val에 저장
Serial.println(val); // 입력값 출력
}

if (val_distance > 50) { // 거리가 50cm 이상이면 입력값 실행
parseCommand(val); // parse the input
} else { // 거리가 50cm 이내이면
stop();
delay(20);
b_stop(); // 거리가 50cm 이상이 될 때까지 후진 후, 정지
}
delay(50);
}

void parseCommand(char input) {
switch (input) {

case 'F':
go_forward();
break;

case 'B':
go_backward();
break;

case 'L':
if(LR_Direct==0) {
go_f_left();
} else if(LR_Direct==1) {
go_b_left();
}
break;

case 'R':
if(LR_Direct==0) {
go_f_right();
} else if(LR_Direct==1) {
go_b_right();
}
break;

case 'O':
stop();
break;
}
delay(50);
}

void distance() {
digitalWrite(TRIG, LOW); // Start Ultrasonic sensor
delayMicroseconds(2);
digitalWrite(TRIG, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG, LOW);

val_distance = pulseIn(ECHO, HIGH) / 58.2; // 거리 측정
}

// Move specific motor at speed and direction
// motor: A(Left) -> 0, B(Right) -> 1
// speed: 0 is off, and 255 is full speed
// direction: 0 clockwise, 1 counter-clockwise

void move(int motorLR, int speed, boolean inPin1, boolean inPin2) {
digitalWrite(STBY, HIGH); // Disable Standby

if (motorLR == 0) {
analogWrite(A_PWM, speed);
digitalWrite(A_IN1, inPin1);
digitalWrite(A_IN2, inPin2);
}

if (motorLR == 1) {
analogWrite(B_PWM, speed);
digitalWrite(B_IN1, inPin1);
digitalWrite(B_IN2, inPin2);
}
}

void go_forward() {
Serial.println("F");
move(0, L_MaxSpeed, 0, 1); // Left motor, Left Speed, forward(0,1)
move(1, R_MaxSpeed, 0, 1); // Right motor, Right Speed, forward(0,1)
LR_Direct = 0; // Forward
delay(100);
}

void go_backward() {
Serial.println("B");
move(0, L_MaxSpeed, 1, 0); // Left motor, Left Speed, backward(1,0)
move(1, R_MaxSpeed, 1, 0); // Right motor, Right Speed, backward(1,0)
LR_Direct = 1; // Backward
delay(100);
}

void go_f_left() {
Serial.println("F_L");
move(0, L_TurnSpeed, 0, 1); // Left motor, Left Speed, forward(0,1)
move(1, R_MaxSpeed, 0, 1); // Right motor, Right Speed, forward(0,1)
delay(100);
}

void go_b_left() {
Serial.println("B_R");
move(0, L_TurnSpeed, 1, 0); // Left motor, Left Speed, backward(1,0)
move(1, R_MaxSpeed, 1, 0); // Right motor, Right Speed, backward(1,0)
delay(100);
}

void go_f_right() {
Serial.println("F_R");
move(0, L_MaxSpeed, 0, 1); // Left motor, Left Speed, forward(0,1)
move(1, R_TurnSpeed, 0, 1); // Right motor, Right Speed, forward(0,1)
delay(100);
}

void go_b_right() {
Serial.println("B_R");
move(0, L_MaxSpeed, 1, 0); // Left motor, Left Speed, backward(1,0)
move(1, R_TurnSpeed, 1, 0); // Right motor, Right Speed, backward(1,0)
delay(100);
}

void b_stop() {
Serial.println("B_STOP");
while (val_distance < 50) { // 거리가 50cm 이내인 경우 반복
go_backward(); // 후진
delay(100);
distance(); // 거리를 다시 측정
delay(10);
}
while (!BTSerial.available()) { // 입력이 없으면
stop(); // 정지상태 유지
}
LR_Direct = 0; // 앞으로 갈 준비
}

void stop() {
Serial.println("STOP");
digitalWrite(STBY, LOW); // Enable Standby
}

DC모터 + 4WD 자동차 만들기 (TB6612FNG)

DC모터 사용하기

아두이노의 5V, 3.3V핀으로는 모터에 필요한 충분한 전압과 전류를 공급하기 어렵다. 그러므로 사용하려는 모터에 맞는 전압과 전류의 공급을 위한 모터드라이버를 사용해야 한다. 아두이노에서 주로 사용하는 모듈은 L293D, L298N와 TB6612FNG 등이 있으며, 여기서는 TB6612FNG 모터드라이버와 함께, 모터 및 아두이노 단독사용을 위하여 별도의 외부전원을 사용한다. 외부 전원으로는 모터를 위한 1.5V AA*4개와 함께 아두이노 단독사용을 위한 9V 사각전지(6F22)를 추가로 연결하였다.

1.5V AA 6개 혹은 3.7V 18650 2개를 직렬로 연결하여 사용하면, 9V 사각전지(6F22)가 필요하지 않으므로 이 방법을 추천한다. 또한 AA 건전지를 사용하는 경우 4WD기준 연속사용시간은 10분 정도뿐일 정도로 전지 소모가 매우 빠르므로, 충전이 가능한 18650 사용을 추천한다.


TB6612FNG

VM
VCC
GND(*)
AOUT1
AOUT2
BOUT2
BOUT1
GND
PWMA
AIN2
AIN1
STBY
BIN1
BIN2
PWMB
GND
  • VM (모터 전압) = 15V max
  • VCC (로직 전압) = 2.7 ~ 5.5V
  • GND
    • TB6612fng 모듈을 여러개 테스트한 결과, 3번핀 GND에 연결할 경우 작동이 안되는 몇몇 제품이 있었음
    • 그러므로 8번핀, 9번핀의 GND 사용을 권장
  • 출력전류: 정전류 1.2A (3.2A peak)까지 (모터 2개 사용시 합산 전류임)
  • 모터 제어모드: CW, CCW, short-brake, STOP, stand-by
  • 두개의 모터 출력을 개별 제어하며, 100kHz의 PWM으로 속도 제어
  • 써멀 셧다운 및 저전압 감지회로 내장

Pin의 사용
핀번호 모터드라이브1 아두이노 외부전원 모터 / 역할
1 VM (+) (DC모터에 사용하는 전압 사용)
2 VCC +5V
3 GND (사용 비추천)
4 A_OUT1 모터A
5 A_OUT2 모터A
6 B_OUT2 모터B
7 B_OUT1 모터B
8 GND (-)
9 GND GND
10 B_PWM 6 모터B 속도제어
11 B_IN2 12 모터B 방향제어
12 B_IN1 11 모터B 방향제어
13 STBY 10 모터 상태신호
14 A_IN1 9 모터A 방향제어
15 A_IN2 8 모터A 방향제어
16 A_PWM 5 모터A 속도제어
  • 속도제어에 사용하는 A_PWM, B_PWM은 아두이노의 PWM이 가능한 핀에 연결하여야 합니다. 여기서는 ~5, ~6번 핀 사용.

주의: Pin Map이 다른 모듈의 경우

TB6612FNG를 여러 개 구입하여 사용해본 결과, 위의 핀맵과 조금 다른 모듈도 있었다.

핀번호 모터드라이브1 아두이노 외부전원 모터 / 역할
1 GND GND (-)
2 VCC (2.7~5.5V) +5V
3 A_OUT1 모터A
4 A_OUT2 모터A
5 B_OUT2 모터B
6 B_OUT1 모터B
7 VM (15V max) (+) (DC모터 요구 전압 사용)
8 GND
9 GND
10 B_PWM 6 모터B 속도제어
11 B_IN2 12 모터B 방향제어
12 B_IN1 11 모터B 방향제어
13 NC(STBY) 10 모터 상태신호
14 A_IN1 9 모터A 방향제어
15 A_IN2 8 모터A 방향제어
16 A_PWM 5 모터A 속도제어

TB6612FNG와 DC모터의 연결

Pin Map

TB6612FNG 모터드라이브는 2채널 형태이며, 만일 2WD 자동차를 만든다면 디지털핀 7개가 필요하다. 4개의 DC모터를 사용하여 4WD 자동차를 만드는 방법은 3가지로 나눠 생각해볼 수 있는데, 모든 바퀴를 각각 컨트롤 하는 방법, 왼쪽 바퀴와 오른쪽 바퀴로 나누어 컨트롤 하는 방법, 앞 바퀴와 뒷 바퀴로 나누어 컨트롤 하는 방법 등이 있다.

  • 모든 바퀴를 각각 컨트롤 하는 경우 : 모터드라이브 2개와 디지털핀 14개 사용
    • 그런데, 아두이노에서 실제 사용할 수 있는 디지털핀의 개수가 12개(2~13번)이므로, 2개의 디지털핀이 부족하다.
      • 아날로그핀을 디지털핀과 동일하게 사용할 수 있으므로, 2개의 아날로그핀을 추가로 사용한다.
      • 또는 스케치 업로드시에만 0,1번 핀을 빼두었다가, 스케치 업로드가 끝나면 0,1번 핀을 사용한다. 이렇게 사용하면 0~13번까지 총 14개의 디지털핀을 확보할 수 있다.
  • 왼쪽과 오른쪽 바퀴로 나누어 컨트롤 하는 방법 : 디지털핀 7개 사용 (아래 본문에서 사용한 방법)
  • 앞과 뒷 바퀴로 나누어 컨트롤 하는 방법 : 디지털핀 7개 사용. (단, 조향을 위한 추가적인 방법을 고려해야 함.)

H-SW Control Function

Input Output
IN1 IN2 PWM STBY OUT1 OUT2 Mode
H H H/L H L L Short Brake
L H H H L H Count-Clockwise
L H L H L L Short Brake
H L H H H L Clockwise
H L L H L L Short Brake
L L H H OFF(High impedance) OFF(High impedance) Stop
H/L H/L H/L L OFF(High impedance) OFF(High impedance) Standby

출력 결과는 아래와 같으며,

  • IN1이 LOW, IN2가 HIGH, PWM으로 신호를 출력할 경우 시계 반대 방향(CCW)으로 회전
  • IN1이 HIGH, IN2가 LOW, PWM으로 신호를 출력할 경우 시계 방향(CW)으로 회전
  • 그 밖의 경우는 모두 stop 상태로 해석하면 된다.

모터 1개 컨트롤하기

여기서 사용하는 DC모터는 아래의 형태를 가진 기어드모터이며, 아두이노를 사용하여 자동차를 만드는데 많이 사용되는 저렴이 모터이다.


schematic

UNO보드와 컴퓨터를 USB선으로 연결하여 사용할 경우


UNO보드와 컴퓨터의 연결없이 Vin핀을 사용하여 단독으로 사용할 경우
  • UNO 보드의 Vin에 외부전원(7.4V) (+)에 연결: 6.6V~12V 범위의 전원 연결 가능 (7.2V 이상 추천)
  • UNO 보드의 GND를 TB6612fng 모듈의 GND에 연결


sketch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
int STBY = 10;            // Standby

// Motor A
int A_PWM = 5; // Speed Control
int A_IN1 = 8;
int A_IN2 = 9;

void setup() {
pinMode(STBY, OUTPUT);
pinMode(A_PWM, OUTPUT);
pinMode(A_IN1, OUTPUT);
pinMode(A_IN2, OUTPUT);
}

void loop() {
move(1, 128, 0); // motor A(right wheels), half speed, moving forward

delay(1000); // go for 1 second
stop(); // stop
delay(250); // hold for 250ms until move again

move(1, 128, 1); // motor A(right wheels), half speed, moving backward

delay(1000);
stop();
delay(250);
}

void move(int motor, int speed, int direction) {
/*
- Move specific motor at speed and direction
- motor: A -> 1
- speed: 0 is off, and 255 is full speed
= direction: 0 clockwise, 1 counter-clockwise
*/
digitalWrite(STBY, HIGH); // Disable Standby

boolean inPin1 = LOW; // Defalut(direction=0) - Clockwise
boolean inPin2 = HIGH;

if(direction == 1) { // Count-clockwise
inPin1 = HIGH;
inPin2 = LOW;
}

if(motor == 1){ // if motor == 1, right wheel
digitalWrite(A_IN1, inPin1);
digitalWrite(A_IN2, inPin2);
analogWrite(A_PWM, speed);
}
else { // if motor is not 1, left wheel

}
}

void stop() {
digitalWrite(STBY, LOW); // Enable Standby
}

회전 방향이 (생각했던 방향과) 반대로 회전하는 경우

A_IN1핀과 A_IN2 핀의 번호를 바꿔준다. 즉, 위 스케치에서

1
2
int A_IN1 = 8;
int A_IN2 = 9;

부분을

1
2
int A_IN1 = 9;
int A_IN2 = 8;

로 바꿔준다.


4WD 자동차 만들기

schematic

UNO보드와 컴퓨터를 USB선으로 연결하여 사용할 경우


UNO보드와 컴퓨터의 연결없이 Vin핀을 사용하여 단독으로 사용할 경우
  • UNO 보드의 Vin에 외부전원(7.4V) (+)에 연결: 6.6V~12V 범위의 전원 연결 가능 (7.2V 이상 추천)
  • UNO 보드의 GND를 TB6612fng 모듈의 GND에 연결


sketch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
// front wheel : B1 - A1
// rear wheel : B2 - A2

int STBY = 10; // Standby

// Motor A 1 & 2(Right)
int A_PWM = 5; // Speed Control
int A_IN1 = 8;
int A_IN2 = 9;

// Motor B 1 & 2(Left)
int B_PWM = 6; // Speed Control
int B_IN1 = 11;
int B_IN2 = 12;

void setup() {
pinMode(STBY, OUTPUT);
pinMode(A_PWM, OUTPUT);
pinMode(A_IN1, OUTPUT);
pinMode(A_IN2, OUTPUT);
pinMode(B_PWM, OUTPUT);
pinMode(B_IN1, OUTPUT);
pinMode(B_IN2, OUTPUT);
}

void loop() {
move(1, 128, 0); // motor A(right wheels), half speed, moving forward
move(2, 128, 0); // motor B(left wheels), half speed, moving forward

delay(1000); // go for 1 second
stop(); // stop
delay(250); // hold for 250ms until move again

move(1, 128, 1); // motor A(right wheels), half speed, moving backward
move(2, 128, 1); // motor B(left wheels), half speed, moving backward

delay(1000);
stop();
delay(250);
}

void move(int motor, int speed, int direction) { //Move specific motor at speed and direction
//motor: A(Right) -> 1, B(Left) -> 2
//speed: 0 is off, and 255 is full speed
//direction: 0 clockwise, 1 counter-clockwise

digitalWrite(STBY, HIGH); // Disable Standby

boolean inPin1 = LOW; // Defalut(direction=0) - Clockwise
boolean inPin2 = HIGH;

if(direction == 1) { // Count-clockwise
inPin1 = HIGH;
inPin2 = LOW;
}

if(motor == 1){ // if motor == 1, right wheel
digitalWrite(A_IN1, inPin1);
digitalWrite(A_IN2, inPin2);
analogWrite(A_PWM, speed);
} else { // if motor is not 1, left wheel
digitalWrite(B_IN1, inPin1);
digitalWrite(B_IN2, inPin2);
analogWrite(B_PWM, speed);
}
}

void stop() {
digitalWrite(STBY, LOW); // Enable Standby
}


IR 리모컨으로 4WD 컨트롤하기

IR리모컨을 사용하여 RC카를 컨트롤 하기 위해서는 모터와 회로에서 발생하는 노이즈를 제거하기 위한 104 케패시터(0.1uF)를 사용하는 것이 원칙입니다. 반드시 사용해야 하는 것은 아니지만, 회로보호를 위해 캐패시터가 있는 경우에는 IR 수신부의 (+)와 (-)에 병렬연결하세요. 이에 더하여 후진시 방향전환도 가능하도록 수정해 봅니다.


schematic

UNO보드와 컴퓨터를 USB선으로 연결하여 사용할 경우


UNO보드와 컴퓨터의 연결없이 Vin핀을 사용하여 단독으로 사용할 경우



IRremote 라이브러리 추가하기

1
2
3
4
#include <boarddefs.h>
#include <IRremote.h>
#include <IRremoteInt.h>
#include <ir_Lego_PF_BitStreamEncoder.h>

사용할 리모컨 버튼 HEX값

Remote Button unsigned int data define
▲ (전진 / 속도 증가) 0XFF18E7 BTN_U
▼ (후진 / 속도 감소) 0XFFA4B5 BTN_D
◀ (좌회전 / 좌회전각 증가) 0XFF10EF BTN_L
▶ (우회전 / 우회전각 증가) 0XFF5AA5 BTN_R
OK (정지) 0XFF38C7 BTN_O

보유하고 있는 리모컨의 종류마다 HEX값이 다르므로, HEX값은 다를 수 있습니다. HEX값을 알아내는 방법은 IR Remote를 다루는 페이지를 참고하세요.


sketch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
//Initialize IR remote
#include <boarddefs.h>
#include <IRremote.h>
#include <IRremoteInt.h>
#include <ir_Lego_PF_BitStreamEncoder.h>

int RECV_PIN = 13; // IR Signal pin
IRrecv irrecv(RECV_PIN);
decode_results results;

#define BTN_F 0xFF18E7 // Forward Button
#define BTN_B 0xFF4AB5 // Backward Button
#define BTN_L 0xFF10EF // Left Button
#define BTN_R 0xFF5AA5 // Right Button
#define BTN_O 0xFF38C7 // OK Button

// Initialize TB6612FNG Motor drive
int STBY = 10; // STBY pin on TB6612FNG. Must be HIGH to enable motor
int A_PWM = 5; // Right motor speed control using analogWrite() function. Value between 0 - 255
int A_IN1 = 9; // Right motor - LOW should go forward
int A_IN2 = 8; // Right motor - HIGH should go forward
int B_PWM = 6; // Left motor speed control using analogWrite() function. Value between 0 - 255
int B_IN1 = 11; // Left motor - LOW should go forward
int B_IN2 = 12; // Left motor - HIGH should go forward

int R_MaxSpeed = 255; //set motor speed to max speed
int L_MaxSpeed = 255; //set motor speed to max speed
int R_TurnSpeed = 128; //set motor speed to max speed
int L_TurnSpeed = 128; //set motor speed to max speed

int LR_Direct = 0; //for Direction

void setup() {
pinMode(A_PWM, OUTPUT);
pinMode(A_IN1, OUTPUT);
pinMode(A_IN2, OUTPUT);
pinMode(B_PWM, OUTPUT);
pinMode(B_IN1, OUTPUT);
pinMode(B_IN2, OUTPUT);
pinMode(STBY, OUTPUT);

Serial.begin(9600);
irrecv.enableIRIn(); // Start the receiver
}

void loop() {
if (irrecv.decode(&results)) { // Read IR remote control
//Serial.println(results.value, HEX); // for debug
irrecv.resume(); // Receive the next value
}

switch(results.value) {

case BTN_F:
go_forward();
break;

case BTN_B:
go_backward();
break;

case BTN_L:
if(LR_Direct==0) {
go_f_left();
} else if(LR_Direct==1) {
go_b_left();
}
break;

case BTN_R:
if(LR_Direct==0) {
go_f_right();
} else if(LR_Direct==1) {
go_b_right();
}
break;

case BTN_O:
stop();
break;
}
delay(50);
}

// Move specific motor at speed and direction
// motor: A(Right) -> 0, B(Left) -> 1
// speed: 0 is off, and 255 is full speed
// direction: 0 clockwise, 1 counter-clockwise

void move(int motorLR, int speed, boolean inPin1, boolean inPin2) {
digitalWrite(STBY, HIGH); // Disable Standby

if (motorLR == 0) {
analogWrite(A_PWM, speed);
digitalWrite(A_IN1, inPin1);
digitalWrite(A_IN2, inPin2);
}

if (motorLR == 1) {
analogWrite(B_PWM, speed);
digitalWrite(B_IN1, inPin1);
digitalWrite(B_IN2, inPin2);
}
}

void go_forward() {
Serial.println("F");
move(0, R_MaxSpeed, 0, 1); // Right motor, Right Speed, forward(0,1)
move(1, L_MaxSpeed, 0, 1); // Left motor, Left Speed, forward(0,1)
LR_Direct = 0; // Forward
delay(100);
}

void go_backward() {
Serial.println("B");
move(0, R_MaxSpeed, 1, 0); // Right motor, Right Speed, backward(1,0)
move(1, L_MaxSpeed, 1, 0); // Left motor, Left Speed, backward(1,0)
LR_Direct = 1; // Backward
delay(100);
}

void go_f_left() {
Serial.println("F_L");
move(0, R_MaxSpeed, 0, 1); // Right motor, Right Speed, forward(0,1)
move(1, L_TurnSpeed, 0, 1); // Left motor, Left Speed, forward(0,1)
delay(100);
}

void go_b_left() {
Serial.println("F_R");
move(0, R_MaxSpeed, 1, 0); // Right motor, Right Speed, backward(1,0)
move(1, L_TurnSpeed, 1, 0); // Left motor, Left Speed, backward(1,0)

delay(100);
}

void go_f_right() {
Serial.println("F_R");
move(0, R_TurnSpeed, 0, 1); // Right motor, Right Speed, forward(0,1)
move(1, L_MaxSpeed, 0, 1); // Left motor, Left Speed, forward(0,1)
delay(100);
}

void go_b_right() {
Serial.println("B_R");
move(0, R_TurnSpeed, 1, 0); // Right motor, Right Speed, backward(1,0)
move(1, L_MaxSpeed, 1, 0); // Left motor, Left Speed, backward(1,0)

delay(100);
}

void stop() {
Serial.println("STOP");
digitalWrite(STBY, LOW); // Enable Standby
}


초음파 센서 장착

초음파 센서를 장착하여 전진시 일정한 거리 내에 장애물이 있으면 자동차를 멈춰보도록 하겠습니다. Trig는 아두이노 2번핀, Echo는 4번핀에 연결하였습니다.


schematic

UNO보드와 컴퓨터를 USB선으로 연결하여 사용할 경우


UNO보드와 컴퓨터의 연결없이 Vin핀을 사용하여 단독으로 사용할 경우


sketch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
//Initialize IR remote
#include <boarddefs.h>
#include <IRremote.h>
#include <IRremoteInt.h>
#include <ir_Lego_PF_BitStreamEncoder.h>

int RECV_PIN = 13; // IR Signal pin
IRrecv irrecv(RECV_PIN);
decode_results results;

#define BTN_F 0xFF18E7 // Forward Button
#define BTN_B 0xFF4AB5 // Backward Button
#define BTN_L 0xFF10EF // Left Button
#define BTN_R 0xFF5AA5 // Right Button
#define BTN_O 0xFF38C7 // OK Button

// Initialize TB6612FNG Motor drive
int STBY = 10; // STBY pin on TB6612FNG. Must be HIGH to enable motor
int A_PWM = 5; // Right motor speed control using analogWrite() function. Value between 0 - 255
int A_IN1 = 9; // Right motor - LOW should go forward
int A_IN2 = 8; // Right motor - HIGH should go forward
int B_PWM = 6; // Left motor speed control using analogWrite() function. Value between 0 - 255
int B_IN1 = 11; // Left motor - LOW should go forward
int B_IN2 = 12; // Left motor - HIGH should go forward

int R_MaxSpeed = 255; //set motor speed to max speed
int L_MaxSpeed = 255; //set motor speed to max speed
int R_TurnSpeed = 128; //set motor speed to max speed
int L_TurnSpeed = 128; //set motor speed to max speed

int LR_Direct = 0; //for Direction

// Initialize Ultrasonic Sensor
#define TRIG 2
#define ECHO 4

void setup() {
pinMode(A_PWM, OUTPUT); // Motor
pinMode(A_IN1, OUTPUT);
pinMode(A_IN2, OUTPUT);
pinMode(B_PWM, OUTPUT);
pinMode(B_IN1, OUTPUT);
pinMode(B_IN2, OUTPUT);
pinMode(STBY, OUTPUT);

pinMode(TRIG, OUTPUT); // Ultrasonic Sensor
pinMode(ECHO, INPUT);

Serial.begin(9600);
irrecv.enableIRIn(); // Start the receiver
}

void loop() {
digitalWrite(TRIG, LOW); // Start Ultrasonic sensor
delayMicroseconds(2);
digitalWrite(TRIG, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG, LOW);

long distance = pulseIn(ECHO, HIGH) / 58.2; // 거리 측정

if (irrecv.decode(&results)) { // Read IR remote control
//Serial.println(results.value, HEX); // for debug
irrecv.resume(); // Receive the next value
}

if (distance < 10) { // 거리가 10cm 이내이면 정지
stop();
delay(500);
}
else { // 거리가 10cm 이상이면,
switch(results.value) {

case BTN_F:
go_forward();
break;

case BTN_B:
go_backward();
break;

case BTN_L:
if(LR_Direct==0) {
go_f_left();
} else if(LR_Direct==1) {
go_b_left();
}
break;

case BTN_R:
if(LR_Direct==0) {
go_f_right();
} else if(LR_Direct==1) {
go_b_right();
}
break;

case BTN_O:
stop();
break;
}
delay(50);
}

// Move specific motor at speed and direction
// motor: A(Right) -> 0, B(Left) -> 1
// speed: 0 is off, and 255 is full speed
// direction: 0 clockwise, 1 counter-clockwise

void move(int motorLR, int speed, boolean inPin1, boolean inPin2) {
digitalWrite(STBY, HIGH); // Disable Standby

if (motorLR == 0) {
analogWrite(A_PWM, speed);
digitalWrite(A_IN1, inPin1);
digitalWrite(A_IN2, inPin2);
}

if (motorLR == 1) {
analogWrite(B_PWM, speed);
digitalWrite(B_IN1, inPin1);
digitalWrite(B_IN2, inPin2);
}
}

void go_forward() {
Serial.println("F");
move(0, R_MaxSpeed, 0, 1); // Right motor, Right Speed, forward(0,1)
move(1, L_MaxSpeed, 0, 1); // Left motor, Left Speed, forward(0,1)
LR_Direct = 0; // Forward
delay(100);
}

void go_backward() {
Serial.println("B");
move(0, R_MaxSpeed, 1, 0); // Right motor, Right Speed, backward(1,0)
move(1, L_MaxSpeed, 1, 0); // Left motor, Left Speed, backward(1,0)

LR_Direct = 1; // Backward
delay(100);
}

void go_f_left() {
Serial.println("F_L");
move(0, R_MaxSpeed, 0, 1); // Right motor, Right Speed, forward(0,1)
move(1, L_TurnSpeed, 0, 1); // Left motor, Left Speed, forward(0,1)
delay(100);
}

void go_b_left() {
Serial.println("F_R");
move(0, R_MaxSpeed, 1, 0); // Right motor, Right Speed, backward(1,0)
move(1, L_TurnSpeed, 1, 0); // Left motor, Left Speed, backward(1,0)
delay(100);
}

void go_f_right() {
Serial.println("F_R");
move(0, R_TurnSpeed, 0, 1); // Right motor, Right Speed, forward(0,1)
move(1, L_MaxSpeed, 0, 1); // Left motor, Left Speed, forward(0,1)
delay(100);
}

void go_b_right() {
Serial.println("B_R");
move(0, R_TurnSpeed, 1, 0); // Right motor, Right Speed, backward(1,0)
move(1, L_MaxSpeed, 1, 0); // Left motor, Left Speed, backward(1,0)
delay(100);
}

void stop() {
Serial.println("STOP");
digitalWrite(STBY, LOW); // Enable Standby
}

Arduino, 라인트레이서, L298N

아두이노를 사용하여 검은색 라인을 따라 움직이는 라인트레이서 자동차를 만들어보자.


TCRT5000 IR reflective sensor

라인트레이서 제작시 가장 널리 사용되는 IR 센서이다. 감지하는 부분이 검은색으로 되어 있는 부분이라면 IR이 모두 흡수되고, 흰색으로 되어 있는 부분에서는 모두 반사되는 원리를 이용한다.

  • 장애물이 없는 경우: 1 반환
  • 센서와 12mm이내의 거리에 밝은색이 위치하는 경우: 0 반환
  • 센서와 12mm이내의 거리에 검은색이 위치하는 경우: 1 반환
  • 검은색의 경우도 아주 가까이 가져가면 적외선 수신량이 증가하여 0의 값을 반환할 수 있으므로, 라인트레이서 구성시 감지하는 적당한 거리를 찾는 것이 중요합니다.
  • 10~12mm 정도의 거리에서 측정하는 것을 추천
  • AO 단자를 이용할 경우 아날로그 신호로 처리도 가능

(위 스펙과는 별도로) 라인트레이서 제작시에는 경험상 센서가 바닥면으로부터 2cm정도 떨어지도록 설치하는 것을 추천함


schematic


sketch

1
2
3
4
5
6
7
8
9
10
11
12
13
int i = 0;
int pin_sensor = 2;

void setup() {
Serial.begin(9600);
pinMode(pin_sensor, INPUT);
}

void loop() {
i = digitalRead(pin_sensor)
Serial.println(i); // 검은색(1), 밝은색(0) 출력
delay(1000);
}


모터드라이버 L298N

대부분의 모터는 구동에 필요한 전력 소모량 커서 아두이노의 자체 출력으로는 구동이 불가능하다. 그러므로 건전지, SMPS 등의 외부 전원을 사용하여 모터에 충분한 전력을 공급하고, 이를 제어하기위한 모터드라이버를 반드시 사용해야 한다.

아두이노에서 사용하는 대표적인 모터드라이브는 L298N, TB6612FNG 등이 있으며, 여기서는 2CH의 2A출력이 가능한 L298N을 사용하여 간단한 라인트레이서를 제작한다.


Pinout

  • 모터A 출력 : 왼쪽 모터를 연결한다.

  • 모터B 출력 : 오른쪽 모터를 연결한다.

  • 12V (입력) : 외부전원을 입력한다. (5~35V)

    • 12V 이상의 외부전원을 입력하는 경우, 레귤레이터 보호를 위해 5V 점퍼선을 제거해야 함
  • 5V (입력/출력)

    • 점퍼핀이 연결되어 있는 경우 5V 출력 (단, 5V출력으로 사용하려면 외부전원을 12V이하로 주어야 하며, 그 이상의 전압을 인가하는 경우 레귤레이터가 손상됨)
    • 점퍼핀 없는 경우 5V 입력을 통해 L298N에 전원 인가
  • Enable 모터A : 왼쪽 모터 PWM (점퍼를 제거하고, INPUT핀 양옆의 핀에 PWM신호 입력)

  • Enable 모터B : 오른쪽 모터 PWM (점퍼를 제거하고, INPUT핀 양옆의 핀에 PWM신호 입력)

  • PWM을 사용하는 경우 모터에 인가하는 전원이 6V(AA*4개)인 경우 PWM값이 낮으면 모터가 회전하지 않으므로 코딩시 주의한다.

  • INPUT : IN1, IN2, IN3, IN4

    Direction IN1 IN2 IN3 IN4
    Go HIGH HIGH LOW LOW
    Back LOW LOW HIGH HIGH
    Brake LOW LOW LOW LOW
    Left_Turn HIGH LOW LOW LOW
    Right_Turn LOW HIGH LOW LOW
    Left_Back_Turn LOW LOW HIGH LOW
    Right_Back_Turm LOW LOW LOW HIGH

Jumper

  • Enable 모터A
    • 점퍼를 연결시키지 않으면 PWM사용
    • 점퍼 연결시 왼쪽 모터에 5V (즉, PWM을 사용하지 않는 경우에만 연결)
  • Enable 모터B
    • 점퍼를 연결시키지 않으면 PWM사용
    • 점퍼 연결시 오른쪽 모터 5V (즉, PWM을 사용하지 않는 경우에만 연결)
  • 5V (※ Pinout의 5V 부분 참고)


전원

  • 대부분의 초보자를 위한 4WD 제작 키트는 AA건전지 4개를 직렬연결할 수 있는 전지소켓을 포함하여 판매하는 경우가 많지만
  • But, 제작 경험으로 볼 때 AA건전지 4개로 얻을 수 있는 전력만으로는 원활한 구동이 되지 않는 경우가 많다.
  • AA건전지 6개를 직렬로 연결하여 9V정도를 공급하거나 (사용시간 10~15분정도 가능)
  • 18650 리튬이온전지 2개를 연결하여 7.4V를 공급하는 것을 추천한다.


모터

아두이노 기초 실습용으로 많이 쓰이는 이름없는 중국산 모터이다.

기어비 48:1 RPM(무부하) 전류 mA (무부하) 토크(kg·cm)
3V 120 40 3.2
6V 240 70 5.5


프레임 조립

준비물


조립과정

  1. 모터 4개와 M-F형태의 8가닥 리드선을 준비하고 모터에 리드선을 납땜할 준비를 합니다.


  1. DC모터를 보면 양쪽에 (+)(-)극을 연결하는 단자가 있습니다.


  1. 준비한 8가닥의 M-F리드선을 2가닥씩 묶어 4갈래로 전체 길이의 1/2의 길이만큼 나누어 준비한뒤,


  1. 2개의 가닥 중 한 부분을 모터의 (+)(-)단자에 끼우고,


  1. 각각을 납땜합니다. 총 4개의 모터를 납땜하여 아래 모습처럼 만듭니다.


※ (주의) 납땜시, 아래 그림처럼 리드선이 모터의 본체에 닿으면 안됩니다.


​ 처음 납땜할 때부터 닿지 않도록 하는 것이 좋지만, 납땜을 이미 진행한 후에 리드선이 본체에 닿아 있다면 아래의 그림처럼 끝 부분을 살짝 휘어두거나, 니퍼로 잘라내도록 합니다.


  1. 외부 전원으로 사용할 건전지소켓을 준비합니다. (사진상에는 1.5V AA건전지 4개가 직렬로 연결되는 건전지소켓을 사용하였지만, 4개의 모터를 구동하기 위해서는 1.5V AA건전지를 6개 직렬로 연결하여 9V 전압을 사용하는 것을 추천합니다. 단, 9V사각전지(6F22) 1개로 사용하는 것은 전류가 약하므로 사용불가!)


​ 건전지소켓에 나와있는 리드선의 끝부분을 스트리퍼를 사용하여 피복을 조금더 벗겨냅니다. 또한 빨간색과 검은색 점퍼선 2개를 별로로 준비한뒤, 한쪽 단자를 잘라내고, 스트리퍼로 피복을 벗겨냅니다.


  1. 건전지소켓의 리드선과 점퍼선을 붙여 떨어지지 않도록 단단히 납땜한 후,


  1. 합선되지 않도록 절연테이프나 열수축튜브로 각각을 잘 감싸둡니다.


  1. 이제 아크릴판을 준비합니다.


  1. 아래 그림의 Step1~2과정과 같이 모터를 고정할 아크릴 조각을 끼울 것입니다.


​ 우선, 모터를 고정할 아크릴 조각 2개를 고정할 위치를 확인합니다. 총 4개의 모터를 고정할 것이므로 아크릴 조각 8개가 필요합니다.


  1. 모터고정 아크릴 조각을 아래 사진처럼 끼웁니다. 하나는 구멍에 집어 넣고, 나머지 하나는 측면의 홈에 살짝 걸쳐둡니다.


  1. 이제 모터를 설치하겠습니다.



​ 나사와 너트를 2개씩 준비한 후


​ 아크릴 조각 2개 사이에 모터를 위치시킨 후, 모터 몸체이 있는 나사 구멍에 나사 2개를 통과시켜


​ 아래 사진 같이 고정합니다.


13. 모터를 고정한 후, 바퀴축의 안쪽 부분에 바퀴의 균형을 잡아줄 동그란 아크릴 조각을 끼워줍니다.


​ 4개의 모터를 같은 방법으로 고정시켜 아래와 같은 형태를 만듭니다. 주의할 것은 모터의 단자가 위치한 은색부분의 모터본체 부분이 서로 맞닿는 형태로 조립을 해야 합니다.


  1. 바퀴를 끼웁니다.


  1. 금속기둥을 세우고 상판을 덮습니다. (아크릴상판이 없는 경우는 생략 가능)



  1. 기판 아래면에 모터가 위치하도록 위아래를 뒤집은 후에, 기판 윗면에 TCRT5000 IR Reflective Sensor, 아두이노, 브레드보드, L298N 모터드라이브, 9V 전지소켓을 차례대로 올리고 나사볼트와 절연테이프 등으로 고정합니다. 대략 아래 사진과 비슷하게 고정하면 됩니다.



아두이노 연결하기

schematic


회로 연결

모터 ↔ 모터드라이버
L/F 모터 U단자 L/F 모터 D단자 L/R 모터 U단자 L/R 모터 D단자 R/F 모터 U단자 R/F 모터 D단자 R/R 모터 U단자 R/R모터 D단자
L298N OUT1
L298N OUT2
L298N OUT3
L298N OUT4

모터드라이버 ↔ 전지소켓 ↔ 아두이노
전지소켓 (+) 전지소켓(-) 아두이노
L298N 12V Vin
L298N GND GND (아두이노) GND
L298N 5V 5V

모터드라이브 ENABLE & INPUT ↔ 아두이노
아두이노
L298N EN(A) 5
L298N IN1 8
L298N IN2 9
L298N IN3 10
L298N IN4 11
L298N EN(B) 6

TCRT5000 Sensor ↔ 아두이노
아두이노
#1 TCRT5000 VCC 5V
#1 TCRT5000 GND GND
#1 TCRT5000 DO 2
#1 TCRT5000 AO
#2 TCRT5000 VCC 5V
#2 TCRT5000 GND GND
#2 TCRT5000 DO 3
#2 TCRT5000 AO

sketch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/* L298N - 2CH Motor Driver
Direction IN1 IN2 IN3 IN4
-------------------------------------------
GO HIGH HIGH LOW LOW
BACK LOW LOW HIGH HIGH
BRAKE LOW LOW LOW LOW
LEFT_TURN HIGH LOW LOW LOW
RIGHT_TURN LOW HIGH LOW LOW
LEFT_BACK_TURN LOW LOW HIGH LOW
RIGHT_BACK_TURN LOW LOW LOW HIGH

PWM: 6V시 최소값 200이상은 되야 바퀴회전 가능 (너무 낮은 전압에서 회전 안됨)
*/

// 모터_전진컨트롤
int Left_motor_IN1 = 8; // IN1
int Right_motor_IN2 = 9; // IN2

// 모터_후진컨트롤
int Left_motor_IN3 = 10; // IN3
int Right_motor_IN4 = 11; // IN4

// 모터_속도컨트롤
int Left_PWM = 5; // 왼쪽모터 속도
int Right_PWM = 6; // 오른쪽모터 속도

// IR Sensor
const int Left_Sensor = 2; // 왼쪽센서
const int Right_Sensor = 3; // 오른쪽센서

int Left_Sensor_Value; // 검은색:1
int Right_Sensor_Value; // 흰색:0

void setup()
{
pinMode(Left_motor_IN1, OUTPUT); // PIN 8
pinMode(Right_motor_IN2, OUTPUT); // PIN 9

pinMode(Left_motor_IN3, OUTPUT); // PIN 10
pinMode(Right_motor_IN4, OUTPUT); // PIN 11

pinMode(Left_PWM, OUTPUT); // PIN 5
pinMode(Right_PWM, OUTPUT); // PIN 6

pinMode(Left_Sensor, INPUT); // 왼쪽센서
pinMode(Right_Sensor, INPUT); // 오른쪽센서
}

// 전진
void go() {
digitalWrite(Left_motor_IN1, HIGH); // 왼쪽모터 전진_정지
digitalWrite(Right_motor_IN2, HIGH); // 오론쪽모터 전진_정지

digitalWrite(Left_motor_IN3, LOW); // 왼쪽모터 후진_회전
digitalWrite(Right_motor_IN4, LOW); // 오른쪽모터 후진_회전

analogWrite(Left_PWM, 255); // PWM값(0~255), 모터 회전속도 조절
analogWrite(Right_PWM, 255); // PWM값(0~255), 모터 회전속도 조절
delay(5);
}

// 후진
void back() {
digitalWrite(Left_motor_IN1, LOW); // 왼쪽모터 전진_정지
digitalWrite(Right_motor_IN2, LOW); // 오론쪽모터 전진_정지

digitalWrite(Left_motor_IN3, HIGH); // 왼쪽모터 후진_회전
digitalWrite(Right_motor_IN4, HIGH); // 오른쪽모터 후진_회전

analogWrite(Left_PWM, 255); // PWM값(0~255), 모터 회전속도 조절
analogWrite(Right_PWM, 255); // PWM값(0~255), 모터 회전속도 조절

}

// 정지
void brake() {
digitalWrite(Left_motor_IN1, LOW); // 왼쪽모터 전진_정지
digitalWrite(Right_motor_IN2, LOW); // 오론쪽모터 전진_정지

digitalWrite(Left_motor_IN3, LOW); // 왼쪽모터 후진_정지
digitalWrite(Right_motor_IN4, LOW); // 오른쪽모터 후진_정지
delay(5);
}

// 좌회전
void turn_left(){
digitalWrite(Left_motor_IN1, LOW); // 왼쪽모터 전진_정지
digitalWrite(Right_motor_IN2, HIGH); // 오론쪽모터 전진_회전

digitalWrite(Left_motor_IN3, LOW); // 왼쪽모터 후진_정지
digitalWrite(Right_motor_IN4, LOW); // 오른쪽모터 후진_정지

analogWrite(Right_PWM, 255); // PWM값(0~255), 모터 회전속도 조절
delay(5);
}

// 우회전
void turn_right() {
digitalWrite(Left_motor_IN1, HIGH); // 왼쪽모터 전진_회전
digitalWrite(Right_motor_IN2, LOW); // 오론쪽모터 전진_정지

digitalWrite(Left_motor_IN3, LOW); // 왼쪽모터 후진_정지
digitalWrite(Right_motor_IN4, LOW); // 오른쪽모터 후진_정지

analogWrite(Left_PWM, 255); // PWM값(0~255), 모터 회전속도 조절
delay(5);
}

void loop()
{
while(1)
{

//신호가 있으면(흰색) LOW, 신호가 없으면(검은색) HIGH
Right_Sensor_Value = digitalRead(Right_Sensor);
Left_Sensor_Value = digitalRead(Left_Sensor);

// 전진: 왼쪽센서 검은색, 오른쪽센서 검은색
if (Left_Sensor_Value == HIGH && Right_Sensor_Value == HIGH)
go();

// 좌회전: 왼쪽센서 검은색, 오른쪽센서 흰색
else if (Left_Sensor_Value == HIGH & Right_Sensor_Value == LOW)
turn_left();

// 우회전: 왼쪽센서 흰색, 오른쪽센서 검은색
else if (Left_Sensor_Value == LOW & Right_Sensor_Value == HIGH)
turn_right();

// 정지: 왼쪽센서 흰색, 오른쪽센서 흰색
else
brake();
}
}

센서 감도 조정
  • 센서 반응을 살피면서 TCRT5000 모듈에 붙어 있는 감도조절기를 십자드라이버로 돌려 라인트레이싱에 가장 최적인 감도를 찾아야 한다.

Arduino, Bluetooth 페어링

아두이노에서 블루투스를 이용하는 방법을 알아보겠습니다.


Bluetooth module 선택

  • 아이폰 : MLT-BT05(AT-09) BLE 모듈 사용 (이 모듈은 안드로이도에서도 사용 가능)

    HC-06, HC-05는 아이폰에서 SPP(Serial Port Profile) 통신이 지원하지 않는 이유로 사용할 수 없습니다. 그러므로 아이폰으로 블루투스 통신을 하기 위해서는 MLT-BT-BT05, AT-09, CC2541, HM-10, HC-02, RN-42 같은 BLE(Bluetooth Low Energy) 모듈을 사용합니다. 연결방법이나 AT명령어 세트는 HC-06과 비슷해요. 주의 할 점은, 시리얼 모니터에서 Both NL & CR 모드를 선택해주어야 하는 것입니다.

  • 안드로이드 : HC-06(or HC-05) 블루투스 모듈 사용 (이 모듈은 아이폰에서 사용 불가)

※ 요즘은 잘 사용하지 않지만, HC-05를 사용하기도 합니다. 생김새가 거의 비슷하지만, 몇가지 차이점이 있는데요. 보통 HC-06과 구별하기 위하여, HC-05에는 칩 위에 V형태로 체크를 해두거나, 화이트로 하얀색 점을 찍어두는 경우가 많습니다. 그리고, 가장 큰 차이점은 HC-05의 경우 6개의 핀이 모두 존재해요.

※ 예전에는 HC-05는 Master, HC-06은 Slave로 나누어 쓰는 경우가 많았지만, HC-06의 버전이 1.7이상인 경우 Master, Slave를 선택하여 사용할 수 있게 되면서 HC-05는 잘 사용하지 않게 되었습니다.


MLT-BT05, HC-06을 사용하는 경우

Pin Map
Arduino Uno
1 STATE (실제 핀 없거나 미사용)
2 RXD Receive Data (수신) 3
3 TXD Transmit Data (송신) 2
4 GND GND GND
5 VCC +5V +5V
6 KEY (실제 핀 없거나 미사용)

schematic


sketch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <SoftwareSerial.h>

SoftwareSerial BTSerial(2, 3); //Connect MLT-BT05 or HC-06 TX,RX

void setup()
{
Serial.begin(9600);
Serial.println("Hello!");

// set the data rate for the BT port
BTSerial.begin(9600);
}

void loop()
{
if (BTSerial.available())
Serial.write(BTSerial.read());
if (Serial.available())
BTSerial.write(Serial.read());
}

HC-05를 사용하는 경우

Pin Map
HC-05 Pin Arduino Uno
1 STATE
2 RXD Receive Data 3
3 TXD Transmit Date 7
4 GND GND GND
5 VCC +5V +5V
6 KEY (ENABLE) AT command 진입을 위한 핀 8

※ TXD로 UNO의 7번핀을 사용했으나, HC-06처럼 2번핀을 사용해도 됩니다. 단, schematic과 sketch도 2번 핀에 맞추어 수정해주세요.


schematic


sketch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <SoftwareSerial.h>
#define KEY 8
SoftwareSerial BTSerial(7, 3); //Connect HC-05 TX,RX

void setup()
{
pinMode(KEY, OUTPUT); //for HC-05
digitalWrite(KEY, HIGH);
Serial.begin(9600);
Serial.println("Hello!");

// set the data rate for the BT port
BTSerial.begin(38400);
}

void loop()
{
if (BTSerial.available())
Serial.write(BTSerial.read());
if (Serial.available())
BTSerial.write(Serial.read());
}


블루투스 모듈을 처음 사용하는 경우

모듈을 처음 사용한다면, AT 명령어 세트를 사용하여 세팅을 해주어야 합니다. 또한 이전에 사용한 경험이 있더라도 블루투스 모듈ID, 페어링 암호 등을 새롭게 세팅할 필요가 있는 경우에도 동일합니다.


MLT-BT05(AT-09)의 AT Command

명령어 Serial Monitor 출력 결과 의미
AT OK 접속상태 확인
AT+NAMESKS100 OKsetname 블루투스 모듈ID 지정 (default MLT-BT05)
AT+PIN PASSWORD 출력 패스워드 확인
AT+PIN1234 OKsetPIN 페어링 암호 설정 (default: 123456)
AT+BAUD4 OK9600 통신속도 설정 (4 → 9600bps) (default: 0 → 115200bps)
AT+VERSION OKVERSION=4.0 MLT-BT05 버전 확인
AT+LADDR +LADDR=(MAC Address) MAC Address 확인
AT+ROLE=0 OK+ROLE:0 SLAVE mode (default)
AT+ROLE=1 OK+ROLE:1 MASTER mode
AT+ROLE=2 OK+ROLE:2 sensor mode
AT+ROLE=3 OK+ROLE:3 iBeacon mode
AT+ROLE=4 OK+ROLE:4 WeChat mode
AT+ROLE OK+ROLE: Role 확인

AT Command set 다운로드


HC-06의 AT Command

명령어 Serial Monitor 출력 결과 의미
AT OK 접속상태 확인
AT+NAMESKS100 OKsetname 블루투스 모듈ID 지정 (SKS100)
AT+PIN PASSWORD출력 패스워드 확인
AT+PIN1234 OKsetPIN 페어링 암호 설정 (default: 1234)
AT+BAUD4 OK9600 통신속도 설정 (4 → 9600bps) (default: 4 → 9600bps)
AT+VERSION OKlinvorV1.8 HC-06 버전 확인
AT+ROLE=S OK+ROLE:S SLAVE 역할
AT+ROLE=M OK+ROLE:M MASTER 역할 (v1.7 이상만 가능)
AT+ROLE OK+ROLE: MASTER/SLAVE 확인

HC-05의 AT Command

※ HC-05는 AT Command를 사용하는 경우, KEY(Enable) 핀을 연결해주어야 합니다. (schematic 참고)

명령어 Serial Monitor 출력 결과 의미
AT OK 접속상태 확인
AT+NAMESKS100 OKsetname 블루투스 모듈ID 지정 (SKS100)
AT+PSWD=0000 OKsetPIN 페어링 암호 설정 (0000)
AT+BAUD6 OK9600 통신속도 설정 (6 → 38400bps)
AT+VERSION OKlinvorV1.8 HC-06 버전 확인
AT+ROLE=0 OK+ROLE:0 SLAVE 역할
AT+ROLE=1 OK+ROLE:1 MASTER 역할
AT+ROLE=? OK+ROLE: MASTER/SLAVE 확인


스마트폰과의 페어링 과정

시리얼모니터에서

HC-06과의 페어링 과정을 보도록 하겠습니다. (다른 블루투스 모듈도 유사하게 진행하면 됩니다.)


  1. 먼저 아두이노IDE의 시리얼 모니터를 엽니다.

※ 이때, 주의할 점이 있는데요. 시리얼 모니터를 열기 전에 스마트폰과 HC-06의 블루투스 연결은 해제된 상태여야 합니다. 기존에 HC-06과 스마트폰을 연결한 적이 있다면, 자기도 모르게 블루투스 연결이 자동으로 되어 있을 수 있는데요. 이런 경우에는 시리얼 모니터가 열리지 않아요.

  • MLT-BT05: 시리얼모니터의 하단에 “Both NL & CR”, “9600 보드레이트” 선택
  • HC-06: 시리얼창의 하단에 “line ending 없음”, “9600 보드레이트 선택
  • HC-05: 시리얼창의 하단에 “Both NL & CR”, “9600 보드레이트” 선택

  1. 위 스케치를 사용하는 모듈에 맞게 선택하여 아두이노에 업로드 한 상태에서 시리얼 모니터를 열면, Hello! 라는 글자가 뜹니다. (업로드한 스케치에 따라 Hello!는 안나올 수도 있음)

  1. AT를 입력하고,

  1. 전송을 누르면 OK가 뜹니다. 이것은 아두이노IDE를 통해 블루투스 모듈과 연결이 정상적으로 되었음을 의미해요.

  1. 블루투스 모듈ID를 설정해보겠습니다. SKS100이라는 ID로 지정하기 위해 AT+NAMESKS100이라고 입력한 뒤, 전송을 누릅니다. (모듈ID는 본인이 사용할 이름으로 바꾸어 지정합니다.)

  1. 그러면 OKsetname이라고 출력이 되면서, 블루투스 모듈ID가 SKS100으로 셋팅이 됩니다.

  1. 이번에는 페어링 암호 설정입니다. AT+PIN0000을 입력하고 전송을 누르면

  1. OKsetPIN이 출력되면서 페어링 암호가 0000으로 설정됩니다.

  1. 통신속도 설정입니다. HC-06은 주로 9600bps를 사용하며, 이를 위해 AT+BAUD4를 입력한 뒤 전송을 누릅니다. (HC-06의 경우 default 속도가 9600bps이므로 이 과정은 생략 가능하며, 다른 모듈을 사용하는 경우에는 각 모듈의 AT Command를 확인하여 통신속도를 변경해주어야 합니다.)

  1. OK9600이 출력되요.

여기까지 진행하면 대체적으로 HC-06을 사용할 준비가 된 것입니다.


스마트폰에서

  1. 이제 스마트폰에서 블루투스 모드를 켜세요. (아래 화면은 스마트폰 기종마다 조금은 다를 수 있습니다. 화면이 다르더라도 거의 비슷할 거에요.) 그리고 새로고침을 통해 블루투스 기기를 찾습니다.

  1. ‘거의 이용하지 않는 기기’ 하나를 찾았네요. 그곳을 누르면 조금전에 설정한 블루투스 모듈ID인 SKS100이 보입니다. 여기를 누르세요.

  1. 블루투스 모듈과 페어링을 하기 위한 암호 입력화면이 나오고, 여기에 AT명령어를 통해 입력한 암호를 넣어줍니다.

  1. 그러면 스마트폰이 SKS100 모듈에 페어링을 시도합니다.

  1. 거의 이용하지 않는 블루투스 기기 부분을 누르면

  1. 페어링된 기기 목록에 SKS100이 뜹니다. 여기까지 진행하면, 스마트폰과 블루투스 모듈이 페어링 완료된 거에요.

Arduino, Bluetooth, Blynk

Blynk 설정

Blynk 라이브러리 설치

  1. docs.blynk.cc 접속 – Downloads – Blynk Library
  2. https://github.com/blynkkk/blynk-library/releases/tag/v0.6.1
    • Blynk_Release_v0.6.1.zip 다운로드
  3. 압축을 풀면 libraries, tools 폴더가 보임
  4. 이 두 폴더를 아두이노IDE의 기본 스케치북 위치로 복사하여 붙여넣기 함
    • 기존의 libraries 폴더와 합쳐짐
    • 기본 스케치북 위치 확인 방법
      • 아두이노IDE – 파일 – 환경설정 – 설정탭 – 스케치북 위치

스마트폰에 Blynk App 다운로드 하여 설치하기

  • 아이폰 : 애플 앱스토어
  • 안드로이드 : 구글 플레이스토어


버튼 위젯을 이용한 LED On/Off 하기

Blynk 계정생성 및 Auth Token 받기

  1. Blynk App 실행
  2. Create New Account : 주로 사용하는 e-mail을 사용하여 계정 생성
  3. Blynk에서 New Project 생성
    • 아이폰용 MLT-BT05의 경우
      • Project Name 설정: BLE Test
      • CHOOSE DEVICE: Arduino UNO
      • CONNECTION TYPE: BLE
      • 설정 후 Create 버튼
    • 안드로이드용 HC-06의 경우
      • Project Name 설정: HC-06 Test
      • CHOOSE DEVICE: Arduino UNO
      • CONNECTION TYPE: Bluetooth
      • 설정 후 Create 버튼
  4. Auth Token 확인
    • 가입시 지정한 이메일로 로그인하여 토큰 확인
    • (매우 복잡한 형태의) 토큰을 복사하여 코딩시 사용할 것이므로, 컴퓨터로 확인!
    • 스마트폰으로 확인하는 것은 무의미함

Blynk 위젯 설정

  1. 아이폰용 MLT-BT05를 사용할 경우
    • 생성된 프로젝트(BLE Test) 바탕화면으로 진입하면 Blynk App 상단의 초록색 메뉴바의 이름이 BLE Test로 바뀜
    • 여기서 ⊕버튼 터치한 후 Button위젯과 BLE위젯 추가
    • 추가된 BUTTON 위젯 선택 후 설정
      • OUTPUT: Select pin: Digital D13 / MODE: SWITCH
  2. 안드로이드용 HC-06를 사용할 경우
    • 생성된 프로젝트(HC-06 Test) 바탕화면으로 진입하면 Blynk App 상단의 초록색 메뉴바의 이름이 HC-06 Test로 바뀜
    • 여기서 ⊕버튼 터치한 후 Button위젯과 Bluetooth위젯 추가
    • 추가된 BUTTON 위젯 선택 후 설정
      • OUTPUT: Select pin: Digital D13 / MODE: SWITCH

Blynk 연결하기

  1. 아두이노 스케치 예제 불러오기
    • 파일 – 예제 – Blynk – Boards_USB_Serial – Arduino_SoftwareSerial 선택
    • 예제의 char auth[] = “YourAuthToken”; 부분을 찾아서 쌍따옴표 사이의 YourAuthToken을 지운 뒤, 이메일에서 복사한 AuthToken 붙여넣기
  2. 아두이노 UNO에 스케치 업로드
  3. 스마트폰의 블루투스 기능을 키고,
    • 아이폰용 MLT-BT05의 경우
      • Bluetooth 모듈 설정에서 설정한 모듈ID 디바이스를 연결(예를 들어 MLT-BT05-T) (디폴트 연결PIN 123456)
      • Blynk App에서 BLE버튼(블루투스 아이콘 모양) 터치 후 Connect BLE device 버튼을 누른 뒤,
      • 연결할 디바이스 MLT-BT05-T를 찾은 후 OK버튼 누름
      • MLT-BT05-T connected 메시지가 나오면 연결 완료된 것임
    • 안드로이드용 HC-06의 경우
      • Bluetooth 모듈 설정에서 설정한 모듈ID 디바이스를 연결(예를 들어 HC-06-T 디바이스를 연결 (디폴트 연결PIN 1234)
      • Blynk App에서 Bluetooth버튼(블루투스 아이콘 모양) 터치 후 Connect Bluetooth device 버튼을 누른 뒤,
      • 연결할 디바이스 HC-06-T를 찾은 후 OK버튼 누름
      • HC-06-T connected 메시지가 나오면 연결 완료된 것임
  4. Blynk App 상단의 뒤로 돌아가기 버튼을 누른 뒤, App 오른쪽 상단의 ▷(플레이버튼)버튼을 누름
    • App상단에 빨간색 숫자1이 보이면 아직 연결이 되지 않았다는 것을 의미함
    • 잠시 기다리면 빨간색 숫자1이 사라지고, 이 상태가 작동 준비가 완료된 것을 의미함
  5. BUTTON 아이콘을 눌러 ON/OFF → 아두이노 Built-in LED ON/OFF
  6. Blynk App 오른쪽 상단의 □(정지버튼)을 누른 뒤, ⊕버튼 터치한 후 Text Input 위젯 추가


슬라이더 위젯을 이용한 LED 밝기 제어

Built-in LED가 사용하는 13번핀은 PWM이 지원되지 않으므로, 11번핀을 사용한다.


sketch

아두이노 스케치는 그대로 사용 (위의 “Blynk 연결하기” 부분 스케치 참고)


Blynk 앱 설정

  1. 버튼 위젯을 삭제: 버튼 위젯을 누르고 맨아래쪽으로 내려가서 Delete
  2. Slider 위젯 추가: OUTPUT D11
  3. Slider 위젯의 위치/크기 조절: Slider 위젯 아이콘을 1초이상 누르면 위치/크기를 조절할 수 있음 (가로 크기를 최대로 늘림)
  4. App 오른쪽 상단의 ▷(플레이버튼)버튼을 누름
  5. 슬라이드를 조절하여 LED의 밝기 조절 확인


과제

3색 RGB LED를 zeRGBa 위젯으로 조정하여 여러 가지 색깔 만들어보기 (스케치는 위의 스케치를 그대로 사용)

Blynk에 센서값 출력하기

원문: http://help.blynk.cc/en/articles/512056-how-to-display-any-sensor-data-in-blynk-app


센서값을 Blynk로 출력하기 위해서는 Blynk가 연결되지 않은 상태에서 시리얼모니터에 센서값들을 출력할 수 있어야 한다. ​센서값을 출력하는 코드가 완성되면, 그 다음으로 Blynk에 센서값을 보내는 작업을 시작하는 것이 좋다.

Blynk앱에 센서값을 표시하는 방법에는 크게 두가지 방법이 있으며, 진행중인 프로젝트에 따라 적절한 방법을 선택하면 된다.


PULL

  • 이 경우에 블링크는 앱이 열려있는 동안에만 Blynk에서 값을 받는다.
  • 앱에서 센서값을 표시하는 가장 간단한 방법이며, Blynk 앱의 Virtual Pins이 구동중일때 센서값을 가져온다. (Blynk를 닫거나 백그라운드에서 실행될때 데이타는 요청되지 않는다.) 타이머를 사용할 필요가 없으므로 코딩이 단순한 편이지만, 데이터가 서버에 저장되지 않으므로 히스토리 그래프를 볼 수 없다는 단점이 있다.

Blynk 앱 설정

  1. 새 프로젝트를 만든다. (새로운 Auth Token값이 당신의 이메일에 전송된다)
  2. Value Display 위젯을 추가한다.
  3. 위젯 세팅으로 가서 PIN 항목을 Virtual Pin V5로 설정한다.
  4. Reading Rate를 3초로 설정한다.

sketch

  1. Analog Pin에서 값을 (간단하게) 읽어들이는 경우에는 코드를 쓸 필요가 없다. 단지 예제파일을 오픈하여 하드웨어와 커넥션만 변경한다.
  2. e-mail을 체크하여 Auth Token을 확인하고, 스케치에 Auth Token을 수정입력한다.
  3. 하드웨어에 코드를 업로드한다.
  4. Blynk에서 플레이버튼을 누른다.
  5. 값이 표시되는 것을 볼 수 있다.
1
2
3
4
BLYNK_READ(V5) { //Blynk app has something on V5
sensorData = analogRead(A0); //reading the sensor on A0
Blynk.virtualWrite(V5, sensorData); //sending to Blynk
}

이 코드는 매시간마다 Blynk가 _READ 요청을 Virtual Pin V5에 보내도록 한다. A0핀에서 센서값을 읽은 후, Blynk에서 Virtual Pin V5에 되돌려 준다.



PUSH

  • 이 경우 하드웨어(아두이노 or ESP32 등)는 Blynk가 오프라인 상태일때도 센서값을 Blynk 서버에 저장하고, 앱을 열어 온라인 상태가 되면 앱으로 전송하여 센서값을 보여준다.

  • Virtual Pin을 통하여 일정 주기로 하드웨어로부터 센서값을 전송하고, 이때마다 Blynk가 값을 받아들인다.


주의할 점

void loop() 에서는 센서 값을 보낼 수 없다!!!

만약 void loop()문 내에 어떤 값을 넣게 되면 MCU가 아주 짧은 시간 간격으로 무한 반복하여 실행하게 되는데, Blynk는 인터넷을 통하여 값을 전송할 것이므로 Blynk 클라우드가 하드웨어로부터 오는 계속적인 데이터를 받게 될 것이다. Blynk 클라우드가 이러한 상황을 감지하면 자동적으로 접속을 끊게 된다.

그러므로 데이타를 일정 간격으로 보내야하며, 이를 위해 타이머를 사용하는 방법을 취한다. 일정간격으로 데이타를 보내는 방법에는 여러가지가 있지만, 가장 간단한 방법으로 BlynkTimer의 사용을 권장한다. BlynkTimer는 Blynk 라이브러리 패키지안에 포함되어 있으므로, Blynk 라이브러리만 제대로 설치되었다면 별도의 라이브러리를 설치하지 않아도 된다.


Blynk 앱 설정

  1. 새 프로젝트를 만든다. (새로운 Auth Token값이 당신의 이메일에 전송된다)
  2. Value Display 위젯을 추가한다.
  3. 위젯 세팅으로 가서 PIN 항목을 Virtual Pin V5로 설정한다.
  4. Frequency를 PUSH로 설정한다.

Example

  1. timer라고 하는 BlynkTimer 오브젝트를 만든다.
    1
    BlynkTimer timer; // Announcing the timer

  1. 그 다음으로 일정 간격으로 실행하게 될 함수를 만든다. 이 함수는 A0에 연결된 센서로부터 값을 Virtual Pin V5를 통하여 전송한다.
    1
    2
    3
    4
    void myTimerEvent() {
    sensorData = analogRead(A0);
    Blynk.virtualWrite(V5, sensorData);
    }

  1. setup()문에서, 서브함수 myTimerEvent()가 매 1000ms, 즉 1초 간격으로 실행되도록 선언할 것이다.
    1
    2
    3
    4
    5
    6
    void setup() {
    ...
    ...
    ...
    timer.setInterval(1000L, myTimerEvent);
    }

  1. loop문은 다음과 같이 설정한다.
    1
    2
    3
    4
    void loop() {
    Blynk.run();
    timer.run(); // running timer every second
    }

Bluetooth Serial Controller 앱

Bluetooth 시리얼 통신

Bluetooth Serial Controller 앱을 통하여 아두이노와 스마트폰 간의 간단한 시리얼 통신을 해보겠습니다. 이를 위해서는 먼저 Bluetooth 페어링 문서를 참고하여 블루투스 모듈과 스마트폰을 페어링 해두어야 합니다. 페어링이 완료된 후 시리얼 통신을 진행합니다.


schematic


sketch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <SoftwareSerial.h>
SoftwareSerial BTSerial(2, 3); //Connect HC-06 TX,RX

void setup()
{
Serial.begin(9600);
Serial.println("Hello!");

// set the data rate for the BT port
BTSerial.begin(9600);
}

void loop()
{
if (BTSerial.available())
Serial.write(BTSerial.read());
if (Serial.available())
BTSerial.write(Serial.read());
}


스마트폰에 Bluetooth Serial Controller 앱 설치하기

  1. 이제 Google Playstore를 통해서 스마트폰에 Bluetooth Serial Controller 앱을 설치합니다. (iOS는 유사한 프로그램으로 설치)

​ 비슷한 역할을 하는 수많은 앱들이 있는데, 여러 앱을 설치하고 사용해본 결과, 이 앱이 사용하기에 유연함을 갖고 있는 것 같더군요.


​ 설치되었습니다.


  1. 설치 후 처음 실행을 하면, LANDSCAPE 모드(가로모드)로 실행이 되는데요. 그대로 사용해도 무방하지만 PORTRAIT 모드(세로 모드)로 바꿔보겠습니다. 먼저 PREFERENCE를 누르세요.

  1. 그러면 아래 화면이 뜹니다. 아무곳이나 누르고 위로 스크롤을 하면 아래에 숨겨져 있는 메뉴가 나타나게 되는데요.

  1. OPTION - Orientation을 선택한 뒤,

  1. PORTRAIT를 누릅니다.

  1. 이제 세로모드로 나타나요. 이제 HC-06모듈과 스마트폰과의 Serial 통신을 해보도록 하겠습니다. 다시 PREFERENCE를 누르고, TERMINAL MODE를 활성화 시키세요.

  1. 아래 화면이 TERMINAL Mode 화면입니다. 여기서 돋보기 모양을 누르고,

  1. Paired Devices 목록에서 SKS100을 선택하세요. 그러면 TERMINAL이 HC-06에 접속하여, 서로간 통신할 수 있는 상태가 됩니다.

​ 만일, 아래와 같이 Paired Devices 목록이 없으면, Scan for devices를 눌러 HC-06을 찾아보세요.


  1. TERMINAL이 SKS100에 접속을 시도합니다.

  1. 접속 완료!


터미널과 Serial 통신하기

  1. 이제 터미널 창에 Test를 입력하고 SEND를 눌러보세요.

  1. 그러면 화면에 Test가 출력됨과 동시에,

​ 시리얼 모니터에 Test가 출력됩니다. 즉, TERMINAL에서 입력한 Test라는 글자가 HC-06을 통해, 아두이노와 연결되어 있는 컴퓨터의 시리얼 모니터에 출력된 것이에요.


  1. 이번에는 반대로 시리얼 모니터에 Good morning! 을 입력하고 전송을 누르면

​ 스마트폰의 TERMINAL에 Good morning! 이 출력됩니다.

Processing 기초

  • processing.org 에서 다운로드 (다운로드 페이지)
  • arduino sketch는 C++기반이고, processing sketch는 java기반입니다.

processing sketch 예제

  1. 예제1 : 프로세싱 윈도우 크기, 배경색, 점, 선, 사각형, 원, 텍스트, 폰트, 사진 입력
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void setup() {
size(400,400);
background(100); // background(0,255,0);
strokeWeight(10); // dot/line width
stroke(0,255,0,100); // dot/line color and transparency, noStroke();
point(200,200); // draw dot
line(0,0,200,150); // draw line
ellipse(300,300,50,80); // draw circle(x,y,xr,yr)
fill(0); // rect/text color, nofill();
rect(100,250,150,300); // draw rectangle(x1,y1,x2,y2)

textSize(20);
PFont font = createFont("NanumGothic", 32);
textFont(font);
text("Hello World!",200,100); // text(string,x1,y2)

PImage img = loadImage("sks.jpg"); // file in '/sketch_XXXXXXa/data/' folder
image(img,20,50); // image(img,x,y,width,height);

}

void draw() {

}

  1. 예제2 : 마우스와 키보드 이벤트
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
void setup() {
size(400,400);
}

void draw() {

}

void mousePressed() {
if(mouseButton == LEFT) {
println("left button Pressed!");
} else if(mouseButton == RIGHT) {
println("right button Pressed!");
} else if(mouseButton == CENTER) {
println("center button Pressed!");
}
}

void keyPressed() {
println("key Pressed!"); // Key event
println(key); // Key String
println(keyCode); // ASCII Code
}

void mouseReleased() {
println("button Released!");
}

void keyReleased() {
println("key Released!");
}

void mouseClicked() {
println("button Clicked!");
}

void mouseMoved() {
println("mouse Moved!");
}

void mouseDragged() {
println("mouse Dragged!");
}

void mouseWheel() {
println("mouse Wheel!");
}

  1. 예제3 : 마우스 움직이는 대로 원 그리기
1
2
3
4
5
6
7
8
9
10
void setup() {
size(400,400);
}

void draw() {
}

void mouseMoved() {
ellipse(mouseX,mouseY,50,50);
}

  1. 예제4 : 자유선 그리기
1
2
3
4
5
6
7
8
9
10
void setup() {
size(400,400);
}

void draw() {
}

void mouseMoved() {
line(pmouseX,pmouseY,mouseX,mouseY);
}


schematic

아두이노의 기본 LED를 사용할 것이므로 별도의 회로 연결없이 아두이노만 연결한다.


library 등록

  1. 아두이노 사이트에서 프로세싱 라이브러리를 다운받은 후

  2. 압축을 풀었을 때 나오는 arduino폴더를 c:\Users\userID\processing\libraries\ 폴더에 복사한다.

  3. 프로세싱 프로그램에서 스케치내부라이브러리Arduino (firmata)를 선택하면,

1
2
3
import cc.arduino.*;
import org.firmata.*;
import processing.serial.*;

가 등록된다.


processing sketch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import cc.arduino.*;
import org.firmata.*;
import processing.serial.*;

Arduino arduino;
int ledPin = 13;

void setup() {
size(400,400);
println(Arduino.list());
arduino = new Arduino(this, Arduino.list()[0], 57600); // Arduino.list()[0] or "COM3"
arduino.pinMode(ledPin, Arduino.OUTPUT);
}

void draw() {
arduino.digitalWrite(ledPin, Arduino.HIGH);
delay(1000);
arduino.digitalWrite(ledPin, Arduino.LOW);
delay(1000);
}

arduino sketch

  1. arduino IDE에서 파일예제firmatastandard firmata를 불러들인 후

  2. 컴파일하고 업로드한다.


processing에서 실행하기

  1. processing에서 실행버튼을 눌러 실행해보기

  2. 파일어플리케이션으로 내보내기를 선택하여 윈도우, Mac, Linux용 실행파일을 만들어보기

※ 어플리케이션으로 내보내기를 하기전에 processing sketch를 저장하여야 한다.



Processing으로 led 제어하기

processing sketch : 사각형 스위치

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import cc.arduino.*;
import org.firmata.*;
import processing.serial.*;

Arduino arduino;
int ledPin = 13;

void setup() {
size(400,400);
background(0);
fill(255);
noStroke();
rect(width/2, 0, width/2, height);
text("LED OFF", 30, 30);
fill(0, 0, 0);
text("LED ON", 230, 30);

println(Arduino.list());
arduino = new Arduino(this, Arduino.list()[0], 57600);
arduino.pinMode(ledPin, Arduino.OUTPUT);
}

void draw() {
}

void mouseMoved() {
if(mouseX < width/2) {
arduino.digitalWrite(ledPin, Arduino.LOW);
} else {
arduino.digitalWrite(ledPin, Arduino.HIGH);
}
}

processing sketch : 그라데이션 가변저항

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import cc.arduino.*;
import org.firmata.*;
import processing.serial.*;

Arduino arduino;
int ledPin = 13;

void setup() {
size(400,400);

for(int i=0; i<width; i++) {
stroke(map(i, 0, width-1, 0, 255)); // 색깔을 점차 변화시키면서
line(i, 0, i, height); // 선을 그려, 그라데이션 효과를 만들어 냄
}

println(Arduino.list());
arduino = new Arduino(this, Arduino.list()[0], 57600);
}

void draw() {
}

void mouseMoved() {
arduino.analogWrite(ledPin, int(map(mouseX, 0, width-1, 0, 255)));
}

1602 LCD, 조도, 초음파, 온습도

1602 LCD 중에서 (I2C 모듈이 추가되어 있지 않은) 가장 기본적인 형태, 즉 1602 LCD만 있는 상태에서의 출력방법을 알아보겠습니다.


schematic


schematic : 백라이트 밝기 조절 추가

백라이트를 사용하지 않으면 너무 어두워서 글자가 잘 보이지 않는다. 적당한 밝기의 백라이트 사용을 위해 LCD 3번핀을 3.3㏀ 거쳐서 GND로 연결(혹은 1㏀ 3개를 직렬연결) 하거나, 아래 회로와 같이 가변 저항을 사용하면 백라이트의 밝기를 적절히 조절할 수도 있다.

Pin Map

LCD Pin Arduino Uno
1 VSS LCD GND GND
2 VDD LCD 전원 +5V
3 VO 글자 대비값 (가변저항 추가/미사용시 3.3㏀ 거쳐서 GND)
4 RS 레지스터 설정 12
5 RW 읽기/쓰기모드 설정 GND
6 E 쓰기모드 활성화 11
7 D0 데이터 핀
8 D1
9 D2
10 D3
11 D4 5
12 D5 4
13 D6 3
14 D7 2
15 A 배경밝기 전압입력 (가변저항 추가)
16 K 배경밝기 GND (가변저항 추가)


sketch

라이브러리 설치하기

이 경우에는 Arduino IDE에 기본적으로 포함되어 있는 LiquidCrystal 라이브러리를 이용한다.

  • 스케치> 라이브러리 포함하기> LiquidCrystal
1
#include <LiquidCrystal.h>

function

function 기능
LiquidCrystal() LCD 클래스 생성(LCD interface 설정)
begin() LCD 화면 초기화(column과 row 설정)
clear() LCD 화면을 지우고 커서를 (0,0) 위치로 옮김
home() 커서를 (0,0) 위치로 옮김
setCursor() 커서를 특정 위치로 옮김
write() 현재 커서에 한 문자를 출력
print() 현재 커서부터 출력 (Serial.print() 함수와 유사)
cursor(), nocursor() 커서 표시 O or X
display(), noDisplay() LCD 화면에 정보 표시 O or X
scrollDisplayLeft(), scrollDisplayRight() LCD 내용과 커서를 좌/우로 한 칸 스크롤
autoscroll(), noautoscroll() 문자 출력 시 이전 내용을 스크롤 할 지를 결정
leftToRight(), rightToLeft() 문자 출력 후 커서의 이동 위치 결정 (문자 출력 방향 결정)
createChar() 사용자 문자 등록, 총 8개까지 가능


예시

sketch : 문자 2열 출력하기

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <LiquidCrystal.h>

LiquidCrystal lcd(12,11,5,4,3,2); // pin 연결에 대한 class 선언 (RS,E,D4,D5,D6,D7)

void setup() {
lcd.begin(16, 2); // LCD크기 지정
lcd.print("SKS Afterschool");
lcd.setCursor(0, 1); // 커서를 (0,1) 즉 두번째 줄(행)의 첫번째 칸(열)으로 이동
lcd.print("Let's Arduino!!");
}

void loop() {
}

sketch : 문자열 좌우 스크롤하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

void setup() {
lcd.begin(16, 2);
lcd.print("SKS Afterschool");
lcd.setCursor(0, 1);
lcd.print("Let's Arduino!!");
delay(1000);
}

void loop() {
for (int left = 0; left < 15; left++) {
lcd.scrollDisplayLeft(); // 15번 왼쪽으로 이동 (15글자)
delay(300);
}

for (int right = 0; right < 31; right++) {
lcd.scrollDisplayRight(); // 31번 오른쪽으로 이동 (15글자+16칸)
delay(300);
}

for (int left = 0; left < 16; left++) {
lcd.scrollDisplayLeft(); // 16번 왼쪽으로 이동 (16칸)
delay(300);
}

delay(1000);
}



문자열과 조도센서값을 출력하기

schematic


sketch : 문자열과 조도센서를 읽은 조도값을 동시에 출력하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <LiquidCrystal.h>

LiquidCrystal lcd(12,11,5,4,3,2); // pin 연결에 대한 class 선언 (RS,E,D4,D5,D6,D7)

void setup() {
lcd.begin(16, 2); // LCD크기 지정
lcd.print("Illumination:");
}

void loop() {
lcd.setCursor(0, 1); // 커서를 (0,1) 즉 첫번째 줄의 두번째 칸으로 이동
lcd.print(analogRead(A0)); // A0에서 읽은 조도값을 출력
delay(200);
}

sketch : 조도 센서값을 위로 스크롤시키기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <LiquidCrystal.h>

int i, j, k=1;
LiquidCrystal lcd(12,11,5,4,3,2);

void setup() {
lcd.begin(16, 2);
lcd.print("Illumination:");
lcd.setCursor(0, 1); // 두번째 줄에
i = analogRead(A0);
lcd.print("Check#");
lcd.print(k); // 측정 횟수
lcd.print(" : ");
lcd.print(i); // 첫번째 조도값 출력
delay(1000);
}

void loop() {
lcd.clear(); // lcd 화면 지우고 (0,0)으로 커서 이동
j = i; // 이전 조도값을 j에 저장
lcd.print("Check#");
lcd.print(k); // 이전 조도값 측정 횟수
lcd.print(" : ");
lcd.print(j); // 이전 조도값 출력

lcd.setCursor(0, 1); // 두번째 줄로 이동
i = analogRead(A0); // 새로운 조도값 측정
k++; // 측정 횟수 증가
lcd.print("check#");
lcd.print(k); // 새로운 측정 횟수
lcd.print(" : ");
lcd.print(i); // 새로운 조도값 출력

delay(1000);
}


초음파 센서 거리값 출력하기

초음파를 이용하여 사물간의 거리를 측정하는 센서입니다. 아두이노에서는 HC-SR04, US-100 제품을 많이 사용하며, 여기서는 HC-SR04를 사용해보겠습니다.


schematic


Pin Map : HC-SR04

DHT11 아두이노
VCC 5V
Trig (초음파 송신) 8 (pinMode 설정함)
Echo (초음파 수신) 9 (pinMode 설정함)
GND GND

sketch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <LiquidCrystal.h>
#define TRIG 8
#define ECHO 9

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

void setup() {
pinMode(TRIG, OUTPUT);
pinMode(ECHO, INPUT);
lcd.begin(16, 2);
}

void loop() {
digitalWrite(TRIG, LOW); // 초음파발사 정지(초기화)
delayMicroseconds(2);
digitalWrite(TRIG, HIGH); // 초음파발사
delayMicroseconds(2);
digitalWrite(TRIG, LOW); // 초음파발사 정지

long distance = pulseIn(ECHO, HIGH)/58.2;
lcd.clear();
lcd.setCursor(0,0);
lcd.print(distance);
lcd.print(" cm");
delay(200);
}


DHT11 온·습도센서 값 출력하기

DHT11 센서를 사용하면 온도와 습도를 동시에 측정할 수 있다. 여기서는 1602 LCD에 온도와 습도를 출력해본다.


schematic


Pin Map

DHT11 1 2 3 4
Arduino 5V 2, 10㏀ 저항 거쳐서 5V GND

라이브러리 추가하기

  • 스케치> 라이브러리 포함하기> 라이브러리 관리> 라이브러리 매니저 에서 “DHT11” 검색
  • DHT sensor library by Adafruit 설치

sketch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <LiquidCrystal.h>
#include <DHT.h>

LiquidCrystal lcd(12, 11, 5, 4, 3, 2); // RS, E, D4, D5, D6, D7
#define DHTPIN 10
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);

void setup() {
dht.begin();
lcd.begin(16, 2);
}

void loop() {
float t = dht.readTemperature(); // read temperature
float h = dht.readHumidity(); // read humidity

lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Temp.: "); // write temperature
lcd.print(t);
lcd.print(" C");
lcd.setCursor(0, 1);
lcd.print("Humi.: "); // write humidity
lcd.print(h);
lcd.print("%");
delay(1000);
}


과제

입력버튼을 만들과, 각 버튼을 누를때마다 A → B → C → D 등 출력하기

7 Segment (FND) 사용하기

FND의 종류

  • FND는 Anode형과 Cathode형으로 분류되며, 이 중 Anode형이 주로 사용되고 있는데요. Anode형은 중앙핀이 VCC이며, Cathode형은 GND입니다. 우리가 사용하는 것은 5611BH로 Common Anode형입니다.

  • 7 Segment (FND)는 숫자나 문자를 표현하는 7개의 LED와 소수점을 표현하는 1개의 LED가 포함되어 있는 부품을 의미합니다. FND는 16X2 LCD와 비교할 때 표현할 수 있는 문자의 개수가 제한되고, 각 문자의 표현을 위하여 총 8개의 LED를 하나씩 조절해주어야 하는 등 사용이 복잡하다는 단점이 있습니다. 그렇지만, 전력소모가 적고 크기가 큰 Segment를 선택하면 비교적 큰 문자도 표현할 수 있다는 장점이 있어 다양한 용도로 사용되고 있습니다.


숫자표시 방법

![](/image/7segment-02.jpg)
Num DP G F E D C B A 2진수 16진수
0 1 1 0 0 0 0 0 0 0b11000000 0xC0
1 1 1 1 1 1 0 0 1 0b11111001 0xF9
2 1 0 1 0 0 1 0 0 0b10100100 0xA4
3 1 0 1 1 0 0 0 0 0b10110000 0xB0
4 1 0 0 1 1 0 0 1 0b10011001 0x99
5 1 0 0 1 0 0 1 0 0b10010010 0x92
6 1 0 0 0 0 0 1 0 0b10000010 0x82
7 1 1 1 1 1 0 0 0 0b11111000 0xF8
8 1 0 0 0 0 0 0 0 0b10000000 0x80
9 1 0 0 1 0 0 0 0 0b10010000 0x90

10진수 숫자와의 구분을 위해 2진수 앞에는 0b, 16진수 앞에는 0x를 표기하고
16진수 표현에 있어서, 각 숫자에 소수점을 포함시키는 경우에는 80을 빼주면 됩니다. 예를 들어,

  • 0. → 0xC0 - 0x80 = 0x40
  • 1. → 0xF9 - 0x80 = 0x79
  • 2. → 0xA4 - 0x80 = 0x24
  • 3. → 0xB0 - 0x80 = 0x30
  • 4. → 0x99 - 0x80 = 0x19
  • 5. → 0x92 - 0x80 = 0x12
  • 6. → 0x82 - 0x80 = 0x12
  • 7. → 0xF8 - 0x80 = 0x78
  • 8. → 0x80 - 0x80 = 0x00
  • 9. → 0x90 - 0x80 = 0x10

Pin Map

7 Segment Arduino
A 2
B 3
C 4
D 5
E 6
F 7
G 8
DP 9

schematic

아래 회로에서는 330Ω 저항 1개를 VCC에 직접 연결하여 사용하였습니다. 상단부와 하단부 중앙핀은 서로 연결이 되어 있으므로, 둘 중 하나만 +5V에 연결하여 사용합니다.


주의 : 위 회로에서는 간단한 회로 구성을 위해 VCC에 저항을 직접 연결하여 사용하였으나, 이것이 좋은 방법은 아닙니다. FND의 각 LED가 직렬이 아닌 병렬로 연결되어 있는데, 이렇게 구성하는 경우 동일한 전류로 “1”를 표시할때는 2개의 Segment만 켜고, “8”을 표시할때는 7개의 Segment를 모두 키므로, 각 숫자마다 밝기의 차이가 생기길 수 있기 때문이죠. 실제 LED의 밝기에 미세한 변화가 생기는 것을 볼 수 있습니다.

이러한 문제를 해결하기 위해서는 각 LED마다 저항을 따로 사용하여야 합니다. 아래 그림과 같이 220~330Ω 저항 8개를 사용하는 것이 원칙이며, 이렇게 하면 밝기를 고르게 유지할 수 있습니다.


sketch : 0~9까지 차례대로 표시하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
const int A = 2;
const int B = 3;
const int C = 4;
const int D = 5;
const int E = 6;
const int F = 7;
const int G = 8;
const int DP = 9;
int dt = 1000;

void setup() {
pinMode(A, OUTPUT);
pinMode(B, OUTPUT);
pinMode(C, OUTPUT);
pinMode(D, OUTPUT);
pinMode(E, OUTPUT);
pinMode(F, OUTPUT);
pinMode(G, OUTPUT);
pinMode(DP, OUTPUT);

// 7 Segment Ready
digitalWrite (A,HIGH);
digitalWrite (B,HIGH);
digitalWrite (C,HIGH);
digitalWrite (D,HIGH);
digitalWrite (E,HIGH);
digitalWrite (F,HIGH);
digitalWrite (G,HIGH);
digitalWrite (DP,HIGH);
}

void loop() {

// digit 0
digitalWrite (A,LOW);
digitalWrite (B,LOW);
digitalWrite (C,LOW);
digitalWrite (D,LOW);
digitalWrite (E,LOW);
digitalWrite (F,LOW);
digitalWrite (G,HIGH);
digitalWrite (DP,HIGH);
delay(dt);

// digit 1
digitalWrite (A,HIGH);
digitalWrite (B,LOW);
digitalWrite (C,LOW);
digitalWrite (D,HIGH);
digitalWrite (E,HIGH);
digitalWrite (F,HIGH);
digitalWrite (G,HIGH);
digitalWrite (DP,HIGH);
delay(dt);

// digit 2
digitalWrite (A,LOW);
digitalWrite (B,LOW);
digitalWrite (C,HIGH);
digitalWrite (D,LOW);
digitalWrite (E,LOW);
digitalWrite (F,HIGH);
digitalWrite (G,LOW);
digitalWrite (DP,HIGH);
delay(dt);

// digit 3
digitalWrite (A,LOW);
digitalWrite (B,LOW);
digitalWrite (C,LOW);
digitalWrite (D,LOW);
digitalWrite (E,HIGH);
digitalWrite (F,HIGH);
digitalWrite (G,LOW);
digitalWrite (DP,HIGH);
delay(dt);

// digit 4
digitalWrite (A,HIGH);
digitalWrite (B,LOW);
digitalWrite (C,LOW);
digitalWrite (D,HIGH);
digitalWrite (E,HIGH);
digitalWrite (F,LOW);
digitalWrite (G,LOW);
digitalWrite (DP,HIGH);
delay(dt);

// digit 5
digitalWrite (A,LOW);
digitalWrite (B,HIGH);
digitalWrite (C,LOW);
digitalWrite (D,LOW);
digitalWrite (E,HIGH);
digitalWrite (F,LOW);
digitalWrite (G,LOW);
digitalWrite (DP,HIGH);
delay(dt);

// digit 6
digitalWrite (A,LOW);
digitalWrite (B,HIGH);
digitalWrite (C,LOW);
digitalWrite (D,LOW);
digitalWrite (E,LOW);
digitalWrite (F,LOW);
digitalWrite (G,LOW);
digitalWrite (DP,HIGH);
delay(dt);

// digit 7
digitalWrite (A,LOW);
digitalWrite (B,LOW);
digitalWrite (C,LOW);
digitalWrite (D,HIGH);
digitalWrite (E,HIGH);
digitalWrite (F,HIGH);
digitalWrite (G,HIGH);
digitalWrite (DP,HIGH);
delay(dt);

// digit 8
digitalWrite (A,LOW);
digitalWrite (B,LOW);
digitalWrite (C,LOW);
digitalWrite (D,LOW);
digitalWrite (E,LOW);
digitalWrite (F,LOW);
digitalWrite (G,LOW);
digitalWrite (DP,HIGH);
delay(dt);

// digit 9
digitalWrite (A,LOW);
digitalWrite (B,LOW);
digitalWrite (C,LOW);
digitalWrite (D,LOW);
digitalWrite (E,HIGH);
digitalWrite (F,LOW);
digitalWrite (G,LOW);
digitalWrite (DP,HIGH);
delay(dt);

// digit 0.
digitalWrite (A,LOW);
digitalWrite (B,LOW);
digitalWrite (C,LOW);
digitalWrite (D,LOW);
digitalWrite (E,LOW);
digitalWrite (F,LOW);
digitalWrite (G,HIGH);
digitalWrite (DP,LOW);
}

sketch : 배열을 사용하여 숫자 출력하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
int numArray[11][8] = {
{0,0,0,0,0,0,1,1}, // 0
{1,0,0,1,1,1,1,1}, // 1
{0,0,1,0,0,1,0,1}, // 2
{0,0,0,0,1,1,0,1}, // 3
{1,0,0,1,1,0,0,1}, // 4
{0,1,0,0,1,0,0,1}, // 5
{0,1,0,0,0,0,0,1}, // 6
{0,0,0,1,1,1,1,1}, // 7
{0,0,0,0,0,0,0,1}, // 8
{0,0,0,0,1,0,0,1}, // 9
{0,0,0,0,0,0,1,0} // 0.
};
int pins[] = {2, 3, 4, 5, 6, 7, 8, 9};

void setup() {
for (int r = 0; r < 8; r++) {
pinMode(pins[r], OUTPUT);
digitalWrite(pins[r], 1); // 7 Segment Ready
}

// 8 출력
for (int r = 0; r < 8; r++) {
digitalWrite(pins[r], numArray[8][r]); // numbers[8][r]이면 8을 출력
}
delay(1000);
}

void loop()
{
for (int num = 0; num < 11; num++) {
for (int r = 0; r < 8; r++) {
digitalWrite(pins[r], numbers[num][r]); // num의 값을 출력
}
delay(1000);
}
}

sketch : 2진수나 16진수를 이용하여 표시하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
int numArray[10] = {
0b11000000, // 0
0b11111001, // 1
0b10100100, // 2
0b10110000, // 3
0b10011001, // 4
0b10010010, // 5
0b10000010, // 6
0b11111000, // 7
0b10000000, // 8
0b10010000 // 9
};
// int numArray[10] = { 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90 };
// 16진수를 사용해도 됨
// 소수점 포함 int numArray[10] = { 0x40, 0x79, 0x24, 0x30, 0x19, 0x12, 0x02, 0x78, 0x00, 0x10 };

int segPins[7] = {2, 3, 4, 5, 6, 7, 8};
int i, j;

void setup() {
for (i=0; i < 8; i++) {
pinMode(segPins[i], OUTPUT);
}
}

void loop() {
for (i=0; i < 10; i++)
{
segLED(i); // i=1이면 1을 출력
delay(1000);
}
}

void segLED(int num) { // num값을 넘겨받아
int data = numArray[num]; // num번째 배열값을 data로 지정

for (j = 0; j < 8; j++)
{ // 2진수 배열 8비트를 &연산자로 가장 오른쪽11 비트값부터 검사
if(data & 0x01) { // 1이면
digitalWrite(segPins[j], HIGH); // HIGH(끄고)
}
else {
digitalWrite(segPins[j], LOW); // 1이 아니면(0이면)
} // LOW(킨다)
data >>= 1; // data값을 1비트씩 오른쪽으로 쉬프트한 후
} // 7번 추가 실행 (총 8번 실행)
}

sketch : 시리얼모니터에서 값을 입력받아 출력하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
int numArray[10] = { 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90 };
// 소수점 포함 int numArray[10] = { 0x40, 0x79, 0x24, 0x30, 0x19, 0x12, 0x02, 0x78, 0x00, 0x10 };
int segPins[7] = {2, 3, 4, 5, 6, 7, 8};
int i, j;

void setup() {
Serial.begin(9600);
for (i=0; i < 8; i++) {
pinMode(segPins[i], OUTPUT);
}
}

void loop() {
Serial.println("Press any number!(0~9)");
delay(1000);

if (Serial.available()) {
int number = Serial.read();
if(number < 10) {
Serial.println(number");
segLED(number); // i=1이면 1을 출력
delay(1000);
}
else {
Serial.println("Please input a lower number than 10!")
}
}
}

void segLED(int num) { // num값을 넘겨받아
int data = numArray[num]; // num번째 배열값을 data로 지정

for (j = 0; j < 8; j++)
{ // 2진수 배열 8비트를 &연산자로 가장 오른쪽11 비트값부터 검사
if(data & 0x01) { // 1이면
digitalWrite(segPins[j], HIGH); // HIGH(끄고)
}
else {
digitalWrite(segPins[j], LOW); // 1이 아니면(0이면)
} // LOW(킨다)
data >>= 1; // data값을 1비트씩 오른쪽으로 쉬프트한 후
} // 7번 추가 실행 (총 8번 실행)
}



2개의 7 Segment를 사용하여 2자리 숫자 나타내기

1개의 7 Segment를 사용하는데 8개의 Digital Pin이 필요하므로, 2개 이상의 7 Segment를 사용하려면 16개의 Digital Pin이 필요합니다. 핀의 개수로만 보면 Arduino Uno로는 표현할 수 없겠지요. 그러므로 2개의 7 Segment를 전원부 제어를 통해 빠른 시간동안 on/off를 반복함으로써 우리 눈에 나타나는 잔상효과를 이용하는 방법으로 여러개의 FND를 사용하는 Dynamic 구동 방식을 사용합니다.

아래 회로를 예로들면 아두이노의 Digital 2~9번핀이 각각 두개의 FND로 병렬연결 되어 있고, 10번핀은 10의 자리를 나타내는 FND1에, 11번 핀은 1의 자리를 나타내는 FND2에 연결되어 있는 것을 볼 수 있습니다. 이 회로를 통해 10번핀을 LOW, 11번핀을 HIGH로 두면 FND1이 작동하고, 마찬가지로 10번핀을 HIGH, 11번핀을 LOW로 두면 FND2가 작동하게 되죠. 이러한 방식으로 2개의 FND를 10개의 핀으로 작동할 수 있게 되는 것입니다.


sketch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
int numArray[10] = {
0b01000000, // 0
0b01111001, // 1
0b00100100, // 2
0b00110000, // 3
0b00011001, // 4
0b00010010, // 5
0b00000010, // 6
0b01111000, // 7
0b00000000, // 8
0b00010000 // 9
};
// int numArray[10] = { 0x40, 0x79, 0x24, 0x30, 0x19, 0x12, 0x02, 0x78, 0x00, 0x10 };
// 16진수를 사용해도 됨

const int segPins[] = { 2, 3, 4, 5, 6, 7, 8, 9 }; // Segment핀 번호
const int segNum = 2; // Segment 개수 (숫자 자리수)
const int vccPins[segNum] = { 10, 11 }; // 10, 11번 핀을 통해 seg#1, seg#2의 전원 인가

void setup() {
for (int i = 0; i < 8; i++) {
pinMode(segPins[i], OUTPUT); // Segment핀 설정
}
for (int i = 0; i < segNum; i++) {
pinMode(vccPins[i], OUTPUT); // seg#0, seg#1의 전원인가 핀 설정
}
}

void loop() {
for (int i = 0; i < 100; i++) { // 0~99까지 숫자를 표시
for (int n = 0; n < 10; n++) { // 빠르게 카운트할 경우 모든 LED가 켜져 있는 것처럼 보여
showNumber(i); // 숫자를 구분할 수 없으므로, 빠르게 10번씩 점멸시키면서 표시
}
}
}

void showNumber(int number) {
if (number == 0) {
showDigit(number, 0); // 0의 경우 (1의 자리수) Segment에만 0을 출력
}
else {
for (int j = 0; j < segNum; j++) { // 10의 자리, 1의 자리 출력을 위해 2번 실행
if (number > 0) { // 출력값이 0보다 큰 경우에만 실행
showDigit(number % 10, j); // 1의 자리 숫자(%10)를 seg#0(j=0)에 출력 (if 10의 자리 숫자 = (number % 100) / 10))
number = number / 10; // ※ 숫자를 10으로 나눠 10의 자리 숫자를 1의 자리 숫자로 만듦
} // if 처음에 주어진 숫자가 한 자리 수였으면,
delay(5); // → 10으로 나눈 후에는 1의 자리값이 0이 되어, 0보다 큰 값이 안되므로 10의 자리 출력하지 않음
} // if 처음에 주어진 숫자가 두 자리 수였으면
} // → 10으로 나눈 후 10의 자리 숫자가 1의 자리 수가 되므로, 10이 자리수를 seg#1(j=1)에 출력
}

void showDigit(int num, int digit) { // num값과 digit(0→1의 자리수에 표시, 1→10의 자리수에 표시)을 넘겨받아
int data = numArray[num]; // num번째 배열값을 digit자리수에 표시할 data로 지정

digitalWrite(vccPins[0], HIGH); // vcc핀에 전원 인가
digitalWrite(vccPins[1], HIGH); // [0] = Arduino Pin10, [1] = Arduino Pin11

for (int segLED = 0; segLED < 7; segLED++) // 숫자 2진수의 7세그먼트 ON, OFF
{
if (data & 0x01) { // 1이면
digitalWrite(segPins[segLED], HIGH); // HIGH(끄고)
}
else { // 1이 아니면(0이면)
digitalWrite(segPins[segLED], LOW); // LOW(끈다)
}
data >>= 1; // data값을 1비트씩 오른쪽으로 쉬프트한 후
}
digitalWrite(vccPins[digit], LOW); // 숫자 LED 한 자리 ON
}



4 FND

4개의 FND를 Dynamic 방식으로 구동하기 위해서는 8개의 데이터 핀과 4개의 전원부 핀이 필요합니다.


schematic


Pin Map

FND Arduino Segment 자리수
1 6 E
2 5 D
3 13 DP
4 4 C
5 8 G
6 9 S1 1의 자리 수
7 3 B
8 10 S2 10의 자리 수
9 11 S3 100의 자리 수
10 7 F
11 2 A
12 12 S4 1000의 자리 수

sketch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// Segment_X - Arduino Pin // FND Pin
#define SEG_A 2 // 11
#define SEG_B 3 // 7
#define SEG_C 4 // 4
#define SEG_D 5 // 2
#define SEG_E 6 // 1
#define SEG_F 7 // 10
#define SEG_G 8 // 5

// FND_num - Arduino Pin // FND Pin
#define FND1 9 // 6
#define FND2 10 // 8
#define FND3 11 // 9
#define FND4 12 // 12

void seglight(byte number);
byte numbers[] = {
B0111111, // MSB -- LSB
B0000110,
B1011011,
B1001111,
B1100110,
B1101101,
B1111101,
B0000111,
B1111111,
B1101111
};

int FND[] = {SEG_A, SEG_B, SEG_C, SEG_D, SEG_E, SEG_F, SEG_G };
int FND_NO[] = {FND1, FND2, FND3, FND4};

void setup() {
for (int i = 0 ; i < 7; i++) {
pinMode(FND[i], OUTPUT);
}
for (int i = 0 ; i < 4; i++) {
pinMode(FND_NO[i], OUTPUT);
digitalWrite(FND_NO[i], HIGH);
}
}

int ON = OUTPUT;
int intCnt = 0 ;

void loop() {

if(intCnt < 10000) {
intCnt++;
} else {
intCnt = 0;
}

unsigned long start = millis();
for (unsigned long j = 0 ; j < 10 ; j = millis() - start) { // 600 밀리초 마다 실행

// Dynamic 구동
digitalWrite(FND1, LOW); // 1의 자리 켜기
seglight(numbers[intCnt % 10]);
delay(5); // 5ms 유지 후
digitalWrite(FND1, HIGH); // 끄기

digitalWrite(FND2, LOW); // 10의 자리 켜기
seglight(numbers[(intCnt / 10) % 10]);
delay(5); // 5ms 유지 후
digitalWrite(FND2, HIGH); // 끄기

digitalWrite(FND3, LOW); // 100의 자리 켜기
seglight(numbers[(intCnt / 100) % 10]);
delay(5); // 5ms 유지 후
digitalWrite(FND3, HIGH); // 끄기

digitalWrite(FND4, LOW); // 1000의 자리 켜기
seglight(numbers[(intCnt / 1000) % 10]);
delay(5); // 5ms 유지 후
digitalWrite(FND4, HIGH); // 끄기
}
}

// Bit값으로 FND 각 LED 점등
void seglight(byte number) {
for (int i = 0 ; i < 7 ; i++) {
int bit = bitRead(number, i) ;
digitalWrite(FND[i], bit);
}
}