Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/esphome/esphome.io/llms.txt

Use this file to discover all available pages before exploring further.

ESPHome’s touchscreen component bridges capacitive and resistive touch controllers to the rest of the ESPHome ecosystem — including display pages, LVGL widgets, and binary sensor virtual buttons. It handles coordinate translation, calibration, and multi-touch tracking, so your display project can respond to finger presses, drags, and releases with only a few lines of YAML.

Minimal Example

touchscreen:
  - platform: gt911
    display: my_display
    interrupt_pin: GPIO38
    reset_pin: GPIO48
    on_touch:
      then:
        - logger.log:
            format: "Touch at x=%d, y=%d"
            args: [touch.x, touch.y]

Base Touchscreen Configuration

All touchscreen platforms share these configuration variables.
touchscreen:
  - platform: ...
    display: display1
    transform:
      mirror_x: false
      mirror_y: false
      swap_xy: false
    update_interval: 50ms
    touch_timeout: 300ms
    calibration:
      x_min: 281
      x_max: 3848
      y_min: 347
      y_max: 3878
    on_touch:
      then: ...
    on_update:
      then: ...
    on_release:
      then: ...
display
ID
required
The ID of the display component this touchscreen is attached to. The touchscreen uses the display’s dimensions to scale coordinates.
transform
object
Hardware coordinate transformation applied before scaling. All defaults are false.
  • swap_xy — swap the X and Y axes.
  • mirror_x — flip the X axis.
  • mirror_y — flip the Y axis.
update_interval
Time
Polling interval when no interrupt pin is used. Defaults to 50ms.
touch_timeout
Time
How long to wait before considering a touch ended on controllers that don’t report release events. Default varies by platform.
calibration
object
Manual calibration for resistive touchscreens whose raw values do not map directly to screen pixels (e.g. XPT2046). See Calibration below.
  • x_min / x_max — raw values at left/right edge.
  • y_min / y_max — raw values at top/bottom edge.

TouchPoint Argument Type

Both on_touch and on_update receive touch point objects with these fields:
FieldTypeDescription
idintTouch identifier for multi-touch tracking
stateint1 = initial touch, 2 = moved, 4+ = released
x, yintCurrent position in display pixels
x_prev, y_previntPrevious position
x_org, y_orgintPosition when touch was first detected
x_raw, y_rawintRaw values (useful for calibration)

Automation Triggers

on_touch

Fires once when an initial touch is detected. Not fired again until all touches are released. Provides touch (single TouchPoint) and touches (list of all active points).
on_touch:
  then:
    - logger.log:
        format: "Touch at x=%d y=%d"
        args: [touch.x, touch.y]

on_update

Fires whenever a touch position changes or a new finger lands — useful for gesture detection. Provides touches — a list of TouchPoint objects.
on_update:
  then:
    - lambda: |-
        for (auto t : touches) {
          if (t.state <= 2) {
            ESP_LOGI("touch", "id=%d x=%d y=%d", t.id, t.x, t.y);
          }
        }
Always check touch.state in on_update — state 0 means the touch is no longer valid.

on_release

Fires when all touches are released from the screen. No touch arguments are provided.
on_release:
  then:
    - logger.log: "Released"

Binary Sensor Virtual Buttons

Define rectangular areas on the touchscreen as binary_sensor entities. When a touch lands inside the area, the sensor reports ON.
binary_sensor:
  - platform: touchscreen
    name: "Top Left Button"
    x_min: 0
    x_max: 100
    y_min: 0
    y_max: 100
    page_id: home_page
x_min
int
required
Left edge of the touch zone (pixels).
x_max
int
required
Right edge of the touch zone (pixels).
y_min
int
required
Top edge of the touch zone (pixels).
y_max
int
required
Bottom edge of the touch zone (pixels).
page_id
ID
Only activate this sensor when the specified display page is showing. Use pages (list) if you want multiple pages.
use_raw
boolean
If true, accepts raw coordinates outside the display area. Useful for resistive touchscreens that extend beyond the visible screen.

Calibration

Resistive touchscreens (e.g. XPT2046) return raw ADC values that bear no relationship to pixel coordinates. You must calibrate them by touching the corners and recording the raw values. Step 1: Log raw values from each corner using on_touch:
touchscreen:
  platform: xpt2046
  cs_pin: GPIO15
  on_touch:
    - lambda: |-
        ESP_LOGI("cal", "x=%d y=%d x_raw=%d y_raw=%d",
          touch.x, touch.y, touch.x_raw, touch.y_raw);
Step 2: Touch each corner several times and note the minimum/maximum raw X and Y values from the logs. Step 3: Apply calibration:
touchscreen:
  platform: xpt2046
  cs_pin: GPIO15
  calibration:
    x_min: 281
    x_max: 3848
    y_min: 347
    y_max: 3878
  transform:
    mirror_x: false
    mirror_y: false
    swap_xy: false
Step 4: Verify by drawing a circle at the touch point:
display:
  - platform: ili9341
    lambda: |-
      auto t = id(my_touch)->get_touch();
      if (t) it.filled_circle(t.value().x, t.value().y, 10, RED);
If touching the left edge produces large X raw values, enable mirror_x: true. If the X axis maps to the display’s Y axis, enable swap_xy: true.

GT911

Capacitive multi-touch controller. Common in ESP32-S3 display boards. Supports up to 5 simultaneous touch points.

CST816S / CST226

Low-power capacitive controller popular in round displays. Supports swipe gesture recognition.

XPT2046

Resistive 4-wire touchscreen controller. Requires calibration but works with older, cheaper panels.

FT5x06 / FT63x6

Capacitive multi-touch I²C controller used in many 3.5” and 4” TFT displays.

TT21100

Capacitive I²C touchscreen controller used in several Espressif development boards.

EKTF2232

Touchscreen controller used in the Inkplate 6 Plus and similar e-ink display devices.

Advanced Example: Swipe Gesture Detection

touchscreen:
  - platform: gt911
    display: my_display
    id: my_touch
    on_touch:
      then:
        - globals.set:
            id: touch_start_x
            value: !lambda "return touch.x;"
    on_release:
      then:
        - lambda: |-
            int delta = id(touch_start_x) - id(my_touch)->get_touch().value_or(TouchPoint{}).x_org;
            if (delta > 50) {
              id(my_display).show_next_page();
            } else if (delta < -50) {
              id(my_display).show_previous_page();
            }

Build docs developers (and LLMs) love