LilyGo T-Deck
T-Deck
The LilyGo T-Deck is a development board for the ESP32-S3 module. It features a nice touchscreen display and expansion headers.
The espp::TDeck component provides a singleton hardware abstraction for initializing the touch, display, audio, and micro-SD card subsystems.
API Reference
Header File
Classes
-
class TDeck : public espp::BaseComponent
The TDeck class provides an interface to the LilyGo T-Deck ESP32-S3 development board.
The class provides access to the following features:
Touchpad
Keyboard
Audio
Interrupts
I2C
microSD (uSD) card
For more information, see https://github.com/Xinyuan-LilyGO/T-Deck/tree/master and https://github.com/Xinyuan-LilyGO/T-Deck/blob/master/examples/UnitTest/utilities.h
The class is a singleton and can be accessed using the get() method.
Example
espp::TDeck &tdeck = espp::TDeck::get(); tdeck.set_log_level(espp::Logger::Verbosity::INFO); static auto rotation = LV_DISPLAY_ROTATION_0; auto keypress_callback = [&](uint8_t key) { logger.info("Key pressed: {}", key); if (key == 8) { // delete key will clear the circles logger.info("Clearing circles"); std::lock_guard<std::recursive_mutex> lock(lvgl_mutex); clear_circles(); } else if (key == ' ') { // space key will rotate the display logger.info("Rotating display"); std::lock_guard<std::recursive_mutex> lock(lvgl_mutex); clear_circles(); rotation = static_cast<lv_display_rotation_t>((static_cast<int>(rotation) + 1) % 4); lv_display_t *disp = lv_display_get_default(); lv_disp_set_rotation(disp, rotation); } else if (key == 'm') { // 'm' key will toggle audio mute logger.info("Toggling mute"); tdeck.mute(!tdeck.is_muted()); logger.info("Muted: {}", tdeck.is_muted()); } else if (key == 'n') { // 'n' key will decrease audio volume (left of 'm' key) logger.info("Decreasing volume"); tdeck.volume(tdeck.volume() - 10.0f); logger.info("Volume: {}", tdeck.volume()); } else if (key == '$') { // '$' key will increase audio volume (right of 'm' key) logger.info("Increasing volume"); tdeck.volume(tdeck.volume() + 10.0f); logger.info("Volume: {}", tdeck.volume()); } }; auto touch_callback = [&](const auto &touch) { // NOTE: since we're directly using the touchpad data, and not using the // TouchpadInput + LVGL, we'll need to ensure the touchpad data is // converted into proper screen coordinates instead of simply using the // raw values. static auto previous_touchpad_data = tdeck.touchpad_convert(touch); auto touchpad_data = tdeck.touchpad_convert(touch); if (touchpad_data != previous_touchpad_data) { logger.info("Touch: {}", touchpad_data); previous_touchpad_data = touchpad_data; // if there is a touch point, draw a circle if (touchpad_data.num_touch_points > 0) { play_click(tdeck); std::lock_guard<std::recursive_mutex> lock(lvgl_mutex); draw_circle(touchpad_data.x, touchpad_data.y, 10); } } }; auto trackball_callback = [&](const auto &trackball) { logger.debug("Trackball: {}", trackball); }; // initialize the uSD card using SdCardConfig = espp::TDeck::SdCardConfig; SdCardConfig sdcard_config{}; if (!tdeck.initialize_sdcard(sdcard_config)) { logger.warn("Failed to initialize uSD card, there may not be a uSD card inserted!"); } // initialize the Keyboard bool start_task = true; if (!tdeck.initialize_keyboard(start_task, keypress_callback)) { logger.error("Failed to initialize Keyboard!"); return; } // initialize the sound if (!tdeck.initialize_sound()) { logger.error("Failed to initialize sound!"); return; } // initialize the LCD if (!tdeck.initialize_lcd()) { logger.error("Failed to initialize LCD!"); return; } // set the pixel buffer to be 50 lines high static constexpr size_t pixel_buffer_size = tdeck.lcd_width() * 50; // initialize the LVGL display for the T-Deck if (!tdeck.initialize_display(pixel_buffer_size)) { logger.error("Failed to initialize display!"); return; } // initialize the touchpad if (!tdeck.initialize_touch(touch_callback)) { logger.error("Failed to initialize touchpad!"); return; } // initialize the trackball if (!tdeck.initialize_trackball(trackball_callback)) { logger.error("Failed to initialize trackball!"); return; } // set the background color to black lv_obj_t *bg = lv_obj_create(lv_screen_active()); lv_obj_set_size(bg, tdeck.lcd_width(), tdeck.lcd_height()); lv_obj_set_style_bg_color(bg, lv_color_make(0, 0, 0), 0); // add text in the center of the screen lv_obj_t *label = lv_label_create(lv_screen_active()); lv_label_set_text(label, "Touch the screen!\nPress the delete key to clear circles.\nPress the " "space key to rotate the display."); lv_obj_align(label, LV_ALIGN_CENTER, 0, 0); lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0); // add a button in the top left which (when pressed) will rotate the display // through 0, 90, 180, 270 degrees lv_obj_t *btn = lv_btn_create(lv_screen_active()); lv_obj_set_size(btn, 50, 50); lv_obj_align(btn, LV_ALIGN_TOP_LEFT, 0, 0); lv_obj_t *label_btn = lv_label_create(btn); lv_label_set_text(label_btn, LV_SYMBOL_REFRESH); // center the text in the button lv_obj_align(label_btn, LV_ALIGN_CENTER, 0, 0); lv_obj_add_event_cb( btn, [](auto event) { std::lock_guard<std::recursive_mutex> lock(lvgl_mutex); clear_circles(); rotation = static_cast<lv_display_rotation_t>((static_cast<int>(rotation) + 1) % 4); lv_display_t *disp = lv_display_get_default(); lv_disp_set_rotation(disp, rotation); }, LV_EVENT_PRESSED, nullptr); // disable scrolling on the screen (so that it doesn't behave weirdly when // rotated and drawing with your finger) lv_obj_set_scrollbar_mode(lv_screen_active(), LV_SCROLLBAR_MODE_OFF); lv_obj_clear_flag(lv_screen_active(), LV_OBJ_FLAG_SCROLLABLE); // 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 { { std::lock_guard<std::recursive_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", }}); lv_task.start(); // load the audio file (wav file bundled in memory) size_t wav_size = load_audio(); logger.info("Loaded {} bytes of audio", wav_size); // unmute the audio and set the volume to 20% tdeck.mute(false); tdeck.volume(20.0f); // set the display brightness to be 75% tdeck.brightness(75.0f); // now just loop forever while (true) { std::this_thread::sleep_for(1s); }
Note
The keyboard has a backlight, which you can control with the shortcut
alt + b
. The keyboard backlight is off by default.Public Types
-
using button_callback_t = espp::Interrupt::event_callback_fn
Alias for the button callback function.
-
using keypress_callback_t = TKeyboard::key_cb_fn
Alias for the keypress callback for keyboard keypresses.
-
using touch_callback_t = std::function<void(const TouchpadData&)>
Alias for the touch callback when touch events are received.
-
using trackball_callback_t = std::function<void(const PointerData&)>
Alias for the callback used to inform the user code of new trackball data.
Public Functions
-
inline I2c &internal_i2c()
Get a reference to the internal I2C bus
Note
The internal I2C bus is used for the touchscreen
- Returns:
A reference to the internal I2C bus
-
inline espp::Interrupt &interrupts()
Get a reference to the interrupts
- Returns:
A reference to the interrupts
-
void peripheral_power(bool on)
Enable or disable power to the peripherals
- Parameters:
on – Whether to enable or disable power to the peripherals
-
bool peripheral_power() const
Get the state of the peripheral power
- Returns:
true if power is enabled, false otherwise
-
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
-
bool initialize_keyboard(bool start_task = true, const keypress_callback_t &key_cb = nullptr, std::chrono::milliseconds poll_interval = std::chrono::milliseconds(10))
Initialize the keyboard
See also
See also
Note
The Keyboard has an interrupt pin connected from it (the esp32c3) to the main esp32s3. However, the default firmware on the keyboard (esp32c3) does not use this interrupt pin. Instead, the main esp32s3 must poll the keyboard to get key presses. This is done by the keyboard task. If you update the firmware on the keyboard to use the interrupt pin, you can set start_task to false and wire up the interrupt to the keyboard()->read_key() method.
- Parameters:
start_task – Whether to start the keyboard task
key_cb – The key callback function, called when a key is pressed if not null and the keyboard task is started
poll_interval – The interval at which to poll the keyboard
- Returns:
true if the keyboard was successfully initialized, false otherwise
-
std::shared_ptr<TKeyboard> keyboard() const
Get the keyboard
Note
The keyboard is only available if it was successfully initialized
- Returns:
A shared pointer to the keyboard
-
bool initialize_trackball(const trackball_callback_t &trackball_cb = nullptr, int sensitivity = 10)
Initialize the trackball
See also
See also
See also
See also
- Parameters:
trackball_cb – The trackball callback function, called when the trackball is moved if not null
sensitivity – The sensitivity of the trackball. The higher the sensitivity, the faster the trackball will move
- Returns:
true if the trackball was successfully initialized, false otherwise
-
std::shared_ptr<PointerInput> trackball() const
Get the trackball
See also
See also
See also
Note
The trackball is only available if it was successfully initialized
Note
This is the same as the pointer_input() method
- Returns:
A shared pointer to the trackball
-
void set_trackball_sensitivity(int sensitivity)
Set the trackball sensitivity
Note
The sensitivity can be negative, which will invert the direction of the trackball
- Parameters:
sensitivity – The sensitivity of the trackball. The higher the sensitivity, the faster the trackball will move
-
std::shared_ptr<PointerInput> pointer_input() const
Get the pointer input for the trackball
- Returns:
A shared pointer to the pointer input for the trackball
-
PointerData trackball_data() const
Get the most recent trackball data
- Returns:
The trackball data
-
void trackball_read(int &x, int &y, bool &left_pressed, bool &right_pressed)
Get the most recent trackball data
Note
This method is a convenience method for integrating with LVGL, the data it returns is identical to the data returned by the trackball_data() method
- Parameters:
x – The x coordinate
y – The y coordinate
left_pressed – Whether the left button is pressed
right_pressed – Whether the right button is pressed
-
bool initialize_touch(const touch_callback_t &touch_cb = nullptr)
Initialize the touchpad
Note
This will configure the touchpad interrupt pin which will automatically call the touch callback function when the touchpad is touched
Warning
This method should be called after the display has been initialized if you want the touchpad to be recognized and used with LVGL and its objects.
- Parameters:
touch_cb – The touch callback function, called when the touchpad is touched if not null
- Returns:
true if the touchpad was successfully initialized, false otherwise
-
std::shared_ptr<TouchpadInput> touchpad_input() const
Get the touchpad input
- Returns:
A shared pointer to the touchpad input
-
TouchpadData touchpad_data() const
Get the most recent touchpad data
- Returns:
The touchpad data
-
void touchpad_read(uint8_t *num_touch_points, uint16_t *x, uint16_t *y, uint8_t *btn_state)
Get the most recent touchpad data
See also
Note
This method is a convenience method for integrating with LVGL, the data it returns is identical to the data returned by the touchpad_data() method
- Parameters:
num_touch_points – The number of touch points
x – The x coordinate
y – The y coordinate
btn_state – The button state (0 = button released, 1 = button pressed)
-
TouchpadData touchpad_convert(const TouchpadData &data) const
Convert touchpad data from raw reading to display coordinates
Note
Uses the touch_invert_x and touch_invert_y settings to determine if the x and y coordinates should be inverted
- Parameters:
data – The touchpad data to convert
- Returns:
The converted touchpad data
-
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
-
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
- Parameters:
brightness – The brightness of the backlight as a percentage (0 - 100)
-
float brightness() const
Get the brightness of the backlight
- 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_command(uint8_t command, std::span<const uint8_t> parameters, uint32_t user_data)
Write command and optional parameters to the LCD
Note
This method is designed to be used by the display driver
Note
This method queues the data to be written to the LCD, only blocking if there is an ongoing SPI transaction
- Parameters:
command – The command to write
parameters – The command parameters to write
user_data – User data to pass to the spi transaction callback
-
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
-
void write_lcd_lines(int xs, int ys, int xe, int ye, const uint8_t *data, uint32_t user_data)
Write lines 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:
xs – The x start coordinate
ys – The y start coordinate
xe – The x end coordinate
ye – The y end coordinate
data – The data to write
user_data – User data to pass to the spi transaction callback
- bool initialize_sound (uint32_t default_audio_rate=48000, const espp::Task::BaseConfig &task_config={ .name="audio",.stack_size_bytes=4096,.priority=19,.core_id=1})
Initialize the sound subsystem
- Parameters:
default_audio_rate – The default audio rate
task_config – The task configuration for the audio task
- Returns:
true if the sound subsystem was successfully initialized, false otherwise
-
uint32_t audio_sample_rate() const
Get the audio sample rate
- Returns:
The audio sample rate, in Hz
-
void audio_sample_rate(uint32_t sample_rate)
Set the audio sample rate
- Parameters:
sample_rate – The audio sample rate, in Hz
-
size_t audio_buffer_size() const
Get the audio buffer size
- Returns:
The audio buffer size, in bytes
-
void mute(bool mute)
Mute or unmute the audio
- Parameters:
mute – true to mute the audio, false to unmute the audio
-
bool is_muted() const
Check if the audio is muted
- Returns:
true if the audio is muted, false otherwise
-
void volume(float volume)
Set the volume
- Parameters:
volume – The volume in percent (0 - 100)
-
float volume() const
Get the volume
- Returns:
The volume in percent (0 - 100)
-
void play_audio(const std::vector<uint8_t> &data)
Play audio
- Parameters:
data – The audio data to play
-
void play_audio(const uint8_t *data, uint32_t num_bytes)
Play audio
- Parameters:
data – The audio data to play
num_bytes – The number of bytes to play
-
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 TDeck &get()
Access the singleton instance of the TDeck class.
- Returns:
Reference to the singleton instance of the TDeck class
-
static inline constexpr auto peripheral_power_pin()
Get the GPIO pin for the peripheral power
Note
This pin is used to enable/disable power to the peripherals, such as the keyboard, screen, etc.
- Returns:
The GPIO pin for the peripheral power
-
static inline constexpr auto keyboard_interrupt()
Get the GPIO pin for the keyboard interrupt
Note
This pin is used to detect when a key is pressed on the keyboard and is connected to the main esp32s3, however the default firmware on the keyboard does not use this pin
- Returns:
The GPIO pin for the keyboard interrupt
-
static inline constexpr auto trackball_up_gpio()
Get the GPIO pin for the trackball up button
- Returns:
The GPIO pin for the trackball up button
-
static inline constexpr auto trackball_down_gpio()
Get the GPIO pin for the trackball down button
- Returns:
The GPIO pin for the trackball down button
-
static inline constexpr auto trackball_left_gpio()
Get the GPIO pin for the trackball left button
- Returns:
The GPIO pin for the trackball left button
-
static inline constexpr auto trackball_right_gpio()
Get the GPIO pin for the trackball right button
- Returns:
The GPIO pin for the trackball right button
-
static inline constexpr auto trackball_btn_gpio()
Get the GPIO pin for the trackball button
- Returns:
The GPIO pin for the trackball button
-
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
-
static inline constexpr auto get_mute_pin()
Get the GPIO pin for the mute button (top of the box)
- Returns:
The GPIO pin for the mute button
Public Static Attributes
-
struct SdCardConfig
Configuration for the uSD card.