Diesmal gibt es wieder kleinen Ausflug in die Bastelecke. Ich habe mir ein LED Matrix Panel mit 64×32 LEDs zugelegt und steuere es mit einem ESP8266 NodeMCU an. Aber einfach nur vordefinierte Dinge anzeigen ist ja langweilig. Also musste eine Verbindung zur Außenwelt her. Was eignet sich also besser als MQTT.
Die Hardware
Zum Einsatz kommen ein NodeMCU, ein LED Matrix Panel 64×32, ein normales 5Volt USB Netzteil, sowie viele Jumper Kabel
Das LED Panel erhältst du hier.
Im ersten Schritt muss das Panel mit dem NodeMCU verkabelt werden. Das ist etwas Fummelei. Wie das genau zu funktionieren hat, wird von Brian Lough sehr gut beschrieben. Er nutzt im Beispiel einen Wemos D1, du kannst aber problemlos einen NodeMCU ESP8266 nutzen und einfach die entsprechenden Pins nutzen. Sie sind identisch beschriftet.
Als Netzteil nutze ich ein normales 5Volt USB Netzteil. Einfach den USB Anschluss abschneiden und die Drähte über Klemmen mit Jumper Kabeln verbinden. So lässt sich das Netzteil wunderbar benutzen. Achte darauf, dass das Netzteil genug Power hat. 2,5A sollten es schon sein.
Die Programmierung der LED Matrix
Der sogenannte Sketch ist relativ einfach. In meinem Fall zeige ich die Außentemperatur an. Je nach Temperatur ändert sich die Schriftfarbe. Der Clou: Die Datenübertragung findet über MQTT stattt. So ist es problemlos möglich beliebige Daten von openHAB an die Matrix zu senden und anzeigen zu lassen. Auch mehrere Daten im Wechsel sind kein Problem.
Falls du noch nie mit dem NodeMCU gearbeitet hast und die Entwicklungsumgebung erst einrichten musst, findest du eine super Anleitung bei OpenDataLab. Hier wird dir alles Schritt für Schritt erklärt.
Der NodeMCU
Sobald das erledigt ist und du deinen NodeMCU mit der Arudino IDE programmieren kannst, können wir damit beginnen den Code für die Matrix zu programmieren.
Die benötigten Bibliotheken
Für dieses Projekt benötigen wir folgende Bibliotheken einige Bibliotheken, die, sofern noch nicht geschehen, installiert werden müssen. Das wird im Menü Sketch – Bibilothek einbinden – Bibliotheken verwalten. Es öffnet sich der Bibliotheksverwalter. Hier über die Suche die folgenden Bibliotheken suchen und installieren:
- Ticker (sollte bereits vorinstalliert sein)
- PxMatrix
- ESP8266WiFi
- PubSubClient
- ESP8266mDNS
- WiFiUdp
Der Code sieht folgendermaßen aus:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
/******************************************************************* Daten per MQTT empfangen und über eine 64x32 LED Matrix ausgeben. Ansprechen der LED Matrix umgesetzt von Brian Laugh Gesamter Sketch umgesetzt von https://zukunftathome.de *******************************************************************/ #include <Ticker.h> #include <PxMatrix.h> // The library for controlling the LED Matrix // Needs to be manually downloaded and installed // https://github.com/2dom/PxMatrix #include <ESP8266WiFi.h> #include <PubSubClient.h> #include <ESP8266mDNS.h> #include <WiFiUdp.h> #include <ArduinoOTA.h> const char* ssid = "XXXXXXXXX"; const char* password = "12345678"; const char* mqtt_server = "broker.hivemq.com"; unsigned long previousMillis; String temperatur; String zeit; int openhab; int bildschirm = 1; WiFiClient espClient; PubSubClient client(espClient); void setup_wifi() { delay(100); Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } randomSeed(micros()); Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); } Ticker display_ticker; // Pins for LED MATRIX #define P_LAT 16 #define P_A 5 #define P_B 4 #define P_C 15 #define P_OE 2 #define P_D 12 #define P_E 0 // PxMATRIX display(32,16,P_LAT, P_OE,P_A,P_B,P_C); // PxMATRIX display(64,32,P_LAT, P_OE,P_A,P_B,P_C,P_D); PxMATRIX display(64, 32, P_LAT, P_OE, P_A, P_B, P_C, P_D, P_E); // Some standard colors uint16_t myRED = display.color565(255, 0, 0); uint16_t myGREEN = display.color565(0, 255, 0); uint16_t myBLUE = display.color565(0, 0, 255); uint16_t myLIGHTBLUE = display.color565(0, 153, 255); uint16_t myWHITE = display.color565(255, 255, 255); uint16_t myYELLOW = display.color565(255, 255, 0); uint16_t myCYAN = display.color565(0, 255, 255); uint16_t myMAGENTA = display.color565(255, 0, 255); uint16_t myBLACK = display.color565(0, 0, 0); uint16_t currentCOLOR; uint16 myCOLORS[8] = {myRED, myGREEN, myBLUE, myWHITE, myYELLOW, myCYAN, myMAGENTA, myBLACK}; // ISR for display refresh void display_updater() { display.display(20); } //MQTT DATEN IN VARIABLEN SCHREIBEN void callback(char* topic, byte* payload, unsigned int length) { Serial.print("Command from MQTT broker is : ["); Serial.print(topic); Serial.println(); Serial.print(" publish data is:"); display.setTextColor(myGREEN); char* payload_str; payload_str = (char*) malloc(length + 1); memcpy(payload_str, payload, length); payload_str[length] = '\0'; if(String(topic) == "Haus/openhab"){ if(String(payload_str) == "on"){ openhab = 1; } else if(String(payload_str) == "off"){ openhab = 0; } } else if(String(topic) == "Haus/Matrix/Feuchtigkeit"){ (String)payload_str; temperatur = (String)payload_str; } else if(String(topic) == "Haus/Matrix/Zeit"){ (String)payload_str; zeit = (String)payload_str; } } void reconnect() { while (!client.connected()) { Serial.print("Attempting MQTT connection..."); display.clearDisplay(); display.drawPixel(0, 0, myRED); String clientId = "YOU_CHOOSE_id"; clientId += String(random(0xffff), HEX); if (client.connect(clientId.c_str())) { Serial.println("connected"); display.clearDisplay(); display.drawPixel(0, 0, myGREEN); //MQTT TOPICS EINTRAGEN client.subscribe("Haus/openhab"); client.subscribe("Haus/Matrix/Feuchtigkeit"); client.subscribe("Haus/Matrix/Zeit"); //VARIABLE OPENHAB BEI START AUF 1, DAMIT DIREKT DIE DATEN ANGEZEIGT WERDEN openhab = 1; } else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(" try again in 5 seconds"); delay(6000); } } } void setup() { Serial.begin(9600); setup_wifi(); client.setServer(mqtt_server, 1883); display.begin(16); display.clearDisplay(); Serial.print("Pixel draw latency in us: "); unsigned long start_timer = micros(); display.drawPixel(1, 1, 0); unsigned long delta_timer = micros() - start_timer; Serial.println(delta_timer); Serial.print("Display update latency in us: "); start_timer = micros(); display.display(0); delta_timer = micros() - start_timer; Serial.println(delta_timer); display_ticker.attach(0.002, display_updater); yield(); display.clearDisplay(); delay(500); //OTA Programmierung, damit der NodeMCU auch per WLAN programmiert werden kann ArduinoOTA.setHostname("LED_Matrix"); ArduinoOTA.onStart([]() { Serial.println("Start"); }); ArduinoOTA.onEnd([]() { Serial.println("\nEnd"); }); ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { Serial.printf("Progress: %u%%\r", (progress / (total / 100))); }); ArduinoOTA.onError([](ota_error_t error) { Serial.printf("Error[%u]: ", error); if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed"); else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed"); else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed"); else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed"); else if (error == OTA_END_ERROR) Serial.println("End Failed"); }); ArduinoOTA.begin(); Serial.println("Ready"); Serial.print("IP address: "); Serial.println(WiFi.localIP()); } void loop() { if (!client.connected()) { reconnect(); } client.setCallback(callback); client.loop(); ArduinoOTA.handle(); if (openhab == 1) { //Zeit einstellen, in der die einzelnen Screens wechseln 10000 entspricht 10 Sekunden unsigned long currentMillis = millis(); if (millis() - previousMillis >= 10000 ) { previousMillis = currentMillis; //Bildschirm 1 if (bildschirm == 1){ display.fillRect(1, 0, 64, 32, myBLACK); display.setTextSize(1); display.setTextColor(myYELLOW); display.setCursor(1,1); display.print("Zeit"); display.setTextSize(2); display.setTextColor(myWHITE); display.setCursor(1, 16); display.print(zeit); bildschirm = 2; } //Bildschirm 2 else if (bildschirm == 2){ display.fillRect(1, 0, 64, 32, myBLACK); display.setTextSize(1); display.setTextColor(myYELLOW); display.setCursor(1,1); display.print("Feuchte"); display.setTextSize(2); display.setTextColor(myWHITE); display.setCursor(1, 16); display.print(feuchtigkeit); bildschirm=1; } } } } |
Die Werte für SSID und Password müssen durch die eigenen Netzwerkdaten ersetzt werden. Sollte ein eigener MQTT Broker im Einsatz sein, ist auch die entsprechende Adresse bei mqtt_server einzutragen. Solange keine sensiblen Daten übertragen werden, kann auch durchaus auf einen openMQTT Service zurückgegriffen werden. In diesem Fall muss nichts geändert werden. Wichtig: Der NodeMCU unterstützt nur das 2,4 GHz Band.
const char* ssid = „XXXXXXXXX“;
const char* password = „12345678“;
const char* mqtt_server = „broker.hivemq.com“;
Gehe weiter bis zu der Stelle, an der du die Topics abonnierst:
//MQTT TOPICS EINTRAGEN
client.subscribe(„Haus/openhab“);
client.subscribe(„Haus/Matrix/Temperatur“);
client.subscribe(„Haus/Matrix/Zeit“);
Wie du sehen kannst, gibt es bei mir 3 Topics.
Haus/openhab
Dieses Topic sorgt dafür, dass du per MQTT die Anzeige der Daten auf der Matrix aktivieren/deaktivieren kannst.
Haus/Matrix/Temperatur
Die Temperatur wird als String auf dieses Topic übertragen und vom NodeMCU empfangen.
Haus/Matrix/Zeit
Die Zeit wird als String auf dieses Topic übertragen und vom NodeMCU empfangen.
Nun springe zu folgender Stelle:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
if(String(topic) == "Haus/openhab"){ if(String(payload_str) == "on"){ openhab = 1; } else if(String(payload_str) == "off"){ openhab = 0; } } else if(String(topic) == "Haus/Matrix/Feuchtigkeit"){ (String)payload_str; feuchtigkeit = (String)payload_str; } else if(String(topic) == "Haus/Matrix/Zeit"){ (String)payload_str; zeit = (String)payload_str; } |
Hier tauchen die zuvor abonnierten Topics wieder auf. Solltest du andere Topics verwendet haben, musst du diese an dieser Stelle ebenfalls ändern.
Jetzt sind alle vorbereitungen abgeschlossen und wir können endlich damit beginnen die Anzeige auf der Matrix zu programmieren.
Ausgabe auf der LED Matrix programmieren
Die Ausgabe auf der Matrix wird im Loop des Sketches programmiert. Dazu springe an folgende Stelle unten im Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
if (openhab == 1) { //Zeit einstellen, in der die einzelnen Screens wechseln 10000 entspricht 10 Sekunden unsigned long currentMillis = millis(); if (millis() - previousMillis >= 10000 ) { previousMillis = currentMillis; //Bildschirm 1 if (bildschirm == 1){ display.fillRect(1, 0, 64, 32, myBLACK); display.setTextSize(1); display.setTextColor(myYELLOW); display.setCursor(1,1); display.print("Zeit"); display.setTextSize(2); display.setTextColor(myWHITE); display.setCursor(1, 16); display.print(zeit); bildschirm = 2; } //Bildschirm 2 else if (bildschirm == 2){ display.fillRect(1, 0, 64, 32, myBLACK); display.setTextSize(1); display.setTextColor(myYELLOW); display.setCursor(1,1); display.print("Feuchte"); display.setTextSize(2); display.setTextColor(myWHITE); display.setCursor(1, 16); display.print(feuchtigkeit); bildschirm=1; } } } //BILDSCHIRM AUS else if (openhab == 0){ display.fillRect(0, 0, 64, 32, myBLACK); } |
Der oben stehende Code sorgt dafür, dass Uhrzeit und Feuchtigkeit im Wechsel auf der LED Matrix angezeigt werden, bzw. der Bildschirm komplett ausgeschaltet wird.
unsigned long currentMillis = millis();
if (millis() – previousMillis >= 10000 ) {
previousMillis = currentMillis;
Dieser Teil sorgt dafür, dass der nachstehende Code innerhalb der geschweiften Klammer alle 10 Sekunden ausgeführt wird. 10000 steht für für 10 Sekunden. Hier kannst du natürlich auch längere Zeiten einstellen.
//Bildschirm 1
if (bildschirm == 1){
fragt ab, ob die Variable Bildschirm aktuell den Wert 1 hat. Das ist wichtig, damit der Text durchgewechselt werden kann.
display.fillRect(1, 0, 64, 32, myBLACK);
zeichnet ein Rechteck beginnend bei Position 1, 0 (x, y Achse) mit der Breite von 64 Pixeln und einer Höhe von 32 Pixeln. Das entspricht der Größe der Matrix. Dadurch wird eventuell zuvor stehender Text „gelöscht“
display.setTextSize(1);
setzt die Schriftgröße auf 1. Entspricht einer Höhe von 7 Pixeln.
display.setTextColor(myYELLOW);
setzt die Schriftfarbe auf Gelb.
display.setCursor(1,1);
Positioniert den Startpunkt ab dem geschrieben wird. x und y Achse.
display.print(„Zeit“);
schreibt das Wort „Zeit“
display.setTextSize(2);
setzt die Schriftgröße auf 2. Enspricht einer Höhe von 14 Pixeln
display.setTextColor(myWHITE);
setzt die Schriftfarbe auf Weiß
display.setCursor(1, 16);
Positioniert den Startpunkt ab dem geschrieben wird. x und y Achse.
display.print(zeit);
Schreibt die Variable „Zeit“, die den empfangenen MQTT Wert enthält
bildschirm = 2;
setzt die Variable bildschirm auf 2, damit der 2. Bildschirm mit der Luftfeuchtigkeit angezeigt werden kann
}
Das ganz wiederholt sich für den 2. Bildschirm.
//BILDSCHIRM AUS
else if (openhab == 0){
display.fillRect(0, 0, 64, 32, myBLACK);
}
Wenn openhab == 0, dann wird ein schwarzes Rechteck gezeichnet. Es wird also kein Text mehr angezeigt.
Damit wäre der Code für den NodeMCU fertig.
Werte von openHAB via MQTT an LED Matrix senden
Jetzt müssen nur noch die entsprechenden Werte aus openHAB über MQTT an die Matrix gesendet werden. Ich setze an dieser Stelle zur Vereinfachung voraus, dass in deiner Installation bereits Items für Zeit und Feuchtigkeit existieren. Es kann sich natürlich auch um beliebig andere Werte handeln. Das Prinzip bleibt das gleiche. Zum Einsatz kommt das MQTT2 Binding in openHAB. Wie das korrekt eingerichtet wird, erfährst du hier.
Dummy Items anlegen
Um die Daten zu per MQTT zu senden, ist grundsätzlich kein weiteres Dummy Item notwendig. Allerdings werden die Daten im Normalfall in einer unpassenden Darstellung ausgeliefert. Bei der Zeit Beispielsweise wird Datum, Stunden, Minuten, Sekunden, etc. ausgegeben. Das soll für die Ausgabe natürlich etwas „aufgeräumter“ aussehen. Daher habe ich in einer dummies.items Datei folgende Dummy Items angelegt:
1 2 |
String Zeit_MQTT String Feuchtigkeit_MQTT |
Diese Items können direkt in der Regel umformatiert werden, so dass die gewünschte Ausgabe herauskommt. Die Regel zu beiden Werten sehen folgendermaßen aus.
1 2 3 4 5 6 7 8 |
rule "Zeit MQTT" when Time cron "0 0/1 * 1/1 * ? *" then val String Zeit_MQTT = Zeit.state.format("%1$tH:%1$tM") val mqttActions = getActions("mqtt","mqtt:broker:brokerhome") mqttActions.publishMQTT("Haus/Matrix/Zeit",Zeit_MQTT) end |
Ausgelöst wird die Regel jede Minute, damit sich die Uhrzeit auch minütlich aktualisiert.
val String Zeit_MQTT = Zeit.state.format(„%1$tH:%1$tM“)
Hier wird dem Dummy Item Zeit_MQTT der Wert des ursprünglichen Zeit Items zugewiesen – allerdings bereits formatiert. In diesem Fall entspricht es der Darstellung HH:MM.
val mqttActions = getActions(„mqtt“,“mqtt:broker:brokerhome“)
Hier ist der von dir eingerichtete MQTT Broker einzutragen, an den die Werte gesendet werden sollen. Die Anleitung findest du hier.
mqttActions.publishMQTT(„Haus/Matrix/Zeit“,Zeit_MQTT)
Hier gibst du das Topic an, an das der Wert gesendet werden soll. Dieses muss natürlich mit dem Topic übereinstimmen, welches du im NodeMCU Code hinterlegt hast.
1 2 3 4 5 6 7 8 |
rule "Luftfeuchte MQTT" when Item Luftfeuchte_Schnitt received update then val String Feuchtigkeit_MQTT = Luftfeuchte_Schnitt.state.format("%.1f%%") val mqttActions = getActions("mqtt","mqtt:broker:brokerhome") mqttActions.publishMQTT("Haus/Matrix/Feuchtigkeit",Feuchtigkeit_MQTT) end |
Die Regel für das Senden der Luftfeuchtigkeit wird nur bei Änderung der Luftfeuchtigkeit ausgelöst, um nicht unnötig die gleichen Daten durch die Gegend zu schicken.
val String Feuchtigkeit_MQTT = Luftfeuchte_Schnitt.state.format(„%.1f%%“)
Auch hier wird dem Dummy Item der formatierte Wert des ursprünglichen Luftfeuchte Items übergeben. In diesem Fall wird die Luftfeuchte mit einer Nachkommastelle angezeigt.
val mqttActions = getActions(„mqtt“,“mqtt:broker:brokerhome“)
Hier ist der von dir eingerichtete MQTT Broker einzutragen, an den die Werte gesendet werden sollen. Die Anleitung findest du hier.
mqttActions.publishMQTT(„Haus/Matrix/Zeit“,Zeit_MQTT)
Hier gibst du das Topic an, an das der Wert gesendet werden soll. Dieses muss natürlich mit dem Topic übereinstimmen, welches du im NodeMCU Code hinterlegt hast.
Geschafft – Die LED Matrix erwacht
Das wars! Jetzt solltest du auf deiner LED Matrix Werte von openHAB angezeigt bekommen. Die Möglichkeiten der Darstellung sind vielfältig. Auf dem folgenden Bild kannst du eine alternative Darstellung für Zeit, Temperatur und aktuellem Stromverbrauch. Falls du etwas Ähnlichliches umgesetzt hast, oder mit Hilfe dieser Anleitung kreativ wudest, schreib mir. Ich freue mich über Leserprojekte zu berichten.
Hallo,
habe es gefunden: display.setRotate(true);
LG
Hannes
Hallo,
danke für die tolle Anleitung!
Wäre es auch möglich, Daten auf der LED Matrix im Hochformat auszugeben?
Falls ja, wie?
Danke,
Hannes
Hallo Hannes,
da bin ich in diesem Fall leider überfragt.
Es gibt inzwischen von WLED eine Unterstützung für Matrix Darstellungen. Dort kannst du relativ frei das Format und die Laufrichtung bestimmen. Schau dir das Projekt mal an.
Hi!
Ich kämpfe mittlerweile seit fast zwei Wochen und bekomme ein Flackern vom Display nicht weg. Ich habe mal ein Video gemacht vom einfachen Test mitels PxMatrix/pixeltime:
https://www.dropbox.com/s/zu9kjk8saqttlns/nodemcu-ledMatrix.mov?dl=0
Irgendeine Idee, woran das liegen könnte? Erste Hälfte des Videos zeigt noch die Programmierung von NodeMCU, die zweite dann pixeltime.
(Hardware: NodeMCU ESP8266 12-F von AZDelivery sowie 64×32/P3 Adafruit Matrix)
Hallo Paul,
das sieht ja super seltsam aus. Kannst du mir einmal den Code schicken? Dann lade ich den mal auf meinen Nodemcu und schaue was der so treibt. Sind die Blinkeffekte immer gleich oder ist das zufällig? Eventuell gibt es einen Wackelkontakt bei den Kabeln.
Schicke auch gerne einmal Bilder von der Verkabelung mit.
Viele Grüße
Hi Ich habe das soweit nach gebaut funktioniert auch fast alles 🙂
bis auf die Funktion Openhab=0 dann wird das Panel nicht schwarz sondern wechselt einfach nicht mehr zwischen den Bildschirmen. Aber das finde ich sicher noch im Code. Ich hab mal ne ganz andere Frage. Bekommt man das Display Heller?
Wenn ich das draussen einsetze sieht man kaum noch was.
Hallo Hafti,
schön, dass es soweit funktioniert. Hast du getestet, ob über MQTT der entsprechende Befehl gesendet wird?
Bezüglich der Helligkeit gibt habe ich folgendes gefunden:
// Set the brightness of the panels (default is 255)
//display.setBrightness(50);
Ich vermute also fast, dass es schon die maximale Helligkeit ist. Je nach Netzteil kann es natürlich sein, dass die LEDs nicht genug versorgt werden.
Hat das jemad nachgebaut ?
Ich fine keine der angegebenen Bilbloteken