코디와 함께하는 R2D2 제작 🤖 Ep.6

발행: (2026년 6월 7일 PM 11:13 GMT+9)
7 분 소요
원문: Dev.to

Source: Dev.to

에피소드 6: 우키를 승리하게 하라 — R2 움직인다!

“누가 이 거대한 걸어다니는 카펫을 내 길에서 치워줄래?” 🦾

한 솔로가 에피소드 1부터 이 순간을 기다해 온 듯한 에너지로 작업실에 들어선다.

HAN: “자, 들어봐. 깜빡이는 LED는 견뎌냈고, 삑삑 소리는 귀여웠어. 화면도—괜찮고, 유용하고. 하지만 이 드로이드를 실제로 움직여야 해. 지금은 마치 아주 의견이 강한 램프처럼 앉아 있는데, 난 감동받지 않았어.”

R2‑D2가 고음으로 길게 삑삑거린다.

HAN: “그래, 그래, 넌 정말 유능하다는 거 알아. 증명해 봐. 에피소드 6. 시작하자.”

어딘가에서 우키가 열정적으로 포효한다.

HAN: “츄이도 동의한다. 드로이드를 움직이게 하라.”

🗂️ SIPOC — 움직임 시스템

공급자입력프로세스출력고객
당신(제작자)“GPIO14에 돔 회전을 위한 서보, GPIO 25‑27에 휠용 L298N 모터 드라이버 추가”Codey가 ESP32‑S3 LEDC 서보 코드 + L298N 모터 제어를 작성하고 motion.h를 생성좌우로 흔들리는 돔, 앞·뒤·회전이 가능한 휠R2‑D2 — 마침내 명성을 입증한 드로이드
ESP32‑S3 LEDC PWM50 Hz PWM 신호, 3.3 V서보 신호 라인 구동(레벨 시프터 포함)0°, 45°, 90°, 135°, 180° 위치의 돔 서보SG90 서보 — R2의 돔을 회전
L298N 모터 드라이버5 V‑12 V 외부 전원, ESP32‑S3에서 온 방향 + PWM 신호H‑브리지로 각 DC 모터 전류 방향 제어전진·후진·좌회전·우회전두 개의 DC 모터 — R2의 휠 구동
Codey 배선도모터 전원 요구사항 포함 전체 부품 목록모터용 별도 전원 레일을 표시한 색상 구분 다이어그램 작성논리 전원(3.3 V) + 모터 전원 2레일 배선도당신 — 논리 전원과 모터 전원을 정확히 분리

부품 🔧

요다가 긴 생각에 잠긴 표정으로 모터 드라이버를 살펴본다.

YODA: “오늘 추가된 움직이는 부품들. 조심해야 해. 모터는 전류를 탐욕스럽게 잡아먹거든. ESP32‑S3에서 바로 전원을 공급하면—마이크로컨트롤러가 파괴될 거야.”

부품수량비고
ESP32‑S3 N16R81에피소드 5에서 만든 우리의 두뇌
SG90 마이크로 서보1돔 회전용 — 5 V, 3‑선
L298N 듀얼 H‑브리지 모터 드라이버1두 개 DC 모터 구동, 별도 5 V‑12 V 필요
DC 기어 모터(휠 포함)23 V‑6 V, 정지 전류 약 200 mA
74AHCT125 레벨 시프터1서보 신호: 3.3 V → 5 V
9 V 배터리 또는 2S LiPo1모터 전원 공급—논리 전원과 분리
점퍼 와이어12다양한 게이지
USB 케이블1
YODA: “전원 레일은 두 개로 나눌 거야. 논리 전원: USB 5 V → ESP32‑S3용 3.3 V. 모터 전원: L298N과 모터용 별도 배터리. 서로 섞어서는 안 돼.”

ESP32‑S3와 서보 제어: 50 Hz LEDC ⚙️

C‑3PO가 조심스러운 손가락을 들어 경고한다.

C‑3PO: “서보 PWM 상황을 설명해야겠어요. Arduino Servo 라이브러리는 Timer1 하드웨어를 사용했죠. ESP32‑S3에서는 LEDC 주변장치를 사용합니다—에피소드 5에서 부저 톤에 썼던 것과 동일하지만, 서보 제어를 위해 50 Hz로 설정합니다. 표준 취미용 서보는 0.5 ms~2.5 ms 펄스를 50 Hz 주기로 받길 기대합니다. Codey가 이 계산을 자동으로 처리합니다.”

