# hello_lvgl Minimal LVGL v9 + touch demo for the CUBE32 board. It shows how to start LVGL through the BSP, create widgets, handle touch events and rotate the screen with a swipe. ## Features - **LVGL start-up through the BSP**: `cube32_init()` brings up the display, LVGL and touch; the example only uses `cube32::LvglDisplay` - **Basic widgets**: title and subtitle labels, a styled button with a pressed-state style, a counter label, status labels - **Touch input**: tap the **Click Me!** button to increment the counter - **Swipe to rotate**: swipe right for the next rotation, swipe left for the previous one (0 → 90 → 180 → 270°); the UI is rebuilt for the new screen size - **Thread-safe LVGL access**: every widget update runs between `lvgl.lock()` and `lvgl.unlock()` - **Colour animation**: the counter label changes colour continuously from the main loop - **Footer**: shows the BSP and LVGL versions ## Hardware - CUBE32 board (ESP32-S3) with a supported touch display (ST7789 or ST7796S, auto-detected at boot) ## Configuration `sdkconfig.defaults` of the release example already has what the demo needs: | Setting | Why | |---------|-----| | `CONFIG_CUBE32_LVGL_ENABLED=y` | LVGL display driver | | `CONFIG_CUBE32_TOUCH_ENABLED=y` | Touch input (button and swipe) | | `CONFIG_LV_FONT_MONTSERRAT_14=y` | Font used by all labels | | `CONFIG_LV_FONT_MONTSERRAT_10/20/24=y` | Required by the BLE OTA UI whenever LVGL is on | In the BSP dev project select the demo with `idf.py menuconfig` → `CUBE32 Application Selection` → `Hello LVGL Example` (it is only listed when LVGL is enabled). ## Build and run ```bash idf.py set-target esp32s3 idf.py build idf.py -p <port> flash monitor ``` Run `idf.py set-target esp32s3` again after deleting `sdkconfig`; otherwise the target falls back to `esp32`. ## Usage 1. Flash the example; the screen shows **CUBE32 / LVGL + Touch Demo**. 2. Tap **Click Me!**: `Count` increases by one for every tap. 3. Swipe left or right across the screen: the screen rotates and `Rotation: <deg>` is updated. The counter value is kept. 4. If touch is not available, the example shows `Touch: N/A` instead of `Swipe to rotate`. ## Console output ``` I (xxxx) hello_lvgl: ======================================== I (xxxx) hello_lvgl: CUBE32 Hello LVGL Example I (xxxx) hello_lvgl: ======================================== I (xxxx) hello_lvgl: Touch input: Enabled I (xxxx) hello_lvgl: Swipe left/right to rotate screen! I (xxxx) hello_lvgl: Creating UI for 240x320 screen I (xxxx) hello_lvgl: UI created successfully I (xxxx) hello_lvgl: Button clicked! Count: 1 I (xxxx) hello_lvgl: Swipe right detected - rotating to next I (xxxx) hello_lvgl: Rotating screen to 90 degrees ``` The screen size in the log depends on the detected display. ## How it works | Part | Function | |------|----------| | `app_main()` | Calls `cube32_init()`, checks that LVGL is initialised, reads the current rotation, creates the UI and loops every 50 ms | | `create_ui()` | Builds the screen under the LVGL lock | | `btn_event_cb()` | Increments the counter and updates the label on `LV_EVENT_CLICKED` | | `screen_gesture_cb()` | Records the press and release points; a horizontal move of more than 50 px is a swipe | | `rotate_screen_next()` / `rotate_screen_prev()` | Call `LvglDisplay::setRotation()` and rebuild the UI with `recreate_ui()` | | `animate_counter()` | Changes the counter label colour from the main loop | ## Notes - LVGL runs its own rendering task, so the main loop only needs a short delay. - If LVGL is not enabled in the configuration, the example logs the menuconfig path to enable it and exits. - To build your own LVGL UI, start from this example and replace `create_ui()`.
To create a project from this example, run:
idf.py create-project-from-example "cube32esp/cube32_bsp=0.0.17:hello_lvgl"