Waveshare ESP32-S3-LCD-Driver-Board w/ 2.8" touch display

ESP & Display info: https://www.waveshare.com/wiki/ESP32-S3-LCD-Driver-Board
EPS-32-S3-WROOM-1-N8R8 module, 8MB PSRAM
ST7701S LCD display
GT911 touch screen sensor

I’ve managed to get the board to boot, configured I2C and SPI peripherals and have touch working. I’m trying to get the display working and not having a lot of luck.

Config:

esphome:
  name: esp32s3

esp32:
  board: esp32-s3-devkitc-1
  flash_size: 8MB
  variant: esp32s3
  framework:
    type: esp-idf
    sdkconfig_options:
      CONFIG_ESPTOOLPY_FLASHMODE_QIO: y
      CONFIG_ESPTOOLPY_FLASHFREQ: 80M
      CONFIG_FLASHMODE_QIO: y
      CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240: y
      CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ: '240'

psram:
  mode: octal
  speed: 80MHz

# Enable logging
logger:

spi:
  clk_pin: GPIO02
  mosi_pin: GPIO01
  interface: spi3

i2c:
  frequency: 400000
  sda: GPIO15
  scl: GPIO07

pca9554:
  - id: 'p_c_a'

display:
  - platform: st7701s
    id: my_display
    spi_mode: MODE3
    pclk_frequency: 30MHz
    color_order: bgr
    dimensions:
      width: 480
      height: 480
    cs_pin: GPIO42
    reset_pin:
      pca9554: p_c_a
      number: 3
    de_pin: GPIO40
    hsync_pin: GPIO38
    vsync_pin: GPIO39
    pclk_pin: GPIO41
    init_sequence:
      - 3
      - delay 5ms
    data_pins:
      red:
        - 46       #r1
        - 3        #r2
        - 8        #r3
        - 18       #r4
        - 17       #r5
      green:
        - 14       #g0
        - 13       #g1
        - 12       #g2
        - 11       #g3
        - 10       #g4
        - 9        #g5
      blue:
        - 5        #b1
        - 45       #b2
        - 48       #b3
        - 47       #b4
        - 21       #b5

touchscreen:
  platform: gt911
  id: my_touchscreen
  display: my_display
  interrupt_pin: GPIO16
  on_touch:
    - lambda: |-
          ESP_LOGI("touch", "x=%d, y=%d, x_raw=%d, y_raw=%0d",
              touch.x,
              touch.y,
              touch.x_raw,
              touch.y_raw
              );

Output when booted:

