In diesem kurzen Artikel zeige ich Euch eine Lösung für ein super günstiges (ca. 11,- Euro) und super cooles LED Display, mit dem Ihr Eurer Kreativität unendlich viel Futter geben könnt! 🙂
RGB LEDs fand ich schon immer sehr interessant. Schon mit einer einzigen RGB LED kann ich viele Dinge programmieren, wie zum Beispiel durch Farbe visualisierte Zustände, natürliche Lichtfarben oder einfach oszillierende Farben. Kombiniere ich davon mehrere zusammen in Ringen oder Streifen, so multiplizieren sich die Möglichkeiten noch einmal. Der Gipfel des Programmierer Glücks sind aber viele hundert davon in einer rechteckigen Fläche angeordnet. Dafür gibt es schier unendliche viele Anwendungszwecke. Ich persönlich betrachte ein solches LED Display aber auch aus einer künstlerischen Perspektive und sehe es als lebende Leinwand für unzählige phantasievolle Kreationen. Seht Euch auf Youtube meine Animationen für Weihnachten an.
Letztes Jahr zur Weihnachtszeit habe ich mich einmal wieder auf den einschlägigen Webseiten umgesehen und bin dabei auf ein besonders günstiges LED Display gestoßen, welches ich mir für Experimente bestellte. Entscheidend war der Preis und die Angabe, dass es sich beim Display um durch WS2812b ICs gesteuerte LEDs handelt, von denen ich wusste, dass sie sich über eine Bibliothek von Adafruit ansteuern lassen. Also brauche ich im Grund nur noch einen Mikrocontroller, der einen Ausgabe PIN frei hat um die LEDs anzusteuern.
Es sei darauf hingewiesen, dass diese Displays aus einem sehr dünnen flexiblen, aber nicht sich von selbst zurück biegenden Material bestehen und sie leicht geknickt bei mir angekommen sind. Ich vermute es ist Kunststoff, aber es verhält sich wie sehr dünnes Blech. Vielleicht ist es aber auch laminierte Kupferfolie. Es ist möglich sie mit etwas Gefühl einigermaßen plan zu biegen. Für mich kein wirklich großes Problem. Aber es könnte für den einen oder anderen von Bedeutung sein.
Auf dieser Seite von Aliexpress findet Ihr das Display:
Eigentlich werden da sogar 3 verschiedene Displays angeboten:
8 x 8 RGB LED
Das kostet sogar nur knapp über 3,- Euro und wer auf jeden Euro sehen muss, der kann das für erste Experimente bestellen. Auch mit 8 x 8 Pixel kann man schon sehr viele coole Dinge programmieren.
8 x 32 RGB LED
Das ist ideal wenn es nicht nur um Kunst geht, sondern man vielleicht einen Text scrollen lassen will oder eine Uhrzeit anzeigen möchte.
16 x 16 RGB LED
Dieses habe ich mir ausgesucht. Die letzten beiden kosten jeweils weniger als 9,- Euro! Die Displays können miteinander verbunden werden! Es ist also zum Beispiel möglich aus 4 16×16 Displays ein großes 32×32 Pixel zusammen zu stecken! Dafür besitzt jedes Display 3 Anschlüsse:
Einen Eingang, einen Ausgang und noch einmal einen extra Anschluss für die Stromversorgung. Die Verbindung geschieht einfach durch das Verbinden der Anschlüsse von Eingang und Ausgang der zu verbindenden Displays. Passende Buchsen und Stecker sind schon dran. Der extra Stromanschluss ist nicht unbedingt notwendig. Es kommt darauf an wie hell und wie viele LEDs Ihr mit Eurem Programm ansteuern werdet. Wenn sich das in Grenzen hält, könnt Ihr das Display nur mit dem Anschluss für den Eingang (DIN) verwenden. Ich übernehme aber keine Verantwortung, wenn bei Euch irgendetwas abraucht! Wenn Ihr für Eure Kreationen die volle Helligkeit benötigt und viele LEDs gleichzeitig leuchten sollen, so ist der Einsatz eines zusätzlichen Netzteils und damit der Stromanschluss des Displays notwendig. Ich habe aber im weiteren nur mit dem Eingang gearbeitet und den Stromanschluss und den Ausgang abgelötet!
Wenn das Display schon günstiger als eine durchschnittliche Kinokarte ist, will ich natürlich auch nicht viel Geld für den Mikrocontroller ausgeben. Darum habe ich mir zusätzlich noch einen ESP32-C3 Super Mini dazu bestellt:
Der kostet bei Aliexpress unter 3,- Euro! Es gibt Angebote wo er teurer ist. Sucht ein bisschen herum. Er ist wirklich klein! Die Speicherkarte habe ich nur zum Größenvergleich daneben gelegt.
Wenn Ihr beides habt müsst Ihr das Display und den Controller folgendermaßen verbinden (zusammenlöten):
Display Eingang |
ESP32 C3 Super Mini |
5V |
5V |
GND |
G |
DIN |
PIN 3 |
Ich habe dazu vom ‚DIN‘ Anschluss den Stecker bzw. Buchse mit einem Seitenschneider abgeschnitten, die 3 Drahtenden etwa 2 – 3 mm abisoliert, verzinnt und dann an die passenden Kontakte des Super Mini gelötet. Das war es auch schon! Wenn Ihr jetzt ein USB-C Kabel einsteckt, könnt Ihr den Controller und damit das Display über die Arduino IDE programmieren.
In einer für ESP32 eingerichteten Arduino IDE müsst Ihr als Board ‚ESP32C3 Dev Module‘ auswählen. Leider schaltet der Super Mini nicht automatisch in einen Modus um ein kompiliertes Programm zu empfangen. Ihr müsst erst den ‚Boot‘-Taster drücken und halten, dann den ‚Reset‘-Taster drücken und wieder los lassen und dann den ‚Boot‘-Taster wieder loslassen. Mit dieser Methode muss der Super Mini für jede Programmänderung präpariert werden. Um ein Programm nach dem Upload auszuprobieren, muss dann noch einmal der ‚Reset‘-Taster gedrückt werden.
Die blaue LED auf dem Board lässt sich übrigens über GPIO 8 ansprechen und den ‚Boot‘-Taster könnt Ihr über GPIO 9 verwenden.
Ich habe mir aus Wellpappe von alten Kartons einen Rahmen gebastelt, ihn mit schwarzer Farbe eingesprüht und mit über Heftzwecken befestigtes Kordel und einen Saugnapfhaken von innen zur Weihnachtszeit in ein Fenster gehängt.
Einige Hinweise zur Programmierung
Für das Debugging mit ‚Serial.println‘ ist es wichtig in der Arduino IDE die Option ‚Tools-> USB CDC on Boot:‘ auf ‚Enabled‘ stehen zu haben!
Ergänzung: Mir ist gerade (07.02.2024) aufgefallen, dass der Upload auf den Mini jetzt auch funktioniert ohne dass ich die Boot- und Reset-Taste drücken muss! Ich glaube aber dass das nicht funktioniert hat, als ich diese Einstellung lediglich geändert hatte. Möglicherweise klappt das erst, nachdem ich die Arduino IDE einmal komplett herunter gefahren und anschließend wieder gestartet habe. Auch das Booten des Computers könnte notwendig gewesen sein oder aber ein erster Upload nach der Umstellung. Jedenfalls ist die Arbeit mit dem ESP32-C3 Super Mini so wesentlich angenehmer.
Um die LEDs anzusteuern benötigt Ihr die Adafruit Neopixel Bibliothek. Die könnt Ihr über den ‚Library Manager‘ der Arduino IDE installieren.
Wenn man mit der Adafruit Neopixel Bibliothek arbeitet, so muss man sich klar machen, dass die LEDs dabei immer als eindimensionale Folge betrachtet werden. Also egal wie sie tatsächlich geographisch angeordnet sind, sind sie in Wirklichkeit alle hintereinander wie eine Kette elektronisch verbunden. Die am nahesten am Anschluss befindliche LED ist die Nr. 1, die nächste LED die Nr. 2 und immer so weiter. Auch im Falle des Displays ist das so. Ich habe das Display so gedreht, dass sich die erste LED rechts oben befindet, dann geht es nach links, am linken Rand eine Zeile runter und wieder nach rechts, also im Zick-Zack nach unten. Bei der Programmierung einer Animationsidee ist es mir aber lieber wenn ich mit einer Fläche arbeiten und die einzelnen Pixel über x und y adressieren kann. Darum habe ich mir 2 kleine Funktionen ‚setPixel‘ und ‚getPixel‘ programmiert, die aus einer übergebenen XY Koordinate die korrekte Nummer in der LED Kette berechnen. Ab jetzt brauche ich mich nicht mehr damit beschäftigen, dass das Display eigentlich eine LED Kette ist. Mit ‚getPixel‘ könnt Ihr die gesetzte Farbe an der entsprechenden Position zurück bekommen.
Zum Ausprobieren habe ich Euch ein paar Funktionen programmiert:
-
chainWay – zeigt Euch den ‚natürlichen‘ Weg der LEDs.
-
matrixWay – zeigt Euch wie man über x und y die Fläche ansprechen kann.
-
calcHalfIntensity – zeigt wie man Farben auf Bit-Ebene beeinflussen kann. Ok, das ist ein bisschen tricky, aber vielleicht für manche der Einstieg in ein tieferes Verständnis wie Computer eigentlich funktionieren.
-
drawArray – ermöglicht es eine vordefinierte Grafik mit einem einzigen Befehl direkt in das Display zu malen.
Wichtiger Tipp: Viele Animationen sind sehr von exakten zeitlichen Abläufen abhängig, damit sie gut aussehen. Darum arbeitet mit Konstanten in denen ihr die Zeiten in Millisekunden festlegt und erstellt Euch Programmschleifen die in Abhängigkeit vom Wert der Konstanten regelmäßig durchlaufen werden.
Beispielhaft könnt Ihr das in der ‚loop‘-Schleife sehen! Experimentiert mit der Änderung der Werte der beiden Konstanten SPAWNLED und DIMLED. Versucht zu verstehen wie die ‚loop‘-Schleife im Detail funktioniert.
Wie kann man da jetzt kreativ werden?
Ihr müsst Euch klar machen, dass Ihr in einer Sekunde etwa 130 Mal die Farbe aller 256 LEDs neu bestimmen könnt! Wie man so etwas ausrechnen kann, könnt Ihr im Adafruit NeoPixel Überguide nachlesen. Damit das menschliche Auge Zustandsänderungen nicht mehr direkt wahrnehmen kann, sollten Animationen mit etwa 24 Bildern pro Sekunde ablaufen. Das ermöglicht es völlig fließende Animationen zu programmieren. Das ist aber nur eine Anregung, bei manchen Ideen können auch langsamer ablaufende Änderungen einen tollen Effekt bewirken. Am Anfang könnt Ihr Euch damit beschäftigen wie man eine einzige LED so programmieren kann, dass sie wie eine Kerze flackert. Wenn das wirklich gut aussehen soll, ist es gar nicht so einfach. Ich arbeite viel mit vordefinierten Farbpaletten in eindimensionalen Arrays. Manche Ideen lassen sich schnell programmieren, andere sind vielleicht sehr schwer zu realisieren und erfordern ausgetüftelte Algorithmen. Also insofern ist die Programmierung von solchen Animationen intellektuell eine Herausforderung und eine hervorragende Übung für jeden Programmierer.
Wie wäre es ein Feuer zu simulieren? Schneeflocken? Einen Weihnachtsbaum mit flackernden Kerzen? 2 flackernde Kerzen deren Flamme aus vielen LEDs besteht? Einen Matrix Screensaver? Das lässt sich alles realisieren!
Der ESP32 C3 Super Mini kann aber bei weitem mehr! So ist es möglich über WiFi Kontakt mit dem Internet aufzunehmen und von dort Informationen abzufragen. Also könnte das Display die Uhrzeit anzeigen, einen Aktienkurs, das Wetter abfragen und grafisch hübsch aufbereitet darstellen, oder die Anzahl neuer Emails! Aber auch alles zusammen und nacheinander auf dem Display dargestellt!
#include <Adafruit_NeoPixel.h>
#define SCREENWIDTH 16
#define SCREENHEIGHT 16
#define SPAWNLED 10
#define DIMLED 200
#define PIN 3
#define N_LEDS 256
Adafruit_NeoPixel strip = Adafruit_NeoPixel(N_LEDS, PIN, NEO_GRB + NEO_KHZ800);
int christmasTree[16][16] = {
{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 1, 5, 1, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 1, 2, 3, 2, 1, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 1, 2, 3, 2, 3, 2, 1, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 1, 1, 2, 2, 2, 1, 1, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 1, 3, 2, 2, 2, 3, 1, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 1, 2, 2, 2, 3, 2, 2, 2, 1, 0, 0, 0 },
{ 0, 0, 0, 0, 1, 1, 2, 2, 2, 2, 2, 1, 1, 0, 0, 0 },
{ 0, 0, 0, 0, 1, 2, 2, 3, 2, 3, 2, 2, 1, 0, 0, 0 },
{ 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0 },
{ 0, 0, 0, 1, 1, 2, 3, 2, 3, 2, 3, 2, 1, 1, 0, 0 },
{ 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0 },
{ 0, 0, 1, 2, 2, 3, 2, 3, 2, 3, 2, 3, 2, 2, 1, 0 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 4, 1, 1, 1, 1, 1, 1, 1 },
{ 0, 0, 0, 0, 0, 0, 0, 1, 4, 1, 0, 0, 0, 0, 0, 0 }
};
uint32_t christmasTreeColors[10] = { 0x000000, 0x000000, 0x003F00, 0x001F00, 0x0F0A00, 0x00FF00, 0x000000, 0x000000, 0x000000, 0x000000 };
long lastSpawnLed;
long lastDimLed;
void setup() {
Serial.begin(115200);
strip.begin();
strip.setBrightness(255);
strip.clear();
randomSeed(analogRead(37));
drawArray(christmasTree, christmasTreeColors);
strip.show();
delay(2000);
chainWay();
matrixWay();
}
void loop() {
int x;
int y;
int r;
int g;
int b;
uint32_t color;
while (true) {
// Spawn new color
if (millis() - lastSpawnLed > SPAWNLED) {
x = random(SCREENWIDTH);
y = random(SCREENHEIGHT);
r = random(64);
g = random(64);
b = random(64);
color = strip.Color(r, g, b);
setPixel(x, y, color);
lastSpawnLed = millis();
}
// Dim all LEDs
if (millis() - lastDimLed > DIMLED) {
for (int y = 0; y < SCREENHEIGHT; y++) {
for (int x = 0; x < SCREENWIDTH; x++) {
setPixel(x, y, calcHalfIntensity(getPixel(x, y)));
}
}
lastDimLed = millis();
}
strip.show();
}
}
void chainWay() {
strip.clear();
for (int i = 0; i < N_LEDS; i++) {
strip.setPixelColor(i, 0x303000);
strip.show();
delay(25);
}
}
void matrixWay() {
strip.clear();
for (int y = 0; y < SCREENHEIGHT; y++) {
for (int x = 0; x < SCREENWIDTH; x++) {
setPixel(x, y, 0x003030);
strip.show();
delay(25);
}
}
}
void setPixel(int x, int y, uint32_t color) {
int index;
int invertedX = SCREENWIDTH - 1 - x;
if (y % 2 == 0) {
index = y * SCREENWIDTH + invertedX;
} else {
index = (y + 1) * SCREENWIDTH - 1 - invertedX;
}
strip.setPixelColor(index, color);
}
unsigned int getPixel(int x, int y) {
int index;
int invertedX = SCREENWIDTH - 1 - x;
if (y % 2 == 0) {
index = y * SCREENWIDTH + invertedX;
} else {
index = (y + 1) * SCREENWIDTH - 1 - invertedX;
}
return strip.getPixelColor(index);
}
void drawArray(int picture[16][16], uint32_t pictureColors[10]) {
for (int y = 0; y < SCREENHEIGHT; ++y) {
for (int x = 0; x < SCREENWIDTH; ++x) {
if (picture[x][y] != 0) {
setPixel(y, x, pictureColors[picture[x][y]]);
}
}
}
}
uint32_t calcHalfIntensity(uint32_t startColor) {
int startR = (uint8_t)(startColor >> 16);
int startG = (uint8_t)(startColor >> 8);
int startB = (uint8_t)startColor;
float newR = startR / 2;
float newG = startG / 2;
float newB = startB / 2;
return strip.Color(int(newR), int(newG), int(newB));
}