Apple HomeKit – WS2812B Lichterkette mit ESP32 steuern

Apple HomeKit – WS2812B Lichterkette mit ESP32 steuern

Es ist tatsächlich noch gar nicht allzu lange her, da habe ich einen Artikel darüber verfasst wie wir mithilfe eines Raspberry Pi‘s einen WS2812B LED Streifen steuern können. Dabei haben wir uns auf das Steuern dieser Lichterkette über HomeKit respektive Siri und einen Raspberry Pi beschränkt.

Ich möchte jetzt natürlich nicht behaupten, dass unsere vorherige Lösung nicht richtig oder schlecht gewesen wäre, allerdings habe ich eine etwas weniger fehleranfällige und energiesparendere Lösung entdeckt und auch gleich umgesetzt. Wer jetzt also das frühere Projekt bereits mit dem Raspberry Pi umgesetzt haben sollte, der muss jetzt nicht unbedingt alles wieder über den Haufen werfen und mich für das ausgegebenen Geld verfluchen. Unsere alte Lösung ist und bleibt weiterhin funktional, allerdings geht es auch günstiger und stabiler.

Wie? - Des Rätsels Lösung nennt sich ESP32 und weil die Kürzel und Begriffe so schön sind noch einmal in Kurform. Wir steuern per HomeBridge auf einem RPi W einen WS2812B Streifen per ESP32 Controller.

Für alle Jünglinge auf dem Gebiet: ESP32 und WS2812B Streifen, aber kein SMB5050 und schon gar nicht umgesetzt mit dem ESP8266.
Nichts verstanden? - Ich auch nicht.

Fangen wir also langsam an, denn den Witz konnte ich mir einfach nicht verkneifen.

WS2812B vs. SMB5050

Ein WS2812B LED Streifen, oder besser gesagt ein LED Streifen mit aufgelöteten WS2812B Chips, bedient sich zwei Leitungen für den Strom und einer Datenleitung. Ein SMB5050 LED Streifen dagegen besitzt keine eigenen Logikchips und wird mittels einem 4-adrigen Stromkabel angesprochen. Dabei besitzt der SMB5050 LED Streifen aufgrund des fehlenden Chips keine Datenleitung. Stattdessen schließen wir den Streifen an einen Pluspol (VCC) an. Je nachdem auf welche der übrigen Adern wir eine Spannung ansetzen (also mit GND verbinden) leuchtet der gesamte Streifen in Rot, Grün, Blau oder in beliebigen Mischfarben (Alle = Weiß; Rot+Blau = Lila; etc).

Wir werden jedoch einen WS2812B Led Streifen verwenden, da dieser über die Chips beliebig gesteuert werden kann. Wir können zum Beispiel ausschließlich die dritte LED in Orange bei einer Helligkeit von 56% leuchten lassen. Ein Unterfangen, welche auf dem SMB5050 Streifen nicht möglich wäre.

Hier noch einmal schemenhaft dargestellt:

SMB5050 oben, WS2812B unten (Schwarz = Minuspol, Rot = Pluspol, Grün = Datenleitung)


ESP32 vs. Raspberry vs. ESP8266

Nachdem wir festgelegt haben welchen LED Streifen wir verwenden wollen muss ich glaube ich noch einmal darauf eingehen warum der ESP32 eine "bessere" Lösung ist. Außerdem hat mich der blöde Witz zum ESP8266 dazu gebracht auch diesen noch einmal kurz in Vergleich zu setzen.

Der Raspberry Pi Zero ist ein Wunderwerk der Technik und mit diesem hat man innerhalb des Portmonees immer einen vollwertigen Rechner bei sich. Was wir allerdings erledigen wollen ist das reagieren auf den Aufruf via WLAN und das einfache weiterleiten von Befehlen an unseren LED Streifen. Ein Raspberry Pi ist dafür theoretisch bestens geeignet, allerdings schießen wir hier gerade mit Kanonen auf Spatzen. Wir brauchen kein komplettes Betriebssystem im Hintergrund laufen lassen, der Stromverbrauch eines Raspberry Pi ist zwar gering aber höher als der eines Microcontrollers und dadurch dass wir im Hintergrund ein ganzes Betriebssystem laufen lassen schleichen sich auch mehr Fehlerquellen ein. 

Nebst dem Stromverbrauch haben wir auch noch die Anschaffungskosten als kleinen Vorteil. Ein Raspberry Pei mit samt einem USB Kabel vielleicht einem Anschluss für HDMI und eine SD Speicherkarte kostet in etwa 20 €. Ein ESP32 Bedarf weniger Einrichtungsaufwand und kostet in etwa fünf Euro in der Anschaffung. Die Vorteile sind klar.

