Waveshare ESP32-P4-WIFI6-Touch-LCD-7B working config

This is an extremely fast little screen. It seems like a good one for ESPHome. It has dual noise canceling microphones for Voice Assistant.

We now have a working config here

3 Likes

I haven’t spotted any post - looks like a serious bit of hardware.

If all you need is a 7 inch display with adequate resolution for 7 inch, this one has tons of support:

But if you can get the one you posted running that would be interesting…

That’s a nice screen but it does not have built in dimming for the backlight LED. This is a requirement for me. There are a lot of hardware mods to get it working.

There is a good thread on this form for that screen already

Let’s keep this thread about the Waveshare ESP32-P4-WIFI6-Touch-LCD-7B only so as not to create confusion.

The Discord team got it working!!

logger:
  hardware_uart: USB_SERIAL_JTAG

api:

ota:
  platform: esphome

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  
esphome:
  name: waveshare-esp32-p4-touch-lcd-7b
  friendly_name: waveshare-esp32-p4-wifi6-touch-lcd-7b

external_components:
  - source: github://pr#11886
    components: [mipi_dsi]
    refresh: 1h

esp32:
  variant: esp32p4
  flash_size: 16MB
  cpu_frequency: 360MHz
  framework:
    type: esp-idf
    advanced:
      enable_idf_experimental_features: true

esp32_hosted:
  variant: ESP32C6
  reset_pin: GPIO54
  cmd_pin: GPIO19
  clk_pin: GPIO18
  d0_pin: GPIO14
  d1_pin: GPIO15
  d2_pin: GPIO16
  d3_pin: GPIO17
  active_high: true

psram:
  mode: hex
  speed: 200MHz

preferences:
  flash_write_interval: 5min

esp_ldo:
  - voltage: 2.5V
    channel: 3

# -------------------------------------------
# Touchscreen gt911 i2c
# -------------------------------------------
i2c:
  - id: bus_a
    sda: GPIO07
    scl: GPIO08
    frequency: 400kHz

touchscreen:
  - platform: gt911
    id: my_touchscreen
    i2c_id: bus_a
    reset_pin: GPIO23
    update_interval: 50ms
    transform:
      swap_xy: false
      mirror_x: false
      mirror_y: false
      # on_touch:
            # - logger.log:
            # format: Touch at (%d, %d)
            # args: [touch.x, touch.y]
            # - lambda: |-
                # ESP_LOGI("cal", "x=%d, y=%d, x_raw=%d, y_raw=%0d",
                # touch.x,
                # touch.y,
                # touch.x_raw,
                # touch.y_raw
                # );

# -------------------------------------------
# Backlight
# -------------------------------------------
output:
  - platform: ledc
    id: gpio_backlight_pwm
    pin: GPIO32
    inverted: true
    frequency: 1000Hz

light:
  - platform: monochromatic
    output: gpio_backlight_pwm
    name: Display Backlight
    icon: mdi:lightbulb-on
    id: display_backlight
    restore_mode: ALWAYS_ON
    default_transition_length: 250ms

# -------------------------------------------
# Display
# -------------------------------------------
display:
  - platform: mipi_dsi
    id: my_display
    model: WAVESHARE-ESP32-P4-WIFI6-TOUCH-LCD-7B
    rotation: 180
    update_interval: never
    auto_clear_enabled: false
    dimensions:
      width: 1024
      height: 600

# LVGL settings required for this display
lvgl:
  buffer_size: 100%
  byte_order: little_endianweb_server:
  port: 80
  version: 3

logger:
  hardware_uart: USB_SERIAL_JTAG

api:

ota:
  platform: esphome

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  
esphome:
  name: waveshare-esp32-p4-touch-lcd-7b
  friendly_name: waveshare-esp32-p4-wifi6-touch-lcd-7b

external_components:
  - source: github://pr#11886
    components: [mipi_dsi]
    refresh: 1h

esp32:
  variant: esp32p4
  flash_size: 16MB
  cpu_frequency: 360MHz
  framework:
    type: esp-idf
    advanced:
      enable_idf_experimental_features: true

esp32_hosted:
  variant: ESP32C6
  reset_pin: GPIO54
  cmd_pin: GPIO19
  clk_pin: GPIO18
  d0_pin: GPIO14
  d1_pin: GPIO15
  d2_pin: GPIO16
  d3_pin: GPIO17
  active_high: true

psram:
  mode: hex
  speed: 200MHz

preferences:
  flash_write_interval: 5min

esp_ldo:
  - voltage: 2.5V
    channel: 3

# -------------------------------------------
# Touchscreen gt911 i2c
# -------------------------------------------
i2c:
  - id: bus_a
    sda: GPIO07
    scl: GPIO08
    frequency: 400kHz

touchscreen:
  - platform: gt911
    id: my_touchscreen
    i2c_id: bus_a
    reset_pin: GPIO23
    update_interval: 50ms
    transform:
      swap_xy: false
      mirror_x: false
      mirror_y: false
      # on_touch:
            # - logger.log:
            # format: Touch at (%d, %d)
            # args: [touch.x, touch.y]
            # - lambda: |-
                # ESP_LOGI("cal", "x=%d, y=%d, x_raw=%d, y_raw=%0d",
                # touch.x,
                # touch.y,
                # touch.x_raw,
                # touch.y_raw
                # );

# -------------------------------------------
# Backlight
# -------------------------------------------
output:
  - platform: ledc
    id: gpio_backlight_pwm
    pin: GPIO32
    inverted: true
    frequency: 1000Hz

light:
  - platform: monochromatic
    output: gpio_backlight_pwm
    name: Display Backlight
    icon: mdi:lightbulb-on
    id: display_backlight
    restore_mode: ALWAYS_ON
    default_transition_length: 250ms

# -------------------------------------------
# Display
# -------------------------------------------
display:
  - platform: mipi_dsi
    id: my_display
    model: WAVESHARE-ESP32-P4-WIFI6-TOUCH-LCD-7B
    rotation: 180
    update_interval: never
    auto_clear_enabled: false
    dimensions:
      width: 1024
      height: 600

# LVGL settings required for this display
lvgl:
  buffer_size: 100%
  byte_order: little_endian
2 Likes

I solved the problem. I just have to add “password: !secret ota_password in ota.yaml”

>  `ota:
>   - platform: web_server
>   - platform: esphome
>     id: ota_state
>     password: !secret ota_password`

Hi @andrew_NH Andrew i looked at your github and i tried several times to get your code up and running i always got the error message “NFO Package configuration completed successfully
INFO Package configuration completed successfully
INFO Successfully compiled program.
INFO Connecting to 192.168.178.53 port 3232…
INFO Connected to 192.168.178.53
INFO Uploading /data/build/waveshare-esp32-s3-touch-lcd-7/.pioenvs/waveshare-esp32-s3-touch-lcd-7/firmware.bin (1773856 bytes)
ERROR ESP requests password, but no password given!” but i did not set any password for ota but beside that there is an ota password in my secrets.yaml. so my question is where is the part where your configuration is asking for an ota password? Thanks in advance for an hint :smiley:

Nothing in my repo has a password set. But you can set one if you want. It is not required.

yes, i got it working more or less but it seems that the waveshare ESP 32 Touch LCD 7B (without P4 and wifi 6) in general a bit buggy. By the way, is there any documantatation how your modules are working and how to bring them up to an display to like the light buttons, the weather? I’ve tried but got nothing to work.

Yeah sure everything you need to know on how to set it all up is here in the readme file.

Start with the demo that matches your screen.

The code is very well documented so you can see the lines that control the buttons. Just change them to match a light you have in your HA setup.

example just change the entity_id to match a switch you have. That’s it.

        # Button 2 - Standard light button with state updates
        - button: !include
            file: esphome-modular-lvgl-buttons/buttons/switch_button.yaml
            vars:
              uid: button_2
              height: $button_height_single
              text: Button 2
              icon: $mdi_lightbulb
              entity_id: "switch.athom_smart_plug_v3_50ebc0_switch"
1 Like

I think over all the waveshare-esp32-s3-touch-lcd-7B is a very nice screen. It’s a bit slow to do swipes but other then that it works great!

1 Like

Hey, thank you so much for your response. I’ve now managed to get the display running, and the page with the buttons is also showing up.

What I haven’t quite figured out yet is how to integrate individual modules, for example for the weather. Do I need to add a new page in the main configuration under LVGL to display the weather data and weather icons? From what I understand, the pages are added in the main configuration under LVGL & Pages. I just don’t have any idea how I need to insert the individual weather YAML, or what the LVGL code should look like. Would it be possible to provide the code for the weather module? That way, I could understand the structure based on the example and adapt it to my configuration – that would really help me a lot. I’m unfortunately not yet deep enough into LVGL and ESP Home to understand it right away.

I know these are a lot of questions, but I hope you can give me a little nudge in the right direction.

When compiling the firmware, there were a few deprecation warnings regarding the select option.

config/esphome/esphome-modular-lvgl-buttons/common/theme_style.yaml: In lambda function:
/config/esphome/esphome-modular-lvgl-buttons/common/theme_style.yaml:57:29: warning: 'esphome::select::Select::state' is deprecated: Use current_option() instead of .state. Will be removed in 2026.5.0 [-Wdeprecated-declarations]
   57 |               - lambda: 'return id(current_theme).state == "Dark";'
      |                             ^~~~~
In file included from src/esphome/core/application.h:72,
                 from src/esphome/components/api/api_frame_helper.h:13,
                 from src/esphome/components/api/api_connection.h:5,
                 from src/esphome.h:3,
                 from src/main.cpp:3:
src/esphome/components/select/select.h:39:15: note: declared here
   39 |   std::string state{};
      |               ^~~~~
/config/esphome/esphome-modular-lvgl-buttons/common/theme_style.yaml:57:29: warning: 'esphome::select::Select::state' is deprecated: Use current_option() instead of .state. Will be removed in 2026.5.0 [-Wdeprecated-declarations]
   57 |               - lambda: 'return id(current_theme).state == "Dark";'
      |                             ^~~~~
src/esphome/components/select/select.h:39:15: note: declared here
   39 |   std::string state{};
      |               ^~~~~
/config/esphome/esphome-modular-lvgl-buttons/common/theme_style.yaml:57:29: warning: 'esphome::select::Select::state' is deprecated: Use current_option() instead of .state. Will be removed in 2026.5.0 [-Wdeprecated-declarations]
   57 |               - lambda: 'return id(current_theme).state == "Dark";'
      |                             ^~~~~
src/esphome/components/select/select.h:39:15: note: declared here
   39 |   std::string state{};
      |               ^~~~~... and so on

I have corrected these, as you can see below. The control also works with these changes; I have tested them.

Thank you very much in advance for your help

And here the Code snippets to prevent the deprecated messeages

in theme_style.yaml:

select:
  - platform: template
    name: "Current Theme"
    id: current_theme
    icon: mdi:theme-light-dark
    optimistic: true
    options:
      - Dark
      - Light
    initial_option: Dark
    on_value:
      then:
        - if:
            condition:
              - lambda: 'return id(current_theme).current_option == "Dark";'
            then:
              - lvgl.style.update:
                  id: page_style
                  bg_color: black
                  text_color: white
              - lvgl.widget.redraw
        - if:
            condition:
              - lambda: 'return id(current_theme).current_option == "Light";'
            then:
              - lvgl.style.update:
                  id: page_style
                  bg_color: white
                  text_color: black
              - lvgl.widget.redraw

and the part in backlignt_time.yaml:

lvgl:
  on_idle:
    timeout: $touchscreen_idle_timeout
    then:
      - logger.log: "LVGL is idle"
      - if:
          condition:
            - lambda: 'return id(brightness_mode).current_option() == "Day";'  
          then:
            - light.turn_on:
                id: display_backlight
                brightness: ${touchscreen_daytime_brightness}
      - if:
          condition:
            - lambda: 'return id(brightness_mode).current_option() == "Evening";'  
          then:
            - light.turn_on:
                id: display_backlight
                brightness: ${touchscreen_nighttime_brightness}
      - if:
          condition:
            - lambda: 'return id(brightness_mode).current_option() == "Night";'  
          then:
            - light.turn_off: display_backlight
            - lvgl.pause:
                show_snow: true

# Wake on screen touch
touchscreen:
    on_touch:
      then:
        - logger.log: "LVGL is active"
        - lvgl.resume
        - lvgl.widget.redraw
        - if:
            condition:
              - lambda: 'return id(brightness_mode).current_option() == "Day";'  
            then:
              - light.turn_on:
                  id: display_backlight
                  brightness: ${touchscreen_daytime_brightness}
        - if:
            condition:
              - lambda: 'return id(brightness_mode).current_option() == "Evening";'  
            then:
              - light.turn_on:
                  id: display_backlight
                  brightness: ${touchscreen_nighttime_brightness}
        - if:
            condition:
              - lambda: 'return id(brightness_mode).current_option() == "Night";'  
            then:
              - light.turn_on:
                  id: display_backlight
                  brightness: ${touchscreen_nighttime_brightness}

select:
  - platform: template
    name: "Brightness mode"
    id: brightness_mode
    icon: mdi:television-shimmer
    optimistic: true
    options:
      - Day
      - Evening
      - Night
    initial_option: Day
    on_value:
      then:
        - lvgl.resume
        - lvgl.widget.redraw
        - if:
            condition:
              - lambda: 'return id(brightness_mode).current_option() == "Day";'  
            then:
              - light.turn_on:
                  id: display_backlight
                  brightness: ${touchscreen_daytime_brightness}
        - if:
            condition:
              - lambda: 'return id(brightness_mode).current_option() == "Evening";'  
            then:
              - light.turn_on:
                  id: display_backlight
                  brightness: ${touchscreen_nighttime_brightness}
        - if:
            condition:
              - lambda: 'return id(brightness_mode).current_option() == "Night";'  
            then:
              - light.turn_on:
                  id: display_backlight
                  brightness: ${touchscreen_nighttime_brightness}

My code is quite flexible you can make a new page with the weather or add it to an existing page.

I have examples of both here

This one shows both the weather and a tide clock on the same screen.

Turning the Display Completely Off

I was annoyed with the display remaining on/dim when the backlight is turned off. Unfortunately, turning the display on/off by adjusting the voltage of the LDO caused the HSYNC/VSYNC (and other parameters) to be incorrect when turned back on.

The ESPHome mipi_dsi component does not implement the on/off commands for the display, and the handles necessary to send the MIPI DCS commands via lambdas are protected. So I created a class to access the mipi_dsi protected members by creating it in the same namespace and using inheritance.

This has been working for me without issue, so I thought I would share what I have so far.

Prerequisites: This requires a configured mipi_dsi display in your ESPHome configuration.

  1. Save the following code to a file (e.g., includes/mipi_dsi_ext.h).

    Contents of mipi_dsi_ext.h file
    #pragma once
    
    #include "esphome/components/mipi_dsi/mipi_dsi.h"
    #include "esp_lcd_panel_ops.h"
    #include "esp_lcd_panel_io.h"
    #include "esp_lcd_mipi_dsi.h"
    
    namespace esphome {
    namespace mipi_dsi {
    
    // Standard MIPI DCS commands
    #define MIPI_DCS_SLEEP_IN      0x10
    #define MIPI_DCS_SLEEP_OUT     0x11
    #define MIPI_DCS_DISPLAY_OFF   0x28
    #define MIPI_DCS_DISPLAY_ON    0x29
    
    // Access protected members by being in the same namespace and using inheritance
    class MIPI_DSI_Ext : public MIPI_DSI {
    public:
      // Static accessors that cast any MIPI_DSI* to access protected members
      static esp_lcd_panel_handle_t get_handle(MIPI_DSI *d) {
        return static_cast<MIPI_DSI_Ext*>(d)->handle_;
      }
      
      static esp_lcd_dsi_bus_handle_t get_bus_handle(MIPI_DSI *d) {
        return static_cast<MIPI_DSI_Ext*>(d)->bus_handle_;
      }
      
      static esp_lcd_panel_io_handle_t get_io_handle(MIPI_DSI *d) {
        return static_cast<MIPI_DSI_Ext*>(d)->io_handle_;
      }
      
      static const std::vector<uint8_t>& get_init_sequence(MIPI_DSI *d) {
        return static_cast<MIPI_DSI_Ext*>(d)->init_sequence_;
      }
      
      static GPIOPin* get_reset_pin(MIPI_DSI *d) {
        return static_cast<MIPI_DSI_Ext*>(d)->reset_pin_;
      }
    
      // Send a DCS command with no parameters
      static esp_err_t send_dcs_cmd(MIPI_DSI *d, uint8_t cmd) {
        auto io = static_cast<MIPI_DSI_Ext*>(d)->io_handle_;
        if (io == nullptr) {
          ESP_LOGE("mipi_dsi_ext", "IO handle is null");
          return ESP_ERR_INVALID_STATE;
        }
        return esp_lcd_panel_io_tx_param(io, cmd, nullptr, 0);
      }
    
      // Send a DCS command with parameters
      static esp_err_t send_dcs_cmd(MIPI_DSI *d, uint8_t cmd, const uint8_t *data, size_t len) {
        auto io = static_cast<MIPI_DSI_Ext*>(d)->io_handle_;
        if (io == nullptr) {
          ESP_LOGE("mipi_dsi_ext", "IO handle is null");
          return ESP_ERR_INVALID_STATE;
        }
        return esp_lcd_panel_io_tx_param(io, cmd, data, len);
      }
    
      // Turn display on/off using MIPI DCS commands
      static void set_display_on(MIPI_DSI *d, bool on) {
        esp_err_t ret;
        if (on) {
          ret = send_dcs_cmd(d, MIPI_DCS_SLEEP_OUT);
          if (ret != ESP_OK) {
            ESP_LOGE("mipi_dsi_ext", "Sleep out failed: %s", esp_err_to_name(ret));
            return;
          }
          vTaskDelay(pdMS_TO_TICKS(120));  // Wait for sleep out
          ret = send_dcs_cmd(d, MIPI_DCS_DISPLAY_ON);
          if (ret != ESP_OK) {
            ESP_LOGE("mipi_dsi_ext", "Display on failed: %s", esp_err_to_name(ret));
            return;
          }
          ESP_LOGI("mipi_dsi_ext", "Display ON");
        } else {
          ret = send_dcs_cmd(d, MIPI_DCS_DISPLAY_OFF);
          if (ret != ESP_OK) {
            ESP_LOGE("mipi_dsi_ext", "Display off failed: %s", esp_err_to_name(ret));
            return;
          }
          vTaskDelay(pdMS_TO_TICKS(20));
          ret = send_dcs_cmd(d, MIPI_DCS_SLEEP_IN);
          if (ret != ESP_OK) {
            ESP_LOGE("mipi_dsi_ext", "Sleep in failed: %s", esp_err_to_name(ret));
            return;
          }
          ESP_LOGI("mipi_dsi_ext", "Display OFF (sleep mode)");
        }
      }
    
      // Hardware reset using the reset pin
      static void hw_reset(MIPI_DSI *d) {
        auto *ext = static_cast<MIPI_DSI_Ext*>(d);
        auto reset_pin = ext->reset_pin_;
        
        if (reset_pin == nullptr) {
          ESP_LOGW("mipi_dsi_ext", "No reset pin configured");
          return;
        }
        
        ESP_LOGI("mipi_dsi_ext", "Performing hardware reset");
        reset_pin->digital_write(false);
        vTaskDelay(pdMS_TO_TICKS(10));
        reset_pin->digital_write(true);
        vTaskDelay(pdMS_TO_TICKS(120));  // Wait for panel to initialize
      }
    
      // Send the stored init sequence
      static void send_init_sequence(MIPI_DSI *d) {
        auto *ext = static_cast<MIPI_DSI_Ext*>(d);
        auto io = ext->io_handle_;
        
        if (io == nullptr) {
          ESP_LOGE("mipi_dsi_ext", "IO handle is null");
          return;
        }
        
        const auto &seq = ext->init_sequence_;
        ESP_LOGI("mipi_dsi_ext", "Sending init sequence (%d bytes)", seq.size());
        
        size_t i = 0;
        while (i < seq.size()) {
          uint8_t cmd = seq[i++];
          if (i >= seq.size()) break;
          uint8_t len = seq[i++];
          
          if (len == 0xFF) {
            // Delay marker
            if (i >= seq.size()) break;
            uint8_t delay_ms = seq[i++];
            ESP_LOGD("mipi_dsi_ext", "Delay %d ms", delay_ms);
            vTaskDelay(pdMS_TO_TICKS(delay_ms));
          } else {
            ESP_LOGD("mipi_dsi_ext", "CMD 0x%02X, len=%d", cmd, len);
            if (i + len > seq.size()) break;
            esp_lcd_panel_io_tx_param(io, cmd, len > 0 ? &seq[i] : nullptr, len);
            i += len;
          }
        }
        ESP_LOGI("mipi_dsi_ext", "Init sequence complete");
      }
    
      // Full reinitialize: HW reset + init sequence + display on
      static void reinit_panel(MIPI_DSI *d) {
        ESP_LOGI("mipi_dsi_ext", "Reinitializing panel...");
        hw_reset(d);
        send_init_sequence(d);
        set_display_on(d, true);
        ESP_LOGI("mipi_dsi_ext", "Panel reinitialized");
      }
    };
    
    }  // namespace mipi_dsi
    }  // namespace esphome
    
  2. Include the file in your configuration:

    esphome:
      includes:
        - includes/mipi_dsi_ext.h  # or your chosen path
    
  3. Add the set_display_on functions to the on_turn_on/off triggers for your backlight (replace my_display with your display’s ID)

    light:
      - platform: monochromatic
        output: backlight_pwm
        name: "Display Backlight"
        id: light_display_backlight
        gamma_correct: 1.0
        restore_mode: ALWAYS_ON
        default_transition_length: 1s
        on_turn_on:
          - lambda: |-
              mipi_dsi::MIPI_DSI_Ext::set_display_on(id(my_display), true);
        on_turn_off:
          - lambda: |-
              mipi_dsi::MIPI_DSI_Ext::set_display_on(id(my_display), false);
    
  4. (Optional) Add the off command to trigger before OTA updates so the screen doesn’t flicker during the process:

    ota:
      - platform: esphome
        on_begin:
          then:
            - lambda: |-
                mipi_dsi::MIPI_DSI_Ext::set_display_on(id(my_display), false);
    