[17:52:36]I (108) boot: End of partition table
[17:52:36]I (111) boot: No factory image, trying OTA 0
[17:52:36]I (116) esp_image: segment 0: paddr=00010020 vaddr=3c030020 size=11b30h ( 72496) map
[17:52:36]I (135) esp_image: segment 1: paddr=00021b58 vaddr=3fc94900 size=02d64h ( 11620) load
[17:52:36]I (139) esp_image: segment 2: paddr=000248c4 vaddr=40374000 size=0b754h ( 46932) load
[17:52:36]I (150) esp_image: segment 3: paddr=00030020 vaddr=42000020 size=2c5c8h (181704) map
[17:52:36]I (179) esp_image: segment 4: paddr=0005c5f0 vaddr=4037f754 size=05180h ( 20864) load
[17:52:36]I (190) boot: Loaded app from partition at offset 0x10000
[17:52:36]I (219) boot: Set actual ota_seq=1 in otadata[0]
[17:52:36]I (219) boot: Disabling RNG early entropy source...
[17:52:36]I (220) cpu_start: Multicore app
[17:52:36]I (223) octal_psram: vendor id    : 0x0d (AP)
[17:52:36]I (228) octal_psram: dev id       : 0x02 (generation 3)
[17:52:36]I (233) octal_psram: density      : 0x03 (64 Mbit)
[17:52:36]I (239) octal_psram: good-die     : 0x01 (Pass)
[17:52:36]I (244) octal_psram: Latency      : 0x01 (Fixed)
[17:52:36]I (249) octal_psram: VCC          : 0x01 (3V)
[17:52:36]I (254) octal_psram: SRF          : 0x01 (Fast Refresh)
[17:52:36]I (260) octal_psram: BurstType    : 0x01 (Hybrid Wrap)
[17:52:36]I (266) octal_psram: BurstLen     : 0x01 (32 Byte)
[17:52:36]I (272) octal_psram: Readlatency  : 0x02 (10 cycles@Fixed)
[17:52:36]I (278) octal_psram: DriveStrength: 0x00 (1/1)
[17:52:36]I (284) MSPI Timing: PSRAM timing tuning index: 5
[17:52:36]I (288) esp_psram: Found 8MB PSRAM device
[17:52:36]I (293) esp_psram: Speed: 80MHz
[17:52:36]I (297) cpu_start: Pro cpu up.
[17:52:36]I (301) cpu_start: Starting app cpu, entry point is 0x40376fcc
[17:52:36]I (0) cpu_start: App cpu up.
[17:52:36]I (730) esp_psram: SPI SRAM memory test OK
[17:52:36]I (738) cpu_start: Pro cpu start user code
[17:52:36]I (739) cpu_start: cpu freq: 240000000 Hz
[17:52:36]I (739) cpu_start: Application information:
[17:52:36]I (740) cpu_start: Project name:     esp32s3
[17:52:36]I (740) cpu_start: App version:      2024.12.2
[17:52:36]I (741) cpu_start: Compile time:     Jan 15 2025 17:35:35
[17:52:36]I (741) cpu_start: ELF file SHA256:  b6033a5e38bbb6f3...
[17:52:36]I (742) cpu_start: ESP-IDF:          5.1.5
[17:52:36]I (743) cpu_start: Min chip rev:     v0.0
[17:52:36]I (743) cpu_start: Max chip rev:     v0.99 
[17:52:36]I (743) cpu_start: Chip rev:         v0.2
[17:52:36]I (744) heap_init: Initializing. RAM available for dynamic allocation:
[17:52:36]I (745) heap_init: At 3FC98108 len 00051608 (325 KiB): DRAM
[17:52:36]I (745) heap_init: At 3FCE9710 len 00005724 (21 KiB): STACK/DRAM
[17:52:36]I (746) heap_init: At 3FCF0000 len 00008000 (32 KiB): DRAM
[17:52:36]I (747) heap_init: At 600FE010 len 00001FD8 (7 KiB): RTCRAM
[17:52:36]I (747) esp_psram: Adding pool of 8192K of PSRAM memory to heap allocator
[17:52:36]I (748) spi_flash: detected chip: gd
[17:52:36]I (748) spi_flash: flash io: qio
[17:52:36]I (749) sleep: Configure to isolate all GPIO pins in sleep state
[17:52:36]I (750) sleep: Enable automatic switching of GPIO sleep configuration
[17:52:36]I (751) app_start: Starting scheduler on CPU0
[17:52:36]I (751) app_start: Starting scheduler on CPU1
[17:52:36]I (752) main_task: Calling app_main()
[17:52:36][I][logger:171]: Log initialized
[17:52:36][I][app:029]: Running through setup()...
[17:52:36][D][spi:035]: Setting up SPI bus...
[17:52:36][C][i2c.idf:017]: Setting up I2C bus...
[17:52:36][I][i2c.idf:252]: Performing I2C bus recovery
[17:52:36][D][esp-idf:000]: I (831) gpio: GPIO[7]| InputEn: 1| OutputEn: 1| OpenDrain: 1| Pullup: 1| Pulldown: 0| Intr:0 
[17:52:36]
[17:52:36][D][esp-idf:000]: I (832) gpio: GPIO[15]| InputEn: 1| OutputEn: 1| OpenDrain: 1| Pullup: 1| Pulldown: 0| Intr:0 
[17:52:36]
[17:52:36][C][pca9554:016]: Setting up PCA9554/PCA9554A...
[17:52:36][D][pca9554:036]: Initialization complete. Warning: 0, Error: 0
[17:52:36][C][display.st7701s:009]: Setting up ST7701S
[17:52:36][D][spi_device:362]: mode 3, data_rate 1000kHz
[17:52:36][D][esp-idf:000]: I (843) gpio: GPIO[42]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 
[17:52:36]
[17:52:36][D][display.st7701s:172]: write MADCTL 8
[17:52:36][C][display.st7701s:047]: ST7701S setup complete
[17:52:36][C][gt911.touchscreen:028]: Setting up GT911 Touchscreen...
[17:52:36][D][gt911.touchscreen:053]: Read from switches: 0x05
[17:52:36][D][esp-idf:000]: I (1006) gpio: GPIO[16]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 
[17:52:36]
[17:52:36][D][touchscreen:016]: Attach Touch Interupt
[17:52:36][D][gt911.touchscreen:074]: calibration max_x/max_y 480/480
[17:52:36][C][gt911.touchscreen:084]: GT911 Touchscreen setup complete
[17:52:36][I][app:062]: setup() finished successfully!
[17:52:37][W][component:237]: Component display took a long time for an operation (685 ms).
[17:52:37][W][component:238]: Components should block for at most 30 ms.
[17:52:37][W][touchscreen:032]: Touch Polling Stopped. You can safely remove the 'update_interval:' variable from the YAML file.
[17:52:37][I][app:100]: ESPHome version 2024.12.2 compiled on Jan 15 2025, 17:52:26
[17:52:37][C][logger:185]: Logger:
[17:52:37][C][logger:186]:   Level: DEBUG
[17:52:37][C][logger:188]:   Log Baud Rate: 115200
[17:52:37][C][logger:189]:   Hardware UART: USB_SERIAL_JTAG
[17:52:37][C][spi:064]: SPI bus:
[17:52:37][C][spi:065]:   CLK Pin: GPIO2
[17:52:37][C][spi:066]:   SDI Pin: 
[17:52:37][C][spi:067]:   SDO Pin: GPIO1
[17:52:37][C][spi:072]:   Using HW SPI: SPI3_HOST
[17:52:37][C][i2c.idf:075]: I2C Bus:
[17:52:37][C][i2c.idf:076]:   SDA Pin: GPIO15
[17:52:37][C][i2c.idf:077]:   SCL Pin: GPIO7
[17:52:37][C][i2c.idf:078]:   Frequency: 400000 Hz
[17:52:37][C][i2c.idf:084]:   Recovery: bus successfully recovered
[17:52:37][I][i2c.idf:094]: Results from i2c bus scan:
[17:52:37][I][i2c.idf:100]: Found i2c device at address 0x20
[17:52:37][I][i2c.idf:100]: Found i2c device at address 0x5D
[17:52:37][C][pca9554:048]: PCA9554:
[17:52:37][C][pca9554:049]:   I/O Pins: 8
[17:52:37][C][pca9554:050]:   Address: 0x20
[17:52:37][C][psram:020]: PSRAM:
[17:52:37][C][psram:021]:   Available: YES
[17:52:37][C][psram:024]:   Size: 8192 KB
[17:52:37][C][:183]: ST7701S RGB LCD
[17:52:37][C][display.st7701s:184]:   Height: 480
[17:52:37][C][display.st7701s:185]:   Width: 480
[17:52:37][C][display.st7701s:186]:   CS Pin: GPIO42
[17:52:37][C][display.st7701s:188]:   DE Pin: GPIO40
[17:52:37][C][display.st7701s:189]:   Reset Pin: 3 via PCA9554
[17:52:37][C][display.st7701s:192]:   Data pin 0: GPIO11
[17:52:37][C][display.st7701s:192]:   Data pin 1: GPIO10
[17:52:37][C][display.st7701s:192]:   Data pin 2: GPIO9
[17:52:37][C][display.st7701s:192]:   Data pin 3: GPIO5
[17:52:37][C][display.st7701s:192]:   Data pin 4: GPIO45
[17:52:37][C][display.st7701s:192]:   Data pin 5: GPIO48
[17:52:37][C][display.st7701s:192]:   Data pin 6: GPIO47
[17:52:37][C][display.st7701s:192]:   Data pin 7: GPIO21
[17:52:37][C][display.st7701s:192]:   Data pin 8: GPIO46
[17:52:37][C][display.st7701s:192]:   Data pin 9: GPIO3
[17:52:37][C][display.st7701s:192]:   Data pin 10: GPIO8
[17:52:37][C][display.st7701s:192]:   Data pin 11: GPIO18
[17:52:37][C][display.st7701s:192]:   Data pin 12: GPIO17
[17:52:37][C][display.st7701s:192]:   Data pin 13: GPIO14
[17:52:37][C][display.st7701s:192]:   Data pin 14: GPIO13
[17:52:37][C][display.st7701s:192]:   Data pin 15: GPIO12
[17:52:37][C][display.st7701s:193]:   SPI Data rate: 1MHz
[17:52:37][C][gt911.touchscreen:127]: GT911 Touchscreen:
[17:52:37][C][gt911.touchscreen:128]:   Address: 0x5D
[17:52:37][C][gt911.touchscreen:129]:   Interrupt Pin: GPIO16
[17:52:38][W][component:237]: Component display took a long time for an operation (685 ms).
[17:52:38][W][component:238]: Components should block for at most 30 ms.

