Raspberry Pi Pico 2 W auf Waveshare ResTouch 2.8 - Touchprobleme

Hallo Community,

ich bin dabei ein Projekt mit dem Waveshare Pico ResTouch LCD 2.8 und einem Raspberry Pi Pico 2 W zu realisieren. Das Grundprogramm läuft bereit, leider will der Touchscreen nich so wie ich das möchte. Das Display erkennt zwar jeden touch (Programm wird durch Berührung des Bildschirm aus dem Standby geholt → funktioniert tadellos), jedoch immer auf x=4095 und y=4095, egal wo ich wirklich eintippe. Die (manuelle) Kalibrierung verläuft somit erfolglos. Hatte jemand ähnliche Probleme oder eine Idee woran das liegen könnte?

Danke schon mal.

LG Frank

Zeige Link zu deinen Sachen, 4095 ist doch viel zu viel.

Das hört sich nach einem (maximalen) Wert aus einem 12bit ADC an.

@deln54 neben dem Link zu dem, was Du hast, zeig mal den Code

sieht etwas wild aus da ich verzweifelt nach Lösungen gesucht habe bisher.

Hardware:

2,8 Zoll Touch Display Modul für Raspberry Pi Pico, 262K Farben, 320×240, SPI - kaufen bei BerryBase

Raspberry Pi Pico 2W, RP2350 + WLAN + Bluetooth Mikrocontroller-Board - kaufen bei BerryBase

#include <TFT_eSPI.h>
#include <XPT2046_Touchscreen.h>
#include <EEPROM.h>
#include <SoftwareSerial.h>
#include "logo.h"
#include <User_Setups/Setup23_TTGO_TM.h>
#include <SPI.h>
#include "User_Setup.h"


// --- Pins ---
#define TOUCH_CS 16
#define TOUCH_IRQ 17
#define BT_TX 3
#define BT_RX 4
#define RP2040_PIO_SPI

// --- HW-Objekte ---
SoftwareSerial btSerial(BT_RX, BT_TX);
TFT_eSPI tft = TFT_eSPI();
XPT2046_Touchscreen ts(TOUCH_CS, TOUCH_IRQ);

// --- Kalibrierungswerte ---
int xMin, xMax, yMin, yMax;  // Kalibrierungsvariablen für den Touchscreen

// --- Zustände / Werte ---
volatile bool touchDetected = false;
bool inStandby = false;
unsigned long lastInteraction = 0;
const unsigned long standbyTimeout = 8000;  // 8 s

int volume ;    // 0..100 (Default)
int subwoofer ;  // 0..15  (Default)
int preset ;     // 0..3   (P1..P4)

// --- Funktionsprototypen ---
void drawUI();
void drawButton(int x, int y, int w, int h, const String &label, bool inverted);
void drawScreensaver();
void enterStandby();
void exitStandby();
void handleTouch(int x, int y);
void saveSettings();
void sendDSP();
void drawLabelCentered(int x, int y, int w, int h, const String &label, int font, int textSize);
void calibrateTouch();

// --- Touch-IRQ ---
void touchISR() {
  touchDetected = true;
}