Warum nun also kein ESP8266? Trotz der höheren Nummer des ESP8266 gegenüber dem ESP32 ist dieser eine ältere Version und ich habe schlichtweg keinen gefunden, welcher einen 5V Ausgang bietet. Diesen benötigen wir jedoch für den LED Streifen.

Was wollen wir erreichen?

Wer wissen möchte wie genau die Zusammenhänge zu verstehen sind, dem empfehle ich den theoretischen Teil des vorherigen Artikels zu lesen. Apple Homekit - WS2812B Lichterkette per Raspberry Pi steuern 

Hier werde ich die Unterschiede deutlich machen: Erreichen wollen wir im Prinzip den selben Aufgaben wie auch schon mit unserem Raspberry Pi Zero. Dieses mal verzichten wir jedoch auf einen Apache Webserver und das Weiterleitern der Befehle per JSON-Datei. Der Microcontroller nimmt die Befehle direkt per Lightweight Webserver von userer Homebridge per lokalem Netzwerktraffic entgegen und steuert den LED Streifen ohne Verzögerung. Dies können wir ohne Bedenken machen, da wir kein Sicherheitsrisiko eingehen. Ein Raspberry Pi besitzt ein vollwertiges Betriebssystem und kann somit einfacher "infiltriert" werden, als ein Microcontroller, welcher per Webserver nicht reprogrammierbar ist. Das Ganze klingt etwas weit her geholt und unverständlich, aber darauf in der Tiefe einzugehen würde den Rahmen sprengen. Nehmen wir das einfach so hin.

Was benötigen wir?

Wie auch schon im vorherigen Artikel angesprochen benötigen wir einen LED Streifen, ein Netzteil, unter Umständen ein Stromkabel, einen Widerstand, einen Kondensator für Spannungsspritzen, ein paar Kabel für die Stromversorgung und möglicherweise ein paar Klemmen. Dazu gehört selbstverständlich noch der ESP32 und ein USB Kabel.


Die Einrichtung

Alles was wir für die Einrichtung benötigen ist die Arduino IDE. Diese muss von euch einmal auf eurem Rechner installiert werden. Dabei ist es in der Regel irrelevant welches Betriebssystem im Hintergrund läuft. Einen Link findet ihr hier: Arduino IDE Download

Nach dem Download müssen wir der Arduino IDE "beibringen" EPS32 zu sprechen. Hierfür führen wir folgende Schritte aus:

  1. Datei -> Einstellungen -> Zusätzliche Boardverwalter-URLs -> "https://dl.espressif.com/dl/package_esp32_index.json" eintragen -> OK
  2. Werkzeuge -> Board: xxxx -> Boardverwalter... -> Suche nach "ESP32" -> Installieren von "esp32 by Espressi Systems" -> Schließen
  3. Werkzeuge -> Board: xxxx -> ESP32 Arduino -> ESP32 Der Module

Jetzt wo die Arduino IDE in der Lage ist den entsprechenden ESP32 Microcontroller anzusprechen und für diesen zu kompilieren ist es an der Zeit eine zusätzliche Bibliothek einzubinden. Diese sind nötig, um (A) eine WLAN Verbindung aufzubauen und per Webserver ansprechbar zu sein und (B) um den WS2812B LED Streifen steuern zu können.

Wir sehen: Erst muss der PC mit dem ESP32 sprechen können, dann der ESP32 mit dem WLAN, andere Geräte anschließend mit dem ESP32 via HTTP-Protokoll und ganz zum Schluss noch der ESP32 mit der Lichterkette.

Folgende Bibliothek binden wir folgendermaßen ein:

  1. Werkzeuge
  2. Bibliotheken verwalten ...
  3. Suche nach "Adafruit NeoPixel"
  4. Installation von "Adafruit NeoPixel by Adafruit"

Programmierung

Der Programmcode ist etwas länger, allerdings recht simpel. Wir fügen folgenden Code in das Textfeld ein:

#include <WiFi.h>
#include <Adafruit_NeoPixel.h>

// Wifi Name und Passwort
const char* ssid = "WLAN_NAME";               // BITTE ÄNDERN
const char* password = "WLAN_PASSWORT";  // BITTE ÄNDERN

// Webserver aufsetzen und statische IP Adresse zuweisen
WiFiServer server(80); 
IPAddress ip(192, 168, 178, 50);                    // BITTE ÄNDERN
IPAddress gateway(192, 168, 1, 1);                  // BITTE ÄNDERN
IPAddress subnet(255, 255, 255, 0);

// Allgemeine Variablen
String header;
char setBrightnessUrl[] = "GET /brightness/value";

