ESP32-S3 WS2812 LED 링 레인보우 플로우 회전 효과 완벽 튜토리얼
난이도: ⭐⭐☆☆☆ (초보자도 시작 가능) 예상 소요 시간: 30분 테스트 환경: Arduino IDE 2.3.8 + FastLED v3.10.3 + ESP32 Arduino Core 3.3.8
TL;DR (빠른 시작):
- 배선: WS2812 LED 링
DIN→ ESP32-S3GPIO40,VCC→ 5V,GND→ GND- 라이브러리 설치: Arduino 라이브러리 매니저에서
FastLED(작성자 Daniel Garcia) 검색 후 최신 버전 설치- 필요에 따라 코드의
NUM_LEDS(LED 개수)와LED_PIN(핀 번호) 수정- 업로드 후 전원 인가, LED 링 회전 시작
들어가며
집에 WS2812 LED 링이 하나 방치되어 있었습니다. “나중에 시간 날 때 해봐야지”라고 미루다가 먼지 쌓인 걸 보고, 간단한 예제를 만들어보기로 했습니다.
WS2812 시리즈 LED 스트립/링/모듈의 가장 큰 장점은 전체 LED를 단 한 가닥의 데이터 선으로 제어할 수 있다는 것입니다. 전원선까지 포함해 총 3가닥만으로 구동 가능하며, 각 LED는 내장 드라이버 칩 덕분에 독립적으로 색상 제어가 가능합니다. 디코더도, 시프트 레지스터도 필요 없고, 코드 몇 십 줄이면 끝납니다.
이 글의 목표: ESP32-S3 + FastLED 라이브러리를 사용해 레인보우 색상이 링을 따라 회전하는 플로우 애니메이션을 구현합니다. 전체 논블로킹 방식으로 이후 기능 확장에도 영향을 주지 않습니다.
실험 결과

