Codey와 함께하는 R2D2 제작 🤖 3편

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

출처: Dev.to

에피소드 3: 돔이 깨어나다

“이 전압 수준에 대해 나쁜 예감이 들어요” ⚡

C‑3PO가 12‑LED NeoPixel 링을 들고 작업실에 들어선다. 마치 열폭탄을 다루는 듯한 조심스러운 경외심을 가지고 있다.

C‑3PO: “우선 이 연결에 대해 최소 23가지의 잠재적 실패 모드를 계산했음을 말씀드리고 싶습니다. NeoPixel WS2812B LED 링은 5V 로직에서 동작합니다. Arduino UNO R3 역시 5V 로직을 사용합니다. 지금까지는 아주 좋습니다. 하지만 이 작업실에서 어떤 상황에서도 ESP32(3.3V 로직) 사용을 시도하려는 사람이 있다면—”

R2‑D2가 “어서 해라”라는 의미의 비프음으로 끼어든다.

C‑3PO: “네, 알겠습니다. Codey의 전압 안전 검사가 그 대화를 대신해 줄 겁니다. 시작할까요?”

🗂️ SIPOC — 돔 라이트 시스템

공급자입력프로세스출력고객
당신(제작자)“R2‑D2 돔 라이트용 12‑LED NeoPixel 링을 핀 6에 추가”Codey가 Adafruit_NeoPixel 라이브러리를 선택하고 라이트 애니메이션 코드를 생성완성된 .ino.h 헤더 파일 (애니메이션 패턴)Arduino UNO R3 – 12개의 RGB LED 구동
Codey 전압 안전 검사NeoPixel 링(5V) + Arduino UNO(5V)보드 전압과 부품 로직 요구 사항 교차 확인호환 시 초록불, 아니면 레벨 시프터 권고와 경고당신 – 매직 스모크를 피하는 사람
NeoPixel 링 (12 LED)5V 전원 + 핀 6의 PWM 데이터 신호WS2812B 프로토콜이 각 RGB LED를 개별적으로 구동12개의 독립 주소 지정 가능한 전색 LEDR2‑D2의 돔 – 개성 있게 빛남
클라우드 컴파일러다중 파일 프로젝트: r2d2-dome.ino + animations.hCodey 서버에 사전 설치된 Adafruit_NeoPixel으로 컴파일업로드 준비된 펌웨어Arduino UNO – 웹 시리얼을 통해

부품 소개 🔧

루크가 작업대 위에 놓인 새 부품들을 살펴본다.

LUKE: “이거 정말 멋지다. 파란색과 흰색… 영화 속 돔과 똑같아.”

부품수량비고
Arduino UNO R31에피소드 1에서 사용
NeoPixel 링 (12 LED, WS2812B)15V 입력 – 뒷면 흰색 글씨는 5V 버전 의미
470Ω 저항1데이터 라인 보호 – 신호 링잉 방지
100µF 전해 콘덴서1전원 디커플링 – 5V와 GND 사이
점퍼 와이어4
USB 케이블1

LUKE: “왜 콘덴서가 필요하지?”

요다가 부품 서랍 근처에 나타난다.

YODA: “NeoPixel은 처음 켤 때 급격한 전류 요구가 있어. 그 스파이크를 부드럽게 하는 게 콘덴서다. 없으면 애니메이션 도중 Arduino가 리셋될 수도 있지.”

LUKE: “전혀 몰랐어요—”

YODA: “배울 것이 아직 많다. 하지만 Codey와 함께라면 빨리 배울 수 있겠지.”

Codey 프롬프트: 돔 설명 🎙️

Codey 프로젝트를 엽니다. 먼저 Plan 모드(계획 검토)로, 그 다음 Agent 모드로 빌드합니다:

Plan 모드 먼저, 그 다음 agent:

I'm building R2-D2's dome light system.
Add a 12-LED NeoPixel ring (WS2812B) connected to digital pin 6
with a 470 ohm resistor on the data line.
Add a 100uF capacitor across the power pins for stability.

Create these light animations:
  1. "idle" — gentle blue-white breathing pattern, slow
  2. "alert" — fast red sweep around the ring, 3 times
  3. "happy" — rainbow sweep, then blue-white sparkle
  4. "scan" — single blue LED orbiting the ring slowly

