ESP32 und Arduino

WS2812B per MQTT: Einen adressierbaren LED-Streifen mit JSON-Befehlen steuern

Steuern Sie einen adressierbaren WS2812B-LED-Streifen am ESP32 mit JSON-MQTT-Befehlen für Farbe, Helligkeit und Effekte und veröffentlichen Sie den Zustand retained, damit jedes Dashboard synchron bleibt.

Farbenfroher WS2812B-LED-Streifen, gesteuert von einem ESP32 in einem WS2812B-MQTT-Beleuchtungsprojekt
Probieren Sie es auf Ihrem eigenen MQTT-Broker ausHolen Sie sich im Free-Tarif einen privaten, verwalteten MQTT-Broker und starten Sie mit fertigen Codebeispielen.
Kostenlos testen Codebeispiele Keine Kreditkarte erforderlich

Mit einer WS2812B-MQTT-Steuerung ändern Sie Farbe, Helligkeit und Animation eines adressierbaren LED-Streifens vom Smartphone, aus einem Skript, aus Node-RED oder von einer Hausautomations-Zentrale aus. In diesem Tutorial bauen Sie eine ESP32-Firmware, die JSON-Befehle annimmt, Effekte nicht blockierend abspielt und ihren Zustand als Retained Message veröffentlicht. Getestet wird alles mit broker.connectmq.com und einfachen mosquitto_pub-Befehlen.

Befehlsformat und Topics

Adressierbare Streifen haben mehr Zustand als ein Relais: Ein/Aus, Farbe, Helligkeit und Effekt. Jeden Wert über ein eigenes Topic zu senden wird schnell unübersichtlich, daher nutzt dieses Projekt ein JSON-Befehls-Topic und ein JSON-State-Topic. Jedes Feld in einem Befehl ist optional, sodass ein Client nur die Helligkeit ändern kann, ohne die aktuelle Farbe zu kennen.

Topic Richtung Retained Beispiel
connectmq/demo/<id>/light/set App zum ESP32 Nein {"color":[255,80,0],"brightness":120}
connectmq/demo/<id>/light/state ESP32 zu den Apps Ja {"state":"ON","color":[255,80,0],"brightness":120,"effect":"solid"}
connectmq/demo/<id>/status ESP32 zu den Apps Ja online / offline

Unterstützte Felder:

Feld Typ Bedeutung
state "ON" / "OFF" Schaltet den Streifen ein oder aus, ohne die Farbe zu vergessen
color [r,g,b], jeweils 0 bis 255 Grundfarbe für die Effekte solid und breathe
brightness 0 bis 255 Globale Helligkeit, begrenzt durch ein Firmware-Limit
effect solid, rainbow, breathe, chase Animationsmodus
speed 1 bis 100 Animationsgeschwindigkeit

Diese Struktur ähnelt dem, was viele Hausautomations-Tools für JSON-Leuchten verwenden, sodass Sie sie mit wenig Aufwand an ein bestehendes Setup anpassen können. Wenn Sie tiefer in Parsing-Muster einsteigen möchten, behandelt der Leitfaden zu ArduinoJson mit MQTT auf dem ESP32 Dokumentgrößen und Validierung ausführlicher.

Hardware: Verdrahtung und Stromversorgung richtig gemacht

Die meisten gescheiterten LED-Projekte scheitern an der Stromversorgung, nicht am Code. Eine WS2812B-LED kann bei vollem Weiß beträchtlichen Strom ziehen, und ein paar Meter Streifen übersteigen leicht, was ein USB-Port oder der Spannungsregler des ESP32 liefern kann.

Sie brauchen:

  • ESP32-Entwicklungsboard.
  • WS2812B-Streifen (dieses Tutorial geht von 60 LEDs aus; passen Sie NUM_LEDS an Ihren an).
  • Ein 5-V-Netzteil, das für Ihren Streifen mit Reserve dimensioniert ist.
  • Einen 1000-µF-Kondensator zwischen 5 V und GND am Eingang des Streifens.
  • Einen 330-Ω-Widerstand in Reihe mit der Datenleitung.
  • Optional, aber empfohlen: einen 74AHCT125 oder einen ähnlichen Pegelwandler für das Datensignal.
Leitung am Streifen Anschließen an
5V Netzteil +5 V
GND GND des Netzteils und GND des ESP32
DIN ESP32 GPIO 5 über 330 Ω (ggf. über den Pegelwandler)

Der ESP32 gibt 3,3-V-Logik aus, während der WS2812B einen High-Pegel nahe 0,7 × VDD erwartet. Viele Streifen funktionieren ohne Pegelwandler, besonders mit kurzen Datenleitungen, doch flackernde erste Pixel oder zufällige Farben sind das klassische Symptom eines grenzwertigen Signals. Bei langen Streifen speisen Sie an beiden Enden ein, damit die entfernten LEDs nicht orange werden, weil die Spannung entlang des Kupfers abfällt.

