Skip to content

Commit

Permalink
Add T-Deck and Wrover-Kit components (#277)
Browse files Browse the repository at this point in the history
* Add T-Deck and Wrover-Kit components
* Add `t-deck` component which has: touchpad, display, and keyboard
* Add `wrover-kit` component which has: display
* Update docs
* Update CI

* add required idf targets to hal components since chip is known. add peripheral power to t-deck

* update t-deck example since its gt911 has no home button.

* fix wrover kit and update readmes

* fix color inversion and backlight on value; update to draw a circle of circles instead of random

* update circle drawing function

* update circle drawing function

* fix configuration and conversion for touch on t-deck

* updated formatting
  • Loading branch information
finger563 authored Jul 6, 2024
1 parent 74620be commit a93e6a7
Show file tree
Hide file tree
Showing 27 changed files with 1,687 additions and 9 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ jobs:
target: esp32s3
- path: 'components/state_machine/example'
target: esp32
- path: 'components/t-deck/example'
target: esp32s3
- path: 'components/t_keyboard/example'
target: esp32s3
- path: 'components/tabulate/example'
Expand All @@ -125,6 +127,8 @@ jobs:
target: esp32s3
- path: 'components/wifi/example'
target: esp32
- path: 'components/wrover-kit/example'
target: esp32

steps:
- name: Checkout repo
Expand Down
4 changes: 3 additions & 1 deletion components/bldc_motor/include/foc_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,7 @@ inline float normalize_angle(float angle) {
return a >= 0 ? a : (a + _2PI);
}

inline float calc_electrical_angle(float shaft_angle, int pole_pairs) { return shaft_angle * pole_pairs; }
inline float calc_electrical_angle(float shaft_angle, int pole_pairs) {
return shaft_angle * pole_pairs;
}
} // namespace espp
10 changes: 8 additions & 2 deletions components/display/include/display.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,10 @@ class Display : public BaseComponent {
gpio_num_t backlight_pin; /**< GPIO pin for the backlight. */
bool backlight_on_value{
true}; /**< Value to write to the backlight pin to turn the backlight on. */
Task::BaseConfig task_config{.name="Display", .stack_size_bytes=4096, .priority=20, .core_id=0}; /**< Task configuration. */
Task::BaseConfig task_config{.name = "Display",
.stack_size_bytes = 4096,
.priority = 20,
.core_id = 0}; /**< Task configuration. */
std::chrono::duration<float> update_period{
0.01}; /**< How frequently to run the update function. */
bool double_buffered{
Expand Down Expand Up @@ -87,7 +90,10 @@ class Display : public BaseComponent {
gpio_num_t backlight_pin; /**< GPIO pin for the backlight. */
bool backlight_on_value{
true}; /**< Value to write to the backlight pin to turn the backlight on. */
Task::BaseConfig task_config{.name="Display", .stack_size_bytes=4096, .priority=20, .core_id=0}; /**< Task configuration. */
Task::BaseConfig task_config{.name = "Display",
.stack_size_bytes = 4096,
.priority = 20,
.core_id = 0}; /**< Task configuration. */
std::chrono::duration<float> update_period{
0.01}; /**< How frequently to run the update function. */
Rotation rotation{Rotation::LANDSCAPE}; /**< Default / Initial rotation of the display. */
Expand Down
1 change: 1 addition & 0 deletions components/esp-box/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ idf_component_register(
INCLUDE_DIRS "include"
SRC_DIRS "src"
REQUIRES driver esp_lcd base_component codec display display_drivers i2c input_drivers gt911 task tt21100
REQUIRED_IDF_TARGETS "esp32s3"
)
4 changes: 2 additions & 2 deletions components/esp-box/example/main/esp_box_example.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ extern "C" void app_main(void) {
static void draw_circle(int x0, int y0, int radius) {
lv_obj_t *my_Cir = lv_obj_create(lv_scr_act());
lv_obj_set_scrollbar_mode(my_Cir, LV_SCROLLBAR_MODE_OFF);
lv_obj_set_size(my_Cir, 42, 42);
lv_obj_set_pos(my_Cir, x0 - 21, y0 - 21);
lv_obj_set_size(my_Cir, radius * 2, radius * 2);
lv_obj_set_pos(my_Cir, x0 - radius, y0 - radius);
lv_obj_set_style_radius(my_Cir, LV_RADIUS_CIRCLE, 0);
circles.push_back(my_Cir);
}
Expand Down
3 changes: 2 additions & 1 deletion components/esp-box/src/esp-box.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ void EspBox::detect() {

bool EspBox::initialize_touch() {
if (!display_) {
logger_.warn("You should call initialize_display() before initialize_touch(), otherwise lvgl will not properly handle the touchpad input!");
logger_.warn("You should call initialize_display() before initialize_touch(), otherwise lvgl "
"will not properly handle the touchpad input!");
}
switch (box_type_) {
case BoxType::BOX3:
Expand Down
9 changes: 6 additions & 3 deletions components/gt911/include/gt911.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ class Gt911 : public BasePeripheral<std::uint16_t> {
static constexpr size_t DATA_LEN = CONTACT_SIZE * MAX_CONTACTS;
static uint8_t data[DATA_LEN];
read_many_from_register((uint16_t)Registers::POINT_INFO, data, 1, ec);
if (ec) return false;
if (ec)
return false;

// the below is copied from
// https://github.com/espressif/esp-bsp/blob/master/components/lcd_touch/esp_lcd_touch_gt911/esp_lcd_touch_gt911.c
Expand All @@ -50,7 +51,8 @@ class Gt911 : public BasePeripheral<std::uint16_t> {
// only the keys were pressed, read them
uint8_t key_max = MAX_KEYS;
read_many_from_register((uint16_t)Registers::KEY, data, key_max, ec);
if (ec) return false;
if (ec)
return false;
// set the button state
home_button_pressed_ = data[0];
logger_.debug("Home button is {}", home_button_pressed_ ? "pressed" : "released");
Expand All @@ -65,7 +67,8 @@ class Gt911 : public BasePeripheral<std::uint16_t> {
if (num_touch_points_ > 0) {
read_many_from_register((uint16_t)Registers::POINTS, data, CONTACT_SIZE * num_touch_points_,
ec);
if (ec) return false;
if (ec)
return false;
// convert the data pointer to a GTPoint*
const GTPoint *point = (GTPoint *)&data[0];
x_ = point->x;
Expand Down
7 changes: 7 additions & 0 deletions components/t-deck/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# only register the component if the target is esp32s3
idf_component_register(
INCLUDE_DIRS "include"
SRC_DIRS "src"
REQUIRES driver esp_lcd base_component display display_drivers i2c input_drivers gt911 task t_keyboard
REQUIRED_IDF_TARGETS "esp32s3"
)
21 changes: 21 additions & 0 deletions components/t-deck/example/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)

include($ENV{IDF_PATH}/tools/cmake/project.cmake)

# add the component directories that we want to use
set(EXTRA_COMPONENT_DIRS
"../../../components/"
)

set(
COMPONENTS
"main esptool_py t-deck"
CACHE STRING
"List of components to include"
)

project(t_deck_example)

set(CMAKE_CXX_STANDARD 20)
34 changes: 34 additions & 0 deletions components/t-deck/example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# T-Deck Example

This example shows how to use the `espp::TDeck` hardware abstraction component
initialize the components on the LilyGo T-Deck.

It initializes the touch, display, and t-keyboard subsystems. It reads the
touchpad state and each time you touch the screen it uses LVGL to draw a circle
where you touch. If you press the home button on the display, it will clear the
circles.

## How to use example

### Hardware Required

This example is designed to run on the LilyGo T-Deck.

### Build and Flash

Build the project and flash it to the board, then run monitor tool to view
serial output:

```
idf.py -p PORT flash monitor
```

(Replace PORT with the name of the serial port to use.)

(To exit the serial monitor, type ``Ctrl-]``.)

See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.

## Example Output

![CleanShot 2024-07-05 at 23 43 27@2x](https://github.com/esp-cpp/espp/assets/213467/03d1dad5-e9fa-461c-9eb2-1e5d314dcfdb)
2 changes: 2 additions & 0 deletions components/t-deck/example/main/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
idf_component_register(SRC_DIRS "."
INCLUDE_DIRS ".")
121 changes: 121 additions & 0 deletions components/t-deck/example/main/t_deck_example.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
#include <chrono>
#include <stdlib.h>
#include <vector>

#include "t-deck.hpp"

using namespace std::chrono_literals;

static std::vector<lv_obj_t *> circles;

static void draw_circle(int x0, int y0, int radius);
static void clear_circles();

extern "C" void app_main(void) {
espp::Logger logger({.tag = "T-Deck Example", .level = espp::Logger::Verbosity::INFO});
logger.info("Starting example!");

//! [t-deck example]
espp::TDeck &tdeck = espp::TDeck::get();
tdeck.set_log_level(espp::Logger::Verbosity::INFO);

auto keypress_callback = [&](uint8_t key) {
logger.info("Key pressed: {}", key);
if (key == 8) {
clear_circles();
}
};

// initialize the Keyboard
bool start_task = true;
if (!tdeck.initialize_keyboard(start_task, keypress_callback)) {
logger.error("Failed to initialize Keyboard!");
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()) {
logger.error("Failed to initialize touchpad!");
return;
}

// set the background color to black
lv_obj_t *bg = lv_obj_create(lv_scr_act());
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_scr_act());
lv_label_set_text(label, "Touch the screen!\nPress the delete key to clear circles.");
lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0);

// 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 {
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();

// set the display brightness to be 75%
tdeck.brightness(75.0f);

auto previous_touchpad_data = tdeck.touchpad_convert(tdeck.touchpad_data());
while (true) {
std::this_thread::sleep_for(100ms);
if (tdeck.update_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.
auto touchpad_data = tdeck.touchpad_convert(tdeck.touchpad_data());
if (touchpad_data != previous_touchpad_data) {
logger.info("Touch: {}", touchpad_data);
previous_touchpad_data = touchpad_data;
// if the button is pressed, clear the circles
if (touchpad_data.btn_state) {
clear_circles();
}
// if there is a touch point, draw a circle and play a click sound
if (touchpad_data.num_touch_points > 0) {
draw_circle(touchpad_data.x, touchpad_data.y, 10);
}
}
}
}
//! [t-deck example]
}

static void draw_circle(int x0, int y0, int radius) {
lv_obj_t *my_Cir = lv_obj_create(lv_scr_act());
lv_obj_set_scrollbar_mode(my_Cir, LV_SCROLLBAR_MODE_OFF);
lv_obj_set_size(my_Cir, radius * 2, radius * 2);
lv_obj_set_pos(my_Cir, x0 - radius, y0 - radius);
lv_obj_set_style_radius(my_Cir, LV_RADIUS_CIRCLE, 0);
circles.push_back(my_Cir);
}

static void clear_circles() {
// remove the circles from lvgl
for (auto circle : circles) {
lv_obj_del(circle);
}
// clear the vector
circles.clear();
}
46 changes: 46 additions & 0 deletions components/t-deck/example/sdkconfig.defaults
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
CONFIG_IDF_TARGET="esp32s3"

CONFIG_FREERTOS_HZ=1000

# set compiler optimization level to -O2 (compile for performance)
CONFIG_COMPILER_OPTIMIZATION_PERF=y

CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
CONFIG_ESPTOOLPY_FLASHSIZE="16MB"
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y # over twice as fast as DIO

# ESP32-specific
#
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=240

# Common ESP-related
#
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=4096
CONFIG_ESP_MAIN_TASK_STACK_SIZE=16384

# Set esp-timer task stack size to 6KB
CONFIG_ESP_TIMER_TASK_STACK_SIZE=6144

# set the functions into IRAM
CONFIG_SPI_MASTER_IN_IRAM=y

#
# LVGL configuration - # Color settings
#
# CONFIG_LV_COLOR_DEPTH_32 is not set
CONFIG_LV_COLOR_DEPTH_16=y
# CONFIG_LV_COLOR_DEPTH_8 is not set
# CONFIG_LV_COLOR_DEPTH_1 is not set
CONFIG_LV_COLOR_DEPTH=16
CONFIG_LV_COLOR_16_SWAP=y
CONFIG_LV_COLOR_MIX_ROUND_OFS=128
CONFIG_LV_COLOR_CHROMA_KEY_HEX=0x00FF00

#
# LVGL configuration - # Themes
#
CONFIG_LV_USE_THEME_DEFAULT=y
CONFIG_LV_THEME_DEFAULT_DARK=y
CONFIG_LV_THEME_DEFAULT_GROW=y
CONFIG_LV_THEME_DEFAULT_TRANSITION_TIME=80
Loading

0 comments on commit a93e6a7

Please sign in to comment.