kcr
November 28, 2025, 12:41pm
1
I’ve just got one of these devices:
ESP32-S3 1.8inch AMOLED Touch Display Development Board, 32-bit LX7 Dual-core Processor, 368×448 Pixels, Accelerometer And Gyroscope Sensor, ESP32 With Display | ESP32-S3-Touch-AMOLED-1.8
SH8601 Display Driver and FT3168 Touch
It is not officially supported by ESPHome, but I wondered if anyone has managed to get it working and could share an example? I’ve had a search through the forum and I can see some references to working with the SH8601 and FT3168 separately.
Thanks
kcr
December 4, 2025, 3:29pm
2
I can’t find a specific example for this device, but I managed to get a basic configuration together by cribbing bits and pieces from data sheets and various ESPHome and non ESPHome configs that other people have built for similar boards.
The display works successfully, but I can’t get the touch screen working. When I tap the screen, I just get the following errors logged:
[15:20:10.094][D][esp-idf:000]: E (29193) i2c.master: I2C hardware NACK detected
[15:20:10.094][D][esp-idf:000]: E (29194) i2c.master: I2C transaction unexpected nack detected
[15:20:10.096][D][esp-idf:000]: E (29195) i2c.master: s_i2c_synchronous_transaction(945): I2C transaction failed
[15:20:10.098][D][esp-idf:000]: E (29197) i2c.master: i2c_master_execute_defined_operations(1401): I2C transaction failed
I’ve experimented with various frequencies, without success.
Config with working display but non-working touch screen:
esphome:
name: home-monitor-2
esp32:
variant: esp32s3
framework:
type: esp-idf
flash_size: 16MB
psram:
mode: octal
# Enable logging
logger:
# Enable Home Assistant API
api:
on_client_connected:
- logger.log: "api_connected"
# Allow Over-The-Air updates
ota:
- platform: esphome
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
manual_ip:
static_ip: 192.168.1.51
gateway: 192.168.1.254
subnet: 255.255.255.0
i2c:
id: i2c_1
scl: GPIO14
sda: GPIO15
frequency: 40kHz
scan: false
pca9554:
- id: 'pca9554_device'
touchscreen:
platform: cst816
display: display_1
i2c_id: i2c_1
id: touchscreen_1
interrupt_pin: GPIO21
reset_pin:
pca9554: pca9554_device
number: 0
skip_probe: true
on_touch:
- lambda: |-
ESP_LOGI("cal", "x=%d, y=%d, x_raw=%d, y_raw=%0d",
touch.x,
touch.y,
touch.x_raw,
touch.y_raw
);
spi:
type: quad
clk_pin: GPIO11
data_pins: [GPIO4, GPIO5, GPIO6, GPIO7]
# Display configuration
display:
- platform: mipi_spi
id: display_1
bus_mode: quad
dimensions:
width: 368
height: 448
model: CUSTOM
cs_pin: GPIO12
init_sequence:
- [0x00]
- delay 120ms
- [0x01, 0xD1]
- [0x00]
- [0x20]
- delay 10ms
- [0x00, 0x00, 0x01, 0x6F]
- [0x00, 0x00, 0x01, 0xBF]
- [0x00]
- delay 10ms
- [0x00]
- delay 10ms
- [0xFF]
lvgl:
#buffer_size: 25%
widgets:
- label:
align: CENTER
text: 'Hello World!'
- button:
x: 84
y: 100
width: 200
height: 80
id: btn_id
checkable: true
widgets:
- label:
align: center
text: "Tap me"
on_value:
then:
- logger.log:
format: "Button checked state: %d"
args: [ x ]
- slider:
id: dimmer_slider
x: 20
y: 50
width: 30
height: 220
pad_all: 8
min_value: 0
max_value: 255
Karosm
(Karosm)
December 4, 2025, 5:11pm
3
schematic gives ext02 for touch reset pin…
kcr
December 4, 2025, 5:23pm
4
You’re correct. The “0” in the config I posted was incorrectly included because I wasn’t sure if I was reading the spec correctly and was experimenting with different values.
I get the same error using pin 2, so I think there is something else wrong with my configuration.
kcr
December 5, 2025, 12:31am
5
Success!
I dug through the Waveshare example repository (GitHub - waveshareteam/ESP32-display-support: Waveshare ESP32 with screen product code summary ) and found the two missing pieces of information to get the touchscreen working:
Backtracking on the preceding discussion, pin 0 is actually the correct value for the pca9554 I/O extender reset_pin, not pin 2 (in retrospect this is obvious from the schematic pin names: LCD_RESET EXIO0 rather than TP_RESET EXIO2)
address: 0x38 is required for the touchscreen: definition
I also removed the frequency: value from my i2c: definition and it seems to be running OK on the default.
There are a lot of other features on this device (like audio I/O) that I have not attempted to configure, but it does appear to be functional for display and touch with ESPHome.
esphome:
name: home-monitor-2
esp32:
variant: esp32s3
framework:
type: esp-idf
flash_size: 16MB
psram:
mode: octal
# Enable logging
logger:
# Enable Home Assistant API
api:
on_client_connected:
- logger.log: "api_connected"
# Allow Over-The-Air updates
ota:
- platform: esphome
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
manual_ip:
static_ip: 192.168.1.51
gateway: 192.168.1.254
subnet: 255.255.255.0
i2c:
id: i2c_1
scl: GPIO14
sda: GPIO15
scan: false
pca9554:
- id: 'pca9554_device'
touchscreen:
platform: cst816
display: display_1
address: 0x38
i2c_id: i2c_1
id: touchscreen_1
interrupt_pin: GPIO21
reset_pin:
pca9554: pca9554_device
number: 0
skip_probe: true
on_touch:
- lambda: |-
ESP_LOGI("cal", "x=%d, y=%d, x_raw=%d, y_raw=%0d",
touch.x,
touch.y,
touch.x_raw,
touch.y_raw
);
spi:
type: quad
clk_pin: GPIO11
data_pins: [GPIO4, GPIO5, GPIO6, GPIO7]
# Display configuration
display:
- platform: mipi_spi
id: display_1
bus_mode: quad
dimensions:
width: 368
height: 448
model: CUSTOM
cs_pin: GPIO12
init_sequence:
- [0x00]
- delay 120ms
- [0x01, 0xD1]
- [0x00]
- [0x20]
- delay 10ms
- [0x00, 0x00, 0x01, 0x6F]
- [0x00, 0x00, 0x01, 0xBF]
- [0x00]
- delay 10ms
- [0x00]
- delay 10ms
- [0xFF]
lvgl:
#buffer_size: 25%
widgets:
- label:
align: CENTER
text: 'Hello World!'
- button:
x: 84
y: 100
width: 200
height: 80
id: btn_id
checkable: true
widgets:
- label:
align: center
text: "Tap me"
on_value:
then:
- logger.log:
format: "Button checked state: %d"
args: [ x ]
- slider:
id: dimmer_slider
x: 20
y: 50
width: 30
height: 220
pad_all: 8
min_value: 0
max_value: 255
kcr
December 5, 2025, 10:32am
6
A small change that fixes some issues and simplifies the config:
I couldn’t get brightness working with the CUSTOM display model: configuration I had cobbled together from some code examples, but discovered that I could drop the custom init_sequence and just use the model: WAVESHARE-ESP32-S3-TOUCH-AMOLED-1.75 option, which seems to work fine (the pin errors in my initial config efforts obscured the fact that this option was valid).
[Edit: I have an unresolved issue with the touchscreen only working immediately after flashing; if I subsequently power cycle the device the touch screen no longer responds ]
Updated config:
esphome:
name: home-monitor-2
esp32:
variant: esp32s3
framework:
type: esp-idf
flash_size: 16MB
psram:
mode: octal
# Enable logging
logger:
# Enable Home Assistant API
api:
on_client_connected:
- logger.log: "api_connected"
# Allow Over-The-Air updates
ota:
- platform: esphome
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
manual_ip:
static_ip: 192.168.1.51
gateway: 192.168.1.254
subnet: 255.255.255.0
i2c:
id: i2c_1
scl: GPIO14
sda: GPIO15
scan: false
frequency: 100kHz
pca9554:
- id: 'pca9554_device'
touchscreen:
platform: cst816
display: display_1
address: 0x38
i2c_id: i2c_1
id: touchscreen_1
interrupt_pin: GPIO21
reset_pin:
pca9554: pca9554_device
number: 0
skip_probe: true
on_touch:
- lambda: |-
ESP_LOGI("cal", "x=%d, y=%d, x_raw=%d, y_raw=%0d",
touch.x,
touch.y,
touch.x_raw,
touch.y_raw
);
spi:
type: quad
clk_pin: GPIO11
data_pins: [GPIO4, GPIO5, GPIO6, GPIO7]
# Display configuration
display:
- platform: mipi_spi
id: display_1
brightness: 150
bus_mode: quad
dimensions:
width: 368
height: 448
model: WAVESHARE-ESP32-S3-TOUCH-AMOLED-1.75
cs_pin: GPIO12
# init_sequence:
# - [0x00]
# - delay 120ms
# - [0x01, 0xD1]
# - [0x00]
# - [0x20]
# - delay 10ms
# - [0x00, 0x00, 0x01, 0x6F]
# - [0x00, 0x00, 0x01, 0xBF]
# - [0x00]
# - delay 10ms
# - [0x00]1
# - delay 10ms
# - [0xFF]
lvgl:
#buffer_size: 25%
pages:
- id: main_page
bg_color: black
widgets:
- label:
align: CENTER
text: 'Hello World!'
text_color: white
- button:
bg_color: LightGreen
x: 84
y: 100
width: 200
height: 80
id: btn_id
checkable: true
checked:
bg_color: Grey
widgets:
- label:
align: center
text: "Tap me"
on_value:
then:
- logger.log:
format: "Button checked state: %d"
args: [ x ]
- slider:
id: dimmer_slider
x: 20
y: 50
width: 30
height: 220
pad_all: 8
min_value: 0
max_value: 255
2 Likes
argsnd
(argsnd)
December 7, 2025, 5:03pm
7
You can get the touch screen working reliably like this:
touchscreen:
- platform: ft5x06
id: my_touchscreen
display: my_display
interrupt_pin: GPIO21
address: 0x38
kcr
December 7, 2025, 6:33pm
8
Thanks very much. That was a well timed reply because I was just about to post another message after failing to find a solution to the reboot problem.
I switched to platform: ft5x06 and can confirm that the touchscreen is working correctly when flashed, and also continuing to work when the device is rebooted.
My revised configuration is below. With the fx5x06 plaform, the reset_pin and skip_probe options are no longer valid, and the pca9554 definition is therefore no longer required.
Thanks again!
Revised config:
esphome:
name: home-monitor-2
esp32:
variant: esp32s3
framework:
type: esp-idf
flash_size: 16MB
psram:
mode: octal
# Enable logging
logger:
# Enable Home Assistant API
api:
on_client_connected:
- logger.log: "api_connected"
# Allow Over-The-Air updates
ota:
- platform: esphome
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
manual_ip:
static_ip: 192.168.1.51
gateway: 192.168.1.254
subnet: 255.255.255.0
i2c:
id: i2c_1
scl: GPIO14
sda: GPIO15
scan: true
frequency: 100kHz
touchscreen:
platform: ft5x06
display: display_1
address: 0x38
i2c_id: i2c_1
id: touchscreen_1
interrupt_pin: GPIO21
on_touch:
- lambda: |-
ESP_LOGI("cal", "x=%d, y=%d, x_raw=%d, y_raw=%0d",
touch.x,
touch.y,
touch.x_raw,
touch.y_raw
);
spi:
type: quad
clk_pin: GPIO11
data_pins: [GPIO4, GPIO5, GPIO6, GPIO7]
# Display configuration
display:
- platform: mipi_spi
id: display_1
brightness: 150
bus_mode: quad
dimensions:
width: 368
height: 448
model: WAVESHARE-ESP32-S3-TOUCH-AMOLED-1.75
cs_pin: GPIO12
lvgl:
#buffer_size: 25%
pages:
- id: main_page
on_swipe_right:
- lvgl.page.previous:
on_swipe_left:
- lvgl.page.next:
bg_color: black
widgets:
- label:
align: CENTER
text: 'Hello World!'
text_color: white
- button:
bg_color: LightGreen
x: 84
y: 100
width: 200
height: 80
id: btn_id
checkable: true
checked:
bg_color: Grey
widgets:
- label:
align: center
text: "Tap me"
on_value:
then:
- logger.log:
format: "Button checked state: %d"
args: [ x ]
- slider:
id: dimmer_slider
x: 20
y: 50
width: 30
height: 220
pad_all: 8
min_value: 0
max_value: 255
- id: page_2
on_swipe_right:
- lvgl.page.previous:
on_swipe_left:
- lvgl.page.next:
bg_color: black
widgets:
- label:
align: CENTER
text: 'Hello World 2!'
text_color: white
1 Like
argsnd
(argsnd)
December 7, 2025, 7:54pm
9
Audio works fine too, here’s a tap to talk voice assistant with a volume slider:
esphome:
name: esp-assistant
friendly_name: esp-assistant
esp32:
board: esp32-s3-devkitc-1
framework:
type: esp-idf
psram:
mode: octal
logger:
api:
ota:
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
captive_portal:
# I2C Bus - Shared by touchscreen (FT5x06) and audio codec (ES8311)
i2c:
id: i2c_bus
sda: GPIO15
scl: GPIO14
frequency: 200kHz
scan: true
# Quad SPI Bus - AMOLED display
spi:
- id: quad_spi
type: quad
clk_pin: GPIO11
data_pins:
- GPIO4 # D0
- GPIO5 # D1
- GPIO6 # D2
- GPIO7 # D3
# AMOLED Display (368x448)
display:
- platform: mipi_spi
id: my_display
brightness: 150
bus_mode: quad
dimensions:
width: 368
height: 448
model: WAVESHARE-ESP32-S3-TOUCH-AMOLED-1.75
cs_pin: GPIO12
# FT5x06 Capacitive Touchscreen
touchscreen:
- platform: ft5x06
id: my_touchscreen
display: my_display
interrupt_pin: GPIO21
address: 0x38
transform:
swap_xy: false
mirror_x: false
mirror_y: false
# ============================================================================
# AUDIO CONFIGURATION
# ============================================================================
# I2S Audio Bus - Connects ESP32 to ES8311 codec
i2s_audio:
- id: i2s_audio_bus
i2s_lrclk_pin: GPIO45 # Word select / left-right clock
i2s_bclk_pin: GPIO9 # Bit clock
i2s_mclk_pin: GPIO16 # Master clock
# ES8311 Audio Codec
audio_dac:
- platform: es8311
id: es8311_codec
address: 0x18
i2c_id: i2c_bus
use_mclk: true
use_microphone: false # Analog microphone (not PDM digital)
bits_per_sample: 16bit
sample_rate: 16000
mic_gain: 18DB
# Speaker - Audio output through ES8311 DAC
speaker:
- platform: i2s_audio
id: external_speaker
dac_type: external
audio_dac: es8311_codec
i2s_audio_id: i2s_audio_bus
i2s_dout_pin: GPIO8
sample_rate: 16000
bits_per_sample: 16bit
timeout: 2s
# Microphone - Audio input through ES8311 ADC
microphone:
- platform: i2s_audio
id: external_microphone
adc_type: external
i2s_audio_id: i2s_audio_bus
i2s_din_pin: GPIO10
bits_per_sample: 16bit
sample_rate: 16000
# Power Amplifier - GPIO46 controls speaker amplifier enable
switch:
- platform: gpio
pin: GPIO46
id: pa_enable
name: "Speaker Amplifier"
restore_mode: ALWAYS_ON
internal: false
# Volume Control - Synced with ES8311 hardware volume and UI slider
number:
- platform: template
id: volume_control
name: "Volume"
min_value: 0
max_value: 100
initial_value: 70
step: 5
optimistic: true
set_action:
- lambda: |-
id(es8311_codec).set_volume(x / 100.0);
ESP_LOGI("volume", "Volume set to: %.0f%%", x);
# RTTTL Ringtone Player - Plays simple tones and melodies
rtttl:
id: buzzer
speaker: external_speaker
# ============================================================================
# VOICE ASSISTANT
# ============================================================================
voice_assistant:
id: va
microphone: external_microphone
speaker: external_speaker
# Listening state - Button turns blue
on_listening:
- logger.log: "Voice assistant started listening"
- lvgl.label.update:
id: status_label
text: "Listening..."
- lvgl.widget.update:
id: btn_id
bg_color: DodgerBlue
# Speech-to-text complete - Display recognized text
on_stt_end:
- logger.log:
format: "Speech recognized: %s"
args: [ 'x.c_str()' ]
- lvgl.label.update:
id: status_label
text: !lambda 'return "You said: " + x;'
# Text-to-speech starting - Display speaking status
on_tts_start:
- logger.log: "Speaking response..."
- lvgl.label.update:
id: status_label
text: "Speaking..."
# Session complete - Button returns to green
on_end:
- logger.log: "Voice assistant session ended"
- lvgl.label.update:
id: status_label
text: "Ready"
- lvgl.widget.update:
id: btn_id
bg_color: LightGreen
# Error handling - Flash red, then return to green
on_error:
- logger.log:
format: "Voice assistant error: %s"
args: [ 'code.c_str()' ]
- lvgl.label.update:
id: status_label
text: "Error!"
- lvgl.widget.update:
id: btn_id
bg_color: Red
- delay: 1s
- lvgl.widget.update:
id: btn_id
bg_color: LightGreen
- lvgl.label.update:
id: status_label
text: "Ready"
# ============================================================================
# USER INTERFACE (LVGL)
# ============================================================================
lvgl:
pages:
- id: main_page
bg_color: black
widgets:
# Status label - Shows current voice assistant state
- label:
id: status_label
align: CENTER
y: 50
text: 'Ready'
text_color: white
# Main voice assistant button - Tap to activate
- button:
bg_color: LightGreen
x: 84
y: 100
width: 200
height: 160
id: btn_id
widgets:
- label:
align: center
text: "Tap to Talk"
on_click:
then:
- if:
condition:
not: voice_assistant.is_running
then:
- rtttl.play: 'beep:d=16,o=5,b=100:c6'
- delay: 200ms
- voice_assistant.start:
else:
- logger.log: "Ignored tap: Voice Assistant already running"
# Volume slider - Vertical slider on left side
- slider:
id: volume_slider
x: 20
y: 50
width: 30
height: 308
pad_all: 8
min_value: 0
max_value: 100
value: 70
on_value:
then:
- logger.log:
format: "Volume slider: %d"
args: [ x ]
- number.set:
id: volume_control
value: !lambda 'return x;'
# Volume label
- label:
x: 60
y: 180
text_color: white
text: "Vol"
2 Likes