ESPHome GUI component using LVGL

In my experience when the screen seems to be functioning but is blank, it’s normally the backlight, just make sure there’s no pin or config missing to turn on the backlight

I believe it’s labelled as TFT_RS on that board. You’ll also want to specify CS and RESET. You’ll need a schematic or sample code to figure out which pins they are. And you’ll need to turn on the backlight with the TFT_BL pin - a monochromatic light component will allow you to adjust the brightness.

Thanks for the tips.

Based on this wiring diagram for the TFT:
image

I figured the RS/DC is IO2 and CS is IO15.

There’s that pin labeled RESET, but it seems to be hard-wired. Is that normal?

Regarding the BACKLIGHT, the screen seems to be lit up very bright (white). So at least I think it’s on by default.

There appears to be sample code here along with the wiring diagram I got here. (After translation, it says RESET is the LCD reset signal line. Because the GPIO on the start board is tight, it is connected to the RESET line of the system. Similarly, BL is the backlight control line of the LCD, which is also directly connected to 3.3V. – but I don’t know if that’s correct, or lost in translation. At least it would explain the backlight being always on.)

However, the sample code is very frustrating, because it uses macros like PIN_NUM_RST, bit the headers that define those constants are not available – or at least I couldn’t figure out how to get them, even after having Google translate the page from Chinese.

Any suggestions?

For those interested in working with LVGL, I’ve successfully configured it using the following hardware setup:

Microcontroller: Lolin D32 PRO
Display: TFT 2.4 Touch Shield
TFT Driver: ILI9341
Touch Controller: XPT2046

Also notice the framework, not esp-idf but arduino did the trick for me.

esphome:
  name: esp-display-lolin
  friendly_name: ESP Display Lolin
  platformio_options:
    build_flags: "-DBOARD_HAS_PSRAM"

esp32:
  board: lolin_d32_pro
  framework:
    type: arduino

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

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key:  !secret esphome_api

ota:
  - platform: esphome
    password: !secret esphome_ota

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
    
web_server:
  local: true

captive_portal:

color:
  - id: my_red
    red: 100%
    green: 0%
    blue: 0%
  - 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_black
    red: 0%
    green: 0%
    blue: 0%
  - id: my_white
    red: 100%
    green: 100%
    blue: 100%

font:
  - 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

time:
  - platform: homeassistant
    id: esptime

sensor:
  - platform: homeassistant
    id: inside_temperature
    entity_id: sensor.gw2000a_indoor_temperature
    internal: true

  - platform: homeassistant
    id: outside_temperature
    entity_id: sensor.gw2000a_temperature_1
    internal: true
    
  - platform: wifi_signal
    name: "WiFi Signal Strength"
    update_interval: 60s

spi:
 clk_pin: 18 
 mosi_pin: 23 
 miso_pin: 19

touchscreen:
  platform: xpt2046
  id: touch_key1
  cs_pin: 12
  update_interval: 50ms
  calibration:
    x_min: 281
    x_max: 3848
    y_min: 347
    y_max: 3878
  transform:
    mirror_x: false
    mirror_y: false
    swap_xy: false
            
lvgl:
  displays:
    - tft_ha_test
  pages:
    - id: main_page
      widgets:
        - label:
            align: CENTER
            text: 'Hello World!'
            
display:
  - platform: ili9xxx
    model: ili9341
    cs_pin: 14
    dc_pin: 27
    reset_pin: 33
    rotation: 90
    auto_clear_enabled: false
    invert_colors: false
    show_test_card: false
    id: tft_ha_test

I’m trying to get LVGL going on a Lanbon L8 but all I got was a blank screen UNTIL I removed the update_interval: never line on the display. But I’m getting run-time errors:

[15:22:40][W][component:237]: Component display took a long time for an operation (72 ms).
[15:22:40][W][component:238]: Components should block for at most 30 ms.
[15:22:45][W][component:237]: Component display took a long time for an operation (72 ms).
[15:22:45][W][component:238]: Components should block for at most 30 ms.

