Chapter 8. Neopixel(WS2812B) 화려한 조명 제어
단 한 가닥의 데이터 선으로 수많은 RGB LED를 개별 제어하는 기술을 배웁니다. Pico 2의 고속 PIO 기능을 활용해 부드러운 무지개 효과를 구현합니다.
Neopixel: VCC→5V(VSYS), DIN→GP22, GND→GND. LED 개수가 많으면 별도 5V 전원을 사용하고 GND는 Pico와 공통으로 묶습니다.
8.1 준비물과 연결 핀
Neopixel은 데이터 선 하나로 여러 LED를 순서대로 제어합니다. 첫 번째 LED의 DIN 방향을 꼭 확인하고, 전류가 커지는 프로젝트에서는 밝기를 제한해서 시작하세요.
| Neopixel 핀 | Pico 2 W 핀 | 설명 |
|---|---|---|
| DIN | GP22 | 색상 데이터 입력 |
| 5V / VCC | VSYS 또는 외부 5V | LED 전원 |
| GND | GND | 공통 기준 전압 |
8.2 WS2812B(Neopixel) 통신 원리 쉽게 이해하기
WS2812B는 하나의 데이터 선(DIN)만으로 수십~수백 개의 LED를 각각 다른 색으로 제어할 수 있는 "주소 지정형(addressable)" LED입니다. 초보자가 헷갈리기 쉬운 핵심 3가지를 짚고 넘어갑시다.
💡 ① 데이터가 체인처럼 전달됩니다
Pico가 GP22로 색상 데이터를 보내면, 첫 번째 LED가 자신의 몫(24비트)만 읽어서 저장하고, 나머지 데이터는 그대로 다음 LED로 흘려보냅니다. 이 과정이 줄줄이 반복되어 LED 여러 개를 한 가닥의 선으로 순서대로 제어할 수 있습니다.
💡 ② 색상 순서는 GRB (RGB 아님!)
WS2812B 칩은 내부적으로 초록(G) → 빨강(R) → 파랑(B) 순서로 24비트(각 8비트씩)의 데이터를 받습니다. 다행히 MicroPython의 neopixel 라이브러리가 이 변환을 자동으로 처리해 주기 때문에, 우리는 코드에서 항상 (R, G, B) 순서의 튜플만 신경 쓰면 됩니다. 예: np[0] = (255, 0, 0) → 빨간색.
💡 ③ 타이밍이 매우 중요합니다
WS2812B는 "0"과 "1"을 펄스의 길이(마이크로초 단위)로 구분합니다. 대략 0.4µs(짧은 펄스)면 0, 0.8µs(긴 펄스)면 1로 인식하는 방식입니다. 이 미세한 타이밍은 사람이 time.sleep()으로 직접 맞추기 불가능할 정도로 정밀해서, Pico 2(RP2350)는 PIO(Programmable I/O)라는 전용 하드웨어 회로를 이용해 CPU 부담 없이 정확한 타이밍을 자동 생성합니다. neopixel.NeoPixel() 객체를 만드는 순간 이 PIO 설정이 내부적으로 완료됩니다.
이런 정밀 타이밍 생성을 Pico 2의 PIO 하드웨어가 자동으로 처리해 줍니다.
8.3 밝기 제한 무지개 효과 코드
import machine
import neopixel
import time
NUM_LEDS = 8
DATA_PIN = 22
BRIGHTNESS = 0.25
np = neopixel.NeoPixel(machine.Pin(DATA_PIN), NUM_LEDS)
def wheel(pos):
if pos < 85:
return (pos * 3, 255 - pos * 3, 0)
elif pos < 170:
pos -= 85
return (255 - pos * 3, 0, pos * 3)
else:
pos -= 170
return (0, pos * 3, 255 - pos * 3)
def dim(color, brightness=BRIGHTNESS):
r, g, b = color
return (int(r * brightness), int(g * brightness), int(b * brightness))
def clear():
for i in range(NUM_LEDS):
np[i] = (0, 0, 0)
np.write()
def rainbow_cycle(delay=0.02):
for offset in range(256):
for i in range(NUM_LEDS):
color_index = (i * 256 // NUM_LEDS + offset) & 255
np[i] = dim(wheel(color_index))
np.write()
time.sleep(delay)
print("Neopixel 효과 시작...")
clear()
while True:
rainbow_cycle()
8.6 동작 흐름
wheel() 함수는 0~255 값을 빨강, 초록, 파랑이 이어지는 색상으로 바꿉니다. 각 LED에 서로 다른 색상 인덱스를 주고, 반복할 때마다 인덱스를 조금씩 밀어주면 무지개가 흐르는 것처럼 보입니다. BRIGHTNESS 값은 전류를 줄이기 위한 안전 장치입니다.
⚠️ 전원 주의사항
Neopixel은 밝기가 밝을수록 많은 전류를 소비합니다. Pico의 3V3 핀 대신 VSYS(5V) 핀을 사용하세요. LED가 30개 이상이라면 5V 외부 전원 어댑터를 사용해야 보드가 손상되지 않습니다.
8.4 특정 LED만 색칠하기
전체 LED를 한 번에 다루기 전에, 인덱스 번호로 원하는 LED 딱 하나(혹은 몇 개)만 켜는 연습을 해봅시다. np[i]에서 i는 0번부터 시작하는 LED의 순서 번호입니다.
import machine
import neopixel
import time
NUM_LEDS = 8
DATA_PIN = 22
np = neopixel.NeoPixel(machine.Pin(DATA_PIN), NUM_LEDS)
# 먼저 전체 소등
for i in range(NUM_LEDS):
np[i] = (0, 0, 0)
np.write()
# 0번 LED만 빨간색, 3번 LED만 초록색, 7번(마지막) LED만 파란색
np[0] = (50, 0, 0) # 빨강 (밝기를 낮게 설정: 0~255 중 50)
np[3] = (0, 50, 0) # 초록
np[7] = (0, 0, 50) # 파랑
np.write() # write()를 호출해야 실제로 LED에 반영됩니다!
print("0번, 3번, 7번 LED만 켜졌습니다.")
💡 np.write()를 꼭 호출해야 하는 이유
np[i] = (r, g, b)는 아직 메모리 상의 색상 값만 바꿔놓은 상태입니다. 실제로 LED에 데이터가 전송되어 화면에 반영되려면 반드시 np.write()를 호출해야 합니다. 초보자가 가장 자주 하는 실수가 이 write()를 빠뜨리는 것입니다!
8.5 밝기 조절하기
같은 색상이라도 RGB 숫자 값 자체를 낮추면 더 어둡게, 높이면 더 밝게 표현됩니다(0~255 범위). 아래 예제는 하나의 색상을 점점 밝게 켰다가 다시 어둡게 줄이는 "숨쉬기(breathing)" 효과입니다.
import machine
import neopixel
import time
NUM_LEDS = 8
DATA_PIN = 22
np = neopixel.NeoPixel(machine.Pin(DATA_PIN), NUM_LEDS)
def set_all(r, g, b):
for i in range(NUM_LEDS):
np[i] = (r, g, b)
np.write()
print("밝기 조절(숨쉬기) 효과 시작...")
while True:
# 0 -> 100까지 점점 밝게 (흰색 기준)
for level in range(0, 101, 5):
set_all(level, level, level)
time.sleep(0.03)
# 100 -> 0까지 점점 어둡게
for level in range(100, -1, -5):
set_all(level, level, level)
time.sleep(0.03)
💡 밝기 조절 팁
모든 LED를 최대 밝기(255,255,255)로 오래 켜두면 전류 소모가 매우 커집니다. 실습 중에는 밝기 값을 100 이하로 제한하는 습관을 들이면 USB 전원만으로도 안전하게 실습할 수 있습니다.
8.7 과제 해결해보기!
과제 1. 짝수/홀수 LED 다른 색으로 켜기
8개의 LED 중에서 짝수 번째(0, 2, 4, 6)는 파란색, 홀수 번째(1, 3, 5, 7)는 노란색으로 켜는 코드를 작성해 보세요. (힌트: i % 2 연산을 활용하면 짝수/홀수를 구분할 수 있습니다.)
과제 2. 한 칸씩 이동하는 "달리는 불빛" 만들기
8개의 LED 중 단 하나의 LED만 흰색으로 켜지고, 그 위치가 0번 → 7번까지 한 칸씩 이동하다가 끝에 도달하면 다시 0번으로 돌아오는(무한 반복) 효과를 만들어 보세요.
- 현재 켜진 LED를 제외한 나머지는 항상 꺼져 있어야 합니다.
- 이동 속도는
time.sleep(0.15)정도로 설정합니다.
8.8 Wokwi 시뮬레이션으로 실습하기
Chapter 1.8에서 소개한 Wokwi 온라인 시뮬레이터를 이용하면 실제 Neopixel LED 스트립이 없어도 브라우저에서 바로 색상 제어 코드를 테스트해 볼 수 있습니다.
💻 실습 순서
- wokwi.com에서 [New Project] → "Raspberry Pi Pico" (MicroPython) 템플릿을 선택합니다.
- 왼쪽 diagram.json 탭을 열고, 아래 제공된 코드를 그대로 붙여넣어 Pico와 Neopixel(8구 스트립)을 자동으로 연결합니다.
- 오른쪽 main.py 탭에 이번 챕터의 무지개 효과 코드(8.3) 또는 밝기 조절 코드(8.5)를 붙여넣습니다.
- 화면 상단의 ▶️ (녹색 재생 버튼)을 클릭하면 시뮬레이션이 시작되고, 8개의 LED가 실시간으로 색이 바뀌는 모습을 확인할 수 있습니다.
{
"version": 1,
"author": "Pico 2 IoT Guide",
"editor": "wokwi",
"parts": [
{ "type": "wokwi-pi-pico", "id": "pico", "top": 0, "left": 0, "attrs": {} },
{ "type": "wokwi-neopixel", "id": "strip1", "top": -60, "left": 150, "attrs": { "pixels": "8" } }
],
"connections": [
[ "pico:GP22", "strip1:DIN", "green", [] ],
[ "pico:VSYS", "strip1:VCC", "red", [] ],
[ "pico:GND", "strip1:GND", "black", [] ]
]
}
⚠️ 참고 사항
Wokwi 에디터의 부품 이름이나 속성 키(pixels 등)는 버전 업데이트에 따라 달라질 수 있습니다. 위 diagram.json을 붙여넣었을 때 부품이 보이지 않는다면, 에디터 좌측의 부품 검색(parts search)창에서 "neopixel"을 직접 검색하여 이름을 확인하고 type 값을 알맞게 수정하세요.