void setup() {
  Serial.begin(115200);
  btSerial.begin(38400);

  tft.init();
  tft.setRotation(3);  // LANDSCAPE: 320 (Breite) x 240 (Höhe)
  tft.fillScreen(TFT_BLACK);

  ts.begin();
  ts.setRotation(3);  // gleiche Rotation wie Display

  // Versuche, die Kalibrierungswerte aus dem EEPROM zu laden
  EEPROM.begin(16);
  /*if (EEPROM.read(5) == 0x42) {
    // Kalibrierungswerte aus EEPROM laden
    xMin = EEPROM.read(6);
    xMax = EEPROM.read(7);
    yMin = EEPROM.read(8);
    yMax = EEPROM.read(10);
    Serial.println("Kalibrierung aus EEPROM geladen.");
  } else {*/
    // Kalibrierung durchführen und Ergebnisse speichern
    calibrateTouch();
    Serial.println("Kalibrierung durchgeführt und gespeichert.");
  

  // TFT Bildschirm löschen und eine Nachricht anzeigen
  tft.fillScreen(TFT_BLACK);
  tft.setTextColor(TFT_WHITE);
  tft.setTextSize(2);
  tft.setCursor(40, 40);
  tft.println("Touchscreen kalibriert.");
  
  pinMode(TOUCH_IRQ, INPUT);
  attachInterrupt(digitalPinToInterrupt(TOUCH_IRQ), touchISR, FALLING);

  EEPROM.begin(16);

  // Erste Initialisierung via Magic-Byte (Addr 9)
  if (EEPROM.read(9) != 0x42) {
    volume = 20;
    subwoofer = 7;
    preset = 0;
    EEPROM.write(0, volume);
    EEPROM.write(1, subwoofer);
    EEPROM.write(2, preset);
    EEPROM.write(9, 0x42);
    EEPROM.commit();
  } else {
    volume = EEPROM.read(0);
    subwoofer = EEPROM.read(1);
    preset = EEPROM.read(2);
    // Validieren
    if (volume < 0 || volume > 100) volume = 20;
    if (subwoofer < 0 || subwoofer > 15) subwoofer = 7;
    if (preset < 0 || preset > 3) preset = 0;
  }
  resetCalibration();
  drawUI();
  lastInteraction = millis();
}

void loop() {
  // Screensaver nach Timeout
  if (!inStandby && (millis() - lastInteraction > standbyTimeout)) {
    enterStandby();
  }

  // Auf Touch reagieren
  if (touchDetected) {
    touchDetected = false;  // Entprellen am Anfang

    if (ts.tirqTouched()) {
      TS_Point p = ts.getPoint();

      int x = map(p.x, 300, 3800, 0, 320);  // links..rechts (0..319)
      int y = map(p.y, 300, 3800, 0, 240);  // oben..unten  (0..239)

      //x = 320 - x;
      //y = 240 - y;

      if (inStandby) {
        exitStandby();  // UI neu aufbauen
        delay(80);      // Debounce – Weck-Touch nicht auswerten
        lastInteraction = millis();
        return;
      }

      handleTouch(x, y);
      lastInteraction = millis();
    }
  }
}

void calibrateTouch() {
  Serial.println("Berühre die 4 Kalibrierungspunkte...");
  delay(2000);

  // Kalibrierungspunkte durchlaufen
  for (int i = 0; i < 4; i++) {
    // Zeige einen Punkt auf dem Display an
    if (i == 0) {
      tft.fillCircle(10, 10, 10, TFT_GREEN);  // oben links
    } else if (i == 1) {
      tft.fillCircle(310, 10, 10, TFT_GREEN);  // oben rechts
    } else if (i == 2) {
      tft.fillCircle(10, 230, 10, TFT_GREEN);  // unten links
    } else if (i == 3) {
      tft.fillCircle(310, 230, 10, TFT_GREEN);  // unten rechts
    }

    delay(5000);  // Warten, bevor der Benutzer den Punkt berührt

    // Lese den Touchscreen-Wert
    while (!ts.tirqTouched()) {
      delay(5000);  // Warten, bis der Benutzer den Punkt berührt
    }

    TS_Point p = ts.getPoint();

    // Zeige die Koordinaten an, die berührt wurden
    tft.setCursor(10, 220);  // Position für die Anzeige der Koordinaten
    tft.setTextSize(1);  // Kleinere Schriftgröße
    tft.setTextColor(TFT_WHITE, TFT_BLACK);  // Textfarbe (Weiß auf Schwarz)
    tft.fillRect(10, 220, 300, 20, TFT_BLACK);  // Lösche vorherige Koordinatenanzeige
    tft.print("X: ");
    tft.print(p.x);
    tft.print(" Y: ");
    tft.println(p.y);

    // Speichere die gemessenen Werte (die X- und Y-Koordinaten des Touchscreens)
    if (i == 0) {
      xMin = p.x;
      yMin = p.y;
    }
    if (i == 1) {
      xMax = p.x;
      yMin = p.y;
    }
    if (i == 2) {
      xMin = p.x;
      yMax = p.y;
    }
    if (i == 3) {
      xMax = p.x;
      yMax = p.y;
    }

    delay(5000);  // Kurze Pause nach dem Messen

    // Blinken des Kalibrierungspunkts (Punkt für eine kurze Zeit ausblenden und wieder einblenden)
    tft.fillScreen(TFT_BLACK);  // Bildschirm löschen
    delay(200);  // Warte kurz, um den Punkt ausblenden zu lassen
    // Zeige den Punkt erneut für ein weiteres kurzes Intervall
    if (i == 0) {
      tft.fillCircle(10, 10, 10, TFT_GREEN);  // oben links
    } else if (i == 1) {
      tft.fillCircle(310, 10, 10, TFT_GREEN);  // oben rechts
    } else if (i == 2) {
      tft.fillCircle(10, 230, 10, TFT_GREEN);  // unten links
    } else if (i == 3) {
      tft.fillCircle(310, 230, 10, TFT_GREEN);  // unten rechts
    }

    delay(5000);  // Kurze Zeit warten, damit der Punkt für den Benutzer sichtbar bleibt

    // Bildschirm wieder löschen, bevor der nächste Punkt angezeigt wird
    tft.fillScreen(TFT_BLACK);
  }
  // Speichere die Kalibrierungswerte im EEPROM
  EEPROM.write(5, 0x00);
  EEPROM.write(6, xMin);
  EEPROM.write(7, xMax);
  EEPROM.write(8, yMin);
  EEPROM.write(10, yMax);
  EEPROM.commit();
}

