Содержание
Покопавшись в интернете, я наткнулся на вот такую интересную вещь — Беспроводной модуль LILYGO T-Display S3 AMOLED. T-Display S3 — это плата для разработки с микроконтроллером ESP32-S3 и встроенным AMOLED-дисплеем, идеально подходящая для проектов IoT с ярким визуальным выводом и более чем достаточной вычислительной мощностью для небольших проектов.

В этой статье я делюсь своим процессом прошивки, настройки и рендеринга контента на этой плате разработки AMOLED с использованием механизма отображения ESPHome. Вы можете использовать его для передачи данных из Home Assistant, создания аккуратных дисплеев статуса и даже использовать его в качестве контроллера.
LILYGO T-Display-S3 AMOLED доступен на АлиЭкспресс а также Яндекс.Маркет. На Яндексе дороже, но доставка около трех дней и меньше проблем с возвратом при браке, тогда как на Алиэкпресс доставка, в лучшем случае, около двух недель и непонятный возврат.
Различные версии LILYGO T-Display S3
Прежде чем углубляться дальше, важно отметить, что обе версии этого экрана —Basic и Touch (Базовый и Сенсорный) — почти одинаковы, с небольшими различиями в распиновке и функциях. Эти различия в первую очередь касаются сенсорной функциональности версии Touch, имеющей дополнительный Контакт Touch_INT на GPIO21 и Контакт включения питания на GPIO38 для поддержки сенсорного экрана, которого нет в базовой версии.
На данный момент версия 2.0 этой платы имеет следующую распиновку:

- LILYGO T-Display S3 AMOLED Basic
- ESP32-S3R8 Двухъядерный LX7
- AMOLED 1,91 дюйма 240×536
- КСПИ RM67162
- 16 МБ флэш-памяти, 8 МБ PSRAM
- Wi-Fi 802.11, БЛЕ 5.0

- LILYGO T-Display S3 AMOLED Touch
- ESP32-S3R8 Двухъядерный LX7
- AMOLED Touch 1,91 дюйма, 240×536
- КСПИ RM67162
- 16 МБ флэш-памяти, 8 МБ PSRAM
- Wi-Fi 802.11, БЛЕ 5.0
Это руководство для AMOLED базовой версии.
Начальная прошивка в ESPHome
Прежде чем мы сможем настроить компонент дисплея и фактически отображать объекты на этой плате, его необходимо инициализировать в ESPHome. Создайте новое устройство в ESPHome, назовите его как хотите и инициализируйте его с помощью следующего кода:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
esphome: name: lilygos3 friendly_name: LILYGO-S3 T-Display platformio_options: build_unflags: -Werror=all board_build.flash_mode: dio esp32: board: esp32-s3-devkitc-1 variant: esp32s3 framework: type: esp-idf api: encryption: key: "XXXXX" #Replace ota: - platform: esphome password: "XXXXX" #Replace wifi: ssid: !secret wifi_ssid password: !secret wifi_password ap: ssid: "S3 Fallback Hotspot" password: "zQ9tuPKIfFMu" logger: |
После первой вспышки на экране ничего не будет отображаться. Поскольку мы не настроили распиновку для spi и display компоненты, плата просто загрузится, и вы сможете получить к ней беспроводной доступ из сети.
Следующим шагом будет настройка компонента дисплея, добавление текстового шрифта и отрисовка чего-нибудь простого с помощью механизма рендеринга дисплея ESPHome. Чтобы это работало, необходимо установить соответствующие контакты. Из официальных изображений распиновки видно, как должна выглядеть конфигурация Базовой версии LILYGO T-Display S3:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
spi: id: qspi_bus type: quad clk_pin: GPIO47 data_pins: - GPIO18 - GPIO7 - GPIO48 - GPIO5 display: - platform: qspi_amoled model: RM67162 id: s3_display update_interval: 1s dimensions: height: 536 width: 240 color_order: rgb brightness: 255 cs_pin: GPIO6 reset_pin: GPIO17 rotation: 0 lambda: |- it.print(10, 20, id(font_roboto), Color(255, 255, 255), "Hello, SmartHomeScene!"); #Add a simple Google Font font: - file: "gfonts://Roboto" id: font_roboto size: 20 |
Если вы видите что-то на дисплее, это означает, что ваша конфигурация правильна и работает:

Понимание механизма рендеринга дисплея ESPHome
Чтобы создать что-то полезное на LILYGO T-Display S3, необходимо использовать механизм рендеринга дисплея ESPHome. Этот движок может выполнять такие задачи, как рисование фигур, отображение текста с использованием пользовательских шрифтов и даже рендеринг изображений. Эта гибкость достигается за счет лямбда-системы ESPHome, которая позволяет писать код C++ для управления дисплеем.
В лямбде можно писать код как в любой лямбда в ESPHome. Отображаемым лямбда-выражениям дополнительно передается переменная с именем it который представляет объект механизма рендеринга. При рендеринге координат отображения в ESPHome начало координат (0, 0) начинается с верхний левый угол экрана. Координаты X увеличиваются вправо, а координаты Y увеличиваются вниз. Цвета отображаются с использованием значений RGB, обычно в формате 1где значения варьируются от 0 до 255 для каждого канала. Шрифты определяются путем указания шрифтов и размеров, что позволяет печатать текст в различных стилях. Изображения выводятся на дисплей с использованием растровых данных, обычно предварительно загружаемых или извлекаемых с помощью кода, и визуализируются на основе размещения координат.
Вот как выглядит красный круг с центром посередине, если использовать двух помощников, чтобы получить width и height экрана автоматически:
|
1 2 3 |
lambda: |- // Draw a red circle in the middle of the display it.filled_circle(it.get_width() / 2, it.get_height() / 2, 120, Color(255, 0, 0)); |

Поворот экрана
Прежде чем экран станет полностью полезным, важно понять параметр конфигурации вращения в ESPHome. Вы можете добавить rotation на экран и установите для него значение 90, 180 или 270 градусов, чтобы перевернуть компоненты на экране.
Очень важно отметить, что вращение тесно коррелировано с get_width() и get_height() помощники в ESPHome. Если установлено значение 90 или 270 градусов, ширина и высота меняются местами, то есть то, что раньше считалось шириной (более длинная ось), становится высотой, и наоборот. Это гарантирует правильную адаптацию размеров к ориентации дисплея, обеспечивая правильную визуализацию независимо от угла поворота.
Поскольку ширина и высота экрана (оси X и Y) выбираются динамически, размер треугольника будет варьироваться в зависимости от размеров дисплея. Помните об этом при рендеринге контента, поскольку это может повлиять на макет. Это поможет вам избежать разочарований и необходимости постоянно перепрошивать устройство во время тестирования.
Добавление шрифтов
Шрифты можно добавить в компонент отображения ESPHome, загрузив их локально или просто используя Google Шрифты. Вам необходимо назначить идентификаторы каждому шрифту, определить его размер и любые дополнительные символы, если это необходимо. Идентификатор вызывается в лямбда-функции компонента отображения, который отображает запрошенный вами текст с соответствующим шрифтом. Вот как можно определить шрифты:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
font: - file: "fonts/Comic Sans MS.ttf" id: comic_sans_20 size: 20 bpp: 2 - file: "gfonts://Roboto" id: roboto_20 size: 20 - file: type: gfonts family: Roboto weight: 900 id: roboto_16 size: 16 .... |
Вы можете просмотреть дополнительные примеры и параметры конфигурации на странице ESPHome. Компонент шрифта.
Добавление изображений и значков
Подобно шрифтам, изображения можно загружать на устройства ESP32 с совместимыми экранами. Компонент изображения также требуется уникальный идентификатор, который вызывается в лямбда-функции компонента дисплея. Вот как можно определить и изменить размер изображений или значков:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
image: - file: "smarthomescene.png" #Local image id: my_image resize: 100x100 type: RGBA #full color image image: - file: mdi:alert-outline #MDI Icon id: alert resize: 80x80 image: - file: https://esphome.io/_images/logo.png #URL image id: esphome_logo resize: 200x162 |
Например, предположим, что я хочу визуализировать локальное изображение в центре LILYGO T-Display S3. Мне нужно загрузить изображение в ESPHome папку, в которой находится основной .yaml файл. Или я мог бы использовать другую папку в этом каталоге.

|
1 2 3 4 5 6 7 8 9 10 11 12 |
display: ....... lambda: |- // Render the image in the center of the display it.image(it.get_width() / 2 - 175, it.get_height() / 2 - 77, id(smarthomescene)); image: - file: "the-logo.png" id: smarthomescene resize: 350x155 type: RGBA |
Чтобы центрировать изображение на дисплее S3, вам необходимо вычислить смещение от центра дисплея, вычитая половину размеров изображения. Сначала разделите ширину дисплея на 2, чтобы получить горизонтальный центр. Затем вычтите половину ширины изображения, чтобы расположить его правильно. Например, с Изображение шириной 350 пикселей (the-logo.png), вы вычитаете 175 (половину ширины). То же самое относится и к вертикали: разделите высоту дисплея на 2 и вычтите половину высоты изображения (77 для изображения высотой 155 пикселей). Этот обеспечивает изображение центрируется по обеим осям.
Предоставление объектов из Home Assistant
Как и любое другое устройство ESPHome, вы можете отображать датчики и объекты от Home Assistant в LILYGO S3 и используйте его в качестве дисплея состояния или контроллера. Это может включать в себя любую сущность, которую вы пожелаете, желательно ту, для которой вы найдете полезную функциональность. Например, предположим, что я хочу использовать этот дисплей для демонстрации своего офиса. температура и влажность с красивыми иконками. Я хочу, чтобы они располагались горизонтально посередине экрана с правой стороны интересным и читаемым шрифтом. Я хочу, чтобы логотип был меньше слева:

Во-первых, мне нужно сделать два датчика из Home Assistant доступными для платы LILYGO, добавив это в конфигурацию:
|
1 2 3 4 5 6 7 8 9 |
sensor: - platform: homeassistant entity_id: sensor.office_sensor_temperature id: office_temperature name: "Office Temperature" - platform: homeassistant entity_id: sensor.office_sensor_humidity id: office_humidity name: "Office Humidity" |
Далее мне нужно добавить шрифт и изображения (и значки) соответствующего размера. Размер 60×60 пикселей хорошо подходит для значков в моем примере, а сохранение изображения шириной 200 соответствует примерно половине экрана с некоторым пространством между ними:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
font: - file: "gfonts://Audiowide" id: audiowide_font size: 40 image: - file: mdi:thermometer id: temperature_icon resize: 60x60 - file: mdi:water_percent id: humidity_icon resize: 60x60 - file: "the-logo.png" id: smarthomescene resize: 200x88 type: rgba |
И, наконец, добавьте в код лямбду рендеринга дисплея. Эта функция использует get_height помощник для чтения высоты экрана и использования его для центрирования изображения по вертикали (умная домашняя сцена). Остальная часть функции статически устанавливает координаты X и Y для датчиков с соответствующими шрифтами и позиционирует их на экране:
|
1 2 3 4 5 6 7 |
lambda: |- int screen_height = it.get_height(); it.image(20, (screen_height - 88) / 2, id(smarthomescene)); it.image(260, 40, id(temperature_icon)); it.printf(320, 40, id(audiowide_font), Color(255, 255, 255), "%.1f°C", id(office_temperature).state); it.image(260, 140, id(humidity_icon)); it.printf(320, 140, id(audiowide_font), Color(255, 255, 255), "%.1f%%", id(office_humidity).state); |
Циклическое переключение нескольких страниц отображения
Компонент отображения ESPHome позволяет отображать несколько страниц с различным содержимым и циклически перебирать их по таймеру или по любым входным событиям. Поскольку AMOLED-дисплей LILYGO T-Display S3 оснащен двумя кнопками спереди, мы можем использовать одну из кнопок для изменения информации, отображаемой на экране. Первая кнопка использует GPIO0который представляет собой связующую булавку, поэтому мы хотим этого избежать. Второй использует GPIO21который можно бесплатно использовать в этой автоматизации.
В этом примере я создал вторую страницу с приветственным сообщением, сохранил исходную страницу с температурой и влажностью в качестве второй и создал третью страницу с некоторой базовой информацией о моем системном мониторе:

|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
display: - platform: qspi_amoled ........... pages: - id: page1 lambda: |- it.printf(10, 100, id(audiowide_font), Color(255, 255, 255), "Hello SmartHomeScene!"); - id: page2 lambda: |- int screen_height = it.get_height(); it.image(20, (screen_height - 88) / 2, id(logo)); it.image(260, 40, id(temperature_icon)); it.printf(320, 40, id(audiowide_font), Color(255, 255, 255), "%.1f°C", id(office_temperature).state); it.image(260, 140, id(humidity_icon)); it.printf(320, 140, id(audiowide_font), Color(255, 255, 255), "%.1f%%", id(office_humidity).state); - id: page3 lambda: |- // CPU Usage it.image(20, 20, id(cpu_icon)); // CPU icon it.printf(100, 20, id(audiowide_font), Color(255, 255, 255), "CPU: %.1f%%", id(system_monitor_processor_use).state); // Memory Usage it.image(20, 100, id(memory_icon)); // Memory icon it.printf(100, 100, id(audiowide_font), Color(255, 255, 255), "RAM: %.1fMB", id(system_monitor_memory_use).state); // Disk Usage it.image(20, 180, id(harddisk_icon)); // Disk icon it.printf(100, 180, id(audiowide_font), Color(255, 255, 255), "DISK: %.1fGB", id(system_monitor_disk_use).state); #Button on GPIO21 as trigger for page cycling binary_sensor: - platform: gpio pin: GPIO21 name: "Page Switch Button" on_press: then: - display.page.show_next: s3_display - component.update: s3_display # Fonts and icons for each image used font: - file: "gfonts://Audiowide" id: audiowide_font size: 40 image: - file: mdi:thermometer id: temperature_icon resize: 60x60 - file: mdi:cpu-64-bit id: cpu_icon resize: 60x60 ............ # Exposed sensors from Home Assistant sensor: - platform: homeassistant entity_id: sensor.office_sensor_temperature id: office_temperature name: "Office Temperature" name: "CPU Usage" - platform: homeassistant entity_id: sensor.system_monitor_memory_use id: system_monitor_memory_use name: "Memory Usage" ............. |
Краткий итог
Есть так много всего, что можно сделать с Механизмом рендеринга дисплея ESPHome. Это мощный и гибкий инструмент, но его сложно использовать. По моему опыту, для получения желаемого эффекта требуется много перепрошивок, настроек и перепрошивок. Если этой статьи недостаточно для начала, я настоятельно рекомендую вам просмотреть официальную документацию и попытаться понять хотя бы самые основные параметры, прежде чем прошивать этот AMOLED-экран LILYGO T-Display S3.

