이 튜토리얼에서는 어떤 방향으로든 이동할 수 있는 아두이노 메카넘 휠 로봇을 만드는 방법을 배웁니다. 이 독특한 이동성은 메카넘 휠(Mecanum Wheels)이라는 특수 휠을 사용해 달성합니다.
다음 영상을 시청하거나 아래 작성된 튜토리얼을 읽어보세요.
개요
이 휠들은 구매하려면 좀 비싸서 직접 설계하고 3D 프린팅했습니다. 꽤 잘 작동하며 이 로봇 플랫폼을 운전하는 건 정말 재미있습니다. NRF24L01 무선 트랜시버 모듈로 무선 제어하거나, 제 경우 이전 영상에서 만든 DIY RC 송신기로 제어합니다.
스마트폰으로 블루투스 통신 제어도 가능하게 했습니다. 커스텀 안드로이드 앱으로 메카넘 휠 로봇을 어떤 방향으로든 움직일 수 있고, 앱의 슬라이더로 이동 속도도 조절할 수 있습니다.
이 로봇 플랫폼의 두뇌는 아두이노 메가 보드로, 각 휠을 개별 제어합니다. 각 휠은 NEMA 17 스테퍼 모터에 연결되며, 스테퍼 모터를 정밀 제어할 수 있다는 점을 이용해 앱에 자동 이동 프로그래밍 기능을 추가했습니다. Save 버튼으로 각 위치/단계를 저장하면 로봇이 자동으로 이 단계들을 반복 실행합니다. 같은 버튼으로 일시정지, 리셋/삭제 후 새 단계 저장이 가능합니다.
메카넘 휠 로봇 3D 모델
3D 모델링 소프트웨어로 이 메카넘 휠 로봇을 설계했습니다. 로봇의 베이스 플랫폼은 8mm 두께 MDF 보드로 만들 간단한 박스 형태입니다.
네 개의 스테퍼 모터가 이 플랫폼에 장착되고 메카넘 휠이 모터 샤프트에 연결됩니다.
Thangs에서 3D 모델을 찾아 다운로드하거나 브라우저에서 탐색할 수 있습니다.
Thangs에서 어셈블리 3D 모델 다운로드.
3D 프린팅용 STL 파일:
메카넘 휠 작동 원리
메카넘 휠은 휠 둘레에 롤러가 달린 휠입니다. 이 롤러들은 휠 회전축에 대해 대각선(45도) 방향으로 배치됩니다. 이로 인해 휠이 전진/후진할 때 대각선 방향으로 힘을 가하게 됩니다.
휠들을 특정 패턴으로 회전시키면 이 대각선 힘들을 이용해 로봇이 어떤 방향으로든 이동할 수 있습니다.
왼손/오른손 메카넘 휠 두 종류가 필요합니다. 롤러 방향이 다르며 로봇의 특정 위치에 장착해야 합니다. 각 휠의 상단 롤러 회전축이 로봇 중심을 향해야 합니다.
휠 회전 방향에 따른 로봇 이동 데모:
네 휠 모두 전진 → 로봇 전진 (모두 후진 → 후진)
우측 휠은 안쪽(로봇 중앙 방향), 좌측 휠은 바깥쪽으로 회전 → 로봇 우측 이동 (좌측 이동은 반대)
두 휠만 회전시켜 대각선 이동도 가능
메카넘 휠 로봇 제작
이제 로봇 플랫폼 제작 과정을 보여드리겠습니다. 베이스는 8mm MDF 보드로 만듭니다. 테이블 톱으로 3D 모델 치수대로 모든 조각을 자릅니다.
3mm 드릴과 25mm 포스트너 비트로 사이드 패널에 스테퍼 모터 장착용 구멍을 뚫습니다. 조각들이 준비되면 조립합니다. 목공 본드와 나사로 고정합니다. 가장 중요한 건 모터 장착 구멍을 정밀하게 만들어 나중에 네 휠이 지면과 고르게 접촉하게 하는 것입니다.
베이스 플랫폼을 3D 프린팅으로 대체할 수도 있으니 웹사이트 기사에 3D 파일을 포함하겠습니다. 마지막으로 베이스와 커버를 흰색 스프레이 페인트로 도색했습니다.
이제 메카넘 휠 제작입니다. 비싸서 직접 설계해 3D 프린팅했습니다. 휠은 외부/내부 두 파트로 구성되며 M4 볼트/너트로 고정합니다. 각각 10개 롤러와 NEMA 17 스테퍼 모터에 맞는 샤프트 커플러가 있습니다.
Creality CR-10 3D 프린터로 모든 메카넘 휠 부품을 프린팅했습니다.
3D 프린팅 부품이 준비되면 롤러용 샤프트를 만듭니다. 3mm 강철 와이어를 사용하며, 길이는 약 40mm로 로터리 툴로 잘랐습니다.
메카넘 휠 조립 시작: 양쪽 측면과 샤프트 커플러를 M4 볼트/너트 4개로 고정합니다(볼트 길이 45mm).
롤러 장착: 내부 측면 둘레 구멍에 샤프트를 살짝 끼웁니다.
작은 M3 와셔 → 롤러 → 샤프트를 외부 측면 슬롯까지 끝까지 밀어 넣습니다. 반대편 와셔 넣을 공간이 없어 와셔 하나만 사용했습니다.
10개 롤러 모두 같은 과정으로 장착합니다. 꽤 쉽고 재미있는 작업입니다. 중요한 건 롤러가 자유롭게 회전해야 한다는 점입니다.
마지막으로 내부 구멍에 순간접착제 몇 방울 떨어뜨려 샤프트가 풀리지 않게 합니다.
휠이 완성됐으니 로봇 전체 조립으로 넘어갑니다. 먼저 스테퍼 모터를 베이스 플랫폼에 장착합니다. M3 볼트(길이 12mm)로 고정합니다.
휠을 모터 샤프트에 장착합니다. 제가 만든 샤프트 커플러에 M3 너트 슬롯이 있어 M3 볼트로 휠을 샤프트에 고정합니다.
상단 커버 고정을 위해 베이스 두 모서리에 나사봉을 심고 커버에도 같은 위치에 구멍을 뚫어 쉽게 끼우고 고정할 수 있게 했습니다.
베이스 후면에 전원 스위치용 20mm 구멍과 LED용 5mm 구멍을 뚫었습니다.
회로도
이제 전자 부품 연결입니다. 전체 회로도입니다.
네 개의 NEMA 17 스테퍼 모터를 DRV8825 스테퍼 드라이버 4개로 제어합니다(A4988도 가능). 스테퍼와 로봇 전체 전원에는 12V 전원 공급, 제 경우 3S 리튬폴리머 배터리(약 12V)를 사용합니다. 무선 통신용 NRF24L01 모듈, 블루투스 통신용 HC-05 모듈, 배터리 전압 모니터링용 전압 분배기, 배터리 전압 11V 미만 시 점등될 LED를 포함했습니다.
향후 로봇 팔 프로젝트와 결합할 계획이라 서보 모터용 5V 공급을 위해 3A 용량의 전용 5V 전압 레귤레이터도 포함시켰습니다(선택 사항).
필요한 부품:
스테퍼 모터 – NEMA 17
DRV8825 스테퍼 드라이버
NRF24L01 트랜시버 모듈
HC-05 블루투스 모듈
리튬폴리머 배터리
아두이노 메가 보드
PCB 설계
전자 부품을 정리하고 배선 난잡함을 없애기 위해 EasyEDA로 커스텀 PCB를 설계했습니다. 이 PCB는 아두이노 메가 쉴드 역할을 해 아두이노 메가 위에 직접 꽂을 수 있습니다. 탑/보텀 레이어 모두 배선에 사용했습니다. 사용하지 않은 아두이노 핀들도 핀 헤더로 빼두어 향후 확장 가능하게 했습니다. 12V, 5V, GND 연결 핀과 드라이버 스테핑 분해능 선택 핀도 포함했습니다.
PCB 프로젝트 파일 링크입니다. 설계 완료 후 제조용 거버 파일을 생성했습니다.
거버 파일:
JLCPCB(이 영상 후원사)에 PCB를 주문했습니다.
거버 파일을 드래그 앤 드롭해 업로드하면 거버 뷰어에서 검토 가능합니다. 문제 없으면 원하는 PCB 속성 선택 후 주문합니다. 이번엔 아두이노 보드 색상과 맞추려 PCB 색상을 파란색으로 선택했습니다. 합리적인 가격에 주문 가능합니다. JLCPCB 첫 주문 시 최대 10장까지 $2입니다.
며칠 후 PCB 도착. 품질 훌륭하고 설계와 정확히 일치합니다.
PCB 조립
PCB 조립 시작. 작은 부품부터 납땜: 저항, 커패시터. 아두이노 연결용 수 핀 헤더를 PCB에 꽂아 납땜했습니다.
암 핀 헤더들도 제자리에 놓고 납땜. 스테퍼 모터 연결 핀들과 스테핑 분해능 선택 핀들은 수 핀 헤더를 사용했습니다. 이렇게 하면 모터를 PCB에 직접 연결하고 점퍼로 분해능 선택 가능합니다. 단자대, 트리머, 전압 레귤레이터도 납땜했습니다.
PCB 완성. 드라이버 장착과 모터 연결로 넘어갑니다. 먼저 드라이버 스테핑 분해능 선택 점퍼를 꽂습니다. MS3 핀을 5V에 연결해 1/16 스텝 분해능을 선택했습니다.
그 위에 DRV8825 드라이버들을 올리고, NRF24L01 모듈과 HC-05 블루투스 모듈도 연결합니다. 이제 PCB를 아두이노 보드에 꽂기만 하면 됩니다.
배터리를 적절한 단자대에 연결하고 베이스 플랫폼에 넣습니다.
전원 스위치를 제자리에 끼우고 다른 단자대에 연결했습니다. 전원 스위치 바로 위에 배터리 표시 LED도 장착했습니다.
모터를 PCB에 연결합니다. 주의: 반대편 모터 연결 시 커넥터도 반대 방향으로 연결해야 합니다. 나중에 프로그래밍할 때, 예를 들어 전진 명령 시 두 모터가 같은 방향으로 돌게 하기 위함입니다(실제로는 뒤집혀 있어 한 쪽은 시계 방향, 다른 쪽은 반시계 방향).
마지막으로 상단 커버를 끼우면 메카넘 휠 로봇 프로젝트 완료입니다.
메카넘 휠 로봇 아두이노 코드
아두이노 코드를 살펴볼 차례입니다. 실제로는 두 가지 별도 코드가 있습니다. 하나는 NRF24L01 모듈로 제어하는 코드, 다른 하나는 스마트폰으로 제어하는 코드입니다.
NRF24L01 무선 제어용 아두이노 코드
/* === Arduino Mecanum Wheels Robot === Radio control with NRF24L01 by Dejan, www.HowToMechatronics.com Libraries: RF24, https://github.com/tmrh20/RF24/ AccelStepper by Mike McCauley: https://www.airspayce.com/mikem/arduino/AccelStepper/index.html */
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <AccelStepper.h>
RF24 radio(48, 49); // nRF24L01 (CE, CSN)
const byte address[6] = "00001";
unsigned long lastReceiveTime = 0;
unsigned long currentTime = 0;
AccelStepper LeftBackWheel(1, 42, 43);
AccelStepper LeftFrontWheel(1, 40, 41);
AccelStepper RightBackWheel(1, 44, 45);
AccelStepper RightFrontWheel(1, 46, 47);
int wheelSpeed = 1500;
struct Data_Package {
byte j1PotX; byte j1PotY; byte j1Button;
byte j2PotX; byte j2PotY; byte j2Button;
byte pot1; byte pot2;
byte tSwitch1; byte tSwitch2;
byte button1; byte button2; byte button3; byte button4;
};
Data_Package data;
void setup() {
LeftFrontWheel.setMaxSpeed(3000);
LeftBackWheel.setMaxSpeed(3000);
RightFrontWheel.setMaxSpeed(3000);
RightBackWheel.setMaxSpeed(3000);
radio.begin();
radio.openReadingPipe(0, address);
radio.setAutoAck(false);
radio.setDataRate(RF24_250KBPS);
radio.setPALevel(RF24_PA_LOW);
radio.startListening();
Serial.begin(115200);
}
void loop() {
currentTime = millis();
if (currentTime - lastReceiveTime > 1000) {
resetData();
}
if (radio.available()) {
radio.read(&data, sizeof(Data_Package));
lastReceiveTime = millis();
}
// 속도 설정 - 좌측 포텐셔미터
wheelSpeed = map(data.pot1, 0, 255, 100, 3000);
// 조이스틱 입력에 따른 이동 방향 결정
if (data.j1PotX > 150) { moveSidewaysLeft(); }
else if (data.j1PotX < 100) { moveSidewaysRight(); }
else if (data.j1PotY > 160) { moveForward(); }
else if (data.j1PotY < 100) { moveBackward(); }
else if (data.j2PotX < 100 & data.j2PotY > 160) { moveRightForward(); }
else if (data.j2PotX > 160 & data.j2PotY > 160) { moveLeftForward(); }
else if (data.j2PotX < 100 & data.j2PotY < 100) { moveRightBackward(); }
else if (data.j2PotX > 160 & data.j2PotY < 100) { moveLeftBackward(); }
else if (data.j2PotX < 100) { rotateRight(); }
else if (data.j2PotX > 150) { rotateLeft(); }
else { stopMoving(); }
// 스테퍼 실행
LeftFrontWheel.runSpeed();
LeftBackWheel.runSpeed();
RightFrontWheel.runSpeed();
RightBackWheel.runSpeed();
// 배터리 전압 모니터링
int sensorValue = analogRead(A0);
float voltage = sensorValue * (5.0 / 1023.00) * 3;
if (voltage < 11) { digitalWrite(led, HIGH); }
else { digitalWrite(led, LOW); }
}
void moveForward() {
LeftFrontWheel.setSpeed(wheelSpeed);
LeftBackWheel.setSpeed(wheelSpeed);
RightFrontWheel.setSpeed(wheelSpeed);
RightBackWheel.setSpeed(wheelSpeed);
}
void moveBackward() {
LeftFrontWheel.setSpeed(-wheelSpeed);
LeftBackWheel.setSpeed(-wheelSpeed);
RightFrontWheel.setSpeed(-wheelSpeed);
RightBackWheel.setSpeed(-wheelSpeed);
}
void moveSidewaysRight() {
LeftFrontWheel.setSpeed(wheelSpeed);
LeftBackWheel.setSpeed(-wheelSpeed);
RightFrontWheel.setSpeed(-wheelSpeed);
RightBackWheel.setSpeed(wheelSpeed);
}
void moveSidewaysLeft() {
LeftFrontWheel.setSpeed(-wheelSpeed);
LeftBackWheel.setSpeed(wheelSpeed);
RightFrontWheel.setSpeed(wheelSpeed);
RightBackWheel.setSpeed(-wheelSpeed);
}
void rotateLeft() {
LeftFrontWheel.setSpeed(-wheelSpeed);
LeftBackWheel.setSpeed(-wheelSpeed);
RightFrontWheel.setSpeed(wheelSpeed);
RightBackWheel.setSpeed(wheelSpeed);
}
void rotateRight() {
LeftFrontWheel.setSpeed(wheelSpeed);
LeftBackWheel.setSpeed(wheelSpeed);
RightFrontWheel.setSpeed(-wheelSpeed);
RightBackWheel.setSpeed(-wheelSpeed);
}
void moveRightForward() {
LeftFrontWheel.setSpeed(wheelSpeed);
LeftBackWheel.setSpeed(0);
RightFrontWheel.setSpeed(0);
RightBackWheel.setSpeed(wheelSpeed);
}
void moveRightBackward() {
LeftFrontWheel.setSpeed(0);
LeftBackWheel.setSpeed(-wheelSpeed);
RightFrontWheel.setSpeed(-wheelSpeed);
RightBackWheel.setSpeed(0);
}
void moveLeftForward() {
LeftFrontWheel.setSpeed(0);
LeftBackWheel.setSpeed(wheelSpeed);
RightFrontWheel.setSpeed(wheelSpeed);
RightBackWheel.setSpeed(0);
}
void moveLeftBackward() {
LeftFrontWheel.setSpeed(-wheelSpeed);
LeftBackWheel.setSpeed(0);
RightFrontWheel.setSpeed(0);
RightBackWheel.setSpeed(-wheelSpeed);
}
void stopMoving() {
LeftFrontWheel.setSpeed(0);
LeftBackWheel.setSpeed(0);
RightFrontWheel.setSpeed(0);
RightBackWheel.setSpeed(0);
}
void resetData() {
data.j1PotX = 127; data.j1PotY = 127;
data.j2PotX = 127; data.j2PotY = 127;
data.j1Button = 1; data.j2Button = 1;
data.pot1 = 1; data.pot2 = 1;
data.tSwitch1 = 1; data.tSwitch2 = 1;
data.button1 = 1; data.button2 = 1; data.button3 = 1; data.button4 = 1;
}
설명: RF24 라이브러리로 무선 통신, AccelStepper 라이브러리로 스테퍼 모터 제어. 핀 정의, 변수 선언, setup에서 스테퍼 최대 속도 설정과 라디오 통신 초기화.
loop에서 RC 송신기 데이터를 읽습니다. 송신기 코드와 통신 원리는 별도 튜토리얼 참고.
수신 데이터에 따라 이동 방향 결정. 예: 좌측 조이스틱 전진(값 > 160) 시 moveForward() 호출. 이 함수는 모든 모터 속도를 양수로 설정. 후진은 음수. 다른 방향도 휠 회전을 적절히 설정해 구현(초반 설명 참조).
loop에서 runSpeed()로 모든 스테퍼 실행. 배터리 전압도 전압 분배기 아날로그 입력으로 읽어 11V 미만 시 LED 점등.
스마트폰 제어용 아두이노 코드
/* === Arduino Mecanum Wheels Robot === Smartphone control via Bluetooth by Dejan, www.HowToMechatronics.com Libraries: RF24, https://github.com/tmrh20/RF24/ AccelStepper by Mike McCauley: https://www.airspayce.com/mikem/arduino/AccelStepper/index.html */
#include <SoftwareSerial.h>
#include <AccelStepper.h>
SoftwareSerial Bluetooth(A8, 38); // Arduino(RX, TX) - HC-05 Bluetooth (TX, RX)
AccelStepper LeftBackWheel(1, 42, 43);
AccelStepper LeftFrontWheel(1, 40, 41);
AccelStepper RightBackWheel(1, 44, 45);
AccelStepper RightFrontWheel(1, 46, 47);
#define led 14
int wheelSpeed = 1500;
int dataIn, m;
int lbw[50], lfw[50], rbw[50], rfw[50];
int index = 0;
void setup() {
LeftFrontWheel.setMaxSpeed(3000);
LeftBackWheel.setMaxSpeed(3000);
RightFrontWheel.setMaxSpeed(3000);
RightBackWheel.setMaxSpeed(3000);
Serial.begin(38400);
Bluetooth.begin(38400);
Bluetooth.setTimeout(1);
delay(20);
pinMode(led, OUTPUT);
}
void loop() {
if (Bluetooth.available() > 0) {
dataIn = Bluetooth.read();
if (dataIn == 0) { m = 0; }
if (dataIn == 1) { m = 1; }
if (dataIn == 2) { m = 2; }
if (dataIn == 3) { m = 3; }
if (dataIn == 4) { m = 4; }
if (dataIn == 5) { m = 5; }
if (dataIn == 6) { m = 6; }
if (dataIn == 7) { m = 7; }
if (dataIn == 8) { m = 8; }
if (dataIn == 9) { m = 9; }
if (dataIn == 10) { m = 10; }
if (dataIn == 11) { m = 11; }
if (dataIn == 12) { m = 12; }
if (dataIn == 14) { m = 14; }
if (dataIn >= 16) { wheelSpeed = dataIn * 10; Serial.println(wheelSpeed); }
}
if (m == 4) { moveSidewaysLeft(); }
if (m == 5) { moveSidewaysRight(); }
if (m == 2) { moveForward(); }
if (m == 7) { moveBackward(); }
if (m == 3) { moveRightForward(); }
if (m == 1) { moveLeftForward(); }
if (m == 8) { moveRightBackward(); }
if (m == 6) { moveLeftBackward(); }
if (m == 9) { rotateLeft(); }
if (m == 10) { rotateRight(); }
if (m == 0) { stopMoving(); }
// SAVE 버튼
if (m == 12) {
if (index == 0) {
LeftBackWheel.setCurrentPosition(0);
LeftFrontWheel.setCurrentPosition(0);
RightBackWheel.setCurrentPosition(0);
RightFrontWheel.setCurrentPosition(0);
}
lbw[index] = LeftBackWheel.currentPosition();
lfw[index] = LeftFrontWheel.currentPosition();
rbw[index] = RightBackWheel.currentPosition();
rfw[index] = RightFrontWheel.currentPosition();
index++;
m = 0;
}
// RUN 버튼
if (m == 14) {
runSteps();
if (dataIn != 14) {
stopMoving();
memset(lbw, 0, sizeof(lbw));
memset(lfw, 0, sizeof(lfw));
memset(rbw, 0, sizeof(rbw));
memset(rfw, 0, sizeof(rfw));
index = 0;
}
}
LeftFrontWheel.runSpeed();
LeftBackWheel.runSpeed();
RightFrontWheel.runSpeed();
RightBackWheel.runSpeed();
// 배터리 전압 모니터링
int sensorValue = analogRead(A0);
float voltage = sensorValue * (5.0 / 1023.00) * 3;
if (voltage < 11) { digitalWrite(led, HIGH); }
else { digitalWrite(led, LOW); }
}
void runSteps() {
for (int i = index - 1; i >= 0; i--) {
LeftFrontWheel.moveTo(lfw[i]); LeftFrontWheel.setSpeed(wheelSpeed);
LeftBackWheel.moveTo(lbw[i]); LeftBackWheel.setSpeed(wheelSpeed);
RightFrontWheel.moveTo(rfw[i]); RightFrontWheel.setSpeed(wheelSpeed);
RightBackWheel.moveTo(rbw[i]); RightBackWheel.setSpeed(wheelSpeed);
while (LeftBackWheel.currentPosition() != lbw[i] & LeftFrontWheel.currentPosition() != lfw[i] & RightFrontWheel.currentPosition() != rfw[i] & RightBackWheel.currentPosition() != rbw[i]) {
LeftFrontWheel.runSpeedToPosition();
LeftBackWheel.runSpeedToPosition();
RightFrontWheel.runSpeedToPosition();
RightBackWheel.runSpeedToPosition();
if (Bluetooth.available() > 0) {
dataIn = Bluetooth.read();
if (dataIn == 15) { // PAUSE
while (dataIn != 14) {
if (Bluetooth.available() > 0) {
dataIn = Bluetooth.read();
if (dataIn == 13) { stopMoving(); break; }
}
}
}
if (dataIn >= 16) { wheelSpeed = dataIn * 10; dataIn = 14; }
if (dataIn == 13) { break; }
}
}
}
// 역방향 실행
for (int i = 1; i <= index - 1; i++) {
LeftFrontWheel.moveTo(lfw[i]); LeftFrontWheel.setSpeed(wheelSpeed);
LeftBackWheel.moveTo(lbw[i]); LeftBackWheel.setSpeed(wheelSpeed);
RightFrontWheel.moveTo(rfw[i]); RightFrontWheel.setSpeed(wheelSpeed);
RightBackWheel.moveTo(rbw[i]); RightBackWheel.setSpeed(wheelSpeed);
while (LeftBackWheel.currentPosition() != lbw[i] & LeftFrontWheel.currentPosition() != lfw[i] & RightFrontWheel.currentPosition() != rfw[i] & RightBackWheel.currentPosition() != rbw[i]) {
LeftFrontWheel.runSpeedToPosition();
LeftBackWheel.runSpeedToPosition();
RightFrontWheel.runSpeedToPosition();
RightBackWheel.runSpeedToPosition();
if (Bluetooth.available() > 0) {
dataIn = Bluetooth.read();
if (dataIn == 15) {
while (dataIn != 14) {
if (Bluetooth.available() > 0) {
dataIn = Bluetooth.read();
if (dataIn == 13) { stopMoving(); break; }
}
}
}
if (dataIn >= 16) { wheelSpeed = dataIn * 10; dataIn = 14; }
if (dataIn == 13) { break; }
}
}
}
}
// 이동 방향 함수들 (위 NRF24L01 코드와 동일)
void moveForward() { ... }
void moveBackward() { ... }
void moveSidewaysRight() { ... }
void moveSidewaysLeft() { ... }
void rotateLeft() { ... }
void rotateRight() { ... }
void moveRightForward() { ... }
void moveRightBackward() { ... }
void moveLeftForward() { ... }
void moveLeftBackward() { ... }
void stopMoving() { ... }
설명: 스마트폰 제어 코드도 매우 비슷하고 같은 방식으로 작동합니다. 라디오 모듈 대신 블루투스 모듈을 정의하고 setup에서 통신 초기화. 다시 스마트폰/앱에서 들어오는 데이터 읽고 로봇 이동 방향 지시.
안드로이드 앱을 보면 버튼 눌릴 때 블루투스로 0~15 숫자를 보냅니다.
앱은 MIT App Inventor로 만들었으며 자세한 내용은 별도 튜토리얼 참고.
앱과 편집 가능한 프로젝트 파일 다운로드:
이 앱으로 자동 로봇 이동 프로그래밍: "SAVE" 버튼 누르면 현재 스테퍼 모터 위치를 배열에 저장. "RUN" 버튼 누르면 runSteps() 커스텀 함수가 for/while 루프로 저장된 모든 단계 실행.
이 튜토리얼이 즐겁고 유익했길 바랍니다. 질문은 댓글로 남겨주시고 아두이노 프로젝트 모음 도 확인해보세요.