Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 23 additions & 23 deletions bsp/m5dial/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,30 +69,30 @@ M5Dial provides versatile power supply options to cater to various needs. It acc

## LVGL Benchmark

**DATE:** 01.10.2025 13:12
**DATE:** 07.11.2025 09:03

**LVGL version:** 9.3.0
**LVGL version:** 9.4.0

| Name | Avg. CPU | Avg. FPS | Avg. time | render time | flush time |
| ---- | :------: | :------: | :-------: | :---------: | :--------: |
| Empty screen | 96% | 37 | 23 | 6 | 17 |
| Moving wallpaper | 98% | 38 | 23 | 11 | 12 |
| Single rectangle | 32% | 97 | 1 | 0 | 1 |
| Multiple rectangles | 90% | 59 | 13 | 10 | 3 |
| Multiple RGB images | 40% | 94 | 1 | 1 | 0 |
| Multiple ARGB images | 35% | 90 | 4 | 4 | 0 |
| Rotated ARGB images | 81% | 57 | 16 | 16 | 0 |
| Multiple labels | 70% | 94 | 4 | 2 | 2 |
| Screen sized text | 98% | 23 | 40 | 39 | 1 |
| Multiple arcs | 23% | 90 | 0 | 0 | 0 |
| Containers | 42% | 83 | 13 | 9 | 4 |
| Containers with overlay | 97% | 32 | 27 | 21 | 6 |
| Containers with opa | 51% | 78 | 16 | 12 | 4 |
| Containers with opa_layer | 55% | 73 | 20 | 17 | 3 |
| Containers with scrolling | 98% | 32 | 28 | 22 | 6 |
| Widgets demo | 99% | 26 | 20 | 18 | 2 |
| All scenes avg. | 69% | 62 | 14 | 11 | 3 |



<!-- END_BENCHMARK -->
| Empty screen | 96% | 38 | 22 | 6 | 16 |
| Moving wallpaper | 97% | 38 | 23 | 11 | 12 |
| Single rectangle | 37% | 97 | 1 | 0 | 1 |
| Multiple rectangles | 92% | 59 | 12 | 9 | 3 |
| Multiple RGB images | 33% | 89 | 1 | 1 | 0 |
| Multiple ARGB images | 34% | 93 | 4 | 4 | 0 |
| Rotated ARGB images | 81% | 56 | 17 | 16 | 1 |
| Multiple labels | 57% | 90 | 6 | 5 | 1 |
| Screen sized text | 97% | 23 | 41 | 39 | 2 |
| Multiple arcs | 32% | 89 | 1 | 1 | 0 |
| Containers | 37% | 80 | 14 | 9 | 5 |
| Containers with overlay | 90% | 32 | 28 | 22 | 6 |
| Containers with opa | 51% | 76 | 17 | 13 | 4 |
| Containers with opa_layer | 50% | 70 | 20 | 18 | 2 |
| Containers with scrolling | 97% | 31 | 30 | 25 | 5 |
| Widgets demo | 99% | 26 | 21 | 20 | 1 |
| All scenes avg. | 67% | 61 | 15 | 12 | 3 |



<!-- END_BENCHMARK -->
4 changes: 3 additions & 1 deletion bsp/m5dial/idf_component.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ tags:

dependencies:
idf: ">=5.3" # We use I2C Driver-NG from IDF v5.2 but esp-codec-dev supports from v5.3
esp_lcd_touch_ft5x06: "^1"
esp_lcd_touch_ft5x06:
version: "^1"
override_path: "../../components/lcd_touch/esp_lcd_touch_ft5x06"

espressif/esp_lcd_gc9a01:
version: "^2"
Expand Down
6 changes: 6 additions & 0 deletions components/esp_lvgl_port/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## 2.7.0

### Features

- Added support for multi-touch gestures.

## 2.6.3

