Lilygo T-Display S3 on ESPHome?

I have bought this display: T-Display S3 AMOLED – LILYGO®

Can anyone tell me if it’s supported by ESPHome at this stage? I’ve followed this video https://www.youtube.com/watch?v=LJCeelAzlS0 and bought the source code but my device just bricks. I have to flash it with Arduino to get it working again.

The closest I could find is this cst816 Touch Screen Controller — ESPHome but that’s for the touch, which I do not have.

I don’t want to attempt an Arduino app if ESPHome is already supporting it. The supplied examples in the GitHub - Xinyuan-LilyGO/T-Display-S3 repo is so complex I can’t even figure out what the mosi and clk pins are.

Many thanks for any help!

2 Likes

The link you gave covers two versions of the display. I have been using version 2 for several months with ESPHome and Home Assistant. My code to display my Blood Glucose:

# Uses the following devices
# - LilyGo T-Display S3 AMOLED with Touch
# - Blood_Sugar sensor from Home Assistant NightScout Integration
# touchscreen:
  # platform: cst816
# display:
  # - platform: qspi_amoled
    # model: RM67162

substitutions:
  disp_name: CGM Display Amoled

esphome:
  name: cgm-display-amoled
  friendly_name: CGM Display Amoled
  platformio_options:
    build_unflags: -Werror=all
    board_build.flash_mode: dio

esp32:
  board: esp32-s3-devkitc-1
  variant: esp32s3
  framework:
    type: esp-idf

logger:
  level: error

api:

ota:

web_server:
  port: 80

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  ap:
    ssid: "CDM Display Amoled"

spi:
  id: quad_spi
  type: quad
  clk_pin: 47
  data_pins: [18, 7, 48, 5]

i2c:
  sda: 3
  scl: 2

touchscreen:
  - platform: cst816
    id: my_touchscreen
    interrupt_pin: 
      number: 21
    transform:
      mirror_x: true
      swap_xy: true      
    on_touch:
      - lambda: |-
          ESP_LOGI("cal", "x=%d, y=%d, x_raw=%d, y_raw=%d",
            touch.x,
            touch.y,
            touch.x_raw,
            touch.y_raw
          );
      - logger.log:
          format: Touch %d at (%d, %d)
          args: [touch.id, touch.x, touch.y]
      - display.page.show_next: my_display

color:
  - id: my_black
    red: 0%
    green: 0%
    blue: 0%
  - id: my_red
    hex: fc0000
  - id: my_green
    hex: 00fc4c
  - id: my_yellow
    hex: e0e637
  - id: my_orange
    hex: ff7728
  - id: my_white
    hex: ffffff

font:
  - file: 'fonts/arialbd.ttf'
    id: font1
    size: 144
  - file: 'fonts/roboto.ttf'
    id: font2
    size: 45

graph:
  - id: blood_glucose_graph
    duration: 2h
    x_grid: 15min
    y_grid: 50.0
    max_value: 400
    min_value: 50 
    width: 500
    height: 200
    traces:
      - sensor: blood_sugar
        line_type: SOLID
        continuous: true
        line_thickness: 3
        color: my_green

display:
  - platform: qspi_amoled
    id: my_display
    model: RM67162
    dimensions:
      height: 240
      width: 536
      offset_height: 0
      offset_width: 0
    transform:
      mirror_x: true
      mirror_y: false
      swap_xy: true
    color_order: rgb
    brightness: 150
    cs_pin: 6
    reset_pin: 17
    enable_pin: 38
    pages:
      - id: page1
        lambda: |-
          it.filled_rectangle(0, 0, 536, 240, my_black);
          if (id(blood_sugar).has_state()) {
            if (id(blood_sugar).state < 55.0) {
              it.printf(268, 100, id(font1), id(my_red), TextAlign::CENTER, "%.0f", id(blood_sugar).state);
            }
            if ((id(blood_sugar).state >= 55.0) && (id(blood_sugar).state < 70.0)) {
              it.printf(268, 100, id(font1), id(my_yellow), TextAlign::CENTER, "%.0f", id(blood_sugar).state);
            }
            if ((id(blood_sugar).state >= 70.0) && (id(blood_sugar).state < 180.0)) {
              it.printf(268, 100, id(font1), id(my_green), TextAlign::CENTER, "%.0f", id(blood_sugar).state);
            }
            if ((id(blood_sugar).state >= 180.0) && (id(blood_sugar).state < 250.0)) {
              it.printf(268, 100, id(font1), id(my_yellow), TextAlign::CENTER, "%.0f", id(blood_sugar).state);
            }
            if (id(blood_sugar).state >= 250.0) {
              it.printf(268, 100, id(font1), id(my_orange), TextAlign::CENTER, "%.0f", id(blood_sugar).state);
            }
          }
          it.strftime(10, 235, id(font2), id(my_white), TextAlign::BOTTOM_LEFT, "%I : %M %p", id(esptime).now());
      - id: page2
        lambda: |-
          it.graph(10, 20, id(blood_glucose_graph), my_yellow);

