고급⏱ 예상 시간: 60분📝 7단계

ESP32 스마트 조명

ESP32 웹서버로 네오픽셀 LED의 색상, 밝기, 패턴을 스마트폰에서 원격 제어하는 스마트 조명을 만듭니다.

🧩 필요한 모듈

브레드보드
ESP32
점퍼 와이어
네오픽셀 LED 스트립 (WS2812B)
저항 (220Ω / 10kΩ)

📖 단계별 설명서

📝

1단계준비물 확인

다음 부품들을 준비해주세요:

  • ESP32 보드 1개
  • 브레드보드 1개
  • 점퍼 와이어 5개
  • 네오픽셀 LED 스트립 (WS2812B) 1개 (8개 이상 권장)
  • 470Ω 저항 1개 (데이터 핀 보호용)

💡 팁: 네오픽셀 LED가 많을수록 전력 소모가 큽니다. 8개 이하라면 ESP32의 5V로 충분하지만, 그 이상이면 외부 전원을 사용하세요.

📝

2단계회로 연결하기

  1. 네오픽셀 데이터 핀을 470Ω 저항을 통해 ESP32 GPIO 5에 연결합니다.
  2. 네오픽셀 VCC를 5V에, GND를 GND에 연결합니다.
  3. ESP32와 네오픽셀의 GND를 반드시 공유합니다.

📐 회로도

회로도 이미지

/images/projects/esp32-smart-light-circuit.png

📝

3단계라이브러리 설치

아두이노 IDE에서:

  • 보드 매니저에서 ESP32 보드를 설치합니다.
  • 라이브러리 관리에서 Adafruit NeoPixel을 설치합니다.
📝

4단계코드 작성하기

code.ino
#include <WiFi.h>
#include <WebServer.h>
#include <Adafruit_NeoPixel.h>

#define LED_PIN    5
#define LED_COUNT  8

const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";

Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
WebServer server(80);

int r = 255, g = 100, b = 0;
int brightness = 128;
int pattern = 0;  // 0: solid, 1: rainbow, 2: breathe

const char HTML[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>Smart Light</title>
  <style>
    body{font-family:sans-serif;text-align:center;padding:20px;background:#1a1a2e;color:#fff}
    h1{color:#e94560}
    .slider{width:80%;margin:10px auto}
    input[type=range]{width:100%;accent-color:#e94560}
    .btn{padding:12px 24px;margin:5px;border:none;border-radius:8px;font-size:16px;cursor:pointer}
    .btn-pattern{background:#0f3460;color:#fff}
    .btn-pattern.active{background:#e94560}
    label{display:block;margin-top:15px;font-size:14px}
  </style>
</head>
<body>
  <h1>Smart Light</h1>
  <div id="preview" style="width:100px;height:100px;border-radius:50%;margin:10px auto;border:3px solid #333"></div>
  <label>R: <span id="rv">255</span></label>
  <div class="slider"><input type="range" id="r" min="0" max="255" value="255" oninput="update()"></div>
  <label>G: <span id="gv">100</span></label>
  <div class="slider"><input type="range" id="g" min="0" max="255" value="100" oninput="update()"></div>
  <label>B: <span id="bv">0</span></label>
  <div class="slider"><input type="range" id="b" min="0" max="255" value="0" oninput="update()"></div>
  <label>Brightness: <span id="brv">128</span></label>
  <div class="slider"><input type="range" id="br" min="0" max="255" value="128" oninput="update()"></div>
  <div style="margin-top:15px">
    <button class="btn btn-pattern active" onclick="setPattern(0)">Solid</button>
    <button class="btn btn-pattern" onclick="setPattern(1)">Rainbow</button>
    <button class="btn btn-pattern" onclick="setPattern(2)">Breathe</button>
  </div>
  <script>
    function update(){
      var rv=document.getElementById('r').value;
      var gv=document.getElementById('g').value;
      var bv=document.getElementById('b').value;
      var br=document.getElementById('br').value;
      document.getElementById('rv').innerText=rv;
      document.getElementById('gv').innerText=gv;
      document.getElementById('bv').innerText=bv;
      document.getElementById('brv').innerText=br;
      document.getElementById('preview').style.background='rgb('+rv+','+gv+','+bv+')';
      fetch('/set?r='+rv+'&g='+gv+'&b='+bv+'&br='+br);
    }
    function setPattern(p){
      fetch('/pattern?p='+p);
      document.querySelectorAll('.btn-pattern').forEach((b,i)=>b.classList.toggle('active',i==p));
    }
    update();
  </script>
</body>
</html>
)rawliteral";

void handleRoot() {
  server.send(200, "text/html", HTML);
}

void handleSet() {
  r = server.arg("r").toInt();
  g = server.arg("g").toInt();
  b = server.arg("b").toInt();
  brightness = server.arg("br").toInt();
  pattern = 0;
  server.send(200, "text/plain", "OK");
}

void handlePattern() {
  pattern = server.arg("p").toInt();
  server.send(200, "text/plain", "OK");
}

void setup() {
  Serial.begin(115200);
  strip.begin();
  strip.setBrightness(brightness);
  strip.show();

  WiFi.begin(ssid, password);
  Serial.print("Connecting WiFi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println();
  Serial.print("IP: ");
  Serial.println(WiFi.localIP());

  server.on("/", handleRoot);
  server.on("/set", handleSet);
  server.on("/pattern", handlePattern);
  server.begin();
}

void loop() {
  server.handleClient();
  strip.setBrightness(brightness);

  if (pattern == 0) {
    // Solid color
    for (int i = 0; i < LED_COUNT; i++) {
      strip.setPixelColor(i, strip.Color(r, g, b));
    }
  } else if (pattern == 1) {
    // Rainbow
    static uint16_t hue = 0;
    for (int i = 0; i < LED_COUNT; i++) {
      strip.setPixelColor(i, strip.gamma32(
        strip.ColorHSV(hue + (i * 65536L / LED_COUNT))));
    }
    hue += 256;
  } else if (pattern == 2) {
    // Breathe
    static int breathVal = 0;
    static int breathDir = 5;
    breathVal += breathDir;
    if (breathVal >= 255 || breathVal <= 0) breathDir = -breathDir;
    for (int i = 0; i < LED_COUNT; i++) {
      strip.setPixelColor(i, strip.Color(
        r * breathVal / 255,
        g * breathVal / 255,
        b * breathVal / 255));
    }
  }

  strip.show();
  delay(20);
}
+129 줄 더 보기
📝

5단계Wi-Fi 설정 변경

코드에서 YOUR_WIFI_SSIDYOUR_WIFI_PASSWORD를 실제 Wi-Fi 정보로 변경합니다.

📝

6단계업로드 및 테스트

  1. ESP32 보드를 선택하고 코드를 업로드합니다.
  2. 시리얼 모니터를 열어 ESP32가 받은 IP 주소를 확인합니다.
  3. 같은 Wi-Fi에 연결된 스마트폰 브라우저에서 해당 IP를 입력합니다.
  4. 슬라이더로 색상과 밝기를 조절하고, 패턴 버튼으로 효과를 변경합니다.

💡 팁: IP 주소를 매번 확인하기 번거롭다면 mDNS를 설정하여 http://smartlight.local로 접속할 수도 있습니다.

📝

7단계심화 — 시간 기반 자동 모드

NTP 시간 동기화를 추가하면 시간대별로 자동으로 색상과 밝기가 변하는 기능을 구현할 수 있습니다.

🎉

축하합니다!

"ESP32 스마트 조명" 프로젝트를 완성했습니다!