Die Firmware begrenzt die Helligkeit außerdem mit MAX_BRIGHTNESS. Das ist eine günstige Versicherung: Selbst wenn jemand "brightness":255 mit Weiß sendet, bleibt der Streifen innerhalb dessen, was Ihr Netzteil verkraftet.

Bibliotheken

Installieren Sie über den Arduino-Bibliotheksverwalter:

  • PubSubClient von Nick O'Leary.
  • Adafruit NeoPixel von Adafruit, die den WS2812B auf dem ESP32 unterstützt.
  • ArduinoJson Version 7 von Benoit Blanchon.

Die ESP32-Firmware für WS2812B per MQTT

#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;   // schützt Ihr Netzteil

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);                 // Platz für größere JSON-Befehle
  mqtt.setSocketTimeout(5);
}

void loop() {
  // Animierte Effekte werden per Timer neu gezeichnet, solid nur bei Änderungen
  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();
}

Designentscheidungen, die sich zum Nachmachen lohnen

Zusammenführen statt ersetzen. Der Callback ändert nur die Felder, die im Befehl enthalten sind. {"brightness":40} dimmt den Streifen, ohne Farbe oder Effekt anzutasten, genau so, wie man es von einer Leuchte erwartet.

Nicht blockierende Animation. Jeder Effekt berechnet anhand eines Zählers ein einzelnes Frame und kehrt zurück. Es gibt kein delay() und keine for-Schleife, die sekundenlang läuft, sodass mqtt.loop() weiterläuft und Befehle selbst mitten in einem Regenbogen sofort übernommen werden.

Statische Farben werden nicht ständig neu gezeichnet. strip.show() deaktiviert auf manchen Plattformen kurzzeitig Interrupts, während die Daten ausgetaktet werden. Eine statische Farbe Hunderte Male pro Sekunde neu zu zeichnen, verschwendet CPU-Zeit und bringt nichts.

Der Zustand wird nach jedem Befehl veröffentlicht. Da das State-Topic retained ist, sieht ein später geöffnetes Dashboard sofort die aktuelle Farbe.

Mit mosquitto_pub testen

Beobachten Sie das Gerät:

mosquitto_sub -h broker.connectmq.com -p 1883 -i watch-92be0f -v -t "connectmq/demo/leds-92be0f/#"

Senden Sie ein paar Befehle:

# Warmes Orange mit mittlerer Helligkeit
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"}'

# Regenbogen, schneller
mosquitto_pub -h broker.connectmq.com -p 1883 -i cmd-92be0f -t "connectmq/demo/leds-92be0f/light/set" -m '{"effect":"rainbow","speed":80}'

# Aus, die Farbe bleibt für das nächste Mal erhalten
mosquitto_pub -h broker.connectmq.com -p 1883 -i cmd-92be0f -t "connectmq/demo/leds-92be0f/light/set" -m '{"state":"OFF"}'

Auf jeden Befehl folgt eine Zustandsnachricht wie:

{"state":"ON","color":[255,90,10],"brightness":120,"effect":"rainbow","speed":80}

Der öffentliche MQTT-Broker unter broker.connectmq.com ist ideal für solche schnellen Experimente und Prototypen, aber alles darauf ist für jeden lesbar, daher gehören keine Produktionsdaten darauf. Verwenden Sie eigene Zugangsdaten, bevor Sie den Streifen zu Hause ins Regal legen.

Den Streifen aus einem Skript steuern

Tests über die Kommandozeile sind praktisch, doch die meisten LED-Projekte werden am Ende von etwas Automatischem gesteuert: einem Sonnenuntergangs-Zeitplan, einem Build-Status, einer Türklingel. Das kurze Python-Skript unten nutzt paho-mqtt 2.x, um den Streifen auf eine warme Abendfarbe herunterzudimmen, und bestätigt die Änderung anschließend über den retained Zustand.

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()

Da jeder Befehl ein Teil-Update ist und das Gerät immer mit seinem vollständigen Zustand antwortet, muss das Skript nichts über den vorherigen Modus des Streifens wissen. Dieselbe Idee funktioniert in Node-RED mit einem mqtt out-Node und einem Function-Node, der das JSON-Objekt aufbaut, oder mit einer Hausautomations-Zentrale, die für eine JSON-Leuchte konfiguriert ist. Halten Sie die Dimmschritte grob: Ein Befehl pro Sekunde reicht völlig, und die Effekt-Engine auf dem ESP32 ist der richtige Ort für flüssige Animation, nicht das Netzwerk. In der Fertigung funktioniert dasselbe Muster übrigens als einfache Andon-Signalleuchte an einer Montagelinie, die den Linienstatus aus einem MQTT-Topic anzeigt.

