Điều khiển dải đèn LED RGB qua trang web với ESP32
Đèn LED RGB điều khiển qua app điện thoại thì đầy ngoài chợ, nhưng phần lớn bắt bạn tạo tài khoản, cài app nặng nề, và đôi khi phụ thuộc vào server của hãng ở xa. Dự án này làm điều tương tự nhưng hoàn toàn cục bộ: ESP32 tự chạy một web server nhỏ, trả về một trang HTML chọn màu, và bạn điều khiển dải LED WS2812 ngay từ trình duyệt điện thoại hay laptop — không cần cài gì, không cần internet, chỉ cần cùng mạng WiFi.
Nguyên lý hoạt động
- ESP32 kết nối WiFi và chạy một
WebServerlắng nghe cổng 80. - Khi truy cập địa chỉ IP của ESP32, server trả về một trang HTML nhúng sẵn bảng chọn màu và vài nút chọn hiệu ứng.
- Trang web gọi một endpoint dạng
/set?r=255&g=0&b=0(hoặc gửi JSON) mỗi khi người dùng chọn màu mới. - ESP32 nhận request, parse tham số, và cập nhật màu cho toàn bộ dải LED qua thư viện
FastLED. - Với hiệu ứng động (như hiệu ứng cầu vồng), một cờ trạng thái được bật trong
loop()để liên tục cập nhật màu theo thời gian thay vì set một màu tĩnh.
Linh kiện
- ESP32 DevKit
- Dải LED WS2812B / NeoPixel (bắt đầu với 30-60 LED để dễ cấp nguồn)
- Nguồn 5V riêng đủ dòng cho dải LED — mỗi LED có thể kéo tới 60mA ở độ sáng trắng tối đa, dải 60 LED cần nguồn gần 3.5A nếu bật full trắng
- Tụ 1000µF nối giữa 5V và GND ngay gần đầu dải LED để chống sụt áp đột ngột
- Điện trở 300-500Ω nối tiếp trên dây tín hiệu data (tùy chọn nhưng nên có, giúp bảo vệ LED đầu tiên)
Về mức logic: WS2812 chuẩn thiết kế cho tín hiệu data 5V, trong khi GPIO của ESP32 chỉ ra 3.3V. Với dải ngắn (dưới khoảng 1-2 mét, ít LED), tín hiệu 3.3V thường vẫn đủ để LED nhận diện đúng, đặc biệt nếu dây tín hiệu ngắn và nguồn 5V khỏe. Nhưng để chắc chắn, đặc biệt với dải dài hoặc khi gặp hiện tượng LED đầu nhấp nháy sai màu, nên dùng IC chuyển mức như 74HCT245, hoặc mẹo đơn giản hơn là mắc một con diode thường (1N4148) nối tiếp trên đường 5V cấp cho LED để kéo mức “high” mà ESP32 coi là hợp lệ xuống gần 3.3V hơn.
Code mẫu
#include <WiFi.h>
#include <WebServer.h>
#include <FastLED.h>
#define LED_PIN 5
#define NUM_LEDS 60
CRGB leds[NUM_LEDS];
const char* ssid = "TEN_WIFI";
const char* password = "MAT_KHAU_WIFI";
WebServer server(80);
enum Mode { SOLID, RAINBOW };
Mode currentMode = SOLID;
uint8_t rainbowHue = 0;
const char PAGE[] PROGMEM = R"rawliteral(
<!DOCTYPE html><html><body style="font-family:sans-serif;text-align:center;padding:2em;">
<h2>Dieu khien den LED</h2>
<input type="color" id="picker" value="#ff0000" style="width:100px;height:60px;">
<br><br>
<button onclick="setSolid()">Ap dung mau</button>
<button onclick="setRainbow()">Hieu ung cau vong</button>
<script>
function setSolid() {
const hex = document.getElementById('picker').value;
const r = parseInt(hex.substr(1,2), 16);
const g = parseInt(hex.substr(3,2), 16);
const b = parseInt(hex.substr(5,2), 16);
fetch(`/set?r=${r}&g=${g}&b=${b}`);
}
function setRainbow() { fetch('/rainbow'); }
</script>
</body></html>
)rawliteral";
void handleRoot() {
server.send(200, "text/html", PAGE);
}
void handleSet() {
if (server.hasArg("r") && server.hasArg("g") && server.hasArg("b")) {
int r = server.arg("r").toInt();
int g = server.arg("g").toInt();
int b = server.arg("b").toInt();
currentMode = SOLID;
fill_solid(leds, NUM_LEDS, CRGB(r, g, b));
FastLED.show();
}
server.send(200, "text/plain", "OK");
}
void handleRainbow() {
currentMode = RAINBOW;
server.send(200, "text/plain", "OK");
}
void setup() {
Serial.begin(115200);
FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS);
FastLED.setBrightness(120); // gioi han do sang de giam tai nguon
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) delay(500);
Serial.println(WiFi.localIP());
server.on("/", handleRoot);
server.on("/set", handleSet);
server.on("/rainbow", handleRainbow);
server.begin();
}
void loop() {
server.handleClient();
if (currentMode == RAINBOW) {
fill_rainbow(leds, NUM_LEDS, rainbowHue, 255 / NUM_LEDS);
FastLED.show();
rainbowHue++;
delay(20); // toc do chay cua hieu ung
}
}
Những lỗi thường gặp
- Không cấp nguồn riêng cho dải LED — lấy 5V từ cổng USB nạp code của ESP32 chỉ đủ cho vài LED sáng yếu; dải dài hơn 10-15 LED ở độ sáng cao sẽ làm sụt áp, LED nhấp nháy hoặc đổi màu sai.
- Quên nối chung GND giữa ESP32 và nguồn cấp cho dải LED — đây là lỗi cực kỳ phổ biến, tín hiệu data sẽ vô nghĩa nếu hai bên không chung điểm 0V tham chiếu.
- Gọi
FastLED.show()quá dày trong vòng lặp chính khi server đang xử lý nhiều request cùng lúc — nên giới hạn tốc độ cập nhật hiệu ứng động bằngdelay()hoặc kiểm tramillis()thay vì update mỗi vòng lặp. - Không giới hạn
setBrightness()— chạy 100% độ sáng trên dải dài dễ vượt quá khả năng cấp dòng của nguồn, dải LED nóng bất thường là dấu hiệu cần giảm độ sáng hoặc nâng cấp nguồn.
Mở rộng
Thêm hiệu ứng “thở” (breathing) bằng cách điều biến độ sáng theo hàm sin, hoặc lưu màu yêu thích vào bộ nhớ Preferences của ESP32 để dải LED tự khôi phục đúng màu sau khi mất điện.
Bình luận