Raspberry Pi Pico 2 W auf Waveshare ResTouch 2.8 - Touchprobleme

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
}