// --- UI: Volume oben, Subwoofer Mitte, Presets unten ---
void drawUI() {
  tft.fillScreen(TFT_BLACK);
  tft.setTextColor(TFT_WHITE, TFT_BLACK);

  // --- Volume ---
  tft.setTextSize(2);
  tft.drawCentreString("Volume", 160, 8, 2);

  // Zeile: y = 40..80 (Mitte bei y=60)
  drawButton(10, 40, 80, 40, "-", false);  // x:10..90,   y:40..80
  // Wert exakt zentriert bei (160, 60)
  tft.setTextSize(3);
  tft.setTextDatum(MC_DATUM);
  tft.drawString(String(volume), 160, 60, 2);
  tft.setTextDatum(TL_DATUM);
  drawButton(230, 40, 80, 40, "+", false);  // x:230..310, y:40..80

  // --- Subwoofer ---
  tft.setTextSize(2);
  tft.drawCentreString("Subwoofer", 160, 95, 2);

  // Zeile: y = 127..167 (Mitte bei y=147)
  drawButton(10, 127, 80, 40, "-", false);  // x:10..90,   y:127..167
  tft.setTextSize(3);
  tft.setTextDatum(MC_DATUM);
  tft.drawString(String(subwoofer), 160, 147, 2);
  tft.setTextDatum(TL_DATUM);
  drawButton(230, 127, 80, 40, "+", false);  // x:230..310, y:127..167

  // --- Presets unten: y = 195..235 (40 px Höhe), vier Felder à 80 px
  for (int i = 0; i < 4; i++) {
    int bx = i * 80;  // 0, 80, 160, 240
    int by = 195;     // 195..235

    if (i == preset) {
      tft.fillRect(bx, by, 80, 40, TFT_RED);
      tft.drawRect(bx, by, 80, 40, TFT_RED);
      tft.setTextColor(TFT_BLACK, TFT_RED);
    } else {
      tft.fillRect(bx, by, 80, 40, TFT_BLACK);
      tft.drawRect(bx, by, 80, 40, TFT_WHITE);
      tft.setTextColor(TFT_WHITE, TFT_BLACK);
    }

    // "P1".."P4" exakt mittig im Kasten
    drawCenteredInBox(bx, by, 80, 40, "P" + String(i + 1), 2, 2);
  }
}
// --- Standby ---
void enterStandby() {
  inStandby = true;
  drawScreensaver();
}

void exitStandby() {
  inStandby = false;
  drawUI();
}

void drawScreensaver() {
  // Falls dein logo.h nicht 320×240 ist, vorher skalieren oder anpassen
  tft.fillScreen(TFT_BLACK);

  // Logo mittig anzeigen (falls kleiner als 320x240)
  int x = (320 - logoWidth) / 2;
  int y = (240 - logoHeight) / 2;
  tft.pushImage(x, y, logoWidth, logoHeight, logoBitmap);
}