HAN: “쓰리‑PO. 서보. 그냥 서보 말이야.”

C‑3PO: “네. 다음으로 넘어갑시다.”

Agent mode에서:

// 우리는 ESP32‑S3 N16R8에 R2‑D2를 계속 올립니다.

// 움직임 시스템 추가:

// 1. 돔 회전을 위한 SG90 서보:
//    - GPIO14 (74AHCT125 레벨 시프터 통해) 신호
//    - VIN 레일에서 5V 전원
//    - 다음 동작을 구현:
//      a. idle_scan: 60°→120°를 천천히 스윕하고 되돌아감, 주기 3초
//      b. alert_snap: 센서 방향으로 즉시 스냅
//      c. full_sweep: "행복" 상태에서 0°→180°→0° 전체 스윕

// 2. 두 휠용 L298N 모터 드라이버:
//    - Motor A: IN1=GPIO25, IN2=GPIO26, ENA=GPIO27 (PWM)
//    - Motor B: IN3=GPIO32, IN4=GPIO33, ENB=GPIO34 (PWM)
//    - 구현 함수: forward(), backward(), turnLeft(), turnRight(), stop()
//    - 속도: 0‑255를 LEDC 듀티 사이클에 매핑

// 3. 동작 로직 예시:
if (distance == 120) domeSweepDir = -1;
if (currentDomeAngle == 180) { fullSweepDir = -1; }
if (fullSweepAngle <= 0)   { fullSweepDone = true; }
setDomeAngle(fullSweepAngle);
}

// ── L298N MOTOR DRIVER ────────────────────────────────────────────
#define MOTOR_A_IN1  25
#define MOTOR_A_IN2  26
#define MOTOR_A_EN   27   // PWM 채널 2

#define MOTOR_B_IN3  32
#define MOTOR_B_IN4  33
#define MOTOR_B_EN   34   // PWM 채널 3

#define MOTOR_CHANNEL_A  2
#define MOTOR_CHANNEL_B  3
#define MOTOR_FREQ       5000   // 모터용 5 kHz PWM
#define MOTOR_RES        8      // 8‑bit → 0–255

void initMotors() {
  // 방향 핀 설정
  pinMode(MOTOR_A_IN1, OUTPUT);
  pinMode(MOTOR_A_IN2, OUTPUT);
  pinMode(MOTOR_B_IN3, OUTPUT);
  pinMode(MOTOR_B_IN4, OUTPUT);

  // EN 핀용 PWM 채널 설정
  ledcSetup(MOTOR_CHANNEL_A, MOTOR_FREQ, MOTOR_RES);
  ledcSetup(MOTOR_CHANNEL_B, MOTOR_FREQ, MOTOR_RES);
  ledcAttachPin(MOTOR_A_EN, MOTOR_CHANNEL_A);
  ledcAttachPin(MOTOR_B_EN, MOTOR_CHANNEL_B);

  // 정지 상태로 시작
  ledcWrite(MOTOR_CHANNEL_A, 0);
  ledcWrite(MOTOR_CHANNEL_B, 0);
  Serial.println("Motor driver online.");
}

void motorForward(uint8_t speed = 180) {
  digitalWrite(MOTOR_A_IN1, HIGH); digitalWrite(MOTOR_A_IN2, LOW);
  digitalWrite(MOTOR_B_IN3, HIGH); digitalWrite(MOTOR_B_IN4, LOW);
  ledcWrite(MOTOR_CHANNEL_A, speed);
  ledcWrite(MOTOR_CHANNEL_B, speed);
}

void motorBackward(uint8_t speed = 180) {
  digitalWrite(MOTOR_A_IN1, LOW);  digitalWrite(MOTOR_A_IN2, HIGH);
  digitalWrite(MOTOR_B_IN3, LOW);  digitalWrite(MOTOR_B_IN4, HIGH);
  ledcWrite(MOTOR_CHANNEL_A, speed);
  ledcWrite(MOTOR_CHANNEL_B, speed);
}

void motorTurnLeft(uint8_t speed =
0 조회
Back to Blog

관련 글

더 보기 »