ESP32-2432S028 mit 2.8" Touchscreen (Cheap Yellow Display)
Erste Schritte: Programmierung der RGB-LED

Nachdem ich in meinem ersten Artikel zum TouchScreen-ESP32 auf die Hardware und das Pinout eingegangen war, wollen wir uns heute ein wenig mit der Software beschäftigen.

Genauer gesagt, mit der eigenen Programmierung des Cheap Yellow Display, wie es auch genannt wird.

Dazu gibt es zwei populäre Lösungen, nunja, genau genommen eigentlich drei, wenn man nach Versionen unterscheiden will: Im Grunde genommen ist unser Cheap Yellow Display nachwievor ein ESP32 mit ein wenig Peripherie drumherum. Mein Artikel zur Ersteinrichtung eines ESP32 hat also auch hier Gültigkeit. Der behandelt das Programmieren mit der Arduino IDE V 1.8. Die Arduino IDE Version 2.0 funktioniert aber analog.

In meinem Artikel Taugt Visual Studio Code mit Platform IO als Ersatz für die die Arduino IDE? beschreibe ich die Vorzüge von PlatformIO gegenüber der Arduino IDE V1.8. Dort findet man zwar die Einrichtung des älteren ESP8266, aber grundsätzlich ist das dasselbe wie beim ESP32. Aber ich werde die Unterschiede zum ESP32, insbesondere zum ESP32-2432S028 mit 2.8" Touchscreen, hier noch einmal aufzeigen.

Ich habe viele Projekte sowohl in der Arduino IDE als auch mit der Platform IO gemacht. Sei es mit Arduino Uno, Nano, dem Digispark, der STM32 Bluepill, mit dem ESP8266 und mit dem ESP32. Und immer hat mir VS Code mit PlatformIO besser gefallen. Es erinnert mit seinen vielen Funktionen halt eher an eine professionelle Umgebung wie man es von anderen Microsoft Visual Studio (z. B. für C++) her kennt. Die für sie noch unbekannten Funktionen können Anfänger verwirren bzw. ablenken, so dass es diesen unter Umständen schwer fällt, die für sie momentan richtigen Funktionen zu finden. Aber keine Angst. Ich werde hier erklären, was man wissen muss. Damit wird alles ganz easy.

Das ESP32-2432S028 CYD in VS Code mit Platform IO einbinden

Nachdem ihr VS Code und PlatformIO wie hier beschrieben installiert habt, habt ihr in der linken Spalte einen Alien-Kopf , den ihr anklickt. So kommt ihr in die Funktionen der PlatformIO, die Mikrocontroller in VS Code einbindet.

Klickt dort auf "Create New Project" und es erscheint rechts die "PIO Home" Seite mit großen, orangen Alien-Kopf. Oder ist es eine Ameise? Ist ja auch egal.

Klickt dann auf "New Project" und vergebt einen Namen für euer Projekt, vielleicht "ESP32-CYD-Beispiel". Nun müssen wir das passende Board auswählen. Das ist wichtig, denn damit legen wir fest, welche GPIOs für die einzelnen Ports benutzt werden, wieviel Flash und RAM der ESP32 hat und so weiter und so fort.

Wir wählen "Espressif ESP32 Dev Module" aus und als Framework "Arduino". By the way: In der Arudino IDE heißt das auszuwählende Board "ESP32 Dev Module". Als Location belassen wir es bei "Use default location", denn PlatformIO scheint irgendwie nur unterhalb des User-Ordners speichern zu können. Verzeichnisse außerhalb kann man nicht angeben. Darum belassen wir es bei der Standard-Angabe. Dann klicken wir auf OK und warten kurz, bis das Projekt konfiguriert ist.

Aus unserem Firmware-Download-Erfahrungen weiß ich schon, dass der Download von Firmware am besten mit 460800 Baud funktioniert. Außerdem haben ich mehrere serielle Adapter am PC und füge eine weitere Zeile an, damit PlatformIO mein CYD schneller findet. Die platformio.ini sieht dann wie folgt aus (die Kommentarzeilen am Anfang haben ich mal weggelassen):
[env:esp32dev] platform = espressif32 board = esp32dev framework = arduino upload_speed = 460800 upload_port = COM17
Außerdem hat uns PIO ein paar Ordner angelegt. Die sehen wir links. Klicken wir also auf den Pfeil vor "src", um den Ordner aufzuklappen. Dann finden wir dort "main.cpp". Das ist unser Programm und Dreh- und Angelpunkt. Doppelklicken wir ihn.