Any suggestions welcome.

What are you expecting? There doesn’t seem to be any code to write something to the screen (unless I mossed it).

Good point. I guess I was expecting the screen to come on white and not have any errors in the console.

I added bare bones LVGL:

lvgl:
  displays:
    - my_display
  pages:
    - id: main_page
      widgets:
        - label:
            align: CENTER
            text: 'Hello World!'

The screen lights up now, but the image isn’t right. It is white with a lot of parallel black lines across it.

Close, but not quite.

Looks like I have managed to get it mostly working.

Only thing I can’t seem to work out is how to adjust the duty cycle of the GPIO PWM that is used to drive the backlight - although removing the GPIO PWM and associated light from the config doesn’t seem to change anything :man_shrugging:

Init sequence was cribbed from the demo files available on the Waveshare Wiki: ESP32-S3-LCD-Driver-Board_demo.zip

ESP32-S3-LCD-Driver-Board with 2.8" round touchscreen config
esphome:
  name: esp32s3

esp32:
  board: esp32-s3-devkitc-1
  flash_size: 8MB
  variant: esp32s3
  framework:
    type: esp-idf
    sdkconfig_options:
      CONFIG_ESPTOOLPY_FLASHFREQ: "80MHz" # this is needed for flashing
      CONFIG_ESPTOOLPY_FLASHMODE_QIO: y
      CONFIG_FLASHMODE_QIO: y
      CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240: y
      CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ: '240'
      CONFIG_SPIRAM_RODATA: "y"

