ArduiTouch 1 - Funktionsweise und Verwendung des Touch-Screen

Der ArduiTouch ist eine Platine in einem schicken Wandgehäuse mit einem 2,4" TFT-Display, das mit einem Touch-Screen ausgestattet ist. Das Gerät wird als Bausatz geliefert. Eine sehr detaillierte Bauanleitung kann heruntergeladen werden.

Als Controller würde ich auf Grund der besseren Speicherausstattung das ESP32 Dev Kit C empfehlen.

Wenn die Platine fertig bestückt ist, sollten wir noch eine kleine Erweiterung vornehmen, damit ein Programm zum ESP32 hochgeladen werden kann, wenn er im ArduiTouch eingebaut ist. Für den TouchScreen Interrupt wird der GPIO2 Pin verwendet, der zum Flashen des ESP32 auf Low sein muss. Da der Interrupt Ausgang des Touchscreen Controller einen Pull Up Widerstand hat ist dieser Pin nicht auf Low, sodass das Hochladen des Programms nicht funktioniert. Als Abhilfe bauen wir eine zweipolige Stiftleiste ein über die wir mittels Jumper GPIO2 mit Masse verbinden können.

Das Bild zeigt die beiden Drahtverbindungen auf der Bauteileseite. Und das folgende Bild die Stiftleiste mit Jumper auf der Rückseite neben dem ESP32.

Natürlich muss nach dem Programmieren der Jumper entfernt werden, damit der Touch-Screen benutzt werden kann.

In diesem ersten Beitrag zum ArduiTouch möchte ich auf die Funktionsweise und Nutzung des Touch-Screens eingehen. Der hier verbaute Touch-Screen arbeitet nach dem Spannungsteiler Prinzip. Er besteht aus einer dünnen Glasplatte und darüber einer Polyester-Schicht, die beide mit IndiumZinnOxyd einem lichtdurchlässigen Halbleiter beschichtet sind. 

Legt man eine Spannung an die beiden gelben Anschluss-Streifen so erhält man einen linearen Spannungsverlauf entlang der Schicht. Bringt man nun die beiden Schichten an einem Punkt in Verbindung (punktierte Linie), so kann man an den grünen Anschluss-Streifen die gelbe Spannung am Berührungspunkt messen, sofern die Messung hochohmig erfolgt. Diese Spannung ist also ein Maß für die Lage des Berührungs-Punktes in der gelben Richtung. Legt man nun die Spannung an die grünen Anschluss-Streifen erhält man die Position in der grünen Richtung.

Der Touchscreen-Controller  XPT2046 legt abwechselnd Spannung an die beiden Folien und misst die Spannung an der gegenüberliegenden Folien. Die damit ermittelten Messwerte für x und y sowie ein Maß für den Kontaktdruck (z) stellt der Controller über den SPI Bus zur Verfügung. 

 

Das folgende Programm zeigt die Verwendung des Touch-Screen, vermittelt ein Gefühl für die Nutzung und ermöglicht es den Touchscreen zu kalibrieren. Das Display zeigt die Position oben als relative Position zur linken oberen Ecke in Bildschirm Koordinaten, unten die Messwerte, die der Touch-Screen-Controller liefert. Der gelbe Kreis wird rot wenn eine Berührung erkannt wurde. Die Pfeile in den Ecken zeigen auf die Eckpunkte des Displays und können zur Kalibrierung verwendet werden. Berührt man das grüne Rechteck in der Mitte, wird die Bildschirmausrichtung umgeschaltet.

Code:

#include <SPI.h>
#include "Adafruit_GFX.h" //Grafik Bibliothek
#include "Adafruit_ILI9341.h" // Display Treiber
#include <XPT2046_Touchscreen.h> // Touchscreen Treiber
#include <Fonts/FreeSans9pt7b.h> //Verwendete Schrift

#define _debug 1 //Anzeige von Meldunge am Seriellen Monitor

//Verwendete Pins am Display
#define TFT_CS   5
#define TFT_DC   4
#define TFT_MOSI 23
#define TFT_CLK  18
#define TFT_RST  22
#define TFT_MISO 19
#define TFT_LED  15