1 Like

Sorry for the delay to answere but i was a bit busy because of my job.
Really thank you more then a million times. That pushed me in the right direction and i got it working. Again thanks a lot for your support and your hard work which you have done for the esphome-modular-lvgl-buttons repo.

That is really nice. Do you want to add a feature request to ESPHome? I think this would be a great upgrade.

Awesome - got it working thanks to this; I changed the board to variant, the same as I did with the first gen Waveshare touch 7 inch.

Has anyone got the display working with RGB rather than RGB565? I’m not sure how this works - but looking at Add WAVESHARE-ESP32-P4-WIFI6-TOUCH-LCD-7B driver the init sequence seems to be a lot smaller than the others and I wondered if that could be preventing it from using RGB.

I did some digging and found the EK79007 datasheet from the EK79007 readme which has a decent description of how to use the init sequence.

Probably barking up the wrong tree and I’ve missed something obvious (not the first time lol)

A big gotcha for me was that the image: component doesn’t inherit the LVGLs endiness.

Edit: The espressif page for the device I believe is: waveshare/esp32_p4_wifi6_touch_lcd_7b • v1.0.2 • ESP Component Registry but @andrew_NH to confirm this is the exact version etc.

Second Edit: I’ve successfully built and flashed the LVGL demo from the waveshare github repo to see if that reveals anything.

Thanks,
Charlie

Just an update - I was completely barking up the wrong tree with this.

24bit mode is only supported in lvgl@9 - and the settings for using the display are:

    pixel_mode: 24bit
    color_depth: 24

Thanks,
Charlie

By any chance, did anyone manage to make the audio work?

I got to a point where I hear something, but the sound is heavily distorted.

…and no mic.

What I have seen from the schematics is that the codec and mic are on the same i2c bus.

Found a repo that extends the audio module of esphome with pipelines and, according to the docs… It /should/ work… But did not get that far.

32bit should work with LVGL 8.4 (I played with it before on other devices), but ESPHome’s mipi_dsp driver only supports 16 or 24 at the moment.

1 Like

Have you tried the audio configuration from here?

I just tried the Media Player from Home Assistant using those settings and it seemed to work well. The mic was also receiving data, but I haven’t done much testing with it yet.

RGB888 is new for lvgl 9: Changes in master (v9 development) · Issue #4011 · lvgl/lvgl · GitHub

Also from https://forum.lvgl.io/t/color-depth-24-is-deprecated/10618/2:

Actually a 24 bit color depth was never truly supported. Earlier it meant (X)RGB8888.

However, in v9 native rendering to RGB888 format will be supported.

Not sure if it made it into [lvgl] Migrate to library v9.4.0 by clydebarrow · Pull Request #12312 · esphome/esphome · GitHub or not.

Also - doing a quick scan, there’s 32bit for one display driver for lvgl.