LilyGo T-Dongle S3
T-Dongle S3
The LilyGo T-Dongle S3 is a development board for the ESP32-S3 module. It features a USB-A connector which doubles as a micro-SD card reader, a color LCD, an RGB LED, and a button.
The espp::TDongleS3 component provides a singleton hardware abstraction for initializing the display and LED subsystems.
API Reference
Header File
Classes
-
class TDongleS3 : public espp::BaseComponent
The TDongleS3 class provides an interface to the LilyGo T-Dongle-S3 ESP32-S3 development board.
The class provides access to the following features:
The class is a singleton and can be accessed using the get() method.
Example
espp::TDongleS3 &tdongle = espp::TDongleS3::get(); tdongle.set_log_level(espp::Logger::Verbosity::INFO); // initialize the LED if (!tdongle.initialize_led()) { logger.error("Failed to initialize led!"); return; } // initialize the LCD if (!tdongle.initialize_lcd()) { logger.error("Failed to initialize LCD!"); return; } // set the pixel buffer to be a full screen buffer static constexpr size_t pixel_buffer_size = tdongle.lcd_width() * tdongle.lcd_height(); // initialize the LVGL display for the T-Dongle-S3 if (!tdongle.initialize_display(pixel_buffer_size)) { logger.error("Failed to initialize display!"); return; } // initialize the uSD card using SdCardConfig = espp::TDongleS3::SdCardConfig; SdCardConfig sdcard_config{}; if (!tdongle.initialize_sdcard(sdcard_config)) { logger.warn("Failed to initialize SD card, continuing without it."); } // initialize the button, which we'll use to cycle the rotation of the display logger.info("Initializing the button"); lv_obj_t *bg = nullptr; lv_obj_t *label = nullptr; static auto update_layout = [&]() { int width = tdongle.rotated_display_width(); int height = tdongle.rotated_display_height(); lv_obj_set_size(bg, width, height); lv_obj_align(label, LV_ALIGN_CENTER, 0, 0); if (circle_layer) { lv_obj_set_size(circle_layer, width, height); lv_obj_align(circle_layer, LV_ALIGN_CENTER, 0, 0); lv_obj_move_foreground(circle_layer); lv_obj_invalidate(circle_layer); } }; auto on_button_pressed = [&](const auto &event) { if (event.active) { // lock the display mutex std::lock_guard<std::mutex> lock(lvgl_mutex); static auto rotation = LV_DISPLAY_ROTATION_0; rotation = static_cast<lv_display_rotation_t>((static_cast<int>(rotation) + 1) % 4); fmt::print("Setting rotation to {}\n", (int)rotation); lv_display_t *disp = lv_display_get_default(); lv_disp_set_rotation(disp, rotation); update_layout(); } }; tdongle.initialize_button(on_button_pressed); // set the LED to be red espp::Hsv hsv(150.0f, 1.0f, 1.0f); float brightness = 5.0f; // 5% brightness tdongle.led(hsv, brightness); // set the background color to black bg = lv_obj_create(lv_screen_active()); lv_obj_set_size(bg, tdongle.rotated_display_width(), tdongle.rotated_display_height()); lv_obj_set_style_bg_color(bg, lv_color_make(0, 0, 0), 0); if (!initialize_circle_layer(tdongle.rotated_display_width(), tdongle.rotated_display_height())) { logger.error("Failed to initialize circle layer!"); return; } // add text in the center of the screen label = lv_label_create(lv_screen_active()); lv_label_set_text(label, "Drawing circles\nto the screen."); lv_obj_align(label, LV_ALIGN_CENTER, 0, 0); lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0); update_layout(); lv_obj_move_foreground(circle_layer); // start a simple thread to do the lv_task_handler every 16ms espp::Task lv_task({.callback = [](std::mutex &m, std::condition_variable &cv) -> bool { { // lock the display mutex std::lock_guard<std::mutex> lock(lvgl_mutex); lv_task_handler(); } std::unique_lock<std::mutex> lock(m); cv.wait_for(lock, 16ms); return false; }, .task_config = { .name = "lv_task", .core_id = 1, }}); lv_task.start(); // set the display brightness to be 75% tdongle.brightness(75.0f); // make a task to constantly shift the hue of the LED espp::Task led_task({.callback = [&](std::mutex &m, std::condition_variable &cv) -> bool { static float hue = 0.0f; hue += 1.0f; if (hue >= 360.0f) { hue = 0.0f; } espp::Hsv hsv(hue, 1.0f, 1.0f); espp::TDongleS3::get().led(hsv, brightness); std::unique_lock<std::mutex> lock(m); cv.wait_for(lock, 25ms); return false; }, .task_config = { .name = "led_task", }}); led_task.start(); while (true) { auto start = esp_timer_get_time(); // if there are 10 circles on the screen, clear them if (visible_circle_count >= MAX_CIRCLES) { // lock the lvgl mutex std::lock_guard<std::mutex> lock(lvgl_mutex); clear_circles(); } else { // draw a circle of circles on the screen (just draw the next circle) int middle_x = tdongle.rotated_display_width() / 2; int middle_y = tdongle.rotated_display_height() / 2; static constexpr int radius = 30; float angle = visible_circle_count * 2.0f * M_PI / MAX_CIRCLES; int x = middle_x + radius * cos(angle); int y = middle_y + radius * sin(angle); // lock the lvgl mutex std::lock_guard<std::mutex> lock(lvgl_mutex); draw_circle(x, y, 5); } auto end = esp_timer_get_time(); auto elapsed = end - start; std::this_thread::sleep_for(100ms - std::chrono::microseconds(elapsed)); }
Public Types
-
using button_callback_t = espp::Interrupt::event_callback_fn
Alias for the button callback function.
-
using Pixel = lv_color16_t
Alias for the pixel type used by the T-Dongle-S3 display.
Public Functions
-
espp::Interrupt &interrupts()
Get a reference to the interrupts
- Returns:
A reference to the interrupts
-
bool initialize_button(const button_callback_t &callback = nullptr)
Initialize the button
- Parameters:
callback – The callback function to call when the button is pressed
- Returns:
true if the button was successfully initialized, false otherwise
-
bool button_state() const
Get the button state
- Returns:
The button state (true = button pressed, false = button released)
-
bool initialize_led()
Initialize the RGB LED
- Returns:
true if the RGB LED was successfully initialized, false otherwise
-
std::shared_ptr<LedStrip> led() const
Get a shared pointer to the RGB LED
- Returns:
A shared pointer to the RGB LED
-
bool led(const Hsv &hsv, float brightness = 100.0f)
Set the color of the LED
- Parameters:
hsv – The color of the LED in HSV format
brightness – The brightness of the LED as a percentage (0 - 100)
- Returns:
true if the color was successfully set, false otherwise
-
bool led(const Rgb &rgb, float brightness = 100.0f)
Set the color of the LED
- Parameters:
rgb – The color of the LED in RGB format
brightness – The brightness of the LED as a percentage (0 - 100)
- Returns:
true if the color was successfully set, false otherwise
-
bool initialize_lcd()
Initialize the LCD (low level display driver)
- Returns:
true if the LCD was successfully initialized, false otherwise
-
bool initialize_display(size_t pixel_buffer_size)
Initialize the display (lvgl display driver)
Note
This will also allocate two full frame buffers in the SPIRAM
- Parameters:
pixel_buffer_size – The size of the pixel buffer
- Returns:
true if the display was successfully initialized, false otherwise
-
size_t rotated_display_width() const
Get the display width in pixels, according to the current orientation
- Returns:
The display width in pixels, according to the current orientation
-
size_t rotated_display_height() const
Get the display height in pixels, according to the current orientation
- Returns:
The display height in pixels, according to the current orientation
-
std::shared_ptr<Display<Pixel>> display() const
Get a shared pointer to the display
- Returns:
A shared pointer to the display
-
void brightness(float brightness)
Set the brightness of the backlight
Note
This function will only work after initialize_lcd() has been called
- Parameters:
brightness – The brightness of the backlight as a percentage (0 - 100)
-
float brightness() const
Get the brightness of the backlight
Note
This function will only work after initialize_lcd() has been called
- Returns:
The brightness of the backlight as a percentage (0 - 100)
-
Pixel *vram0() const
Get the VRAM 0 pointer (DMA memory used by LVGL)
Note
This is the memory used by LVGL for rendering
Note
This is null unless initialize_display() has been called
- Returns:
The VRAM 0 pointer
-
Pixel *vram1() const
Get the VRAM 1 pointer (DMA memory used by LVGL)
Note
This is the memory used by LVGL for rendering
Note
This is null unless initialize_display() has been called
- Returns:
The VRAM 1 pointer
-
uint8_t *frame_buffer0() const
Get the frame buffer 0 pointer
Note
This memory is designed to be used by the application developer and is provided as a convenience. It is not used by the display driver.
Note
This is null unless initialize_display() has been called
- Returns:
The frame buffer 0 pointer
-
uint8_t *frame_buffer1() const
Get the frame buffer 1 pointer
Note
This memory is designed to be used by the application developer and is provided as a convenience. It is not used by the display driver.
Note
This is null unless initialize_display() has been called
- Returns:
The frame buffer 1 pointer
-
void write_lcd_frame(const uint16_t x, const uint16_t y, const uint16_t width, const uint16_t height, uint8_t *data)
Write a frame to the LCD
Note
This method queues the data to be written to the LCD, only blocking if there is an ongoing SPI transaction
- Parameters:
x – The x coordinate
y – The y coordinate
width – The width of the frame, in pixels
height – The height of the frame, in pixels
data – The data to write
-
bool initialize_sdcard(const SdCardConfig &config)
Initialize the uSD card
- Parameters:
config – The configuration for the uSD card
- Returns:
True if the uSD card was initialized properly.
-
inline sdmmc_card_t *sdcard() const
Get the uSD card
Note
The uSD card is only available if it was successfully initialized and the mount point is valid
- Returns:
A pointer to the uSD card
-
inline const std::string &get_name() const
Get the name of the component
Note
This is the tag of the logger
- Returns:
A const reference to the name of the component
-
inline void set_log_tag(const std::string_view &tag)
Set the tag for the logger
- Parameters:
tag – The tag to use for the logger
-
inline espp::Logger::Verbosity get_log_level() const
Get the log level for the logger
See also
See also
- Returns:
The verbosity level of the logger
-
inline void set_log_level(espp::Logger::Verbosity level)
Set the log level for the logger
See also
See also
- Parameters:
level – The verbosity level to use for the logger
-
inline void set_log_verbosity(espp::Logger::Verbosity level)
Set the log verbosity for the logger
See also
See also
See also
Note
This is a convenience method that calls set_log_level
- Parameters:
level – The verbosity level to use for the logger
-
inline espp::Logger::Verbosity get_log_verbosity() const
Get the log verbosity for the logger
See also
See also
See also
Note
This is a convenience method that calls get_log_level
- Returns:
The verbosity level of the logger
-
inline void set_log_rate_limit(std::chrono::duration<float> rate_limit)
Set the rate limit for the logger
See also
Note
Only calls to the logger that have _rate_limit suffix will be rate limited
- Parameters:
rate_limit – The rate limit to use for the logger
Public Static Functions
-
static inline TDongleS3 &get()
Access the singleton instance of the TDongleS3 class.
- Returns:
Reference to the singleton instance of the TDongleS3 class
-
static inline constexpr size_t num_leds()
Get the number of LEDs in the strip
- Returns:
The number of LEDs in the strip
-
static inline constexpr size_t lcd_width()
Get the width of the LCD in pixels
- Returns:
The width of the LCD in pixels
-
static inline constexpr size_t lcd_height()
Get the height of the LCD in pixels
- Returns:
The height of the LCD in pixels
-
static inline constexpr auto get_lcd_dc_gpio()
Get the GPIO pin for the LCD data/command signal
- Returns:
The GPIO pin for the LCD data/command signal
Public Static Attributes
-
struct SdCardConfig
Configuration for the uSD card.
-
using button_callback_t = espp::Interrupt::event_callback_fn