# Enable PSRAM on ESP32-S3
psram:
  mode: octal
  speed: 80MHz

# Enable logging
logger:

# I2C bus configuration
i2c:
  - id: i2c_bus
    sda: GPIO15
    scl: GPIO07
    scan: true

# PCA9554 GPIO extender for display reset
pca9554:
  - id: p_c_a
    address: 0x20
    i2c_id: i2c_bus

# SPI bus configuration
spi:
  - id: spi_lcd
    clk_pin: GPIO02
    mosi_pin: GPIO01
    interface: spi3


# Backlight PWM output and light component
output:
  - platform: ledc
    pin: GPIO6
    id: display_backlight_pwm
    frequency: 2000Hz
    channel: 0

light:
  - platform: monochromatic
    output: display_backlight_pwm
    id: display_backlight
    name: "Display Backlight"
    gamma_correct: 1

# ST7701S Display configuration (adjust the init_sequence to match your display)
display:
  - platform: st7701s
    id: my_display
    spi_id: spi_lcd
    spi_mode: MODE1
    # pclk_frequency: 30MHz
    color_order: bgr
    auto_clear_enabled: false
    dimensions:
      width: 480
      height: 480
    cs_pin: GPIO42
    reset_pin:
      pca9554: p_c_a
      number: 3
    de_pin: GPIO40
    hsync_pin: GPIO38
    vsync_pin: GPIO39
    pclk_pin: GPIO41
    init_sequence:
      - [ 0xFF, 0x77, 0x01, 0x00, 0x00, 0x13 ]
      - [ 0xEF, 0x08 ]
      - [ 0xFF, 0x77, 0x01, 0x00, 0x00, 0x10 ]
      - [ 0xC0, 0x3B, 0x00 ]
      - [ 0xC1, 0x10, 0x0C ]
      - [ 0xC2, 0x07, 0x0A ]
      - [ 0xC7, 0x00 ]
      - [ 0xCC, 0x10 ]
      - [ 0xCD, 0x08 ]
      - [ 0xB0, 0x05, 0x12, 0x98, 0x0E, 0x0F, 0x07, 0x07, 0x09, 0x09, 0x23, 0x05, 0x52, 0x0F, 0x67, 0x2C, 0x11 ]
      - [ 0xB1, 0x0B, 0x11, 0x97, 0x0C, 0x12, 0x06, 0x06, 0x08, 0x08, 0x22, 0x03, 0x51, 0x11, 0x66, 0x2B, 0x0F ]
      - [ 0xFF, 0x77, 0x01, 0x00, 0x00, 0x11 ]
      - [ 0xB0, 0x5D ]
      - [ 0xB1, 0x3E ]
      - [ 0xB2, 0x81 ]
      - [ 0xB3, 0x80 ]
      - [ 0xB5, 0x4E ]
      - [ 0xB7, 0x85 ]
      - [ 0xB8, 0x20 ]
      - [ 0xC1, 0x78 ]
      - [ 0xC2, 0x78 ]
      - [ 0xD0, 0x88 ]
      - [ 0xE0, 0x00, 0x00, 0x02 ]
      - [ 0xE1, 0x06, 0x30, 0x08, 0x30, 0x05, 0x30, 0x07, 0x30, 0x00, 0x33, 0x33 ]
      - [ 0xE2, 0x11, 0x11, 0x33, 0x33, 0xF4, 0x00, 0x00, 0x00, 0xF4, 0x00, 0x00, 0x00 ]
      - [ 0xE3, 0x00, 0x00, 0x11, 0x11 ]
      - [ 0xE4, 0x44, 0x44 ]
      - [ 0xE5, 0x0D, 0xF5, 0x30, 0xF0, 0x0F, 0xF7, 0x30, 0xF0, 0x09, 0xF1, 0x30, 0xF0, 0x0B, 0xF3, 0x30, 0xF0 ]
      - [ 0xE6, 0x00, 0x00, 0x11, 0x11 ]
      - [ 0xE7, 0x44, 0x44 ]
      - [ 0xE8, 0x0C, 0xF4, 0x30, 0xF0, 0x0E, 0xF6, 0x30, 0xF0, 0x08, 0xF0, 0x30, 0xF0, 0x0A, 0xF2, 0x30, 0xF0 ]
      - [ 0xE9, 0x36, 0x01 ]
      - [ 0xEB, 0x00, 0x01, 0xE4, 0xE4, 0x44, 0x88, 0x40 ]
      - [ 0xED, 0xFF, 0x10, 0xAF, 0x76, 0x54, 0x2B, 0xCF, 0xFF, 0xFF, 0xFC, 0xB2, 0x45, 0x67, 0xFA, 0x01, 0xFF ]
      - [ 0xEF, 0x08, 0x08, 0x08, 0x45, 0x3F, 0x54 ]
      - [ 0xFF, 0x77, 0x01, 0x00, 0x00, 0x00 ]
      - delay 120ms
      - [ 0x11 ]
      - delay 120ms
      - [ 0x3A, 0x66 ]
      - [ 0x36, 0x00 ]
      - [ 0x35, 0x00 ]
      - [ 0x20 ]
      - delay 120ms
      - [ 0x29 ]
    data_pins:
      red:
        - 46       #r1
        - 3        #r2
        - 8        #r3
        - 18       #r4
        - 17       #r5
      green:
        - 14       #g0
        - 13       #g1
        - 12       #g2
        - 11       #g3
        - 10       #g4
        - 9        #g5
      blue:
        - 5        #b1
        - 45       #b2
        - 48       #b3
        - 47       #b4
        - 21       #b5