#define HAVE_TOUCHPAD
#define TOUCH_CS 14
#define TOUCH_IRQ 2

// Parameter für Touchscreen
#define ILI9341_ULTRA_DARKGREY    0x632C
#define MINPRESSURE 10
#define MAXPRESSURE 2000
//Messbereich muss eventuell kalibriert werden
#define TS_MINX 230
#define TS_MINY 350
#define TS_MAXX 3700
#define TS_MAXY 3900


//Treiber Instanzen
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
XPT2046_Touchscreen touch(TOUCH_CS, TOUCH_IRQ);

//aktuelle Positionen
int tsx, tsy, tsxraw, tsyraw;
//aktueller Touch Zustand
bool tsdown = false;
//aktuelle Bildschirmausrichtung
uint8_t rotation = 0;

//Vorbereitung
void setup() {
  #ifdef _debug
  Serial.begin(115200);
  #endif
  pinMode(TFT_LED, OUTPUT);
  digitalWrite(TFT_LED, HIGH);    // Display-Beleuchtung einschalten
  //Treiber starten
  tft.begin();
  touch.begin();
  #ifdef _debug
    //Auflösung des Displays
    Serial.print("tftx ="); Serial.print(tft.width()); Serial.print(" tfty ="); Serial.println(tft.height());
  #endif
  //aktuelle Werte zurücksetzen
  tsx = 0;
  tsy = 0;
  tsxraw = 0;
  tsyraw = 0;
  tsdown = false;
  rotation = 0;
  //Anzeigen
  draw_screen(rotation);

}

void loop() {
    //auf Berührung reagieren
    handleTouch();
    delay(100);
  }

//aktuelle Position und Berührungszustand
//vom Touchscreen ermitteln
void handleTouch() {
  TS_Point p;
  p = touch.getPoint(); //aktuelle Daten lesen
  tsxraw = p.x; //x und y als Rohwerte merken
  tsyraw = p.y;
  delay(1);
  //Bildschirm Ausrichtung ermitteln
  uint8_t rot = tft.getRotation();
  //je nach Ausrichtung relative Bildschirmpositionen
  //ermitteln
  switch (rot) {
    case 0: tsx = map(tsyraw, TS_MINY, TS_MAXY, 240, 0);
            tsy = map(tsxraw, TS_MINX, TS_MAXX, 0, 320);
            break;
    case 1: tsx = map(tsxraw, TS_MINX, TS_MAXX, 0, 320);
            tsy = map(tsyraw, TS_MINY, TS_MAXX, 0, 240);
            break;
    case 2: tsx = map(tsyraw, TS_MINY, TS_MAXY, 0, 240);
            tsy = map(tsxraw, TS_MINX, TS_MAXX, 320, 0);
            break;
    case 3: tsx = map(tsxraw, TS_MINX, TS_MAXX,320, 0);
            tsy = map(tsyraw, TS_MINY, TS_MAXY, 240, 0);
            break;
  }
  //Berührungszustand ermitteln
  if ((p.z > MINPRESSURE) != (tsdown)) {
    tsdown = (p.z > MINPRESSURE);
    //Überprüfen ob das grüne Rechteck in der Mitte berührt wurde
    if (tsdown && (tsx > (tft.width() / 2 - 20)) && (tsx < (tft.width() / 2 + 20)) 
       && (tsy > (tft.height() / 2 - 20)) && (tsy < (tft.height() / 2 + 20))) {
      //wenn ja dann Bildschirmausrichtung ändern
      rotation ++;
      if (rotation > 3) rotation = 0;
    }
    //Bildschirm neu zeichnen
    draw_screen(rotation);
  }


}

