ESP32-S3 3.5-inch capacitive touch IPS module 320 * 480

When working with an image i get the below error

/config/esphome/esphome-web-220778.yaml: In lambda function:
/config/esphome/esphome-web-220778.yaml:165:46: error: 'class esphome::image::Image' has no member named 'get_lv_img_dsc'
               src: boot_logo
                                              ^             
Compiling .pioenvs/esphome-web-220778/libb78/lvgl/core/lv_disp.o
*** [.pioenvs/esphome-web-220778/src/main.o] Error 1

i use the below code

substitutions:
  name: esphome-web-220778
  friendly_name: WEKKER

esphome:
  name: ${name}
  friendly_name: ${friendly_name}
  min_version: 2024.6.0
  name_add_mac_suffix: false
  platformio_options:
    build_flags: "-DBOARD_HAS_PSRAM"
    board_build.arduino.memory_type: qio_opi

  on_boot:
    - delay: 5s
    - lvgl.widget.hide: boot_screen

  project:
    name: esphome.web
    version: dev

psram:
  mode: octal
  speed: 120MHz

esp32:
  board: esp32-s3-devkitc-1
  framework:
    type: esp-idf
    sdkconfig_options:
      CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240: "y"
      CONFIG_ESP32S3_DATA_CACHE_64KB: "y"
      CONFIG_SPIRAM_FETCH_INSTRUCTIONS: y
      CONFIG_SPIRAM_RODATA: y

# Enable logging
logger:
  logs:
    component: DEBUG
# Enable Home Assistant API
api:

# Allow Over-The-Air updates
ota:
- platform: esphome

# Allow provisioning Wi-Fi via serial
improv_serial:

wifi:
  # Set up a wifi access point
  ap: {}

# In combination with the `ap` this allows the user
# to provision wifi credentials to the device via WiFi AP.
captive_portal:

# To have a "next url" for improv serial
web_server:


external_components:
  - source: github://clydebarrow/esphome@lvgl-rounding
    components: [lvgl]
  - source: github://ageurtse/esphome-components
    components: [ axs15231 ]

spi:
  id: display_qspi
  type: quad
  clk_pin: 47
  data_pins: [21,48,40,39]

time:
  - platform: homeassistant
    id: homeassistant_time

# Define a PWM output on the ESP32
output:
  - platform: ledc
    pin: 1
    id: gpio_backlight_pwm

# Define a monochromatic, dimmable light for the backlight
light:
  - platform: monochromatic
    output: gpio_backlight_pwm
    name: "Display Backlight"
    id: back_light
    restore_mode: ALWAYS_ON

sensor:
  - platform: homeassistant
    id: light_brightness
    entity_id: light.your_dimmer
    attribute: brightness
    on_value:
      - lvgl.slider.update:
          id: dimmer_slider
          value: !lambda return x;
i2c:
  sda: 4
  scl: 8
  id: touchscreen_bus

image:
  - file: https://esphome.io/_static/favicon-512x512.png
    id: boot_logo
    resize: 200x200
    type: RGB565
    use_transparency: true

display:
  - platform: qspi_amoled
    id: main_display
    model: RM67162
    data_rate: 40MHz
    dimensions:
      height: 480
      width: 320
    cs_pin:
      number: 45
      ignore_strapping_warning: true
    auto_clear_enabled: false
    update_interval: never

touchscreen:
  - platform: axs15231
    id: main_touch
    display: main_display
    i2c_id: touchscreen_bus

lvgl:
  draw_from_origin: true
  draw_rounding: 8
  widgets:
    - slider:
        id: dimmer_slider
        x: 20
        y: 50
        width: 30
        height: 220
        pad_all: 8
        min_value: 0
        max_value: 255
        on_release:
          - homeassistant.action:
              action: light.turn_on
              data:
                entity_id: light.your_dimmer
                brightness: !lambda return int(x);
    - obj:
        id: boot_screen
        x: 0
        y: 0
        width: 100%
        height: 100%
        bg_color: 0xffffff
        bg_opa: COVER
        radius: 0
        pad_all: 0
        border_width: 0
        widgets:
          - image:
              src: boot_logo
          - spinner:
              align: CENTER
              y: 95
              height: 50
              width: 50
              spin_time: 1s
              arc_length: 60deg
              arc_width: 8
              indicator:
                arc_color: 0x18bcf2
                arc_width: 8
        on_press:
          - lvgl.widget.hide: boot_screen





