GUITION 4" 480x480 ESP32-S3-4848S040 Smart Display with LVGL

I like your idea of adding a OTA progress display. I copied your code from some weeks ago into my custom yaml. But it seems when OTA is active I see the progress text label flickering all over the screen in multiple smaller boxes. I added the progress bar label in the top_layer :

##########Top layer####################
  top_layer:
    widgets:
      # OTA popup widget
      - obj:
          id: ota_widget
          hidden: true
          width: 480
          height: 450
          align: center
          clickable: false
          bg_color: color_slate_blue_gray
          #border_opa: transp
          #shadow_opa: transp
          radius: 0
          widgets:
            - label:
                id: ota_label_primary
                y: -50
                align: center
                text_font: nunito_20
                text_color: color_misty_blue
                text: "Starting OTA!"
            - label:
                id: ota_label_secondary
                y: -20
                align: center
                text_font: nunito_18
                text_color: color_misty_blue
                text: "Wait..."
            - bar:
                id: ota_bar_value
                y: 20
                width: 300
                value: 0
                align: center
                min_value: 0
                max_value: 100
                animated: true
                bg_color: color_steel_blue
                indicator:
                  bg_color: color_misty_blue

      - buttonmatrix:
          align: bottom_mid
          styles: header_footer
          pad_all: 0
          outline_width: 0
          id: top_layer
          items:
            styles: header_footer
          rows:
            - buttons:
              - id: page_prev
                text: "\uF053"
                on_press:
                  then:
                    lvgl.page.previous:
              - id: page_home
                text: "\uF015"
                on_press:
                  then:
                    lvgl.page.show: main_page
              - id: page_next
                text: "\uF054"
                on_press:
                  then:
                    lvgl.page.next:

##########Pages####################
  page_wrap: true
  pages:
##########Page 1: main####################
    - id: main_page
      skip: false
      layout:
        type: grid
        grid_rows: [25%, 25%, 25%, 25%]
        grid_columns: [33%, 33%, 33%]
        pad_row: 1
        pad_column: 1
      width: 100%
      bg_color: 0x000000
      bg_opa: cover
      pad_all: 5
      widgets:
        - obj:
            checkable: true
            id: lv_button_1
            widgets:
              - label:
                  text_font: light32
                  align: top_left
                  text: $light_recessed
                  id: lv_button_1_icon
              - label:
                  align: bottom_mid
                  text: "Iris bed"
                  long_mode: dot
            on_short_click:
              - homeassistant.service:
                  service: light.toggle
                  data:
                    entity_id: light.hue_iris_1
        - obj:
            checkable: true
            id: lv_button_2
            widgets:
              - label:
                  text_font: light32
                  align: top_mid
                  text: $string_lights
                  id: lv_button_2_icon
              - label:
                  align: bottom_mid
                  text: "Nachthal"
                  long_mode: dot              
            on_short_click:
              - homeassistant.service:
                  service: light.toggle
                  data:
                    entity_id: light.nachthal
        - obj:
            checkable: true
            id: lv_button_3
            widgets:
              - label:
                  text_font: light32
                  align: top_right
                  text: $floor_lamp
                  id: lv_button_3_icon
              - label:
                  align: bottom_mid
                  text: "Keuken dimmer"
                  long_mode: dot
            on_short_click:
              - homeassistant.service:
                  service: light.toggle
                  data:
                    entity_id: light.dimmer_keuken_current_value
        - obj:
            checkable: true
            id: lv_button_4
            widgets:
              - label:
                  text_font: light32
                  align: top_left
                  text: $lightbulb
                  id: lv_button_4_icon
              - label:
                  align: bottom_mid
                  text: "Patio wit"
                  long_mode: dot
            on_short_click:
              - homeassistant.service:
                  service: light.toggle
                  data:
                    entity_id: light.patio_vast_wit
        - obj:
            checkable: true
            id: lv_button_5
            widgets:
              - label:
                  text_font: light32
                  align: top_mid
                  text: $lightbulb
                  id: lv_button_5_icon
              - label:
                  align: bottom_mid
                  text: "Kachel wit"
                  long_mode: dot  
            on_short_click:
              - homeassistant.service:
                  service: light.toggle
                  data:
                    entity_id: light.kachel_vast_wit
        - obj:
            checkable: true
            id: lv_button_6
            widgets:
              - label:
                  text_font: light32
                  align: top_right
                  text: $clock
                  id: lv_button_6_icon
              - label:
                  align: bottom_mid
                  text: "Lichtboom"
                  long_mode: dot                             
            on_short_click:
              - homeassistant.service:
                  service: switch.toggle
                  data:
                    entity_id: stopcontact_lichtboom_current_value
        - obj:
            checkable: true
            id: lv_button_7
            widgets:
              - label:
                  text_font: light32
                  align: top_left
                  text: $lightbulb
                  id: lv_button_7_icon
              - label:
                  align: bottom_left
                  text: "Salon kleur"
                  long_mode: dot
            on_short_click:
              - homeassistant.service:
                  service: light.toggle
                  data:
                    entity_id: light.salon_kleur
        - obj:
            checkable: true
            id: lv_button_8
            widgets:
              - label:
                  text_font: light32
                  align: top_left
                  text: $lightbulb
                  id: lv_button_8_icon
              - label:
                  align: bottom_left
                  text: "Terras wit"
                  long_mode: dot              
            on_short_click:
              - homeassistant.service:
                  service: light.toggle
                  data:
                    entity_id: light.terras_dyn_wit
        - obj:
            checkable: true
            id: lv_button_9
            widgets:
              - label:
                  text_font: light32
                  align: top_left
                  text: $lightbulb
                  id: lv_button_9_icon
              - label:
                  align: bottom_left
                  text: "TL keukenkast"
                  long_mode: dot
            on_short_click:
              - homeassistant.service:
                  service: light.toggle
                  data:
                    entity_id: light.tl_lampen_keukenkast
        - obj:
            checkable: true
            id: lv_button_10
            widgets:
              - label:
                  text_font: light32
                  align: top_left
                  text: $lightbulb
                  id: lv_button_10_icon
              - label:
                  align: bottom_left
                  text: "TL dampkap"
                  long_mode: dot
            on_short_click:
              - homeassistant.service:
                  service: light.toggle
                  data:
                    entity_id: light.tl_lamp_dampkap
        - button:
            checkable: true
            id: lv_button_11
            widgets:
              - label:
                  text_font: light32
                  align: top_left
                  text: $curtains
                  id: lv_button_11_icon
              - label:
                  align: bottom_left
                  text: "Covers"
                  long_mode: dot      
        - button:
            checkable: true
            id: lv_button_12
            widgets:
              - label:
                  text_font: mdi_icons_52
                  align: top_left
                  text: $shield_home_icon
                  id: lv_button_12_icon
              - label:
                  align: bottom_left
                  text: "Alarm"
                  long_mode: dot      
            on_press:
              - lvgl.page.show: 
                  id: alarm_panel_page
                  animation: OUT_RIGHT
                  time: 300ms

my OTA code is current (AI generated):
current is to avoid flickering but it does not work.
Second in comment was more like your code, but it also flickers on the whole screen.

Problem is when a grid view is visible (like my other page) and the progress bar is visible on top of it I see my progress bar & label in very small text resizing all over the display in smaller boxes and flickering effect.

I also added a fixed navigation bar button below on the top layer (visible on all screens), maybe that can’t work with the progress OTA label ?
Or do i have to adapt my other screens in a away they can coexist with the OTA progress label ?