//Hauptbildschirm anzeigen
void draw_screen(uint8_t rot) {
  uint16_t w,h;
  //Ausrichtung Farben und Schrift auswählen
  tft.setRotation(rot); 
  tft.fillScreen(ILI9341_BLACK);
  tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
  tft.setFont(&FreeSans9pt7b);
  w = tft.width();
  h = tft.height();
  //je nach Ausrichtung Messwerte und
  //grünes Rechteck in der Mitte zeichnen
  if ((rot==1) || (rot == 3)) {
    drawPosition1(tsxraw,tsyraw,tsx,tsy,tsdown); 
    tft.fillRect(w/2 - 10, h/2 - 20,20,40, ILI9341_GREEN);
  } else { 
    drawPosition2(tsxraw,tsyraw,tsx,tsy,tsdown);
    tft.fillRect(w/2 - 20, h/2 - 10,40,20, ILI9341_GREEN);
  }
  //Rotations index im grünen Rechteck anzeigen
  tft.setCursor(w/2-5,h/2+6);
  tft.setTextColor(ILI9341_BLACK, ILI9341_GREEN);
  tft.print(rot);
  //Pfeile zu den Eckpunkten anzeigen
  tft.drawLine(0,0,20,0,ILI9341_WHITE);
  tft.drawLine(0,0,0,20,ILI9341_WHITE);
  tft.drawLine(0,0,40,40,ILI9341_WHITE);
  tft.drawLine(w-1,0,w-20,0,ILI9341_WHITE);
  tft.drawLine(w-1,0,w-1,20,ILI9341_WHITE);
  tft.drawLine(w-1,0,w-40,40,ILI9341_WHITE);
  tft.drawLine(w-1,h-1,w-40,h,ILI9341_WHITE);
  tft.drawLine(w-1,h-1,w,h-40,ILI9341_WHITE);
  tft.drawLine(w-1,h-1,w-40,h-40,ILI9341_WHITE);
  tft.drawLine(0,h-1,20,h-1,ILI9341_WHITE);
  tft.drawLine(0,h-1,0,h-20,ILI9341_WHITE);
  tft.drawLine(0,h-1,40,h-40,ILI9341_WHITE);
}

//Messwerte für Querformat anzeigen
void drawPosition1(uint16_t xraw, uint16_t yraw, uint16_t x, uint16_t y, bool down) {
  tft.setCursor(20,60);
  tft.print("X = ");
  display_right(110,60,String(x));
  tft.setCursor(180,60);
  tft.print("Y = ");
  display_right(270,60,String(y));

  tft.setCursor(20,180);
  tft.print("Xraw = ");
  display_right(120,180,String(xraw));
  tft.setCursor(180,180);
  tft.print("Yraw = ");
  display_right(280,180,String(yraw));
  if (down) tft.fillCircle(160,160,10,ILI9341_RED); else tft.fillCircle(160,160,10,ILI9341_YELLOW);
}  

//Messwerte für Hochformat anzeigen
void drawPosition2(uint16_t xraw, uint16_t yraw, uint16_t x, uint16_t y, bool down) {
  tft.setCursor(20,60);
  tft.print("X = ");
  display_right(110,60,String(x));
  tft.setCursor(20,100);
  tft.print("Y = ");
  display_right(110,100,String(y));

  tft.setCursor(20,240);
  tft.print("Xraw = ");
  display_right(120,240,String(xraw));
  tft.setCursor(20,280);
  tft.print("Yraw = ");
  display_right(120,280,String(yraw));
  if (down) tft.fillCircle(120,200,10,ILI9341_RED); else tft.fillCircle(120,200,10,ILI9341_YELLOW);
}  

//Eine Zahl rechtsbündig ausgeben 
void display_right(int x, int y, String val) {
  int16_t x1, y1;
  uint16_t w, h;
  int str_len =  val.length() + 1;
  char char_array[str_len];
  val.toCharArray(char_array, str_len);
  tft.getTextBounds(char_array, x, y, &x1, &y1, &w, &h);  
  tft.setCursor(x - w, y);
  tft.print(char_array);
}

Für die Kalibrierung benötigen wir die minimalen und maximalen Messwerte in X und Y Richtung. Dazu tippen wir zum Beispiel mit einem Kugelschreiber leicht auf die Spitzen der Pfeile und notieren die Messwerte Xraw und Yraw. Daraus können wir die minimalen und maximalen Werte ermitteln. Diese können wir dann im Programm für 