Rechts öffnet sich ein weiterer Tab "main.cpp" mit den Beispiel-Funktionen myFunction(), setup() und loop(). Setup() und Loop() kennen wir ja schon von der Arduino IDE her; myFunction ist ein Beispiel für eine eigene Funktion, die einfach nur zwei Zahlen addiert.

Ein kleiner Ausflug ins Thema Function-Prototyping: Es gilt als guter Stil, Funktionen, die im C-Source-Code stehen, am Anfang zu deklarieren, zu "prototypen". Damit weiß man (und der Compiler), welche Funktionen es in dieser Datei gibt und wie diese aussehen (Rückgabe-Variablenart, Parameter-Anzahl und Art). Eigentlich ist Function-Prototyping Pflicht, manche Compiler sind aber intelligent genug, sich die Funktionen und deren Aufbau herauszusuchen und sich selbst eine Tabelle dafür zu erstellen. PIO macht das nicht, und so kommt es, dass der Compiler einen Fehler in Zeile int result = myFunction(2, 3); in setup() anzeigt, würde die Zeile oben mit dem Prototyping int myFunction(int, int); fehlen. Eben, weil er zu diesem Zeitpunkt die myFunction-Funktion noch nicht kennt. Der PIO-Compiler geht nur einmal von oben nach unten durch.

Die myFunction-Zeilen können wir getrost löschen, sie dienen nur als Beispiel und Mahnung, das Prototyping nicht zu vergessen. Die erste Zeile #include <Arduino.h> allerdings muss drin bleiben, damit PIO weiß, dass wir hier nach "Arduino"-Art programmieren.

Ein erstes, einfaches Beispiel-Progrämmchen

Für ein erstes Beispiel wollen wir es simpel halten und noch gar nicht auf den Touchscreen eingehen. Wir wollen einfach nur eine Start-Meldung ausgeben und dann die RGB-LED auf der Rückseite ein bisschen bunt machen. Das soll fürs allererste genügen.

Beim Eingeben des Codes zeigt VS Code / PIO seine Stärken. Während des Tippens werden nach ein paar Buchstaben die in Frage kommenden Befehle unter der Eingabe angezeigt. Nun muss man nur noch Cursor hoch/runter und Tab drücken und der Befehl steht vervollständigt da. Gibt man dann die Klammer auf für die Funktion an, werden automatisch die erwarteten Parameter angezeigt und ggf. die Überlagerungen der Funktion mit den unterschiedlichen Parameter-Sets.

Fehlerhafte Dinge werden rot unterschlängelt, so ähnlich wie Rechtschreibfehler in Word. Außerdem zeigt die Zahl hinter dem (X) in der blauen Zeile ganz unten an, wieviele Fehler wir in unserem Code haben. Hinter dem /!\ stehen die Warnungen. Ein Klick darauf öffnet ein kleines Fenster unten, in dem die Fehler mit näherer Erklärung, was falsch ist, verzeichnet sind. Ein Klick wiederum auf eine solche Fehlerzeile springt an die entsprechende Stelle im Code.

Sind durch die Hinweise von PIO alle Fehler beseitigt, und zwar ohne den Code jedesmal kompilieren oder hochladen zu müssen, dann können wir unseren Code hochladen und testen. Dazu gibt es zwei Möglichkeiten: Einmal kann man auf die Ameise in der Leiste ganz links klicken und dann auf "Upload and Monitor". Das kompiliert, lädt hoch und startet dann automatisch den Serial Monitor. Sehr praktisch. Oder die zweite Möglichkeit: unten in der blauen Leiste zuerst auf den Pfeil nach rechts für Hochladen und dann später auf den Stecker für den Serial Monitor klicken. Vorteil: das Explorer-Fenster links bleibt so (ansonsten einfach auf die beiden Blätter in der linken Leiste klicken, um die wiederzubekommen); Nachteil: ein Klick mehr.

