Skip to content

Add initial support for LilyGO T-HMI S3 with device configuration and…#509

Open
Revers-BR wants to merge 8 commits intoTactilityProject:mainfrom
Revers-BR:lilygo_thmi_s3
Open

Add initial support for LilyGO T-HMI S3 with device configuration and…#509
Revers-BR wants to merge 8 commits intoTactilityProject:mainfrom
Revers-BR:lilygo_thmi_s3

Conversation

@Revers-BR
Copy link
Contributor

@Revers-BR Revers-BR commented Feb 21, 2026

Add initial support for LilyGO T-HMI S3 with device configuration and driver implementations

📝 Description

This commit adds complete support for the LilyGO T-HMI S3, an ESP32-S3 based display device, including all the necessary hardware configurations, drivers, and initialization routines.

🎯 What's Added

Configuration Files

  • device.properties – Hardware specifications (16MB flash, SPI RAM in OCT mode, 2.8" 240×320 display, 125 DPI)

  • devicetree.yaml – Device tree dependencies and bindings

  • lilygo,thmi-s3.dts – Device tree source with GPIO configuration (49 GPIO pins)

  • Core Implementation

  • Configuration.cpp – Hardware initialization factory, registering power, SD card, display, and single-button device

  • Init.cpp – Boot initialization including power signal setup (GPIO 10 & 14) and PWM backlight initialization (GPIO 38)

  • CMakeLists.txt – Build configuration with required dependencies

  • module.cpp – Module registration structure

Driver Components

  • Display (Display.cpp/h) – ST7789 display controller via i8080 interface with XPT2046 resistive touch support
  • Power (Power.cpp/h) – Estimated power monitoring via ADC with 2.11x multiplier
  • SD Card (SdCard.cpp/h) – SDMMC interface (1-bit mode on GPIO 11-13)

✨ Key Features

  • 240×320 resolution with 16-bit color depth
  • Single button interface (GPIO 0)
  • SD card mounting at boot
  • PWM backlight control
  • TinyUSB support

🔧 Dependencies

  • ButtonControl
  • XPT2046SoftSPI
  • PwmBacklight
  • EstimatedPower
  • ST7789-i8080
  • driver, vfs, fatfs (ESP-IDF)

📋 Hardware Details

Display Interface

  • Controller: ST7789 via Intel 8080 parallel interface
  • Resolution: 240×320 pixels
  • Color Depth: 16-bit
  • Touch: XPT2046 resistive touch via SPI

Power Management

  • GPIO 10: Power enable signal
  • GPIO 14: Power on signal
  • GPIO 38: Backlight PWM control
  • ADC-based voltage monitoring with 2.11x compensation

SD Card Interface

  • Mode: 1-bit SDMMC
  • GPIO 11: CMD line
  • GPIO 12: Clock line
  • GPIO 13: Data line
  • Behavior: Mounted at boot

Button

  • GPIO 0: Button
  • Type: Single-button interface

🚀 Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

✅ Checklist

  • Code follows project style guidelines
  • Self-review completed
  • Display driver implementation verified
  • Power management functional
  • Button (GPIO 0) working
  • Hardware mappings are correct
  • Dependencies are properly declared
  • SD Card driver fully functional

https://lilygo.cc/en-us/products/t-hmi?srsltid=AfmBOormg1UM8XAeQ7_zp3xhivIllgu3_fREdxg7SxNG8-itKqPkpL2D

IMG_20260221_094655

IMG_20260221_094647

IMG_20260221_094640

IMG_20260221_094631

@coderabbitai
Copy link

coderabbitai bot commented Feb 21, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds support for the LilyGO T‑HMI S3 (ESP32‑S3): an ESP‑IDF CMake component registration with a recursive Source/.c glob; boot initialization function initBoot that configures power GPIOs and initializes PWM backlight; factory implementations and headers for display (ST7789 i8080 with XPT2046 touch), power (EstimatedPower with ADC multiplier), and SD card (SDMMC); a device module export; device.properties and devicetree (lilygo,thmi-s3.dts) files; and GPIO/resolution constants and wiring functions.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title correctly describes the primary change: adding initial support for the LilyGO T-HMI S3 device with hardware configuration and driver implementations.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (5)
Devices/lilygo-thmi-s3/Source/devices/SdCard.h (1)

7-7: Avoid using declarations at file scope in headers.

using tt::hal::sdcard::SdCardDevice; in a header pollutes the global namespace of every includer. Consider using the fully-qualified name in the declaration on line 14 instead, or moving the using inside a namespace or function scope.

♻️ Suggested fix
-using tt::hal::sdcard::SdCardDevice;
-
 constexpr auto SD_DIO_CMD = GPIO_NUM_11;
 constexpr auto SD_DIO_SCLK = GPIO_NUM_12;
 constexpr auto SD_DIO_DATA0 = GPIO_NUM_13;
 constexpr auto SD_DIO_NC = GPIO_NUM_NC;
 
-std::shared_ptr<SdCardDevice> createSdCard();
+std::shared_ptr<tt::hal::sdcard::SdCardDevice> createSdCard();
Devices/lilygo-thmi-s3/lilygo,thmi-s3.dts (1)

5-6: Unused includes: esp32_i2c.h and esp32_spi.h.

No I2C or SPI nodes are defined in this DTS. If these are placeholders for future expansion, consider adding a brief comment; otherwise, they can be removed to keep the file minimal.

Devices/lilygo-thmi-s3/Source/Init.cpp (1)

5-5: SystemEvents.h appears unused — remove or verify.

No SystemEvents symbols are referenced anywhere in this translation unit. This looks like a stale include. Also note the inconsistency: line 5 uses "..." quoting while line 6 uses <...> for a sibling Tactility header.

🔧 Proposed change
-#include "Tactility/kernel/SystemEvents.h"
 `#include` <Tactility/TactilityCore.h>
Devices/lilygo-thmi-s3/Source/devices/Display.cpp (1)

8-8: touchSpiInitialized is declared but never read or written — dead code.

This static flag is initialized to false and never referenced again anywhere in the file. Remove it.

🔧 Proposed change
-static bool touchSpiInitialized = false;
-
 static std::shared_ptr<tt::hal::touch::TouchDevice> createTouch() {
Devices/lilygo-thmi-s3/Source/devices/Display.h (1)

5-5: #include "driver/spi_common.h" appears to be an unused include.

The display bus is i8080 (parallel); touch uses soft-SPI, not the hardware SPI bus. No types from spi_common.h are referenced in this header or in Display.cpp. Consider removing it to keep the include surface minimal.

🔧 Proposed change
 `#include` <driver/gpio.h>
 `#include` <Tactility/hal/display/DisplayDevice.h>
-
-#include "driver/spi_common.h"

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
Devices/lilygo-thmi-s3/Source/devices/Power.h (1)

7-10: Consider wrapping constants and factory declaration in a dedicated namespace.

THMI_S3_POWEREN_GPIO, THMI_S3_POWERON_GPIO, and createPower() all live in the global namespace. The device-prefix mitigates collision risk, but a namespace (e.g., lilygo::thmi_s3) would make the ownership explicit and consistent with how the rest of the HAL is structured under tt::hal::*.

♻️ Suggested namespace wrapping
+namespace lilygo::thmi_s3 {
+
 constexpr auto THMI_S3_POWEREN_GPIO = GPIO_NUM_10;
 constexpr auto THMI_S3_POWERON_GPIO = GPIO_NUM_14;

 std::shared_ptr<tt::hal::power::PowerDevice> createPower();
+
+} // namespace lilygo::thmi_s3

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
Devices/lilygo-thmi-s3/Source/Configuration.cpp (1)

10-12: Redundant tt::hal:: prefix with using namespace tt::hal in scope.

Line 12 still qualifies Device with the full namespace despite the using namespace tt::hal directive on line 10. The Configuration on line 21 already drops the prefix. Consider being consistent.

♻️ Proposed fix
-static std::vector<std::shared_ptr<tt::hal::Device>> createDevices() {
+static std::vector<std::shared_ptr<Device>> createDevices() {

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
Devices/lilygo-thmi-s3/Source/devices/SdCard.h (1)

7-7: Avoid using declarations at global scope in headers.

using tt::hal::sdcard::SdCardDevice; in a header injects SdCardDevice into the global namespace of every translation unit that includes SdCard.h. Prefer the fully-qualified name in the declaration and drop the using.

♻️ Proposed fix
-using tt::hal::sdcard::SdCardDevice;
-
 constexpr auto SD_DIO_CMD = GPIO_NUM_11;
 constexpr auto SD_DIO_SCLK = GPIO_NUM_12;
 constexpr auto SD_DIO_DATA0 = GPIO_NUM_13;
 constexpr auto SD_DIO_NC = GPIO_NUM_NC;
 constexpr auto SD_DIO_BUS_WIDTH = 1;

-std::shared_ptr<SdCardDevice> createSdCard();
+std::shared_ptr<tt::hal::sdcard::SdCardDevice> createSdCard();

The using in SdCard.cpp (.cpp scope) is fine and can stay as-is.

Copy link
Contributor

@KenVanHoeylandt KenVanHoeylandt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution! I've reviewed it and left some comments.

Don't forget to update .github/workflows/build.yml, otherwise the build won't pick it up.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file can be removed

gpio0 {
compatible = "espressif,esp32-gpio";
gpio-count = <49>;
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A SPI device is missing here.

Comment on lines 1 to 34
#pragma once
#include <driver/gpio.h>
#include <Tactility/hal/display/DisplayDevice.h>

#include "driver/spi_common.h"

class St7789i8080Display;

constexpr auto DISPLAY_CS = GPIO_NUM_6;
constexpr auto DISPLAY_DC = GPIO_NUM_7;
constexpr auto DISPLAY_WR = GPIO_NUM_8;
constexpr auto DISPLAY_RD = GPIO_NUM_NC;
constexpr auto DISPLAY_RST = GPIO_NUM_NC;
constexpr auto DISPLAY_BL = GPIO_NUM_38;
constexpr auto DISPLAY_I80_D0 = GPIO_NUM_48;
constexpr auto DISPLAY_I80_D1 = GPIO_NUM_47;
constexpr auto DISPLAY_I80_D2 = GPIO_NUM_39;
constexpr auto DISPLAY_I80_D3 = GPIO_NUM_40;
constexpr auto DISPLAY_I80_D4 = GPIO_NUM_41;
constexpr auto DISPLAY_I80_D5 = GPIO_NUM_42;
constexpr auto DISPLAY_I80_D6 = GPIO_NUM_45;
constexpr auto DISPLAY_I80_D7 = GPIO_NUM_46;
constexpr auto DISPLAY_HORIZONTAL_RESOLUTION = 240;
constexpr auto DISPLAY_VERTICAL_RESOLUTION = 320;

// Touch (XPT2046, resistive, shared SPI with display)
constexpr auto TOUCH_MISO_PIN = GPIO_NUM_4;
constexpr auto TOUCH_MOSI_PIN = GPIO_NUM_3;
constexpr auto TOUCH_SCK_PIN = GPIO_NUM_1;
constexpr auto TOUCH_CS_PIN = GPIO_NUM_2;
constexpr auto TOUCH_IRQ_PIN = GPIO_NUM_9;

// Factory function for registration
std::shared_ptr<tt::hal::display::DisplayDevice> createDisplay();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment doesn't seem to be correct. The display isn't a SPI one.

DISPLAY_VERTICAL_RESOLUTION
);

return std::make_shared<Xpt2046SoftSpi>(std::move(config));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There doesn't seem to be a need to use SoftSPI here. SoftSPI is only for when you need a third SPI device, but a hardware one is not available.
Xpt2046Touch should be used instead, unless there's a really good reason to not use it (and then it should be documented here)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried using Xpt2046 Touch

However, there was no touch response.

Only Xpt2046SoftSpi worked without problems, passing all the necessary pins.

I don't have much knowledge about this; I practically only adapted it from lilygo-tdisplay to thmi.

But after publication, if you want to make the adjustments, I can test it and get back to you.

return ERROR_NONE;
}

/** @warning The variable name must be exactly "device_module" */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment can be removed as it's not correct anymore.

}

/** @warning The variable name must be exactly "device_module" */
struct Module device_module = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The naming recently changed: it should now be "lilygo_thmi_s3_module".
The documentation wasn't updated for this yet, sorry!

it's basically the device id, replacing "-" with "_" and post-fixed with "_module".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants