Im nun zweiten Teil zum ESP32 Development Board möchte ich dir gerne zeigen, wie die Touchfunktion des Displays in der Arduino IDE programmiert wird. Im ersten Teil habe ich dir das Board bereits ausführlich vorgestellt und gezeigt, wie du Grafiken und Texte auf dem Display anzeigen kannst.
Dieser Beitrag ist wie erwähnt die Fortführung des Beitrages ESP32 Development Board mit 2,8 Zoll Touch Display: Programmieren für Anfänger ich gehe im nachfolgenden davon aus, dass du das Board bereits bei dir eingerichtet und betriebsbereit hast. Sollte dieses jedoch nicht so sein, so empfehle ich dir zunächst einen Blick in den zuvor verlinkten Beitrag.
Inhaltsverzeichnis
- Pinbelegung des Touchfeldes vom ESP32 Development Board
- Programmieren des Touchfeldes in der Arduino IDE
- Beispiel – erkennen einer Berührung auf dem Display
- Ein Button Zeichnen und Touchfunktion hinterlegen
- Fazit
Pinbelegung des Touchfeldes vom ESP32 Development Board
Schauen wir uns zunächst die Pinbelegung des Touchfeldes an:
Bezeichnung | Pin |
---|---|
CLK | IO25 |
CS | IO33 |
DIN / MOSI | IO32 |
OUT / MISO | IO39 |
IRQ | IO36 |
Das Board findest du sehr günstig auf Aliexpress.com zusätzlich findest du dort auch noch einen Schaltplan, aus welchem du weitere Informationen entnehmen kannst.
Programmieren des Touchfeldes in der Arduino IDE
Wie im Beitrag zuvor möchte ich das TFT Display in der Arduino IDE programmieren. Dazu benötigst du eine Bibliothek. Welche du über den internen Bibliotheksverwalter (1) installieren kannst. Wenn dieser geöffnet ist, dann suchst du zunächst nach “LovyanGFX” und wählst die Schaltfläche “INSTALLIEREN” (3).
Zusätzlich zu dieser Bibliothek benötigen wir noch die Datei lgfx_ESP32_2432S028.h welche wir uns vom GitHub Repository OttoMeister / ARDUINO_ESP32-2432S028R herunterladen können.
Probleme beim Upload
Bevor wir mit der Programmierung beginnen, möchte ich dich auf eine Besonderheit mit dem Umgang der Bibliothek LovyanGFX in der Arduino IDE 2.0 hinweisen.
Zum einen musste ich feststellen, dass der Upload zum Mikrocontroller sehr lange dauert (> 15 min.) und zum anderen manchmal mit dem Fehler “ping: timeout” abbricht. Als “Lösung” habe ich zur klassischen Arduino IDE 1.8.19 gewechselt, der Upload hier dauerte jedoch das erste mal etwas länger aber nach dem ersten Kompilieren dauerte der Vorgang dann nurnoch so knapp 30 Sekunden.
Beispiel – erkennen einer Berührung auf dem Display
Starten wir nun mit einem Beispiel und schreiben ein Programm, welches eine Berührung auf dem Touchdisplay erkennt und die X & Y Koordinate ausgibt.
Der Code hierfür ist recht einfach, theoretisch müssen wir nur eine Funktion aufrufen und erhalten, ob ein Klick auf dem Display erfolgte und zusätzlich erhalten wir die Koordinaten.
/* Einbinden der Bibliothek zum kommunizieren mit dem Display. */ #define LGFX_USE_V1 #include <LovyanGFX.hpp> #include "lgfx_ESP32_2432S028.h" //Größe der Zeichenfläche definieren #define MAX_X 319 #define MAX_Y 239 //Felder für die ermittelte Position //bei einem klick uint16_t x = 0, y = 0; //Instanz des Displays static LGFX lcd; void setup(void) { //beginn der seriellen Kommunikation mit //115200 Baud Serial.begin(115200); //beginn der Kommunikation mit dem Display lcd.init(); //drehen des Displays lcd.setRotation(1); //füllen des Displays mit der Farbe Schwarz lcd.fillScreen(TFT_BLACK); //eine kleine Pause von 100ms. delay(100); } void loop() { //Wenn ein Touchaktion ausgeführt / erkannt wurde, //dann liefert die Funktion getTouch den Wert 1 und //befüllt die übergebenen Parametern mit den Koordinaten if (lcd.getTouch(&x, &y) == 1) { //befüllen des Displays mit der Farbe schwarz lcd.fillScreen(TFT_BLACK); //Wenn die ermittelte X oder Y Positon außerhalb der Range ist, //dann soll die Funktion hier verlassen werden. if (x > MAX_X || y > MAX_Y) { return; } //ein Kreuz an der X & Y Koordinate Zeichnen lcd.drawLine(x - 20, y, x + 20, y, 0xFFFF00U); lcd.drawLine(x, y - 20, x, y + 20, 0xFFFF00U); //setzen der Schriftart & Schriftgröße lcd.setFont(&fonts::Font2); //setzen des Cursors abhängig von der X/Y Koordinate setCursorPosition(0); //schreiben der X Position lcd.printf("x:%d", x); //setzen des Cursors abhängig von der X/Y Koordinate //mit einem Offset von 20 Pixel setCursorPosition(20); //schreiben der X Position lcd.printf("y:%d", y); //Zusätzlich werden die ermittelten Koordinaten auf der //Konsole ausgegeben. Serial.printf("x:%d,y:%d", x, y); } //eine Pause von 50ms. delay(50); } void setCursorPosition(int offset) { if (x < 280 && y < 200) { lcd.setCursor(x + 5, y + offset); } else { if (x > 280 && y > 200) { //|| y > 200 lcd.setCursor(x - 35, y - 40 + offset); } else if ((x < 20 && y > 200) || (x > 20 && y > 200)) { lcd.setCursor(x + 5, y - 40 + offset); } else if (x > 280) { lcd.setCursor(x - 35, y + offset); } } }
Ein Button Zeichnen und Touchfunktion hinterlegen
Wollen wir nun einen Button auf dem Display zeichnen und diesem eine Aktion hinterlegen. Dabei soll der Button sein Style ändern, wenn dieser gedrückt wird.
Das Styling ist etwas rustikal, jedoch funktional. Für coole Dashboards gibt es andere Tools, welche ich dir noch separat in eigenen Beiträgen präsentieren möchte.
Vorwort
Meine Programme schreibe ich immer sehr dynamisch, d.h. es ist manchmal etwas mehr Code, aber dafür kann dieser deutlich besser wiederverwendet werden. Genau das mache ich in diesem Beispiel hier auch und erstelle mir ein struct welches die Eigenschaften für den Button speichert.
//Eigenschaften eines Buttons definieren struct Button { bool isPressed; //ist dieser gedrückt? int coord_x; //X Koordinate int coord_y; //Y Koordinate int width; //Breite int height; //Höhe uint8_t backgroundcolor; //Hintergrundfarbe uint8_t textcolor; //Textfarbe String caption; //Text auf dem Button };
Wenn du das fertige Programm herunterladen möchtest, dann kannst du dieses über nachfolgenden Link machen oder auf mein GitHub Repository StefanDraeger / ESP32_Development_Board_ESP32-2432S028R.
einen Button mit Effekt programmieren
Im ersten Schritt erstellen wir ein Rechteck mit abgerundeten Ecken und für den Schatten das gleiche nochmal in Grau. Somit hat man ein oldschool 3D Effekt.
Ich habe mir zunächst 2 Buttons definiert (siehe das struct oben) und in ein Array hinterlegt, das macht es einfacher, wenn wir diese auswerten und zeichnen möchten (die Eigenschaften sind ja somit gleich bzw. wir haben diese als Attribute hinterlegt.)
//definieren von zwei Buttons Button btn1 = { false, 20, 20, 50, 25, lcd.color332(255, 0, 0), lcd.color332(255, 255, 255), "Test" }; Button btn2 = { false, 20, 60, 100, 25, lcd.color332(0, 255, 0), lcd.color332(0, 0, 0), "Hallo Welt!" }; //die beiden Buttons in ein Array aufnehmen //das macht es später einfacher diese abzuarbeiten const int NUM_BUTTONS = 2; Button buttons[2] = { btn1, btn2 }; //Offset für den Schatten des Buttons const int SHADOW_OFFSET = 2;
Wie bereits erwähnt arbeite ich mit einem struct vom Typ Button, das ermöglicht es mir die Eigenschaften an einem “Objekt” zu speichern und dieses einfach einer Funktion zu übergeben.
//Zeichnen eines Buttons void drawButton(Button button) { //Wenn der Button gedrückt ist, //dann soll der Schatten nach oben links zeigen if (button.isPressed) { lcd.fillRoundRect(button.coord_x - SHADOW_OFFSET, button.coord_y - SHADOW_OFFSET, button.width, button.height, 10, lcd.color332(176, 190, 197)); } else { //Wenn der Button nicht gedrückt ist, //dann soll der Schatten nach unten rechts zeigen lcd.fillRoundRect(button.coord_x + SHADOW_OFFSET, button.coord_y + SHADOW_OFFSET, button.width, button.height, 10, lcd.color332(176, 190, 197)); } //Zeichnen eines Rechtecks mit abgerundeten Ecken mit den Eigenschaften des Buttons lcd.fillRoundRect(button.coord_x, button.coord_y, button.width, button.height, 10, button.backgroundcolor); //Berechnen der Position für den Text. int textLenght = (button.caption.length() * 3); int newX = (button.width / 2) - (textLenght / 2) + 5; //setzen der Textfarbe und der Hintergrundfarbe lcd.setTextColor(button.textcolor, button.backgroundcolor); //positionieren des Cursors lcd.setCursor(button.coord_x + (newX / 2), button.coord_y + 8); //schreiben des Textes an die gesetzte Position lcd.print(button.caption); }
Die Eigenschaft isPressed wird verwendet, um die Aktion auszuwerten, wenn der Button geklickt wird. Es soll zunächst der Schatten wechseln, damit wird optisch ein Drücken sichtbar.
In der loop prüfen wir nun fortlaufend ob ein Button geklickt wurde.
void loop() { //Wenn auf das Display geklickt wird, dann //liefert die Funktion getTouch eine 1 ansonsten 0 //zusätzlich werden die Koordinaten in die Felder //x & y gespeichert. if (lcd.getTouch(&x, &y) == 1) { //Wenn die Koordinaten außerhalb der Range sind, //dann soll die Funktion hier verlassen werden. if (x > MAX_X || y > MAX_Y) { return; } //Schleife über das Array mit den Buttons for (int i = 0; i < NUM_BUTTONS; i++) { //Button aus dem Array mit dem Wert aus //der Laufvariable i lesen. Button button = buttons[i]; //Wenn die geklickte Position auf dem //Display innerhalb der Abgrenzung eines //Buttons ist, dann... if (checkCoordButton(button, x, y)) { //Togglen des Buttons toggleButton(button); } } } //eine Pause von 50 Millisekunden. delay(50); }
An den Buttons haben wir die Koordinaten und Breite & Höhe hinterlegt, von der Funktion getTouch erhalten wir zusätzlich die geklickten Koordinaten. Mit der nachfolgenden Funktion prüfen wir dann ob sich die geklickte Koordinate auf dem Button befindet.
//Prüfen ob die angeklickte Position auf dem Display innerhalb der Abgrenzung //des Buttons ist. bool checkCoordButton(Button button, int x, int y) { return (x >= button.coord_x && x <= (button.coord_x + button.width) && y >= button.coord_y && y <= (button.coord_y + button.height)); }
Wenn dieses so ist, dann soll der Button getoggelt werden, d.h. es wird optisch signalisiert das dieser gedrückt wurde.
//Einen angeklickten Button togglen. //D.h. es wird kurz der schatten nach oben links //verschoben und danach wieder nach unten rechts. void toggleButton(Button button) { //Button entfernen removeButton(button); //setzen das der Button gedrückt ist button.isPressed = true; //zeichnen des Buttons drawButton(button); //eine kleine Pause von 85 Millisekunden delay(85); //Button entfernen removeButton(button); //setzen das der Button nicht gedrückt ist button.isPressed = false; //zeichnen des Buttons drawButton(button); }
Dabei wird der Button mit einem schwarzen Rechteck gelöscht und neu gezeichnet, der schatten wird dann jedoch nach links oben verschoben. Die Pause dient dazu das man dieses erkennt und gleich danach wird wiederum der Button gelöscht und normal gezeichnet.
//entfernen eines Buttons void removeButton(Button button) { //ein einfaches schwarzes Rechteck an die Position des Buttons zeichnen lcd.fillRect(button.coord_x - 5, button.coord_y - 5, button.width + 10, button.height + 10, lcd.color332(0, 0, 0)); }
eine LED via Button steuern
Wir haben nun die logik für den Button programmiert, was nun fehlt ist das Event welches ausgeführt werden soll wenn dieser gedrückt wird.
Ich habe zusätzlich noch das struct zum Button um eine ID erweitert damit diese beim ausführen einer Aktion besser zugeordnet werden kann.
//Eigenschaften eines Buttons definieren struct Button { int id; //der Identifier für den Button bool isPressed; //ist dieser gedrückt? int coord_x; //X Koordinate int coord_y; //Y Koordinate int width; //Breite int height; //Höhe uint8_t backgroundcolor; //Hintergrundfarbe uint8_t textcolor; //Textfarbe String caption; //Text auf dem Button };
Zusätzlich habe ich für die LEDs auch ein struct erstellt, dieser Typ hält mir den Pin und auch den zustand der LED.
//Eigenschaften für die LEDs definieren struct LED { int pin; bool active; }; //konfigurieren der beiden LEDs LED ledGelb = { ledGelbPin, false }; LED ledBlau = { ledBlauPin, false };
In der Funktion setup müssen wir nun die Pins der LEDs als Ausgang definieren.
void setup(void) { ... pinMode(ledGelb.pin, OUTPUT); pinMode(ledBlau.pin, OUTPUT); ... }
Die Aktion beim drücken des Buttons wird in der Funktion toggleButton ausgeführt.
//Einen angeklickten Button togglen. //D.h. es wird kurz der schatten nach oben links //verschoben und danach wieder nach unten rechts. void toggleButton(Button button) { //Button entfernen removeButton(button); //setzen das der Button gedrückt ist button.isPressed = true; //zeichnen des Buttons drawButton(button); //aktion auslösen callButtonAction(button); //eine kleine Pause von 85 Millisekunden delay(85); //Button entfernen removeButton(button); //setzen das der Button nicht gedrückt ist button.isPressed = false; //zeichnen des Buttons drawButton(button); }
Die aktion callButtonAction erhält als Parameter den gedrückten Button wobei dieser dann über seine ID ausgewertet wurd und dann abhängig von dieser ID die blaue oder gelbe LED aktiviert/deaktiviert wird.
//Anhand der ID vom Button soll eine Aktion ausgeführt werden, //in diesem simplen Beispiel ist es lediglich das Aktivieren einer //LED. void callButtonAction(Button button) { switch (button.id) { case 0: activateLED(ledGelb); break; case 1: activateLED(ledBlau); break; } } //Umkehren des Zustandes der LED, zusätzlich wird der neue Zustand //auch an dem Feld LED gespeichert. void activateLED(LED &led) { led.active = !led.active; digitalWrite(led.pin, led.active); }
Fazit
Die Touchfunktion am ESP32 Development Board ist cool und bietet viele Möglichkeiten, jedoch ist die erstellung von Dashboards über das zeichnen von geometrischen Figuren sehr mühselig und auch old school. Es gibt andere Tools welche hier den Entwickler deutlich unterstützen, diese werde ich mir nun einmal anschauen und dir dann passende Beiträge dazu liefern.
Hallo Stefan,
wie immer klasse erklärt, obwohl es schon recht komplex ist.
Ich bin auf weitere Teil(e) gespannt.
VG
Holger F.