// --- Touch-Auswertung: Zonen exakt passend zu drawUI() ---
void handleTouch(int x, int y) {
  // Volume –
  // Button gezeichnet: 10,40,80x40 => x:10..90, y:40..80
  if (x >= 10 && x <= 90 && y >= 40 && y <= 80) {
    if (volume > 0) volume--;
    drawUI();
    sendDSP();
    saveSettings();
    return;
  }
  // Volume +
  // Button gezeichnet: 230,40,80x40 => x:230..310, y:40..80
  if (x >= 230 && x <= 310 && y >= 40 && y <= 80) {
    if (volume < 100) volume++;
    drawUI();
    sendDSP();
    saveSettings();
    return;
  }

  // Subwoofer –
  // Button gezeichnet: 10,127,80x40 => x:10..90, y:127..167
  if (x >= 10 && x <= 90 && y >= 127 && y <= 167) {
    if (subwoofer > 0) subwoofer--;
    drawUI();
    sendDSP();
    saveSettings();
    return;
  }
  // Subwoofer +
  // Button gezeichnet: 230,127,80x40 => x:230..310, y:127..167
  if (x >= 230 && x <= 310 && y >= 127 && y <= 167) {
    if (subwoofer < 15) subwoofer++;
    drawUI();
    sendDSP();
    saveSettings();
    return;
  }

  // Presets unten (vier Felder à 80 px), y:195..235
  if (y >= 195 && y <= 235) {
    int idx = x / 80;  // 0..3
    if (idx < 0) idx = 0;
    if (idx > 3) idx = 3;
    if (preset != idx) {
      preset = idx;
      drawUI();
      sendDSP();
      saveSettings();
    }
    return;
  }
}

// --- Persistenz ---
void saveSettings() {
  bool changed = false;
  if (EEPROM.read(0) != volume) {
    EEPROM.write(0, volume);
    changed = true;
  }
  if (EEPROM.read(1) != subwoofer) {
    EEPROM.write(1, subwoofer);
    changed = true;
  }
  if (EEPROM.read(2) != preset) {
    EEPROM.write(2, preset);
    changed = true;
  }
  if (changed) EEPROM.commit();
}

// --- DSP Senden (UART zu BT-Modul), inkl. Mapping ---
void sendDSP() {
  // Mapping auf DSP-Bytes:
  // Volume: 0..100  -> 0xF0..0x00  (rechts=laut)
  uint8_t volByte = map(volume, 0, 100, 0xF0, 0x00);
  // Subwoofer: 0..15 -> 0x00..0x0F
  uint8_t subByte = map(subwoofer, 0, 15, 0x00, 0x0F);
  uint8_t preByte = preset & 0x03;
  uint8_t cmd[8] = { 0x08, 0x43, volByte, subByte, 0xFF, 0xFF, preByte, 0x00 };

  btSerial.write(cmd, sizeof(cmd));  // an BT-Modul
 
}
void drawCenteredInBox(int x, int y, int w, int h, const String &label, int font, int textSize) {
  tft.setTextSize(textSize);
  tft.setTextDatum(MC_DATUM);  // Mittelpunkt als Bezugsdatum
  tft.drawString(label, x + w / 2, y + h / 2, font);
  tft.setTextDatum(TL_DATUM);  // zurück zum Standard
}
void drawButton(int x, int y, int w, int h, const String &label, bool inverted) {
  if (inverted) {
    tft.fillRect(x, y, w, h, TFT_WHITE);
    tft.drawRect(x, y, w, h, TFT_BLACK);
    tft.setTextColor(TFT_BLACK, TFT_WHITE);
  } else {
    tft.fillRect(x, y, w, h, TFT_BLACK);
    tft.drawRect(x, y, w, h, TFT_WHITE);
    tft.setTextColor(TFT_WHITE, TFT_BLACK);
  }
  // Zeichen exakt mittig (Font 2, Größe 3 sieht gut aus)
  drawCenteredInBox(x, y, w, h, label, 2, 3);
}
void resetCalibration() {
  // Kalibrierungswerte im EEPROM löschen
  EEPROM.write(0, 0x00);  // Magic Byte löschen
  EEPROM.write(1, 0);     // xMin zurücksetzen
  EEPROM.write(2, 0);     // xMax zurücksetzen
  EEPROM.write(3, 0);     // yMin zurücksetzen
  EEPROM.write(4, 0);     // yMax zurücksetzen
  EEPROM.commit();        // Änderungen speichern
}