LED 링의 40개 LED가 동시에 점등되며, 색상이 레인보우 그라데이션으로 분포하고 전체 색조가 계속 회전하여, 마치 컬러 빛이 한 바퀴 흐르는 것처럼 보입니다.
---
부품 설명
WS2812 LED 링
WS2812 LED 링은 “말하면 전해주기” 게임과 같습니다. 모든 데이터를 첫 번째 LED에게 보내면, 자기 몫의 색상 정보만 남기고 나머지를 다음 LED로 전달합니다. 다음 LED도 자기 몫의 색상만 남기고 또 그 다음으로 전달하는 방식이 계속 이어집니다. 이것이 데이지 체인(daisy-chain) 방식이며, 단 한 가닥의 선으로 수십 개(심지어 수백 개)의 LED를 각각 다른 색상으로 제어할 수 있습니다.
예시: 데이터 명령 시퀀스: [빨강, 파랑, 초록, 노랑]
↓
LED ¹ "빨강"을 가져와 빨간색 점등 → [파랑, 초록, 노랑]을 다음으로 전달
↓
LED ² "파랑"을 가져와 파란색 점등 → [초록, 노랑]을 다음으로 전달
↓
LED ³ "초록"을 가져와 초록색 점등 → [노랑]을 다음으로 전달
...
...
...
| 파라미터 | 수치 |
|---|---|
| 구동 전압 | 5V |
| LED 1개당 최대 전류 | 60mA (R/G/B 각 20mA, 전체 켜짐) |
| 데이터 신호 레벨 | 3.3V 로직 호환 (레벨 변환 불필요) |
| 통신 프로토콜 | 단일 버스 NZR (Non-Return-to-Zero) |
| 컬러 순서 | GRB |
| 리프레시 레이트 | 400Hz / 800Hz (모델에 따라 다름) |
선택 이유: 배선이 매우 간단(데이터 선 1가닥), FastLED 기본 지원, 커뮤니티 자료 풍부, 초보자도 실수하기 어려움.
WS2812B 한 가닥의 데이터 선(단일 GPIO)으로 이론적/실제로 몇 개의 LED를 구동할 수 있을까?
이론적 한계
거의 엄격한 제한이 없습니다 (수천 개, 심지어 수만 개까지 구동 가능). WS2812B는 데이지 체인(daisy-chain) 방식을 사용하여, 뒤쪽 LED의 DO 단자가 다음 LED의 DI 단자에 연결되고 데이터가 하나씩 아래로 전달됩니다. 마이크로컨트롤러가 제때 완전한 데이터 프레임을 보낼 수 있다면 이론적으로 무한 연결이 가능합니다.
실제 권장 수량 (한 가닥 데이터 선)
| 사용 시나리오 | 권장 최대 수량 | 설명 |
|---|---|---|
| 부드러운 애니메이션/게임 (고 주사율) | 300~600개 | 권장 범위, 주사율 30~60fps 이상 유지 가능 |
| 일반 효과/분위기 조명 | 800~1200개 | 일반적인 상한, 주사율 약 15~30fps |
| 극한 상황 | 2000~4000+개 | 가능하지만 주사율이 매우 낮음 (<10fps), 신호 문제 발생 가능 |
| 전문/대형 프로젝트 | 수천~수만 개 | 여러 데이터 선으로 분할하여 병렬 구동 필수 (ESP32에 매우 적합) |
주요 제한 요소
- 주사율 (가장 중요) LED 1개당 데이터 약 30μs (24bit).
- 1000개 ≈ 30ms → 약 33fps
- 2000개 ≈ 60ms → 약 16fps (눈에 띄는 끊김)
- 신호 품질
- 데이터 선이 너무 길거나(>10~15m) LED 수가 너무 많으면 뒤쪽 LED에서 노이즈, 색상 오류, 깜빡임이 발생하기 쉽습니다.
- 500~1000개 정도마다 신호 증폭기(74HCT245 / SN74AHCT125 등) 또는 중계 모듈 추가를 권장합니다.
- 전원 (데이터 선의 제한은 아니지만 반드시 해결해야 함)
- 각 LED 전체 백색 점등 시 최대 약 60mA (일반적으로 평균 20~30mA).
- 반드시 다중 포인트 전원 공급 (1~2m마다 전원 공급), 그렇지 않으면 전압 강하로 후단이 어두워지거나 색상이 변합니다.
BOM (부품 목록)
| 부품 | 사양 | 수량 |
|---|---|---|
| ESP32-S3 개발 보드 | GPIO가 있는 아무 버전이나 가능 | ×1 |
| WS2812 LED 링 | 40개 LED (또는 다른 수량, 코드에서 한 줄 수정) | ×1 |
| 점퍼 와이어 | 암-수 / 수-수, 실제 필요에 따라 | 약간 |
부품 핀 설명
WS2812 LED 링은 일반적으로 다음 4개 핀이 있습니다:
| 핀 표시 | 설명 |
|---|---|
| VCC / 5V | 전원 양극, 5V에 연결 |
| GND | 전원 음극, GND에 연결 |
| DIN / Data In | 데이터 입력, ESP32-S3 GPIO에 연결 |
| DOUT / Data Out | 데이터 출력, 여러 LED 링을 연결할 때 사용, 이 프로젝트에서는 사용 안 함 |
⚠️ 일부 LED 링은
+,-,Data로만 표시되어 있을 수 있습니다. 동일한 대응이니 당황하지 마세요.
배선 방법
| WS2812 LED 링 핀 | ESP32-S3 |
|---|---|
| VCC / 5V | 5V (개발 보드 5V 핀 또는 외부 5V) |
| GND | GND |
| DIN | GPIO40 |
💡 배선 완료 후 핀 하나하나 다시 확인하는 것을 추천합니다. 디버깅 시간의 80%를 줄일 수 있습니다. 특히 VCC를 3.3V에 연결하지 마세요 — LED는 켜지지만 색상이 틀리고 밝기가 떨어져 불필요한 디버깅 시간만 낭비하게 됩니다.
설치 필요 라이브러리
Arduino IDE 라이브러리 매니저에서 **FastLED**를 검색하세요. 작성자는 Daniel Garcia이며, 최신 버전을 설치합니다 (이 글 테스트 버전: v3.10.3).
설치 경로: 도구 → 라이브러리 관리 → FastLED 검색 → 설치
전체 코드
/*
* ESP32-S3 WS2812 레인보우 플로우 회전 링
* FastLED 논블로킹 버전 — loop()를 막지 않아 이후 버튼, 센서 등 기능 추가가 용이
*/
#include <FastLED.h>
// ===== 실제 상황에 맞게 여기를 수정하세요 =====
#define LED_PIN 40 // 데이터 선이 연결된 GPIO 핀
#define NUM_LEDS 40 // LED 링의 LED 개수
#define BRIGHTNESS 204 // 전체 밝기, 범위 0 (전체 꺼짐) ~ 255 (전체 켜짐)
// ====================================
#define LED_TYPE WS2812B
#define COLOR_ORDER GRB // WS2812의 색상 순서는 GRB입니다, RGB가 아니니 헷갈리지 마세요
CRGB leds[NUM_LEDS]; // 각 LED의 색상 배열
uint8_t gHue = 0; // 레인보우 시작 색상, 매 프레임 증가하여 "회전" 효과 구현
void setup() {
// 첫 번째: 하드웨어에 1초 시작 시간 부여, 전원 인가 순간 전류 서지로 인한 LED 깜빡임 방지
delay(1000);
// 두 번째: FastLED 초기화, 어느 핀을 사용하는지, 어떤 LED인지, 몇 개인지 알려줌
FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS)
.setCorrection(TypicalLEDStrip); // 색온도 자동 보정, 흰색이 더 희게 보이도록
// 세 번째: 전체 밝기 설정 (RGB 값을 변경하는 것보다 여기를 수정하는 것이 편리)
FastLED.setBrightness(BRIGHTNESS);
}
void loop() {
// 네 번째: 레인보우 그라데이션으로 전체 LED 링 채우기
// gHue는 시작 색상, 255/NUM_LEDS는 각 LED 사이의 색상 간격
fill_rainbow(leds, NUM_LEDS, gHue, 255 / NUM_LEDS);
// 다섯 번째: 색상 데이터를 LED 링으로 전송
FastLED.show();
// 여섯 번째: 10ms마다 색상 +1, 값이 작을수록 회전이 빠르고, 클수록 느림
EVERY_N_MILLISECONDS(10) {
gHue++;
}
}
코드 설명
| 주요 코드 | 역할 |
|---|---|
fill_rainbow(...) | FastLED 내장 함수, 레인보우 그라데이션 색상을 자동 계산하여 배열에 채움, HSV 계산을 직접 작성할 필요 없음 |
FastLED.show() | leds[] 배열의 색상 데이터를 GPIO40을 통해 전송, 이 줄을 호출하기 전까지 LED는 변하지 않음 |
EVERY_N_MILLISECONDS(10) | FastLED 내장 논블로킹 타이머, “10ms마다 한 번씩 실행”과 동일, loop()를 막지 않음 |
gHue++ | 매번 색상 +1, 다음 프레임에서 fill_rainbow의 시작 색상이 이동하여 회전하는 것처럼 보임 |
setCorrection(TypicalLEDStrip) | LED 색온도 자동 보정, 혼합 후 흰색이 녹색으로 치우치지 않도록, WS2812에 적합 |
회전 속도를 변경하려면:
EVERY_N_MILLISECONDS(10)의 숫자를 조정하세요. 10 → 5는 2배 빠르게, 10 → 20은 절반 속도로 느려집니다.
자주 발생하는 문제 해결
당황하지 마세요. 문제의 90%는 다음 몇 가지에서 발생합니다:
문제 1: 전원 인가 후 LED가 전혀 켜지지 않음
- DIN이
GPIO40에 연결되어 있는지 확인 (코드에서LED_PIN으로 정의한 핀) - VCC가 5V에 연결되어 있는지 확인 (3.3V가 아님)
- GND가 연결되어 있는지 확인 — GND가 공통이 아니면 데이터 신호가 전송되지 않습니다
문제 2: 일부 LED만 켜지거나 색상이 랜덤하게 깜빡임
- 십중팔구 전원 부족입니다. 40개 LED가 전체 백색 점등 시 전류가 최대 2.4A까지 올라가며, USB 포트의 500mA로는 부족합니다. 외부 5V 2A 이상 전원 사용을 권장합니다
문제 3: 색상이 이상함 — 빨간색을 지정했는데 녹색으로 표시됨
COLOR_ORDER정의가 잘못되었습니다. WS2812B는 GRB 순서입니다. 코드에서GRB를RGB로 변경해 보거나, 그 반대로 해보세요
문제 4: 컴파일 에러 FastLED.h: No such file
- 라이브러리가 설치되지 않았습니다. 라이브러리 매니저를 다시 열어 FastLED 상태가 “설치됨”으로 표시되는지 확인한 후 Arduino IDE를 재시작하세요
문제 5: 업로드는 정상이지만 LED가 계속 움직이지 않음
NUM_LEDS가 실제 LED 링의 LED 개수와 일치하는지 확인하세요. 수량이 맞지 않으면 표시가 비정상적으로 됩니다
FAQ
Q: WS2812와 WS2812B의 차이는 무엇이며, 코드를 공통으로 사용할 수 있나요?
A: WS2812B는 WS2812의 업그레이드 버전으로, 패키지가 더 작고 타이밍이 약간 조정되었습니다. FastLED는 둘 다 지원합니다. LED_TYPE에 WS2812B를 입력하면 되며, 다른 코드는 수정할 필요가 없습니다.
Q: 내 LED 링이 12/16/24개인데 코드를 어떻게 수정하나요?
A: 한 줄만 수정하세요: #define NUM_LEDS 24를 실제 LED 개수로 변경하면 되고, 나머지는 그대로 두면 됩니다.
Q: GPIO40을 다른 핀으로 변경할 수 있나요?
A: 가능합니다. ESP32-S3의 대부분의 GPIO를 사용할 수 있습니다 (0, 3, 45, 46 등 부팅 관련 핀은 피하세요). #define LED_PIN 40의 숫자를 변경하고, 배선도 해당 핀으로 변경하세요.
Q: 여러 LED 링을 동시에 구동할 수 있나요?
A: 가능합니다. 각 LED 링을 독립적인 GPIO에 연결하고, 코드에서 addLeds를 한 번 더 호출하여 다른 leds[] 배열 구간을 할당하면 됩니다.
Q: LED 링에 독립적인 전원 공급이 필요한가요? A: LED 수가 8개 이하이고 밝기를 최대로 올리지 않는다면 개발 보드의 5V 핀으로 구동 가능합니다. 8개를 초과하거나 전체 백색 점등이 필요한 경우 외부 5V 2A 이상 전원 사용을 강력히 권장하며, 외부 전원의 GND와 개발 보드의 GND를 공통으로 연결해야 합니다.
Q: EVERY_N_MILLISECONDS는 무엇이며, 왜 delay()를 직접 사용하지 않나요?
A: EVERY_N_MILLISECONDS는 FastLED 내장 논블로킹 타이머입니다. loop()는 정상적으로 실행되며, 지정된 시간 간격으로 내부 코드를 한 번씩 실행할 뿐입니다. delay()를 사용하면 전체 프로그램이 거기서 멈추어 버튼, 시리얼 통신 등을 동시에 처리할 수 없습니다.
Q: 레인보우 회전 방향을 반대로 할 수 있나요?
A: 가능합니다. gHue++를 gHue--로 변경하면 역방향으로 회전합니다.
더 활용해 보기
- 버튼을 추가하여 효과 전환 (호흡등 / 마퀴 효과 / 레인보우 플로우 자유롭게 전환)
- 마이크 모듈을 연결하여 오디오 반응형 LED 스펙트럼 효과 구현
- 여러 LED 링을 데이지 체인으로 연결, DIN을 첫 번째의 DOUT에 연결하여 더 긴 LED 스트립 효과 구현
- OLED 디스플레이를 연결하여 현재 효과 이름과 밝기 수치 표시