ota:
  - platform: esphome
    id: ota_state
    #password: !secret display_ota
    password: "ae1eafab4cf49f00224f4243215ca282"

    on_begin:
      - logger.log: "OTA Start"
      - light.turn_on: display_backlight
      - lambda: |-
          // Show OTA screen once
          lv_obj_clear_flag(id(ota_widget), LV_OBJ_FLAG_HIDDEN);
          lv_label_set_text(id(ota_label_primary), "Starting OTA!");
          lv_label_set_text(id(ota_label_secondary), "Wait...");
          lv_bar_set_value(id(ota_bar_value), 0, LV_ANIM_OFF);

    on_progress:
      - lambda: |-
          static int last = -1;
          int val = (int)x;
          if (val != last) {
            last = val;
            lv_bar_set_value(id(ota_bar_value), val, LV_ANIM_OFF);
            char buf[64];
            snprintf(buf, sizeof(buf), "Firmware progress %0.1f%%", x);
            lv_label_set_text(id(ota_label_secondary), buf);
          }

    on_end:
      - lambda: |-
          lv_bar_set_value(id(ota_bar_value), 100, LV_ANIM_OFF);
          lv_label_set_text(id(ota_label_primary), "Update complete!");
          lv_label_set_text(id(ota_label_secondary), "Rebooting...");

    on_error:
      - lambda: |-
          lv_label_set_text(id(ota_label_primary), "Update ERROR!");
          lv_label_set_text(id(ota_label_secondary), "Rebooting...");    

    # on_begin: 
    #   - logger.log: "OTA Start"
    #   - light.turn_on: display_backlight
    #   - lvgl.widget.show: ota_widget
    #   - lvgl.label.update:
    #       id: ota_label_primary
    #       text: "Starting OTA!"
    #   - lvgl.label.update:
    #       id: ota_label_secondary
    #       text: "Wait..."
    #   - lvgl.bar.update:
    #       id: ota_bar_value
    #       value: 0
    #   # Only call LVGL loop once after all updates
    #   - lambda: |-
    #       id(lvgl_comp).loop();

    # on_progress: 
    #   - lvgl.bar.update:
    #       id: ota_bar_value
    #       value: !lambda "return (int)x;"
    #   - lvgl.label.update:
    #       id: ota_label_secondary
    #       text: !lambda |-
    #         static char buffer[64];
    #         snprintf(buffer, sizeof(buffer), "Firmware progress %0.1f%%", x);
    #         return buffer;
    #   # Only call LVGL loop once after all updates
    #   - lambda: |-
    #       id(lvgl_comp).loop();

    # on_end:
    #   - logger.log: "OTA End"
    #   - lvgl.bar.update:
    #       id: ota_bar_value
    #       value: 100
    #   - lvgl.label.update:
    #       id: ota_label_primary
    #       text: "Update complete!"
    #   - lvgl.label.update:
    #       id: ota_label_secondary
    #       text: "Rebooting..."
    #   # Only call LVGL loop once after all updates
    #   - lambda: |-
    #       id(lvgl_comp).loop();

    # on_error:
    #   - logger.log:
    #       format: "OTA update error %d"
    #       args: ["x"]
    #   - lvgl.label.update:
    #       id: ota_label_primary
    #       text: "Update ERROR!"
    #   - lvgl.label.update:
    #       id: ota_label_secondary
    #       text: "Rebooting..."
    #   # Only call LVGL loop once after all updates
    #   - lambda: |-
    #       id(lvgl_comp).loop();

my OTA looks again like your current code :slight_smile:

ota:
  - platform: esphome
    id: ota_state
    #password: !secret display_ota

    on_begin: 
      - logger.log: "OTA Start"
      - light.turn_on: display_backlight
      - lambda: "id(display_backlight).loop();"
      - lvgl.resume:
      - lvgl.widget.redraw:
      - lvgl.label.update:
          id: ota_label_primary
          text: !lambda |-
            return "Starting OTA!";
      - lvgl.label.update:
          id: ota_label_secondary
          text: !lambda |-
            return "Wait...";
      - lvgl.widget.show: ota_widget
      - lvgl.resume:
      - lvgl.widget.redraw:
      - lambda: "id(lvgl_comp).loop();"
      - lambda: "id(lvgl_comp).loop();"
      - logger.log: "OTA widget shown"

    on_progress: 
      - lvgl.bar.update:
          id: ota_bar_value
          value: !lambda return (int)x;
      - lvgl.label.update:
          id: ota_label_primary
          text: " "
      - lvgl.label.update:
          id: ota_label_secondary
          text: !lambda |-
            static char buffer[64];
            snprintf(buffer, sizeof(buffer), "Firmware progress %0.1f%%", x);
            return buffer;
      - lambda: "id(lvgl_comp).loop();"

    on_end:
      - logger.log: "OTA End"
      - lvgl.bar.update:
          id: ota_bar_value
          value: 100
      - lvgl.label.update:
          id: ota_label_primary
          text: !lambda |-
            return "Update complete!";
      - lvgl.label.update:
          id: ota_label_secondary
          text: !lambda |-
            return "Rebooting...";
      - lambda: |-
          id(lvgl_comp).loop();

    on_error:
      - logger.log:
            format: "OTA update error %d"
            args: ["x"]
      - lvgl.label.update:
          id: ota_label_primary
          text: !lambda |-
            return "Update ERROR!";
      - lvgl.label.update:
          id: ota_label_secondary
          text: !lambda |-
            return "Rebooting...";
      - lambda: |-
          id(lvgl_comp).loop();

