Display + touch implementation -> how to change displayed image on touch event

Hello,

This is my first post here, I recently discovered esphome and I love it. So much easy than the basic c++ programming. This is also my first project so please bare with me if the programming style is a bit novice. I am still learning the entire component, template, lambda etc

I have built esp32+ ILI9341 (with XPT2046 chip). It is an touch switch + display for my HVAC. For the beginning I designed the interface and implemented the functionality. It has 3 touch buttons (on the touch screen) that in the end will command 3 GPIO’s and also send the status to HomeAssistant.

I have used the lambda on the touch component to find out the area of the screen that is being touched and depending on that I am triggering toggle on some virtual (for now, later on will be connected to gpios) switches. Everything works fine so far.

Here are my questions:

  1. How do I change the image on the screen when a button is touched ( i want to change it from switch_on to switch_off). If i tried to use the “it.image…” on the touch component it says it is out of scope. So how do I “draw” on the display when a touch event happens?

  2. Instead of the lamba on the touch component I tried defining binary_sensors for 3 area of the screen. I can see them in HA but when i click one the other one gets unchecked (sort of like a radio button on html). Is there a way to convert them to “checkbox” style? … when one get clicked it will toggle it’s own status and not affect the other 2 options?

Here is my code for reference

esphome:
  name: testdevice

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "xxx"

ota:
  password: "xxx"

wifi:
  ssid: "xxx"
  password: "xxx"
  
  # Optional manual IP
  manual_ip:
    static_ip: 192.168.4.36
    gateway: 192.168.4.1
    subnet: 255.255.255.0

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Touchscreen-1"
    password: "yvQHjll2ZtK6"
captive_portal:

# Example configuration entry
globals:
  - id: x
    type: int
    restore_value: no
    initial_value: '0'
  - id: y
    type: int
    restore_value: no
    initial_value: '0'
  - id: touched
    type: bool   
    
color:
  - id: my_red
    red: 100%
    green: 3%
    blue: 5%
  - id: my_blue
    red: 3%
    green: 3%
    blue: 100%
  - id: my_green
    red: 3%
    green: 100%
    blue: 5%
    
image:
  - file: "images/airflow-on.png"
    id: airflow_on
    type: RGB24
    resize: 60x60
    
  - file: "images/airflow-off.png"
    id: airflow_off
    type: RGB24  
    resize: 60x60
    
  - file: "images/humidifier.png"
    id: humidifier
    type: RGB24  
    resize: 60x60    
    
  - file: "images/off-button.png"
    id: button_off
    type: RGB24  
    resize: 60x60
    
  - file: "images/on-button.png"
    id: button_on
    type: RGB24      
    resize: 60x60
    
font:
  - file: "fonts/OpenSans-Regular.ttf"
    id: font_title
    size: 30
    
  - file: "fonts/OpenSans-Regular.ttf"
    id: font_normal
    size: 15
    
output:
  # backlight
  - platform: ledc
    pin: 2
    id: gpio_15_backlight_pwm
    
# Define a monochromatic, dimmable light for the backlight
light:
  # backlight for HA
  - platform: monochromatic
    output: gpio_15_backlight_pwm
    name: "ILI9341 Display Backlight"
    id: back_light
    restore_mode: ALWAYS_ON
    
spi:
  clk_pin: GPIO18
  mosi_pin: GPIO23
  miso_pin: GPIO19
  
# Example minimal configuration entry
display:
  - platform: ili9341
    model: TFT 2.4
    id: touch_display
    cs_pin: 5
    dc_pin: 4
    reset_pin: 22
    #auto_clear_enabled: true
    update_interval: 5s
    rotation: 0
    lambda: |-
      it.print(35, 0, id(font_title), "HVAC Control");
      
      it.line(  0,  40, 239,  40);
      it.print(5, 40, id(font_normal), "Upstairs Air-Flow");
      it.image( 45, 65, id(airflow_on) );
      it.image( 145, 55, id(button_on) );
      
      it.line(  0,  135, 239,  135);
      it.print(5, 135, id(font_normal), "Basement Air-Flow");
      it.image( 45, 165, id(airflow_on) );
      it.image( 145, 150, id(button_on) );
      
      it.line(  0, 230, 239, 230);
      it.print(5, 232, id(font_normal), "Humidifier");
      it.image( 45, 260, id(humidifier) );
      it.image( 145, 245, id(button_on) );
      
      
touchscreen:
  - platform: xpt2046
    id: touchscreen2
    cs_pin: 15
    interrupt_pin: 21
    update_interval: 50ms
    report_interval: 1s
    on_touch:
     - lambda: |-
        if( id(touchscreen2).x > 130 && id(touchscreen2).x < 200 && id(touchscreen2).y > 170 && id(touchscreen2).y < 250)
        {
            id(upstairs_airflow_switch).toggle();
        };
        
        if( id(touchscreen2).x > 130 && id(touchscreen2).x < 200 && id(touchscreen2).y > 114 && id(touchscreen2).y < 160)
        {
            id(basement_airflow_switch).toggle();
        };
        
        if( id(touchscreen2).x > 130 && id(touchscreen2).x < 200 && id(touchscreen2).y > 0 && id(touchscreen2).y < 100)
        {
            id(humidifier_switch).toggle();
            it.image( 145, 245, id(button_off) );
        };
        
        ESP_LOGI("cal", "x=%d, y=%d, x_raw=%d, y_raw=%d",
            id(touchscreen2).x,
            id(touchscreen2).y,
            id(touchscreen2).x_raw,
            id(touchscreen2).y_raw
            );
            
switch:
  - platform: template
    id: upstairs_airflow_switch
    name: "Upstairs Air-Flow"
    optimistic: true
    
  - platform: template
    id: basement_airflow_switch
    name: "Basement Air-Flow"
    optimistic: true
    
  - platform: template
    id: humidifier_switch
    name: "Humidifier"
    optimistic: true

After this line

id(upstairs_airflow_switch).toggle();

i would like to update the image something like

it.image( 45, 65, id(airflow_off) );

P.S. My code is not finalized yet, i realize that I should check for the status of the switch before selection the “on” “off” button … will be doing this later on.

Thank you,

Hi - first question - does the lambda to do screen updates actually work outside a display: block?

I would be interested if it did, I was under the impression that all you can do outside the display component is switch pages.

that is exactly the issue that i am facing, it does not. then what is the solution?

I think I found the answer myself today, totally different approach. In fact you don’t need to “write” to the display from a different component. On the display you set a refresh rate fast enough and inside the lambda of the display you print graphic element based on the status of the switch. This way you never “draw” from a different component the display always adjusts itself based on the status of changed done between the display’s refreshes. I will test it tonight when i get at home.

Sounds like a plan. Otherwise - to save refresh cycles and assuming you have a limited number of image changes, use pages.

Because you are changing the image name via code, the image names and in fact the whole page definition will be identical. Just wait for touch or some other event, set the image details, then switch to the other page.

Did you figure this out? Would you mind posting code