Is the documentation wrong or am I doing something wrong?

Also, trying to test the touch screen by having a large checkable button in the middle of the screen. The label is there but not the button (it’s in the upper left) and it is TINY!

FYI, I’m familiar with LVGL using SquareLine Studio.

psram:
  mode: octal
  speed: 80MHz

spi:
  clk_pin: GPIO19
  mosi_pin: GPIO23
  miso_pin: GPIO25

i2c:
  sda: GPIO4
  scl: GPIO0

font:
    # gfonts://family[@weight]
  - file: "gfonts://Roboto"
    id: roboto_20
    size: 20

display:
  - id: langbon_L8
    platform: st7789v
    model: Custom
    height: 320
    width: 240
    offset_height: 0
    offset_width: 0
    backlight_pin: GPIO5
    cs_pin: GPIO22
    dc_pin: GPIO21
    reset_pin: GPIO18
    auto_clear_enabled: false

touchscreen:
  - platform: ft63x6

output:
  - platform: ledc
    pin: GPIO26
    id: moodRed
  - platform: ledc
    pin: GPIO32
    id: moodGreen
  - platform: ledc
    pin: GPIO33
    id: moodBlue
  - platform: gpio
    pin: GPIO12
    id: relay_1
  - platform: gpio
    pin: GPIO14
    id: relay_2
  - platform: gpio
    pin: GPIO27
    id: relay_3

light:
  - platform: rgb
    name: "Mood Light"
    red: moodRed
    green: moodGreen
    blue: moodBlue

lvgl:
  displays:
    - langbon_L8
  pages:
    - id: main_page
      widgets:
        - button:
            align: CENTER
            height: 60
            width: 200
            checkable: true
            bg_color: 0x0000FF
            default:
              bg_color: 0x00FF00
            checked:
              bg_color: 0xFF0000
        - label:
            text: "Click ME!"
            align: CENTER

As per the docs, use the ili9xxx display component, not the deprecated st7789v component.

If you want the button to contain the label, it has to be a child of the button. See the example in the docs: LVGL Widgets — ESPHome

Switching to the ili9xxxx fixed the update issue and making the label a child of the button centered it on the button, but the button is still on the upper left and no sign that is is clickable.

display:
  - id: langbon_L8
    platform: ili9xxx
    model: ST7789V
    invert_colors: false
    dimensions: 240x320
    cs_pin: GPIO22
    dc_pin: GPIO21
    reset_pin: GPIO18
    auto_clear_enabled: false
    update_interval: never
...
lvgl:
  pages:
    - id: main_page
      widgets:
        - button:
            x: 120
            y: 120
            height: 60
            width: 150
            checkable: true
            default:
              bg_color: 0x00FF00
            checked:
              bg_color: 0xFF0000
            widgets:
              - label:
                  text: "Click ME!"
                  align: CENTER
            on_value:
              then:
                - logger.log:
                    format: "Button checked state: %d"
                    args: [ x ]

I can probably figure out the LGVL layout issues eventually (and how to manage the now-explicit backlight) but the lack of clicking needs help.

It is clickable (you will see it change when pressed) but the checkable flag isn’t being set. Move the bg_color setting out from default to just under the button itself. Not sure why that affects it (could be a bug) but you never need to use the default key anyway, since anything applied directly to the widget is the default.

Change

            checkable: true
            default:
              bg_color: 0x00FF00
            checked:
              bg_color: 0xFF0000

to

            checkable: true
            bg_color: 0x00FF00
            checked:
              bg_color: 0xFF0000

just spent the weekend getting exactly this going!

here is my esphome config and it includes a display timeout and a clickable button that turns a homeassistant light entity on and off

spi:
  clk_pin: GPIO19  
  mosi_pin: GPIO23 
  miso_pin: GPIO25 