# GT911 Touchscreen Controller
touchscreen:
  - platform: gt911
    id: my_touchscreen
    i2c_id: i2c_bus
    reset_pin:
      pca9554: p_c_a
      number: 1
    interrupt_pin: GPIO16
    on_update:
        - lvgl.label.update:
            id: coord_text
            text: !lambda return str_sprintf("Touch points:\n id=%d x=%d, y=%d", touches[0].id, touches[0].x, touches[0].y);
        - lambda: |-
            for (auto touch: touches)  {
                if (touch.state <= 2) {
                  ESP_LOGI("Touch points:", "id=%d x=%d, y=%d", touch.id, touch.x, touch.y);
                }
            }
    on_release:
      - if:
          condition: lvgl.is_paused
          then:
            - logger.log: "LVGL resuming"
            - lvgl.resume:
            - lvgl.widget.redraw:
            - light.turn_on: 
                id: display_backlight
                brightness: 50%

# LVGL Configuration
lvgl:
  - id: lvgl_display
    displays:
      - my_display
    widgets:
      - label:
          id: coord_text
          align: CENTER
          text_align: CENTER
          text: 'Touch the screen'
    on_idle:
      timeout: !lambda "return (id(display_timeout).state * 1000);"
      then:
        - logger.log: "LVGL is idle"
        - light.turn_off:
            id: display_backlight
        - lvgl.pause:

number:
  - platform: template
    name: LVGL Screen timeout
    optimistic: true
    id: display_timeout
    unit_of_measurement: "s"
    initial_value: 45
    restore_value: true
    min_value: 10
    max_value: 180
    step: 5
    mode: box