my pages & top_layer look like :

##########Top layer####################
  top_layer:
    widgets:
      # OTA popup widget
      - obj:
          id: ota_widget
          hidden: true
          width: 480
          height: 450
          align: center
          clickable: false
          bg_color: color_slate_blue_gray
          border_opa: transp
          shadow_opa: transp
          radius: 0
          widgets:
            - label:
                id: ota_label_primary
                y: -50
                align: center
                text_font: nunito_20
                text_color: color_misty_blue
                text: "Starting OTA!"
            - label:
                id: ota_label_secondary
                y: -20
                align: center
                text_font: nunito_18
                text_color: color_misty_blue
                text: "Wait..."
            - bar:
                id: ota_bar_value
                y: 20
                width: 300
                value: 0
                align: center
                min_value: 0
                max_value: 100
                animated: true
                bg_color: color_steel_blue
                indicator:
                  bg_color: color_misty_blue

      - buttonmatrix:
          align: bottom_mid
          styles: header_footer
          pad_all: 0
          outline_width: 0
          id: top_layer
          items:
            styles: header_footer
          rows:
            - buttons:
              - id: page_prev
                text: "\uF053"
                on_press:
                  then:
                    lvgl.page.previous:
              - id: page_home
                text: "\uF015"
                on_press:
                  then:
                    lvgl.page.show: main_page
              - id: page_next
                text: "\uF054"
                on_press:
                  then:
                    lvgl.page.next:        

##########Pages####################
  pages:
##########Page 1: main####################
    - id: main_page
      skip: false
      layout:
        type: grid
        grid_rows: [25%, 25%, 25%, 25%]
        grid_columns: [33%, 33%, 33%]
        pad_row: 1
        pad_column: 1
      width: 100%
      height: 450     # 480 - 30 for bottom buttons
      bg_color: color_black
      scrollable: false
      bg_opa: cover  # cover = Fully opaque background (solid color) / transp = Fully transparent background (invisible)
      pad_all: 5
      widgets:
        - obj:
            checkable: true
            id: lv_button_1
            widgets:
              - label:
                  text_font: light32
                  align: top_left
                  text: $light_recessed
                  id: lv_button_1_icon
              - label:
                  align: bottom_mid
                  text: "Iris bed"
                  long_mode: dot
            on_short_click:
              - homeassistant.service:
                  service: light.toggle
                  data:
                    entity_id: light.hue_iris_1
        - obj:
            checkable: true
            id: lv_button_2
            widgets:
              - label:
                  text_font: light32
                  align: top_mid
                  text: $string_lights
                  id: lv_button_2_icon
              - label:
                  align: bottom_mid
                  text: "Nachthal"
                  long_mode: dot              
            on_short_click:
              - homeassistant.service:
                  service: light.toggle
                  data:
                    entity_id: light.nachthal
        - obj:
            checkable: true
            id: lv_button_3
            widgets:
              - label:
                  text_font: light32
                  align: top_right
                  text: $floor_lamp
                  id: lv_button_3_icon
              - label:
                  align: bottom_mid
                  text: "Keuken dimmer"
                  long_mode: dot
            on_short_click:
              - homeassistant.service:
                  service: light.toggle
                  data:
                    entity_id: light.dimmer_keuken_current_value
        - obj:
            checkable: true
            id: lv_button_4
            widgets:
              - label:
                  text_font: light32
                  align: top_left
                  text: $lightbulb
                  id: lv_button_4_icon
              - label:
                  align: bottom_mid
                  text: "Patio wit"
                  long_mode: dot
            on_short_click:
              - homeassistant.service:
                  service: light.toggle
                  data:
                    entity_id: light.patio_vast_wit
        - obj:
            checkable: true
            id: lv_button_5
            widgets:
              - label:
                  text_font: light32
                  align: top_mid
                  text: $lightbulb
                  id: lv_button_5_icon
              - label:
                  align: bottom_mid
                  text: "Kachel wit"
                  long_mode: dot  
            on_short_click:
              - homeassistant.service:
                  service: light.toggle
                  data:
                    entity_id: light.kachel_vast_wit
        - obj:
            checkable: true
            id: lv_button_6
            widgets:
              - label:
                  text_font: light32
                  align: top_right
                  text: $clock
                  id: lv_button_6_icon
              - label:
                  align: bottom_mid
                  text: "Lichtboom"
                  long_mode: dot                             
            on_short_click:
              - homeassistant.service:
                  service: switch.toggle
                  data:
                    entity_id: stopcontact_lichtboom_current_value
        - obj:
            checkable: true
            id: lv_button_7
            widgets:
              - label:
                  text_font: light32
                  align: top_left
                  text: $lightbulb
                  id: lv_button_7_icon
              - label:
                  align: bottom_left
                  text: "Salon kleur"
                  long_mode: dot
            on_short_click:
              - homeassistant.service:
                  service: light.toggle
                  data:
                    entity_id: light.salon_kleur
        - obj:
            checkable: true
            id: lv_button_8
            widgets:
              - label:
                  text_font: light32
                  align: top_left
                  text: $lightbulb
                  id: lv_button_8_icon
              - label:
                  align: bottom_left
                  text: "Terras wit"
                  long_mode: dot              
            on_short_click:
              - homeassistant.service:
                  service: light.toggle
                  data:
                    entity_id: light.terras_dyn_wit
        - obj:
            checkable: true
            id: lv_button_9
            widgets:
              - label:
                  text_font: light32
                  align: top_left
                  text: $lightbulb
                  id: lv_button_9_icon
              - label:
                  align: bottom_left
                  text: "TL keukenkast"
                  long_mode: dot
            on_short_click:
              - homeassistant.service:
                  service: light.toggle
                  data:
                    entity_id: light.tl_lampen_keukenkast
        - obj:
            checkable: true
            id: lv_button_10
            widgets:
              - label:
                  text_font: light32
                  align: top_left
                  text: $lightbulb
                  id: lv_button_10_icon
              - label:
                  align: bottom_left
                  text: "TL dampkap"
                  long_mode: dot
            on_short_click:
              - homeassistant.service:
                  service: light.toggle
                  data:
                    entity_id: light.tl_lamp_dampkap
        - button:
            checkable: true
            id: lv_button_11
            widgets:
              - label:
                  text_font: light32
                  align: top_left
                  text: $curtains
                  id: lv_button_11_icon
              - label:
                  align: bottom_left
                  text: "Covers"
                  long_mode: dot      
        - button:
            checkable: true
            id: lv_button_12
            widgets:
              - label:
                  text_font: mdi_icons_52
                  align: top_left
                  text: $shield_home_icon
                  id: lv_button_12_icon
              - label:
                  align: bottom_left
                  text: "Alarm"
                  long_mode: dot      
            on_press:
              - lvgl.page.show: 
                  id: alarm_panel_page
                  animation: OUT_RIGHT
                  time: 300ms

still flickering resizing stuff, it seems as if the OTA label text is placed inside my page items causing multiple small bars with update progress, but so small, unreadable.

Hey guys! Hello everyone! And a massive thank you for your support and interest in the project, for your stars and donations! The project will keep evolving - more widgets and options are coming. I have tons of plans for development. This isn’t just about this device, but many others in different formats, including 10-inch devices and encoders. ESP32-P4 devices have way more potential. And if the ESPHome folks don’t finish proper color support, I’m willing to spend time writing C++ firmware for ESP32-P4 to get a UI comparable to desktop. Further project discussions will happen on Telegram. Why there? Because it’s convenient! If you want to support me or participate directly in the project, there’s a private, international channel, russian channel where I’ll tackle tasks by priority. There’s also a public channel where all interested users can share their experience, code, and help each other out. I hope our community grows and together we’ll make our open source project the most popular one! Coming soon:

  • new widgets: cover (gates, curtains, etc.), alarm_panel, input_boolean (scenarios), fan, switch. After release, I’ll add boiler/heater support.
  • porting firmware to Waveshare-ESP32-P4-86-Panel-ETH-2RO (with extra features)
  • launching ELVAF
  • custom microSD integrations
  • developing firmware for large screens and encoders
  • support
6 Likes

@monty_burns_007 Check main.yaml for these options:

