From 2d1b59e6c79e99608b5a460dc3419a3ebf55d17d Mon Sep 17 00:00:00 2001 From: maidang Date: Thu, 30 Apr 2026 10:14:00 +0800 Subject: [PATCH 1/7] feat(lvgl_simulator): add LVGL PC simulator for UI development - Introduced a new LVGL PC simulator to run TuyaOpen App's LVGL UI on a Linux desktop without hardware. - Added configuration files and build scripts to facilitate easy switching between different apps. - Implemented necessary stubs and logging support for TAL and TKL interfaces. - Updated existing UI source files to support conditional compilation for hardware and simulator environments. - Enhanced documentation for building and running the simulator, including prerequisites and usage instructions. --- .../src/display/ui/ai_chat_screen.c | 14 +- .../src/display/ui/camera_screen.c | 14 +- .../src/display/ui/camera_screen.h | 6 + .../src/display/ui/main_screen.c | 5 +- .../src/display/ui/screen_manager.h | 2 + .../graphics/lvgl_simulator/CMakeLists.txt | 138 ++++++++++++ examples/graphics/lvgl_simulator/README_en.md | 212 ++++++++++++++++++ examples/graphics/lvgl_simulator/README_zh.md | 212 ++++++++++++++++++ .../lvgl_simulator/include/tuya_kconfig.h | 7 + .../graphics/lvgl_simulator/sim_config.cmake | 45 ++++ .../graphics/lvgl_simulator/sim_main.c.in | 39 ++++ src/liblvgl/v9/conf/lv_conf.h | 30 ++- src/liblvgl/v9/lvgl/src/core/lv_obj_event.c | 2 + src/liblvgl/v9/lvgl/src/core/lv_obj_tree.c | 2 + src/liblvgl/v9/lvgl/src/misc/lv_timer.c | 2 + src/liblvgl/v9/lvgl/src/stdlib/lv_mem.c | 4 +- 16 files changed, 716 insertions(+), 18 deletions(-) create mode 100644 examples/graphics/lvgl_simulator/CMakeLists.txt create mode 100644 examples/graphics/lvgl_simulator/README_en.md create mode 100644 examples/graphics/lvgl_simulator/README_zh.md create mode 100644 examples/graphics/lvgl_simulator/include/tuya_kconfig.h create mode 100644 examples/graphics/lvgl_simulator/sim_config.cmake create mode 100644 examples/graphics/lvgl_simulator/sim_main.c.in diff --git a/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/ai_chat_screen.c b/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/ai_chat_screen.c index 1af8844d0..e170a5203 100644 --- a/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/ai_chat_screen.c +++ b/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/ai_chat_screen.c @@ -5,15 +5,14 @@ */ #include "tuya_cloud_types.h" -#include "lv_vendor.h" +#ifdef ENABLE_LVGL_HARDWARE +#include "lv_vendor.h" #include "ai_ui_manage.h" - #include "toast_screen.h" #include "rfid_scan_screen.h" #include "ai_log_screen.h" #include "main_screen.h" - #include "app_display.h" /*********************************************************** @@ -37,7 +36,7 @@ static const CHAT_EMOJI_SHOW_T cCHAT_EMOJI_SHOW_LIST[] = { {EMOJI_ANGRY, "PET: Angry"}, {EMOJI_FEARFUL, "PET: Fearful"}, {EMOJI_SAD, "PET: Sad"}, -}; +}; /*********************************************************** ***********************function define********************** @@ -90,7 +89,6 @@ void __ui_set_custom_msg(uint32_t type, uint8_t *data, int len ) rfid_scan_screen_load(); break; case POCKET_DISP_TP_AI_LOG: - PR_DEBUG("AI LOG: %d", len); if (data && len > 0) { ai_log_screen_update_log((const char *)data, len); } @@ -114,3 +112,9 @@ OPERATE_RET ai_ui_chat_register(void) return ai_ui_register(&intfs); } + +#else /* ENABLE_LVGL_HARDWARE */ + +OPERATE_RET ai_ui_chat_register(void) { return OPRT_OK; } + +#endif /* ENABLE_LVGL_HARDWARE */ diff --git a/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/camera_screen.c b/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/camera_screen.c index 3182a12dd..2a4bea208 100644 --- a/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/camera_screen.c +++ b/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/camera_screen.c @@ -60,6 +60,9 @@ static lv_obj_t *threshold_label; // Threshold value label static lv_obj_t *status_label; // Camera status label static lv_timer_t *update_timer; // Timer for updating display +// Lifecycle callback (not hardware-specific) +static camera_screen_lifecycle_cb_t sg_lifecycle_callback = NULL; + #ifdef ENABLE_LVGL_HARDWARE static uint8_t *canvas_buffer = NULL; // Canvas buffer for monochrome image static TDL_DISP_FRAME_BUFF_T *sg_p_display_fb = NULL; @@ -87,9 +90,6 @@ static BINARY_CONFIG_T sg_binary_config = { // Calculated threshold for adaptive and otsu methods static uint8_t sg_calculated_threshold = 128; -// Lifecycle callback -static camera_screen_lifecycle_cb_t sg_lifecycle_callback = NULL; - // Photo print callback static camera_photo_print_cb_t sg_print_callback = NULL; #endif @@ -129,18 +129,20 @@ void camera_screen_register_lifecycle_cb(camera_screen_lifecycle_cb_t callback) * @brief Register photo print callback for camera screen * @param callback Callback function, NULL to unregister */ +#ifdef ENABLE_LVGL_HARDWARE void camera_screen_register_print_cb(camera_photo_print_cb_t callback) { sg_print_callback = callback; printf("[Camera] Print callback %s\n", callback ? "registered" : "unregistered"); } +#endif /** * @brief Get method name string */ +#ifdef ENABLE_LVGL_HARDWARE static const char *get_method_name(BINARY_METHOD_E method) { -#ifdef ENABLE_LVGL_HARDWARE switch (method) { case BINARY_METHOD_FIXED: return "Fixed"; @@ -163,10 +165,10 @@ static const char *get_method_name(BINARY_METHOD_E method) default: return "Unknown"; } +} #else - return "N/A"; +static const char *get_method_name(int method) { (void)method; return "N/A"; } #endif -} /** * @brief Update info area display diff --git a/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/camera_screen.h b/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/camera_screen.h index 508f1031a..600ec1e70 100644 --- a/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/camera_screen.h +++ b/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/camera_screen.h @@ -25,7 +25,9 @@ extern "C" { #endif #include "screen_manager.h" +#ifndef ENABLE_LVGL_HARDWARE #include "yuv422_to_binary.h" +#endif /** * @brief Camera screen lifecycle callback type * Called when screen is initialized or deinitialized @@ -34,6 +36,7 @@ extern "C" { */ typedef void (*camera_screen_lifecycle_cb_t)(BOOL_T is_init); +#ifndef ENABLE_LVGL_HARDWARE /** * @brief Camera photo print callback type * Called when ENTER key is pressed to print current photo from raw YUV422 data @@ -41,6 +44,7 @@ typedef void (*camera_screen_lifecycle_cb_t)(BOOL_T is_init); * @note The yuv422_data buffer will be freed after callback returns */ typedef void (*camera_photo_print_cb_t)(const YUV422_TO_BINARY_PARAMS_T *params); +#endif extern Screen_t camera_screen; @@ -53,12 +57,14 @@ void camera_screen_deinit(void); */ void camera_screen_register_lifecycle_cb(camera_screen_lifecycle_cb_t callback); +#ifndef ENABLE_LVGL_HARDWARE /** * @brief Register photo print callback for camera screen * Called when ENTER key is pressed with current photo data * @param callback Callback function, NULL to unregister */ void camera_screen_register_print_cb(camera_photo_print_cb_t callback); +#endif #ifdef __cplusplus } /*extern "C"*/ diff --git a/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/main_screen.c b/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/main_screen.c index 79822a7ef..9219644c5 100644 --- a/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/main_screen.c +++ b/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/main_screen.c @@ -31,7 +31,9 @@ #include "ebook_screen.h" #include "rfid_scan_screen.h" #include "camera_screen.h" +#ifdef ENABLE_LVGL_HARDWARE #include "game_pet.h" +#endif #include #include #include @@ -1056,6 +1058,7 @@ void main_screen_set_pet_animation_state(ai_pet_state_t state) printf("[%s] Pet animation state changing: %d -> %d\n", main_screen.name, current_animation_state, state); +#ifdef ENABLE_LVGL_HARDWARE // Play sound effect based on pet state switch (state) { case AI_PET_STATE_SLEEP: @@ -1086,9 +1089,9 @@ void main_screen_set_pet_animation_state(ai_pet_state_t state) game_pet_play_alert(PET_ALERT_THREE_STAGE_UP_TONE); break; default: - // No sound for AI_PET_STATE_NORMAL break; } +#endif // Simply update the state variable - timer will handle GIF switching current_animation_state = state; diff --git a/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/screen_manager.h b/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/screen_manager.h index 7fa6e71db..1c1a8a5c3 100644 --- a/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/screen_manager.h +++ b/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/screen_manager.h @@ -24,7 +24,9 @@ /*********************************************************** ************************macro define************************ ***********************************************************/ +#ifndef LVGL_PC_SIMULATOR #define ENABLE_LVGL_HARDWARE +#endif #ifdef ENABLE_LVGL_HARDWARE #include "tuya_cloud_types.h" #include "tal_log.h" diff --git a/examples/graphics/lvgl_simulator/CMakeLists.txt b/examples/graphics/lvgl_simulator/CMakeLists.txt new file mode 100644 index 000000000..86c2d8c7b --- /dev/null +++ b/examples/graphics/lvgl_simulator/CMakeLists.txt @@ -0,0 +1,138 @@ +cmake_minimum_required(VERSION 3.14) +project(LVGLSimulator C) + +# 加载用户配置(修改 sim_config.cmake 切换 App) +include(${CMAKE_CURRENT_SOURCE_DIR}/sim_config.cmake) + +# 定位 TuyaOpen 根目录(simulator 在 examples/graphics/lvgl_simulator/) +get_filename_component(TUYAOPEN_ROOT + "${CMAKE_CURRENT_SOURCE_DIR}/../../.." ABSOLUTE) + +set(LVGL_DIR "${TUYAOPEN_ROOT}/src/liblvgl/v9") +set(PLATFORM_DIR "${TUYAOPEN_ROOT}/platform/LINUX/tuyaos_adapter") +set(TAL_DIR "${TUYAOPEN_ROOT}/src/tal_system") +set(COMMON_DIR "${TUYAOPEN_ROOT}/src/common") + +# 可执行文件输出到 dist/ +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/dist) + +# ───────────────────────────────────────────────────────────────────────────── +# 生成 main.c(注入入口函数名和分辨率) +# ───────────────────────────────────────────────────────────────────────────── +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/sim_main.c.in + ${CMAKE_BINARY_DIR}/sim_main.c + @ONLY +) + +# ───────────────────────────────────────────────────────────────────────────── +# 源文件收集 +# ───────────────────────────────────────────────────────────────────────────── + +# LVGL 核心源码(含内置 SDL 驱动;排除 port/ 的嵌入式端口文件) +file(GLOB_RECURSE LVGL_SRCS "${LVGL_DIR}/lvgl/src/*.c") + +# UI 业务代码 +set(UI_SRCS "") +foreach(dir ${SIM_UI_SRC_DIRS}) + file(GLOB_RECURSE _srcs "${dir}/*.c") + list(APPEND UI_SRCS ${_srcs}) +endforeach() + +# platform/LINUX 工具库(链表、哈希表等) +file(GLOB UTIL_SRCS "${PLATFORM_DIR}/include/utilities/src/*.c") + +# platform/LINUX TKL 适配层(仅选取 TAL 依赖的核心模块) +set(TKL_CORE_SRCS + ${PLATFORM_DIR}/src/tkl_memory.c + ${PLATFORM_DIR}/src/tkl_mutex.c + ${PLATFORM_DIR}/src/tkl_semaphore.c + ${PLATFORM_DIR}/src/tkl_queue.c + ${PLATFORM_DIR}/src/tkl_thread.c + ${PLATFORM_DIR}/src/tkl_system.c + ${PLATFORM_DIR}/src/tkl_output.c + ${PLATFORM_DIR}/src/tkl_fs.c + ${PLATFORM_DIR}/src/tkl_ota.c +) + +# TAL 抽象层(含 tal_log.c 提供 PR_xxx 宏实现;排除依赖网络/UART/KV 头的 tal_event/fs) +set(TAL_SRCS + ${TAL_DIR}/src/tal_api.c + ${TAL_DIR}/src/tal_log.c + ${TAL_DIR}/src/tal_sleep.c + ${TAL_DIR}/src/tal_sw_timer.c + ${TAL_DIR}/src/tal_system.c + ${TAL_DIR}/src/tal_thread.c + ${TAL_DIR}/src/tal_time_serivce.c + ${TAL_DIR}/src/tal_workq_service.c + ${TAL_DIR}/src/tal_workqueue.c +) + +# ───────────────────────────────────────────────────────────────────────────── +# 可执行目标 +# ───────────────────────────────────────────────────────────────────────────── +find_package(SDL2 REQUIRED) + +add_executable(lvgl_sim + ${CMAKE_BINARY_DIR}/sim_main.c + ${LVGL_SRCS} + ${UI_SRCS} + ${UTIL_SRCS} + ${TKL_CORE_SRCS} + ${TAL_SRCS} +) + +# ───────────────────────────────────────────────────────────────────────────── +# 编译宏 +# ───────────────────────────────────────────────────────────────────────────── +target_compile_definitions(lvgl_sim PRIVATE + LVGL_PC_SIMULATOR # 总开关:控制 lv_conf.h / ENABLE_LVGL_HARDWARE + LV_CONF_INCLUDE_SIMPLE # LVGL 使用 lv_conf.h 而非相对路径查找 + LV_LVGL_H_INCLUDE_SIMPLE # 内部头以 "lvgl.h" 方式 include + OPERATING_SYSTEM=100 # SYSTEM_LINUX:激活 tuya_cloud_types.h 的 Linux 分支 +) + +# ───────────────────────────────────────────────────────────────────────────── +# Include 路径(顺序关键:stub 头必须在最前面) +# ───────────────────────────────────────────────────────────────────────────── +target_include_directories(lvgl_sim PRIVATE + # 1. platform/LINUX 真实头文件(优先于 stub,提供 tuya_cloud_types.h 和 tkl_*.h / tal_*.h) + ${PLATFORM_DIR}/include/utilities/include # tuya_cloud_types.h 及工具类头 + ${PLATFORM_DIR}/include/system # tkl_*.h + ${TAL_DIR}/include # tal_*.h(含 tal_log.h / PR_xxx) + ${COMMON_DIR}/include # tuya_error_code.h, tuya_iot_config.h + + # 2. Stub 头(屏蔽硬件 SDK 专属头:lv_vendor.h 等) + ${CMAKE_CURRENT_SOURCE_DIR}/include + + # 3. LVGL 驱动路径 trick:使 "../lvgl/lvgl.h" 解析为 lvgl/lvgl.h + ${LVGL_DIR}/lvgl + + # 4. LVGL conf(含 LVGL_PC_SIMULATOR 条件分支的 lv_conf.h) + ${LVGL_DIR}/conf + + # 5. 生成的 main.c 所在目录 + ${CMAKE_BINARY_DIR} + + # 6. App 专属头(game_pet.h 等) + ${SIM_UI_INC_DIRS} + + # 7. SDL2 + ${SDL2_INCLUDE_DIRS} +) + +target_link_libraries(lvgl_sim PRIVATE + ${SDL2_LIBRARIES} + m + pthread +) + +# ───────────────────────────────────────────────────────────────────────────── +# 调试信息 +# ───────────────────────────────────────────────────────────────────────────── +message(STATUS "=== LVGL Simulator ===") +message(STATUS "App: ${SIM_APP_NAME}") +message(STATUS "Resolution: ${SIM_SCREEN_W}x${SIM_SCREEN_H}") +message(STATUS "Entry func: ${SIM_ENTRY_FUNC}()") +message(STATUS "UI sources: ${SIM_UI_SRC_DIRS}") +message(STATUS "Output: ${CMAKE_CURRENT_SOURCE_DIR}/dist/lvgl_sim") diff --git a/examples/graphics/lvgl_simulator/README_en.md b/examples/graphics/lvgl_simulator/README_en.md new file mode 100644 index 000000000..09275777e --- /dev/null +++ b/examples/graphics/lvgl_simulator/README_en.md @@ -0,0 +1,212 @@ +# LVGL PC Simulator + +Run any TuyaOpen App's LVGL UI on a Linux desktop—no hardware required. UI source files are compiled directly into a native binary and rendered in an SDL2 window. + +The simulator also compiles the `platform/LINUX` TKL adapter layer and the `src/tal_system` TAL layer, so UI code can call `tal_*` / `tkl_*` interfaces natively and use `PR_xxx` macros for logging. + +--- + +## Directory Layout + +``` +lvgl_simulator/ +├── CMakeLists.txt # Build script (rarely needs editing) +├── sim_config.cmake # App config — the only file you need to edit to switch apps +├── sim_main.c.in # main.c template (generated into .build/ by cmake) +├── include/ # Stub headers (tuya_kconfig.h and other simulator-only stubs) +├── dist/ # Build output: lvgl_sim executable +└── .build/ # CMake intermediate files (safe to delete) +``` + +--- + +## Prerequisites + +```bash +# Ubuntu / Debian +sudo apt install cmake gcc libsdl2-dev + +# Fedora / RHEL +sudo dnf install cmake gcc SDL2-devel +``` + +--- + +## Build & Run + +### First build (or after changing sim_config.cmake / CMakeLists.txt) + +```bash +cd examples/graphics/lvgl_simulator + +cmake -B .build -DCMAKE_BUILD_TYPE=Debug +cmake --build .build -j$(nproc) + +./dist/lvgl_sim +``` + +### Incremental build (UI source files changed only) + +```bash +cmake --build .build -j$(nproc) +./dist/lvgl_sim +``` + +### Release build + +```bash +cmake -B .build -DCMAKE_BUILD_TYPE=Release +cmake --build .build -j$(nproc) +``` + +### Clean + +```bash +# Remove executable only +rm -f dist/lvgl_sim + +# Full clean — forces a complete rebuild next time +rm -rf .build dist +``` + +--- + +## Configuring sim_config.cmake + +`sim_config.cmake` is the single file you edit to switch between apps. All variables: + +```cmake +# Window title (any string, display-only) +set(SIM_APP_NAME "tuya_t5_pocket_ai") + +# Screen resolution — match the target hardware +set(SIM_SCREEN_W 384) +set(SIM_SCREEN_H 168) + +# UI entry function — called by the simulator's main() +set(SIM_ENTRY_FUNC "screens_init") + +# UI source directories — all .c files are compiled recursively +# Multiple directories can be listed +set(SIM_UI_SRC_DIRS + "${CMAKE_SOURCE_DIR}/../../../apps/your_app/src/display" +) + +# Header search paths (no SDK paths needed; simulator stubs cover them) +# Multiple directories can be listed +set(SIM_UI_INC_DIRS + "${CMAKE_SOURCE_DIR}/../../../apps/your_app/include" + "${CMAKE_SOURCE_DIR}/../../../apps/your_app/src/expand/inc" +) +``` + +After editing `sim_config.cmake`, re-run `cmake -B .build` — an incremental build is not sufficient. + +--- + +## Macro Reference + +The simulator uses a set of compile-time macros to separate PC and hardware environments. + +### Core macros + +| Macro | When defined | Purpose | +|-------|-------------|---------| +| `LVGL_PC_SIMULATOR` | Injected by CMake during simulator build | Master switch — identifies the PC simulator environment | +| `ENABLE_LVGL_HARDWARE` | Defined by `screen_manager.h` on hardware | Identifies the real hardware environment | +| `LV_CONF_INCLUDE_SIMPLE` | Injected by CMake | Tells LVGL to locate config via `lv_conf.h` | +| `LV_LVGL_H_INCLUDE_SIMPLE` | Injected by CMake | Tells LVGL internals to `#include "lvgl.h"` | +| `OPERATING_SYSTEM=100` | Injected by CMake | `SYSTEM_LINUX` — activates Linux branch in `tuya_cloud_types.h` | + +The relationship defined in `screen_manager.h`: + +```c +// ENABLE_LVGL_HARDWARE is only defined outside the simulator +#ifndef LVGL_PC_SIMULATOR +#define ENABLE_LVGL_HARDWARE +#endif +``` + +### lv_conf.h options affected by LVGL_PC_SIMULATOR + +| Option | Hardware value | Simulator value | Notes | +|--------|---------------|-----------------|-------| +| `LV_USE_STDLIB_MALLOC` | `LV_STDLIB_CUSTOM` | `LV_STDLIB_CLIB` | Embedded uses tlsf; PC uses system malloc | +| `LV_USE_SDL` | `0` | `1` | SDL2 display/input driver | +| `LV_USE_PNG` | `1` (Tuya custom) | `0` | Hardware PNG decoder (requires PSRAM, unavailable on PC) | +| `LV_USE_LODEPNG` | `0` | `1` | Pure-software PNG decoder used on PC | + +### Writing hardware guards in UI source files + +**Recommended — use `ENABLE_LVGL_HARDWARE`:** + +```c +// Hardware-only headers +#ifdef ENABLE_LVGL_HARDWARE +#include "lv_vendor.h" +#include "tkl_output.h" +#include "tdl_camera_manage.h" +#endif + +// Hardware operation with simulator stub +#ifdef ENABLE_LVGL_HARDWARE + camera_hw_start(); +#else + printf("[SIM] camera stub\n"); +#endif +``` + +**Function-level stub pattern:** + +```c +OPERATE_RET ai_ui_chat_register(void) +{ +#ifdef ENABLE_LVGL_HARDWARE + AI_UI_INTFS_T intfs = {0}; + intfs.disp_emotion = __ui_set_emotion; + intfs.disp_wifi_state = __ui_set_network; + return ai_ui_register(&intfs); +#else + return OPRT_OK; // simulator: no-op +#endif +} +``` + +**Note:** `#ifdef ENABLE_LVGL_HARDWARE` and `#ifndef LVGL_PC_SIMULATOR` are equivalent; prefer the former for consistency across the codebase. + +--- + +## TAL/TKL Interfaces & PR_xxx Logging + +The simulator compiles the full `platform/LINUX` TKL adapter and `src/tal_system` TAL layer. UI code can use these interfaces directly: + +- **`PR_ERR` / `PR_WARN` / `PR_NOTICE` / `PR_INFO` / `PR_DEBUG` / `PR_TRACE`** — TAL log macros, output to stdout +- **`tal_mutex_*` / `tal_semaphore_*`** — thread synchronization backed by Linux pthread +- **`tal_thread_*`** — thread management +- **`tal_system_*`** — system information +- **`tal_log_*`** — logging system, initialized at `TAL_LOG_LEVEL_DEBUG` in `main()` + +Example log output: +``` +[E][file.c:42] some error message +[D][file.c:100] debug info +``` + +**Compiled TKL modules** (Linux-native): `tkl_memory`, `tkl_mutex`, `tkl_semaphore`, `tkl_queue`, `tkl_thread`, `tkl_system`, `tkl_output`, `tkl_fs` + +**Compiled TAL modules**: `tal_log`, `tal_sleep`, `tal_sw_timer`, `tal_system`, `tal_thread`, `tal_time_service`, `tal_workqueue`, `tal_workq_service` + +> **Not compiled**: `tal_api` (requires network/UART/KV subsystems, not needed by the UI layer), `tal_event`, `tal_fs` (both depend on `tal_api.h`). + +--- + +## Keyboard Controls + +SDL keyboard events are mapped to LVGL key codes: + +| Key | LVGL event | +|-----|-----------| +| ↑ / ↓ / ← / → | `LV_KEY_UP` / `LV_KEY_DOWN` / `LV_KEY_LEFT` / `LV_KEY_RIGHT` | +| Enter | `LV_KEY_ENTER` | +| Esc | `LV_KEY_ESC` | +| Tab | `LV_KEY_NEXT` | diff --git a/examples/graphics/lvgl_simulator/README_zh.md b/examples/graphics/lvgl_simulator/README_zh.md new file mode 100644 index 000000000..efe18031f --- /dev/null +++ b/examples/graphics/lvgl_simulator/README_zh.md @@ -0,0 +1,212 @@ +# LVGL PC 模拟器 + +在 Linux 桌面上运行 TuyaOpen App 的 LVGL UI,无需烧录硬件。UI 源码直接编译为原生可执行文件,通过 SDL2 窗口渲染 LVGL 界面。 + +模拟器同时编译了 `platform/LINUX` 的 TKL 适配层和 `src/tal_system` 的 TAL 层,使得 UI 代码可以直接调用 `tal_*` / `tkl_*` 接口,并使用 `PR_xxx` 宏输出日志。 + +--- + +## 目录结构 + +``` +lvgl_simulator/ +├── CMakeLists.txt # 构建脚本(通常无需修改) +├── sim_config.cmake # App 配置文件(修改此文件切换 App) +├── sim_main.c.in # main.c 模板(cmake 时自动生成到 .build/) +├── include/ # Stub 头文件(tuya_kconfig.h 等模拟器专用桩) +├── dist/ # 编译产物:可执行文件 lvgl_sim +└── .build/ # CMake 中间产物(可安全删除) +``` + +--- + +## 依赖安装 + +```bash +# Ubuntu / Debian +sudo apt install cmake gcc libsdl2-dev + +# Fedora / RHEL +sudo dnf install cmake gcc SDL2-devel +``` + +--- + +## 编译与运行 + +### 首次编译(或修改了 sim_config.cmake / CMakeLists.txt 后) + +```bash +cd examples/graphics/lvgl_simulator + +cmake -B .build -DCMAKE_BUILD_TYPE=Debug +cmake --build .build -j$(nproc) + +./dist/lvgl_sim +``` + +### 仅修改了 UI 源码后(增量编译) + +```bash +cmake --build .build -j$(nproc) +./dist/lvgl_sim +``` + +### Release 构建 + +```bash +cmake -B .build -DCMAKE_BUILD_TYPE=Release +cmake --build .build -j$(nproc) +``` + +### 清除编译产物 + +```bash +# 只清除可执行文件 +rm -f dist/lvgl_sim + +# 清除所有中间产物(完全重新编译) +rm -rf .build dist +``` + +--- + +## 修改配置文件 + +`sim_config.cmake` 是切换 App 的唯一入口,所有变量说明如下: + +```cmake +# 窗口标题(任意字符串,仅用于显示) +set(SIM_APP_NAME "tuya_t5_pocket_ai") + +# 屏幕分辨率(与目标硬件保持一致) +set(SIM_SCREEN_W 384) +set(SIM_SCREEN_H 168) + +# UI 入口函数名(模拟器 main() 会调用此函数) +set(SIM_ENTRY_FUNC "screens_init") + +# UI 源码目录(递归搜索所有 .c 文件参与编译) +# 可以列多个目录 +set(SIM_UI_SRC_DIRS + "${CMAKE_SOURCE_DIR}/../../../apps/your_app/src/display" +) + +# UI 头文件搜索路径(不含 SDK 路径,模拟器 stub 已覆盖) +# 可以列多个目录 +set(SIM_UI_INC_DIRS + "${CMAKE_SOURCE_DIR}/../../../apps/your_app/include" + "${CMAKE_SOURCE_DIR}/../../../apps/your_app/src/expand/inc" +) +``` + +修改 `sim_config.cmake` 后,需要重新运行 `cmake -B .build` 才能生效(增量编译不够)。 + +--- + +## 宏定义说明 + +模拟器通过一组编译宏区分 PC 模拟环境与真实硬件环境。 + +### 核心宏 + +| 宏 | 定义时机 | 作用 | +|----|---------|------| +| `LVGL_PC_SIMULATOR` | 模拟器编译时由 CMake 注入 | 总开关,标识当前为 PC 模拟器环境 | +| `ENABLE_LVGL_HARDWARE` | 硬件编译时由 `screen_manager.h` 定义 | 标识当前为真实硬件环境 | +| `LV_CONF_INCLUDE_SIMPLE` | 模拟器编译时注入 | 让 LVGL 以 `lv_conf.h` 方式查找配置头 | +| `LV_LVGL_H_INCLUDE_SIMPLE` | 模拟器编译时注入 | 让 LVGL 内部以 `"lvgl.h"` 方式 include | +| `OPERATING_SYSTEM=100` | 模拟器编译时注入 | `SYSTEM_LINUX`,激活 `tuya_cloud_types.h` 的 Linux 分支 | + +`screen_manager.h` 中的定义关系: + +```c +// 非模拟器环境下才定义硬件宏 +#ifndef LVGL_PC_SIMULATOR +#define ENABLE_LVGL_HARDWARE +#endif +``` + +### lv_conf.h 中受 LVGL_PC_SIMULATOR 影响的配置 + +| 配置项 | 硬件值 | 模拟器值 | 说明 | +|--------|--------|---------|------| +| `LV_USE_STDLIB_MALLOC` | `LV_STDLIB_CUSTOM` | `LV_STDLIB_CLIB` | 内存分配器:嵌入式用 tlsf,PC 用系统 malloc | +| `LV_USE_SDL` | `0` | `1` | SDL2 显示/输入驱动 | +| `LV_USE_PNG` | `1`(Tuya 定制) | `0` | 硬件 PNG 解码器(依赖 PSRAM,PC 不可用) | +| `LV_USE_LODEPNG` | `0` | `1` | 纯软件 PNG 解码器(PC 模拟器使用) | + +### 在 UI 源码中使用宏 + +**推荐写法:使用 `ENABLE_LVGL_HARDWARE`** + +```c +// 硬件专属头文件 +#ifdef ENABLE_LVGL_HARDWARE +#include "lv_vendor.h" +#include "tkl_output.h" +#include "tdl_camera_manage.h" +#endif + +// 硬件操作分支 +#ifdef ENABLE_LVGL_HARDWARE + camera_hw_start(); +#else + printf("[SIM] camera stub\n"); +#endif +``` + +**函数级整体屏蔽写法:** + +```c +OPERATE_RET ai_ui_chat_register(void) +{ +#ifdef ENABLE_LVGL_HARDWARE + AI_UI_INTFS_T intfs = {0}; + intfs.disp_emotion = __ui_set_emotion; + intfs.disp_wifi_state = __ui_set_network; + return ai_ui_register(&intfs); +#else + return OPRT_OK; // 模拟器:空实现 +#endif +} +``` + +**注意:** `#ifndef LVGL_PC_SIMULATOR` 与 `#ifdef ENABLE_LVGL_HARDWARE` 等价,但推荐统一使用后者,保持代码一致性。 + +--- + +## TAL/TKL 接口与 PR_xxx 日志 + +模拟器编译了完整的 `platform/LINUX` TKL 适配层和 `src/tal_system` TAL 层,UI 代码可以直接使用: + +- **`PR_ERR` / `PR_WARN` / `PR_NOTICE` / `PR_INFO` / `PR_DEBUG` / `PR_TRACE`** — TAL 日志宏,输出到 stdout +- **`tal_mutex_*` / `tal_semaphore_*`** — 线程同步,底层调用 Linux pthread +- **`tal_thread_*`** — 线程管理 +- **`tal_system_*`** — 系统信息 +- **`tal_log_*`** — 日志系统,已在 `main()` 中初始化为 `TAL_LOG_LEVEL_DEBUG` + +日志格式示例: +``` +[E][file.c:42] some error message +[D][file.c:100] debug info +``` + +已编译的 TKL 模块(Linux 原生实现):`tkl_memory`, `tkl_mutex`, `tkl_semaphore`, `tkl_queue`, `tkl_thread`, `tkl_system`, `tkl_output`, `tkl_fs` + +已编译的 TAL 模块:`tal_log`, `tal_sleep`, `tal_sw_timer`, `tal_system`, `tal_thread`, `tal_time_service`, `tal_workqueue`, `tal_workq_service` + +> **未编译**:`tal_api`(依赖网络/UART/KV 子系统,UI 层无需)、`tal_event`、`tal_fs`(依赖 `tal_api.h`)。 + +--- + +## 键盘控制 + +SDL 键盘事件映射到 LVGL 按键: + +| 按键 | LVGL 事件 | +|------|-----------| +| ↑ / ↓ / ← / → | `LV_KEY_UP` / `LV_KEY_DOWN` / `LV_KEY_LEFT` / `LV_KEY_RIGHT` | +| Enter | `LV_KEY_ENTER` | +| Esc | `LV_KEY_ESC` | +| Tab | `LV_KEY_NEXT` | diff --git a/examples/graphics/lvgl_simulator/include/tuya_kconfig.h b/examples/graphics/lvgl_simulator/include/tuya_kconfig.h new file mode 100644 index 000000000..26baa5b62 --- /dev/null +++ b/examples/graphics/lvgl_simulator/include/tuya_kconfig.h @@ -0,0 +1,7 @@ +/* Auto-generated stub for LVGL PC simulator — replaces Kconfig-generated header */ +#ifndef TUYA_KCONFIG_H +#define TUYA_KCONFIG_H + +/* OPERATING_SYSTEM is injected by CMake as OPERATING_SYSTEM=100 (SYSTEM_LINUX) */ + +#endif /* TUYA_KCONFIG_H */ diff --git a/examples/graphics/lvgl_simulator/sim_config.cmake b/examples/graphics/lvgl_simulator/sim_config.cmake new file mode 100644 index 000000000..24913bdd1 --- /dev/null +++ b/examples/graphics/lvgl_simulator/sim_config.cmake @@ -0,0 +1,45 @@ +# ───────────────────────────────────────────────────────────────────────────── +# LVGL PC Simulator — App 配置文件 +# 修改此文件切换不同 App 的 UI,其他文件无需改动。 +# ───────────────────────────────────────────────────────────────────────────── + +# App 名称(仅用于窗口标题) +set(SIM_APP_NAME "tuya_t5_pocket_ai") + +# 屏幕分辨率(与目标硬件一致) +set(SIM_SCREEN_W 384) +set(SIM_SCREEN_H 168) + +# UI 入口函数(在 main.c 中被调用) +set(SIM_ENTRY_FUNC "screens_init") + +# UI 源码目录(递归搜索 .c 文件) +set(SIM_UI_SRC_DIRS + "${CMAKE_SOURCE_DIR}/../../../apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display" +) + +# App 专属头文件目录(不含 SDK 路径,stub 已覆盖) +set(SIM_UI_INC_DIRS + "${CMAKE_SOURCE_DIR}/../../../apps/tuya_t5_pocket/tuya_t5_pocket_ai/include" + "${CMAKE_SOURCE_DIR}/../../../apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/expand/inc" +) + +# ───────────────────────────────────────────────────────────────────────────── +# 切换到其他 App 示例(取消注释并注释上方配置即可): +# +# your_chat_bot: +# set(SIM_APP_NAME "your_chat_bot") +# set(SIM_SCREEN_W 480) +# set(SIM_SCREEN_H 320) +# set(SIM_ENTRY_FUNC "ui_init") +# set(SIM_UI_SRC_DIRS "${CMAKE_SOURCE_DIR}/../../../apps/tuya.ai/your_chat_bot/src/display2") +# set(SIM_UI_INC_DIRS "") +# +# your_robot_dog: +# set(SIM_APP_NAME "your_robot_dog") +# set(SIM_SCREEN_W 480) +# set(SIM_SCREEN_H 320) +# set(SIM_ENTRY_FUNC "tuya_robot_init") +# set(SIM_UI_SRC_DIRS "${CMAKE_SOURCE_DIR}/../../../apps/tuya.ai/your_robot_dog/src/display") +# set(SIM_UI_INC_DIRS "") +# ───────────────────────────────────────────────────────────────────────────── diff --git a/examples/graphics/lvgl_simulator/sim_main.c.in b/examples/graphics/lvgl_simulator/sim_main.c.in new file mode 100644 index 000000000..35e3d64c8 --- /dev/null +++ b/examples/graphics/lvgl_simulator/sim_main.c.in @@ -0,0 +1,39 @@ +/* Auto-generated by CMake — do not edit manually */ +#include "lvgl.h" +#include "src/drivers/sdl/lv_sdl_window.h" +#include "src/drivers/sdl/lv_sdl_mouse.h" +#include "src/drivers/sdl/lv_sdl_keyboard.h" +#include "tal_log.h" +#include "tkl_output.h" +#include +#include + +extern void @SIM_ENTRY_FUNC@(void); + +int main(void) +{ + /* Initialize TAL logging so PR_xxx macros produce output */ + tal_log_init(TAL_LOG_LEVEL_DEBUG, 4096, (TAL_LOG_OUTPUT_CB)tkl_log_output); + + lv_init(); + + lv_display_t *disp = lv_sdl_window_create(@SIM_SCREEN_W@, @SIM_SCREEN_H@); + lv_sdl_window_set_title(disp, "@SIM_APP_NAME@ [@SIM_SCREEN_W@x@SIM_SCREEN_H@]"); + + lv_sdl_mouse_create(); + + lv_indev_t *kb = lv_sdl_keyboard_create(); + lv_group_t *g = lv_group_create(); + lv_group_set_default(g); + lv_indev_set_group(kb, g); + + printf("[sim] Starting UI: @SIM_ENTRY_FUNC@()\n"); + @SIM_ENTRY_FUNC@(); + + while (1) { + uint32_t ms = lv_timer_handler(); + if (ms > 50) ms = 50; + usleep(ms * 1000); + } + return 0; +} diff --git a/src/liblvgl/v9/conf/lv_conf.h b/src/liblvgl/v9/conf/lv_conf.h index d3c09a172..4287cd13a 100755 --- a/src/liblvgl/v9/conf/lv_conf.h +++ b/src/liblvgl/v9/conf/lv_conf.h @@ -49,7 +49,11 @@ * - LV_STDLIB_RTTHREAD: RT-Thread implementation * - LV_STDLIB_CUSTOM: Implement the functions externally */ -#define LV_USE_STDLIB_MALLOC LV_STDLIB_CUSTOM +#ifdef LVGL_PC_SIMULATOR + #define LV_USE_STDLIB_MALLOC LV_STDLIB_CLIB +#else + #define LV_USE_STDLIB_MALLOC LV_STDLIB_CUSTOM +#endif #define LV_USE_STDLIB_STRING LV_STDLIB_BUILTIN #define LV_USE_STDLIB_SPRINTF LV_STDLIB_BUILTIN @@ -453,7 +457,11 @@ #endif #ifndef LV_FONT_MONTSERRAT_32 -#define LV_FONT_MONTSERRAT_32 0 + #ifdef LVGL_PC_SIMULATOR + #define LV_FONT_MONTSERRAT_32 1 + #else + #define LV_FONT_MONTSERRAT_32 0 + #endif #endif #ifndef LV_FONT_MONTSERRAT_34 @@ -485,7 +493,11 @@ #endif #ifndef LV_FONT_MONTSERRAT_48 -#define LV_FONT_MONTSERRAT_48 0 + #ifdef LVGL_PC_SIMULATOR + #define LV_FONT_MONTSERRAT_48 1 + #else + #define LV_FONT_MONTSERRAT_48 0 + #endif #endif @@ -754,15 +766,21 @@ /*PNG decoder library*/ // Modified by TUYA Start +#ifndef LVGL_PC_SIMULATOR #define LV_USE_PNG 1 #if LV_USE_PNG #define LV_PNG_USE_PSRAM 1 /* 0: use default sram memory, 1: use psram memory. */ #endif +#else +#define LV_USE_PNG 0 +#endif // Modified by TUYA End /*LODEPNG decoder library*/ #ifdef ENABLE_LVGL_LODEPNG #define LV_USE_LODEPNG ENABLE_LVGL_LODEPNG +#elif defined(LVGL_PC_SIMULATOR) +#define LV_USE_LODEPNG 1 #else #define LV_USE_LODEPNG 0 #endif @@ -966,7 +984,11 @@ *==================*/ /*Use SDL to open window on PC and handle mouse and keyboard*/ -#define LV_USE_SDL 0 +#ifdef LVGL_PC_SIMULATOR + #define LV_USE_SDL 1 +#else + #define LV_USE_SDL 0 +#endif #if LV_USE_SDL #define LV_SDL_INCLUDE_PATH #define LV_SDL_RENDER_MODE LV_DISPLAY_RENDER_MODE_DIRECT /*LV_DISPLAY_RENDER_MODE_DIRECT is recommended for best performance*/ diff --git a/src/liblvgl/v9/lvgl/src/core/lv_obj_event.c b/src/liblvgl/v9/lvgl/src/core/lv_obj_event.c index f92654b55..1fd336fed 100755 --- a/src/liblvgl/v9/lvgl/src/core/lv_obj_event.c +++ b/src/liblvgl/v9/lvgl/src/core/lv_obj_event.c @@ -98,8 +98,10 @@ lv_event_dsc_t * lv_obj_add_event_cb(lv_obj_t * obj, lv_event_cb_t event_cb, lv_ lv_obj_allocate_spec_attr(obj); // Modified by TUYA Start +#ifndef LVGL_PC_SIMULATOR void lvMsgEventReg(lv_obj_t *obj, lv_event_code_t eventCode); lvMsgEventReg(obj, filter); +#endif // Modified by TUYA End return lv_event_add(&obj->spec_attr->event_list, event_cb, filter, user_data); diff --git a/src/liblvgl/v9/lvgl/src/core/lv_obj_tree.c b/src/liblvgl/v9/lvgl/src/core/lv_obj_tree.c index b8fcfb90c..16f951fcc 100755 --- a/src/liblvgl/v9/lvgl/src/core/lv_obj_tree.c +++ b/src/liblvgl/v9/lvgl/src/core/lv_obj_tree.c @@ -546,8 +546,10 @@ static void obj_delete_core(lv_obj_t * obj) // Modified by TUYA Start //del self event obj +#ifndef LVGL_PC_SIMULATOR void lvMsgEventDel(lv_obj_t *obj); lvMsgEventDel(obj); +#endif // Modified by TUYA End /*All children deleted. Now clean up the object specific data*/ diff --git a/src/liblvgl/v9/lvgl/src/misc/lv_timer.c b/src/liblvgl/v9/lvgl/src/misc/lv_timer.c index a03ac01b1..78dfe8715 100755 --- a/src/liblvgl/v9/lvgl/src/misc/lv_timer.c +++ b/src/liblvgl/v9/lvgl/src/misc/lv_timer.c @@ -65,8 +65,10 @@ LV_ATTRIBUTE_TIMER_HANDLER uint32_t lv_timer_handler(void) LV_TRACE_TIMER("begin"); // Modified by TUYA Start +#ifndef LVGL_PC_SIMULATOR void lvMsgHandle(void); lvMsgHandle(); +#endif // Modified by TUYA End lv_timer_state_t * state_p = &state; diff --git a/src/liblvgl/v9/lvgl/src/stdlib/lv_mem.c b/src/liblvgl/v9/lvgl/src/stdlib/lv_mem.c index ac7939c41..5c0cc91b2 100755 --- a/src/liblvgl/v9/lvgl/src/stdlib/lv_mem.c +++ b/src/liblvgl/v9/lvgl/src/stdlib/lv_mem.c @@ -154,7 +154,7 @@ lv_result_t lv_mem_test(void) return LV_RESULT_INVALID; } -#if LV_USE_STDLIB_MALLOC != LV_STDLIB_CUSTOM +#if LV_USE_STDLIB_MALLOC == LV_STDLIB_BUILTIN if(lv_tlsf_check(tlsf)) { LV_LOG_WARN("failed"); return LV_RESULT_INVALID; @@ -173,7 +173,7 @@ void lv_mem_monitor(lv_mem_monitor_t * mon_p) { lv_memzero(mon_p, sizeof(lv_mem_monitor_t)); -#if LV_USE_STDLIB_MALLOC != LV_STDLIB_CUSTOM +#if LV_USE_STDLIB_MALLOC == LV_STDLIB_BUILTIN lv_mem_monitor_core(mon_p); #endif } From 8b4cc53aa5f8287214def121a8a1f0a84b101379 Mon Sep 17 00:00:00 2001 From: maidang Date: Thu, 30 Apr 2026 13:52:51 +0800 Subject: [PATCH 2/7] refactor(app_ui_helper): streamline volume and date/time functions for simulator compatibility - Refactored volume management functions to include simulator-specific behavior. - Simplified date and time retrieval functions, ensuring compatibility with both hardware and simulator environments. - Added network status change handling and improved date/time update mechanisms. - Updated conditional compilation directives to enhance code clarity and maintainability. --- .../src/display2/app_ui_helper.c | 260 ++++++++---------- .../src/display2/ui_wechat/screens/ui_home.c | 2 + .../src/display/ui/camera_screen.h | 6 +- .../src/display/ui/screen_manager.h | 1 + .../graphics/lvgl_simulator/CMakeLists.txt | 72 +++-- examples/graphics/lvgl_simulator/README_en.md | 131 ++++++--- examples/graphics/lvgl_simulator/README_zh.md | 133 ++++++--- .../lvgl_simulator/include/tuya_kconfig.h | 10 +- .../graphics/lvgl_simulator/sim_config.cmake | 67 +++-- src/liblvgl/v9/conf/lv_conf.h | 12 +- 10 files changed, 389 insertions(+), 305 deletions(-) diff --git a/apps/tuya.ai/your_chat_bot/src/display2/app_ui_helper.c b/apps/tuya.ai/your_chat_bot/src/display2/app_ui_helper.c index 525b359e6..315a92107 100644 --- a/apps/tuya.ai/your_chat_bot/src/display2/app_ui_helper.c +++ b/apps/tuya.ai/your_chat_bot/src/display2/app_ui_helper.c @@ -8,32 +8,20 @@ #include "app_ui_helper.h" #if defined(ENABLE_CHAT_DISPLAY2) && (ENABLE_CHAT_DISPLAY2 == 1) + #include "lang_config.h" +#include "tal_time_service.h" /* POSIX_TM_S, tal_time_get_local_time_custom */ +#include +#include +#ifndef LVGL_PC_SIMULATOR #include "ai_chat_main.h" - -#include "tal_time_service.h" #include "tuya_iot.h" #include "tuya_lvgl.h" - #include "screens/ui_setting.h" #include "ui.h" - #include "tal_api.h" #include "netmgr.h" -#include - -/*********************************************************** -************************macro define************************ -***********************************************************/ - -/*********************************************************** -***********************typedef define*********************** -***********************************************************/ - -/*********************************************************** -********************function declaration******************** -***********************************************************/ /*********************************************************** ***********************variable define********************** @@ -44,14 +32,6 @@ static TIMER_ID s_date_timer_id = NULL; /*********************************************************** ***********************function define********************** ***********************************************************/ - -uint8_t app_ui_get_volume_value(void) -{ - int volume = 0; - ai_audio_player_get_vol(&volume); - return volume; -} - static void __app_ui_set_volume_value_tm_cb(TIMER_ID timer_id, void *arg) { uint8_t value = *(uint8_t *)arg; @@ -68,21 +48,6 @@ static void __app_ui_set_volume_value_tm_cb(TIMER_ID timer_id, void *arg) tuya_lvgl_mutex_unlock(); } -void app_ui_set_volume_value(uint8_t value) -{ - static uint8_t s_volume_value = 0; - - s_volume_value = value; - - if (NULL == s_volume_timer_id) { - tal_sw_timer_create(__app_ui_set_volume_value_tm_cb, &s_volume_value, &s_volume_timer_id); - } - - if (s_volume_timer_id) { - tal_sw_timer_start(s_volume_timer_id, 300, TAL_TIMER_ONCE); - } -} - static void __app_ui_get_date_tm_cb(TIMER_ID timer_id, void *arg) { uint32_t year = 0, month = 0, day = 0; @@ -98,7 +63,6 @@ static void __app_ui_get_date_tm_cb(TIMER_ID timer_id, void *arg) PR_DEBUG("date: %04d-%02d-%02d, time: %02d:%02d", year, month, day, hour, minute); - // update date time display tuya_lvgl_mutex_lock(); ui_setting_date_update(year, month, day); ui_setting_time_update(hour, minute); @@ -110,43 +74,89 @@ static void __app_ui_get_date_tm_cb(TIMER_ID timer_id, void *arg) static uint8_t __app_ui_date_get_next_time(void) { POSIX_TM_S tm = {0}; - tal_time_get_local_time_custom(0, &tm); - return 60 - tm.tm_sec; } +static int __app_ui_time_sync_cb(void *data) +{ + __app_ui_get_date_tm_cb(NULL, NULL); + return 0; +} + +static int __app_ui_network_status_change_cb(void *data) +{ + netmgr_status_e net_status = *(netmgr_status_e *)data; + uint8_t connected = (net_status == NETMGR_LINK_UP) ? 1 : 0; + + tuya_lvgl_mutex_lock(); + ui_setting_wifi_update(connected); + ui_set_system_msg(connected ? SYSTEM_MSG_WIFI_SSID : SYSTEM_MSG_WIFI_DISCONNECTED); + tuya_lvgl_mutex_unlock(); + + return 0; +} +#endif /* !LVGL_PC_SIMULATOR */ + +/*********************************************************** +***********************function define********************** +***********************************************************/ + +uint8_t app_ui_get_volume_value(void) +{ +#ifndef LVGL_PC_SIMULATOR + int volume = 0; + ai_audio_player_get_vol(&volume); + return volume; +#else + return 50; +#endif +} + +void app_ui_set_volume_value(uint8_t value) +{ +#ifndef LVGL_PC_SIMULATOR + static uint8_t s_volume_value = 0; + s_volume_value = value; + + if (NULL == s_volume_timer_id) { + tal_sw_timer_create(__app_ui_set_volume_value_tm_cb, &s_volume_value, &s_volume_timer_id); + } + if (s_volume_timer_id) { + tal_sw_timer_start(s_volume_timer_id, 300, TAL_TIMER_ONCE); + } +#else + (void)value; +#endif +} + void app_ui_get_date_time_loop_start(void) { +#ifndef LVGL_PC_SIMULATOR if (NULL == s_date_timer_id) { tal_sw_timer_create(__app_ui_get_date_tm_cb, NULL, &s_date_timer_id); } - if (s_date_timer_id) { uint8_t next_time = __app_ui_date_get_next_time(); tal_sw_timer_start(s_date_timer_id, next_time * 1000 + 300, TAL_TIMER_ONCE); } +#endif } void app_ui_get_date_time_loop_stop(void) { +#ifndef LVGL_PC_SIMULATOR if (s_date_timer_id) { tal_sw_timer_stop(s_date_timer_id); } -} - -static int __app_ui_time_sync_cb(void *data) -{ - __app_ui_get_date_tm_cb(NULL, NULL); - return 0; +#endif } void app_ui_get_date(uint32_t *year, uint32_t *month, uint32_t *day) { - +#ifndef LVGL_PC_SIMULATOR OPERATE_RET rt = tal_time_check_time_sync(); if (rt != OPRT_OK) { - // subscribe time sync event tal_event_subscribe("app.time.sync", "app_ui_helper", __app_ui_time_sync_cb, SUBSCRIBE_TYPE_ONETIME); return; } @@ -158,24 +168,31 @@ void app_ui_get_date(uint32_t *year, uint32_t *month, uint32_t *day) *day = tm.tm_mday; PR_DEBUG("date: %04d-%02d-%02d", *year, *month, *day); - - return; +#else + if (year) *year = 2025; + if (month) *month = 1; + if (day) *day = 1; +#endif } void app_ui_get_time(uint32_t *hour, uint32_t *minute) { +#ifndef LVGL_PC_SIMULATOR POSIX_TM_S tm = {0}; tal_time_get_local_time_custom(0, &tm); *hour = tm.tm_hour; *minute = tm.tm_min; PR_DEBUG("time: %02d:%02d", *hour, *minute); - - return; +#else + if (hour) *hour = 12; + if (minute) *minute = 0; +#endif } void app_ui_get_wifi_status(uint8_t *status) { +#ifndef LVGL_PC_SIMULATOR netmgr_status_e net_status = NETMGR_LINK_DOWN; netmgr_conn_get(NETCONN_WIFI, NETCONN_CMD_STATUS, &net_status); if (net_status == NETMGR_LINK_UP) { @@ -183,55 +200,49 @@ void app_ui_get_wifi_status(uint8_t *status) } else { *status = 0; } - - return; -} - -static int __app_ui_network_status_change_cb(void *data) -{ - netmgr_status_e net_status = *(netmgr_status_e *)data; - uint8_t connected = (net_status == NETMGR_LINK_UP) ? 1 : 0; - - tuya_lvgl_mutex_lock(); - ui_setting_wifi_update(connected); - ui_set_system_msg(connected ? SYSTEM_MSG_WIFI_SSID : SYSTEM_MSG_WIFI_DISCONNECTED); - tuya_lvgl_mutex_unlock(); - - return 0; +#else + if (status) *status = 1; +#endif } void app_ui_network_status_change_subscribe(void) { - tal_event_subscribe(EVENT_LINK_STATUS_CHG, "app_ui_helper", __app_ui_network_status_change_cb, - SUBSCRIBE_TYPE_NORMAL); +#ifndef LVGL_PC_SIMULATOR + tal_event_subscribe(EVENT_LINK_STATUS_CHG, "app_ui_helper", + __app_ui_network_status_change_cb, SUBSCRIBE_TYPE_NORMAL); +#endif } void app_ui_network_status_change_unsubscribe(void) { - tal_event_unsubscribe(EVENT_LINK_STATUS_CHG, "app_ui_helper", __app_ui_network_status_change_cb); +#ifndef LVGL_PC_SIMULATOR + tal_event_unsubscribe(EVENT_LINK_STATUS_CHG, "app_ui_helper", + __app_ui_network_status_change_cb); +#endif } void app_ui_reset_device(void) { +#ifndef LVGL_PC_SIMULATOR tuya_iot_reset(tuya_iot_client_get()); +#endif } /* waveform power function start */ #include -// Audio power estimation #define AUDIO_POWER_BUFFER_SIZE 160 #define AUDIO_POWER_NORMALIZATION 50000.0f +#ifndef LVGL_PC_SIMULATOR static int16_t sg_audio_power_buffer[AUDIO_POWER_BUFFER_SIZE] = {0}; -static float sg_audio_power = 0.0f; -static MUTEX_HANDLE sg_audio_power_mutex = NULL; +static float sg_audio_power = 0.0f; +static MUTEX_HANDLE sg_audio_power_mutex = NULL; +#endif -/** - * @brief Calculate audio power from PCM samples - */ void app_ui_helper_calculate_audio_power(uint8_t *audio_data, uint32_t data_len) { +#ifndef LVGL_PC_SIMULATOR OPERATE_RET rt = OPRT_OK; if (NULL == sg_audio_power_mutex) { rt = tal_mutex_create_init(&sg_audio_power_mutex); @@ -245,14 +256,13 @@ void app_ui_helper_calculate_audio_power(uint8_t *audio_data, uint32_t data_len) return; } - uint32_t num_samples = data_len / 2; // 16-bit samples = 2 bytes per sample + uint32_t num_samples = data_len / 2; if (num_samples > AUDIO_POWER_BUFFER_SIZE) { num_samples = AUDIO_POWER_BUFFER_SIZE; } int16_t *pcm_samples = (int16_t *)audio_data; - // Update circular buffer if (num_samples >= AUDIO_POWER_BUFFER_SIZE) { memcpy(sg_audio_power_buffer, pcm_samples, AUDIO_POWER_BUFFER_SIZE * sizeof(int16_t)); } else { @@ -262,7 +272,6 @@ void app_ui_helper_calculate_audio_power(uint8_t *audio_data, uint32_t data_len) num_samples * sizeof(int16_t)); } - // Calculate max absolute value float max_value = 0.0f; for (int i = 0; i < AUDIO_POWER_BUFFER_SIZE; i++) { float abs_sample = fabsf((float)sg_audio_power_buffer[i]); @@ -271,29 +280,29 @@ void app_ui_helper_calculate_audio_power(uint8_t *audio_data, uint32_t data_len) } } - // Normalize float normalized_power = max_value / AUDIO_POWER_NORMALIZATION; - if (normalized_power > 1.0f) - normalized_power = 1.0f; - if (normalized_power < 0.0f) - normalized_power = 0.0f; + if (normalized_power > 1.0f) normalized_power = 1.0f; + if (normalized_power < 0.0f) normalized_power = 0.0f; - // Update power with mutex protection if (sg_audio_power_mutex != NULL) { tal_mutex_lock(sg_audio_power_mutex); sg_audio_power = normalized_power; tal_mutex_unlock(sg_audio_power_mutex); } +#else + (void)audio_data; (void)data_len; +#endif } float app_ui_helper_get_audio_power(void) { +#ifndef LVGL_PC_SIMULATOR static float power_max = 0.0f; static float power_min = 0.0f; - static float smoothed_power = 0.0f; // smoothed power value - static float display_value = 0.0f; // final display value (with inertia) - static float adaptive_threshold = 0.01f; // adaptive threshold - static uint32_t frame_count = 0; // frame counter + static float smoothed_power = 0.0f; + static float display_value = 0.0f; + static float adaptive_threshold = 0.01f; + static uint32_t frame_count = 0; float power = 0.0f; if (sg_audio_power_mutex != NULL) { @@ -302,85 +311,48 @@ float app_ui_helper_get_audio_power(void) tal_mutex_unlock(sg_audio_power_mutex); } - // PR_DEBUG("---> audio power: %f", power); - - // update power max and min - if (power > power_max) { - power_max = power; - } - if (power < power_min) { - power_min = power; - } + if (power > power_max) power_max = power; + if (power < power_min) power_min = power; - // update adaptive threshold every 100 frames (about 3 seconds at 33fps) frame_count++; if (frame_count >= 100) { - // use 15% of the maximum value as reference, but at least 0.003 adaptive_threshold = power_max * 0.15f; - if (adaptive_threshold < 0.003f) { - adaptive_threshold = 0.003f; - } - // gradually decay power_max, to avoid long-term impact after one large volume + if (adaptive_threshold < 0.003f) adaptive_threshold = 0.003f; power_max *= 0.9f; frame_count = 0; } - // PR_DEBUG("---> audio power: %f, power_max: %f, threshold: %f", power, power_max, adaptive_threshold); - - // 1. dynamic range adjustment - normalize according to adaptive threshold - // use smaller divisor for more sensitivity float normalized_power = power / (adaptive_threshold * 1.2f); - if (normalized_power > 1.0f) { - normalized_power = 1.0f; - } + if (normalized_power > 1.0f) normalized_power = 1.0f; - // 2. nonlinear mapping - use power curve to amplify changes - // square for values < 0.5 to boost small signals - // direct use for values >= 0.5 to preserve dynamics float enhanced_power; if (normalized_power < 0.5f) { - // amplify small values: x^0.7 (between sqrt and linear) enhanced_power = powf(normalized_power, 0.7f); } else { - // preserve large values with slight boost enhanced_power = 0.5f * powf(0.5f, 0.7f) + (normalized_power - 0.5f) * 1.3f; - if (enhanced_power > 1.0f) { - enhanced_power = 1.0f; - } + if (enhanced_power > 1.0f) enhanced_power = 1.0f; } - // 3. smoothing filter - exponential moving average (EMA) - // alpha = 0.6 means new value weight 60%, history value weight 40% (more responsive) const float alpha = 0.6f; - smoothed_power = alpha * enhanced_power + (1.0f - alpha) * smoothed_power; + smoothed_power = alpha * enhanced_power + (1.0f - alpha) * smoothed_power; - // 4. inertia effect - rise very fast(0.85), fall medium(0.3) float target_value = smoothed_power; if (target_value > display_value) { - // rise very fast for immediate response display_value = 0.85f * target_value + 0.15f * display_value; } else { - // fall at medium speed for natural decay display_value = 0.3f * target_value + 0.7f * display_value; } - // 5. add minimum animation threshold, below which display is 0 - if (display_value < 0.03f) { - display_value *= 0.7f; // faster decay to 0 - } - - // final limit range - if (display_value > 1.0f) { - display_value = 1.0f; - } - if (display_value < 0.0f) { - display_value = 0.0f; - } - - // PR_DEBUG("---> display_value: %f", display_value); + if (display_value < 0.03f) display_value *= 0.7f; + if (display_value > 1.0f) display_value = 1.0f; + if (display_value < 0.0f) display_value = 0.0f; return display_value; +#else + return 0.0f; +#endif } /* waveform power function end */ -#endif \ No newline at end of file + +#endif /* ENABLE_CHAT_DISPLAY2 */ diff --git a/apps/tuya.ai/your_chat_bot/src/display2/ui_wechat/screens/ui_home.c b/apps/tuya.ai/your_chat_bot/src/display2/ui_wechat/screens/ui_home.c index 5423a5efa..e1139c4c2 100644 --- a/apps/tuya.ai/your_chat_bot/src/display2/ui_wechat/screens/ui_home.c +++ b/apps/tuya.ai/your_chat_bot/src/display2/ui_wechat/screens/ui_home.c @@ -8,7 +8,9 @@ #include "lang_config.h" #include "src/core/lv_obj_style_gen.h" +#ifndef LVGL_PC_SIMULATOR #include "tal_api.h" +#endif #include #define UI_CHAT_MSG_MAX_COUNT (20) diff --git a/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/camera_screen.h b/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/camera_screen.h index 600ec1e70..a1b3112a7 100644 --- a/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/camera_screen.h +++ b/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/camera_screen.h @@ -25,7 +25,7 @@ extern "C" { #endif #include "screen_manager.h" -#ifndef ENABLE_LVGL_HARDWARE +#ifdef ENABLE_LVGL_HARDWARE #include "yuv422_to_binary.h" #endif /** @@ -36,7 +36,7 @@ extern "C" { */ typedef void (*camera_screen_lifecycle_cb_t)(BOOL_T is_init); -#ifndef ENABLE_LVGL_HARDWARE +#ifdef ENABLE_LVGL_HARDWARE /** * @brief Camera photo print callback type * Called when ENTER key is pressed to print current photo from raw YUV422 data @@ -57,7 +57,7 @@ void camera_screen_deinit(void); */ void camera_screen_register_lifecycle_cb(camera_screen_lifecycle_cb_t callback); -#ifndef ENABLE_LVGL_HARDWARE +#ifdef ENABLE_LVGL_HARDWARE /** * @brief Register photo print callback for camera screen * Called when ENTER key is pressed with current photo data diff --git a/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/screen_manager.h b/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/screen_manager.h index 1c1a8a5c3..f3bca0c52 100644 --- a/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/screen_manager.h +++ b/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/screen_manager.h @@ -27,6 +27,7 @@ #ifndef LVGL_PC_SIMULATOR #define ENABLE_LVGL_HARDWARE #endif + #ifdef ENABLE_LVGL_HARDWARE #include "tuya_cloud_types.h" #include "tal_log.h" diff --git a/examples/graphics/lvgl_simulator/CMakeLists.txt b/examples/graphics/lvgl_simulator/CMakeLists.txt index 86c2d8c7b..51b031aa6 100644 --- a/examples/graphics/lvgl_simulator/CMakeLists.txt +++ b/examples/graphics/lvgl_simulator/CMakeLists.txt @@ -1,10 +1,10 @@ cmake_minimum_required(VERSION 3.14) project(LVGLSimulator C) -# 加载用户配置(修改 sim_config.cmake 切换 App) +# Load app config — edit sim_config.cmake to switch apps include(${CMAKE_CURRENT_SOURCE_DIR}/sim_config.cmake) -# 定位 TuyaOpen 根目录(simulator 在 examples/graphics/lvgl_simulator/) +# Locate TuyaOpen root (simulator lives at examples/graphics/lvgl_simulator/) get_filename_component(TUYAOPEN_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/../../.." ABSOLUTE) @@ -13,11 +13,11 @@ set(PLATFORM_DIR "${TUYAOPEN_ROOT}/platform/LINUX/tuyaos_adapter") set(TAL_DIR "${TUYAOPEN_ROOT}/src/tal_system") set(COMMON_DIR "${TUYAOPEN_ROOT}/src/common") -# 可执行文件输出到 dist/ +# Output executable to dist/ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/dist) # ───────────────────────────────────────────────────────────────────────────── -# 生成 main.c(注入入口函数名和分辨率) +# Generate main.c (inject entry function name and screen resolution) # ───────────────────────────────────────────────────────────────────────────── configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/sim_main.c.in @@ -26,23 +26,27 @@ configure_file( ) # ───────────────────────────────────────────────────────────────────────────── -# 源文件收集 +# Source file collection # ───────────────────────────────────────────────────────────────────────────── -# LVGL 核心源码(含内置 SDL 驱动;排除 port/ 的嵌入式端口文件) +# LVGL core sources (includes built-in SDL driver; embedded port files excluded) file(GLOB_RECURSE LVGL_SRCS "${LVGL_DIR}/lvgl/src/*.c") -# UI 业务代码 +# UI source files (SIM_UI_SRC_DIRS entries can be directories or individual .c file paths) set(UI_SRCS "") -foreach(dir ${SIM_UI_SRC_DIRS}) - file(GLOB_RECURSE _srcs "${dir}/*.c") +foreach(entry ${SIM_UI_SRC_DIRS}) + if(IS_DIRECTORY "${entry}") + file(GLOB_RECURSE _srcs "${entry}/*.c") + else() + set(_srcs "${entry}") + endif() list(APPEND UI_SRCS ${_srcs}) endforeach() -# platform/LINUX 工具库(链表、哈希表等) +# platform/LINUX utility library (linked lists, hash tables, etc.) file(GLOB UTIL_SRCS "${PLATFORM_DIR}/include/utilities/src/*.c") -# platform/LINUX TKL 适配层(仅选取 TAL 依赖的核心模块) +# platform/LINUX TKL adapter layer (only the core modules required by TAL) set(TKL_CORE_SRCS ${PLATFORM_DIR}/src/tkl_memory.c ${PLATFORM_DIR}/src/tkl_mutex.c @@ -55,7 +59,8 @@ set(TKL_CORE_SRCS ${PLATFORM_DIR}/src/tkl_ota.c ) -# TAL 抽象层(含 tal_log.c 提供 PR_xxx 宏实现;排除依赖网络/UART/KV 头的 tal_event/fs) +# TAL abstraction layer (tal_log.c provides PR_xxx macros; +# tal_event.c and tal_fs.c excluded — they depend on network/UART/KV headers) set(TAL_SRCS ${TAL_DIR}/src/tal_api.c ${TAL_DIR}/src/tal_log.c @@ -69,7 +74,7 @@ set(TAL_SRCS ) # ───────────────────────────────────────────────────────────────────────────── -# 可执行目标 +# Executable target # ───────────────────────────────────────────────────────────────────────────── find_package(SDL2 REQUIRED) @@ -83,41 +88,50 @@ add_executable(lvgl_sim ) # ───────────────────────────────────────────────────────────────────────────── -# 编译宏 +# Compile-time definitions # ───────────────────────────────────────────────────────────────────────────── + +# Convert SIM_APP_NAME to a valid C identifier and inject as a preprocessor macro +# (read by tuya_kconfig.h to enable per-app feature macros) +string(MAKE_C_IDENTIFIER "${SIM_APP_NAME}" _sim_app_id) + target_compile_definitions(lvgl_sim PRIVATE - LVGL_PC_SIMULATOR # 总开关:控制 lv_conf.h / ENABLE_LVGL_HARDWARE - LV_CONF_INCLUDE_SIMPLE # LVGL 使用 lv_conf.h 而非相对路径查找 - LV_LVGL_H_INCLUDE_SIMPLE # 内部头以 "lvgl.h" 方式 include - OPERATING_SYSTEM=100 # SYSTEM_LINUX:激活 tuya_cloud_types.h 的 Linux 分支 + LVGL_PC_SIMULATOR # Master switch: identifies PC simulator environment + LV_CONF_INCLUDE_SIMPLE # Tell LVGL to locate its config via lv_conf.h + LV_LVGL_H_INCLUDE_SIMPLE # Tell LVGL internals to #include "lvgl.h" + OPERATING_SYSTEM=100 # SYSTEM_LINUX: activates the Linux branch in tuya_cloud_types.h + SIM_APP_${_sim_app_id} # Per-app identifier, read by tuya_kconfig.h ) # ───────────────────────────────────────────────────────────────────────────── -# Include 路径(顺序关键:stub 头必须在最前面) +# Include paths (order matters: real headers before stub headers) # ───────────────────────────────────────────────────────────────────────────── target_include_directories(lvgl_sim PRIVATE - # 1. platform/LINUX 真实头文件(优先于 stub,提供 tuya_cloud_types.h 和 tkl_*.h / tal_*.h) - ${PLATFORM_DIR}/include/utilities/include # tuya_cloud_types.h 及工具类头 + # 1. Real platform/LINUX headers (provide tuya_cloud_types.h, tkl_*.h, tal_*.h) + ${PLATFORM_DIR}/include/utilities/include # tuya_cloud_types.h and utility headers ${PLATFORM_DIR}/include/system # tkl_*.h - ${TAL_DIR}/include # tal_*.h(含 tal_log.h / PR_xxx) + ${TAL_DIR}/include # tal_*.h (including tal_log.h / PR_xxx) ${COMMON_DIR}/include # tuya_error_code.h, tuya_iot_config.h - # 2. Stub 头(屏蔽硬件 SDK 专属头:lv_vendor.h 等) + # 2. Stub headers (shadow hardware-only SDK headers such as lv_vendor.h) ${CMAKE_CURRENT_SOURCE_DIR}/include - # 3. LVGL 驱动路径 trick:使 "../lvgl/lvgl.h" 解析为 lvgl/lvgl.h + # 3. LVGL driver path trick: resolves "../lvgl/lvgl.h" relative to the driver directory ${LVGL_DIR}/lvgl - # 4. LVGL conf(含 LVGL_PC_SIMULATOR 条件分支的 lv_conf.h) + # 4. LVGL parent directory: resolves "lvgl/lvgl.h" (used by SquareLine Studio–generated code) + ${LVGL_DIR} + + # 5. LVGL config directory (contains lv_conf.h with LVGL_PC_SIMULATOR conditionals) ${LVGL_DIR}/conf - # 5. 生成的 main.c 所在目录 + # 6. Directory of the generated main.c ${CMAKE_BINARY_DIR} - # 6. App 专属头(game_pet.h 等) + # 7. App-specific headers ${SIM_UI_INC_DIRS} - # 7. SDL2 + # 8. SDL2 ${SDL2_INCLUDE_DIRS} ) @@ -128,7 +142,7 @@ target_link_libraries(lvgl_sim PRIVATE ) # ───────────────────────────────────────────────────────────────────────────── -# 调试信息 +# Status output # ───────────────────────────────────────────────────────────────────────────── message(STATUS "=== LVGL Simulator ===") message(STATUS "App: ${SIM_APP_NAME}") diff --git a/examples/graphics/lvgl_simulator/README_en.md b/examples/graphics/lvgl_simulator/README_en.md index 09275777e..b7b837764 100644 --- a/examples/graphics/lvgl_simulator/README_en.md +++ b/examples/graphics/lvgl_simulator/README_en.md @@ -1,6 +1,6 @@ # LVGL PC Simulator -Run any TuyaOpen App's LVGL UI on a Linux desktop—no hardware required. UI source files are compiled directly into a native binary and rendered in an SDL2 window. +Run any TuyaOpen App's LVGL UI on a desktop—no hardware required. UI source files are compiled directly into a native binary and rendered in an SDL2 window. The simulator also compiles the `platform/LINUX` TKL adapter layer and the `src/tal_system` TAL layer, so UI code can call `tal_*` / `tkl_*` interfaces natively and use `PR_xxx` macros for logging. @@ -22,14 +22,52 @@ lvgl_simulator/ ## Prerequisites +### Linux + ```bash # Ubuntu / Debian sudo apt install cmake gcc libsdl2-dev # Fedora / RHEL sudo dnf install cmake gcc SDL2-devel + +# Arch Linux +sudo pacman -S cmake gcc sdl2 +``` + +### macOS + +Install [Homebrew](https://brew.sh) if not already installed, then: + +```bash +brew install cmake sdl2 ``` +Xcode Command Line Tools are also required (`xcode-select --install`). + +### Windows + +**Option A — WSL2 (recommended)** + +Install [WSL2](https://learn.microsoft.com/en-us/windows/wsl/install) with Ubuntu, then follow the Linux instructions above. The SDL2 window renders via WSLg (Windows 11) or an X server such as [VcXsrv](https://sourceforge.net/projects/vcxsrv/) (Windows 10). + +```powershell +# In PowerShell (admin) — one-time WSL2 setup +wsl --install +``` + +**Option B — MSYS2 / MinGW-w64** + +1. Download and install [MSYS2](https://www.msys2.org/). +2. Open the **MSYS2 MinGW64** shell and run: + +```bash +pacman -S mingw-w64-x86_64-cmake mingw-w64-x86_64-gcc mingw-w64-x86_64-SDL2 +``` + +3. Build using the same commands as Linux (run inside the MinGW64 shell). + Replace `$(nproc)` with a fixed number, e.g. `-j8`. + --- ## Build & Run @@ -76,7 +114,7 @@ rm -rf .build dist `sim_config.cmake` is the single file you edit to switch between apps. All variables: ```cmake -# Window title (any string, display-only) +# Window title / app identifier (also used to derive the SIM_APP_ macro) set(SIM_APP_NAME "tuya_t5_pocket_ai") # Screen resolution — match the target hardware @@ -86,14 +124,12 @@ set(SIM_SCREEN_H 168) # UI entry function — called by the simulator's main() set(SIM_ENTRY_FUNC "screens_init") -# UI source directories — all .c files are compiled recursively -# Multiple directories can be listed +# UI source directories or individual .c file paths — compiled recursively for directories set(SIM_UI_SRC_DIRS "${CMAKE_SOURCE_DIR}/../../../apps/your_app/src/display" ) # Header search paths (no SDK paths needed; simulator stubs cover them) -# Multiple directories can be listed set(SIM_UI_INC_DIRS "${CMAKE_SOURCE_DIR}/../../../apps/your_app/include" "${CMAKE_SOURCE_DIR}/../../../apps/your_app/src/expand/inc" @@ -102,6 +138,43 @@ set(SIM_UI_INC_DIRS After editing `sim_config.cmake`, re-run `cmake -B .build` — an incremental build is not sufficient. +### Predefined app configurations + +The file ships with four ready-to-use configurations (uncomment the one you want): + +| App name | Resolution | Entry function | Notes | +|----------|-----------|---------------|-------| +| `tuya_t5_pocket_ai` | 384×168 | `screens_init` | Tuya T5 pocket AI device (default active) | +| `your_chat_bot_wechat` | 480×320 | `ui_init` | Chat-bot — WeChat-style UI | +| `your_chat_bot_chatbot` | 480×320 | `ui_init` | Chat-bot — chatbot-style UI | + +--- + +## tuya_kconfig.h — Per-app Feature Macros + +`include/tuya_kconfig.h` is a simulator stub for the Kconfig-generated header that normally lives in the firmware build. It enables the feature macros that each app's UI code depends on. + +CMake converts `SIM_APP_NAME` to a C identifier and injects it as a compile-time macro: + +```cmake +string(MAKE_C_IDENTIFIER "${SIM_APP_NAME}" _sim_app_id) +# e.g. "your_chat_bot_wechat" → SIM_APP_your_chat_bot_wechat +target_compile_definitions(... SIM_APP_${_sim_app_id}) +``` + +`tuya_kconfig.h` can then selectively enable macros per app: + +```c +// Always enable common font sizes used across UIs +#define LV_FONT_MONTSERRAT_16 1 +#define LV_FONT_MONTSERRAT_22 1 +#define LV_FONT_MONTSERRAT_32 1 +#define LV_FONT_MONTSERRAT_48 1 + +``` + +Add new entries here whenever a new app requires feature macros that aren't defined elsewhere. + --- ## Macro Reference @@ -113,19 +186,11 @@ The simulator uses a set of compile-time macros to separate PC and hardware envi | Macro | When defined | Purpose | |-------|-------------|---------| | `LVGL_PC_SIMULATOR` | Injected by CMake during simulator build | Master switch — identifies the PC simulator environment | -| `ENABLE_LVGL_HARDWARE` | Defined by `screen_manager.h` on hardware | Identifies the real hardware environment | +| `ENABLE_LVGL_HARDWARE` | Defined by `screen_manager.h` on hardware | Identifies the real hardware environment (pocket project only) | | `LV_CONF_INCLUDE_SIMPLE` | Injected by CMake | Tells LVGL to locate config via `lv_conf.h` | | `LV_LVGL_H_INCLUDE_SIMPLE` | Injected by CMake | Tells LVGL internals to `#include "lvgl.h"` | | `OPERATING_SYSTEM=100` | Injected by CMake | `SYSTEM_LINUX` — activates Linux branch in `tuya_cloud_types.h` | - -The relationship defined in `screen_manager.h`: - -```c -// ENABLE_LVGL_HARDWARE is only defined outside the simulator -#ifndef LVGL_PC_SIMULATOR -#define ENABLE_LVGL_HARDWARE -#endif -``` +| `SIM_APP_` | Injected by CMake | Per-app identifier for `tuya_kconfig.h` conditional macros | ### lv_conf.h options affected by LVGL_PC_SIMULATOR @@ -138,18 +203,17 @@ The relationship defined in `screen_manager.h`: ### Writing hardware guards in UI source files -**Recommended — use `ENABLE_LVGL_HARDWARE`:** +**Standard pattern — `#ifndef LVGL_PC_SIMULATOR`:** ```c // Hardware-only headers -#ifdef ENABLE_LVGL_HARDWARE +#ifndef LVGL_PC_SIMULATOR +#include "tal_api.h" #include "lv_vendor.h" -#include "tkl_output.h" -#include "tdl_camera_manage.h" #endif // Hardware operation with simulator stub -#ifdef ENABLE_LVGL_HARDWARE +#ifndef LVGL_PC_SIMULATOR camera_hw_start(); #else printf("[SIM] camera stub\n"); @@ -159,9 +223,9 @@ The relationship defined in `screen_manager.h`: **Function-level stub pattern:** ```c -OPERATE_RET ai_ui_chat_register(void) +OPERATE_RET ai_ui_register_intfs(void) { -#ifdef ENABLE_LVGL_HARDWARE +#ifndef LVGL_PC_SIMULATOR AI_UI_INTFS_T intfs = {0}; intfs.disp_emotion = __ui_set_emotion; intfs.disp_wifi_state = __ui_set_network; @@ -172,8 +236,6 @@ OPERATE_RET ai_ui_chat_register(void) } ``` -**Note:** `#ifdef ENABLE_LVGL_HARDWARE` and `#ifndef LVGL_PC_SIMULATOR` are equivalent; prefer the former for consistency across the codebase. - --- ## TAL/TKL Interfaces & PR_xxx Logging @@ -186,27 +248,8 @@ The simulator compiles the full `platform/LINUX` TKL adapter and `src/tal_system - **`tal_system_*`** — system information - **`tal_log_*`** — logging system, initialized at `TAL_LOG_LEVEL_DEBUG` in `main()` -Example log output: -``` -[E][file.c:42] some error message -[D][file.c:100] debug info -``` - -**Compiled TKL modules** (Linux-native): `tkl_memory`, `tkl_mutex`, `tkl_semaphore`, `tkl_queue`, `tkl_thread`, `tkl_system`, `tkl_output`, `tkl_fs` - -**Compiled TAL modules**: `tal_log`, `tal_sleep`, `tal_sw_timer`, `tal_system`, `tal_thread`, `tal_time_service`, `tal_workqueue`, `tal_workq_service` - -> **Not compiled**: `tal_api` (requires network/UART/KV subsystems, not needed by the UI layer), `tal_event`, `tal_fs` (both depend on `tal_api.h`). - --- ## Keyboard Controls -SDL keyboard events are mapped to LVGL key codes: - -| Key | LVGL event | -|-----|-----------| -| ↑ / ↓ / ← / → | `LV_KEY_UP` / `LV_KEY_DOWN` / `LV_KEY_LEFT` / `LV_KEY_RIGHT` | -| Enter | `LV_KEY_ENTER` | -| Esc | `LV_KEY_ESC` | -| Tab | `LV_KEY_NEXT` | +SDL keyboard events are mapped to LVGL key codes \ No newline at end of file diff --git a/examples/graphics/lvgl_simulator/README_zh.md b/examples/graphics/lvgl_simulator/README_zh.md index efe18031f..f96faa48c 100644 --- a/examples/graphics/lvgl_simulator/README_zh.md +++ b/examples/graphics/lvgl_simulator/README_zh.md @@ -1,6 +1,6 @@ # LVGL PC 模拟器 -在 Linux 桌面上运行 TuyaOpen App 的 LVGL UI,无需烧录硬件。UI 源码直接编译为原生可执行文件,通过 SDL2 窗口渲染 LVGL 界面。 +在桌面上运行 TuyaOpen App 的 LVGL UI,无需烧录硬件。UI 源码直接编译为原生可执行文件,通过 SDL2 窗口渲染 LVGL 界面。 模拟器同时编译了 `platform/LINUX` 的 TKL 适配层和 `src/tal_system` 的 TAL 层,使得 UI 代码可以直接调用 `tal_*` / `tkl_*` 接口,并使用 `PR_xxx` 宏输出日志。 @@ -22,14 +22,52 @@ lvgl_simulator/ ## 依赖安装 +### Linux + ```bash # Ubuntu / Debian sudo apt install cmake gcc libsdl2-dev # Fedora / RHEL sudo dnf install cmake gcc SDL2-devel + +# Arch Linux +sudo pacman -S cmake gcc sdl2 +``` + +### macOS + +如未安装 [Homebrew](https://brew.sh),先安装,然后: + +```bash +brew install cmake sdl2 ``` +同时需要 Xcode 命令行工具(`xcode-select --install`)。 + +### Windows + +**方案 A — WSL2(推荐)** + +安装 [WSL2](https://learn.microsoft.com/zh-cn/windows/wsl/install)(Ubuntu),然后按照上方 Linux 步骤操作即可。SDL2 窗口通过 WSLg(Windows 11)或 X 服务器(如 [VcXsrv](https://sourceforge.net/projects/vcxsrv/),Windows 10)渲染。 + +```powershell +# 在管理员 PowerShell 中一次性安装 WSL2 +wsl --install +``` + +**方案 B — MSYS2 / MinGW-w64** + +1. 下载并安装 [MSYS2](https://www.msys2.org/)。 +2. 打开 **MSYS2 MinGW64** 终端,执行: + +```bash +pacman -S mingw-w64-x86_64-cmake mingw-w64-x86_64-gcc mingw-w64-x86_64-SDL2 +``` + +3. 在 MinGW64 终端中按照与 Linux 相同的命令进行编译。 + 将 `$(nproc)` 替换为具体数字,如 `-j8`。 + --- ## 编译与运行 @@ -76,7 +114,7 @@ rm -rf .build dist `sim_config.cmake` 是切换 App 的唯一入口,所有变量说明如下: ```cmake -# 窗口标题(任意字符串,仅用于显示) +# 窗口标题 / App 标识符(同时用于派生 SIM_APP_ 宏) set(SIM_APP_NAME "tuya_t5_pocket_ai") # 屏幕分辨率(与目标硬件保持一致) @@ -86,14 +124,12 @@ set(SIM_SCREEN_H 168) # UI 入口函数名(模拟器 main() 会调用此函数) set(SIM_ENTRY_FUNC "screens_init") -# UI 源码目录(递归搜索所有 .c 文件参与编译) -# 可以列多个目录 +# UI 源码目录或单个 .c 文件路径;目录会递归搜索所有 .c 文件 set(SIM_UI_SRC_DIRS "${CMAKE_SOURCE_DIR}/../../../apps/your_app/src/display" ) # UI 头文件搜索路径(不含 SDK 路径,模拟器 stub 已覆盖) -# 可以列多个目录 set(SIM_UI_INC_DIRS "${CMAKE_SOURCE_DIR}/../../../apps/your_app/include" "${CMAKE_SOURCE_DIR}/../../../apps/your_app/src/expand/inc" @@ -102,6 +138,43 @@ set(SIM_UI_INC_DIRS 修改 `sim_config.cmake` 后,需要重新运行 `cmake -B .build` 才能生效(增量编译不够)。 +### 预置 App 配置 + +文件中已内置四套配置(取消注释对应段落即可激活): + +| App 名称 | 分辨率 | 入口函数 | 说明 | +|---------|--------|---------|------| +| `tuya_t5_pocket_ai` | 384×168 | `screens_init` | Tuya T5 口袋 AI 设备(当前默认激活) | +| `your_chat_bot_wechat` | 480×320 | `ui_init` | 聊天机器人 — 微信风格 UI | +| `your_chat_bot_chatbot` | 480×320 | `ui_init` | 聊天机器人 — chatbot 风格 UI | + +--- + +## tuya_kconfig.h — 按 App 开启功能宏 + +`include/tuya_kconfig.h` 是 Kconfig 生成头文件的模拟器桩,用于开启各 App UI 代码所依赖的功能宏。 + +CMake 将 `SIM_APP_NAME` 转换为 C 标识符并注入为编译宏: + +```cmake +string(MAKE_C_IDENTIFIER "${SIM_APP_NAME}" _sim_app_id) +# 例:"your_chat_bot_wechat" → SIM_APP_your_chat_bot_wechat +target_compile_definitions(... SIM_APP_${_sim_app_id}) +``` + +`tuya_kconfig.h` 即可按 App 有条件地开启宏: + +```c +// 各 UI 通用字体大小,无条件开启 +#define LV_FONT_MONTSERRAT_16 1 +#define LV_FONT_MONTSERRAT_22 1 +#define LV_FONT_MONTSERRAT_32 1 +#define LV_FONT_MONTSERRAT_48 1 + +``` + +新增 App 时,若 UI 代码依赖额外的功能宏,在此处添加对应条件定义。 + --- ## 宏定义说明 @@ -113,19 +186,11 @@ set(SIM_UI_INC_DIRS | 宏 | 定义时机 | 作用 | |----|---------|------| | `LVGL_PC_SIMULATOR` | 模拟器编译时由 CMake 注入 | 总开关,标识当前为 PC 模拟器环境 | -| `ENABLE_LVGL_HARDWARE` | 硬件编译时由 `screen_manager.h` 定义 | 标识当前为真实硬件环境 | +| `ENABLE_LVGL_HARDWARE` | 硬件编译时由 `screen_manager.h` 定义 | 标识当前为真实硬件环境(仅 pocket 项目使用) | | `LV_CONF_INCLUDE_SIMPLE` | 模拟器编译时注入 | 让 LVGL 以 `lv_conf.h` 方式查找配置头 | | `LV_LVGL_H_INCLUDE_SIMPLE` | 模拟器编译时注入 | 让 LVGL 内部以 `"lvgl.h"` 方式 include | | `OPERATING_SYSTEM=100` | 模拟器编译时注入 | `SYSTEM_LINUX`,激活 `tuya_cloud_types.h` 的 Linux 分支 | - -`screen_manager.h` 中的定义关系: - -```c -// 非模拟器环境下才定义硬件宏 -#ifndef LVGL_PC_SIMULATOR -#define ENABLE_LVGL_HARDWARE -#endif -``` +| `SIM_APP_` | 由 CMake 注入 | 每个 App 的唯一标识,供 `tuya_kconfig.h` 条件开启功能宏 | ### lv_conf.h 中受 LVGL_PC_SIMULATOR 影响的配置 @@ -138,18 +203,17 @@ set(SIM_UI_INC_DIRS ### 在 UI 源码中使用宏 -**推荐写法:使用 `ENABLE_LVGL_HARDWARE`** +**标准写法 — 使用 `#ifndef LVGL_PC_SIMULATOR`:** ```c // 硬件专属头文件 -#ifdef ENABLE_LVGL_HARDWARE +#ifndef LVGL_PC_SIMULATOR +#include "tal_api.h" #include "lv_vendor.h" -#include "tkl_output.h" -#include "tdl_camera_manage.h" #endif -// 硬件操作分支 -#ifdef ENABLE_LVGL_HARDWARE +// 硬件操作分支,模拟器提供默认实现 +#ifndef LVGL_PC_SIMULATOR camera_hw_start(); #else printf("[SIM] camera stub\n"); @@ -159,9 +223,9 @@ set(SIM_UI_INC_DIRS **函数级整体屏蔽写法:** ```c -OPERATE_RET ai_ui_chat_register(void) +OPERATE_RET ai_ui_register_intfs(void) { -#ifdef ENABLE_LVGL_HARDWARE +#ifndef LVGL_PC_SIMULATOR AI_UI_INTFS_T intfs = {0}; intfs.disp_emotion = __ui_set_emotion; intfs.disp_wifi_state = __ui_set_network; @@ -172,8 +236,6 @@ OPERATE_RET ai_ui_chat_register(void) } ``` -**注意:** `#ifndef LVGL_PC_SIMULATOR` 与 `#ifdef ENABLE_LVGL_HARDWARE` 等价,但推荐统一使用后者,保持代码一致性。 - --- ## TAL/TKL 接口与 PR_xxx 日志 @@ -186,27 +248,8 @@ OPERATE_RET ai_ui_chat_register(void) - **`tal_system_*`** — 系统信息 - **`tal_log_*`** — 日志系统,已在 `main()` 中初始化为 `TAL_LOG_LEVEL_DEBUG` -日志格式示例: -``` -[E][file.c:42] some error message -[D][file.c:100] debug info -``` - -已编译的 TKL 模块(Linux 原生实现):`tkl_memory`, `tkl_mutex`, `tkl_semaphore`, `tkl_queue`, `tkl_thread`, `tkl_system`, `tkl_output`, `tkl_fs` - -已编译的 TAL 模块:`tal_log`, `tal_sleep`, `tal_sw_timer`, `tal_system`, `tal_thread`, `tal_time_service`, `tal_workqueue`, `tal_workq_service` - -> **未编译**:`tal_api`(依赖网络/UART/KV 子系统,UI 层无需)、`tal_event`、`tal_fs`(依赖 `tal_api.h`)。 - --- ## 键盘控制 -SDL 键盘事件映射到 LVGL 按键: - -| 按键 | LVGL 事件 | -|------|-----------| -| ↑ / ↓ / ← / → | `LV_KEY_UP` / `LV_KEY_DOWN` / `LV_KEY_LEFT` / `LV_KEY_RIGHT` | -| Enter | `LV_KEY_ENTER` | -| Esc | `LV_KEY_ESC` | -| Tab | `LV_KEY_NEXT` | +SDL 键盘事件映射到 LVGL 按键 diff --git a/examples/graphics/lvgl_simulator/include/tuya_kconfig.h b/examples/graphics/lvgl_simulator/include/tuya_kconfig.h index 26baa5b62..360828fed 100644 --- a/examples/graphics/lvgl_simulator/include/tuya_kconfig.h +++ b/examples/graphics/lvgl_simulator/include/tuya_kconfig.h @@ -1,7 +1,15 @@ -/* Auto-generated stub for LVGL PC simulator — replaces Kconfig-generated header */ +/* Simulator stub for Kconfig-generated header. + * App-specific feature macros are enabled here based on SIM_APP_ + * injected by CMake (see CMakeLists.txt target_compile_definitions). + */ #ifndef TUYA_KCONFIG_H #define TUYA_KCONFIG_H /* OPERATING_SYSTEM is injected by CMake as OPERATING_SYSTEM=100 (SYSTEM_LINUX) */ +#define LV_FONT_MONTSERRAT_16 1 +#define LV_FONT_MONTSERRAT_22 1 +#define LV_FONT_MONTSERRAT_32 1 +#define LV_FONT_MONTSERRAT_48 1 + #endif /* TUYA_KCONFIG_H */ diff --git a/examples/graphics/lvgl_simulator/sim_config.cmake b/examples/graphics/lvgl_simulator/sim_config.cmake index 24913bdd1..8c714db56 100644 --- a/examples/graphics/lvgl_simulator/sim_config.cmake +++ b/examples/graphics/lvgl_simulator/sim_config.cmake @@ -1,45 +1,54 @@ # ───────────────────────────────────────────────────────────────────────────── -# LVGL PC Simulator — App 配置文件 -# 修改此文件切换不同 App 的 UI,其他文件无需改动。 +# LVGL PC Simulator — App config file +# Modify this file to switch between different apps, other files do not need to be modified. +# After modification, you need to re-run cmake -B .build to take effect (incremental compilation is not enough). # ───────────────────────────────────────────────────────────────────────────── -# App 名称(仅用于窗口标题) -set(SIM_APP_NAME "tuya_t5_pocket_ai") - -# 屏幕分辨率(与目标硬件一致) -set(SIM_SCREEN_W 384) -set(SIM_SCREEN_H 168) - -# UI 入口函数(在 main.c 中被调用) -set(SIM_ENTRY_FUNC "screens_init") - -# UI 源码目录(递归搜索 .c 文件) +# ── current active config: tuya_t5_pocket_ai ──────────────────────────────────── +# tuya_t5_pocket_ai (384×168): +set(SIM_APP_NAME "tuya_t5_pocket_ai") +set(SIM_SCREEN_W 384) +set(SIM_SCREEN_H 168) +set(SIM_ENTRY_FUNC "screens_init") set(SIM_UI_SRC_DIRS "${CMAKE_SOURCE_DIR}/../../../apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display" ) - -# App 专属头文件目录(不含 SDK 路径,stub 已覆盖) set(SIM_UI_INC_DIRS "${CMAKE_SOURCE_DIR}/../../../apps/tuya_t5_pocket/tuya_t5_pocket_ai/include" "${CMAKE_SOURCE_DIR}/../../../apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/expand/inc" ) # ───────────────────────────────────────────────────────────────────────────── -# 切换到其他 App 示例(取消注释并注释上方配置即可): # -# your_chat_bot: -# set(SIM_APP_NAME "your_chat_bot") -# set(SIM_SCREEN_W 480) -# set(SIM_SCREEN_H 320) +# your_chat_bot / ui_wechat (480×320): +# set(SIM_APP_NAME "your_chat_bot_wechat") +# set(SIM_SCREEN_W 320) +# set(SIM_SCREEN_H 480) # set(SIM_ENTRY_FUNC "ui_init") -# set(SIM_UI_SRC_DIRS "${CMAKE_SOURCE_DIR}/../../../apps/tuya.ai/your_chat_bot/src/display2") -# set(SIM_UI_INC_DIRS "") +# set(SIM_UI_SRC_DIRS +# "${CMAKE_SOURCE_DIR}/../../../apps/tuya.ai/your_chat_bot/src/display2/ui_wechat" +# "${CMAKE_SOURCE_DIR}/../../../apps/tuya.ai/your_chat_bot/src/display2/app_ui_helper.c" +# ) +# set(SIM_UI_INC_DIRS +# "${CMAKE_SOURCE_DIR}/../../../apps/tuya.ai/your_chat_bot/src/display2" +# "${CMAKE_SOURCE_DIR}/../../../apps/tuya.ai/your_chat_bot/src/display2/ui_wechat" +# "${CMAKE_SOURCE_DIR}/../../../apps/tuya.ai/ai_components/assets/include" +# ) # -# your_robot_dog: -# set(SIM_APP_NAME "your_robot_dog") -# set(SIM_SCREEN_W 480) -# set(SIM_SCREEN_H 320) -# set(SIM_ENTRY_FUNC "tuya_robot_init") -# set(SIM_UI_SRC_DIRS "${CMAKE_SOURCE_DIR}/../../../apps/tuya.ai/your_robot_dog/src/display") -# set(SIM_UI_INC_DIRS "") +# ───────────────────────────────────────────────────────────────────────────── +# your_chat_bot / ui_chatbot (480×320): +# set(SIM_APP_NAME "your_chat_bot_chatbot") +# set(SIM_SCREEN_W 480) +# set(SIM_SCREEN_H 320) +# set(SIM_ENTRY_FUNC "ui_init") +# set(SIM_UI_SRC_DIRS +# "${CMAKE_SOURCE_DIR}/../../../apps/tuya.ai/your_chat_bot/src/display2/ui_chatbot" +# "${CMAKE_SOURCE_DIR}/../../../apps/tuya.ai/your_chat_bot/src/display2/app_ui_helper.c" +# ) +# set(SIM_UI_INC_DIRS +# "${CMAKE_SOURCE_DIR}/../../../apps/tuya.ai/your_chat_bot/src/display2" +# "${CMAKE_SOURCE_DIR}/../../../apps/tuya.ai/your_chat_bot/src/display2/ui_chatbot" +# "${CMAKE_SOURCE_DIR}/../../../apps/tuya.ai/ai_components/assets/include" +# ) + # ───────────────────────────────────────────────────────────────────────────── diff --git a/src/liblvgl/v9/conf/lv_conf.h b/src/liblvgl/v9/conf/lv_conf.h index 4287cd13a..32cb36e8c 100755 --- a/src/liblvgl/v9/conf/lv_conf.h +++ b/src/liblvgl/v9/conf/lv_conf.h @@ -457,11 +457,7 @@ #endif #ifndef LV_FONT_MONTSERRAT_32 - #ifdef LVGL_PC_SIMULATOR - #define LV_FONT_MONTSERRAT_32 1 - #else - #define LV_FONT_MONTSERRAT_32 0 - #endif +#define LV_FONT_MONTSERRAT_32 #endif #ifndef LV_FONT_MONTSERRAT_34 @@ -493,11 +489,7 @@ #endif #ifndef LV_FONT_MONTSERRAT_48 - #ifdef LVGL_PC_SIMULATOR - #define LV_FONT_MONTSERRAT_48 1 - #else - #define LV_FONT_MONTSERRAT_48 0 - #endif +#define LV_FONT_MONTSERRAT_48 0 #endif From 269eac8b9f91d27be8ed5b60f74f61619991886e Mon Sep 17 00:00:00 2001 From: maidang Date: Thu, 30 Apr 2026 14:38:11 +0800 Subject: [PATCH 3/7] chore(docs): update installation instructions and remove Windows setup details - Revised installation instructions in English and Chinese README files for the LVGL simulator. - Updated package installation commands for Ubuntu, Fedora, and Arch Linux to include necessary build tools. - Removed Windows-specific setup instructions to streamline documentation and focus on Linux environments. --- examples/graphics/lvgl_simulator/README_en.md | 25 -------------- examples/graphics/lvgl_simulator/README_zh.md | 33 +++---------------- src/liblvgl/v9/conf/lv_conf.h | 2 +- 3 files changed, 5 insertions(+), 55 deletions(-) diff --git a/examples/graphics/lvgl_simulator/README_en.md b/examples/graphics/lvgl_simulator/README_en.md index b7b837764..24997f396 100644 --- a/examples/graphics/lvgl_simulator/README_en.md +++ b/examples/graphics/lvgl_simulator/README_en.md @@ -43,31 +43,6 @@ Install [Homebrew](https://brew.sh) if not already installed, then: brew install cmake sdl2 ``` -Xcode Command Line Tools are also required (`xcode-select --install`). - -### Windows - -**Option A — WSL2 (recommended)** - -Install [WSL2](https://learn.microsoft.com/en-us/windows/wsl/install) with Ubuntu, then follow the Linux instructions above. The SDL2 window renders via WSLg (Windows 11) or an X server such as [VcXsrv](https://sourceforge.net/projects/vcxsrv/) (Windows 10). - -```powershell -# In PowerShell (admin) — one-time WSL2 setup -wsl --install -``` - -**Option B — MSYS2 / MinGW-w64** - -1. Download and install [MSYS2](https://www.msys2.org/). -2. Open the **MSYS2 MinGW64** shell and run: - -```bash -pacman -S mingw-w64-x86_64-cmake mingw-w64-x86_64-gcc mingw-w64-x86_64-SDL2 -``` - -3. Build using the same commands as Linux (run inside the MinGW64 shell). - Replace `$(nproc)` with a fixed number, e.g. `-j8`. - --- ## Build & Run diff --git a/examples/graphics/lvgl_simulator/README_zh.md b/examples/graphics/lvgl_simulator/README_zh.md index f96faa48c..966cb915f 100644 --- a/examples/graphics/lvgl_simulator/README_zh.md +++ b/examples/graphics/lvgl_simulator/README_zh.md @@ -26,13 +26,13 @@ lvgl_simulator/ ```bash # Ubuntu / Debian -sudo apt install cmake gcc libsdl2-dev +sudo apt install build-essential cmake libsdl2-dev # Fedora / RHEL -sudo dnf install cmake gcc SDL2-devel +sudo dnf install @development-tools cmake SDL2-devel # Arch Linux -sudo pacman -S cmake gcc sdl2 +sudo pacman -S base-devel cmake sdl2 ``` ### macOS @@ -40,34 +40,9 @@ sudo pacman -S cmake gcc sdl2 如未安装 [Homebrew](https://brew.sh),先安装,然后: ```bash -brew install cmake sdl2 +brew install sdl2 cmake make ``` -同时需要 Xcode 命令行工具(`xcode-select --install`)。 - -### Windows - -**方案 A — WSL2(推荐)** - -安装 [WSL2](https://learn.microsoft.com/zh-cn/windows/wsl/install)(Ubuntu),然后按照上方 Linux 步骤操作即可。SDL2 窗口通过 WSLg(Windows 11)或 X 服务器(如 [VcXsrv](https://sourceforge.net/projects/vcxsrv/),Windows 10)渲染。 - -```powershell -# 在管理员 PowerShell 中一次性安装 WSL2 -wsl --install -``` - -**方案 B — MSYS2 / MinGW-w64** - -1. 下载并安装 [MSYS2](https://www.msys2.org/)。 -2. 打开 **MSYS2 MinGW64** 终端,执行: - -```bash -pacman -S mingw-w64-x86_64-cmake mingw-w64-x86_64-gcc mingw-w64-x86_64-SDL2 -``` - -3. 在 MinGW64 终端中按照与 Linux 相同的命令进行编译。 - 将 `$(nproc)` 替换为具体数字,如 `-j8`。 - --- ## 编译与运行 diff --git a/src/liblvgl/v9/conf/lv_conf.h b/src/liblvgl/v9/conf/lv_conf.h index 32cb36e8c..36906b6e9 100755 --- a/src/liblvgl/v9/conf/lv_conf.h +++ b/src/liblvgl/v9/conf/lv_conf.h @@ -457,7 +457,7 @@ #endif #ifndef LV_FONT_MONTSERRAT_32 -#define LV_FONT_MONTSERRAT_32 +#define LV_FONT_MONTSERRAT_32 0 #endif #ifndef LV_FONT_MONTSERRAT_34 From b2fb987e301deb76c70d09b4ccd86ee169425b19 Mon Sep 17 00:00:00 2001 From: maidang Date: Wed, 6 May 2026 10:25:32 +0800 Subject: [PATCH 4/7] docs(spec): add LVGL simulator component design spec Co-Authored-By: Claude Sonnet 4.6 --- ...6-05-06-lvgl-simulator-component-design.md | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 docs/superpowers/specs/2026-05-06-lvgl-simulator-component-design.md diff --git a/docs/superpowers/specs/2026-05-06-lvgl-simulator-component-design.md b/docs/superpowers/specs/2026-05-06-lvgl-simulator-component-design.md new file mode 100644 index 000000000..136ab4c7a --- /dev/null +++ b/docs/superpowers/specs/2026-05-06-lvgl-simulator-component-design.md @@ -0,0 +1,127 @@ +# LVGL PC Simulator Component Design + +**Date:** 2026-05-06 +**Branch:** lvgl_pc_simulator +**Scope:** Fix current implementation to match dev.md requirements + +--- + +## Background + +A previous session implemented the LVGL PC simulator as `src/liblvgl/simulator/` component. The implementation is mostly correct but has two problems identified in the updated dev.md (2026-05-06): + +1. `lvgl_sim.c` depends on `tal_log.h` / `tkl_output.h` — violates the "no strong platform binding" requirement +2. `app_default.config` was incorrectly modified during development testing — simulator is enabled and embedded board selection was removed + +--- + +## Requirements + +- `src/liblvgl/simulator/` is a pure LVGL + SDL2 bootstrap — no TAL/TKL dependencies +- UI design code only uses LVGL (`#ifdef ENABLE_LVGL_HARDWARE` guards handle hardware code) +- Default config remains the embedded T5AI baseline; simulator activated manually via `tos.py config menu` +- No changes to `tuya_main.c`, no new config files, no changes to `tos.py` + +--- + +## Architecture + +The component structure remains unchanged. Only `lvgl_sim.c` internals change. + +``` +src/liblvgl/simulator/ +├── Kconfig — unchanged (depends on ENABLE_LIBLVGL && PLATFORM_LINUX) +├── CMakeLists.txt — unchanged +├── include/lvgl_sim.h — unchanged +└── src/lvgl_sim.c — remove tal_log_init / tkl_log_output +``` + +### Dependency chain after fix + +``` +lvgl_sim.c → LVGL v9 + SDL2 drivers only +``` + +No TAL, no TKL, no embedded platform headers. + +### UI guard mechanism (unchanged) + +`screen_manager.h` defines the gate: + +```c +#ifndef LVGL_PC_SIMULATOR +#define ENABLE_LVGL_HARDWARE +#endif +``` + +All hardware-dependent includes in display/ui/*.c are wrapped in `#ifdef ENABLE_LVGL_HARDWARE`. This is already correct in the codebase. + +--- + +## Changes + +### Change 1 — `src/liblvgl/simulator/src/lvgl_sim.c` + +**Remove:** +```c +#include "tal_log.h" +#include "tkl_output.h" +// and the call: +tal_log_init(TAL_LOG_LEVEL_DEBUG, 4096, (TAL_LOG_OUTPUT_CB)tkl_log_output); +``` + +**Result:** `lvgl_sim_start()` begins directly with `lv_init()` then SDL window/input setup. + +### Change 2 — `apps/tuya_t5_pocket/tuya_t5_pocket_ai/app_default.config` + +Restore via: +```bash +git checkout HEAD -- apps/tuya_t5_pocket/tuya_t5_pocket_ai/app_default.config +``` + +Restores the embedded T5AI baseline: +- `CONFIG_BOARD_CHOICE_T5AI=y` +- `CONFIG_BOARD_CHOICE_TUYA_T5AI_POCKET=y` +- `CONFIG_LVGL_PC_SIMULATOR` not set (defaults to `n`) + +--- + +## Files Unchanged + +| File | Reason | +|------|--------| +| `src/liblvgl/simulator/Kconfig` | `depends on PLATFORM_LINUX` is correct for manual workflow | +| `src/liblvgl/simulator/CMakeLists.txt` | Conditional compile logic correct | +| `src/liblvgl/simulator/include/lvgl_sim.h` | Interface unchanged | +| `src/liblvgl/Kconfig` | `rsource "simulator/Kconfig"` correct | +| `src/liblvgl/CMakeLists.txt` | `add_subdirectory(simulator)` correct | +| `apps/.../CMakeLists.txt` | if/else conditional compile correct | +| `platform/LINUX/.../tuyaopen_adapter.cmake` | SDL2 link correct | +| All `display/ui/*.c` | `#ifdef ENABLE_LVGL_HARDWARE` guards in place | +| `rfid_scan_screen.c/.h` | Already wrapped in `#ifdef ENABLE_LVGL_HARDWARE` | +| `main_screen.c` | rfid_scan_screen load already commented out | + +--- + +## Usage Workflow + +### Simulator development +```bash +tos.py config menu # Select LINUX/Ubuntu board → enable LVGL PC Simulator → save +tos.py build # Compiles SDL executable +``` + +### Embedded development +```bash +tos.py config menu # Select T5AI board → LVGL PC Simulator off → save +tos.py build # Compiles embedded firmware +``` + +--- + +## Out of Scope + +- `tuya_main.c` — not modified +- `tos.py` / `cli_build.py` — not modified (manual board selection workflow retained) +- No new config files +- No auto platform selection From c91cfc5e2faa424508d85d398d896e4dd6c815f6 Mon Sep 17 00:00:00 2001 From: maidang Date: Wed, 6 May 2026 10:28:40 +0800 Subject: [PATCH 5/7] fix(lvgl_simulator): remove TAL/TKL platform dependencies from lvgl_sim.c --- src/liblvgl/simulator/src/lvgl_sim.c | 69 ++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 src/liblvgl/simulator/src/lvgl_sim.c diff --git a/src/liblvgl/simulator/src/lvgl_sim.c b/src/liblvgl/simulator/src/lvgl_sim.c new file mode 100644 index 000000000..f670c3199 --- /dev/null +++ b/src/liblvgl/simulator/src/lvgl_sim.c @@ -0,0 +1,69 @@ +/** + * @file lvgl_sim.c + * @brief LVGL PC simulator — SDL2 bootstrap, entry point, main loop + * @version 1.0 + * @date 2025-04-30 + * @copyright Copyright (c) Tuya Inc. + */ +#include "lvgl_sim.h" +#include "lvgl.h" +#include "src/drivers/sdl/lv_sdl_window.h" +#include "src/drivers/sdl/lv_sdl_mouse.h" +#include "src/drivers/sdl/lv_sdl_keyboard.h" +#include + +#ifndef CONFIG_SIM_SCREEN_WIDTH +#define CONFIG_SIM_SCREEN_WIDTH 384 +#endif + +#ifndef CONFIG_SIM_SCREEN_HEIGHT +#define CONFIG_SIM_SCREEN_HEIGHT 168 +#endif + +#ifndef CONFIG_SIM_WINDOW_TITLE +#define CONFIG_SIM_WINDOW_TITLE "TuyaOpen LVGL Simulator" +#endif + +/** + * @brief Start LVGL simulator with SDL2 + * @param[in] entry_cb UI init function + * @return none + */ +void lvgl_sim_start(LVGL_SIM_ENTRY_CB entry_cb) +{ + lv_init(); + + lv_display_t *disp = lv_sdl_window_create(CONFIG_SIM_SCREEN_WIDTH, + CONFIG_SIM_SCREEN_HEIGHT); + lv_sdl_window_set_title(disp, CONFIG_SIM_WINDOW_TITLE); + lv_sdl_mouse_create(); + + lv_indev_t *kb = lv_sdl_keyboard_create(); + lv_group_t *g = lv_group_create(); + lv_group_set_default(g); + lv_indev_set_group(kb, g); + + if (entry_cb) { + entry_cb(); + } + + while (1) { + uint32_t ms = lv_timer_handler(); + if (ms > 50) { + ms = 50; + } + usleep(ms * 1000); + } +} + +/* SIM_ENTRY_FUNC is injected via compile definition (e.g. screens_init) */ +extern void SIM_ENTRY_FUNC(void); + +/** + * @brief Simulator entry point, replaces the app's user_main + * @return none + */ +void user_main(void) +{ + lvgl_sim_start(SIM_ENTRY_FUNC); +} From e36035c35c5d9f792049ac15b40b92aae2135252 Mon Sep 17 00:00:00 2001 From: maidang Date: Wed, 6 May 2026 17:31:48 +0800 Subject: [PATCH 6/7] feat(lvgl_simulator): enhance LVGL PC simulator with new configuration and UI initialization - Added support for a new LVGL PC simulator configuration, allowing for a dedicated simulator mode. - Introduced a new entry function `ui_init` for initializing the UI, replacing the previous `screens_init`. - Updated relevant CMake and configuration files to support the new simulator structure. - Refactored display management functions to ensure compatibility with both hardware and simulator environments. - Improved documentation in README files to reflect changes in entry functions and simulator usage. --- apps/tuya.ai/your_chat_bot/app_default.config | 2 +- .../tuya_t5_pocket_ai/CMakeLists.txt | 25 ++++ .../config/TUYA_LINUX_LVGL_SIMULATOR.config | 5 + .../src/display/ui/main_screen.c | 2 +- .../src/display/ui/rfid_scan_screen.c | 5 +- .../src/display/ui/rfid_scan_screen.h | 3 + .../src/display/ui/screen_manager.c | 11 ++ .../src/display/ui/screen_manager.h | 18 ++- .../tuya_t5_pocket_ai/src/game_pet.c | 2 +- ...6-05-06-lvgl-simulator-component-design.md | 127 ------------------ examples/graphics/lvgl_simulator/README_en.md | 4 +- examples/graphics/lvgl_simulator/README_zh.md | 4 +- .../graphics/lvgl_simulator/sim_config.cmake | 2 +- src/liblvgl/CMakeLists.txt | 1 + src/liblvgl/Kconfig | 3 + src/liblvgl/simulator/CMakeLists.txt | 33 +++++ src/liblvgl/simulator/Kconfig | 30 +++++ src/liblvgl/simulator/include/lvgl_sim.h | 34 +++++ src/liblvgl/simulator/src/lvgl_sim.c | 12 +- src/liblvgl/v9/conf/lv_conf.h | 2 +- 20 files changed, 180 insertions(+), 145 deletions(-) create mode 100644 apps/tuya_t5_pocket/tuya_t5_pocket_ai/config/TUYA_LINUX_LVGL_SIMULATOR.config delete mode 100644 docs/superpowers/specs/2026-05-06-lvgl-simulator-component-design.md create mode 100644 src/liblvgl/simulator/CMakeLists.txt create mode 100644 src/liblvgl/simulator/Kconfig create mode 100644 src/liblvgl/simulator/include/lvgl_sim.h diff --git a/apps/tuya.ai/your_chat_bot/app_default.config b/apps/tuya.ai/your_chat_bot/app_default.config index a201f4824..aff78c58d 100644 --- a/apps/tuya.ai/your_chat_bot/app_default.config +++ b/apps/tuya.ai/your_chat_bot/app_default.config @@ -1,8 +1,8 @@ CONFIG_PROJECT_VERSION="1.0.1" CONFIG_TUYA_PRODUCT_ID="9inb01mvjqh5zhhr" -CONFIG_ENABLE_AI_UI_TEXT_STREAMING=y CONFIG_ENABLE_COMP_AI_AUDIO_CODEC_OPUS=y CONFIG_ENABLE_COMP_AI_VIDEO=y +CONFIG_ENABLE_AI_UI_TEXT_STREAMING=y CONFIG_BUTTON_NAME="ai_chat_button" CONFIG_BOARD_CHOICE_T5AI=y CONFIG_TUYA_T5AI_BOARD_EX_MODULE_35565LCD=y diff --git a/apps/tuya_t5_pocket/tuya_t5_pocket_ai/CMakeLists.txt b/apps/tuya_t5_pocket/tuya_t5_pocket_ai/CMakeLists.txt index 24ca314ea..78dcdcf14 100755 --- a/apps/tuya_t5_pocket/tuya_t5_pocket_ai/CMakeLists.txt +++ b/apps/tuya_t5_pocket/tuya_t5_pocket_ai/CMakeLists.txt @@ -9,6 +9,29 @@ set(APP_PATH ${CMAKE_CURRENT_LIST_DIR}) # APP_NAME get_filename_component(APP_NAME ${APP_PATH} NAME) +if (CONFIG_LVGL_PC_SIMULATOR STREQUAL "y") +######################################## +# Simulator mode: display only +######################################## +add_library(${EXAMPLE_LIB}) + +target_compile_options(${EXAMPLE_LIB} + PRIVATE + "-DLV_LVGL_H_INCLUDE_SIMPLE" +) + +target_include_directories(${EXAMPLE_LIB} + PRIVATE + ${APP_PATH}/include +) + +add_subdirectory(${APP_PATH}/src/display) + +else() +######################################## +# Embedded mode: full build (original) +######################################## + # APP_SRCS aux_source_directory(${APP_PATH}/src APP_SRCS) aux_source_directory(${APP_PATH}/src/media media_alert) @@ -50,3 +73,5 @@ add_subdirectory(${APP_PATH}/src/expand) add_subdirectory(${APP_PATH}/../../tuya.ai/ai_components) target_include_directories(${EXAMPLE_LIB} PRIVATE ${APP_PATH}/../../tuya.ai/ai_components) + +endif() diff --git a/apps/tuya_t5_pocket/tuya_t5_pocket_ai/config/TUYA_LINUX_LVGL_SIMULATOR.config b/apps/tuya_t5_pocket/tuya_t5_pocket_ai/config/TUYA_LINUX_LVGL_SIMULATOR.config new file mode 100644 index 000000000..d91085785 --- /dev/null +++ b/apps/tuya_t5_pocket/tuya_t5_pocket_ai/config/TUYA_LINUX_LVGL_SIMULATOR.config @@ -0,0 +1,5 @@ +CONFIG_PROJECT_VERSION="0.0.1" +CONFIG_BOARD_CHOICE_LINUX=y +CONFIG_BOARD_CHOICE_UBUNTU=y +CONFIG_ENABLE_LIBLVGL=y +CONFIG_LVGL_PC_SIMULATOR=y \ No newline at end of file diff --git a/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/main_screen.c b/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/main_screen.c index 9219644c5..9fa43bf77 100644 --- a/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/main_screen.c +++ b/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/main_screen.c @@ -430,7 +430,7 @@ static void keyboard_event_cb(lv_event_t *e) printf("C key pressed - Setting battery to charging\n"); // screen_load(&standby_screen); // screen_load(&ebook_screen); - screen_load(&rfid_scan_screen); + // screen_load(&rfid_scan_screen); break; #endif default: diff --git a/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/rfid_scan_screen.c b/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/rfid_scan_screen.c index 5b090bf1a..4571d08b6 100644 --- a/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/rfid_scan_screen.c +++ b/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/rfid_scan_screen.c @@ -22,7 +22,7 @@ /*********************************************************** ************************macro define************************ ***********************************************************/ - +#ifdef ENABLE_LVGL_HARDWARE // Screen dimensions #ifndef AI_PET_SCREEN_WIDTH #define AI_PET_SCREEN_WIDTH 384 @@ -534,4 +534,5 @@ void rfid_scan_screen_load(void) if (screen_get_now_screen() != &rfid_scan_screen) { screen_load(&rfid_scan_screen); } -} \ No newline at end of file +} +#endif // ENABLE_LVGL_HARDWARE \ No newline at end of file diff --git a/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/rfid_scan_screen.h b/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/rfid_scan_screen.h index 479a0a5ad..d89100601 100755 --- a/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/rfid_scan_screen.h +++ b/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/rfid_scan_screen.h @@ -23,6 +23,7 @@ extern "C" { #endif #include "screen_manager.h" +#ifdef ENABLE_LVGL_HARDWARE #include "rfid_scan.h" extern Screen_t rfid_scan_screen; @@ -46,4 +47,6 @@ void rfid_scan_screen_load(void); } /*extern "C"*/ #endif +#endif // ENABLE_LVGL_HARDWARE + #endif /*RFID_SCAN_SCREEN_H*/ \ No newline at end of file diff --git a/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/screen_manager.c b/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/screen_manager.c index 39b6dde8e..55fb27f9d 100644 --- a/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/screen_manager.c +++ b/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/screen_manager.c @@ -215,3 +215,14 @@ void screens_init(void) printf("[Error]: startup_screen.screen_obj is NULL or invalid during initialization\n"); } } + +/** + * @brief Initialize the UI + * + * This function is a wrapper for the screens_init function. + * It is used to initialize the UI. + */ +void ui_init(void) +{ + screens_init(); +} \ No newline at end of file diff --git a/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/screen_manager.h b/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/screen_manager.h index f3bca0c52..e4ac421c8 100644 --- a/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/screen_manager.h +++ b/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display/ui/screen_manager.h @@ -19,6 +19,10 @@ #ifndef SCREEN_MANAGER_H #define SCREEN_MANAGER_H +#ifdef __cplusplus +extern "C" { +#endif + #include "../lvgl/lvgl.h" /*********************************************************** @@ -112,4 +116,16 @@ void screen_load(Screen_t *newScreen); */ void screens_init(void); -#endif // SCREEN_STACK_H +/** + * @brief Initialize the UI + * + * This function is a wrapper for the screens_init function. + * It is used to initialize the UI. + */ +void ui_init(void); + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif // SCREEN_MANAGER_H diff --git a/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/game_pet.c b/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/game_pet.c index b3333f8f1..6d740ea8f 100644 --- a/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/game_pet.c +++ b/apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/game_pet.c @@ -409,7 +409,7 @@ static void __game_display_init(void) { lv_vendor_init(DISPLAY_NAME); - screens_init(); + ui_init(); lv_vendor_start(5, 1024*8); } diff --git a/docs/superpowers/specs/2026-05-06-lvgl-simulator-component-design.md b/docs/superpowers/specs/2026-05-06-lvgl-simulator-component-design.md deleted file mode 100644 index 136ab4c7a..000000000 --- a/docs/superpowers/specs/2026-05-06-lvgl-simulator-component-design.md +++ /dev/null @@ -1,127 +0,0 @@ -# LVGL PC Simulator Component Design - -**Date:** 2026-05-06 -**Branch:** lvgl_pc_simulator -**Scope:** Fix current implementation to match dev.md requirements - ---- - -## Background - -A previous session implemented the LVGL PC simulator as `src/liblvgl/simulator/` component. The implementation is mostly correct but has two problems identified in the updated dev.md (2026-05-06): - -1. `lvgl_sim.c` depends on `tal_log.h` / `tkl_output.h` — violates the "no strong platform binding" requirement -2. `app_default.config` was incorrectly modified during development testing — simulator is enabled and embedded board selection was removed - ---- - -## Requirements - -- `src/liblvgl/simulator/` is a pure LVGL + SDL2 bootstrap — no TAL/TKL dependencies -- UI design code only uses LVGL (`#ifdef ENABLE_LVGL_HARDWARE` guards handle hardware code) -- Default config remains the embedded T5AI baseline; simulator activated manually via `tos.py config menu` -- No changes to `tuya_main.c`, no new config files, no changes to `tos.py` - ---- - -## Architecture - -The component structure remains unchanged. Only `lvgl_sim.c` internals change. - -``` -src/liblvgl/simulator/ -├── Kconfig — unchanged (depends on ENABLE_LIBLVGL && PLATFORM_LINUX) -├── CMakeLists.txt — unchanged -├── include/lvgl_sim.h — unchanged -└── src/lvgl_sim.c — remove tal_log_init / tkl_log_output -``` - -### Dependency chain after fix - -``` -lvgl_sim.c → LVGL v9 + SDL2 drivers only -``` - -No TAL, no TKL, no embedded platform headers. - -### UI guard mechanism (unchanged) - -`screen_manager.h` defines the gate: - -```c -#ifndef LVGL_PC_SIMULATOR -#define ENABLE_LVGL_HARDWARE -#endif -``` - -All hardware-dependent includes in display/ui/*.c are wrapped in `#ifdef ENABLE_LVGL_HARDWARE`. This is already correct in the codebase. - ---- - -## Changes - -### Change 1 — `src/liblvgl/simulator/src/lvgl_sim.c` - -**Remove:** -```c -#include "tal_log.h" -#include "tkl_output.h" -// and the call: -tal_log_init(TAL_LOG_LEVEL_DEBUG, 4096, (TAL_LOG_OUTPUT_CB)tkl_log_output); -``` - -**Result:** `lvgl_sim_start()` begins directly with `lv_init()` then SDL window/input setup. - -### Change 2 — `apps/tuya_t5_pocket/tuya_t5_pocket_ai/app_default.config` - -Restore via: -```bash -git checkout HEAD -- apps/tuya_t5_pocket/tuya_t5_pocket_ai/app_default.config -``` - -Restores the embedded T5AI baseline: -- `CONFIG_BOARD_CHOICE_T5AI=y` -- `CONFIG_BOARD_CHOICE_TUYA_T5AI_POCKET=y` -- `CONFIG_LVGL_PC_SIMULATOR` not set (defaults to `n`) - ---- - -## Files Unchanged - -| File | Reason | -|------|--------| -| `src/liblvgl/simulator/Kconfig` | `depends on PLATFORM_LINUX` is correct for manual workflow | -| `src/liblvgl/simulator/CMakeLists.txt` | Conditional compile logic correct | -| `src/liblvgl/simulator/include/lvgl_sim.h` | Interface unchanged | -| `src/liblvgl/Kconfig` | `rsource "simulator/Kconfig"` correct | -| `src/liblvgl/CMakeLists.txt` | `add_subdirectory(simulator)` correct | -| `apps/.../CMakeLists.txt` | if/else conditional compile correct | -| `platform/LINUX/.../tuyaopen_adapter.cmake` | SDL2 link correct | -| All `display/ui/*.c` | `#ifdef ENABLE_LVGL_HARDWARE` guards in place | -| `rfid_scan_screen.c/.h` | Already wrapped in `#ifdef ENABLE_LVGL_HARDWARE` | -| `main_screen.c` | rfid_scan_screen load already commented out | - ---- - -## Usage Workflow - -### Simulator development -```bash -tos.py config menu # Select LINUX/Ubuntu board → enable LVGL PC Simulator → save -tos.py build # Compiles SDL executable -``` - -### Embedded development -```bash -tos.py config menu # Select T5AI board → LVGL PC Simulator off → save -tos.py build # Compiles embedded firmware -``` - ---- - -## Out of Scope - -- `tuya_main.c` — not modified -- `tos.py` / `cli_build.py` — not modified (manual board selection workflow retained) -- No new config files -- No auto platform selection diff --git a/examples/graphics/lvgl_simulator/README_en.md b/examples/graphics/lvgl_simulator/README_en.md index 24997f396..20038336b 100644 --- a/examples/graphics/lvgl_simulator/README_en.md +++ b/examples/graphics/lvgl_simulator/README_en.md @@ -97,7 +97,7 @@ set(SIM_SCREEN_W 384) set(SIM_SCREEN_H 168) # UI entry function — called by the simulator's main() -set(SIM_ENTRY_FUNC "screens_init") +set(SIM_ENTRY_FUNC "ui_init") # UI source directories or individual .c file paths — compiled recursively for directories set(SIM_UI_SRC_DIRS @@ -119,7 +119,7 @@ The file ships with four ready-to-use configurations (uncomment the one you want | App name | Resolution | Entry function | Notes | |----------|-----------|---------------|-------| -| `tuya_t5_pocket_ai` | 384×168 | `screens_init` | Tuya T5 pocket AI device (default active) | +| `tuya_t5_pocket_ai` | 384×168 | `ui_init` | Tuya T5 pocket AI device (default active) | | `your_chat_bot_wechat` | 480×320 | `ui_init` | Chat-bot — WeChat-style UI | | `your_chat_bot_chatbot` | 480×320 | `ui_init` | Chat-bot — chatbot-style UI | diff --git a/examples/graphics/lvgl_simulator/README_zh.md b/examples/graphics/lvgl_simulator/README_zh.md index 966cb915f..bf9281ee3 100644 --- a/examples/graphics/lvgl_simulator/README_zh.md +++ b/examples/graphics/lvgl_simulator/README_zh.md @@ -97,7 +97,7 @@ set(SIM_SCREEN_W 384) set(SIM_SCREEN_H 168) # UI 入口函数名(模拟器 main() 会调用此函数) -set(SIM_ENTRY_FUNC "screens_init") +set(SIM_ENTRY_FUNC "ui_init") # UI 源码目录或单个 .c 文件路径;目录会递归搜索所有 .c 文件 set(SIM_UI_SRC_DIRS @@ -119,7 +119,7 @@ set(SIM_UI_INC_DIRS | App 名称 | 分辨率 | 入口函数 | 说明 | |---------|--------|---------|------| -| `tuya_t5_pocket_ai` | 384×168 | `screens_init` | Tuya T5 口袋 AI 设备(当前默认激活) | +| `tuya_t5_pocket_ai` | 384×168 | `ui_init` | Tuya T5 口袋 AI 设备(当前默认激活) | | `your_chat_bot_wechat` | 480×320 | `ui_init` | 聊天机器人 — 微信风格 UI | | `your_chat_bot_chatbot` | 480×320 | `ui_init` | 聊天机器人 — chatbot 风格 UI | diff --git a/examples/graphics/lvgl_simulator/sim_config.cmake b/examples/graphics/lvgl_simulator/sim_config.cmake index 8c714db56..657b5b0a9 100644 --- a/examples/graphics/lvgl_simulator/sim_config.cmake +++ b/examples/graphics/lvgl_simulator/sim_config.cmake @@ -9,7 +9,7 @@ set(SIM_APP_NAME "tuya_t5_pocket_ai") set(SIM_SCREEN_W 384) set(SIM_SCREEN_H 168) -set(SIM_ENTRY_FUNC "screens_init") +set(SIM_ENTRY_FUNC "ui_init") set(SIM_UI_SRC_DIRS "${CMAKE_SOURCE_DIR}/../../../apps/tuya_t5_pocket/tuya_t5_pocket_ai/src/display" ) diff --git a/src/liblvgl/CMakeLists.txt b/src/liblvgl/CMakeLists.txt index 534c9bfcb..e778fa075 100755 --- a/src/liblvgl/CMakeLists.txt +++ b/src/liblvgl/CMakeLists.txt @@ -8,6 +8,7 @@ if (CONFIG_ENABLE_LIBLVGL STREQUAL "y") add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/v8) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/v9) +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/simulator) ######################################## # Layer Configure diff --git a/src/liblvgl/Kconfig b/src/liblvgl/Kconfig index 04969a68e..f60f26494 100755 --- a/src/liblvgl/Kconfig +++ b/src/liblvgl/Kconfig @@ -76,4 +76,7 @@ menuconfig ENABLE_LIBLVGL # Font configuration rsource "Fonts_Kconfig" + + # PC Simulator + rsource "simulator/Kconfig" endif \ No newline at end of file diff --git a/src/liblvgl/simulator/CMakeLists.txt b/src/liblvgl/simulator/CMakeLists.txt new file mode 100644 index 000000000..e13551c7b --- /dev/null +++ b/src/liblvgl/simulator/CMakeLists.txt @@ -0,0 +1,33 @@ +## +# @file CMakeLists.txt +# @brief LVGL PC simulator component +#/ + +if (CONFIG_LVGL_PC_SIMULATOR STREQUAL "y") + +set(MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}) +set(MODULE_NAME "lvgl_simulator") + +set(LIB_SRCS ${MODULE_PATH}/src/lvgl_sim.c) +set(LIB_PUBLIC_INC ${MODULE_PATH}/include) + +add_library(${MODULE_NAME}) + +target_sources(${MODULE_NAME} PRIVATE ${LIB_SRCS}) +target_include_directories(${MODULE_NAME} PUBLIC ${LIB_PUBLIC_INC}) + +target_compile_definitions(${MODULE_NAME} + PUBLIC + LVGL_PC_SIMULATOR + LVGL_SIM_ENTRY_FUNC=${CONFIG_SIM_ENTRY_FUNC} +) + +######################################## +# Layer Configure +######################################## +list(APPEND COMPONENT_LIBS ${MODULE_NAME}) +set(COMPONENT_LIBS "${COMPONENT_LIBS}" PARENT_SCOPE) +list(APPEND COMPONENT_PUBINC ${LIB_PUBLIC_INC}) +set(COMPONENT_PUBINC "${COMPONENT_PUBINC}" PARENT_SCOPE) + +endif() diff --git a/src/liblvgl/simulator/Kconfig b/src/liblvgl/simulator/Kconfig new file mode 100644 index 000000000..746534ed8 --- /dev/null +++ b/src/liblvgl/simulator/Kconfig @@ -0,0 +1,30 @@ +menuconfig LVGL_PC_SIMULATOR + bool "LVGL PC Simulator (SDL)" + depends on ENABLE_LIBLVGL && PLATFORM_LINUX + default n + help + Build as PC simulator with SDL2 window. + Only display/UI code will be compiled. + +if LVGL_PC_SIMULATOR + + config SIM_SCREEN_WIDTH + int "Screen width" + default 384 + + config SIM_SCREEN_HEIGHT + int "Screen height" + default 168 + + config SIM_WINDOW_TITLE + string "Window title" + default "TuyaOpen LVGL Simulator" + + config SIM_ENTRY_FUNC + string "UI entry function name" + default "ui_init" + help + The function called after LVGL init to create UI. + Must be provided by the app display code. + +endif diff --git a/src/liblvgl/simulator/include/lvgl_sim.h b/src/liblvgl/simulator/include/lvgl_sim.h new file mode 100644 index 000000000..d5ec77d48 --- /dev/null +++ b/src/liblvgl/simulator/include/lvgl_sim.h @@ -0,0 +1,34 @@ +/** + * @file lvgl_sim.h + * @brief LVGL PC simulator interface + * @version 1.0 + * @date 2025-04-30 + * @copyright Copyright (c) Tuya Inc. + */ +#ifndef __LVGL_SIM_H__ +#define __LVGL_SIM_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief UI entry callback type + */ +typedef void (*LVGL_SIM_ENTRY_CB)(void); + +/** + * @brief Start LVGL simulator with SDL2 + * + * Init LVGL, create SDL window/mouse/keyboard, call entry_cb, + * then run lv_timer_handler loop. Does not return. + * + * @param[in] entry_cb UI init function (e.g. ui_init) + * @return none + */ +void lvgl_sim_start(LVGL_SIM_ENTRY_CB entry_cb); + +#ifdef __cplusplus +} +#endif +#endif /* __LVGL_SIM_H__ */ diff --git a/src/liblvgl/simulator/src/lvgl_sim.c b/src/liblvgl/simulator/src/lvgl_sim.c index f670c3199..7cf97a928 100644 --- a/src/liblvgl/simulator/src/lvgl_sim.c +++ b/src/liblvgl/simulator/src/lvgl_sim.c @@ -56,14 +56,14 @@ void lvgl_sim_start(LVGL_SIM_ENTRY_CB entry_cb) } } -/* SIM_ENTRY_FUNC is injected via compile definition (e.g. screens_init) */ -extern void SIM_ENTRY_FUNC(void); +/* LVGL_SIM_ENTRY_FUNC is injected via compile definition (e.g. ui_init) */ +extern void LVGL_SIM_ENTRY_FUNC(void); /** - * @brief Simulator entry point, replaces the app's user_main - * @return none + * @brief Simulator entry point — provides main() for Linux platform */ -void user_main(void) +int main(void) { - lvgl_sim_start(SIM_ENTRY_FUNC); + lvgl_sim_start(LVGL_SIM_ENTRY_FUNC); + return 0; } diff --git a/src/liblvgl/v9/conf/lv_conf.h b/src/liblvgl/v9/conf/lv_conf.h index 36906b6e9..6de5182ad 100755 --- a/src/liblvgl/v9/conf/lv_conf.h +++ b/src/liblvgl/v9/conf/lv_conf.h @@ -100,7 +100,7 @@ * - LV_OS_WINDOWS * - LV_OS_CUSTOM */ #if defined(ENABLE_LVGL_OS_FREERTOS) && (ENABLE_LVGL_OS_FREERTOS == 1) -#define LV_USE_OS LV_OS_FREERTOS +// #define LV_USE_OS LV_OS_FREERTOS #else #define LV_USE_OS LV_OS_NONE #endif From 71a305e8ae2ec01fd7df61257ba36fe8a10443b0 Mon Sep 17 00:00:00 2001 From: maidang Date: Wed, 6 May 2026 19:29:29 +0800 Subject: [PATCH 7/7] feat(your_chat_bot): implement LVGL PC simulator support and display2 UI - Added configuration for LVGL PC simulator in CMake and Kconfig files. - Introduced new source files and directories for display2 UI functionality. - Enhanced CMakeLists to conditionally include source files based on simulator mode. - Updated display management to support both simulator and embedded modes. - Created a new configuration file for the simulator with relevant settings. --- apps/tuya.ai/your_chat_bot/CMakeLists.txt | 54 +++++++++++++++++-- apps/tuya.ai/your_chat_bot/Kconfig | 1 + .../config/TUYA_LINUX_LVGL_SIMULATOR.config | 10 ++++ .../your_chat_bot/src/display2/CMakeLists.txt | 17 +++--- .../your_chat_bot/src/display2/Kconfig | 5 ++ src/liblvgl/simulator/src/lvgl_sim.c | 18 +++---- 6 files changed, 86 insertions(+), 19 deletions(-) create mode 100644 apps/tuya.ai/your_chat_bot/config/TUYA_LINUX_LVGL_SIMULATOR.config diff --git a/apps/tuya.ai/your_chat_bot/CMakeLists.txt b/apps/tuya.ai/your_chat_bot/CMakeLists.txt index 1b3bc370b..a3a6b3658 100755 --- a/apps/tuya.ai/your_chat_bot/CMakeLists.txt +++ b/apps/tuya.ai/your_chat_bot/CMakeLists.txt @@ -1,6 +1,6 @@ ## # @file CMakeLists.txt -# @brief +# @brief #/ # APP_PATH @@ -9,15 +9,59 @@ set(APP_PATH ${CMAKE_CURRENT_LIST_DIR}) # APP_NAME get_filename_component(APP_NAME ${APP_PATH} NAME) -# APP_SRCS -aux_source_directory(${APP_PATH}/src APP_SRCS) +if (CONFIG_LVGL_PC_SIMULATOR STREQUAL "y") +######################################## +# Simulator mode: display only +######################################## +add_library(${EXAMPLE_LIB}) -set(APP_INC ${APP_PATH}/include) +target_compile_definitions(${EXAMPLE_LIB} + PRIVATE + LVGL_PC_SIMULATOR + ENABLE_CHAT_DISPLAY2=1 +) + +target_include_directories(${EXAMPLE_LIB} + PRIVATE + ${APP_PATH}/include + ${APP_PATH}/../ai_components/assets/include +) ######################################## # Generate language config header ######################################## +# set(LANG_SCRIPT "${APP_PATH}/../ai_components/assets/scripts/gen_lang.py") +# set(LANG_HEADER "${APP_PATH}/../ai_components/assets/include/lang_config.h") + +# if(CONFIG_ENABLE_AI_LANGUAGE_ENGLISH STREQUAL "y") +# set(LANG_JSON "${APP_PATH}/../ai_components/assets/language/en-US/language.json") +# else() +# set(LANG_JSON "${APP_PATH}/../ai_components/assets/language/zh-CN/language.json") +# endif() + +# execute_process( +# COMMAND ${CMAKE_COMMAND} -E env PYTHONIOENCODING=utf-8 PYTHONUNBUFFERED=1 +# ${PYTHON_CMD} ${LANG_SCRIPT} --input ${LANG_JSON} --output ${LANG_HEADER} +# RESULT_VARIABLE LANG_GEN_RESULT +# ) + +# if(NOT LANG_GEN_RESULT EQUAL 0) +# message(FATAL_ERROR "Failed to generate lang_config.h") +# endif() +add_subdirectory(${APP_PATH}/src/display2) + +add_subdirectory(${APP_PATH}/../ai_components) + +else() +######################################## +# Embedded mode: full build (original) +######################################## + +# APP_SRCS +aux_source_directory(${APP_PATH}/src APP_SRCS) + +set(APP_INC ${APP_PATH}/include) ######################################## # Target Configure @@ -43,3 +87,5 @@ if (CONFIG_ENABLE_BATTERY STREQUAL "y") endif() add_subdirectory(${APP_PATH}/../ai_components) + +endif() diff --git a/apps/tuya.ai/your_chat_bot/Kconfig b/apps/tuya.ai/your_chat_bot/Kconfig index 12a8fbeb0..e2e00e92b 100644 --- a/apps/tuya.ai/your_chat_bot/Kconfig +++ b/apps/tuya.ai/your_chat_bot/Kconfig @@ -9,5 +9,6 @@ config ENABLE_BATTERY default n rsource "../ai_components/Kconfig" +rsource "src/display2/Kconfig" endmenu \ No newline at end of file diff --git a/apps/tuya.ai/your_chat_bot/config/TUYA_LINUX_LVGL_SIMULATOR.config b/apps/tuya.ai/your_chat_bot/config/TUYA_LINUX_LVGL_SIMULATOR.config new file mode 100644 index 000000000..decb9b35f --- /dev/null +++ b/apps/tuya.ai/your_chat_bot/config/TUYA_LINUX_LVGL_SIMULATOR.config @@ -0,0 +1,10 @@ +CONFIG_PROJECT_VERSION="1.0.1" +CONFIG_BOARD_CHOICE_LINUX=y +CONFIG_BOARD_CHOICE_UBUNTU=y +CONFIG_ENABLE_LIBLVGL=y +CONFIG_ENABLE_CHAT_DISPLAY2=y +CONFIG_LV_FONT_MONTSERRAT_48=y +CONFIG_LVGL_PC_SIMULATOR=y +CONFIG_SIM_SCREEN_WIDTH=320 +CONFIG_SIM_SCREEN_HEIGHT=480 +CONFIG_ENABLE_GUI2_WECHAT=y \ No newline at end of file diff --git a/apps/tuya.ai/your_chat_bot/src/display2/CMakeLists.txt b/apps/tuya.ai/your_chat_bot/src/display2/CMakeLists.txt index f4f7a9690..7961b2243 100644 --- a/apps/tuya.ai/your_chat_bot/src/display2/CMakeLists.txt +++ b/apps/tuya.ai/your_chat_bot/src/display2/CMakeLists.txt @@ -9,19 +9,24 @@ set(APP_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) set(APP_MODULE_SRCS) list(APPEND APP_MODULE_SRCS ${APP_MODULE_PATH}/app_ui_helper.c + ) + +if (NOT CONFIG_LVGL_PC_SIMULATOR STREQUAL "y") + list(APPEND APP_MODULE_SRCS ${APP_MODULE_PATH}/app_display.c ${APP_MODULE_PATH}/tuya_lvgl.c ) +endif() -set(APP_MODULE_INC +set(APP_MODULE_INC ${APP_MODULE_PATH} ) # add subdirectory if (CONFIG_ENABLE_GUI2_CHATBOT STREQUAL "y") -add_subdirectory(${APP_MODULE_PATH}/ui_chatbot) + add_subdirectory(${APP_MODULE_PATH}/ui_chatbot) elseif (CONFIG_ENABLE_GUI2_WECHAT STREQUAL "y") -add_subdirectory(${APP_MODULE_PATH}/ui_wechat) + add_subdirectory(${APP_MODULE_PATH}/ui_wechat) endif() ######################################## @@ -30,10 +35,10 @@ endif() target_sources(${EXAMPLE_LIB} PRIVATE ${APP_MODULE_SRCS} - ) +) target_include_directories(${EXAMPLE_LIB} PRIVATE ${APP_MODULE_INC} - ) +) -endif() \ No newline at end of file +endif() diff --git a/apps/tuya.ai/your_chat_bot/src/display2/Kconfig b/apps/tuya.ai/your_chat_bot/src/display2/Kconfig index 19ef752a7..a4376a3a9 100644 --- a/apps/tuya.ai/your_chat_bot/src/display2/Kconfig +++ b/apps/tuya.ai/your_chat_bot/src/display2/Kconfig @@ -1,3 +1,8 @@ +config ENABLE_CHAT_DISPLAY2 + bool "Enable Chat Display2 UI" + depends on ENABLE_LIBLVGL + default n + if(ENABLE_CHAT_DISPLAY2) choice prompt "Select Display UI Style" diff --git a/src/liblvgl/simulator/src/lvgl_sim.c b/src/liblvgl/simulator/src/lvgl_sim.c index 7cf97a928..40f109966 100644 --- a/src/liblvgl/simulator/src/lvgl_sim.c +++ b/src/liblvgl/simulator/src/lvgl_sim.c @@ -12,16 +12,16 @@ #include "src/drivers/sdl/lv_sdl_keyboard.h" #include -#ifndef CONFIG_SIM_SCREEN_WIDTH -#define CONFIG_SIM_SCREEN_WIDTH 384 +#ifndef SIM_SCREEN_WIDTH +#define SIM_SCREEN_WIDTH 384 #endif -#ifndef CONFIG_SIM_SCREEN_HEIGHT -#define CONFIG_SIM_SCREEN_HEIGHT 168 +#ifndef SIM_SCREEN_HEIGHT +#define SIM_SCREEN_HEIGHT 168 #endif -#ifndef CONFIG_SIM_WINDOW_TITLE -#define CONFIG_SIM_WINDOW_TITLE "TuyaOpen LVGL Simulator" +#ifndef SIM_WINDOW_TITLE +#define SIM_WINDOW_TITLE "TuyaOpen LVGL Simulator" #endif /** @@ -33,9 +33,9 @@ void lvgl_sim_start(LVGL_SIM_ENTRY_CB entry_cb) { lv_init(); - lv_display_t *disp = lv_sdl_window_create(CONFIG_SIM_SCREEN_WIDTH, - CONFIG_SIM_SCREEN_HEIGHT); - lv_sdl_window_set_title(disp, CONFIG_SIM_WINDOW_TITLE); + lv_display_t *disp = lv_sdl_window_create(SIM_SCREEN_WIDTH, + SIM_SCREEN_HEIGHT); + lv_sdl_window_set_title(disp, SIM_WINDOW_TITLE); lv_sdl_mouse_create(); lv_indev_t *kb = lv_sdl_keyboard_create();