Cycle through idle → scan → idle → happy → idle → alert.
Use non-blocking millis() timing throughout.
Split the animations into a separate animations.h file.
Keep the main .ino clean and readable.
Enter fullscreen mode


Exit fullscreen mode

Plan Mode 미리보기

Codey가 코드를 작성하기 전에 계획을 보여줍니다:

Plan for R2-D2 Dome Light System:

1. Main file (r2d2-dome.ino):
   - Include Adafruit_NeoPixel.h and animations.h
   - Setup: initialise ring, set brightness
   - Loop: call state machine from animations.h

2. animations.h:
   - Define RING_PIN = 6, NUMPIXELS = 12
   - Adafruit_NeoPixel ring object
   - State enum: IDLE, ALERT, HAPPY, SCAN
   - Functions: animateIdle(), animateAlert(),
     animateHappy(), animateScan()
   - Timing via millis() — non-blocking

3. Wiring diagram:
   - Pin 6 → 470Ω → NeoPixel DIN
   - 5V → Cap+ and NeoPixel 5V
   - GND → Cap− and NeoPixel GND

Proceed with Agent mode to write the code?
Enter fullscreen mode


Exit fullscreen mode

C‑3PO가 안도하는 표정으로 계획을 읽는다.

C‑3PO: “계획! 코드보다 앞서 있군요! 정말 체계적이군요. 전 이 접근법을 전적으로 승인합니다.”

Proceed 버튼을 클릭하면 — Codey가 Agent 모드로 전환돼 코드를 작성합니다.

생성된 코드: r2d2-dome.ino

// r2d2-dome.ino
// R2-D2 Dome Light System — Main File
// Board: Arduino UNO R3
// NeoPixel ring (WS2812B, 12 LEDs) on pin 6

#include 
#include "animations.h"

void setup() {
  Serial.begin(9600);
  initAnimations();
  Serial.println("R2-D2 dome lights online!");
}

void loop() {
  updateAnimations();
}

생성된 헤더: animations.h

// animations.h
// R2-D2 Dome Light Animations
// NeoPixel ring (WS2812B, 12 LEDs) on pin 6

#pragma once
#include 

#define RING_PIN    6
#define NUMPIXELS   12
#define BRIGHTNESS  80   // 0-255 — keep under 100 for USB power

Adafruit_NeoPixel ring(NUMPIXELS, RING_PIN, NEO_GRB + NEO_KHZ800);

enum DomeState { IDLE, SCAN, HAPPY, ALERT };

DomeState currentState = IDLE;
unsigned long stateStartMs = 0;
unsigned long lastUpdateMs = 0;

// ── State durations (ms) ────────────────────────────────────────
const unsigned long IDLE_DURATION  = 5000;
const unsigned long SCAN_DURATION  = 4000;
const unsigned long HAPPY_DURATION = 3500;
const unsigned long ALERT_DURATION = 2500;

// ── Dome state machine order ────────────────────────────────────
DomeState stateOrder[] = {IDLE, SCAN, IDLE, HAPPY, IDLE, ALERT};
int stateIndex  = 0;
const int NUM_STATES = 6;

// ── Animation helpers ───────────────────────────────────────────
float breatheValue = 0.0f;
float breatheDir   = 0.02f;
int   scanPos      = 0;
int   alertSweep   = 0;
int   alertCount   = 0;
int   rainbowStep  = 0;

uint32_t blue(uint8_t brightness) {
  return ring.Color(0, 0, brightness);
}
uint32_t white(uint8_t brightness) {
  return ring.Color(brightness, brightness, brightness);
}
uint32_t blueWhite(int pos, uint8_t br) {
  return (pos % 3 == 0) ? white(br) : blue(br);
}

// ── IDLE: gentle blue-white breathing ───────────────────────────
void animateIdle() {
  unsigned long now = millis();
  if (now - lastUpdateMs = 1.0f || breatheValue = NUMPIXELS) {
    alertSweep = 0;
    alertCount++;
  }
}

// ── State machine ────────────────────────────────────────────────
void init
0 조회
Back to Blog

관련 글

더 보기 »