esp32:
  board: esp32-s3-devkitc-1
  variant: esp32s3
  flash_size: 16MB
  framework:
    type: esp-idf
    sdkconfig_options:
      CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240: "y"
      CONFIG_SPIRAM_FETCH_INSTRUCTIONS: y
      CONFIG_SPIRAM_RODATA: y
2 Likes

Sorry for off topic, but could anyone advise it the bluetooth proxy works with this display. I added bluetooth_proxy but the boot loop occured. Thanks

@alaltitov, thanks for the great setup! However, I do need some help.

I have downloaded your dev setup, adjusted the substitutions and installed it at my device. Most appears to be working (weather, info page, etc.). However, the time and date doesn’t update. Interestingly, it states 01:00 on Thursday 01 January so not the initial values that are in home.yaml (although these values are not correct).

I tried downloading the latest version but it seemed to be ‘work in progress’ last weekend (I think you are working on covers) so got some error messages.

Any thoughts what I need to correct to get the correct time? Alternatively, can you indicate when there is a new working version on github?

Btw, I just checked the Telegram channel and I saw that somebody else had a similar time issue but he had the continent/city incorrect. Mine is correct and worked on an initial version:
secondary_language: “en”
timezone: “Europe/Amsterdam”

Have you connected the device to HA via API encryption key in Devices & Entities-EspHome ?

The repository has been updated, sensors are added to the main screen when selecting packages, the choice of the number of buttons on the main display has been added, new pages have been added

So guys, here’s the news - as I mentioned before, it won’t be possible to fit all widgets into the Guition ESP32-4848S040 firmware, even with the DEV version, optimizations, and stack size manipulation… That is, the widgets will be there, but you’ll have to select the configuration for stability yourself through trial and error. Currently, the work is 90% complete before release, but it’s paused for a week, maybe two. But there’s good news - the firmware for Waveshare-ESP32-P4-86-Panel-ETH-2RO is already available for testing.
Still need to work on the design, clean up, fix some bugs, and show another version of the light widget. But it’s stable and all widgets work smoothly. The only BUT is that for now you’ll have to flash via UART, since OTA doesn’t want to work with 32Mb PSRAM, and we’ve already exceeded the 16Mb limits, but I think they’ll fix it. Also, you’ll need the DEV version of ESPHome 2025.11.0.

2 Likes

AI is not your solution, its your problem.

I’d probably agree with you. AI is a good assistant if you understand what you’re doing. I don’t know what the original post was originally about, since the author changed it, but if you allow me to express my humble opinion, it’s much faster to write your own firmware simply by reading the documentation. After all, ESPHome was created specifically for those without programming skills, and the YAML markup is intuitive. Just experiment and delve into the matter, and everything becomes quite simple. At least for simple projects. As for AI, it simply provides analytics based on the codebase, which, alas, is very limited for ESPHome and LVGL in particular, especially considering that ESPHome is constantly evolving, and everything is changing, while AI will always lag behind, having an outdated codebase. Is it possible to write something simple with AI if you understand what you’re doing? Yes! Something complex? Unlikely…

Yes. All other connections (e.g. the weather) are working.
I finally got it to work again by reverting back to a clean install and reintroducing some of the changes. It seems that the one item that created my issue has to do with me adding in the main.yam file a ‘manual_ip’ in the wifi section. When I do that, the clock doesn’t update anymore…

I have a JC8048W550 (Guition ESP32S3 5-inch 800x480) that I would like to use as a dashboard for HA.

Sadly, it looks as alaltitov’s firmware for Guition-ESP32-S3-4848S040 won’t for this 5 inch. Is that correct?

If not, then I noticed that there is OpenHASP as well but it’s UI does not look as nice.

There seems to be ESPHome firmware/support for JC8048W550. Can I combine that with alaltitov’s UI components?

There’s no support for this screen; the firmware needs to be rewritten for a rectangular resolution. Currently, the firmware only supports square displays in two resolutions: 480x480 and 720x720. Support for rectangular displays is planned for the future, but only for ESP32-P4 microcontrollers. This is because ESP32-S3-based devices lack the resources for a full-fledged smart home control panel.

2 Likes

openHASP is very limited. I started this thread with my library that runs on many different screens. The library allows you to make dashboards, room controllers, and other smart hardware projects with only a few lines of code.

It supports the Guition esp32-jc8048w550 and many other ESP32 screens.