Wie man der PlatformIO beibringt, nicht neu zu builden (also zu kompilieren), nur weil der Upload schief ging, das habe ich noch nicht herausgefunden. Denn eigentlich blieben die Firmware und die Object-Files ja unverändert, es hat sich ja nichts am Code verändert.

Damit der Serial Monitor auch richtig funktioniert, empfiehlt es sich, die Baudrate in der platformio.ini festzulegen; sonst steht die standardmäßig auf nur 9600 Baud. Die ini sieht dann so aus:
[env:esp32dev] platform = espressif32 board = esp32dev framework = arduino upload_speed = 460800 upload_port = COM17 monitor_speed = 115200 monitor_port = COM17
Und hier das Beispielprogramm schon mal komplett. Vielleicht schon einmal in das Beispielprojekt in VS Code hineinkopieren und dann VS Code und dieses Fenster nebeneinander legen, um die Erklärungen dazu dann hier zu lesen.
// CYD RGB LED Beispielprogramm V1.00 // V1.00, 2024 by Oliver Kuhlemann, cool-web.de // Quelle: https://cool-web.de/esp8266-esp32/ // Weiterverwendung mit Quellenangabe, siehe CC-BY-NC-SA 4.0 // https://creativecommons.org/licenses/by-nc-sa/4.0/deed.de #include <Arduino.h> #define ledPinR 4 /* rot */ #define ledPinG 16 /* grün */ #define ledPinB 17 /* blau */ #define ledChnR 0 /* LEDc-Channel für rot */ #define ledChnG 1 /* LEDc-Channel für grün */ #define ledChnB 2 /* LEDc-Channel für blau */ #define ledRes 8 /* LEDc-Resolution: 8 bit */ #define ledFrq 5000 /* LEDc-Frequency in Hz */ /*** Prototyping ***/ void ledRGB_init(); void ledRGB (uint8_t R, uint8_t G, uint8_t B); void ledRGB (uint8_t R, uint8_t G, uint8_t B) { // Übergabe von 0 (aus) bis 255 (voll hell) ledcWrite(ledChnR, 255-R); // Rot-Wert - Werte der CYD-RGB-LED sind invertiert! 255 = aus, 0 = voll hell ledcWrite(ledChnG, 255-G); // Grün ledcWrite(ledChnB, 255-B); // Blau } void ledRGB_init() { // LEDc PWM für Channel konfigurieren ledcSetup(ledChnR, ledFrq, ledRes); ledcSetup(ledChnG, ledFrq, ledRes); ledcSetup(ledChnB, ledFrq, ledRes); // LEDc GPIO-Pins für Channel konfigurieren ledcAttachPin(ledPinR, ledChnR); ledcAttachPin(ledPinG, ledChnG); ledcAttachPin(ledPinB, ledChnB); ledRGB(0, 0, 0); // alle LEDs sofort aus, die sind nämlich sonst alle an wegen der Invertierung! } void setup() { Serial.begin(115200); Serial.print("Programm gestartet."); ledRGB_init(); } void loop() { ledRGB(255,0,0); delay (2000); // rot ledRGB(0,255,0); delay (2000); // grün ledRGB(0,0,255); delay (2000); // blau ledRGB(255,255,0); delay (2000); // gelb ledRGB(0,127,127); delay (2000); // cyan ledRGB(255,0,255); delay (2000); // magenta ledRGB(255,255,255); delay (2000); // weiß ledRGB(0,0,0); delay (2000); // aus int d = 10; uint8_t r, g, b; while (1) { // Funkel funkel kleiner Stern r= rand(); g= rand(); b= rand(); if (r > 100) r-=100; if (g > 150) g-=150; if (g > 100) g-=100; if (b > 150) b-=150; if (b > 100) b-=100; //Serial.printf("%d %d %d\n", r, g, b); ledRGB(r,g,b); delay(d); } }
Eigentlich ist im Source Code alles gut dokumentiert, aber trotzdem ein paar Anmerkungen: Die GPIO-Pin-Definitionen für die RGB LED des CYD findet ihr auch auf dieser Seite neben dem Foto der LED.