После стольких изменений моего кода мне удалось отобразить некоторые интересные данные о настройке моего офиса. Вот полный конфиг из этой статьи:
Полный пример конфигурации (нажмите, чтобы развернуть)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
esphome: name: lilygos3 friendly_name: LILYGO-S3 T-Display platformio_options: build_unflags: -Werror=all board_build.flash_mode: dio esp32: board: esp32-s3-devkitc-1 variant: esp32s3 framework: type: esp-idf api: encryption: key: "XXXXX" ota: - platform: esphome password: "XXXXX" wifi: ssid: !secret wifi_ssid password: !secret wifi_password ap: ssid: "S3 Fallback Hotspot" password: "XXXXX" logger: level: WARN # Define QSPI interface for the AMOLED spi: id: qspi_bus type: quad clk_pin: GPIO47 data_pins: - GPIO18 # TFT_D0 - GPIO7 # TFT_D1 - GPIO48 # TFT_D2 - GPIO5 # TFT_D3 # Configuration for the RM67162 AMOLED display display: - platform: qspi_amoled model: RM67162 id: s3_display update_interval: 10s dimensions: height: 536 width: 240 color_order: rgb brightness: 255 cs_pin: GPIO6 reset_pin: GPIO17 rotation: 270 pages: - id: page1 lambda: |- it.printf(10, 100, id(audiowide_font), Color(255, 255, 255), "Hello SmartHomeScene!"); - id: page2 lambda: |- int screen_height = it.get_height(); it.image(20, (screen_height - 88) / 2, id(logo)); it.image(260, 40, id(temperature_icon)); it.printf(320, 40, id(audiowide_font), Color(255, 255, 255), "%.1f°C", id(office_temperature).state); it.image(260, 140, id(humidity_icon)); it.printf(320, 140, id(audiowide_font), Color(255, 255, 255), "%.1f%%", id(office_humidity).state); - id: page3 lambda: |- // CPU Usage it.image(20, 20, id(cpu_icon)); // CPU icon it.printf(100, 20, id(audiowide_font), Color(255, 255, 255), "CPU: %.1f%%", id(system_monitor_processor_use).state); // Memory Usage it.image(20, 100, id(memory_icon)); // Memory icon it.printf(100, 100, id(audiowide_font), Color(255, 255, 255), "RAM: %.1fMB", id(system_monitor_memory_use).state); // Disk Usage it.image(20, 180, id(harddisk_icon)); // Disk icon it.printf(100, 180, id(audiowide_font), Color(255, 255, 255), "DISK: %.1fGB", id(system_monitor_disk_use).state); binary_sensor: - platform: gpio pin: GPIO21 # Button on GPIO21 name: "Page Switch Button" on_press: then: - display.page.show_next: s3_display - component.update: s3_display font: - file: "gfonts://Audiowide" id: audiowide_font size: 40 image: - file: mdi:thermometer id: temperature_icon resize: 60x60 - file: mdi:water_percent id: humidity_icon resize: 60x60 - file: "the-logo.png" id: logo resize: 200x88 type: rgba - file: mdi:tape-drive id: harddisk_icon resize: 60x60 - file: mdi:memory id: memory_icon resize: 60x60 - file: mdi:cpu-64-bit id: cpu_icon resize: 60x60 sensor: - platform: homeassistant entity_id: sensor.office_sensor_temperature id: office_temperature name: "Office Temperature" - platform: homeassistant entity_id: sensor.office_sensor_humidity id: office_humidity name: "Office Humidity" - platform: homeassistant entity_id: sensor.system_monitor_processor_use id: system_monitor_processor_use name: "CPU Usage" - platform: homeassistant entity_id: sensor.system_monitor_memory_use id: system_monitor_memory_use name: "Memory Usage" - platform: homeassistant entity_id: sensor.system_monitor_disk_use id: system_monitor_disk_use name: "Disk Usage" #Toggle green LED under the left button light: - platform: binary #Toggle green LED under the left button name: "Green LED" output: green_led output: - platform: gpio pin: GPIO38 id: green_led |