font:
  - file: "fonts/Seven Segment.ttf"
    id: font_std
    size: 40
    glyphs: "!\"%()+=,-_.:°0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz/\\[]|&@#'"

Use the dev or beta ESPHome, or add image to the external component where lvgl is defined. There are changes to the image component not yet released, but in beta.

Here is an updated config, with touchscreen support. There are or will be PRs for the external components in ESPHome.

This works around the limitations of the chip and still delivers reasonable performance. As written it will build against the ESPHome release version.

The display can’t be rotated to landscape with transform as the chip does not support axis swapping, but software rotation as shown below works - there is some performance impact, but it’s not too bad as the screen is pretty fast to begin with.

For portrait mode with no rotation, remove the rotation option from the display and the transform from the touchscreen.

substitutions:
  friendly_name: JC3248W535

esphome:
  name: jc3248w535
  friendly_name: ${friendly_name]
  platformio_options:
    build_unflags: -Werror=all
    board_build.flash_mode: dio

esp32:
  board: esp32-s3-devkitc-1
  framework:
    type: esp-idf
    sdkconfig_options:
      CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240: "y"
      CONFIG_ESP32S3_DATA_CACHE_64KB: "y"
      CONFIG_SPIRAM_FETCH_INSTRUCTIONS: y
      CONFIG_SPIRAM_RODATA: y

external_components:
  - source: github://clydebarrow/esphome@lvgl-rounding
    components: [lvgl, image]
  - source: github://clydebarrow/esphome@qspi
    components: [qspi_dbi]
  - source: github://pr#7592
    components: [axs15231]
  - source: github://pr#7591
    components: [touchscreen]

psram:
  mode: octal
  speed: 80MHz

spi:
  id: display_qspi
  type: quad
  clk_pin: 47
  data_pins: [21,48,40,39]

power_supply:
  id: backlight_id
  pin: 1
  enable_on_boot: true

display:
  - platform: qspi_dbi
    model: CUSTOM
    data_rate: 40MHz
    dimensions:
      height: 480
      width: 320
    transform:
      mirror_x: false
      mirror_y: false
    cs_pin:
      number: 45
      ignore_strapping_warning: true
    auto_clear_enabled: false
    update_interval: never
    draw_from_origin: true
    rotation: 270
    init_sequence:

i2c:
  sda: 4
  scl: 8

touchscreen:
  platform: axs15231
  transform:
    swap_xy: true
    mirror_x: false
    mirror_y: true

lvgl:

Looks like there is somthing broken, i can’t get it to compile.

i’m using the normal ESPhome, when using beta or dev, i get even more error’s

i get a error

INFO ESPHome 2024.9.2
INFO Reading configuration /config/esphome/esphome-web-220778.yaml...
INFO Unable to import component image: No module named 'puremagic'
Failed config

image: [source /config/esphome/esphome-web-220778.yaml:111]
  
  Component not found: image.
  - file: https://esphome.io/_static/favicon-512x512.png
    id: boot_logo
    resize: 200x200
    type: RGB565
    use_transparency: True

when removing image i get a error on component LVGL is missing.

pip install puremagic

Good morning, good afternoon or good evening guys.
I know your going down a different route now with this but I’ve been ploughing on with the original drivers. I believe I’ve fixed the redraw, rotation and touchpad issues so everthing look good to me. I’ve also created a github account :thinking: and uploaded it there.
The drivers and an example are included…

LVGL example

The drivers work with my standard graphics example as well.

The error ‘[draw_from_origin] is an invalid option for [lvgl]’ has returned again although this time it prevents the complilation, previously it compiled and ran fine.
Must be doing something wrong :worried:

I removed that option from LVGL and put it into the driver instead, which is where it belongs. See my earlier post.

Woukd you include the link ?

oops, sorry… :grimacing:

1 Like

The partial update problem is solved, performance is now normal, even with software rotation (swapping axes in hardware does not work.)

Touchscreen supported with PR:

Here is my test project, it was realy fun to do, i make use of the alterd driver from DerHam. later i will try to see how far i will get with the alterd driver from clydebarrow.

https://github.com/ageurtse/esphome-clock

That looks good Arnold…

2024-10-13 13.23.37

I was going to look into how to dim the backlight, you’ve done that as well :+1:

Being new to this I’ve been learning github, c++, yaml, HA and sometimes it starts doing my head in :laughing:
I stil not quite sure what to do with… pip install puremagic ?

me to, but when googling on puremagic, it has somthing to do with the #number’s from github.

to me it looks it only works on the dev version from ESPhome.
maybee clydebarrow, could explain us a bit what he did, and how to use it, so we can learn a bit from it.

Can’t get your newly updates changes to work.
I don’t know what i’m doing wrond.
where and why do i need to install puremagic.

sorry, i’m not as good with github and programming, still learning and trying to understand.

i have includes your external components but the compilers give this error, can’t make anything about it.

INFO ESPHome 2024.11.0-dev
INFO Reading configuration /config/esphome/esphome-web-220778.yaml...
ERROR Unable to import component qspi_dbi.display:
Traceback (most recent call last):
  File "/esphome/esphome/loader.py", line 177, in _lookup_module
    module = importlib.import_module(f"esphome.components.{domain}")
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap>", line 1206, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1178, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1149, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 690, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 940, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/data/external_components/534b7061/esphome/components/qspi_dbi/display.py", line 5, in <module>
    from esphome.const import (
ImportError: cannot import name 'CONF_INIT_SEQUENCE' from 'esphome.const' (/esphome/esphome/const.py)
Failed config

display.qspi_dbi: [source /config/esphome/esphome-web-220778.yaml:316]
  
  Platform not found: 'display.qspi_dbi'.
  platform: qspi_dbi
  model: AXS15231
  data_rate: 40MHz
  dimensions: 
    height: 480
    width: 320
  cs_pin: 
    number: 45
    ignore_strapping_warning: True
  auto_clear_enabled: False
  update_interval: never
  rotation: 270

That’s because there are core changes, which the external component does not bring in. You’ll need to wait at least until it’s merged into the Dev branch, then you can use the ESPHome Dev add-on in HA. Might be a week or two, outside my control.

The alternative right now is to clone my repo and use the CLI, but it sounds like that’s not part of your skill set ATM.

I will wait a while and try it in a week or 2
The cli is one step to much at the moment.

a short update, downloaded the latest dev version off esphome 2014.11.0-dev
and running the code below works out of the box, no external components needed.

@clydebarrow thanks for the work.
@derham, thanks for the first setup with a working example
buglloc, for the very first driver

the below clock, display’s a simple clock.
the font can be download from here. https://github.com/ageurtse/esphome-clock

################################################################################
# Substitution Variables
################################################################################
substitutions:
  device_internal_name: wekker
  device_wifi_name: esphome-wekker
  device_friendly_name: WEKKER
  device_ip_address: 192.168.0.128
  device_sampling_time: 30s

################################################################################
# Globals
################################################################################
globals: 
  - id: wifi_connection
    type: bool
    restore_value: no
    initial_value: "false"

  - id: bgcolor
    type: Color
    initial_value: "Color::random_color()"

  - id: my_selection
    type: int
    initial_value: "0"
################################################################################
# Board Configuration
################################################################################
esphome:
  name: ${device_internal_name}
  friendly_name: ${device_friendly_name}
  platformio_options:
    build_flags: "-DBOARD_HAS_PSRAM"
    board_build.arduino.memory_type: qio_opi
  on_boot:
      priority: -100
      then:
      - lvgl.widget.hide: boot_screen

psram:
  mode: octal
  speed: 120MHz

esp32:
  board: esp32s3box
  variant: ESP32S3
  flash_size: 8MB
  framework:
    type: esp-idf

################################################################################
# Bluethooth tracker
################################################################################
bluetooth_proxy:

################################################################################
# Enable logging
################################################################################
logger:
  logs:
    component: NONE

################################################################################
# Enable Home Assistant API
################################################################################
api:
  reboot_timeout: 0s

################################################################################
# OTA
################################################################################
ota:
- platform: esphome

################################################################################
# WiFi
################################################################################
wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  output_power: 10
  use_address: ${device_ip_address}
  on_connect:
    - lvgl.label.update:
        id: esphome_ip_label
        text:
          format: "IP Address: %s"
          args: [ 'id(ip_address).state.c_str()' ] 
        
  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: ${device_wifi_name}
    password: !secret esphome_pwd

captive_portal:

###############################################################################
# Web Server
################################################################################
web_server:
  port: 80
  version: 2
  include_internal: true

################################################################################
# Output
################################################################################
output:
  - platform: ledc
    pin:
      number: GPIO01
    id: lcdbacklight


################################################################################
# Switch
################################################################################
switch:
  - platform: restart
    name: "Restart"
    id: device_restart
  
  - platform: safe_mode
    name: Use Safe Mode
    id: device_safe_mode

################################################################################
# Light
################################################################################
light:
  - platform: monochromatic
    output: lcdbacklight
    name: "Display Backlight"
    id: back_light
    restore_mode: ALWAYS_ON

################################################################################
# Sensors
################################################################################
sensor:
  - platform: homeassistant
    id: light_brightness
    entity_id: light.your_dimmer
    attribute: brightness

  # WiFi
  - platform: wifi_signal
    name: "WiFi Signal Sensor"
    id: ${device_internal_name}_wifi_signal_sensor
    update_interval: ${device_sampling_time}

  # Uptime
  - platform: uptime
    name: "Uptime Sensor"
    id: ${device_internal_name}_uptime_sensor
    update_interval: ${device_sampling_time}
    internal: true
    on_raw_value:
      then:
        - text_sensor.template.publish:
            id: ${device_internal_name}_uptime_human
            state: !lambda |-
              int seconds = round(id(${device_internal_name}_uptime_sensor).raw_state);
              int days = seconds / (24 * 3600);
              seconds = seconds % (24 * 3600);
              int hours = seconds / 3600;
              seconds = seconds % 3600;
              int minutes = seconds /  60;
              seconds = seconds % 60;
              return (
                (days ? to_string(days) + "d " : "") +
                (hours ? to_string(hours) + "h " : "") +
                (minutes ? to_string(minutes) + "m " : "") +
                (to_string(seconds) + "s")
              ).c_str();

################################################################################
# Interval
################################################################################
interval:
  - interval: 10s
    then:
      - if:
          condition:
            wifi.connected:
          then:
            - lambda: |-
                id(wifi_connection) = true;

          else:
            - lambda: |-
                id(wifi_connection) = false;

time:
  - platform: homeassistant
    id: esptime
    on_time:
      - minutes: "*"
        then:
          - lvgl.label.update:
              id: clock_time
              text:
                format: "%s"
                args: [ 'id(current_time).state.c_str()' ] 

################################################################################
# Text Sensors
################################################################################
text_sensor:
  - platform: wifi_info
    ip_address:
      id: ip_address
      name: "IP Address"
      entity_category: diagnostic
    ssid:
      id: ssid
      name: "Connected SSID"
      entity_category: diagnostic
    mac_address:
      id: mac_address
      name: "Mac Address"
      entity_category: diagnostic


  - platform: version
    id: esphome_version
    name: "ESPHome Version"
    hide_timestamp: true
    on_value:
      then:
        - lvgl.label.update:
            id: esphome_version_label
            text: !lambda |-
              return ("ESPHome Version: " + id(esphome_version).state).c_str();

  - platform: template
    name: "Current Time"
    id: current_time
    update_interval: 60s
    lambda: return  id(esptime).now().strftime("%H:%M");

  #-------------------------------------------------------------------------------
  # Custom Text sensors
  #-------------------------------------------------------------------------------
  - platform: template
    name: Uptime Human Readable
    id: ${device_internal_name}_uptime_human
    icon: mdi:clock-start

################################################################################
# SPI and I2C
################################################################################
spi:
  id: display_qspi
  type: quad
  clk_pin: 47
  data_pins: [21,48,40,39]

i2c:
  sda: 4
  scl: 8
  id: touchscreen_bus
  frequency: 800khz

################################################################################
# Display and Touchscreen
################################################################################
display:
  - platform: qspi_dbi
    model: CUSTOM
    data_rate: 40MHz
    dimensions:
      height: 480
      width: 320
    transform:
      mirror_x: false
      mirror_y: false
    cs_pin:
      number: 45
      ignore_strapping_warning: true
    auto_clear_enabled: false
    update_interval: never
    draw_from_origin: true
    rotation: 270
    init_sequence:

touchscreen:
  platform: axs15231
  transform:
    swap_xy: true
    mirror_x: false
    mirror_y: true

################################################################################
# Image, Fonts and color
################################################################################
image:
  - file: https://esphome.io/_static/favicon-512x512.png
    id: boot_logo
    resize: 200x200
    type: RGB565
    use_transparency: true
    
color:
  - id: my_red
    red: 100%
    green: 0%
    blue: 0%
  - id: my_pink
    red: 100%
    green: 10%
    blue: 40%
  - id: my_yellow
    red: 100%
    green: 100%
    blue: 0%
  - id: my_green
    red: 0%
    green: 100%
    blue: 0%
  - id: my_blue
    red: 0%
    green: 0%
    blue: 100%
  - id: my_gray
    red: 50%
    green: 50%
    blue: 50%
  - id: my_white
    red: 100%
    green: 100%
    blue: 100%
  - id: my_black
    red: 0%
    green: 0%
    blue: 0%

font:
  - file: "fonts/digital-dismay.regular.otf"
    id: font_std
    size: 215
    glyphs: ":0123456789"
  - file: "arial.ttf"
    id: arial_98
    size: 98
  - file: "arial.ttf"
    id: arial_96
    size: 96
  - file: "arial.ttf"
    id: arial_134
    size: 134
  - file: "arial.ttf"
    id: arial_128
    size: 128
  - file: "arial.ttf"
    id: arial_48
    size: 48
  - file: "arial.ttf"
    id: arial_36
    size: 36
  - file: "arial.ttf"
    id: arial_24
    size: 24
  - file: "arial.ttf"
    id: arial_12
    size: 12
  - file: "gfonts://Roboto"
    id: roboto10
    size: 10
    bpp: 4

################################################################################
# LVGL
################################################################################
lvgl:
  bg_color: my_black
  text_font: arial_12
  width: 320
  height: 480
  align: center
  id: screen
  style_definitions:
    - id: date_style
      align: center
      text_color: 0x000000
      bg_opa: cover
      radius: 4
      pad_all: 2

  widgets:
    - label:
        id: clock_time
        text_font: font_std
        text_color: my_red
        align: CENTER
        text: "99:99"
        
        on_press:
          - lvgl.widget.show: boot_screen

    - obj:
        id: boot_screen
        x: 0
        y: 0
        width: 100%
        height: 100%
        bg_color: 0xffffff
        bg_opa: COVER
        radius: 0
        pad_all: 0
        border_width: 0
        widgets:
          - image:
              src: boot_logo
          - spinner:
              align: CENTER
              y: 95
              height: 50
              width: 50
              spin_time: 1s
              arc_length: 60deg
              arc_width: 8
              indicator:
                arc_color: 0x18bcf2
                arc_width: 8
          - label:
              x: 200
              y: 50  
              text:
                format: "MAC Address: %s"
                args: [ 'id(mac_address).state.c_str()' ]                
          - label:
              id: esphome_ip_label
              x: 200
              y: 65  
              text:
                format: "IP Address: %s"
                args: [ 'id(ip_address).state.c_str()' ]                
          - label:
              x: 200
              y: 110  
              id: esphome_version_label           
              text:
                format: "ESPHome Version: %s"
                args: [ 'id(esphome_version).state.c_str()' ]
        on_press:
          - lvgl.widget.hide: boot_screen

Well, 1 step forward and 2 back! My demo works fine under ESPHome 2024.9.2 but something has changed at 2024.10.0 - now the touchpad coordinates are out so the buttons don’t press in the right location anymore - graphics are fine though.
You can wind back the ESPHome version using this add-on…

I tried 2024.11 but was getting strange results there as well so I think I’ll keep to 9.2, for this device, until the full release version is available.

UPDATE:
My demo project is now working fine in all rotations using Clyde’s lastest axs15231 driver in Esphome Dev env. Great, thanks for the work on this.

1 Like