psram:
  mode: octal

time:
  - platform: homeassistant
    id: esptime

switch:
  - platform: restart
    name: ${disp_name} Restart  

sensor: 
  - platform: homeassistant
    id: blood_sugar
    entity_id: sensor.blood_sugar
    accuracy_decimals: 0
    unit_of_measurement: mg/dL

  - platform: wifi_signal
    name: ${disp_name} WiFi Status
    update_interval: 10s


captive_portal:

They are working on changes that will allow you to use LVGL with this display and should be added shortly in the June or July release.

2 Likes

Thank you that is a very interesting use case!

I’m using the non-touch 1.2 version.

I’ve replaced my settings with what I could glean off your code. At least it’s not bricking, but nothing is showing up on the display and I don’t see any obvious errors in the logs. This is my code. I’ll keep checking.

substitutions:
  name: esphome-web-83380c
  friendly_name: S3 Display

esphome:
  name: ${name}
  friendly_name: ${friendly_name}
  name_add_mac_suffix: false
  platformio_options:
    board_build.flash_mode: dio
  project:
    name: esphome.web
    version: '1.0'

esp32:
  board: esp32-s3-devkitc-1
  variant: esp32s3
  framework:
    type: esp-idf

# Enable logging
logger:

# Enable Home Assistant API
api:

# Allow Over-The-Air updates
ota:

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

dashboard_import:
  package_import_url: github://esphome/firmware/esphome-web/esp32s3.yaml@v2
  import_full_config: true

# Sets up Bluetooth LE (Only on ESP32) to allow the user
# to provision wifi credentials to the device.
esp32_improv:
  authorizer: none

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

spi:
  id: quad_spi
  type: quad
  clk_pin: 47
  data_pins: [18, 7, 48, 5]

i2c:
  sda: 3
  scl: 2

display:
  - platform: qspi_amoled
    id: my_display
    model: RM67162
    dimensions:
      height: 240
      width: 536
      offset_height: 0
      offset_width: 0
    transform:
      mirror_x: true
      mirror_y: false
      swap_xy: true
    color_order: rgb
    brightness: 150
    cs_pin: 6
    reset_pin: 17
    enable_pin: 38
    lambda: |-
      it.print(0, 0, id(font1), "Hello World!");

font:
  - file: "gfonts://Roboto"
    id: font1
    size: 20

This is my device:

I just looked at the schematics on the Liligo website. It has two different schematics for the AMOLED displays, one for the touch screen version and one for the AMOLED Lite version (this I believe may be the one that you have. If it is it lists different pins for the I2C.

i2c:
  sda: 1
  scl: 2

You may want to try that combination.

Nice, I use a display with openhasp to do much the same (but it doesn’t graph, I just have a large display next to the bed so I don’t have to look at my phone etc at 3am)

I spent the weekend at a pump trial (new algorithm) and the guy from Tandem was impressed.

I actually have several displays around the house to monitor my blood glucose levels, besides using the AMOLED next to my bed. I also Lolin 2.4" TFT displays next to my computer and in the living room.

The graph works okay to show trend and magnitude/rate of change (haven’t figured out how to get the trend arrows yet), but without grid labels it is hard to get the value and time off of the graph. The graph get erased and starts over if the ESP32 is updated or reset. Is will try take a picture tomorrow (3:00 am here). Will also be changing the code to allow brightness to be adjusted from touchscreen.

I am looking forward to the LVGL support that is coming soon, that should make things easier.

Thanks @carbuthn I tried it, but still just a black screen. Where did you see the new 12c pins ?

I also have little displays all around the house and I’m starting to wonder if it might be possible just to integrate with a smart watch somehow. Then I can check my inverter status etc etc at any time just glancing at my watch.

AMOLED Schematics

One other difference I saw was unger platformio options

esphome:
  name: cgm-display-amoled
  friendly_name: CGM Display Amoled
  platformio_options:
    build_unflags: -Werror=all
    board_build.flash_mode: dio

It looks like LVGL didn’t make it into this month’s release of ESPHome, maybe next month.

Thanks for checking, added the build_unflags, still just black screen.

I’ve tracked down the exact order, it’s this one, from amazon: https://www.amazon.com/dp/B0BF542H39?ref=ppx_yo2ov_dt_b_product_details&th=1

Is it different to the AMOLED one above?

Yes that is a different animal, but should be doable. The pin information is the last picture on the Amazon page.

ESPHome Display Component Information

That display will use the ILI9xxx TFT LCD Series, ST7789V model.

It doesn’t seem to use I2C, so you will need to figure out the data pins. ClydeBarrow has been working on an I80 io_bus interface. https://community.home-assistant.io/t/unable-to-install-to-esp32-s3-lilygo-t-display-s3/467669/23. Look at the last post.

I don’t have that board so I can’t try it out and it about 12:30 am here, will try to look at it some more tomorrow.

Thanks so much. It says it does need a spi component. I could read off the pinouts the following, but it does not show the reset or mosi pins anywhere.


spi:
  clk_pin: GPIO12
  mosi_pin: GPIOXX

display:
  - platform: ili9xxx
    cs_pin: GPIO10
    dc_pin: GPIO11
    reset_pin: : GPIOXX
    model: ST7789V
    lambda: |-
      it.print(10, 10, id(font1), "Hello World!");

The trend arrows are unicode characters, but not every font has them. Finding a font that has all of them can be a bit tricky.

Starting point is here

All the Unicode details are there.

The nightscout API provides trend as one of the strings like “FortyFiveUp”.

So presently the state of my trend sensor is currently “Flat”. You can template to produce the required Unicode char \u2192.

That board does not use SPI to interface to the display. See here: Implement i80 bus for ili9xxx displays by clydebarrow · Pull Request #6537 · esphome/esphome · GitHub

substitutions:
  name: t-display-s3-tft
  friendly_name: T-Display S3 TFT

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


external_components:

 - source: github://clydebarrow/esphome@i8080
   components: [ i80, io_bus, ili9xxx, spi ] 

esp32:
  board: esp32-s3-devkitc-1
  variant: esp32s3
  framework:
    #type: arduino
    type: esp-idf

logger:
  level: DEBUG
  logs:
    lvgl: WARN

#wifi: !include wifi.yaml

#api:

#ota:

power_supply:
  - id: backlight
    enable_on_boot: true
    pin: GPIO38
  - id: lcd_pwr
    enable_on_boot: true
    pin: GPIO15

i80:
  dc_pin: 7
  wr_pin: 8
  rd_pin: 9
  data_pins: 
    - 39
    - 40
    - 41
    - 42
    -
      ignore_strapping_warning: true
      number: 45
    -
      ignore_strapping_warning: true
      number: 46
    - 47
    - 48
  

i2c:
  sda: 17
  scl: 18

psram:
  speed: 80MHz
  mode: octal

display:
  - platform: ili9xxx
    bus_type: i80
    cs_pin: 6
    reset_pin: 5
    id: w32_disp
    model: st7789v
    data_rate: 2MHz
    dimensions:
      height: 320
      width: 170
      offset_width: 35
      offset_height: 0
    transform:
      mirror_y: false
      mirror_x: false
      swap_xy: false
    color_order: bgr
    invert_colors: true
    auto_clear_enabled: false
    update_interval: never

lvgl:


      
1 Like

Yes!! Thank you @clydebarrow it works!! Thanks also to everyone else assisting!

For the life of me, I can’t seem to figure out how to get this to work. Every time, I get editor complaints about the display, but worse, the compile fails earlier with complaints about the config. If I have that trailing :, it fails on that…

INFO ESPHome 2024.10.2
INFO Reading configuration /config/esphome/test.yaml...
ERROR Unable to import component ili9xxx.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/fbb92d5f/esphome/components/ili9xxx/display.py", line 6, in <module>
    from esphome.const import (
ImportError: cannot import name 'CONF_INIT_SEQUENCE' from 'esphome.const' (/esphome/esphome/const.py)
Failed config

display.ili9xxx: [source /config/esphome/test.yaml:72]
  
  Platform not found: 'display.ili9xxx'.
  platform: ili9xxx
  bus_type: i80
  cs_pin: 6
  reset_pin: 5
  id: w32_disp
  model: st7789v
  data_rate: 2MHz
  dimensions: 
    height: 320
    width: 170
    offset_width: 35
    offset_height: 0

ESPHome had a core change, which is reflected in the external component. You’ll need to use the dev branch or wait for the next release.

Thanks for the quick response!