코디와 함께하는 R2D2 제작 🤖 Ep.6
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 PWM | 50 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 N16R8 | 1 | 에피소드 5에서 만든 우리의 두뇌 |
| SG90 마이크로 서보 | 1 | 돔 회전용 — 5 V, 3‑선 |
| L298N 듀얼 H‑브리지 모터 드라이버 | 1 | 두 개 DC 모터 구동, 별도 5 V‑12 V 필요 |
| DC 기어 모터(휠 포함) | 2 | 3 V‑6 V, 정지 전류 약 200 mA |
| 74AHCT125 레벨 시프터 | 1 | 서보 신호: 3.3 V → 5 V |
| 9 V 배터리 또는 2S LiPo | 1 | 모터 전원 공급—논리 전원과 분리 |
| 점퍼 와이어 | 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 =