Bir WS2812B MQTT kontrolcüsü, adreslenebilir bir LED şeridin rengini, parlaklığını ve animasyonunu bir telefondan, bir betikten, Node-RED'den veya bir ev otomasyonu merkezinden değiştirmenizi sağlar. Bu eğitimde JSON komutlarını kabul eden, engellemeyen efektler çalıştıran ve durumunu retained message olarak yayınlayan bir ESP32 firmware'i oluşturacaksınız. Her şey broker.connectmq.com ve düz mosquitto_pub komutlarıyla test edilir.
Komut Biçimi ve Topic'ler
Adreslenebilir şeritlerin bir röleden daha fazla durumu vardır: güç, renk, parlaklık ve efekt. Her birini ayrı bir topic olarak göndermek hızla karmaşık hale gelir; bu yüzden bu proje tek bir JSON komut topic'i ve tek bir JSON durum topic'i kullanır. Bir komuttaki her alan isteğe bağlıdır; yani bir client mevcut rengi bilmeden yalnızca parlaklığı değiştirebilir.
| Topic | Yön | Retained | Örnek |
|---|---|---|---|
connectmq/demo/<id>/light/set |
Uygulamadan ESP32'ye | Hayır | {"color":[255,80,0],"brightness":120} |
connectmq/demo/<id>/light/state |
ESP32'den uygulamalara | Evet | {"state":"ON","color":[255,80,0],"brightness":120,"effect":"solid"} |
connectmq/demo/<id>/status |
ESP32'den uygulamalara | Evet | online / offline |
Desteklenen alanlar:
| Alan | Tür | Anlamı |
|---|---|---|
state |
"ON" / "OFF" |
Rengi unutmadan şeridi açar veya kapatır |
color |
[r,g,b], her biri 0 ile 255 arası |
Solid ve breathe efektleri için temel renk |
brightness |
0 ile 255 arası | Firmware limitiyle sınırlanan genel parlaklık |
effect |
solid, rainbow, breathe, chase |
Animasyon modu |
speed |
1 ile 100 arası | Animasyon hızı |
Bu yapı, pek çok ev otomasyonu aracının JSON ışıklar için kullandığına yakındır; bu yüzden onu az çabayla mevcut bir kuruluma uyarlayabilirsiniz. Ayrıştırma desenlerine daha derinden bakmak isterseniz ESP32'de MQTT ile ArduinoJson rehberi belge boyutlandırma ve doğrulamayı daha ayrıntılı ele alır.
Donanım: Doğru Kablolama ve Güç
Başarısız LED projelerinin çoğu koddan değil güçten başarısız olur. Bir WS2812B LED tam beyazda kayda değer bir akım çekebilir ve birkaç metrelik şerit, bir USB portunun veya ESP32'nin regülatörünün sağlayabileceğini kolayca aşar.
İhtiyacınız olanlar:
- ESP32 geliştirme kartı.
- WS2812B şerit (bu eğitim 60 LED varsayar;
NUM_LEDSdeğerini kendinizinkine göre ayarlayın). - Şeridiniz için paylı boyutlandırılmış 5 V bir güç kaynağı.
- Şeridin girişinde 5 V ile GND arasına 1000 µF bir kondansatör.
- Veri hattına seri 330 Ω bir direnç.
- İsteğe bağlı ama önerilen: veri sinyali için 74AHCT125 veya benzeri bir seviye dönüştürücü.
| Şerit kablosu | Bağlantı |
|---|---|
| 5V | Güç kaynağı +5 V |
| GND | Güç kaynağı GND'si ve ESP32 GND'si |
| DIN | 330 Ω üzerinden ESP32 GPIO 5 (kullanılıyorsa seviye dönüştürücü aracılığıyla) |
ESP32 3,3 V lojik çıkış verirken WS2812B, 0,7 × VDD'ye yakın bir yüksek seviye bekler. Pek çok şerit, özellikle kısa veri kablolarıyla, seviye dönüştürücü olmadan da çalışır; ancak titreyen ilk pikseller veya rastgele renkler sınırda bir sinyalin klasik belirtisidir. Uzun şeritlerde, gerilim bakır boyunca düştükçe uzaktaki LED'lerin turuncuya dönmemesi için gücü her iki uçtan besleyin.
Firmware ayrıca parlaklığı MAX_BRIGHTNESS ile sınırlar. Bu ucuz bir sigortadır: biri beyazla "brightness":255 gönderse bile şerit, kaynağınızın kaldırabileceği sınırlar içinde kalır.
Kütüphaneler
Arduino Library Manager'dan kurun:
- Nick O'Leary'nin PubSubClient kütüphanesi.
- ESP32'de WS2812B'yi destekleyen Adafruit'in Adafruit NeoPixel kütüphanesi.
- Benoit Blanchon'un ArduinoJson kütüphanesi, sürüm 7.
ESP32 WS2812B MQTT Firmware'i
#include <WiFi.h>
#include <PubSubClient.h>
#include <Adafruit_NeoPixel.h>
#include <ArduinoJson.h>
const char* WIFI_SSID = "YOUR_WIFI";
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
const char* MQTT_HOST = "broker.connectmq.com";
const uint16_t MQTT_PORT = 1883;
const char* DEVICE_ID = "leds-92be0f";
const String BASE = String("connectmq/demo/") + DEVICE_ID;
#define LED_PIN 5
#define NUM_LEDS 60
const uint8_t MAX_BRIGHTNESS = 150; // güç kaynağınızı koruyun
Adafruit_NeoPixel strip(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);
WiFiClient net;
PubSubClient mqtt(net);
bool powerOn = true;
uint8_t r = 255, g = 120, b = 20;
uint8_t brightness = 100;
uint8_t speed = 50;
String effect = "solid";
bool dirty = true;
unsigned long lastFrame = 0, lastAttempt = 0;
uint16_t frame = 0;
void publishState() {
JsonDocument doc;
doc["state"] = powerOn ? "ON" : "OFF";
JsonArray c = doc["color"].to<JsonArray>();
c.add(r); c.add(g); c.add(b);
doc["brightness"] = brightness;
doc["effect"] = effect;
doc["speed"] = speed;
char out[160];
size_t n = serializeJson(doc, out, sizeof(out));
String topic = BASE + "/light/state";
mqtt.publish(topic.c_str(), (const uint8_t*)out, n, true);
}
void onMessage(char* topic, byte* payload, unsigned int length) {
JsonDocument doc;
if (deserializeJson(doc, payload, length)) {
Serial.println("Ignoring invalid JSON");
return;
}
if (doc["state"].is<const char*>()) {
powerOn = strcmp(doc["state"].as<const char*>(), "OFF") != 0;
}
if (doc["color"].is<JsonArray>() && doc["color"].size() == 3) {
r = constrain(doc["color"][0].as<int>(), 0, 255);
g = constrain(doc["color"][1].as<int>(), 0, 255);
b = constrain(doc["color"][2].as<int>(), 0, 255);
}
if (doc["brightness"].is<int>()) brightness = constrain(doc["brightness"].as<int>(), 0, 255);
if (doc["speed"].is<int>()) speed = constrain(doc["speed"].as<int>(), 1, 100);
if (doc["effect"].is<const char*>()) {
String e = doc["effect"].as<const char*>();
if (e == "solid" || e == "rainbow" || e == "breathe" || e == "chase") effect = e;
}
dirty = true;
publishState();
}
void render() {
uint8_t level = map(brightness, 0, 255, 0, MAX_BRIGHTNESS);
if (!powerOn) {
strip.clear();
strip.show();
return;
}
strip.setBrightness(level);
if (effect == "solid") {
strip.fill(strip.Color(r, g, b));
} else if (effect == "rainbow") {
strip.rainbow(frame * 256);
} else if (effect == "breathe") {
float k = (sin(frame * 0.05f) + 1.0f) / 2.0f; // 0..1
strip.fill(strip.Color(r * k, g * k, b * k));
} else if (effect == "chase") {
strip.clear();
for (int i = frame % 3; i < NUM_LEDS; i += 3) strip.setPixelColor(i, strip.Color(r, g, b));
}
strip.show();
}
bool connectMqtt() {
String status = BASE + "/status";
if (!mqtt.connect(DEVICE_ID, nullptr, nullptr, status.c_str(), 1, true, "offline")) {
Serial.printf("MQTT failed, state=%d\n", mqtt.state());
return false;
}
mqtt.publish(status.c_str(), "online", true);
mqtt.subscribe((BASE + "/light/set").c_str(), 1);
publishState();
return true;
}
void setup() {
Serial.begin(115200);
strip.begin();
strip.clear();
strip.show();
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
mqtt.setServer(MQTT_HOST, MQTT_PORT);
mqtt.setCallback(onMessage);
mqtt.setBufferSize(512); // daha büyük JSON komutları için yer
mqtt.setSocketTimeout(5);
}
void loop() {
// Animasyonlu efektler bir zamanlayıcıyla yeniden çizilir; solid yalnızca bir şey değiştiğinde
unsigned long interval = map(speed, 1, 100, 120, 10);
bool animated = powerOn && effect != "solid";
if ((animated && millis() - lastFrame >= interval) || dirty) {
lastFrame = millis();
frame++;
render();
dirty = false;
}
if (WiFi.status() != WL_CONNECTED) return;
if (!mqtt.connected()) {
if (millis() - lastAttempt > 5000) { lastAttempt = millis(); connectMqtt(); }
return;
}
mqtt.loop();
}
Kopyalamaya değer tasarım tercihleri
Değiştirme değil, birleştirme. Callback yalnızca komutta bulunan alanları değiştirir. {"brightness":40} göndermek renge veya efekte dokunmadan şeridi kısar; kullanıcıların bir ışıktan beklediği de budur.
Engellemeyen animasyon. Her efekt bir sayaca göre tek bir kare hesaplar ve döner. Saniyelerce çalışan bir delay() veya for döngüsü yoktur; böylece mqtt.loop() çalışmaya devam eder ve komutlar bir gökkuşağının ortasında bile hemen uygulanır.
Düz renkler sürekli yeniden çizilmez. strip.show(), bazı platformlarda veriyi gönderirken kısa süreliğine kesmeleri (interrupt) devre dışı bırakır. Statik bir rengi saniyede yüzlerce kez yeniden çizmek CPU'yu boşa harcar ve hiçbir şey kazandırmaz.
Durum her komuttan sonra yayınlanır. Durum topic'i retained olduğundan, daha sonra açılan bir dashboard mevcut rengi hemen görür.
mosquitto_pub ile Test Edin
Cihazı izleyin:
mosquitto_sub -h broker.connectmq.com -p 1883 -i watch-92be0f -v -t "connectmq/demo/leds-92be0f/#"
Birkaç komut gönderin:
# Orta parlaklıkta sıcak turuncu
mosquitto_pub -h broker.connectmq.com -p 1883 -i cmd-92be0f -t "connectmq/demo/leds-92be0f/light/set" -m '{"color":[255,90,10],"brightness":120,"effect":"solid"}'
# Gökkuşağı, daha hızlı
mosquitto_pub -h broker.connectmq.com -p 1883 -i cmd-92be0f -t "connectmq/demo/leds-92be0f/light/set" -m '{"effect":"rainbow","speed":80}'
# Kapalı, rengi bir sonraki sefer için saklar
mosquitto_pub -h broker.connectmq.com -p 1883 -i cmd-92be0f -t "connectmq/demo/leds-92be0f/light/set" -m '{"state":"OFF"}'
Her komutu şöyle bir durum mesajı izler:
{"state":"ON","color":[255,90,10],"brightness":120,"effect":"rainbow","speed":80}
broker.connectmq.com adresindeki herkese açık MQTT test broker'ı bu tür hızlı deneyler için idealdir, ancak üzerindeki her şey herkes tarafından okunabilir. Şeridi evde bir rafa koymadan önce kendi kimlik bilgilerinizi kullanın.
Şeridi Bir Betikle Sürün
Komut satırı testleri iyidir, ancak çoğu LED projesi sonunda otomatik bir şey tarafından sürülür: gün batımı zamanlaması, bir derleme durumu, bir kapı zili. Aşağıdaki kısa Python betiği, şeridi sıcak bir akşam rengine kısmak için paho-mqtt 2.x kullanır ve ardından retained durumu okuyarak değişikliği doğrular.
import json
import time
import uuid
import paho.mqtt.client as mqtt
BASE = "connectmq/demo/leds-92be0f"
def on_message(client, userdata, msg):
print("state:", msg.payload.decode())
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=f"sched-{uuid.uuid4().hex[:8]}")
client.on_message = on_message
client.connect("broker.connectmq.com", 1883, keepalive=30)
client.subscribe(f"{BASE}/light/state", qos=1)
client.loop_start()
for level in range(200, 59, -20):
client.publish(f"{BASE}/light/set", json.dumps({"state": "ON", "color": [255, 120, 40], "brightness": level}), qos=1)
time.sleep(1)
time.sleep(2)
client.loop_stop()
client.disconnect()
Her komut kısmi bir güncelleme olduğundan ve cihaz her zaman tam durumuyla yanıt verdiğinden, betiğin şeridin önceki modu hakkında hiçbir şey bilmesine gerek yoktur. Aynı fikir Node-RED'de bir mqtt out düğümü ve JSON nesnesini oluşturan bir function düğümüyle ya da JSON ışık olarak yapılandırılmış bir ev otomasyonu merkeziyle de çalışır. Kısma adımlarını kaba tutun: saniyede bir komut fazlasıyla yeterlidir; akıcı animasyonun doğru yeri ağ değil, ESP32'deki efekt motorudur.
Sorun Giderme Kontrol Listesi
- İlk LED yanlış renkte veya titriyor. Zayıf veri sinyali. 330 Ω direnci ekleyin, veri kablosunu kısaltın ve bir seviye dönüştürücü kullanın.
- Renkler yer değiştirmiş (kırmızı yeşil görünüyor). Şeridiniz farklı bir bayt sırası kullanıyor.
NEO_GRByerineNEO_RGBdeneyin. - Şerit beyaza döndüğünde ESP32 resetleniyor. Besleme çöküyor. Şeridi kendi 5 V kaynağından besleyin, toprağı paylaştırın,
MAX_BRIGHTNESSdeğerini düşürün. - Şeridin uzak ucu sarı veya turuncu görünüyor. Şerit boyunca gerilim düşümü. 5 V'u her iki uçtan veya birkaç metrede bir besleyin.
- Büyük JSON komutları sessizce yok sayılıyor. PubSubClient buffer'ından büyük mesajları atar. Sketch bunu 512 bayta çıkarır; piksel başına veri gönderiyorsanız PubSubClient buffer boyutu limitleri yazısına bakın.
- MQTT yeniden bağlanırken efektler takılıyor. Engelleyici bir bağlantı denemesi animasyonu soket zaman aşımı süresine kadar dondurur.
setSocketTimeoutdeğerini düşürün veya ağ işlemlerini kendi görevine taşıyın.
Production'a Taşıyın
Şerit atölye tezgâhından bir oturma odasına veya mağaza vitrinine taşındığında onu kendi broker'ınıza bağlayın. ConnectMQ portalında bir broker oluşturun, ardından üretilen kullanıcı adını, parolayı ve client ID'yi 8883 portunda WiFiClientSecure ile kullanın. Bir bulut MQTT broker, telefonunuzun, Node-RED örneğinizin ve şeridin, router'ınızda port açmadan her yerden aynı uç noktaya ulaşabilmesi demektir.
ConnectMQ ayrıca canlı bir topic gezgini sunar; böylece efektleri test ederken light/state değişimini izleyebilirsiniz; tarayıcı içi MQTT client'ı da terminal olmadan JSON komutları göndermenin hızlı bir yoludur. Şeridin küçük bir ekranla birlikte durum göstermesini istiyorsanız onu MQTT mesajlarını gösteren bir ESP32 OLED ekranla eşleştirin veya geceleri şeridin gücünü tamamen kesmek için röle anahtarlamayla birleştirin.
ConnectMQ ile Başlayın
app.connectmq.com adresinde kredi kartı olmadan ücretsiz bir Serverless broker oluşturun. Güvenli kimlik bilgilerini hemen alır ve WS2812B şeridinizi dakikalar içinde her yerden kontrol edebilirsiniz.
Sık Sorulan Sorular
Tek tek WS2812B piksellerini MQTT üzerinden kontrol edebilir miyim?
Evet, piksel başına bir renk dizisi göndererek; ancak payload'ları küçük tutun. Uzun şeritlerde efekt adlarını ve parametreleri gönderip pikselleri ESP32'nin yerel olarak çizmesine izin vermek genellikle daha iyidir.
Şeridim neden yalnızca Wi-Fi aktifken titriyor?
ESP32'deki Adafruit NeoPixel kütüphanesi, Wi-Fi kesmelerine karşı dayanıklı olan RMT çevre birimini kullanır; bu yüzden Wi-Fi etkinliği sırasındaki titreme genellikle zamanlamadan çok güç gürültüsüne veya sınırda bir veri sinyaline işaret eder.
Tek bir ESP32 kaç LED sürebilir?
Veri protokolü tek bir pinde yüzlerce LED'i yönetebilir; pratik limitler güç kaynağı, kare hızı ve RAM'dir. RGB modunda her LED üç baytlık buffer gerektirir.
Işık durumu retained olmalı mı?
Evet. Retained bir durum mesajı, dashboard veya telefon uygulaması gibi her yeni abonenin bağlandıktan hemen sonra doğru rengi göstermesini sağlar.



