Skip to content

Add LCDwiki ES3C35P 3.5" (ST77922 QSPI, 320x480) CBD ("cheap black display") board - #205

Open
EnygmaINC wants to merge 5 commits into
Keralots:mainfrom
EnygmaINC:add-lcdwiki-es3c35p
Open

EnygmaINC wants to merge 5 commits into
Keralots:mainfrom
EnygmaINC:add-lcdwiki-es3c35p

Conversation

@EnygmaINC

Copy link
Copy Markdown

Summary

Adds support for the LCDwiki ES3C35P, a 3.5" 320x480 ESP32-S3 all-in-one (16MB flash, 8MB octal PSRAM). It is sold under several names, e.g. "CBD" or "cheap black display". New env: es3c35p (in boards/lcdwiki_es3c35p.ini, like the other community boards).

The display is the interesting part. The ST77922 is QSPI but wants register writes on ONE data line and pixels on FOUR ("1-1-4"). LovyanGFX's SPI bus and Arduino_GFX's QSPI bus both send the command phase on all four lines, so neither can drive it. This PR adds Panel_ST77922_QSPI (src/lgfx_panel_st77922_qspi.hpp), a small Panel_Device that owns an ESP-IDF spi_master device. It follows the same contract as Panel_AXS15231B_AGFX: the panel stays at rotation 0, the app draws into the full-frame PSRAM sprite, and the sprite is pushed once per tick. That lets the board reuse PANEL_REQUIRES_AXS_FRAME_SPRITE and the 320x480 layouts unchanged. display_ui.h gets a FramePanel typedef so both panel classes share the code path.

Touch is inside the ST77922 (I2C 0x55, 16-bit register addresses). USE_ST77922_TOUCH adds a level-polling backend (src/button_touch_st77922.cpp), the same shape as the AXS15231B one. RST is GPIO48 and needs a pulse at init.

Other board bits: battery sense on GPIO8 (1:2 divider), onboard WS2812 status LED on GPIO40, backlight GPIO41, buzzer default pin 0 (no buzzer hardware; the ES8311 amp has no driver here). Pin-clash guards in button.cpp and led.cpp are extended for this board.

Also registered in tools/release.py, docs/flasher.js (community badge) and the README (board table and build-file table).

Tested

On one physical unit: display, cold boot, touch (a tap exits clock mode back to the print screen; the controller answers a few seconds after boot, so the first read is a retry), battery reading, Wi-Fi setup, adding a printer, and the WS2812 status LED all work. Not tested: a real print job over a long session, landscape rotations on every screen, 3-4 printer mode, audio.

One unexplained event: during development I saw a single dark-screen boot with a binary identical to one that later worked. Cold boots have been fine since. Flagging it for honesty; I could not reproduce it.

Builds checked (compile only) after these changes: esp32s3, cyd, jc3248w535, ws_lcd_350, es3c35p.

Things you should know

  • Licence of the init table. src/lgfx_panel_st77922_qspi_init.h is the vendor's register init sequence, copied from the board vendor's example (github.com/ydedox/st77922, Example_01), which has no licence file. If that is a problem, I can drop it and ask the vendor, or you can decline that part.
  • The Launcher port for the same board (bmorcelli/Launcher PR #475, DisplayDrivers PR configuration guide video #3) was where the init sequence and the 1-1-4 transfer format were first worked out.
  • Flashing note for anyone building the merged image by hand: esptool merge_bin must use --flash_mode keep; forcing qio rewrites the header and boot-loops (the PlatformIO bootloader is DIO). The octal PSRAM needs board_build.arduino.memory_type = qio_opi (already set in the env).
  • No photo is included in the README row yet.

Happy to adjust naming, split the panel class into its own PR, or drop anything you would rather not carry.

🤖 Generated with Claude Code

EnygmaINC and others added 5 commits November 9, 2026 20:41
New es3c35p env: Panel_ST77922_QSPI (own ESP-IDF spi_master driver, register
writes on 1 line / pixels on 4), USE_ST77922_TOUCH backend (I2C 0x55), battery
sense on GPIO 8, shares the JC3248W535 full-frame PSRAM sprite path via a new
FramePanel type.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The landscape gauge-text and pong-speed workarounds were keyed on the JC
board but belong to the PSRAM frame sprite, so ES3C35P now gets them too.
FramePanel is picked by a config.h capability instead of a board name in
display_ui.h, and Panel_ST77922_QSPI::init() reports why it failed.
@Keralots

Copy link
Copy Markdown
Owner

Thanks for this.

I merged current main into your branch (README build table conflict after v3.9.2) and pushed one small commit on top (8f32f64):

  • The landscape gauge-text workaround was keyed on the JC3248W535 board name, but it really belongs to the PSRAM frame sprite, so ES3C35P now gets it too.
  • FramePanel is selected by a capability macro in config.h (PANEL_FRAME_IS_ST77922) instead of a board name in display_ui.h.
  • Panel_ST77922_QSPI::init() now prints why it failed on Serial, which might help if the dark-screen boot ever shows up again.

Before I merge, could you check a few things on your unit with the current branch head?

  1. Landscape (rotation 90/270): gauge values on the print screen, with 1 and 2 printers - the text should not render with black boxes or black glyphs.
  2. Warm restarts: reboot from the web portal, and one OTA update (upload the same firmware.bin via the portal). The panel has no reset line, so I'd like to confirm it comes back cleanly when the chip restarts without a power cycle.
  3. A photo of the board (front, roughly square crop works best) - it goes on the web flasher card and in the README table.

On the init table licence: it's a register sequence, I'm fine carrying it with the source credited as you did.

Once those check out, I'll merge it and publish an es3c35p build to the web flasher right away, without waiting for the next release.

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.

2 participants