Skip to content

Add lcdwiki-es3c35p: ES3C35P “cheap black display” 3.5" ESP32-S3 (ST77922 QSPI + touch) - #475

Draft
EnygmaINC wants to merge 5 commits into
bmorcelli:mainfrom
EnygmaINC:lcdwiki-es3c35p-port
Draft

EnygmaINC wants to merge 5 commits into
bmorcelli:mainfrom
EnygmaINC:lcdwiki-es3c35p-port

Conversation

@EnygmaINC

Copy link
Copy Markdown

Summary

Adds support for the 3.5" ESP32-S3 320x480 touch module sold as the LCDwiki
ES3C35P (also rebadged, e.g. Hosyond "ESP32-S3 Touchscreen Module 3.5"" and
AliExpress "cheap black display" listings). Requested in #434. Depends on bmorcelli/DisplayDrivers#3 (the display driver).

This panel is not covered by any existing databus: the ST77922 is driven over
quad-SPI with no DC pin, and wants register writes on one line but pixel
data on four. Arduino_GFX's Arduino_ESP32QSPI sends every phase on four
lines, so it cannot drive the panel. The change adds:

  1. Display -- Arduino_ST77922_1L4 (TFT_DATABUS_N=6, TFT_DISPLAY_DRIVER_N=52),
    an own-bus raw ESP-IDF spi_master driver. Lives in the DisplayDrivers
    submodule (separate PR, linked below). USE_CANVAS=1 handles rotation in
    software, since this panel's MADCTL does not remap addressing.
  2. Touch -- new TOUCH_CTRL_ST77922 branch in src/hal/inputs/touch.cpp.
    The touch block is built into the same IC (I2C 0x55, 16-bit big-endian
    register addresses). It is not FT6x36-compatible, despite appearing as
    "FT6336G-compatible" in some third-party notes; the FT6X36 path reads the
    wrong registers on this chip.
  3. Board -- boards/lcdwiki-es3c35p/ (platformio.ini, interface.cpp,
    connections.md), registered in the CI matrix; default_envs entry added
    commented-out like every other board.

Nothing in src/ outside touch.cpp is changed, and the touch.cpp addition
is entirely behind TOUCH_CTRL_ST77922.

Hardware / links

Vendor page https://www.lcdwiki.com/3.5inch_ESP32-S3_Display
AliExpress listing (unit tested) https://www.aliexpress.us/item/3256812149380399.html (submitter confirmed this is the listing the unit came from)
Amazon rebadge Hosyond "ESP32-S3 Touchscreen Module 3.5"", ASIN B0H28X8SQ4 (taken from community docs; not purchased or verified by the submitter)
Vendor example code https://github.com/ydedox/st77922 (Example_01 = display, Example_08 = touch)
Related prior art 78/xiaozhi-esp32 boards/lcdwiki-es3c35p/; https://tinkerpad.co.uk/esp32-s3-35-lcd.html
Request #434

Pinout (all documented in boards/lcdwiki-es3c35p/connections.md):
display CS=10 SCLK=12 D0=11 D1=13 D2=14 D3=9 BL=41 (no RST pin);
touch SDA=38 SCL=39 RST=48 INT=47; SD native 4-bit SDIO CLK=5 CMD=4 D0=6 D1=7 D2=2 D3=3;
battery sense GPIO 8; WS2812 on 40; BOOT=0.

Pitfalls worth a reviewer's attention

  • TFT_DC=-1 / TFT_IPS=0 in the board ini are load-bearing.
    include/pre_compiler.h's "CYD defaults" block silently redefines
    TFT_CS/TFT_SCLK (to 15/14) whenever a board defines none of TFT_DC,
    TFT_IPS, TFT_COL_OFS* or TFT_MOSI. A QSPI board has no DC or MOSI pin,
    so without these two flags the SPI bus init fails ("not using iomux pins").
    This cost most of the bring-up time; it may be worth a comment or a different
    guard in pre_compiler.h as a follow-up (not changed here).
  • Touch must read the whole report block in one transfer (maxTouches * 8
    bytes). Reading only the first record leaves the controller returning a
    stale point forever.
  • GPIO48 (touch reset) idles low; the touch half does not answer on I2C until
    it is pulsed (done once in _setup_gpio()). The display does not need it.
  • powerOff(): touch cannot wake deep sleep here (INT is GPIO47, not an RTC
    pad), so BOOT/GPIO0 (ext0) is the only wake source. The board's powerOff()
    enables it, switches the backlight off and latches the pin low for the sleep,
    and _setup_gpio() releases the latch on boot.

Verification

Tested on one physical unit (AliExpress purchase above) running the image built
from this branch, flashed with esptool at 0x0 (merged image).

Build: pio run -e lcdwiki-es3c35p succeeds on this branch rebased onto
current main (the full test matrix below was run on the same code before
rebasing onto two unrelated upstream Heltec commits; after the rebase the board
was reflashed and re-checked for boot, touch and brightness). Firmware app
image is about 1.42 MB (8.5 % of the 16 MB flash); RAM use 26.4 %.

Area Result How
Display, orientation, colour order Pass on device
Backlight PWM / brightness setting Pass on device (menu setting changes brightness; re-checked on the final build)
Touch: menu navigation Pass on device
Touch: on-screen keyboard (Wi-Fi password entry) Pass on device
SD card (4-bit SDIO) Pass on device
Rotation 0 / 1 / 2 / 3, display and touch Pass Rotation menu, on device
Battery sense (GPIO 8, 1:2 divider) Pass ADC 2.007 V x 2 = 4.01 V vs 3.99-4.00 V on a multimeter while charging; 3.90 V on battery shows 73 %; charging shows 88 % with the charging icon
Turn-off / deep sleep Pass screen fully dark; BOOT wakes; touch does not wake (expected)
Launching firmware from SD Pass two ESP32-S3 images installed from the SD file browser and booted: (a) this branch's own app image, and (b) the official 78/xiaozhi-esp32 v2.5.2 lcdwiki-es3c35p merged image (10.2 MB, SHA-256 matched the release's SHA256SUMS)
Wrong-chip image is rejected Pass a classic-ESP32 image (header chip id 0x0000) fails the install check with "Flash Read Failed" instead of being booted -- expected behaviour, not a port issue
Reboot from menu Pass on device

Observed once, not reproduced: a single episode of touch moving 1-2 menu
entries too far per tap during a long test session (USB plus charging LiPo).
It cleared after a full power cycle (battery and USB removed) and has not
recurred; cause unknown, possibly transient electrical noise while charging.

Observed, not specific to this board: uploading a 10 MB file through the
Launcher web file manager failed with Upload failed: Upload receive failed
(the receive loop got a dropped connection mid-body; serial log shows no panic,
watchdog or reboot). A ~1 MB upload through the same path succeeded. The SD
card, flash and image checks were never reached. Copying the file onto the SD
card directly avoids the web path.

Not tested: deep sleep on battery alone with USB removed; sleep current;
audio (the ES8311 codec / SC8002B amp are only held quiet -- Launcher has no
sound output on any board); only a single hardware unit.

Regression check on shared code: touch.cpp and DisplayDrivers are shared, so
ES3C28P and ES3C40P (same family, standard-SPI panels sharing the touch and
DisplayDrivers code) were also built from this branch: both pio run builds
succeed. These were build checks only; they were not flashed or run.

Merge order

  1. Merge Add Arduino_ST77922_1L4: QSPI ST77922 driver (id 52, bus 6) DisplayDrivers#3 (adds Arduino_ST77922_1L4 and the bus-6 wiring).
  2. This PR then bumps the lib/DisplayDrivers submodule pointer to that commit.
    Until step 1 lands, CI on this PR cannot fetch the submodule commit.
    If the DisplayDrivers PR is squash-merged, its commit hash changes, so the
    submodule pointer here needs updating to the merged hash before this merges.
    This PR is therefore opened as a draft until then.

Provenance / licensing note

The ST77922 init-command table is the one from the board vendor's published
example (https://github.com/ydedox/st77922, Example_01), which carries no
license file. It is register data rather than code, and confirmed working on
hardware, but flagging it so the maintainer can decide whether they are
comfortable with it or want it sourced from the MIT-licensed
78/xiaozhi-esp32 equivalent instead. All other code is original to this PR.

Another contributor (@javaoverride, in #434) has independently got this board
working with a different approach (Arduino_GFX driver files and submodule edits).
This PR does not use their code; happy to coordinate or merge efforts.

Closes #434

🤖 Generated with Claude Code

EnygmaINC and others added 5 commits November 9, 2026 16:58
New board env for the 3.5" ESP32-S3 ST77922 (320x480) module:
- display via the new Arduino_ST77922_1L4 driver (DisplayDrivers submodule)
- TFT_DC=-1 / TFT_IPS=0 to stop pre_compiler.h's CYD defaults clobbering
  TFT_CS/TFT_SCLK
- new TOUCH_CTRL_ST77922 driver (I2C 0x55, 16-bit regs, whole-block read)
- SD_MMC 4-bit, backlight PWM, WS2812 LED, audio amp held off

Verified on hardware: display, touch, SD, all four rotations. Env is left
commented out of default_envs and the CI matrix. Unverified: audio,
battery sense, deep sleep.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
GPIO 8 is a 1:2 divider off the LiPo rail. Found by an ADC scan of the
unclaimed pins and checked against a multimeter (2.007 V x 2 = 4.01 V vs
3.99-4.00 V measured while charging). Default ANALOG_BAT_MULTIPLIER of 2.0
applies, so only ANALOG_BAT_PIN=8 is needed.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
powerOff() was copied from ES3C28P and overrode Launcher's default, which
enables the GPIO0 wake source. It called esp_deep_sleep_start() with no wake
source, so only a reset or power cycle could bring the board back, and the
backlight pad was left to float.

Now enable ext0 wake on GPIO0 (BOOT), latch the backlight pin low for the
sleep and release it in _setup_gpio(). Touch cannot wake deep sleep: its INT
(GPIO47) is not an RTC pad. Verified on hardware: screen fully dark, BOOT
wakes, touch does not.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Launcher has no sound output on any board; the codec and amp are only kept
quiet at boot.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Remove the ST77922_BRINGUP_TEST / ST77922_TOUCH_DEBUG guards, refresh the
stale "not buildable yet" header, and enable the env in the CI matrix now
that it builds and is verified on hardware (per boards/NEW_BOARD_GUIDE.md).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@EnygmaINC EnygmaINC changed the title Add lcdwiki-es3c35p: 3.5" ESP32-S3 ST77922 QSPI display with built-in capacitive touch Add CBD ES3C5P (3.5" Capacitive) lcdwiki-es3c35p: 3.5" ESP32-S3 ST77922 QSPI display with built-in capacitive touch Nov 9, 2026
@EnygmaINC EnygmaINC changed the title Add CBD ES3C5P (3.5" Capacitive) lcdwiki-es3c35p: 3.5" ESP32-S3 ST77922 QSPI display with built-in capacitive touch Add lcdwiki-es3c35p: ES3C35P “cheap black display” 3.5" ESP32-S3 (ST77922 QSPI + touch) Nov 9, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: Support LCDWiki ES3C35P (3.5" ESP32-S3, ST77922 QSPI panel)

1 participant