### Fixes
Expand Down
16 changes: 16 additions & 0 deletions components/esp_lvgl_port/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,22 @@ Display rotation can be changed at runtime.
> [!NOTE]
> During the hardware rotating, the component call [`esp_lcd`](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/lcd.html) API. When using software rotation, you cannot use neither `direct_mode` nor `full_refresh` in the driver. See [LVGL documentation](https://docs.lvgl.io/8.3/porting/display.html?highlight=sw_rotate) for more info.

### Detecting gestures

LVGL (version 9.4 and higher) includes support for software detection of multi-touch gestures.
This detection can be enabled by setting the `LVGL_PORT_ENABLE_GESTURES` config and having `ESP_LCD_TOUCH_MAX_POINTS` > 1.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we need this LVGL_PORT_ENABLE_GESTURES config? What about enable it when it is enabled in LVGL - LV_USE_GESTURE_RECOGNITION + touch controller supports it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I've enabled it for LV_USE_GESTURE_RECOGNITION in all cases. This option will need to be manually configured in sdkconfig so I think it is sufficient.

Example usage of the gesture callback can be found in LVGL [documentation](https://docs.lvgl.io/master/details/main-modules/indev/gestures.html).

The LVGL port task is responsible for passing the touch coordinates to the gesture recognizers and calling the registered callback when a gesture is detected.

To correctly distinguish two finger swipe from rotation, we recommend changing the default value (which is 0) for the rotation threshold.
From our testing we recommend starting with 0.15 radians.

```
lv_indev_t indev = bsp_display_get_input_dev();
lv_indev_set_rotation_rad_threshold(indev, 0.15f);
```

### Using PSRAM canvas

If the SRAM is insufficient, you can use the PSRAM as a canvas and use a small trans_buffer to carry it, this makes drawing more efficient.
Expand Down
2 changes: 1 addition & 1 deletion components/esp_lvgl_port/idf_component.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: "2.6.3"
version: "2.7.0"
description: ESP LVGL port
url: https://github.com/espressif/esp-bsp/tree/master/components/esp_lvgl_port
dependencies:
Expand Down
38 changes: 30 additions & 8 deletions components/esp_lvgl_port/src/lvgl9/esp_lvgl_port_touch.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#include "esp_check.h"
#include "esp_lcd_touch.h"
#include "esp_lvgl_port.h"
#include "esp_timer.h"
#include "sdkconfig.h"

static const char *TAG = "LVGL";

Expand Down Expand Up @@ -117,19 +119,39 @@ static void lvgl_port_touchpad_read(lv_indev_t *indev_drv, lv_indev_data_t *data
assert(touch_ctx);
assert(touch_ctx->handle);

uint16_t touchpad_x[1] = {0};
uint16_t touchpad_y[1] = {0};
uint8_t touchpad_cnt = 0;
uint8_t touch_cnt = 0;
esp_lcd_touch_point_data_t touch_data[CONFIG_ESP_LCD_TOUCH_MAX_POINTS] = {0};

/* Read data from touch controller into memory */
esp_lcd_touch_read_data(touch_ctx->handle);
ESP_ERROR_CHECK(esp_lcd_touch_read_data(touch_ctx->handle));

/* Read data from touch controller */
bool touchpad_pressed = esp_lcd_touch_get_coordinates(touch_ctx->handle, touchpad_x, touchpad_y, NULL, &touchpad_cnt, 1);
ESP_ERROR_CHECK(esp_lcd_touch_get_data(touch_ctx->handle, touch_data, &touch_cnt, CONFIG_ESP_LCD_TOUCH_MAX_POINTS));

if (touchpad_pressed && touchpad_cnt > 0) {
data->point.x = touch_ctx->scale.x * touchpad_x[0];
data->point.y = touch_ctx->scale.y * touchpad_y[0];
#if (CONFIG_ESP_LCD_TOUCH_MAX_POINTS > 1 && CONFIG_LV_USE_GESTURE_RECOGNITION)
// Number of touch points which need to be constantly updated inside gesture recognizers
#define GESTURE_TOUCH_POINTS 2

/* Initialize LVGL touch data for each activated touch point */
lv_indev_touch_data_t touches[GESTURE_TOUCH_POINTS] = {0};

for (int i = 0; i < touch_cnt && i < GESTURE_TOUCH_POINTS; i++) {
touches[i].state = LV_INDEV_STATE_PRESSED;
touches[i].point.x = touch_ctx->scale.x * touch_data[i].x;
touches[i].point.y = touch_ctx->scale.y * touch_data[i].y;
touches[i].id = touch_data[i].track_id;
touches[i].timestamp = esp_timer_get_time() / 1000;
}

/* Pass touch data to LVGL gesture recognizers */
lv_indev_gesture_recognizers_update(indev_drv, touches, GESTURE_TOUCH_POINTS);
lv_indev_gesture_recognizers_set_data(indev_drv, data);

#endif

if (touch_cnt > 0) {
data->point.x = touch_ctx->scale.x * touch_data[0].x;
data->point.y = touch_ctx->scale.y * touch_data[0].y;
data->state = LV_INDEV_STATE_PRESSED;
} else {
data->state = LV_INDEV_STATE_RELEASED;
Expand Down
113 changes: 92 additions & 21 deletions components/lcd_touch/esp_lcd_touch/esp_lcd_touch.c
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
/*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
Expand All @@ -30,7 +31,8 @@ static const char *TAG = "TP";

esp_err_t esp_lcd_touch_enter_sleep(esp_lcd_touch_handle_t tp)
{
assert(tp != NULL);
ESP_RETURN_ON_FALSE(tp != NULL, ESP_ERR_INVALID_ARG, TAG, "Touch point handler can't be NULL");

if (tp->enter_sleep == NULL) {
ESP_LOGE(TAG, "Sleep mode not supported!");
return ESP_FAIL;
Expand All @@ -41,7 +43,8 @@ esp_err_t esp_lcd_touch_enter_sleep(esp_lcd_touch_handle_t tp)

esp_err_t esp_lcd_touch_exit_sleep(esp_lcd_touch_handle_t tp)
{
assert(tp != NULL);
ESP_RETURN_ON_FALSE(tp != NULL, ESP_ERR_INVALID_ARG, TAG, "Touch point handler can't be NULL");

if (tp->exit_sleep == NULL) {
ESP_LOGE(TAG, "Sleep mode not supported!");
return ESP_FAIL;
Expand All @@ -52,7 +55,7 @@ esp_err_t esp_lcd_touch_exit_sleep(esp_lcd_touch_handle_t tp)

esp_err_t esp_lcd_touch_read_data(esp_lcd_touch_handle_t tp)
{
assert(tp != NULL);
ESP_RETURN_ON_FALSE(tp != NULL, ESP_ERR_INVALID_ARG, TAG, "Touch point handler can't be NULL");
assert(tp->read_data != NULL);

return tp->read_data(tp);
Expand All @@ -62,10 +65,10 @@ bool esp_lcd_touch_get_coordinates(esp_lcd_touch_handle_t tp, uint16_t *x, uint1
{
bool touched = false;

assert(tp != NULL);
assert(x != NULL);
assert(y != NULL);
assert(tp->get_xy != NULL);
ESP_RETURN_ON_FALSE(tp != NULL, false, TAG, "Touch point handler can't be NULL");
ESP_RETURN_ON_FALSE(x != NULL, false, TAG, "X coordinates data array can't be NULL");
ESP_RETURN_ON_FALSE(y != NULL, false, TAG, "Y coordinates data array can't be NULL");
ESP_RETURN_ON_FALSE(tp->get_xy != NULL, false, TAG, "Touch driver must be initialized");

touched = tp->get_xy(tp, x, y, strength, point_num, max_point_num);
if (!touched) {
Expand Down Expand Up @@ -106,11 +109,79 @@ bool esp_lcd_touch_get_coordinates(esp_lcd_touch_handle_t tp, uint16_t *x, uint1
return touched;
}

esp_err_t esp_lcd_touch_get_data(esp_lcd_touch_handle_t tp, esp_lcd_touch_point_data_t *data, uint8_t *point_cnt, uint8_t max_point_cnt)
{
ESP_RETURN_ON_FALSE(tp != NULL, ESP_ERR_INVALID_ARG, TAG, "Touch point handler can't be NULL");
ESP_RETURN_ON_FALSE(data != NULL, ESP_ERR_INVALID_ARG, TAG, "Data array can't be NULL");
ESP_RETURN_ON_FALSE(tp->get_xy != NULL, ESP_ERR_INVALID_STATE, TAG, "Touch driver must be initialized");

uint16_t x[max_point_cnt];
uint16_t y[max_point_cnt];
uint16_t strength[max_point_cnt];
uint8_t track_id[max_point_cnt];

bool touched = tp->get_xy(tp, x, y, strength, point_cnt, max_point_cnt);

if (!touched) {
return ESP_OK;
}

/* Process coordinates by user */
if (tp->config.process_coordinates != NULL) {
tp->config.process_coordinates(tp, x, y, strength, point_cnt, max_point_cnt);
}

/* Software coordinates adjustment needed */
bool sw_adj_needed = ((tp->config.flags.mirror_x && (tp->set_mirror_x == NULL)) ||
(tp->config.flags.mirror_y && (tp->set_mirror_y == NULL)) ||
(tp->config.flags.swap_xy && (tp->set_swap_xy == NULL)));

/* Adjust all coordinates */
for (int i = 0; (sw_adj_needed && i < *point_cnt); i++) {

/* Mirror X coordinates (if not supported by HW) */
if (tp->config.flags.mirror_x && tp->set_mirror_x == NULL) {
x[i] = tp->config.x_max - x[i];
}

/* Mirror Y coordinates (if not supported by HW) */
if (tp->config.flags.mirror_y && tp->set_mirror_y == NULL) {
y[i] = tp->config.y_max - y[i];
}

/* Swap X and Y coordinates (if not supported by HW) */
if (tp->config.flags.swap_xy && tp->set_swap_xy == NULL) {
uint16_t tmp = x[i];
x[i] = y[i];
y[i] = tmp;
}
}

/* Process read track IDs */
if (tp->get_track_id != NULL && touched) {
tp->get_track_id(tp, track_id, *point_cnt);
}

/* Initialize the struct array since some features might not be available */
memset(data, 0, sizeof(esp_lcd_touch_point_data_t) * max_point_cnt);

for (int i = 0; i < *point_cnt; i++) {
data[i].x = x[i];
data[i].y = y[i];
data[i].strength = strength[i];
if (tp->get_track_id != NULL) {
data[i].track_id = track_id[i];
}
}

return ESP_OK;
}

#if (CONFIG_ESP_LCD_TOUCH_MAX_BUTTONS > 0)
esp_err_t esp_lcd_touch_get_button_state(esp_lcd_touch_handle_t tp, uint8_t n, uint8_t *state)
{
assert(tp != NULL);
assert(state != NULL);
ESP_RETURN_ON_FALSE(tp != NULL, ESP_ERR_INVALID_ARG, TAG, "Touch point handler can't be NULL");
ESP_RETURN_ON_FALSE(state != NULL, ESP_ERR_INVALID_ARG, TAG, "Pointer to an argument can't be NULL");

*state = 0;

Expand Down Expand Up @@ -140,8 +211,8 @@ esp_err_t esp_lcd_touch_set_swap_xy(esp_lcd_touch_handle_t tp, bool swap)

esp_err_t esp_lcd_touch_get_swap_xy(esp_lcd_touch_handle_t tp, bool *swap)
{
assert(tp != NULL);
assert(swap != NULL);
ESP_RETURN_ON_FALSE(tp != NULL, ESP_ERR_INVALID_ARG, TAG, "Touch point handler can't be NULL");
ESP_RETURN_ON_FALSE(swap != NULL, ESP_ERR_INVALID_ARG, TAG, "Pointer to an argument can't be NULL");

/* Is swap supported by HW? */
if (tp->get_swap_xy) {
Expand All @@ -155,7 +226,7 @@ esp_err_t esp_lcd_touch_get_swap_xy(esp_lcd_touch_handle_t tp, bool *swap)

esp_err_t esp_lcd_touch_set_mirror_x(esp_lcd_touch_handle_t tp, bool mirror)
{
assert(tp != NULL);
ESP_RETURN_ON_FALSE(tp != NULL, ESP_ERR_INVALID_ARG, TAG, "Touch point handler can't be NULL");

tp->config.flags.mirror_x = mirror;

Expand All @@ -169,8 +240,8 @@ esp_err_t esp_lcd_touch_set_mirror_x(esp_lcd_touch_handle_t tp, bool mirror)

esp_err_t esp_lcd_touch_get_mirror_x(esp_lcd_touch_handle_t tp, bool *mirror)
{
assert(tp != NULL);
assert(mirror != NULL);
ESP_RETURN_ON_FALSE(tp != NULL, ESP_ERR_INVALID_ARG, TAG, "Touch point handler can't be NULL");
ESP_RETURN_ON_FALSE(mirror != NULL, ESP_ERR_INVALID_ARG, TAG, "Pointer to an argument can't be NULL");

/* Is swap supported by HW? */
if (tp->get_mirror_x) {
Expand All @@ -184,7 +255,7 @@ esp_err_t esp_lcd_touch_get_mirror_x(esp_lcd_touch_handle_t tp, bool *mirror)

esp_err_t esp_lcd_touch_set_mirror_y(esp_lcd_touch_handle_t tp, bool mirror)
{
assert(tp != NULL);
ESP_RETURN_ON_FALSE(tp != NULL, ESP_ERR_INVALID_ARG, TAG, "Touch point handler can't be NULL");

tp->config.flags.mirror_y = mirror;

Expand All @@ -198,8 +269,8 @@ esp_err_t esp_lcd_touch_set_mirror_y(esp_lcd_touch_handle_t tp, bool mirror)

esp_err_t esp_lcd_touch_get_mirror_y(esp_lcd_touch_handle_t tp, bool *mirror)
{
assert(tp != NULL);
assert(mirror != NULL);
ESP_RETURN_ON_FALSE(tp != NULL, ESP_ERR_INVALID_ARG, TAG, "Touch point handler can't be NULL");
ESP_RETURN_ON_FALSE(mirror != NULL, ESP_ERR_INVALID_ARG, TAG, "Pointer to an argument can't be NULL");

/* Is swap supported by HW? */
if (tp->get_mirror_y) {
Expand All @@ -213,7 +284,7 @@ esp_err_t esp_lcd_touch_get_mirror_y(esp_lcd_touch_handle_t tp, bool *mirror)

esp_err_t esp_lcd_touch_del(esp_lcd_touch_handle_t tp)
{
assert(tp != NULL);
ESP_RETURN_ON_FALSE(tp != NULL, ESP_ERR_INVALID_ARG, TAG, "Touch point handler can't be NULL");

if (tp->del != NULL) {
return tp->del(tp);
Expand All @@ -225,7 +296,7 @@ esp_err_t esp_lcd_touch_del(esp_lcd_touch_handle_t tp)
esp_err_t esp_lcd_touch_register_interrupt_callback(esp_lcd_touch_handle_t tp, esp_lcd_touch_interrupt_callback_t callback)
{
esp_err_t ret = ESP_OK;
assert(tp != NULL);
ESP_RETURN_ON_FALSE(tp != NULL, ESP_ERR_INVALID_ARG, TAG, "Touch point handler can't be NULL");

/* Interrupt pin is not selected */
if (tp->config.int_gpio_num == GPIO_NUM_NC) {
Expand Down Expand Up @@ -259,7 +330,7 @@ esp_err_t esp_lcd_touch_register_interrupt_callback(esp_lcd_touch_handle_t tp, e

esp_err_t esp_lcd_touch_register_interrupt_callback_with_data(esp_lcd_touch_handle_t tp, esp_lcd_touch_interrupt_callback_t callback, void *user_data)
{
assert(tp != NULL);
ESP_RETURN_ON_FALSE(tp != NULL, ESP_ERR_INVALID_ARG, TAG, "Touch point handler can't be NULL");

tp->config.user_data = user_data;
return esp_lcd_touch_register_interrupt_callback(tp, callback);
Expand Down
2 changes: 1 addition & 1 deletion components/lcd_touch/esp_lcd_touch/idf_component.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: "1.1.2"
version: "1.2.0"
description: ESP LCD Touch - main component for using touch screen controllers
url: https://github.com/espressif/esp-bsp/tree/master/components/lcd_touch/esp_lcd_touch
dependencies:
Expand Down
Loading
Loading