// Status
int powerStatus = 0;
int brightnessStatus = 25;
int rgb[] = {255,255,255};
int disconnectiontimer = 50;

// Der Pin, welcher mit dem Datenkabel des LED Streifens verbunden ist.
#define PIN            12

// Wie viele LED's haben alle angeschlossenen LED Streifen zusammen
#define NUMPIXELS      60                           // BITTE ÄNDERN

Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS, PIN);

void setup() {
  Serial.begin(115200);
  
  // Verbindung zum WLAN aufbauen
  WiFi.config(ip, gateway, subnet);
  WiFi.begin(ssid, password);
  Serial.print("Connecting to ");
  Serial.println(ssid);

  // Verbindungsstatus prüfen und ggf. erneut starten
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");

    if(disconnectiontimer == 0){
      ESP.restart();  
    }
    disconnectiontimer--;
  }
  
  // Allgemeine Ausgaben auf der Konsole
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());

  // Server starten und LED ansteuern
  server.begin();
  strip.begin();
  strip.show();
}

void loop(){
  
  WiFiClient client = server.available();
  if (client) {
    String currentLine = "";
    
    while (client.connected()) {      
      if (client.available()) {
        
        char c = client.read();   
        header += c;
        if (c == '\n') { 
          if (currentLine.length() == 0) {
            
            // HTTP headers starten immer so
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println("Connection: close");
            client.println();
            
            // Schalten des LED Streifens oder ausgeben des Status je nach eingehender Anfrage
            if (header.indexOf("GET /on") >= 0) {
              powerStatus=1;
              Serial.write("On");
              client.println("1");
            } else if (header.indexOf("GET /off") >= 0) {
              powerStatus=0;
              Serial.println("off");
              client.println("1");
            } else if (header.indexOf("GET /status") >= 0) {
              Serial.println("Status");
              client.println(powerStatus);
            } else if (header.indexOf(setBrightnessUrl) >= 0) {
              Serial.println("Brightness - Set");
              char *bla = getParameterFromUrl(setBrightnessUrl);
              Serial.println(bla);
              Serial.println(strlen(bla));
              brightnessStatus = char2int(bla, strlen(bla));

              if(brightnessStatus > 100){
                brightnessStatus = 100;
              }else if(brightnessStatus < 0){
                brightnessStatus = 0;
              }
              
              client.println(brightnessStatus);
              
            } else if (header.indexOf("GET /brightness") >= 0) {
              Serial.println("Brightness - Status");
              client.println(brightnessStatus);
            } else if (header.indexOf("GET /color/value") >= 0) {
              
              Serial.println("Color - Set");
              char *colors = getParameterFromUrl("GET /color/value");
              
              colorConverter(colors, rgb);
              Serial.println(rgb[0]);
              Serial.println(rgb[1]);
              Serial.println(rgb[2]);
              
            } else if (header.indexOf("GET /color") >= 0) {
              Serial.println("Brightness - Status");

              char hex[7] = {0};
              sprintf(hex,"%02X%02X%02X",rgb[0],rgb[1],rgb[2]);
              
              client.println(hex);
            }

            changeStrip();
            
            // HTTP Antworten enden mit einer leeren Zeile
            client.println();
            
            break;
          } else {
            currentLine = "";
          }
        } else if (c != '\r') {
          currentLine += c; 
        }
        
      }
    }

    // Zurücksetzen
    header = "";
    client.stop();
  }
}

// Lese die Parameter aus der URL aus
char* getParameterFromUrl(char urlPrefix[]){

  int parameterStartIndex = header.indexOf(urlPrefix) + strlen(urlPrefix) + 1;
  int parameterEndIndex = parameterStartIndex;

  while(!isSpace(header.charAt(parameterEndIndex))){
    parameterEndIndex++;
  }

  char *result = new char[ (parameterEndIndex - parameterStartIndex)+5 ];
  header.substring(parameterStartIndex, parameterEndIndex).toCharArray(result, (parameterEndIndex - parameterStartIndex)+1);

  return result;
}

// Hilfsfunktion
int char2int (char *array, int n){    
    int number = 0;
    int mult = 1;

    n = (int)n < 0 ? -n : n;  

    while (n--){
        if ((array[n] < '0' || array[n] > '9') && array[n] != '-') {
            if (number)
                break;
            else
                continue;
        }

        if (array[n] == '-') { 
            if (number) {
                number = -number;
                break;
            }
        }else {   
            number += (array[n] - '0') * mult;
            mult *= 10;
        }
    }

    return number;
}

