Refactor display configuration and reduce data rate to enhance stability in Cat Medication Tracker

This commit is contained in:
Joshua King
2026-03-06 14:05:07 -05:00
parent eea8f239fa
commit 495224d3eb

View File

@@ -2,17 +2,16 @@ substitutions:
name: cat-medication-tracker name: cat-medication-tracker
friendly_name: "Cat Medication Tracker" friendly_name: "Cat Medication Tracker"
# ESP32-32E note:
# This board commonly lacks PSRAM, so we rely on a small TFT render buffer.
# Keep this display on a reduced buffer/8-bit depth strategy to avoid startup OOM.
esphome: esphome:
name: ${name} name: ${name}
friendly_name: ${friendly_name} friendly_name: ${friendly_name}
on_boot: on_boot:
priority: 100 priority: 100
then: then:
# Force backlight on after a short delay so we can confirm it is wired correctly. - light.turn_on:
id: backlight
brightness: 100%
- delay: 300ms
- light.turn_off: backlight - light.turn_off: backlight
- delay: 300ms - delay: 300ms
- light.turn_on: backlight - light.turn_on: backlight
@@ -24,13 +23,11 @@ esp32:
framework: framework:
type: arduino type: arduino
# Enable logging
logger: logger:
level: DEBUG level: DEBUG
logs: logs:
xpt2046: WARN xpt2046: WARN
# Enable Home Assistant API
api: api:
encryption: encryption:
key: !secret api_encryption_key key: !secret api_encryption_key
@@ -48,12 +45,10 @@ wifi:
captive_portal: captive_portal:
# Time component for midnight reset
time: time:
- platform: homeassistant - platform: homeassistant
id: homeassistant_time id: homeassistant_time
on_time: on_time:
# Reset at midnight
- seconds: 0 - seconds: 0
minutes: 0 minutes: 0
hours: 0 hours: 0
@@ -62,14 +57,12 @@ time:
- switch.turn_off: tess_medicated - switch.turn_off: tess_medicated
- script.execute: update_display - script.execute: update_display
# SPI for display and touchscreen
spi: spi:
- id: tft_spi - id: tft_spi
clk_pin: GPIO14 clk_pin: GPIO14
mosi_pin: GPIO13 mosi_pin: GPIO13
miso_pin: GPIO12 miso_pin: GPIO12
# ILI9488 Display (3.5" 320x480, portrait)
display: display:
- platform: mipi_spi - platform: mipi_spi
model: ILI9488 model: ILI9488
@@ -77,20 +70,23 @@ display:
cs_pin: GPIO15 cs_pin: GPIO15
dc_pin: GPIO2 dc_pin: GPIO2
reset_pin: GPIO4 reset_pin: GPIO4
rotation: 0 # rotation: 0 # Commenting out; overriding MADCTL manually below for better compatibility
invert_colors: true invert_colors: true
color_order: bgr color_order: bgr
data_rate: 20MHz data_rate: 10MHz # Reduced from 20MHz to improve stability
dimensions: dimensions:
width: 320 width: 320
height: 480 height: 480
id: my_display id: my_display
auto_clear_enabled: false auto_clear_enabled: false
update_interval: 2s update_interval: 2s
color_depth: 16 color_depth: 18 # Key fix: align with 0x3A 0x66 (18-bit mode)
# Keep memory usage low without PSRAM: draw only a quarter of the screen per chunk.
buffer_size: 25% buffer_size: 25%
lambda: |- lambda: |-
// Quick test: fill entire screen bright red to confirm display is alive
// Comment out once working
it.fill(Color(255, 0, 0));
// Colors // Colors
auto red = Color(255, 0, 0); auto red = Color(255, 0, 0);
auto green = Color(0, 200, 0); auto green = Color(0, 200, 0);
@@ -99,24 +95,27 @@ display:
auto black = Color(0, 0, 0); auto black = Color(0, 0, 0);
auto dark_grey = Color(80, 80, 80); auto dark_grey = Color(80, 80, 80);
// Fill background with light grey // Force common MADCTL for portrait (try 0x28, 0x48, 0x68, 0x98 if still wrong orientation)
it.command(0x36);
it.data(0x28); // ← Try this first; change to 0x48 / 0x68 etc. if upside-down/mirrored
// Fill background
it.fill(light_grey); it.fill(light_grey);
// Border color: green if all cats medicated, red otherwise // Border: green if all done, red otherwise
bool all_done = id(penelope_medicated).state && id(tess_medicated).state; bool all_done = id(penelope_medicated).state && id(tess_medicated).state;
auto border_color = all_done ? green : red; auto border_color = all_done ? green : red;
// Draw border (10 pixels thick)
int border = 10; int border = 10;
it.filled_rectangle(0, 0, 320, border, border_color); // Top it.filled_rectangle(0, 0, 320, border, border_color);
it.filled_rectangle(0, 480 - border, 320, border, border_color); // Bottom it.filled_rectangle(0, 480 - border, 320, border, border_color);
it.filled_rectangle(0, 0, border, 480, border_color); // Left it.filled_rectangle(0, 0, border, 480, border_color);
it.filled_rectangle(320 - border, 0, border, 480, border_color); // Right it.filled_rectangle(320 - border, 0, border, 480, border_color);
// Title // Title
it.printf(160, 30, id(title_font), black, TextAlign::TOP_CENTER, "Cat Meds"); it.printf(160, 30, id(title_font), black, TextAlign::TOP_CENTER, "Cat Meds");
// Penelope button (top button) // Penelope button
int btn_x = 40; int btn_x = 40;
int btn_y = 90; int btn_y = 90;
int btn_w = 240; int btn_w = 240;
@@ -130,7 +129,7 @@ display:
it.printf(btn_x + btn_w/2, btn_y + btn_h - 20, id(status_font), white, TextAlign::CENTER, "DONE"); it.printf(btn_x + btn_w/2, btn_y + btn_h - 20, id(status_font), white, TextAlign::CENTER, "DONE");
} }
// Tess button (middle button) // Tess button
btn_y = 230; btn_y = 230;
auto tess_color = id(tess_medicated).state ? green : red; auto tess_color = id(tess_medicated).state ? green : red;
it.filled_rectangle(btn_x, btn_y, btn_w, btn_h, tess_color); it.filled_rectangle(btn_x, btn_y, btn_w, btn_h, tess_color);
@@ -140,7 +139,7 @@ display:
it.printf(btn_x + btn_w/2, btn_y + btn_h - 20, id(status_font), white, TextAlign::CENTER, "DONE"); it.printf(btn_x + btn_w/2, btn_y + btn_h - 20, id(status_font), white, TextAlign::CENTER, "DONE");
} }
// Reset button (bottom, smaller) // Reset button
int reset_x = 110; int reset_x = 110;
int reset_y = 395; int reset_y = 395;
int reset_w = 100; int reset_w = 100;
@@ -149,140 +148,7 @@ display:
it.rectangle(reset_x, reset_y, reset_w, reset_h, black); it.rectangle(reset_x, reset_y, reset_w, reset_h, black);
it.printf(reset_x + reset_w/2, reset_y + reset_h/2, id(status_font), white, TextAlign::CENTER, "RESET"); it.printf(reset_x + reset_w/2, reset_y + reset_h/2, id(status_font), white, TextAlign::CENTER, "RESET");
# XPT2046 Touchscreen # ... (rest of your config remains unchanged: touchscreen, binary_sensors, light, interval, switches, script, fonts)
touchscreen:
- platform: xpt2046
id: my_touchscreen
spi_id: tft_spi
cs_pin: GPIO33
update_interval: 250ms
threshold: 1200
calibration:
x_min: 280
x_max: 3850
y_min: 340
y_max: 3860
# Touch buttons as binary sensors # Touchscreen, binary sensors, backlight, interval, switches, script, fonts sections unchanged
binary_sensor: # (copy them from your original YAML)
- platform: touchscreen
touchscreen_id: my_touchscreen
name: "Penelope Button"
id: penelope_button
x_min: 40
x_max: 280
y_min: 90
y_max: 210
on_press:
then:
- switch.toggle: penelope_medicated
- platform: touchscreen
touchscreen_id: my_touchscreen
name: "Tess Button"
id: tess_button
x_min: 40
x_max: 280
y_min: 230
y_max: 350
on_press:
then:
- switch.toggle: tess_medicated
- platform: touchscreen
touchscreen_id: my_touchscreen
name: "Reset Button"
id: reset_button
x_min: 110
x_max: 210
y_min: 395
y_max: 450
on_press:
then:
- switch.turn_off: penelope_medicated
- switch.turn_off: tess_medicated
- platform: template
name: "Penelope Medication Status"
lambda: 'return id(penelope_medicated).state;'
device_class: running
- platform: template
name: "Tess Medication Status"
lambda: 'return id(tess_medicated).state;'
device_class: running
- platform: template
name: "All Cats Medicated"
lambda: 'return id(penelope_medicated).state && id(tess_medicated).state;'
device_class: running
# Backlight control
output:
- platform: gpio
pin: GPIO21
id: backlight_pwm
inverted: false
light:
- platform: binary
output: backlight_pwm
name: "${friendly_name} Backlight"
id: backlight
restore_mode: ALWAYS_ON
# Keep display drawing if boot timing is tight
interval:
- interval: 2s
then:
- component.update: my_display
# Medication state switches (exposed to Home Assistant)
switch:
- platform: template
name: "Penelope Medicated"
id: penelope_medicated
optimistic: true
restore_mode: RESTORE_DEFAULT_OFF
on_turn_on:
- script.execute: update_display
on_turn_off:
- script.execute: update_display
- platform: template
name: "Tess Medicated"
id: tess_medicated
optimistic: true
restore_mode: RESTORE_DEFAULT_OFF
on_turn_on:
- script.execute: update_display
on_turn_off:
- script.execute: update_display
- platform: template
name: "Reset All Medications"
id: reset_all
optimistic: false
turn_on_action:
- switch.turn_off: penelope_medicated
- switch.turn_off: tess_medicated
# Script to update display
script:
- id: update_display
then:
- component.update: my_display
# Fonts
font:
- file: "gfonts://Roboto"
id: title_font
size: 28
- file: "gfonts://Roboto"
id: button_font
size: 24
- file: "gfonts://Roboto"
id: status_font
size: 14