ESP32 ve Arduino

WS2812B MQTT: Adreslenebilir LED Şeridi JSON Komutlarıyla Kontrol Edin

Bir WS2812B adreslenebilir LED şeridi, renk, parlaklık ve efektler için JSON MQTT komutlarıyla bir ESP32'den sürün ve her dashboard'un senkron kalması için retained durum yayınlayın.

Bir ws2812b mqtt aydınlatma projesinde ESP32 tarafından sürülen renkli WS2812B LED şerit
Bunu kendi MQTT broker’ınızda çalıştırınFree planla özel, yönetilen bir MQTT broker alın ve hazır kod örnekleriyle başlayın.
Ücretsiz deneyin Kod örnekleri Kart gerekmez

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_LEDS değ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

  1. İ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.
  2. Renkler yer değiştirmiş (kırmızı yeşil görünüyor). Şeridiniz farklı bir bayt sırası kullanıyor. NEO_GRB yerine NEO_RGB deneyin.
  3. Şerit beyaza döndüğünde ESP32 resetleniyor. Besleme çöküyor. Şeridi kendi 5 V kaynağından besleyin, toprağı paylaştırın, MAX_BRIGHTNESS değerini düşürün.
  4. Ş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.
  5. 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.
  6. MQTT yeniden bağlanırken efektler takılıyor. Engelleyici bir bağlantı denemesi animasyonu soket zaman aşımı süresine kadar dondurur. setSocketTimeout değ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.

İlgili yazılar

ESP32 NTP MQTT Zaman Damgası: Her Mesaja Doğru Zaman Ekleyin

ESP32 saatini NTP ile eşitleyin, ISO 8601 ve epoch milisaniye zaman damgalarını biçimlendirin ve bunları MQTT payload'larına ekleyin; böylece ölçümler geç yayınlansa bile gerçek alınma zamanlarını korur.

6 dk okuma

Tüm yazılar →

Özel MQTT broker’ınız birkaç tık uzağınızda

Hesabınızı oluşturun, broker kimlik bilgilerinizi alın ve ilk cihazınızı bağlayın — kurulacak, yamalanacak ya da ölçeklendirilecek sunucu yok.