이 튜토리얼에서는 아두이노 기반 RC 수신기를 만드는 방법을 배웁니다. 이전 영상에서 DIY 아두이노 RC 송신기를 제작한 후, 많은 분들이 전용 수신기 제작을 요청하셔서 이렇게 준비했습니다.
다음 영상을 시청하거나 아래 작성된 튜토리얼을 읽어보세요.
이제 이 두 장치는 쉽게 통신할 수 있으며, 다양한 장치를 무선으로 제어하는 데 사용할 수 있습니다. 몇 가지 예제를 통해 작동 원리를 설명하겠습니다. 첫 번째 예제에서는 이 아두이노 RC 수신기를 사용해 두 개의 DC 모터로 구성된 간단한 자동차를 제어합니다. 두 번째 예제에서는 브러시리스 모터와 서보 모터 제어 방법을 보여드립니다. 이들은 상용 RC 비행기, 보트, 자동차 등에 흔히 사용되는 부품입니다. 제어 방법을 알면 직접 제작한 아두이노 송신기로 많은 RC 모델을 개조하고 제어할 수 있습니다.
세 번째 예제에서는 이 아두이노 기반 RC 시스템을 개조해 상용 RC 자동차를 제어하는 방법을 보여드리겠습니다.
아두이노 RC 수신기 회로도
먼저 이 시스템의 회로도를 살펴보겠습니다. 무선 통신은 NRF24L01 트랜시버 모듈을 기반으로 합니다.
송신기는 조이스틱, 버튼, 포텐셔미터, 토글 스위치 등 컨트롤러의 데이터를 지속적으로 전송하고, 수신기는 이 데이터를 수신합니다. 이 아두이노 송신기의 작동 원리에 대한 자세한 내용은 별도 튜토리얼을 참고하세요.
이 RC 수신기는 제가 만든 특정 송신기뿐만 아니라, 아두이노 보드와 NRF24L01 모듈로 구성된 다른 유사한 설정과도 호환됩니다.
이 RC 수신기의 핵심은 아두이노 프로 미니(Arduino Pro Mini) 보드입니다. 전원 공급은 5V를 연결하는 VCC 핀이나 6~12V를 연결하는 RAW 핀을 사용할 수 있습니다. 아두이노 프로 미니에는 5V 버전(여기서 사용)과 3.3V 버전 두 가지가 있습니다. 반면 NRF24L01 모듈은 3.3V로 동작하므로 전압 레귤레이터가 필요합니다. 여기서는 5~12V 입력에서 3.3V를 출력하는 AMS1117 전압 레귤레이터를 사용했습니다.
이 아두이노 RC 수신기에 필요한 부품은 아래 링크에서 구할 수 있습니다:
NRF24L01 트랜시버 모듈
NRF24L01 + PA + LNA (고출력 버전)
AMS1117 3.3V 전압 레귤레이터
핀 헤더 (수/암)
아두이노 프로 미니
사용한 것과 같은 아두이노 프로 미니
NRF24L01 모듈은 아두이노와 SPI 프로토콜로 통신하며, 추가로 두 개의 디지털 핀을 사용합니다. 따라서 9개의 디지털 핀이 출력 채널로 남으며, 그중 두 개는 RX와 TX 핀입니다. 이 핀들은 아두이노에 스케치를 업로드하는 동안 아무 것도 연결되지 않은 상태여야 하므로, 별도의 핀 헤더를 통해 연결/분리할 수 있도록 했습니다. 또한 아날로그 입력 핀도 디지털 출력으로 사용할 수 있어, 작은 보드임에도 꽤 많은 출력 채널을 확보할 수 있습니다.
PCB 설계
회로를 콤팩트하게 만들기 위해 EasyEDA 무료 온라인 회로 설계 소프트웨어로 커스텀 PCB를 제작했습니다. 여기서는 8개 채널을 5V와 GND 레일 바로 옆에 배치해 서보와 ESC를 직접 연결할 수 있게 했습니다. 9번 채널은 아두이노의 VCC 핀 근처에 따로 배치했는데, 예를 들어 ESC의 BEC(Battery Eliminator Circuit) 기능으로 5V를 공급받아 아두이노에 전원을 공급하는 용도로 사용할 수 있습니다. 물론 다른 채널로도 가능합니다. VCC 핀이 5V 레일과 연결되어 있기 때문입니다.
7번과 8번 채널은 핀 헤더로 끊어두었습니다. 사용하려면 두 핀을 연결하면 됩니다. 프로그래밍 헤더는 오른쪽 상단에 있으며, 100uF 커패시터는 전압 레귤레이터와 NRF24L01 모듈 모두를 위해 사용됩니다. PCB 왼쪽 하단에는 아날로그 핀들을 배치했습니다.
한 가지 더: 일부 아두이노 프로 미니 보드는 핀 배열이 다를 수 있어, 보드에 맞는 버전을 선택할 수 있도록 PCB 버전을 하나 더 포함했습니다.
이 PCB의 프로젝트 파일 링크입니다. 설계가 완료되면 PCB 제조에 필요한 거버 파일을 생성합니다.
거버 파일:
거버 파일을 JLCPCB(이 영상의 후원사)에 주문했습니다.
거버 파일을 드래그 앤 드롭해 업로드하면 거버 뷰어에서 PCB를 검토할 수 있습니다. 문제가 없으면 원하는 PCB 속성을 선택하고 주문하면 됩니다. JLCPCB 첫 주문 시 최대 5장까지 $2에 제작 가능합니다.
PCB 조립
며칠 후 PCB가 도착했습니다. 품질이 훌륭하고 설계와 정확히 일치합니다.
이제 PCB를 조립합니다. 먼저 아두이노 보드의 핀 헤더를 납땜합니다. 브레드보드에 핀 헤더를 꽂아 고정하면 납땜하기 편합니다. 앞서 언급했듯 보드에 따라 핀 배열이 다를 수 있으니 납땜 시 주의하세요.
PCB 하단에 트레이스가 지나가는 그라운드 핀들은 비워둬야 합니다. 아두이노 보드 납땜 후 핀의 여분 길이를 잘라냈습니다.
나머지 핀 헤더들을 제자리에 놓고 납땜합니다. 수/암 핀 헤더 모두 필요하지만, 어떤 것을 쓸지는 자유입니다. 다만 디지털 채널에는 수 핀 헤더를 추천합니다. 서보 모터와 ESC 커넥터가 암 타입이라 바로 연결하기 좋습니다.
전압 레귤레이터는 SMD 부품이라 블루택 접착제로 고정한 뒤 납땜했습니다. 마지막으로 두 커패시터를 납땜하고 NRF24L01 모듈을 해당 핀 헤더에 장착하면 됩니다.
용도와 필요한 통신 거리에 따라 온보드 안테나 타입이나 외부 안테나 장착 타입(개방 공간에서 최대 700m 통신 가능) 중 선택해 사용하세요. 이제 아두이노 RC 수신기 완성입니다.
수신기 프로그래밍이나 아두이노 프로 미니를 컴퓨터에 연결하려면 USB-시리얼 UART 인터페이스를 프로그래밍 헤더에 연결하면 됩니다.
아두이노 IDE 도구 메뉴에서 보드: "Arduino Pro or Pro Mini", 프로세서 버전 선택, 포트 선택, 프로그래머: "USBasp"로 설정하세요.
이제 아두이노에 코드를 업로드할 수 있습니다.
예제 1 – 아두이노 RC 자동차
첫 번째 예제를 살펴보겠습니다.
두 개의 12V DC 모터로 구성된 간단한 자동차입니다. 이전 영상에서 작동 원리와 제작 방법을 다뤘습니다.
이번에는 새 아두이노 RC 수신기로 제어합니다. DC 모터 구동에는 L298N 모터 드라이버를, 전원에는 3개의 리튬이온 배터리(약 12V)를 사용합니다.
이 예제에 필요한 부품:
L298N 드라이버
12V 고토크 DC 모터
플라스틱 타이어 휠 포함 DC 모터
브레드보드와 점퍼 와이어
연결은 간단합니다. 배터리에서 나온 12V를 수신기의 12V 핀에 연결하고, 드라이버의 6개 제어 핀을 6개 채널에 연결합니다. 모터 속도 제어를 위해 드라이버의 Enable A, B 핀에 PWM 신호를 줘야 하는데, 수신기의 2, 3, 6, 9번 채널이 PWM 출력이 가능합니다. 여기서는 Enable 핀을 2번과 6번 채널에 연결했습니다.
아두이노 코드
/* Arduino RC Receiver - Car Example by Dejan, www.HowToMechatronics.com Library: TMRh20/RF24, https://github.com/tmrh20/RF24/ */
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#define enA 9 // 아두이노 D9 - PCB CH6 - PWM 출력
#define in1 8 // D8 - CH5
#define in2 7 // D7 - CH4
#define in3 6 // D6 - CH3
#define in4 4 // D4 - CH1
#define enB 5 // D5 - CH2 - PWM 출력
RF24 radio(3, 2); // nRF24L01 (CE, CSN)
const byte address[6] = "00001";
unsigned long lastReceiveTime = 0;
unsigned long currentTime = 0;
// 최대 32바이트 (NRF24L01 버퍼 제한)
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;
int steering, throttle;
int motorSpeedA = 0;
int motorSpeedB = 0;
void setup() {
pinMode(enA, OUTPUT);
pinMode(enB, OUTPUT);
pinMode(in1, OUTPUT);
pinMode(in2, OUTPUT);
pinMode(in3, OUTPUT);
pinMode(in4, OUTPUT);
// Serial.begin(9600);
radio.begin();
radio.openReadingPipe(0, address);
radio.setAutoAck(false);
radio.setDataRate(RF24_250KBPS);
radio.setPALevel(RF24_PA_LOW);
radio.startListening(); // 수신 모드 설정
resetData();
}
void loop() {
// 데이터 수신 지속 여부 확인 (연결 끊김 감지)
currentTime = millis();
if (currentTime - lastReceiveTime > 1000) {
// 1초 이상 데이터 수신 없으면 연결 끊김으로 판단
resetData(); // 데이터 초기화로 원치 않는 동작 방지 (예: 드론이 스로틀 업 상태에서 연결 끊김 시 날아가는 것 방지)
}
// 수신 데이터 확인
if (radio.available()) {
radio.read(&data, sizeof(Data_Package)); // 전체 데이터 읽어 구조체에 저장
lastReceiveTime = millis(); // 데이터 수신 시점 기록
}
// 조이스틱 1 데이터 파싱: 스로틀(전진/후진), 스티어링(좌/우)
throttle = data.j1PotY;
steering = data.j1PotX;
// 조이스틱 값: 0~255, 아래=0, 중간=127, 위=255
// 스로틀: 전진/후진 제어
if (throttle < 110) {
// 후진
digitalWrite(in1, HIGH);
digitalWrite(in2, LOW);
digitalWrite(in3, HIGH);
digitalWrite(in4, LOW);
// 110~0 값을 0~255 PWM 값으로 매핑
motorSpeedA = map(throttle, 110, 0, 0, 255);
motorSpeedB = map(throttle, 110, 0, 0, 255);
} else if (throttle > 140) {
// 전진
digitalWrite(in1, LOW);
digitalWrite(in2, HIGH);
digitalWrite(in3, LOW);
digitalWrite(in4, HIGH);
// 140~255 값을 0~255 PWM 값으로 매핑
motorSpeedA = map(throttle, 140, 255, 0, 255);
motorSpeedB = map(throttle, 140, 255, 0, 255);
} else {
// 중간 영역: 모터 정지
motorSpeedA = 0;
motorSpeedB = 0;
}
// 스티어링: 좌/우 제어
if (steering < 110) {
// 좌회전: 왼쪽 모터 감속, 오른쪽 모터 가속
int xMapped = map(steering, 110, 0, 0, 255);
motorSpeedA = motorSpeedA - xMapped;
motorSpeedB = motorSpeedB + xMapped;
if (motorSpeedA < 0) motorSpeedA = 0;
if (motorSpeedB > 255) motorSpeedB = 255;
}
if (steering > 140) {
// 우회전: 오른쪽 모터 감속, 왼쪽 모터 가속
int xMapped = map(steering, 140, 255, 0, 255);
motorSpeedA = motorSpeedA + xMapped;
motorSpeedB = motorSpeedB - xMapped;
if (motorSpeedA > 255) motorSpeedA = 255;
if (motorSpeedB < 0) motorSpeedB = 0;
}
// 저속에서 버징 방지 (모터에 따라 조정 필요. 내 모터는 PWM 70 미만에서 회전 안 함)
if (motorSpeedA < 70) motorSpeedA = 0;
if (motorSpeedB < 70) motorSpeedB = 0;
analogWrite(enA, motorSpeedA); // 모터 A에 PWM 신호 전송
analogWrite(enB, motorSpeedB); // 모터 B에 PWM 신호 전송
}
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;
}
설명: 먼저 SPI와 RF24 라이브러리를 포함하고, 핀 정의, 라디오 객체, 송신기에서 들어올 데이터를 저장할 구조체를 정의합니다. setup에서 핀 모드 설정과 라디오 통신 초기화를 합니다. 각 라인에 대한 자세한 설명은 별도 NRF24L01 튜토리얼을 참고하세요.
loop에서는 지속적으로 데이터 수신 여부를 확인하고, 수신되면 데이터를 읽습니다. 송신기 코드를 보면 조이스틱, 포텐셔미터, 버튼 등 모든 컨트롤러 값을 읽어 단일 패킷으로 전송합니다.
수신한 데이터로 원하는 제어를 구현합니다. 여기서는 조이스틱 1의 Y축을 스로틀(전진/후진), X축을 스티어링(좌/우)에 사용합니다. 조이스틱 값은 0~255 범위입니다. 스틱을 아래로 밀면 후진, 위로 밀면 전진, 좌우로 기울이면 회전합니다. 코드 하단의 resetData() 함수는 통신 두절 시 모든 값을 기본값으로 초기화해 자동차가 정지하게 합니다.
예제 2 – 아두이노 RC 수신기로 서보 및 브러시리스 모터 제어
두 번째 예제: 서보 모터와 브러시리스 모터 제어입니다.
브러시리스 모터 제어에는 ESC(전자 변속기)가 필요합니다. 아두이노는 단일 핀으로 ESC와 통신하며, 특정 PWM 신호를 보내 모터 속도를 제어합니다. ESC는 같은 연결로 BEC 기능을 통해 5V를 공급하므로 수신기 전원으로도 쓸 수 있습니다.
서보 모터도 ESC와 같은 방식의 연결을 사용하므로 사용 가능한 채널에 바로 연결할 수 있습니다.
필요한 부품:
브러시리스 모터
ESC 30A
리튬폴리머 배터리
MG996R 서보 모터
서보와 ESC(브러시리스 모터용) 제어 신호는 거의 동일합니다. 50Hz PWM 신호를 사용하며, 아두이노 Servo 라이브러리로 쉽게 생성할 수 있습니다.
주의: 이 설정에서 MG996R 서보를 사용하면 회로에 문제를 일으켜 아두이노 프로 미니가 타버릴 수 있습니다. 높은 전류를 끌어쓰며 5V 레일에 전압 스파이크를 발생시킬 수 있습니다. 아두이노 프로 미니의 5V 핀은 최대 5.5V까지 견디지만, 스파이크가 발생하면 손상될 수 있습니다. 테스트 중 이 문제를 겪었고, 댓글에서도 같은 보고가 있었습니다. 서보 쪽에 큰 디커플링 커패시터를 달면 해결될 수 있으나 테스트해보진 않았습니다. 이 점 유의하세요.
반면 예제에서 쓴 다른 MG996R 서보 두 개는 아두이노를 태우지 않았습니다(스파이크가 덜했던 듯). 소형 S90 서보 4개로는 문제가 없었습니다.
송신기에서 받은 데이터를 서보용 0~180도 값으로 변환해 write() 함수로 제어합니다. ESC는 1000~2000 마이크로초 범위의 PWM 신호를 쓰므로, 조이스틱 1의 중간(127)~최대(255) 값을 1000~2000으로 매핑해 writeMicroseconds()로 보냅니다. 이렇게 하면 브러시리스 모터 속도를 최저~최고까지 제어할 수 있습니다.
이 방식으로 RC 비행기, 자동차, 보트 등을 제어할 수 있습니다. 이들은 주로 서보와 브러시리스 모터를 사용하기 때문입니다.
예제 3 – RC 자동차 모델 제어
이 RC 자동차는 자체 컨트롤러로 앞바퀴 조향과 전진/후진을 제어합니다.
저가형이라 제어가 디지털 방식(완전 좌/우, 최대 속도만 가능)입니다. 그래도 분해해 내부 구조를 확인하고 아두이노 RC 수신기를 적용해봤습니다.
내부를 보니 두 모터 모두 5V로 동작하는 일반 DC 모터였습니다. 조향용 앞 모터도 연속 회전형 DC 모터였습니다.
이미 DC 모터 제어법을 알므로, 기존 기판을 우리 DIY 아두이노 수신기로 교체하기 쉽습니다. 수신기 외에 두 모터를 동시에 구동할 모터 드라이버만 있으면 됩니다. 첫 예제에서 쓴 L298N 드라이버도 가능하지만, 이 용도엔 너무 큽니다. 그래서 MX1508 드라이버를 선택했습니다. H-브리지와 PWM 제어 기능이 있는 소형 듀얼 DC 모터 드라이버로, 제어 입력 4핀, 모터 출력 4핀, 전원 2핀으로 구성됩니다.
RC 자동차 기판에서 모터 연결을 떼어 드라이버에 납땜했습니다. 뒷면에 전원 핀을 납땜하고, 이제 드라이버와 수신기를 연결합니다. 전원은 자동차 하단의 4.8V Ni-Cd 배터리에서 공급받습니다.
점퍼 와이어로 배터리 단자를 아두이노 VCC 핀에 연결하고, 드라이버의 4개 제어 입력 핀을 4개 디지털 채널에 연결했습니다. 이 드라이버도 PWM 제어를 지원하므로, 후방 모터(모터 B)에는 PWM 채널 2번과 3번을 사용했습니다.
조이스틱 데이터로 스로틀과 스티어링을 제어합니다. 후진 시 analogWrite()로 Input3에 PWM 신호를 보내고 Input4는 LOW로 유지합니다. 전진은 그 반대입니다. 스틱이 중간에 있으면 드라이버에 정지 명령을 보냅니다. 조향 모터도 같은 원리지만 속도 제어는 필요 없어 analogWrite()를 쓰지 않습니다.
스케치 업로드 후 자동차를 재조립했습니다. 소형 드라이버를 내부 하우징에 넣고 나사로 고정했습니다. 수신기와 드라이버를 다시 연결하고 외부 하우징 아래에 수신기를 넣었습니다(충분한 공간이 있었습니다).
이제 이 RC 자동차를 우리 DIY 송신기로 제어할 수 있을 뿐 아니라, PWM 제어가 추가되어 속도 조절도 가능해졌습니다. 개조하려는 RC 모델에 서보와 브러시리스 모터가 쓰인다면 두 번째 예제 방식을 따르세요.
이 튜토리얼이 즐겁고 유익했길 바랍니다. 질문은 댓글로 남겨주시고, 아두이노 프로젝트 모음도 확인해보세요.