[mipi_spi] Add LCDwiki ES3C35P model (ST77922 QSPI) (#19011)

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: J. Nick Koston <nick@koston.org>
Co-authored-by: J. Nick Koston <nick@home-assistant.io>
This commit is contained in:
Jamie Meredith
2026-10-12 00:48:47 +00:00
committed by GitHub
co-authored by Claude Opus 5 J. Nick Koston J. Nick Koston
parent e50a0d902e
commit 59d31505e9
2 changed files with 109 additions and 0 deletions
@@ -0,0 +1,86 @@
# Init sequence sourced from LCDwiki's own published Arduino example for this board:
# https://github.com/ydedox/st77922 (Example_01_Simple_test/Simple_test/Simple_test.ino)
# It is the commented-out panel table in that file, with the 0x71 and 0xBE values from the
# active one, which is what the shipped firmware writes.
from esphome.components.mipi import MODE_RGB, SLPOUT, DriverChip, delay
from esphome.components.spi import TYPE_QUAD
from esphome.const import CONF_MIRROR_X, CONF_MIRROR_Y
# fmt: off
ES3C35P = DriverChip(
"ES3C35P",
width=320,
height=480,
draw_rounding=4,
transforms={CONF_MIRROR_X, CONF_MIRROR_Y},
color_order=MODE_RGB,
invert_colors=True,
bus_mode=TYPE_QUAD,
data_rate="40MHz", # datasheet caps the write clock at 62.5MHz
cs_pin=10,
reset_delay=120,
no_slpout=True, # SLPOUT is in the init sequence, the panel needs 120ms after it
requires={"psram"},
initsequence=(
(0xF1, 0x00),
(0x60, 0x00, 0x00, 0x00),
(0x65, 0x80),
(0x79, 0x06),
(0x7B, 0x00, 0x08, 0x08),
(0x80, 0x55, 0x62, 0x2F, 0x17, 0xF0, 0x52, 0x70, 0xD2, 0x52, 0x62, 0xEA),
(0x81, 0x26, 0x52, 0x72, 0x27),
(0x84, 0x92, 0x25),
(0x87, 0x10, 0x10, 0x58, 0x00, 0x02, 0x3A),
(0x88, 0x00, 0x00, 0x2C, 0x10, 0x04, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x06),
(0x89, 0x00, 0x00, 0x00),
(0x8A, 0x13, 0x00, 0x2C, 0x00, 0x00, 0x2C, 0x10, 0x10, 0x00, 0x3E, 0x19),
(0x8B, 0x15, 0xB1, 0xB1, 0x44, 0x96, 0x2C, 0x10, 0x97, 0x8E),
(0x8C, 0x1D, 0xB1, 0xB1, 0x44, 0x96, 0x2C, 0x10, 0x50, 0x0F, 0x01, 0xC5, 0x12, 0x09),
(0x8D, 0x0C),
(0x8E, 0x33, 0x01, 0x0C, 0x13, 0x01, 0x01),
(0xB3, 0x00, 0x30),
(0x71, 0xD0),
(0x66, 0x02, 0x3F),
(0xBE, 0x26, 0x00, 0x9D),
(0x70, 0x01, 0xA6, 0x11, 0x40, 0xE0, 0x00, 0x11, 0x60, 0x11, 0x00, 0x00, 0x1A),
(0x90, 0x04, 0x04, 0x55, 0x74, 0x00, 0x40, 0x43, 0x2D, 0x2D),
(0x91, 0x04, 0x04, 0x55, 0x75, 0x00, 0x40, 0x42, 0x2D, 0x2D),
(0x92, 0x04, 0x44, 0x55, 0xC0, 0x06, 0x00, 0x07, 0x05, 0x90, 0x2D),
(0x93, 0x04, 0x43, 0x11, 0x00, 0x00, 0x00, 0x00, 0x05, 0x90, 0x2D),
(0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00),
(0x95, 0x96, 0x16, 0x00, 0x00, 0xFF),
(0x96, 0x44, 0x53, 0x03, 0x12, 0x23, 0x24, 0x06, 0x05, 0x9A, 0x2D, 0x00, 0x44),
(0x97, 0x44, 0x53, 0x47, 0x56, 0x20, 0x20, 0x02, 0x01, 0x9A, 0x2D, 0x00, 0x44),
(0xBA, 0x55, 0x9A, 0x2D, 0x9A, 0x2D),
(0x9A, 0x40, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00),
(0x9B, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00),
(0x9C, 0x5C, 0x12, 0x00, 0x00, 0x10, 0x12, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x00),
(0x9D, 0x8A, 0x51, 0x00, 0x00, 0x00, 0x80, 0x1E, 0x01),
(0x9E, 0x51, 0x00, 0x00, 0x00, 0x80, 0x1E, 0x01),
(0xB4, 0x1D, 0x1C, 0x1E, 0x0B, 0x14, 0x02, 0x13, 0x09, 0x1E, 0x00, 0x1E, 0x10),
(0xB5, 0x1D, 0x1C, 0x1E, 0x0A, 0x15, 0x03, 0x11, 0x08, 0x1E, 0x01, 0x1E, 0x12),
(0xB6, 0x77, 0x77, 0x00, 0x0A, 0xFF, 0x0A, 0xFF),
(0x86, 0xC6, 0x04, 0xB1, 0x02, 0x58, 0x12, 0x58, 0x0C, 0x13, 0x01, 0xA5, 0x00, 0xA5, 0xA5),
(0xB7, 0x07, 0x0A, 0x0E, 0x06, 0x05, 0x03, 0x2B, 0x03, 0x03, 0x42, 0x07, 0x10, 0x10, 0x2E, 0x3F, 0x0D),
(0xB8, 0x07, 0x0A, 0x0D, 0x05, 0x05, 0x02, 0x2B, 0x02, 0x03, 0x42, 0x06, 0x10, 0x0F, 0x2E, 0x3F, 0x0D),
(0xB9, 0x23, 0x23),
(0xBF, 0x10, 0x14, 0x14, 0x0B, 0x0B, 0x0B),
(0xF2, 0x00),
(0x73, 0x04, 0xDA, 0x12, 0x54, 0x47),
(0x77, 0x6B, 0x5B, 0xFD, 0xC3, 0xC5),
(0x7A, 0x15, 0x27),
(0x7B, 0x04, 0x57),
(0x7E, 0x01, 0x0E),
(0xBF, 0x36),
(0xE3, 0x40, 0x40),
(0xF0, 0x00),
(0xD0, 0x00),
(SLPOUT,),
delay(120),
),
)
# fmt: on
# The speakerless variant uses the same display
ES3C35P.extend("ES3C35P-NS")
@@ -8,8 +8,11 @@ from esphome.components.mipi import (
BRIGHTNESS,
CONF_INVERT_COLORS,
CONF_PIXEL_MODE,
SLPOUT,
SWRESET,
DriverChip,
)
from esphome.components.mipi_spi.display import MODELS
from esphome.const import CONF_BRIGHTNESS
# A minimal config with no reset pin: enough for get_sequence(add_madctl=False) to run
@@ -96,3 +99,23 @@ def test_get_sequence_omits_brightness_when_not_set() -> None:
sequence = chip.get_sequence(_BASE_CONFIG, add_madctl=False)
assert not any(_has_brightness_command(sequence, v) for v in range(256))
def _count_subsequence(sequence: tuple[int, ...], part: tuple[int, ...]) -> int:
return sum(
sequence[i : i + len(part)] == part
for i in range(len(sequence) - len(part) + 1)
)
def test_es3c35p_waits_after_reset_and_issues_its_own_slpout() -> None:
"""The ES3C35P settles 120ms after reset and sends SLPOUT itself with a 120ms wait."""
sequence = MODELS["ES3C35P"].get_sequence(
_BASE_CONFIG, add_madctl=False, add_reset=True
)
assert sequence[:4] == (SWRESET, 0, 120, 255)
# Exactly one SLPOUT, followed by the panel's 120ms settle, and none appended by the framework
assert _count_subsequence(sequence, (SLPOUT, 0, 120, 255)) == 1
assert _count_subsequence(sequence, (SLPOUT, 0, 10, 255)) == 0
assert "ES3C35P-NS" in MODELS