// Lese Hex -> RGB ein
void colorConverter(String hexValue, int rgb[]){
  long number = (long) strtol( &hexValue[0], NULL, 16);
  int r = number >> 16;
  int g = number >> 8 & 0xFF;
  int b = number & 0xFF;

  rgb[0] = r;
  rgb[1] = g;
  rgb[2] = b;
}

// Das eigentliche Steuern des LED Streifens
void changeStrip(){
  strip.setBrightness(brightnessStatus);
  for (int i = 0; i < NUMPIXELS; i++) {
    if(powerStatus == 1){
      strip.setPixelColor(i, strip.color(rgb[0], rgb[1], rgb[2]));
    }else{
      strip.setPixelColor(i, strip.Color(0, 0, 0));
    }
    strip.show();
  }
}

Nachdem dieser Code eingefügt wurde sollten alle mit "BITTE ÄNDERN" markierten Zeilen geprüft und vielleicht geändert werden. Die Werte können je nach Aufbau und Netzwerkkonfiguration unterschiedlich ausfallen.


Verkabelung

Der LED Streifen und der ESP sind folgendermaßen mit dem Widerstand und dem Kondensator an eine 5V Spannungsquelle anzuschließen:



Konfiguration

So, jetzt haben wir alles zusammen! - Wir haben die Einrichtung und Programmierung der DIY Homebridge Lichterkette erfolgreich abgeschlossen. Machen wir uns also ans Werk unsere Homebridge zu konfigurieren. Im Übrigen ist dies der letzte Schritt auf unserem Weg zur Erleuchtung :D  
Für die Konfiguration müssen wir uns auf die Homebridge aufschalten. Am Besten geht dies über die Administrationskonsole im Browser. Bei mir hat die Homebridge eine statische lokale IP-Adresse bekommen und kann z.b. über folgende "URL" aufgerufen werden: 

http://192.168.178.35:8080/login

Das mag bei euch anders sein! Schaut im Zweifelsfalle einmal in den Router und schaut euch die verbundenen Geräte an. Unter einer Fritzbox geht dies zum Beispiel über Heimnetz -> Netzwerk -> Eintrag mit einem entsprechenden Namen. 
Fritzbox - Homebridge - Verbundene Geräte

Hat man sich in die Homebridge eingeloggt, so müssen wir als Erstes ein Plugin installieren, welches uns die Kommunikation mit einem über HTTP steuerbaren LED Streifen erlaubt.
Klickt auf Plugins -> geht in das Suchfeld "Homebridge Better Http Rgb" ein -> Installieren. Bei mir ist das Plugin bereits installiert, deshalb wird es bereits in der installierten Liste angezeigt.

Dieser Vorgang kann etwas dauern, aber wenn das Plugin erst einmal installiert ist geht es mit der Konfiguration weiter.
Hierzu klicken wir auf Konfiguration -> fügt unten stehende Konfiguration ein -> Speichern -> Homebridge neustarten

Hier eine Beispielkonfiguration, welche jedoch um die IP Adresse der eigenen DIY Homebridge ESP32 Lichterkette erweitert werden muss:

........ 
"accessories": [ 
...... hier stehen andere Konfigurationen ......
{
            "accessory": "HTTP-RGB",
            "name": "Test-Licht",
            "service": "Light",
            "switch": {
                "status": "http://[IP der ESP32 Homebridge Lichterkette]/status",
                "powerOn": "http://[IP der ESP32 Homebridge Lichterkette]/on",
                "powerOff": "http://[IP der ESP32 Homebridge Lichterkette]/off"
            },
            "brightness": {
                "status": "http://[IP der ESP32 Homebridge Lichterkette]/brightness",
                "url": "http://[IP der ESP32 Homebridge Lichterkette]/brightness/value/%s"
            },
            "color": {
                "status": "http:/[IP der ESP32 Homebridge Lichterkette]/color",
                "url": "http://[IP der ESP32 Homebridge Lichterkette]/color/value/%s",
                "brightness": false
            }
        }
.........


Marvin

Ich bin ein Mensch, der sich neben der Programmierung noch für tausend andere Dinge interessiert, die mal mehr und mal weniger verrückt sind. Vor allem aber bin ich Feuer und Flamme mit der Programmierung von eigenen kleinen Apps und Programmen, die mein Leben bereichern.

Hat dir dieser Artikel gefallen?

Kommentar hinzufügen

*Pflichtfeld

  1. Erik

    Mir scheint, im Schaltplan sind 5V und GND falsch verkabelt. Rot geht beim Strip zu DCC am EPS32 aber zu GND und Schwarz geht erst zu GND und dann zu 5V.

  2. Marvin

    Hallo Erik,

    du hast selbstverständlich recht!
    Vielen Dank für den Hinweis, das Bild wurde aktualisiert.

    Gruß
    Marvin