Unter dem Arduino sind wir es gewohnt, analogWrite() zu benutzen, um per PWM eine LED in Helligkeitsstufen leuchten zu lassen. Siehe dazu auch meinen Artikel Einstellen der Farbe einer RGB LED und dort die Funktion void showRGB(byte R, byte G, byte B). AnalogWrite gibt es so aber nicht beim ESP32, hier wird LEDC benutzt.

LEDC steht für LED Control und ist bei Espressif hier dokumentiert. Kurz erklärt müssen wir folgendes tun, um eine LED per PWM via LEDC anzusprechen: Der Übersichtlichkeit halber habe ich die Befehle, um die PWM-Timer zu initialisieren in eine Funktion ledRGB_init() gepackt, die wir einmal zu Anfang im setup() aufrufen. Dort werden die LEDs auch gleich wieder ausgeschaltet, denn wegen der Invertierung sind sie nach der Definition standardässig an.

Damit ich mir wegen der Invertierung nicht immer das Hirn wegen der richtigen (invertierten) Werte verrenken muss, gibt es die Funktion ledRGB(), die das zurecht rückt und bei der 0 "aus" und 255 "volle Helligkeit" bedeutet, so wie sich das gehört. Der übergebe ich einfach die drei Farbwerte und gut ist. Praktischerweise sind das dieselben wie die üblichen RGB-Farbcodes für den PC (8 Bit für 3 Farben, macht 24 Bit zusammen).

Zum Test lassen wir die LED in loop() zuerst in ein paar Grundfarben leuchten und danach endlos in Zufallsfarben leuchten, so dass es aussieht, wie er funkelnder Stern oder ein Flux-Kompensator (eigentlich ja Flux-Kondensator, aber das ist eine andere Geschichte).

Man sollte auf die RGB-LED ein Stück weißes Papier legen, damit sich die Farben der 3 internen LEDs besser mischen. Ich werde mir wohl später ein Gehäuse mit meinem 3D-Drucker herauslassen, indem ich einen Kreis freilasse, in den ich dann ein mit transparentem Filament gedruckten Kreis mit Epoxy einkleben werde, so dann sich damit das Licht diffuser verteilt. Ansonsten kommen die Misch-Farben nicht richtig rüber.

Sinvoll sind sowieso eher nur ein paar Grundfarben. So wie ich sie zum Beispiel in meinem Projekt Luftqualität / Feinstaub-Messgerät mit Panasonic SN-GCJA5 Partikel-Sensor benutzt habe, um die Luftqualität anzuzeigen. Da habe ich eine Art Ampel von blau über grün, gelb, orange bis zu rot benutzt. Diese Farben sind gut erkennbar und auseinanderhaltbar. Und man verbindet die Ampel mit ihr, wo grün gut = freie Fahrt und rot = Stop bedeutet. Man könnte noch magenta / violett hinten anhängen für besonders schwere Fälle.

Video

Ein Demo-Video, dass das Cheap Yellow Display mit diesem Code in Aktion zeigt, möchte ich euch natürlich nicht vorenthalten:



Firmware Download via ESP Web Tools

Du möchtest die Firmware schon einmal ausprobieren, ohne die Entwicklungsumgebung zu installieren oder zu programmieren?

Kein Problem. Schließe einfach dein ESP32-2432S028 an einen USB-Port deines PC an und klicke auf den Connect-Button. Da das Display noch nicht angesprochen wird, funktioniert die Firmware für alle Board-Revisionen.

Das Firmware-Update funktioniert leider nur mit Google Chrome und Chromium-basierten Browsern wie Microsoft Edge, Opera, Vivaldi oder Brave; jedoch nicht mit Firefox. Bitte benutze eine HTTPS-Verbindung, HTTP funktioniert nicht.

Fazit und Aussichten

Unser erstes kleines Programm läuft auf dem ESP32-2432S028. Wir haben gelernt, wie man in VS Code mit Platform IO programmiert und die Firmware hochlädt.

Im nächsten Teil machen wir uns daran, auch das Display zu nutzen.

Quellen, Literaturverweise und weiterführende Links