In der Touchscreen-Lib gibt es ein Example "TouchTest.ino"
Dieses lade mal bitte und passe die Pins und die Geschwindigkeit für den Seriellen Monitor an.

Dann touch mal in alle 4 Ecken.
Was wird auf dem SerMon ausgegeben?

TFT_eSPI hat eigene Touch lib intern,

#include <XPT2046_Touchscreen.h>

Kommentieren und alles was dazugehört auch. Danach so Aufbauen wie im den Beispielen ob es mit raspi tut weiß nicht.

Es wird immer folgendes ausgegeben, egal ob ich was drücke oder nicht, sieht mir ggf nach einem Hardware-defekt aus?

Druck = 4095, x = 0, y = 0
Druck = 4095, x = 0, y = 0
Druck = 4095, x = 0, y = 0
Druck = 4095, x = 0, y = 0
Druck = 4095, x = 0, y = 0
Druck = 4095, x = 0, y = 0
Druck = 4095, x = 0, y = 0
Druck = 4095, x = 0, y = 0
Druck = 4095, x = 0, y = 0
Druck = 4095, x = 0, y = 0
Druck = 4095, x = 0, y = 0
Druck = 4095, x = 0, y = 0
Druck = 4095, x = 0, y = 0
Druck = 4095, x = 0, y = 0

Zumindest ist klar, wo die 4095 her kommen.
Das keine Koordinaten kommen, muss ich mal schauen, ob mir was einfällt.

Ich glaub zumindest schon mal den Display gefunden zu haben: https://www.waveshare.com/wiki/Pico-ResTouch-LCD-2.8

Kann die auch den Controller?
Der ist schon speziell... https://files.waveshare.com/upload/b/b0/XPT2046.pdf

Der Xpt ist Standard Kontroller bei allen günstigen Displays und der wird unterstützt. Normaler Weise funktioniert der sofort mit den Beispielen aus der Bibliotheken

Bin nicht zu Hause sonst hätte die gezeigt. Der Raspi ist sowie so nicht komplett unterstützt..

@deln54 Frag mall Raspi Forum.

Danke schon mal für eurer Informationen. Da das ganze sowieso 2-3 mal bauen muss, habe ich mal schnell 2 neue Displays bestellt, die kommen morgen. Werde dann erst mal eins der neuen dranhängen um die Hardware auszuschließen bevor sich hier jemand umsonst in die Software verrennt.

Das hört sich nach einem Plan an. :waving_hand:

1 Like

Hoffentlich nicht die gleichen. Jeder SPI Display funktioniert mit Raspi.

doch, ich möchte ja wissen ob es die Hardware ist oder nicht, andernfalls gehen die einfach zurück und gut. Wenn aber die TouchTest.ino unabhängig vom Display schon laufen sollte, sie es aber nicht tut, bin ich gespannt was andere baugleiche Display machen. Davon abgesehen sollte das meiner Meinung nach funktionieren wenn ein Hersteller explizit die Kombi mit dem Pico vorgibt. Alternativ, welche Displays wären deiner Meinung nach interessant?

Hast du auch lt. Anleitung !! die Dateien ausgetauscht. ??

Siehe Link in deinen Link !!

Gruß

Pucki

Warum sollte er das tun?
(Im Übrigen klemmen Deine Satzzeichentasten)

ja auch das habe ich gemacht

Weil es in der Anleitung steht. ?? :wink:

Davon abgesehen werden die meisten Displays über eine exteren Datei eingestellt.

Gruß

Pucki

Wo?
Tipp: Lesen bildet!

Dann hoffe ich mal du hast auch NICHT den Pico genommen der auf den Bild angezeigt ist, sondern deinen :wink:

Gruß

Pucki

Und schon wieder Unsinn,wenn man keine Ahnung hat….