ESP32, RC카

샤시 조립

샤시 부품

번호 부품 사진 수량 용도 및 참고사항
1 전륜 회전축 2
2 베어링(대) 2
3 스티어링 컵 2
4 커플러 고정핀 2
5 베어링(중) 2
6 (전륜) 육각 커플러 2
7 4
8 나이록 너트(M4) 2
9 십자형 육각 복스 1
10 볼 조인트 커넥팅 로드 1 홀 간격 80.5mm 유지
11 볼 조인트 커넥팅 로드 1 홀 간격 51mm 유지
12 서보모터
MG996R
1
13 ㄴ자 서보모터 브라켓 2
14 휠 베이스 고정판 2
15 서보 혼 1 (볼트 포함)
16 나이록 너트
(M2.5)
1
17 샤시 하판 1
18 DC엔코더 모터 & 커넥터 2
19 모터 브라켓 2
20 샤프트 2
21 써클립 2
22 샤프트 베어링 4
23 기어
(무두볼트(Set Screw) 포함)
2
24 육각 샤프트 커플링 커넥터 2
25 범퍼 1
26 샤시 상판 1
27 락스위치 1
28 황동 육각 스터드
(M3×22mm)
6 휠 베이스 고정용 4개, 상판 고정용 2개
29 황동 육각 스터드
(M3×16mm)
2 범퍼 고정용 2개
30 볼트
(Pan head, M4×6mm)
6 모터 브라켓과 샤시 하판 고정용 6개
31 볼트
(Bind-Washer head, M3×6mm)
10 서보모터와 서보모터 브라켓 고정용 4개, 서보모터 브라켓과 샤시하판 고정용 4개, 범퍼 고정용 2개
32 볼트
(Bind head, M3×8mm)
![`](/image/r/ESP32-RC-Car-30.png) 14 육각 스터드 고정용 12개, 범퍼 고정용 2개
33 볼트
(Pan head, M2.5×10mm)
7 커넥팅 로드 연결용 3개, 휠 베이스 연결용 4개
34 볼트
(Flat head, M2.5×5mm)
4 모터와 모터브라켓 고정
35 육각렌치 1 기어 무두볼트용
36 저항
1K or 5.1K
2 (선택사항) 인코더 풀업저항
37 홀센서 칩
SH41F(SOT-23타입)
2 (선택사항) 홀 센서를 이중채널로 사용할 필요가 있는 경우에, 각 모터마다 1개씩 추가함
36종

전륜 조향 휠 조립

사진 오른쪽부터 차례대로 8개의 부품을 끼워 휠을 조립한다. (왼쪽휠/오른쪽휠 각각 1개씩, 총 2개 조립)

  1. 먼저 검은색의 스티어링 컵에 큰 베어링을 삽입한다. (상당히 뻑뻑하므로 힘을 강하게 주면서 스티어링컵 안쪽 끝까지 밀어넣어야 한다.)

  1. 베어링이 장착된 스티어링 컵에 회전축을 끼운다. 이때, 회전축 중앙에 있는 작은 구멍이 (고정쇠를 끼울 수 있도록) 보여야 하며, 만약 구멍이 보이지 않으면, 이전 과정에서 베어링을 더 밀어넣어 스티어링 컵에 완전히 밀착시켜야 한다.

  1. 회전축이 나온 스티어링컵 밑바닥에 중간 크기의 베어링을 끼운다. 역시 뻑뻑하므로 강한 힘을 주어 밀어넣어야 한다.

  1. 회전축 중앙의 작은 구멍에 커플러 고정핀를 끼워 넣는다.

  1. 커플러 고정핀 끼워 넣는 홈이 파인 육각 커플러를 준비한 뒤, 커플러 고정핀이 꼽혀있는 방향을 고려하여 스티어링컵에 끼운다.

  1. 스티어링컵에 끼운 육각 커플러를, 휠의 안쪽에 끼워 넣으면(이때, 육각 커플러가 휠의 육각 홈 안쪽까지 완전히 밀착되도록 강한 힘을 가하면서 끼운다.) 휠 바깥쪽면에 회전축의 나사산이 나온다.

  1. 바퀴 바깥쪽으로 빠져나온 회전축 나사를 나이록 너트(M4)를 끼워 고정한다. (십자형 육각 복스로 나이록 너트를 돌릴때 ‘딱딱’하는 소리가 몇번 날때까지 돌리면 고정이 된다.)

  1. 동일한 방법으로 하나를 더 만들어 2개의 휠을 완성한다.

  1. 짧은 커넥팅 로드 1개와 긴 커넥팅 로드 1개, M2.5*10mm 나사(Pan head) 3개를 준비한 뒤,

  1. 오른쪽 휠 컵 암(Arm)에 있는 구멍 2개 중, 끝부분의 구멍(빨간색으로 표시된 부분)에

  1. 준비한 볼트(M2.5*10mm (Pan head)) 1개를 이용하여 긴 커넥팅 로드를 고정한다. 이때, 볼트의 끝 부분을 구멍 아래쪽에서 윗쪽으로 넣듯이 하여 고정한다.

​ 또한, 볼트의 끝부분이 반대편 구멍 바깥쪽으로 튀어나오지 않도록 조임의 정도를 조절한다.

  1. 같은 방법으로 왼쪽 휠 컵의 구멍에 긴 커넥팅 로드의 반대편을 고정한다. (이때, 볼트의 끝부분이 반대편 구멍 바깥쪽으로 튀어나오지 않도록 조임의 정도를 조절한다. )

  2. 오른쪽 휠 컵의 구멍 2개 중, 파란색으로 표시된 부분에는 동일한 볼트를 사용하여 짧은 커넥팅 로드를 연결한다. (이때, 볼트의 끝부분이 반대편 구멍 바깥쪽으로 튀어나오지 않도록 조임의 정도를 조절한다. )

  1. 전륜 휠 베이스 완성된 모습


조향 서보모터 조립

  1. MG996R 서보모터를 준비한 후,

  1. 아래 회로도와 스케치를 참고하여, 서보모터의 초기 위치를 90도 위치로 회전시켜 둔다.

    • schematic

      MG996R 갈색 빨간색 주황색
      ESP32 GND 5V GPIO 15
    • sketch

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      const int ledPin = 15;  // corresponds to GPIO 15

      // setting PWM properties
      const int ledChannel = 0;
      const int freq = 50;
      const int resolution = 16;

      void setup()
      {
      // PWM Setup
      ledcSetup(ledChannel, freq, resolution); // PWM CH0, Frequncy 50 Hz, 16bit resolution
      ledcAttachPin(ledPin, ledChannel); // PWM CH0을 GPIO 15번으로 출력
      }

      void loop()
      {
      ledcWrite(ledChannel, 4910); // 1638 ~ 8192 (90degree = 4910)
      delay(500);
      }
      }
  2. ㄴ자 서보모터 브라켓, 브라켓 고정용 볼트(Pan-Washer head, M3×6mm) 8개, 서보 혼(고정용 볼트 1개 포함)를 준비한다.

  1. 와셔볼트 4개를 사용하여 ㄴ자 브라켓을 고정하고, 서버 혼도 고정용 볼트를 사용하여 장착한다. (아래 사진을 잘 보면서 MG996R 스티커의 글씨 방향, ㄴ자 브라켓이 고정된 방향, 서보 혼 방향(현재 사진에 보이는 서보 날개의 위치가 90도가 됨)에 유의하여 장착한다.)

  1. 샤시 하판에 와셔볼트 4개를 사용하여 서보모터를 고정한다.


샤시 하판에 휠 베이스 고정하기

  1. 준비물: 서보모터가 고정된 샤시 하판, 황동 육각 스터드 M3×22mm 4개, 볼트 M3*8mm 8개, M2.5×10mm 4개, 부채꼴 모양의 휠 베이스 고정판 2개

  1. (위 1번 사진의) 빨간색으로 표시된 부분에 육각 스터드 4개를 고정한다.

  1. (위 1번 사진의) 파란색으로 표시된 부분의 구멍에는, 다음 그림과 같이 긴 커넥팅 로드가 붙어있는 쪽의 휠 컵 구멍(아래 사진에서 파란색으로 표시된 부분)을 맞추어 볼트(M2.5×10mm)로 고정시킨다. (이때, 양쪽 휠 컵의 암(Arm) 부분이 차량 전면을 향하도록 한 상태에서 연결해야 한다.)

  1. 고정이 완료된 상태에서 밑면을 보면 다음과 같으며,

​ 위에서 바라본 모습은 다음과 같다.

  1. (파란색 원으로 표시된 부분) 부채꼴 모양의 휠 베이스 고정판을 양쪽 휠에 있는 육각 스터드와 스티어링 컵의 구멍에 고정한다. (볼트 M3×8mm 8개, M2.5×10mm 4개 사용)

    (빨간색 원으로 표시된 부분) M2.5*10mm 나사와 나이록 너트(M2.5)를 사용하여, 짧은 커넥팅 로드를 서보 혼에 고정한다. 이때, 십자형 육각 복스로 나이록 너트를 돌릴때 ‘딱딱’하는 소리가 몇번 날때까지 돌리면 고정이 된다.)

​ 아래 사진은 완성된 전륜 조향 휠 부분을 정면에서 바라본 모습이다.


후륜 모터 파트 조립

  1. 25GA370 엔코더 모터에 모터 커넥터를 끼우고, 볼트 (Flat head, M2.5×5mm) 2개를 사용하여 모터 브라켓를 고정한다.

  2. 베어링 2개, 샤프트, 써클립, 기어, 육각 샤프트 커플링 커넥터, 써클립(Circlip)을 준비한다.

  1. 샤프트 끝부분에 있는 홈에 써클립을 끼운다.

  1. 모터 브라켓 양쪽 끝에 있는 구멍에 베어링을 끼우고, 써클립을 끼운 샤프트를 밀어넣는다.

​ 샤프트를 끼운 후, 모터를 앞쪽과 뒷쪽에서 보았을때의 모습

  1. 모터에 부착되어 있는 작은 황동 기어에 흰색 플라스틱으로 된 기어가 맞물려지도록 샤프트에 끼우고, 기어 측면에 있는 작은 무두볼트(Set Screw) 2개를 육각렌치를 사용하여 적절하게 분배하여 조여준다. 이때 한쪽 무두볼트만 조여서 고정하면 기어에서 무두볼트가 빠지거나 고정이 되지 않을 수 있다. 이를 고려하여 양쪽의 나사를 적절히 분배하여 조이도록 한다.

  1. 육각 샤프트 커플링 커넥터를 준비하여 샤프트에 끼운 후, 커넥터 측면의 볼트를 돌려 고정한다. 이때, 고정하는 볼트의 끝부분이 샤프트의 편평한 면에 닿도록 조여준다.

  1. 같은 방법으로 후면 왼쪽 모터도 조립한다.

  2. 샤시 하판 뒷편 양쪽에 있는 삼각형 형태로 위치한 구멍에, 모터 브라켓을 올려놓고 볼트(Pan head, M4×6mm)를 사용하여 고정한다.

​ 오른쪽 모터를 샤시 후면에 고정한 모습이다. 같은 방법으로 왼쪽 후면 모터도 고정한다.


범퍼 조립

  1. 육각 스터드(M3×16mm) 2개와 볼트 (Bind-Washer head, M3×6mm) 4개 준비

  1. 준비된 육각 스터드를 샤시 하판의 맨 앞쪽에 고정한다.

  1. 범퍼를 육각기둥에 끼운 후, 와셔볼트를 사용하여 고정한다.


샤시 상판 고정

  1. 육각 스터드(M3×22mm) 2개를 서보모터와 DC엔코더모터 사이에 있는 구멍에 볼트(M3.0×8mm)로 고정한다.

​ 육각 스터드를 고정한 모습이다.

  1. 샤시 상판의 보호필름을 떼어내고, 육각 스터드 홀에 맟춰 올린 뒤,

  1. 볼트(Bind head, M3.0×8mm) 2개를 사용하여 상판을 고정한다.

​ 상판의 오른쪽 하단에 락스위치를 꼽아 고정한 뒤에, 배터리의 (+)라인에 직렬로 배선할 수 있도록 준비한다.


후륜 휠 장착

  1. 육각 휠 너트 앞쪽에 있던 나사를 풀러낸 뒤,

  1. 바퀴를 끼우고, 풀러냈던 나사로 다시 고정한다.


샤시 완성



ESP32 Parts

완성된 샤시의 상판과 하판을 이용하여 ESP32, 브레드보드, 모터드라이버, 배터리, 전원모듈 등을 장작한다.


for RC Car (ESP32 + nRF24L01 + TB6612FNG + 25GA370 + MG996R)

sketch

RC Car에 장착하는 ESP32에 업로드

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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
#include <SPI.h>
#include <RF24.h>
RF24 radio(4, 5);

long long address = 0x1234ABCDEFLL;

struct Value{
uint16_t value_x; // servo motor pwm
uint16_t value_y; // DC motor pwm
};

Value data;

int angle_x = 90; // servo initial position (0~180)
int velo_y = 0; // DC motor inital velocity
int direction_y = 0; // DC motor direction
int i = 0; // joystick center position calculation counter
int center_x = 0; // joystick initial center_x
int center_y = 0; // joystick initial center_y
int ref_xl, ref_xr, ref_ya, ref_yb; // Servo & DC rotation reference value from center position value

//servo setting
const int PIN_SERVO = 15; // servo pwm pin
const int CH_SERVO = 2; // servo pwm channel
const int servoFrequency = 50; // servo pwm frequency (Hz)
const int servoResolution = 16; // servo pwm resolution (bit) (12bit: 0~4095)

// RecvTime
unsigned long lastRecvTime = 0;

void centerData() {
while(i < 100) {
if(radio.available()){
radio.read(&data, sizeof(Value));
center_x = center_x + data.value_x;
center_y = center_y + data.value_y;
i++;
}
}
center_x = center_x / 100; // x축 조이스틱 센터값 (평균)
center_y = center_y / 100; // y축 조이스틱 센터값 (평균)

ref_xl = center_x / 2; // 좌회전 동작 기준값
ref_xr = (4095 + center_x) / 2; // 우회전 동작 기준값
ref_ya = (4095 + center_y) / 2; // accelator 동작 기준값
ref_yb = center_y / 2; // brake 동작 기준값
}

void recvData(){
while(radio.available()){
radio.read(&data, sizeof(Value));
lastRecvTime = millis();
}
}

/*
// TB6612FNG right side & ESP32-WROOM-32D DEVKIT_C V4 left side pin order
const int PIN_PWMA = 32;
const int PIN_INA1 = 33;
const int PIN_INA2 = 25;
const int PIN_STBY = 26;
const int PIN_INB1 = 27;
const int PIN_INB2 = 14;
const int PIN_PWMB = 12;
*/

// TB6612FNG right side & ESP32-WROOM-32 left side pin order
const int PIN_PWMA = 33;
const int PIN_AIN2 = 25;
const int PIN_AIN1 = 26;
const int PIN_STBY = 27;
const int PIN_BIN1 = 14;
const int PIN_BIN2 = 12;
const int PIN_PWMB = 13;

// channel setting (GPIO pin, channel)
// if STBY(CH0) = LOW, Standby states.
// if STBY(CH0) = HIGH, and INA1&INA2 have different value
// and PWMA has some value, CW or CCW rotation is made.

// Channel CW CCW Stanby Stop Brake1 Brake2 Brake3
// PIN_STBY (0) HIGH HIGH LOW HIGH HIGH HIGH HIGH
// PIN_INA1 (1) HIGH LOW * LOW HIGH LOW HIGH
// PIN_INA2 (2) LOW HIGH * LOW HIGH HIGH LOW
// CH_PWMA (3) PWM PWM * HIGH * LOW LOW

// PWM Channel setup
const int CH_PWMA = 0;
const int CH_PWMB = 1;
// PWM frequency and bit resolution setup
const int pwmFrequency = 10000; // Hz
const int bitResolution = 8; // pwm value: 0~255

// led setup
const int PIN_LED = 2;
int ledstate;


void setup() {
// put your setup code here, to run once:
Serial.begin(115200);

radio.begin();
radio.openReadingPipe(1, address);
radio.setPALevel(RF24_PA_LOW);
radio.startListening();

// Servo PWM Setup
ledcSetup(CH_SERVO, servoFrequency, servoResolution); // PWM CH2, Frequncy 50 Hz, 12bit resolution
ledcAttachPin(PIN_SERVO, CH_SERVO); // PWM CH2을 GPIO 15번으로 출력

// center value
centerData();

pinMode(PIN_LED, OUTPUT);

// tb6612fng setting
pinMode(PIN_STBY, OUTPUT);
pinMode(PIN_AIN1, OUTPUT);
pinMode(PIN_AIN2, OUTPUT);
pinMode(PIN_PWMA, OUTPUT);
pinMode(PIN_BIN1, OUTPUT);
pinMode(PIN_BIN2, OUTPUT);
pinMode(PIN_PWMB, OUTPUT);

// motor output(PWM) setting (channel, frequency, bit)
ledcSetup(CH_PWMA, pwmFrequency, bitResolution);
ledcSetup(CH_PWMB, pwmFrequency, bitResolution);
ledcAttachPin(PIN_PWMA, CH_PWMA);
ledcAttachPin(PIN_PWMB, CH_PWMB);
}

void loop() {
ledstate = 0;
unsigned long now = millis();

if( now - lastRecvTime > 5){ // 5ms 마다 서보출력
recvData();
rotate_xy();

move(1, velo_y, direction_y); // motor A(right wheels), velo_y speed, moving direction
move(2, velo_y, direction_y); // motor B(left wheels), velo_y speed, moving direction

if(ledstate > 0) {
digitalWrite(PIN_LED, HIGH);
}
else {
digitalWrite(PIN_LED, LOW);
}
}
}


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(PIN_STBY, HIGH); // move

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

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

if(motor == 1){ // if motor == 1, right wheel
digitalWrite(PIN_AIN1, inPin1);
digitalWrite(PIN_AIN2, inPin2);
ledcWrite(CH_PWMA, speed);
}
else { // if motor is not 1, left wheel
digitalWrite(PIN_BIN1, inPin1);
digitalWrite(PIN_BIN2, inPin2);
ledcWrite(CH_PWMB, speed);
}
}

void stop() {
digitalWrite(PIN_STBY, LOW); // stand-by = stop
}

void rotate_xy() {
Serial.print("x: "); Serial.print(data.value_x);
Serial.print(", y: "); Serial.println(data.value_y);

//x축 서보 구동 조건 설정
if(data.value_x < ref_xl) {
if(angle_x > 55) { // servo 55~125 degree
angle_x--;
}
else {
angle_x = 55;
}
ledstate++;
}
else if(data.value_x > ref_xr) {
if(angle_x < 125) {
angle_x++;
}
else {
angle_x = 125;
}
ledstate++;
}
else {
if(angle_x < 90) {
angle_x++;
}
else if(angle_x > 90) {
angle_x--;
}
else {
angle_x = 90;
}
}
//servo_x.write(angle_x);
servoWrite(CH_SERVO, angle_x);
delay(5); // delay를 더 줄이면 서보 모터 기어에 무리가 감

//y축 DC 구동 조건 설정
if(data.value_y > ref_ya) {
if(direction_y == 0) {
if(velo_y < 255) {
velo_y++;
}
else {
velo_y = 255;
}
}
if(direction_y == 1) {
if(velo_y > 0) {
velo_y--;
}
else {
direction_y = 0;
velo_y = 0;
}
}
ledstate++;
}
else if(data.value_y < ref_yb) {
if(direction_y == 1) {
if(velo_y < 255) {
velo_y++;
}
else {
velo_y = 255;
}
}
if(direction_y == 0) {
if(velo_y > 0) {
velo_y--;
}
else {
direction_y = 1;
velo_y = 0;
}
}
ledstate++;
}
else {
if(velo_y > 0) {
velo_y--;
}
else {
velo_y = 0;
}
}
}

// deg는 0~180도 까지
void servoWrite(int ch, int deg)
{
int servoPWM = map(deg, 0, 180, 2720, 8191); // 16bit (PWM 4.15% ~ 12.5%)
ledcWrite(ch, servoPWM);
}

schematic


16P & 4P jumper block 사용

RC카를 제작해보면 프레임 조립 후 여러가지 구동을 위한 부품을 넣어야 하는데, 큰 브레드보드를 장착하기에는 공간이 좁아 조립이 어려울 수 있다. 그러므로 브레드보드를 제거하고 16P & 4P의 점퍼 블록을 사용해 RC카에 장착해본다.


16P 점퍼블록과 4P 점퍼블록, 그리고 2개의 와이어핀을 준비한다.


2개의 와이어핀을 4P 점퍼블록의 중앙에 꼽아넣음으로써, 4개의 핀소켓이 모두 연결되어 있는 상태가 되도록 만든다. (1개의 와이어만 넣어도 연결은 되지만, 헐거워 고정이 안되기 때문에 2개를 사용하는 것임)


16P 점퍼블록을 사용하여 ESP32 DevkitC V4 보드와 TB6612FNG 모터드라이브를 다음과 같이 연결한다.

32 - PWMA
33 - AIN2
25 - AIN1
26 - NC
27 - BIN1
14 - BIN2
12 - PWMB
GND - GND

ESP32의 5V핀에는 4P 점퍼블록을 꼽아, 5V핀을 3개 사용할 수 있도록 만들어준다. 제작 후 위에서 바라본 모습은 다음과 같다.



for Remote Controller (ESP32 + nRF24L01 + Dual Joystick)

sketch
  • 리모트 컨트롤러로 사용할 TTGO T-energy(ESP32)에 업로드한다.
  • TTGO T-energy를 컴퓨터와 연결하여 사용할 경우에는 반드시 18650 배터리를 제거하거나 스위치를 OFF상태로 두어야 한다.
  • 그러므로 스케치 업로드 시에는 안전한 사용을 위해 18650 배터리를 제거한 뒤 아래의 스케치를 업로드하도록 한다.
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
#include <SPI.h>
#include <RF24.h>
RF24 radio(4, 5);

long long address = 0x1234ABCDEFLL;

// for no interference, two joysticks must be used with each other ADC!
// one joystick use ADC1: GPIO 36,39,32,33,34,35
// the other must use ADC2: GPIO 4,0,2,15,13,12,14,27,25,26
const int Jstick_x_pin = 25; // Left_Right GPIO25
const int Jstick_y_pin = 32; // Forward_Back GPIO32

struct Value{
uint16_t value_x;
uint16_t value_y;
};

Value data;


void setup() {
Serial.begin(115200);

radio.begin();
radio.openWritingPipe(address);
radio.setPALevel(RF24_PA_MIN);
radio.stopListening();
}

void loop() {
data.value_x = Jstick(Jstick_x_pin);
delay(10);
data.value_y = Jstick(Jstick_y_pin);
delay(10);
radio.write(&data, sizeof(Value));

//Serial.print("x: "); Serial.print(data.value_x);
//Serial.print(", y: "); Serial.println(data.value_y);
}

uint16_t Jstick(int Jstick_Pin){
return analogRead(Jstick_Pin);
}

schematic


  • TTGO T-energy 에는 5V 단자가 1개 있음.
  • 2개의 조이스틱에 5V가 각각 입력되어야 하므로, TTGO T-energy의 5V 단자를 2개로 분기할 수 있는 Y자형 케이블을 제작하여 사용하도록 한다.

​ 위 형태는 (오른쪽) 1개의 (F)단자를 (왼쪽) 3개의 (F)단자로 분기하는 형태의 케이블이다.

​ 이를 참고하여 1개의 단자를 2개의 단자로 나눠주는 케이블을 제작하면 된다.

ESP32, DC모터, TB6612FNG

DC모터 사용하기

아두이노의 경우와 마찬가지로 ESP32에서도 TB6612FNG 모터드라이버와 함께, 모터 및 아두이노 단독사용을 위하여 3.7V 18650 2개를 직렬로 연결한 외부전원을 사용한다.


TB6612FNG

기본 핀 배열
VM
VCC
GND(*)
AOUT1
AOUT2
BOUT2
BOUT1
GND
PWMA
AIN2
AIN1
STBY
BIN1
BIN2
PWMB
GND

핀 배열이 다른 경우
GND
VCC
AOUT1
AOUT2
BOUT2
BOUT1
VM
GND
PWMA
AIN2
AIN1
NC (=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의 사용

ESP32와 TB6612FNG모듈을 브레드보드를 통해 연결하기 쉽도록 핀을 구성한 Pinmap이므로, 필요에 따라 수정하여 사용할 수 있다. (아래표는 참고만 할 것!)

핀번호 TB6612FNG 모터드라이브 (기본 핀 배열) ESP32 DEVKIT_C V4 사용시 ESP32 DEVKIT V1 사용시 외부전원 모터 / 역할
1 VM (+) (DC모터에 사용하는 전압 사용)
2 VCC +3.3V +3.3V
3 GND (사용 비추천)
4 A_OUT1 모터A
5 A_OUT2 모터A
6 B_OUT2 모터B
7 B_OUT1 모터B
8 GND (-)
9 GND GND GND
10 B_PWM 12 13 모터B 속도제어
11 B_IN2 14 12 모터B 방향제어
12 B_IN1 27 14 모터B 방향제어
13 STBY 26 27 모터 상태신호
14 A_IN1 25 26 모터A 방향제어
15 A_IN2 33 25 모터A 방향제어
16 A_PWM 32 33 모터A 속도제어

ESP32 DEVKIT V4 사용시
ESP32 DEVKIT V1 사용시


모터 1개 컨트롤하기

(기어박스를 포함한) 일반 DC모터 (엔코더 모듈을 포함한) 엔코더 DC모터
아두이노를 사용한 스마트카 실습용으로 많이 사용됨 Hall sensor를 통해 회전수를 정밀하게 측정가능
(+)와 (-), 2P 단자가 있음 5P or 6P 단자가 있음 (여기서는 (+)와 (-) 2P만 사용)

schematic

ESP32와 컴퓨터를 USB선으로 연결하여 사용할 경우 (단, 모터 외부전원은 사용)


ESP32와 컴퓨터의 연결없이, 외부전원을 사용하여 단독으로 사용할 경우
  • 18650 2S 외부전원(7.4V)을 DC컨버터 모듈에 연결

    • 입력: 8~36V
    • 출력: 1.25~32V 및 USB 5V 출력
    • 75W내 사용 권장, 전류량 최대 5A (4.5A 이내 사용 권장)

  • 여기서는 서보모터 추가 연결을 위해서, 왼쪽 가변 저항을 사용하여 출력 전압을 5V로 세팅하여 사용

  • USB 출력단자는 ESP32의 microUSB 단자에 연결


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
// TB6612FNG right side & ESP32-WROOM-32D DEVKIT_C V4 left side pin order
const int PIN_PWMA = 32;
const int PIN_AIN1 = 33;
const int PIN_AIN2 = 25;
const int PIN_STBY = 26;
const int PIN_BIN1 = 27;
const int PIN_BIN2 = 14;
const int PIN_PWMB = 12;

/*
// TB6612FNG right side & ESP32-WROOM-32 left side pin order
const int PIN_PWMA = 33;
const int PIN_AIN1 = 25;
const int PIN_AIN2 = 26;
const int PIN_STBY = 27;
const int PIN_BIN1 = 14;
const int PIN_BIN2 = 12;
const int PIN_PWMB = 13;
*/

// PWM Channel setup
const int CH_PWMA = 0;
const int CH_PWMB = 1;
// PWM frequency and bit resolution setup
const int pwmFrequency = 10000; // Hz
const int bitResolution = 8; // pwm value: 0~255

const int PIN_LED = 2;

void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
pinMode(PIN_LED, OUTPUT);

// tb6612fng setting
pinMode(PIN_STBY,OUTPUT);
pinMode(PIN_AIN1,OUTPUT);
pinMode(PIN_AIN2,OUTPUT);
pinMode(PIN_PWMA,OUTPUT);
pinMode(PIN_BIN1,OUTPUT);
pinMode(PIN_BIN2,OUTPUT);
pinMode(PIN_PWMB,OUTPUT);

// motor output(PWM) setting (channel, frequency, bit)
ledcSetup(CH_PWMA, pwmFrequency, bitResolution);
ledcSetup(CH_PWMB, pwmFrequency, bitResolution);
ledcAttachPin(PIN_PWMA, CH_PWMA);
ledcAttachPin(PIN_PWMB, CH_PWMB);

// channel setting (GPIO pin, channel)
// if STBY(CH0) = LOW, Standby states.
// if STBY(CH0) = HIGH, and INA1&INA2 have different value
// and PWMA has some value, CW or CCW rotation is made.

// Channel CW CCW Stanby Stop Brake1 Brake2 Brake3
// PIN_STBY (0) HIGH HIGH LOW HIGH HIGH HIGH HIGH
// PIN_INA1 (1) HIGH LOW * LOW HIGH LOW HIGH
// PIN_INA2 (2) LOW HIGH * LOW HIGH HIGH LOW
// CH_PWMA (3) PWM PWM * HIGH * LOW LOW
}

void loop() {

// Motor A : CW
digitalWrite(PIN_STBY, HIGH); // STBY
digitalWrite(PIN_AIN1, HIGH); // AIN1
digitalWrite(PIN_AIN2, LOW); // AIN2
ledcWrite(CH_PWMA, 255); // PWMA
// Motor B : CW
digitalWrite(PIN_BIN1, HIGH); // BIN1
digitalWrite(PIN_BIN2, LOW); // BIN2
ledcWrite(CH_PWMB, 255); // PWMB
delay(3000);

// Motor : Stand-by
digitalWrite(PIN_STBY, LOW); // STBY
//delay(1000);

//LED Blink
digitalWrite(ledPin, LOW);
delay(3500);
digitalWrite(ledPin, HIGH);
delay(500);
digitalWrite(ledPin, LOW);

// Motor A : CCW
digitalWrite(PIN_STBY, HIGH); // STBY
digitalWrite(PIN_AIN1, LOW); // AIN1
digitalWrite(PIN_AIN2, HIGH); // AIN2
ledcWrite(CH_PWMA, 255); // PWMA
// Motor B : CCW
digitalWrite(PIN_BIN1, LOW); // BIN1
digitalWrite(PIN_BIN2, HIGH); // BIN2
ledcWrite(CH_PWMB, 255); // PWMB
delay(3000);

// Motor : Stand-by
digitalWrite(PIN_STBY, LOW); // STBY
//delay(1000);

//LED Blink
digitalWrite(PIN_LED, LOW);
delay(3500);
digitalWrite(PIN_LED, HIGH);
delay(500);
digitalWrite(PIN_LED, LOW);
}

Troubleshooting

스케치 중간에 있는 stand-by가 있거나 없거나 동일한 운동을 보일 것 같지만, 실제로는 완전히 다르다.
  • stand-by가 있는 경우

    • 3초간 CW회전 후 정지 → 3.5초 대기 → 0.5초 LED켜졌다가 꺼짐 → 3초간 CCW회전 후 정지→ 3.5초 대기 → 0.5초 LED켜졌다가 꺼짐
  • stand-by가 없는 경우:

    • 모터
      • 모터의 delay 시간(회전시간)이 3초로 되어 있어도 stand-by 상태로 들어가지 않으므로, 실제로는 7초간 회전 후 방향을 바꿈
      • 7초간 CW회전 후 → (정지하지 않고) 곧바로 7초간 CCW회전 (반복)
    • LED
      • 꺼짐: 6.5초간 꺼져있다가 (모터의 delay 시간 3초 + led low delay 시간 3.5초)
      • 켜짐: 0.5초간 켜짐 (반복)
  • 그러므로, 각 상황에 따라 stand-by를 걸어야 할지, 말아야할지를 결정해야 함.

    • stand-by를 걸지 않은 상태에서 멈추려면 PWMA or PWMB를 0으로 주면 됨.

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

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

1
2
const int AIN2 = 25;
const int AIN1 = 26;

부분을

1
2
const int AIN2 = 26;
const int AIN1 = 25;

로 바꿔준다.



모터 2개 컨트롤하기


sketch

위의 ‘’모터 1개 컨트롤하기’’ 스케치와 동일함.



스케치 최적화

move(), stop()을 만들어서 스케치 최적화

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
// TB6612FNG right side & ESP32-WROOM-32D DEVKIT_C V4 left side pin order
const int PIN_PWMA = 32;
const int PIN_AIN1 = 33;
const int PIN_AIN2 = 25;
const int PIN_STBY = 26;
const int PIN_BIN1 = 27;
const int PIN_BIN2 = 14;
const int PIN_PWMB = 12;

/*
// TB6612FNG right side & ESP32-WROOM-32 left side pin order
const int PIN_PWMA = 33;
const int PIN_AIN1 = 25;
const int PIN_AIN2 = 26;
const int PIN_STBY = 27;
const int PIN_BIN1 = 14;
const int PIN_BIN2 = 12;
const int PIN_PWMB = 13;
*/

// PWM Channel setup
const int CH_PWMA = 0;
const int CH_PWMB = 1;
// PWM frequency and bit resolution setup
const int pwmFrequency = 10000; // Hz
const int bitResolution = 8; // pwm value: 0~255

const int PIN_LED = 2;


void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
pinMode(PIN_LED, OUTPUT);

// tb6612fng setting
pinMode(PIN_STBY,OUTPUT);
pinMode(PIN_AIN1,OUTPUT);
pinMode(PIN_AIN2,OUTPUT);
pinMode(PIN_PWMA,OUTPUT);
pinMode(PIN_BIN1,OUTPUT);
pinMode(PIN_BIN2,OUTPUT);
pinMode(PIN_PWMB,OUTPUT);

// motor output(PWM) setting (channel, frequency, bit)
ledcSetup(CH_PWMA, pwmFrequency, bitResolution);
ledcSetup(CH_PWMB, pwmFrequency, bitResolution);
ledcAttachPin(PIN_PWMA, CH_PWMA);
ledcAttachPin(PIN_PWMB, CH_PWMB);

// channel setting (GPIO pin, channel)
// if STBY(CH0) = LOW, Standby states.
// if STBY(CH0) = HIGH, and INA1&INA2 have different value
// and PWMA has some value, CW or CCW rotation is made.

// Channel CW CCW Stanby Stop Brake1 Brake2 Brake3
// PIN_STBY (0) HIGH HIGH LOW HIGH HIGH HIGH HIGH
// PIN_INA1 (1) HIGH LOW * LOW HIGH LOW HIGH
// PIN_INA2 (2) LOW HIGH * LOW HIGH HIGH LOW
// CH_PWMA (3) PWM PWM * HIGH * LOW LOW
}


void loop() {

move(1, 128, 0); // motor A(right wheels), half speed, moving forward
move(2, 255, 0); // motor B(left wheels), full speed, moving forward
delay(3000);

stop(); // Motor : Stand-by
//delay(1000);

//LED Blink
digitalWrite(ledPin, LOW);
delay(3500);
digitalWrite(ledPin, HIGH);
delay(500);
digitalWrite(ledPin, LOW);

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

stop(); // Motor : Stand-by
//delay(1000);

//LED Blink
digitalWrite(ledPin, LOW);
delay(3500);
digitalWrite(ledPin, HIGH);
delay(500);
digitalWrite(ledPin, LOW);
}

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(PIN_STBY, HIGH); // move

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

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

if(motor == 1){ // if motor == 1, right wheel
digitalWrite(PIN_AIN1, inPin1);
digitalWrite(PIN_AIN2, inPin2);
ledcWrite(CH_PWMA, speed);
} else { // if motor is not 1, left wheel
digitalWrite(PIN_BIN1, inPin1);
digitalWrite(PIN_BIN2, inPin2);
ledcWrite(CH_PWMB, speed);
}
}

void stop() {
digitalWrite(PIN_STBY, LOW); // stand-by = stop
}

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 모듈에 붙어 있는 감도조절기를 십자드라이버로 돌려 라인트레이싱에 가장 최적인 감도를 찾아야 한다.