From de34cbfd7ad76ba97fa6c47c167067208e8c1fc2 Mon Sep 17 00:00:00 2001 From: Ken Van Hoeylandt Date: Tue, 13 Feb 2024 22:40:17 +0100 Subject: [PATCH] Added GPIO app (#45) - Implemented GPIO app - LVGL now allocates freely (as opposed to using a fixed-size buffer): when it goes OOM, it just deletes image cache and starts behaving erratically. --- app-esp/src/main.c | 2 + sdkconfig.board.lilygo_tdeck | 2 + sdkconfig.board.waveshare_s3_touch | 2 + sdkconfig.board.yellow_board | 2 + sdkconfig.defaults | 2 + tactility-esp/src/apps/system/gpio/gpio.c | 205 ++++++++++++++++++++++ 6 files changed, 215 insertions(+) create mode 100644 tactility-esp/src/apps/system/gpio/gpio.c diff --git a/app-esp/src/main.c b/app-esp/src/main.c index c4cb14d5..125e5fd8 100644 --- a/app-esp/src/main.c +++ b/app-esp/src/main.c @@ -7,6 +7,7 @@ extern void wifi_main(void*); extern const ServiceManifest wifi_service; +extern const AppManifest gpio_app; extern const AppManifest wifi_connect_app; extern const AppManifest wifi_manage_app; @@ -18,6 +19,7 @@ void app_main(void) { */ .hardware = TT_BOARD_HARDWARE, .apps = { + &gpio_app, &hello_world_app, &wifi_connect_app, &wifi_manage_app diff --git a/sdkconfig.board.lilygo_tdeck b/sdkconfig.board.lilygo_tdeck index dcb845f3..e0c67454 100644 --- a/sdkconfig.board.lilygo_tdeck +++ b/sdkconfig.board.lilygo_tdeck @@ -7,6 +7,8 @@ CONFIG_LV_FS_STDIO_LETTER=65 CONFIG_LV_FS_STDIO_PATH="" CONFIG_LV_FS_STDIO_CACHE_SIZE=4096 CONFIG_LV_USE_PNG=y +CONFIG_LV_MEM_CUSTOM=y +CONFIG_LV_MEM_CUSTOM_INCLUDE="stdlib.h" CONFIG_FREERTOS_HZ=1000 CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2 CONFIG_PARTITION_TABLE_CUSTOM=y diff --git a/sdkconfig.board.waveshare_s3_touch b/sdkconfig.board.waveshare_s3_touch index 0634bd6d..02e84c0e 100644 --- a/sdkconfig.board.waveshare_s3_touch +++ b/sdkconfig.board.waveshare_s3_touch @@ -7,6 +7,8 @@ CONFIG_LV_FS_STDIO_LETTER=65 CONFIG_LV_FS_STDIO_PATH="" CONFIG_LV_FS_STDIO_CACHE_SIZE=4096 CONFIG_LV_USE_PNG=y +CONFIG_LV_MEM_CUSTOM=y +CONFIG_LV_MEM_CUSTOM_INCLUDE="stdlib.h" CONFIG_FREERTOS_HZ=1000 CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2 CONFIG_PARTITION_TABLE_CUSTOM=y diff --git a/sdkconfig.board.yellow_board b/sdkconfig.board.yellow_board index 1a87d4eb..8ca6fe52 100644 --- a/sdkconfig.board.yellow_board +++ b/sdkconfig.board.yellow_board @@ -7,6 +7,8 @@ CONFIG_LV_FS_STDIO_LETTER=65 CONFIG_LV_FS_STDIO_PATH="" CONFIG_LV_FS_STDIO_CACHE_SIZE=4096 CONFIG_LV_USE_PNG=y +CONFIG_LV_MEM_CUSTOM=y +CONFIG_LV_MEM_CUSTOM_INCLUDE="stdlib.h" CONFIG_FREERTOS_HZ=1000 CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2 CONFIG_PARTITION_TABLE_CUSTOM=y diff --git a/sdkconfig.defaults b/sdkconfig.defaults index 9131ef19..25c582d0 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -7,6 +7,8 @@ CONFIG_LV_FS_STDIO_LETTER=65 CONFIG_LV_FS_STDIO_PATH="" CONFIG_LV_FS_STDIO_CACHE_SIZE=4096 CONFIG_LV_USE_PNG=y +CONFIG_LV_MEM_CUSTOM=y +CONFIG_LV_MEM_CUSTOM_INCLUDE="stdlib.h" CONFIG_FREERTOS_HZ=1000 CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=2 CONFIG_PARTITION_TABLE_CUSTOM=y diff --git a/tactility-esp/src/apps/system/gpio/gpio.c b/tactility-esp/src/apps/system/gpio/gpio.c new file mode 100644 index 00000000..de5425eb --- /dev/null +++ b/tactility-esp/src/apps/system/gpio/gpio.c @@ -0,0 +1,205 @@ +#include "services/loader/loader.h" +#include "ui/toolbar.h" +#include "thread.h" +#include "mutex.h" + +#include "driver/gpio.h" +#include "ui/lvgl_sync.h" + +typedef struct { + lv_obj_t* lv_pins[GPIO_NUM_MAX]; + uint8_t pin_states[GPIO_NUM_MAX]; + Thread* thread; + Mutex* mutex; + bool thread_interrupted; +} Gpio; + +static void lock(Gpio* gpio) { + tt_check(tt_mutex_acquire(gpio->mutex, 1000) == TtStatusOk); +} + +static void unlock(Gpio* gpio) { + tt_check(tt_mutex_release(gpio->mutex) == TtStatusOk); +} + +static void update_pin_states(Gpio* gpio) { + lock(gpio); + // Update pin states + for (int i = 0; i < GPIO_NUM_MAX; ++i) { + gpio->pin_states[i] = gpio_get_level(i); + } + unlock(gpio); +} + +static void update_pin_widgets(Gpio* gpio) { + if (tt_lvgl_lock(100)) { + lock(gpio); + for (int j = 0; j < GPIO_NUM_MAX; ++j) { + int level = gpio->pin_states[j]; + lv_obj_t* label = gpio->lv_pins[j]; + // user_data stores the state, so we can avoid unnecessary updates + if ((void*)level != label->user_data) { + label->user_data = (void*)level; + if (level == 0) { + lv_obj_set_style_text_color(label, lv_color_black(), 0); + } else { + lv_obj_set_style_text_color(label, lv_color_make(0, 200, 0), 0); + } + } + } + tt_lvgl_unlock(); + unlock(gpio); + } +} + +static lv_obj_t* create_gpio_row_wrapper(lv_obj_t* parent) { + lv_obj_t* wrapper = lv_obj_create(parent); + lv_obj_set_style_pad_all(wrapper, 0, 0); + lv_obj_set_style_border_width(wrapper, 0, 0); + lv_obj_set_size(wrapper, LV_SIZE_CONTENT, LV_SIZE_CONTENT); + return wrapper; +} + +// region Task + +static int32_t gpio_task(void* context) { + Gpio* gpio = (Gpio*)context; + bool interrupted = false; + + while (!interrupted) { + tt_delay_ms(100); + + update_pin_states(gpio); + update_pin_widgets(gpio); + + lock(gpio); + interrupted = gpio->thread_interrupted; + unlock(gpio); + } + + return 0; +} + +static void task_start(Gpio* gpio) { + tt_check(gpio->thread == NULL); + lock(gpio); + gpio->thread = tt_thread_alloc_ex( + "gpio", + 4096, + &gpio_task, + gpio + ); + gpio->thread_interrupted = false; + tt_thread_start(gpio->thread); + unlock(gpio); +} + +static void task_stop(Gpio* gpio) { + tt_check(gpio->thread); + lock(gpio); + gpio->thread_interrupted = true; + unlock(gpio); + + tt_thread_join(gpio->thread); + + lock(gpio); + tt_thread_free(gpio->thread); + gpio->thread = NULL; + unlock(gpio); +} + +// endregion Task + +// region App lifecycle + +static void app_show(App app, lv_obj_t* parent) { + Gpio* gpio = tt_app_get_data(app); + + lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); + lv_obj_t* toolbar = tt_toolbar_create_for_app(parent, app); + lv_obj_align(toolbar, LV_ALIGN_TOP_MID, 0, 0); + + // Main content wrapper, enables scrolling content without scrolling the toolbar + lv_obj_t* wrapper = lv_obj_create(parent); + lv_obj_set_width(wrapper, LV_PCT(100)); + lv_obj_set_flex_grow(wrapper, 1); + lv_obj_set_style_border_width(wrapper, 0, 0); + + uint8_t column = 0; + uint8_t column_limit = 10; + lv_coord_t x_spacing = 20; + + lv_obj_t* row_wrapper = create_gpio_row_wrapper(wrapper); + lv_obj_align(row_wrapper, LV_ALIGN_TOP_MID, 0, 0); + + lock(gpio); + for (int i = GPIO_NUM_0; i < GPIO_NUM_MAX; ++i) { + + // Add the GPIO number before the first item on a row + if (column == 0) { + lv_obj_t* prefix = lv_label_create(row_wrapper); + lv_label_set_text_fmt(prefix, "%02d", i); + } + + // Add a new GPIO status indicator + lv_obj_t* status_label = lv_label_create(row_wrapper); + lv_obj_set_pos(status_label, (lv_coord_t)((column+1) * x_spacing), 0); + lv_label_set_text_fmt(status_label, "%s", LV_SYMBOL_STOP); + gpio->lv_pins[i] = status_label; + + column++; + + if (column >= column_limit) { + // Add the GPIO number after the last item on a row + lv_obj_t* postfix = lv_label_create(row_wrapper); + lv_label_set_text_fmt(postfix, "%02d", i); + lv_obj_set_pos(postfix, (lv_coord_t)((column+1) * x_spacing), 0); + + // Add a new row wrapper underneath the last one + lv_obj_t* new_row_wrapper = create_gpio_row_wrapper(wrapper); + lv_obj_align_to(new_row_wrapper, row_wrapper, LV_ALIGN_BOTTOM_LEFT, 0, 4); + row_wrapper = new_row_wrapper; + + column = 0; + } + } + unlock(gpio); + + task_start(gpio); +} + +static void on_hide(App app) { + Gpio* gpio = tt_app_get_data(app); + task_stop(gpio); +} + +static void on_start(App app) { + Gpio* gpio = malloc(sizeof(Gpio)); + *gpio = (Gpio) { + .lv_pins = { 0 }, + .pin_states = { 0 }, + .mutex = tt_mutex_alloc(MutexTypeNormal), + .thread_interrupted = true, + .thread = NULL + }; + tt_app_set_data(app, gpio); +} + +static void on_stop(App app) { + Gpio* gpio = tt_app_get_data(app); + tt_mutex_free(gpio->mutex); + free(gpio); +} + +// endregion App lifecycle + +const AppManifest gpio_app = { + .id = "gpio", + .name = "GPIO", + .icon = NULL, + .type = AppTypeSystem, + .on_start = &on_start, + .on_stop = &on_stop, + .on_show = &app_show, + .on_hide = &on_hide +};