i2c:
  sda: GPIO4
  scl: GPIO0

display:
  - platform: ili9xxx
    model: ST7789V
    cs_pin: GPIO22
    dc_pin: GPIO21
    reset_pin: GPIO18
    invert_colors: false
    auto_clear_enabled: false
    update_interval: never
    show_test_card: true 
    id: disp
    rotation: 180

output:
  - platform: ledc
    pin: GPIO5
    id: backlight_pwm
  - platform: ledc
    id: mood_red
    pin: GPIO26
  - platform: ledc
    id: mood_green
    pin: GPIO32
  - platform: ledc
    id: mood_blue
    pin: GPIO33        

light:
  - platform: monochromatic
    output: backlight_pwm
    name: "Display Backlight"
    id: backlight
    restore_mode: ALWAYS_ON
  - platform: rgb
    name: "Moodlight"
    red: mood_red
    green: mood_green
    blue: mood_blue

touchscreen:
  platform: ft63x6
  id: touch
  display: disp
  calibration:
    x_min: 0
    y_min: 0
    x_max: 230
    y_max: 312

  on_touch:
    - lambda: |-
          ESP_LOGI("cal", "x=%d, y=%d, x_raw=%d, y_raw=%0d",
              touch.x,
              touch.y,
              touch.x_raw,
              touch.y_raw
              );
  on_release:
    - if:
        condition: lvgl.is_paused
        then:
          - logger.log: "LVGL resuming"
          - lvgl.resume:
          - lvgl.widget.redraw:
          - light.turn_on: backlight
lvgl:
  displays:
    - disp
  on_idle:
    timeout: !lambda "return (id(display_timeout).state * 1000);"
    then:
      - logger.log: "LVGL is idle"
      - light.turn_off: backlight
      - lvgl.pause:
  pages:
    - id: main_page
      widgets:
        - button:
            id: light_btn
            align: CENTER
            width: 140
            height: 70
            checkable: true
            widgets:
              - label:
                  align: CENTER
                  text: 'Lights'
            on_click:
              - homeassistant.action:
                  action: light.toggle
                  data:
                    entity_id: light.bedroom_lights
              - logger.log:
                  format: "Button checked"
                  



switch:
  - platform: gpio
    pin: GPIO12
    name: relay1
  - platform: gpio
    pin: GPIO14
    name: relay2
  - platform: gpio
    pin: GPIO27
    name: relay3

binary_sensor:
  - platform: homeassistant
    id: bedroom_light
    entity_id: light.bedroom_lights
    publish_initial_state: true
    on_state:
      then:
        lvgl.widget.update:
          id: light_btn
          state:
            checked: !lambda return x;
  - platform: lvgl
    name: button2
    widget: light_btn
    publish_initial_state: true

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

captive_portal:
2 Likes

Thank you @Jon_White !! It’s working for me now too!

Now onto my GUI design!

Good luck here is a basic button matrix that seems to work a bit better than single buttons.

I will keep uploading my latest configs to: GitHub - jtbnz/esphome_lanbonL8: ESphome config for a Lanbon L8 LCD switch

pages:
- id: main_page
widgets:
- buttonmatrix:
x: 5
y: 40
width: 230
items:
pressed:
bg_color: 0xFFFF00
id: matrix_id
rows:
- buttons:
- id: light_btn
control:
checkable: true
text: ‘Lights’
on_click:
- homeassistant.action:
action: light.toggle
data:
entity_id: light.bedroom_lights
- logger.log:
format: “Button pressed”
- buttons:
- id: relay1_btn
control:
checkable: true
text: ‘Relay1’
on_click:
- switch.toggle: relay1
- logger.log:
format: “Relay Button pressed”

1 Like

I’ve submitted a PR to document the configuration for fully integrated hardware platform.

2 Likes

My write-up on this whole experience of converting openHASP to ESPHome LVGL