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:
- Arduino IDE V 1.8.x: Die klassische Arduino IDE, die wir schon von Arduino Uno und Nano kennen.
- Arduino IDE V 2.x: Die neue Arduino IDE mit verbessertem Editor und Oberfläche, die allerdings noch langsamer ist als die 1.8.
- PlatformIO zusammen mit Microsoft Visual Studio Code: Die meines Erachtens professionellste Lösung mit dem besten Editor mit brauchbarer Syntax-Vervollständigung und Debugging-Möglichkeiten. Dafür vielleicht nicht ganz so verbreitet und einsteigerfreundlich.
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
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
// 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);
}
}
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:
- einen Channel pro PWM-Kanal, also für jede LED definieren. Das geht via ledcSetup(). Der WROOM-32 Chip des CYD hat 16 dieser Channel, die parallel benutzt werden können. Wir greifen uns für unseren drei Farben Channel 0 bis 2 und definieren außerdem Frequenz und Auflösung.
Die Frequenz gibt an, wie oft die LED pro Sekunde flackern kann. 5000 ist ein guter Wert. Wenn jetzt der Duty Cycle des PWM auf 2500 gestellt würde, würde die LED 2500 mal an und 2500 mal aus sein und mit halber Helligkeit leuchten. Als Auflösung habe ich 8 Bit gewählt. Der ESP32 hat einige Timer, die den PWM managen. Im Grunde sind Timer Zähler, die nach einer bestimmten Anzahl überlaufen und somit bestimmte Größen zählen können.
Eine Auflösung von 8 Bit bedeutet, der Timer kann von 0 bis 255 zählen, was 2 hoch 8 entspricht. So ein Timer zählt natürlich, wie alles in der MCU intern, binär. Mit 8-bit-Auflösung haben wir 256 Helligkeitsstufen. Das reicht vollkommen, und wird zum Beispiel auch bei HTML-Farben verwendet. - einem Channel eine GPIO-Pin-Nummer zuweisen, an dem die Hardware hängt. Das ist in unserem Falle GPIO 4, 16 und 17 für die klitzekleinen LEDs in rot, grün und blau, die sich in dem weißen Gehäuse befinden. Das geht mit der Funktion ledcAttachPin().
- der Einfachheit halber habe ich die Werte für die Pins, die Frequenz und die Auflösung als Defines an den Anfang des Sources gesetzt, damit man sie schnell wieder findet, falls man mal was ändern muss. Und dann muss man es nur an einer Stelle ändern.
- mit ledcWrite() schließlich schreibt man auf einen (zuvor definierten) PWM-Kanal dann einen Wert, in unserem Fall zwischen 0 und 255. Den Rest macht LEDC, also das schnelle An- und Ausschalten der LED. Doch Vorsicht! Hier bedeutet 0 nicht etwa aus, sondern 0 bedeutet an und 255 aus. die RGB-LED auf dem Cheap Yellow Display ist "invertiert".
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.
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.