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
}