Ich bringe auf einem CYD ESP32-2432S028R das Touch nicht zum Laufen Hat vielleicht jemand Erfahrung und ein bisschen Coding für mich. Das Display funktioniert.
Hi, schaue mal hier, vielleicht hilft das weiter.
Danke! Habe ich auch schon gesehen. Ist aber ein mächtiges, aber auch spezielles Coding. Wenn da ein Fehler kommt, such ich mir einen Wolf. Deshalb suche ich ja so ein “bisschen” Coding , ein Button oder so, um mich langsam vorzutasten. CGPT habe ich schon zur Verzweifelung getrieben -ohne Ergebnis.
Zeig doch mal was du schon hast!?
Bisher habe ich das Board dazu gebracht Farbe und Schrift anzuzeigen. Jeglicher Versuch, das Touch zu aktivieren hat entweder das Display wieder zerschossen oder gab nur die Koordinaten 0,0 zurück.
logger:
\# HSPI
spi:
clk_pin: GPIO14
mosi_pin: GPIO13
miso_pin: GPIO12
id: hspi
\# Backlight direkt HIGH
output:
\- platform: gpio
pin: GPIO21
id: backlight
switch:
\- platform: output
output: backlight
restore_mode: ALWAYS_ON
name: "Backlight Test"
font:
\- file: "gfonts://Roboto"
id: font_big
size: 26
display:
\- platform: ili9xxx
model: ili9342
id: tft_display
spi_id: hspi
cs_pin: GPIO15
dc_pin: GPIO2
reset_pin: GPIO4
rotation: 0
invert_colors: false
update_interval: 1s
color_palette: 8BIT
lambda: |-
it.fill(Color(0, 255, 0)); // Test grün
it.print(120, 160, id(font_big), TextAlign::CENTER, "ESP32 TFT OK");
EDIT by Jay: Code bitte immer über die Codefunktion „</>“ im Editor einfügen.
Code bitte immer in Code-Tags ( im Editor </>) setzen, sonst kann man das nicht wirklich lesen!?
esphome:
name: cyd-demo-portrait
friendly_name: CYD Demo Portrait (USB unten)
on_boot:
priority: 600
then:
- light.turn_on:
id: backlight
brightness: 0.80
esp32:
board: esp32dev
framework:
type: arduino
flash_size: 4MB
logger:
api:
ota:
- platform: esphome
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
ap:
ssid: "cyd-demo-fallback"
password: "12345678"
captive_portal:
# -----------------------------
# BACKLIGHT
# -----------------------------
output:
- platform: ledc
pin: GPIO21
id: backlight_pwm
light:
- platform: monochromatic
id: backlight
output: backlight_pwm
restore_mode: ALWAYS_ON
default_transition_length: 0s
# -----------------------------
# TIME
# -----------------------------
time:
- platform: sntp
id: esptime
timezone: Europe/Berlin
# -----------------------------
# TOUCH STATE
# -----------------------------
globals:
- id: last_touch_x
type: int
initial_value: "0"
- id: last_touch_y
type: int
initial_value: "0"
- id: touched
type: bool
initial_value: "false"
# -----------------------------
# SPI
# -----------------------------
spi:
- id: tft
clk_pin: GPIO14
mosi_pin: GPIO13
miso_pin: GPIO12
- id: touch_spi
clk_pin: GPIO25
mosi_pin: GPIO32
miso_pin: GPIO39
# -----------------------------
# TOUCHSCREEN
# -----------------------------
touchscreen:
platform: xpt2046
spi_id: touch_spi
cs_pin: GPIO33
interrupt_pin: GPIO36
update_interval: 35ms
threshold: 400
calibration:
x_min: 280
x_max: 3860
y_min: 340
y_max: 3860
transform:
swap_xy: false
mirror_x: true
mirror_y: false
on_touch:
- lambda: |-
if (touches.empty()) return;
id(last_touch_x) = touches[0].x;
id(last_touch_y) = touches[0].y;
id(touched) = true;
on_release:
- lambda: |-
id(touched) = false;
# -----------------------------
# FONT
# -----------------------------
font:
- file: "fonts/RobotoSlab-Medium.ttf"
id: font_small
size: 18
# -----------------------------
# DISPLAY
# -----------------------------
display:
- platform: ili9xxx
spi_id: tft
model: ili9341
cs_pin: GPIO15
dc_pin: GPIO2
invert_colors: false
update_interval: 200ms
auto_clear_enabled: true
color_palette: 8BIT
dimensions:
width: 320
height: 240
rotation: 90
lambda: |-
it.fill(Color(0, 0, 0));
it.printf(8, 8, id(font_small), Color(255,255,255),
"CYD Demo (USB unten)");
it.strftime(8, 32, id(font_small), Color(0,128,255),
"%H:%M:%S", id(esptime).now());
it.printf(8, 60, id(font_small), Color(255,255,255),
"Touch: %d, %d", id(last_touch_x), id(last_touch_y));
if (id(touched)) {
int w = it.get_width();
int h = it.get_height();
int x = id(last_touch_x);
int y = id(last_touch_y);
if (x < 0) x = 0;
if (y < 0) y = 0;
if (x >= w) x = w - 1;
if (y >= h) y = h - 1;
it.filled_circle(x, y, 6, Color(0,255,0));
}
Das sollte eigentlich so gehen (bei mir läuft es jedenfalls so !). ![]()
Und dasselbe in „Ouer!“
CYD Demo – Landscape USB rechts
esphome:
name: cyd-demo-landscape-usb-right
friendly_name: CYD Demo (Landscape USB rechts)
on_boot:
priority: 600
then:
- light.turn_on:
id: backlight
brightness: 0.80
esp32:
board: esp32dev
framework:
type: arduino
flash_size: 4MB
logger:
api:
ota:
- platform: esphome
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
ap:
ssid: "cyd-demo-fallback"
password: "12345678"
captive_portal:
output:
- platform: ledc
pin: GPIO21
id: backlight_pwm
light:
- platform: monochromatic
id: backlight
output: backlight_pwm
restore_mode: ALWAYS_ON
default_transition_length: 0s
globals:
- id: last_touch_x
type: int
initial_value: "0"
- id: last_touch_y
type: int
initial_value: "0"
- id: touched
type: bool
initial_value: "false"
spi:
- id: tft
clk_pin: GPIO14
mosi_pin: GPIO13
miso_pin: GPIO12
- id: touch_spi
clk_pin: GPIO25
mosi_pin: GPIO32
miso_pin: GPIO39
touchscreen:
platform: xpt2046
spi_id: touch_spi
cs_pin: GPIO33
interrupt_pin: GPIO36
update_interval: 35ms
threshold: 400
calibration:
x_min: 280
x_max: 3860
y_min: 340
y_max: 3860
transform:
swap_xy: true
mirror_x: false
mirror_y: false
on_touch:
- lambda: |-
if (touches.empty()) return;
id(last_touch_x) = touches[0].x;
id(last_touch_y) = touches[0].y;
id(touched) = true;
on_release:
- lambda: |-
id(touched) = false;
font:
- file: "fonts/RobotoSlab-Medium.ttf"
id: font_small
size: 18
display:
- platform: ili9xxx
spi_id: tft
model: ili9341
cs_pin: GPIO15
dc_pin: GPIO2
invert_colors: false
rotation: 90
dimensions:
width: 240
height: 320
update_interval: 200ms
auto_clear_enabled: true
color_palette: 8BIT
lambda: |-
const int W = it.get_width();
const int H = it.get_height();
it.fill(Color(0,0,0));
it.printf(10, 10, id(font_small), Color(255,255,255),
"CYD Demo (Landscape USB rechts)");
it.printf(10, 34, id(font_small), Color(180,180,180),
"W=%d H=%d", W, H);
it.printf(10, 64, id(font_small), Color(255,255,255),
"Touch: %d, %d", id(last_touch_x), id(last_touch_y));
if (id(touched)) {
int x = id(last_touch_x);
int y = id(last_touch_y);
if (x < 0) x = 0;
if (y < 0) y = 0;
if (x >= W) x = W - 1;
if (y >= H) y = H - 1;
it.filled_circle(x, y, 6, Color(0,255,0));
}
Danke !! Du hast mich ein ganzes Stück nach vorn gebracht. Portrait funktioniert = gibt Koordinaten zurück. Landscape-Coding stellt weiter in Portrait dar. Der obere Teil hat keine Farbe und die Schrift auf der linken Seite flackert. Wenn Du da jetzt noch einen Tip hättest? Und wenn Du mir ein Beispiel spendiertest, wie ich einen Button einfüge . . .
Demo für ein CYD (ESP32 + ILI9341 + XPT2046) im Landscape-Modus (USB rechts).
Features:
- Startseite mit Header (Uhr + WLAN-Balken) + Footer (leer)
- Demo-Button auf der Startseite (Touch)
- Setup-Seite per Tap auf Header (Helligkeit: ↑ / % / ↓)
- Backlight-Helligkeit wird live gesetzt und dauerhaft gespeichert (restore_value: yes)
- Setup-Seite wird NICHT über Neustart gespeichert (restore_value: no)
Kompletten Code anzeigen
esphome:
name: cyd-demo-landscape-usb-right
friendly_name: CYD Demo (Landscape USB rechts)
comment: Demo für ein CYD (ESP32 + ILI9341 + XPT2046) im Landscape-Modus (USB rechts).
# Features:
# - Startseite mit Header (Uhr + WLAN-Balken) + Footer (leer)
# - Demo-Button auf der Startseite (Touch)
# - Setup-Seite per Tap auf Header (Helligkeit: ↑ / % / ↓)
# - Backlight-Helligkeit wird live gesetzt und dauerhaft gespeichert (restore_value: yes)
# - Setup-Seite wird NICHT über Neustart gespeichert (restore_value: no)
# Beim Booten Backlight auf gespeicherte Helligkeit setzen
on_boot:
priority: 600
then:
- light.turn_on:
id: backlight
brightness: !lambda 'return id(display_brightness);'
esp32:
board: esp32dev
framework:
type: arduino
flash_size: 4MB
# Serielle Logs zur Fehlersuche
logger:
# API für Home Assistant / ESPHome API (optional, aber praktisch)
api:
# OTA Updates (Upload über Netzwerk)
ota:
- platform: esphome
# WLAN + Fallback AP (falls WLAN nicht erreichbar)
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
ap:
ssid: "cyd-demo-fallback"
password: "12345678"
captive_portal:
# ============================================================================
# BACKLIGHT (Display-Hintergrundbeleuchtung)
# ============================================================================
# CYD Backlight hängt häufig an GPIO21. Wir erzeugen PWM über LEDC und steuern
# damit ein monochromes "light". Brightness in ESPHome ist 0.0 .. 1.0.
output:
- platform: ledc
pin: GPIO21
id: backlight_pwm
light:
- platform: monochromatic
id: backlight
output: backlight_pwm
restore_mode: ALWAYS_ON
default_transition_length: 0s
# ============================================================================
# TIME (für Uhrzeit im Header)
# ============================================================================
time:
- platform: sntp
id: esptime
timezone: Europe/Berlin
# ============================================================================
# WIFI SIGNAL (für Balkenanzeige)
# ============================================================================
sensor:
- platform: wifi_signal
id: wifi_rssi
update_interval: 30s
# ============================================================================
# GLOBALS (Zustände, die wir im Code nutzen)
# ============================================================================
globals:
# Setup-Seite anzeigen? (NICHT über Neustart merken)
- id: show_setup_page
type: bool
restore_value: no
initial_value: "false"
# Display-Helligkeit (über Neustart merken)
- id: display_brightness
type: float
restore_value: yes
initial_value: "0.80"
# Demo-Button Status (über Neustart egal -> no)
# Wird beim Tippen auf den Demo-Button umgeschaltet.
- id: demo_button_on
type: bool
restore_value: no
initial_value: "false"
# ============================================================================
# SPI BUSSE
# ============================================================================
# Display SPI
spi:
- id: tft
clk_pin: GPIO14
mosi_pin: GPIO13
miso_pin: GPIO12
# Touch SPI (XPT2046 ist eigener SPI-Bus)
- id: touch_spi
clk_pin: GPIO25
mosi_pin: GPIO32
miso_pin: GPIO39
# ============================================================================
# TOUCHSCREEN (XPT2046)
# ============================================================================
# Wichtig: Die transform-Parameter + calibration Werte sind bei dir funktionierend.
# Touch liefert Koordinaten passend zu unserem Layout (Landscape).
touchscreen:
platform: xpt2046
spi_id: touch_spi
cs_pin: GPIO33
interrupt_pin: GPIO36
update_interval: 35ms
threshold: 400
calibration:
x_min: 280
x_max: 3860
y_min: 340
y_max: 3860
transform:
swap_xy: true
mirror_x: false
mirror_y: false
# --------------------------------------------------------------------------
# Touch-Logik:
# - Tap in Header: Setup-Seite an/aus
# - Wenn Setup offen: ↑ / ↓ Bereiche regeln die Helligkeit
# - Wenn Startseite: Demo-Button toggeln
# --------------------------------------------------------------------------
on_touch:
- lambda: |-
if (touches.empty()) return;
const int tx = touches[0].x;
const int ty = touches[0].y;
// --- Layout Konstanten (müssen mit Display-Lambda zusammenpassen) ---
const int bar_h = 24;
const int header_y0 = 2;
// Header (oben) Bereich: toggelt Setup
if (ty >= header_y0 && ty <= (header_y0 + bar_h)) {
id(show_setup_page) = !id(show_setup_page);
return;
}
// ---------------- SETUP Seite Touch ----------------
if (id(show_setup_page)) {
// Links angeordneter Brightness-Block:
// ↑
// 80%
// ↓
const int left_x = 28;
const int top_y = header_y0 + bar_h + 22;
// Touch-Hitbox (groß genug für Finger)
const int hit_w = 90;
const int hit_h = 40;
// Schrittweite: 5%
const float step = 0.05f;
// Helper: clamp + sofort Backlight setzen
auto apply_brightness = [&]() {
float b = id(display_brightness);
if (b < 0.0f) b = 0.0f;
if (b > 1.0f) b = 1.0f;
id(display_brightness) = b;
auto call = id(backlight).turn_on();
call.set_brightness(b);
call.perform();
};
// UP (Pfeil oben)
if (tx >= left_x &&
tx <= (left_x + hit_w) &&
ty >= top_y &&
ty <= (top_y + hit_h)) {
id(display_brightness) += step;
apply_brightness();
return;
}
// DOWN (Pfeil unten) - enger an Prozent
const int down_y = top_y + 50;
if (tx >= left_x &&
tx <= (left_x + hit_w) &&
ty >= down_y &&
ty <= (down_y + hit_h)) {
id(display_brightness) -= step;
apply_brightness();
return;
}
return; // Setup-Seite: sonst keine weiteren Aktionen
}
// ---------------- START Seite Touch ----------------
// Demo-Button Bereich (muss zum Display-Lambda passen)
const int btn_x = 16;
const int btn_y = header_y0 + bar_h + 36;
const int btn_w = 150;
const int btn_h = 48;
if (tx >= btn_x && tx <= (btn_x + btn_w) &&
ty >= btn_y && ty <= (btn_y + btn_h)) {
// Toggle Demo-Button Zustand
id(demo_button_on) = !id(demo_button_on);
// Optional: kleine visuelle Rückmeldung über Backlight "blinken"
// (kurz heller, dann zurück)
// Achtung: optional, kann entfernt werden.
auto call = id(backlight).turn_on();
call.set_brightness(id(display_brightness));
call.perform();
return;
}
# ============================================================================
# FONTS
# ============================================================================
# font_small: normaler Text
# font_icons: Material Design Icons für Pfeile (chevron-up/down)
font:
- file: "fonts/RobotoSlab-Medium.ttf"
id: font_small
size: 18
- file: "fonts/materialdesignicons-webfont.ttf"
id: font_icons
size: 40
glyphs:
- "\U000F0143" # chevron-up
- "\U000F0140" # chevron-down
# ============================================================================
# DISPLAY (ILI9341) - Landscape (USB rechts)
# ============================================================================
# WICHTIG:
# - rotation: 90
# - dimensions: width: 240, height: 320
# Das ergibt nach rotation effektiv W=320, H=240 im Lambda.
display:
- platform: ili9xxx
spi_id: tft
model: ili9341
cs_pin: GPIO15
dc_pin: GPIO2
invert_colors: false
rotation: 90
dimensions:
width: 240
height: 320
update_interval: 200ms
auto_clear_enabled: true
color_palette: 8BIT
# ------------------------------------------------------------------------
# Rendering:
# 1) Header-Balken (leicht grau) + Linie darunter (+2px)
# 2) Footer-Balken (leicht grau) + Linie darüber (-2px)
# 3) Header-Inhalt:
# - Startseite: Uhr links + WLAN rechts
# - Setup-Seite: "Setup" zentriert + WLAN rechts
# 4) Content:
# - Setup-Seite: Brightness ↑ % ↓ (links)
# - Startseite: Demo-Button + Statusanzeige
# ------------------------------------------------------------------------
lambda: |-
const int W = it.get_width(); // sollte 320 sein
const int H = it.get_height(); // sollte 240 sein
it.fill(Color(0,0,0));
// --- Layout Konstanten ---
const int bar_h = 24;
const int header_y0 = 2;
const int footer_y0 = H - bar_h - 2;
// Farben
Color bar_bg = Color(40,40,40); // Balken Hintergrund (leicht grau)
Color divider_col = Color(70,70,70); // Linienfarbe
Color text_main = Color(235,235,235);
Color text_dim = Color(160,160,160);
Color ok_green = Color(0,255,0);
// ================= HEADER =================
it.filled_rectangle(0, header_y0, W, bar_h, bar_bg);
// Linie unter Header +2px
it.line(0, header_y0 + bar_h + 2, W, header_y0 + bar_h + 2, divider_col);
// ================= FOOTER =================
it.filled_rectangle(0, footer_y0, W, bar_h, bar_bg);
// Linie über Footer -2px
it.line(0, footer_y0 - 2, W, footer_y0 - 2, divider_col);
// ================= HEADER CONTENT =================
if (id(show_setup_page)) {
// Setup: Überschrift mittig
it.printf(W/2, header_y0 + 8, id(font_small), text_main,
TextAlign::CENTER, "Setup");
} else {
// Startseite: Uhr links
it.strftime(8, header_y0 - 2, id(font_small), text_main,
"%H:%M", id(esptime).now());
}
// WLAN Balken rechts (immer)
float rssi = id(wifi_rssi).state;
if (!isnan(rssi)) {
int base_x = W - 30;
int base_y = header_y0 + bar_h - 8;
Color col;
if (rssi > -60) col = ok_green;
else if (rssi > -75) col = Color(255,165,0);
else col = Color(255,0,0);
if (rssi > -85) it.filled_rectangle(base_x, base_y-4, 3, 4, col);
if (rssi > -75) it.filled_rectangle(base_x+5, base_y-8, 3, 8, col);
if (rssi > -65) it.filled_rectangle(base_x+10, base_y-12, 3, 12, col);
if (rssi > -55) it.filled_rectangle(base_x+15, base_y-16, 3, 16, col);
}
// ================= SETUP PAGE =================
if (id(show_setup_page)) {
// Brightness-UI links, kompakt:
// ↑
// 80%
// ↓
const int left_x = 28;
const int top_y = header_y0 + bar_h + 22;
int pct = (int)(id(display_brightness) * 100.0f + 0.5f);
if (pct < 0) pct = 0;
if (pct > 100) pct = 100;
// Pfeil hoch
it.printf(left_x, top_y,
id(font_icons), ok_green,
TextAlign::LEFT, "\U000F0143");
// Prozent direkt darunter
it.printf(left_x + 2, top_y + 32,
id(font_small), Color(255,255,255),
TextAlign::LEFT, "%d%%", pct);
// Pfeil runter (enger an Prozent)
it.printf(left_x, top_y + 50,
id(font_icons), ok_green,
TextAlign::LEFT, "\U000F0140");
return;
}
// ================= STARTSEITE =================
// Content-Bereich zwischen Header und Footer
const int content_top = header_y0 + bar_h + 10;
// Titel
it.printf(W/2, header_y0 + 8, id(font_small), text_main,
TextAlign::CENTER, "Startseite");
// ---------- DEMO BUTTON ----------
// Button Box
const int btn_x = 16;
const int btn_y = content_top + 26;
const int btn_w = 150;
const int btn_h = 48;
// Farbe je nach Zustand
Color btn_bg = id(demo_button_on) ? Color(0,120,255) : Color(60,60,60);
Color btn_border = Color(110,110,110);
it.filled_rectangle(btn_x, btn_y, btn_w, btn_h, btn_bg);
it.rectangle(btn_x, btn_y, btn_w, btn_h, btn_border);
// Button Text
it.printf(btn_x + btn_w/2,
btn_y + 23,
id(font_small),
Color(255,255,255),
TextAlign::CENTER,
"Button");
// Status-Text rechts daneben
it.printf(btn_x + btn_w + 14,
btn_y + 14,
id(font_small),
Color(235,235,235),
TextAlign::LEFT,
"State: %s", id(demo_button_on) ? "ON" : "OFF");
// Zusatzinfo (RSSI)
if (!isnan(rssi)) {
it.printf(16, btn_y + btn_h + 16,
id(font_small), text_dim,
TextAlign::LEFT,
"WLAN: %.0f dBm", rssi);
}
Probier mal!
Habe es noch etwas erweitert, jetzt mit Button, Schalter und Anzeige von Temperaturen aus Home Assistant
Kompletten Code (erweitert) anzeigen
substitutions:
# --------------------------------------------------------------------------
# HIER STELLST DU ALLES EIN, WAS DU SPÄTER ÄNDERN WILLST, OHNE IM CODE ZU SUCHEN
# --------------------------------------------------------------------------
# Temperaturen aus Home Assistant (Entity IDs)
temp_entity_1: "sensor.kuhlschrank_temperatur"
temp_entity_2: "sensor.gefrierschrank_temperatur"
# Texte, die auf dem Display vor den Temperaturen stehen
temp_label_1: "T1"
temp_label_2: "T2"
# Button 1: Wird bei Touch ausgelöst (HA Action + Entity)
btn1_label: "Heizung"
btn1_action: "switch.toggle"
btn1_entity: "switch.ns_panel_stube_relay_1"
# Schalter (statt Button 2): Zeigt Zustand an und setzt ON/OFF
sw_label: "Luefter"
sw_entity: "switch.ns_panel_stube_relay_2"
esphome:
name: cyd-demo-landscape-usb-right
friendly_name: CYD Demo (Landscape USB rechts)
comment: CYD (ESP32 + ILI9341 + XPT2046), Landscape (USB rechts)
# Beim Start: Backlight auf den zuletzt gespeicherten Wert setzen
on_boot:
priority: 600
then:
- light.turn_on:
id: backlight
brightness: !lambda 'return id(display_brightness);'
esp32:
board: esp32dev
framework:
type: arduino
flash_size: 4MB
# Serielle Logs (hilfreich bei Fehlersuche)
logger:
# Verbindung zu Home Assistant (damit HA Entitäten gelesen und Actions gesendet werden können)
api:
# OTA Updates (Firmware per WLAN flashen)
ota:
- platform: esphome
# WLAN + Notfall-Access-Point falls das normale WLAN nicht erreichbar ist
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
ap:
ssid: "cyd-demo-fallback"
password: "12345678"
captive_portal:
# ============================================================================
# DISPLAY-HINTERGRUNDBELEUCHTUNG (Backlight)
# ============================================================================
# Viele CYD-Boards haben Backlight auf GPIO21. Wir steuern es per PWM (leiser/dimmer)
output:
- platform: ledc
pin: GPIO21
id: backlight_pwm
# Aus PWM wird ein "light" (Helligkeit 0.0 .. 1.0)
light:
- platform: monochromatic
id: backlight
output: backlight_pwm
restore_mode: ALWAYS_ON
default_transition_length: 0s
# ============================================================================
# UHRZEIT (für Anzeige oben im Header)
# ============================================================================
# Holt Zeit über Internet (SNTP) – reicht für eine einfache Uhranzeige
time:
- platform: sntp
id: esptime
timezone: Europe/Berlin
# ============================================================================
# SENSOREN
# ============================================================================
sensor:
# WLAN Signalstärke (für dBm Anzeige/Signalbalken)
- platform: wifi_signal
id: wifi_rssi
update_interval: 30s
# Temperatur 1 aus Home Assistant
- platform: homeassistant
id: temp_1
entity_id: ${temp_entity_1}
internal: true
# Temperatur 2 aus Home Assistant
- platform: homeassistant
id: temp_2
entity_id: ${temp_entity_2}
internal: true
# ============================================================================
# INTERNE VARIABLEN (globals)
# ============================================================================
globals:
# Wenn true: Setup-Seite sichtbar (Helligkeit einstellen)
- id: show_setup_page
type: bool
restore_value: no
initial_value: "false"
# Displayhelligkeit (wird gespeichert und nach Neustart wiederhergestellt)
- id: display_brightness
type: float
restore_value: yes
initial_value: "0.80"
# "Pressed"-Effekt: Button 1 kurz blau beim Tippen
- id: btn1_pressed
type: bool
restore_value: no
initial_value: "false"
# "Pressed"-Effekt: Schalter kurz blau beim Tippen
- id: sw_pressed
type: bool
restore_value: no
initial_value: "false"
# ============================================================================
# HOME ASSISTANT: Zustände lesen + Touch-Trigger
# ============================================================================
binary_sensor:
# -------------------------------------------------------
# btn1_touch ist nur ein "Trigger": wir setzen den per Touch kurz ON/OFF.
# Wenn er ON wird, wird eine HA-Action ausgeführt.
# -------------------------------------------------------
- platform: template
id: btn1_touch
internal: true
on_press:
- homeassistant.action:
action: ${btn1_action}
data:
entity_id: ${btn1_entity}
# -------------------------------------------------------
# Schalterzustand direkt aus Home Assistant lesen (ON/OFF)
# Damit können wir auf dem Display "ON/OFF" anzeigen.
# -------------------------------------------------------
- platform: homeassistant
id: sw_state
entity_id: ${sw_entity}
internal: true
# ============================================================================
# SCRIPTS (kleine Abläufe, die wir mehrfach brauchen)
# ============================================================================
script:
# Button 1: Pressed-Farbe nach kurzer Zeit zurücksetzen
- id: btn1_release_delay
mode: restart
then:
- delay: 200ms
- lambda: |-
id(btn1_pressed) = false;
# Schalter: Pressed-Farbe nach kurzer Zeit zurücksetzen
- id: sw_release_delay
mode: restart
then:
- delay: 200ms
- lambda: |-
id(sw_pressed) = false;
# Schalter in Home Assistant EIN schalten
- id: sw_turn_on
mode: restart
then:
- homeassistant.action:
action: switch.turn_on
data:
entity_id: ${sw_entity}
# Schalter in Home Assistant AUS schalten
- id: sw_turn_off
mode: restart
then:
- homeassistant.action:
action: switch.turn_off
data:
entity_id: ${sw_entity}
# ============================================================================
# SPI BUSSE
# ============================================================================
# 1) Display SPI (ILI9341)
# 2) Touch SPI (XPT2046) ist oft ein eigener Bus
spi:
- id: tft
clk_pin: GPIO14
mosi_pin: GPIO13
miso_pin: GPIO12
- id: touch_spi
clk_pin: GPIO25
mosi_pin: GPIO32
miso_pin: GPIO39
# ============================================================================
# TOUCHSCREEN (XPT2046)
# ============================================================================
touchscreen:
platform: xpt2046
spi_id: touch_spi
cs_pin: GPIO33
interrupt_pin: GPIO36
update_interval: 35ms
threshold: 400
# Kalibrierung: sorgt dafür, dass Touch-Koordinaten stimmen
calibration:
x_min: 280
x_max: 3860
y_min: 340
y_max: 3860
# Orientierung: passt zu "Landscape, USB rechts"
transform:
swap_xy: true
mirror_x: false
mirror_y: false
# --------------------------------------------------------------------------
# Touch-Logik:
# - Tippen im Header (oben): Setup-Seite an/aus
# - Setup-Seite: Pfeile hoch/runter ändern die Display-Helligkeit
# - Startseite:
# * Button 1: schaltet eine HA-Entity (toggle)
# * Schalter: zeigt Zustand (ON/OFF) und schaltet gezielt ON/OFF
# --------------------------------------------------------------------------
on_touch:
- lambda: |-
if (touches.empty()) return;
const int tx = touches[0].x;
const int ty = touches[0].y;
const int bar_h = 24;
const int header_y0 = 2;
// 1) Header: Setup-Seite an/aus
if (ty >= header_y0 && ty <= (header_y0 + bar_h)) {
id(show_setup_page) = !id(show_setup_page);
return;
}
// 2) Setup-Seite: Helligkeit hoch/runter
if (id(show_setup_page)) {
const int left_x = 28;
const int top_y = header_y0 + bar_h + 22;
const int hit_w = 90;
const int hit_h = 40;
const float step = 0.05f; // 5%
auto apply_brightness = [&]() {
float b = id(display_brightness);
if (b < 0.0f) b = 0.0f;
if (b > 1.0f) b = 1.0f;
id(display_brightness) = b;
auto call = id(backlight).turn_on();
call.set_brightness(b);
call.perform();
};
// Pfeil hoch
if (tx >= left_x &&
tx <= (left_x + hit_w) &&
ty >= top_y &&
ty <= (top_y + hit_h)) {
id(display_brightness) += step;
apply_brightness();
return;
}
// Pfeil runter
const int down_y = top_y + 50;
if (tx >= left_x &&
tx <= (left_x + hit_w) &&
ty >= down_y &&
ty <= (down_y + hit_h)) {
id(display_brightness) -= step;
apply_brightness();
return;
}
return;
}
// 3) Startseite: Layout-Koordinaten (müssen zum Display-Lambda passen)
const int content_top = header_y0 + bar_h + 10;
const int btn_x = 16;
const int btn_w = 150;
const int btn_h = 48;
const int btn1_y = content_top + 26;
// Schalter-Reihe
const int sw_y = btn1_y + btn_h + 12;
const int sw_w = 150;
const int sw_h = 48;
// Button 1: HA toggle
if (tx >= btn_x && tx <= (btn_x + btn_w) &&
ty >= btn1_y && ty <= (btn1_y + btn_h)) {
ESP_LOGI("touch", "BTN1 hit x=%d y=%d", tx, ty);
id(btn1_pressed) = true;
id(btn1_touch).publish_state(true);
id(btn1_touch).publish_state(false);
id(btn1_release_delay).execute();
return;
}
// Schalter: Zustand prüfen und gezielt ON/OFF setzen
if (tx >= btn_x && tx <= (btn_x + sw_w) &&
ty >= sw_y && ty <= (sw_y + sw_h)) {
ESP_LOGI("touch", "SW hit x=%d y=%d", tx, ty);
id(sw_pressed) = true;
id(sw_release_delay).execute();
if (id(sw_state).has_state() && id(sw_state).state) {
id(sw_turn_off).execute();
} else {
id(sw_turn_on).execute();
}
return;
}
# ============================================================================
# SCHRIFTEN
# ============================================================================
font:
# Normale Schrift
- file: "fonts/RobotoSlab-Medium.ttf"
id: font_small
size: 18
# Icons (Pfeile hoch/runter)
- file: "fonts/materialdesignicons-webfont.ttf"
id: font_icons
size: 40
glyphs:
- "\U000F0143" # chevron-up
- "\U000F0140" # chevron-down
# ============================================================================
# DISPLAY (ILI9341) – Landscape (USB rechts)
# ============================================================================
display:
- platform: ili9xxx
spi_id: tft
model: ili9341
cs_pin: GPIO15
dc_pin: GPIO2
invert_colors: false
# rotation 90 + dimensions 240x320 ergibt effektiv 320x240 im Lambda
rotation: 90
dimensions:
width: 240
height: 320
update_interval: 200ms
auto_clear_enabled: true
color_palette: 8BIT
# ------------------------------------------------------------------------
# Anzeige:
# - Oben: Header (Uhr + WLAN)
# - Unten: Footer (nur Balken)
# - Mitte:
# * Startseite: Button 1 + Schalter + Temperaturen
# * Setup: Helligkeit (hoch / Prozent / runter)
# ------------------------------------------------------------------------
lambda: |-
const int W = it.get_width();
const int H = it.get_height();
it.fill(Color(0,0,0));
const int bar_h = 24;
const int header_y0 = 2;
const int footer_y0 = H - bar_h - 2;
Color bar_bg = Color(40,40,40);
Color divider_col = Color(70,70,70);
Color text_main = Color(235,235,235);
Color text_dim = Color(160,160,160);
Color ok_green = Color(0,255,0);
// Header + Linie
it.filled_rectangle(0, header_y0, W, bar_h, bar_bg);
it.line(0, header_y0 + bar_h + 2, W, header_y0 + bar_h + 2, divider_col);
// Footer + Linie
it.filled_rectangle(0, footer_y0, W, bar_h, bar_bg);
it.line(0, footer_y0 - 2, W, footer_y0 - 2, divider_col);
// Header Inhalt: Uhr / Setup Text
if (id(show_setup_page)) {
it.printf(W/2, header_y0 + 8, id(font_small), text_main,
TextAlign::CENTER, "Setup");
} else {
it.strftime(8, header_y0 - 2, id(font_small), text_main,
"%H:%M", id(esptime).now());
it.printf(W/2, header_y0 + 8, id(font_small), text_main,
TextAlign::CENTER, "Startseite");
}
// WLAN Balken rechts
float rssi = id(wifi_rssi).state;
if (!isnan(rssi)) {
int base_x = W - 30;
int base_y = header_y0 + bar_h - 8;
Color col;
if (rssi > -60) col = ok_green;
else if (rssi > -75) col = Color(255,165,0);
else col = Color(255,0,0);
if (rssi > -85) it.filled_rectangle(base_x, base_y-4, 3, 4, col);
if (rssi > -75) it.filled_rectangle(base_x+5, base_y-8, 3, 8, col);
if (rssi > -65) it.filled_rectangle(base_x+10, base_y-12, 3, 12, col);
if (rssi > -55) it.filled_rectangle(base_x+15, base_y-16, 3, 16, col);
}
// ===================== SETUP SEITE =====================
if (id(show_setup_page)) {
const int left_x = 28;
const int top_y = header_y0 + bar_h + 22;
int pct = (int)(id(display_brightness) * 100.0f + 0.5f);
if (pct < 0) pct = 0;
if (pct > 100) pct = 100;
it.printf(left_x, top_y, id(font_icons), ok_green,
TextAlign::LEFT, "\U000F0143");
it.printf(left_x + 2, top_y + 32, id(font_small), Color(255,255,255),
TextAlign::LEFT, "%d%%", pct);
it.printf(left_x, top_y + 50, id(font_icons), ok_green,
TextAlign::LEFT, "\U000F0140");
return;
}
// ===================== STARTSEITE =====================
const int content_top = header_y0 + bar_h + 10;
const int btn_x = 16;
const int btn_w = 150;
const int btn_h = 48;
const int btn1_y = content_top + 26;
// Schalter-Reihe
const int sw_y = btn1_y + btn_h + 12;
const int sw_w = 150;
const int sw_h = 48;
// Farben
Color border = Color(110,110,110);
// Button 1: kurz blau beim Tippen
Color btn1_bg = id(btn1_pressed) ? Color(0,120,255) : Color(60,60,60);
it.filled_rectangle(btn_x, btn1_y, btn_w, btn_h, btn1_bg);
it.rectangle(btn_x, btn1_y, btn_w, btn_h, border);
it.printf(btn_x + btn_w/2, btn1_y + 23, id(font_small), Color(255,255,255),
TextAlign::CENTER, "%s", "${btn1_label}");
// Schalterzustand aus HA
bool sw_known = id(sw_state).has_state();
bool sw_on = sw_known ? id(sw_state).state : false;
// Schalterfarbe:
// - blau wenn gerade gedrückt
// - grün wenn ON
// - grau wenn OFF/unknown
Color sw_bg;
if (id(sw_pressed)) sw_bg = Color(0,120,255);
else sw_bg = sw_on ? Color(0,160,80) : Color(60,60,60);
it.filled_rectangle(btn_x, sw_y, sw_w, sw_h, sw_bg);
it.rectangle(btn_x, sw_y, sw_w, sw_h, border);
// Schalter-Text links
it.printf(btn_x + 10, sw_y + 10, id(font_small), Color(255,255,255),
TextAlign::LEFT, "%s", "${sw_label}");
// Zustand rechts
const char* st = sw_known ? (sw_on ? "ON" : "OFF") : "--";
it.printf(btn_x + sw_w - 10, sw_y + 10, id(font_small), Color(255,255,255),
TextAlign::RIGHT, "%s", st);
// Temperaturen rechts daneben
const int t_x = btn_x + btn_w + 14;
const int t_y1 = btn1_y + 8;
const int t_y2 = sw_y + 8;
float t1 = id(temp_1).state;
float t2 = id(temp_2).state;
if (!isnan(t1)) {
it.printf(t_x, t_y1, id(font_small), text_main, TextAlign::LEFT,
"%s: %.1f°C", "${temp_label_1}", t1);
} else {
it.printf(t_x, t_y1, id(font_small), text_dim, TextAlign::LEFT,
"%s: --.-°C", "${temp_label_1}");
}
if (!isnan(t2)) {
it.printf(t_x, t_y2, id(font_small), text_main, TextAlign::LEFT,
"%s: %.1f°C", "${temp_label_2}", t2);
} else {
it.printf(t_x, t_y2, id(font_small), text_dim, TextAlign::LEFT,
"%s: --.-°C", "${temp_label_2}");
}
// WLAN dBm unten
if (!isnan(rssi)) {
it.printf(16, sw_y + sw_h + 16,
id(font_small), text_dim,
TextAlign::LEFT,
"WLAN: %.0f dBm", rssi);
}
Danke auch hierfür. Allerdings ist die Ausrichtung immer noch Portrait und deshalb die Darstellung etwas “durcheinander”.
Flash mal damit → ESPConnect und "erase entire bevor flashing " ankreuzen!
Probier bitte auch mal:
display:
- platform: ili9xxx
spi_id: tft
model: ili9341
cs_pin: GPIO15
dc_pin: GPIO2
invert_colors: false
rotation: 180 # Landscape
dimensions:
width: 320
height: 240
update_interval: 500ms
auto_clear_enabled: true
color_palette: 8BIT
die Chinesen ballern da machmal verschiedene Displayvarianten bunt gemischt in der selben Charge raus! ![]()
Danke - aber das muss etwas warten. Ich werde berichten!
Du musst in Home Assistant unter ESPHome dem Gerät erlauben Änderungen im Home Assistant vornehmen zu dürfen!
Danke - auch das hat jetzt geklappt. (Ich darf wohl noch einiges lernen!) Ich werde mich jetzt mal an diesem Beispiel längs hangeln. Wie mach ich das Ganze im Portrait? (Dann lasse ich Dich auch erst mal in Ruhe!)
Hi, einfach die rotation von 180 auf 0 setzen, dann hast du Portrait-Ansicht.
Und den „Touch“ natürlich entsprechend anpassen! ![]()
90 war hier bei mir der richtige Wert. An dieser Stelle nochmal herzlichen Dank. Jetzt habe ich eine funktionierende Basis.
Logo, bei 0 zu 180 steht das ja nur auf dem „Kopf“! Wollten eh bloß testen ob du mit denkst! ![]()
Tja - Prüfung wohl bestanden!,
Einen feinen Tag!