//Messbereich muss eventuell kalibriert werden
#define TS_MINX 230
#define TS_MINY 350
#define TS_MAXX 3700
#define TS_MAXY 3900

einsetzen um den Touchscreen zu kalibrieren. Nachdem wir den Sketch erneut hochgeladen haben sollte ein Tippen auf die Pfeile immer ziemlich genau die entsprechenden Display-Koordinaten liefern.

Der Touchscreen ist unabhängig von der am Display eingestellten Bildschirmausrichtung. Je nach Bildschirmausrichtung müssen die Messwerte unterschiedlich in das Bildschirm Koordinatensystem abgebildet werden. Wir verwenden dazu die map(wert,min,max,min1,max1) Funktion die den Wert "wert" aus dem Wertebereich "min, max"  auf den Wertebereich "min1, max1" abbildet.

Viel Spaß beim Experimentieren mit dem Touchscreen. Weitere Blog Beiträge zu ArduiTouch werden folgen, sodass wir das Gerät schließlich als Smart-Home Zentrale einsetzen können.

Letzter Artikel ArduiTouch 2 - Touchscreen als Eingabetastatur
Neuer Artikel GPS-Koordinaten am LCD ausgeben

Kommentar

Gerald - März 2, 2019

Hallo helhel
Du hast recht wenn Du einen MCU8266 verwendest. Für den ESP32 gilt wie im Beitrag angegeben
//used pins
#define TFT_CS 5 //diplay chip select
#define TFT_DC 4 //display d/c
#define TFT_MOSI 23 //diplay MOSI
#define TFT_CLK 18 //display clock
#define TFT_RST 22 //display reset
#define TFT_MISO 19 //display MISO
#define TFT_LED 15 //display background LED

helhel - Februar 28, 2019

Pin-Belegung im sketch stimmt nicht mit dem arduitouch überein.
richtig:
//Verwendete Pins am Display
#define TFT_CS 5
#define TFT_DC 4
#define TFT_MOSI 13
#define TFT_CLK 14
#define TFT_RST 22
#define TFT_MISO 12
#define TFT_LED 15

claus - Februar 17, 2019

Neuer Status: Alles funktioniert, prima.
Die Fehlermeldung resultiert aus der COPY-Funktion unter dem Script. Ich habe das Script aus dem Bildschirm heraus mit copy-paste ins Arduino-IDE gebracht.

claus - Februar 17, 2019

Der Aufbau des Displays ist einfach.
Der Touchscreen gibt im Debugmodus die Koordinaten im Monitor aus.
Nur das TFT bekomme ich nicht zum Laufen.Ich verwende einen ESP-Wroom-32 als Prozessor.
Ich verwende das Programmbeispiel und erhalte: tft.print(char_array); sketch_feb16b:35:1: error: stray ‘\302’ in program
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
Was mach ich falsch?

Thomas Lehnert - Februar 15, 2019

Ich bin noch relativ unerfahren in der Arduino-Welt, und möchte da gerne neue Erfahrungen machen. Das Ardu touch system spricht mich sehr an, weil ich für mein Zuhause eine Smarthome-Lösung gerne selbst bauen und programmieren möchte.
Mir schweben dazu schon einige Ideen im Kopf herum. Zum Beispiel möchte ich eine Überwachungsfunktion mit Datenlogger für meine Heizungsanlage integrieren, die den Füllstand des Heizöltanks, den Verbrauch sowie die Temperaturverläufe von Vor-und Rücklauftemperatur, Wassertemperatur der Warmwasserbereitung und eventuell weitere Meßwerte aufzeichnet. Eine Steuerung der Raumtemperaturen, des Lichtes usw ist auch denkbar. Ich freue mich über jeden Beitrag, der mir bei der Realisierung meiner Ideen helfen kann. Bin gespannt auf das Kommende.

Hinterlasse einen Kommentar

Kommentare müssen vor der Veröffentlichung überprüft werden

Erforderliche Angabe