Checkliste zur Fehlerbehebung

  1. Erste LED in falscher Farbe oder flackernd. Schwaches Datensignal. Ergänzen Sie den 330-Ω-Widerstand, kürzen Sie die Datenleitung und verwenden Sie einen Pegelwandler.
  2. Farben vertauscht (Rot erscheint grün). Ihr Streifen nutzt eine andere Byte-Reihenfolge. Probieren Sie NEO_RGB statt NEO_GRB.
  3. ESP32 startet neu, wenn der Streifen weiß wird. Spannungseinbruch. Versorgen Sie den Streifen aus einem eigenen 5-V-Netzteil mit gemeinsamer Masse und senken Sie MAX_BRIGHTNESS.
  4. Das hintere Ende des Streifens wirkt gelb oder orange. Spannungsabfall entlang des Streifens. Speisen Sie 5 V an beiden Enden oder alle paar Meter ein.
  5. Große JSON-Befehle werden stillschweigend ignoriert. PubSubClient verwirft Nachrichten, die größer als sein Puffer sind. Der Sketch erhöht ihn auf 512 Bytes; siehe Grenzen der PubSubClient-Puffergröße, wenn Sie Daten pro Pixel senden.
  6. Effekte ruckeln beim MQTT-Reconnect. Ein blockierender Verbindungsversuch friert die Animation bis zum Socket-Timeout ein. Verringern Sie setSocketTimeout oder verlagern Sie die Netzwerkkommunikation in einen eigenen Task.

Ab in den Produktivbetrieb

Wandert der Streifen von der Werkbank ins Wohnzimmer oder ins Schaufenster, verbinden Sie ihn mit Ihrem eigenen Broker. Legen Sie im ConnectMQ-Portal einen Broker an und verwenden Sie die generierten Werte für Benutzername, Passwort und Client ID mit WiFiClientSecure auf Port 8883. Mit einem Cloud-MQTT-Broker erreichen Ihr Smartphone, Ihre Node-RED-Instanz und der Streifen denselben Endpunkt von überall, ohne dass Sie Ports an Ihrem Router öffnen müssen.

ConnectMQ bietet außerdem einen Live-Topic-Explorer, in dem Sie beobachten, wie sich light/state beim Testen von Effekten ändert, und der MQTT-Client im Browser ist ein schneller Weg, JSON-Befehle ohne Terminal zu senden. Soll der Streifen neben einem kleinen Bildschirm Statusinformationen zeigen, kombinieren Sie ihn mit einem ESP32-OLED-Display, das MQTT-Nachrichten anzeigt, oder mit einer Relaisschaltung, um den Streifen nachts komplett vom Strom zu trennen.

Starten Sie mit ConnectMQ

Erstellen Sie einen kostenlosen Serverless Broker unter app.connectmq.com, ohne Kreditkarte. Sie erhalten sofort sichere Zugangsdaten und können Ihren WS2812B-Streifen innerhalb weniger Minuten von überall steuern.

Häufig gestellte Fragen

Kann ich einzelne WS2812B-Pixel per MQTT steuern?

Ja, indem Sie ein Farbarray pro Pixel senden, aber halten Sie die Payloads klein. Bei langen Streifen ist es meist besser, Effektnamen und Parameter zu senden und die Pixel lokal auf dem ESP32 berechnen zu lassen.

Warum flackert mein Streifen nur, wenn das WLAN aktiv ist?

Die Adafruit-NeoPixel-Bibliothek nutzt auf dem ESP32 das RMT-Peripheriemodul, das robust gegenüber WLAN-Interrupts ist. Flackern bei WLAN-Aktivität deutet daher meist auf Störungen in der Stromversorgung oder ein grenzwertiges Datensignal hin, nicht auf ein Timing-Problem.

Wie viele LEDs kann ein ESP32 ansteuern?

Das Datenprotokoll bewältigt Hunderte LEDs an einem Pin; die praktischen Grenzen sind Netzteil, Bildrate und RAM. Jede LED benötigt im RGB-Modus drei Bytes Puffer.

Sollte der Leuchtenzustand retained sein?

Ja. Eine retained Zustandsnachricht ermöglicht es jedem neuen Subscriber, etwa einem Dashboard oder einer Smartphone-App, direkt nach dem Verbinden die richtige Farbe anzuzeigen.

Verwandte Artikel

ESP32 NTP MQTT Zeitstempel: genaue Uhrzeit in jeder Nachricht

Synchronisieren Sie die ESP32-Uhr per NTP, formatieren Sie Zeitstempel in ISO 8601 und Epoch-Millisekunden und hängen Sie sie an MQTT-Payloads, damit Messwerte ihren echten Erfassungszeitpunkt behalten, auch wenn sie verspätet veröffentlicht werden.

8 Min. Lesezeit

Alle Artikel →

Ihr privater MQTT-Broker ist nur wenige Klicks entfernt

Konto anlegen, Broker-Zugangsdaten erhalten und das erste Gerät verbinden — ohne Server, die installiert, gepatcht oder skaliert werden müssen.