Touch Gamepad is an ESP-IDF 6.x firmware for ESP32-S3 touch displays that turns touchscreen gestures into gamepad input for a PC or console host. It renders a gesture surface with LVGL, reads a capacitive touch panel, and reports the resulting buttons and joystick axes over BLE HID or (on capable boards) USB HID.
The firmware presents itself to the host as a Microsoft Xbox Wireless Controller (VID 0x045E, PID 0x0B13), so it is recognised and mapped like a standard Xbox gamepad. The input report carries the genuine Xbox fields plus a trailing Rx/Ry axis pair, which hosts that read the HID report map can use as the right stick (see the mapping sub-menu below); the leading bytes stay byte identical to the genuine controller report. The BLE transport advertises as "Xbox Wireless Controller XXXX", where XXXX is the last two octets of the Bluetooth MAC address as a 4-digit hex number, so several gamepads in radio range can be told apart.
| Board | Display | Touch | Transports |
|---|---|---|---|
| Guition ESP32-S3-4848S040 | 480x480 ST7701 RGB | GT911 | BLE only |
| Waveshare ESP32-S3-Touch-LCD-3.5B (untested) | 320x480 AXS15231B QSPI (portrait) | AXS15231B | BLE or USB |
Warning: the Waveshare ESP32-S3-Touch-LCD-3.5B support is untested on real hardware. Its pin map, panel and touch bring-up (AXS15231B display and touch, AXP2101 PMIC, TCA9554 I/O expander) and transports come from the vendor board support package and have not been confirmed on a device. Treat this board as experimental. The Guition ESP32-S3-4848S040 is the supported, working target.
The Guition ESP32-S3-4848S040 does not expose a USB device port, so it acts only as a BLE gamepad. The Waveshare ESP32-S3-Touch-LCD-3.5B is intended to act as either a BLE or a USB gamepad, with the active transport chosen from the configuration menu and persisted across reboots, but see the warning above about its untested state.
Reference material for the two boards:
- Guition ESP32-S3-4848S040
- Waveshare ESP32-S3-Touch-LCD-3.5B
The screen is split into four tap zones and a joystick area. Their positions depend on the selected screen layout (see the configuration menu):
-
4 buttons on top(default): the four tap zones fill the top third in a 2x2 grid and the joystick fills the bottom two thirds. -
4 buttons on the sides: two tap zones stack along the left border and two along the right border, and the joystick occupies the whole-height middle column, which takes 70% of the screen width (the two side columns share the remaining 30%). On the Waveshare 3.5B this layout is drawn in landscape (the panel's long edge becomes horizontal); the display and touch are rotated 90 degrees to match. The square Guition panel keeps its native orientation. The4 buttons on toplayout is always drawn in portrait. -
Tap zones
- Touching a zone presses the gamepad button bound to that zone and lifting the finger releases it, so the button is held for exactly as long as the finger rests on the zone. The zone lights up while it is held.
- Each zone shows its current assignments: the one-finger button on the left and the two-finger button on the right.
-
Joystick area
- Acts as a one-finger analog joystick with a fixed central point. The joystick tracks a single finger (any additional fingers on the joystick area are ignored). Sliding the finger away from the center drives a joystick axis pair whose values are proportional to the distance from the center, and the axes re-center when the finger lifts.
- The screen draws the central point and a vector pointing from it to the current touch point.
-
Configuration menu unlock gesture
- Tap the corners in the order lower-left, upper-left, upper-right, lower-right. Repeating the sequence closes the menu (discarding unsaved edits, like Cancel).
The menu is a full-screen, scrollable list driven entirely by one-finger taps. Each row is a large touch target. Tap a row to select it:
- On a multiple-choice row, tap the left half of the row to rotate to the
previous value or the right half to rotate to the next value. The current
value is shown between
<and>arrows as a reminder. - On an action row (Buttons and axes, BLE pairing reset, Save, Cancel), a tap runs the action.
If the list is taller than the screen, a one-finger slide up or down scrolls it so every row (including Save and Cancel at the bottom) can be reached. Edits are made on a working copy and only written to NVS when you tap Save; Cancel (or the unlock sequence) leaves the menu without saving.
Main screen rows:
- Transport (BLE/USB)
- The Waveshare 3.5B rotates between BLE and USB; the transport is restarted when the edits are saved.
- Guition stays locked to BLE because the board has no USB device port.
- Color theme
- Rotates between blue-on-black and green-on-black. The change is previewed live and persisted on Save.
- Screen layout
- Rotates between
top(4 buttons on top) andsides(4 buttons on the sides). The tap zones and joystick are repositioned when the edits are saved.
- Rotates between
- Screen updates
- Rotates between
onandoff. When set tooff, the live gameplay screen (tap-zone highlights, joystick vector and status line) stops being redrawn so the picture stays static; gameplay and HID output are unaffected. The configuration menu still renders so the setting can be turned back on.
- Rotates between
- Joystick sensitivity
- Rotates through levels
1to5. Tapping the right half of the row raises the level and the left half lowers it (clamped, no wrap). Higher values make the analog joystick react more strongly to the same finger movement; level3is the neutral default.
- Rotates through levels
- Buttons and axes
- Opens the mapping sub-menu (see below).
- BLE pairing reset
- Removes the current BLE bond and restarts advertising so a new host can pair. Only meaningful in BLE mode; the action runs immediately.
- Save / Cancel
- Save writes every edit to NVS and closes the menu. Cancel discards the edits and closes the menu.
Mapping sub-menu rows:
- Eight
Zone N M-fingerchoice rows, each rotating through the gamepad button bound to that tap (one- and two-finger taps edit separate bindings). - A
Joystick axeschoice row that rotates the joystick's axis pair throughX/Y,Z/Rz,Rx/Ry,X/ZandY/Rz.X/Yis the Xbox left stick andZ/Rzthe Xbox-native right stick. Hosts that map the pad from the HID report map instead of the fixed Xbox report layout usually expect the right stick onRx/Ry; pick that pair whenZ/Rzis not recognised as the right stick. - Save returns to the main menu keeping the mapping edits; Cancel returns to the main menu discarding the mapping edits made in the sub-menu.
main/main.cruns the boot sequence and the touch event loop: it polls the touch controller, drives the buttons and the joystick from the live touch state every poll (press, hold and release in real time), and still tracks corner taps to detect the menu unlock sequence and to drive the menu.main/touch_gamepad.c/main/touch_gamepad.hhold the board preset table, the gesture-detection state machine, the menu state machine, and the NVS-backed configuration.main/boards.hcentralizes the per-board display and touch pin assignments.main/display.cbrings up the panel and the LVGL port: an ST7701 RGB panel on the Guition, or an AXS15231B QSPI panel (portrait) on the Waveshare 3.5B.main/touchpad.cwraps the touch controller (GT911 on the Guition, AXS15231B on the Waveshare 3.5B) and returns up to two touch points.main/ui.cbuilds the LVGL screen: the tap zones that light up while pressed and show their button assignments, the joystick surface with a central point and a live vector to the touch point, a status line, and the menu / mapping overlays.main/gamepad_hid.hdefines the shared Xbox Wireless Controller HID report descriptor and input report struct used by both transports.main/gamepad_backend.ccaches the logical gamepad state and translates it into the Xbox input report for the active transport.main/ble_gamepad.cimplements the BLE HID gamepad (Bluedroid + esp_hid).main/usb_gamepad.cimplements the USB HID gamepad (TinyUSB); it compiles to a stub on boards without USB.
Use ESP-IDF 6.x. The managed component dependencies (LVGL, esp_lvgl_port, the
ST7701, GT911 and AXS15231B drivers, and esp_tinyusb) are listed in
main/idf_component.yml and fetched automatically.
idf.py -D SDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.defaults.guition" set-target esp32s3 buildidf.py -D SDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.defaults.waveshare35b" set-target esp32s3 buildCMake presets are also provided (guition and waveshare35b) that layer the
shared sdkconfig.defaults with the matching board overlay.
All display and touch pins live in main/boards.h. The Guition values come from
the community and ESPHome reference designs listed above. The Waveshare ESP32-S3-
Touch-LCD-3.5B pins, timing and AXS15231B init sequence come from the official
Waveshare board support package. That board carries an AXP2101 PMIC and a TCA9554
I/O expander on the touch I2C bus (SDA/SCL shared with the AXS15231B touch
controller); waveshare_board_bringup() powers the display rails through the
AXP2101 and releases the panel reset through the TCA9554 before the display and
touch controllers are initialized. The panel is driven over QSPI and used in its
native 320x480 portrait orientation.
Runtime configuration is stored in NVS under the touchgp namespace and survives
a reboot:
- selected transport mode
- selected color theme
- selected screen layout
- screen updates enabled or disabled
- eight tap-to-button bindings
- joystick slide axis pair
Every source and configuration file in this repository uses ASCII characters only. This applies to C, header, CMake, Kconfig, YAML, JSON, and Markdown files.
This firmware targets physical ESP32-S3 hardware and the ESP-IDF 6.x build system with the managed components above; it is intended to be built and flashed with the commands in the Building section. Because the panel and touch pin maps (especially for the Waveshare board) come from vendor references, confirm them on the target hardware before relying on a build.
The Guition ESP32-S3-4848S040 preset is the validated target. The Waveshare ESP32-S3-Touch-LCD-3.5B preset is untested on hardware: its panel and touch bring-up, PMIC and I/O expander sequence, touch orientation and transports come from vendor references and have not been confirmed on a device, so they should be considered experimental.