Skip to content

Lesson04_CrowPanel_Touch_LVGL: Knob-Controlled LVGL Brightness Meter Interface

1. Course Introduction

This lesson extends the circular display and knob dimming covered in the previous two lessons into a complete LVGL brightness meter interface. ESPHome scales bj_light_21_1.png to 480 x 480 and converts it into an RGB565 background image; LVGL then draws the title, a 0%-100% arc, and the brightness percentage on top of that background. Each knob step adjusts the value by 5%, with the GPIO6 PWM backlight, arc position, and text changing in sync.

Through this lesson, learners will validate the complete chain of panel power, ST7701S RGB display, PSRAM image resources, LVGL widgets, knob input, and PWM backlight. Because this lesson's code does not configure a touch component, lesson acceptance is based on knob dimming and interface synchronization.

2. Learning Objectives

  • Be able to place bj_light_21_1.png in a location parseable by ESPHome and complete the image conversion.
  • Be able to explain the LVGL stacking relationship among the background image, arc, knob, and percentage label.
  • Be able to use the knob to synchronously control the PWM, arc, and brightness text.
  • Be able to troubleshoot missing resources, encoder anomalies, or PWM desynchronization issues based on the interface, logs, and backlight changes.

3. What You Need to Prepare

  • CrowPanel 2.1inch-HMI ESP32 Rotary Display with Lesson01 and Lesson02 already completed.
  • A USB data cable that supports data transfer.
  • ESPHome Device Builder 1.9.2.
  • ESPHome 2026.7.4.
  • Lesson materials bj_light_21_1.png / bj_light_21_2.png.
  • Network connection required to compile Google Fonts and download the materials.

Code download link: Elecrow Official Lesson_Code

Material download link:

CrowPanel-2.1inch-HMI-ESP32-Rotary-Display-480-480-IPS-Round-Touch-Knob-Screen/example/esphome/Lesson04/Material/2.1 at master · Elecrow-RD/CrowPanel-2.1inch-HMI-ESP32-Rotary-Display-480-480-IPS-Round-Touch-Knob-Screen

Adding Materials Guide:

Next, we will show you how to upload the materials you want so that they can be used on the ESPHome platform.

First, download the Samba Share tool. You need to use this tool so that we can conveniently upload the image materials we want to use.

First, install Samba.

img

image-20260812114749965

img

img

After the download completes, on this tool's page, configure your account. Here, configure the account name and password used by your Home Assistant.

image-20260812114033150

After the configuration is complete, remember to click Save in the bottom-right corner.

Then you can open a file-viewing interface in your computer's file system and enter \ + your Home Assistant IP address.

img

After entering, you will arrive at your current ESPHome file management interface.

img

First enter the config folder, then enter the esphome folder.

img

You can place the materials you need to use here. In this way, the material names referenced in your code must also match the names of the materials you placed. (same operation)

image-20260814172348348

This way, when your code is compiled, it will be able to find the materials you placed and display them on the screen.

4. Software Operation Steps

Open ESPHome Device Builder 1.9.2 and confirm that its ESPHome Core version displays as 2026.7.4. If the versions are inconsistent, complete the upgrade or switch environments first to avoid component syntax differences. Lesson 04 image 10

Create a new project, place 21-lvgl-interface.yaml into the ESPHome configuration directory, and open the corresponding configuration in the device list. Note, however, that device names on the same network must not be duplicated.

A. Click Create device to create a new project.

Lesson 04 image 11

Lesson 04 image 12

B. Select the esp32s3 main controller.

Lesson 04 image 13

C. Name the project. Complete the new project task.

image-20260814171347553

Copy the official code into the project (the code is provided above).

However, ensure that the project name in the code matches the name of the project you just created.

image-20260814171423541

Modify the WiFi.

Lesson 04 image 18

Click the three dots in the top-right corner, then click secret.

You can enter your own WiFi name and password. (Ensure that this WiFi is on the same local network as your Home Assistant system)

WiFi

WiFi

Click Install, select the USB installation method suitable for the current environment, choose the serial port corresponding to the CrowPanel 2.1inch-HMI ESP32 Rotary Display, and complete compilation and flashing. After flashing finishes, open the log window and confirm that the PCF8574, ST7701S display, image resource, Wi-Fi connection, and encoder initialize without repeated resets.

image-20260812114358191

WiFi

Wait for compilation.

WiFi

After compilation completes, click flash to upload.

WiFi

Select the connected serial port.

WiFi

Click to connect to the corresponding serial port (confirm the hardware is already connected).

WiFi

Wait for the download to complete.

WiFi

5. Hardware Operation Steps

With the device powered off, check that the screen, knob housing, and USB interface show no obvious damage, then use a USB cable that supports data transfer to connect the CrowPanel 2.1inch-HMI ESP32 Rotary Display to the computer.

2.1_ESPHome_03_Rotate_To_Adjust_Brightness_16

Wait for the LVGL page to be built and look straight at the center of the circular screen. The page should display the background image, the Brightness title, the brightness arc, and 50%. Rotate the knob clockwise and counterclockwise to confirm that the arc, percentage, and actual backlight change synchronously; the device should not freeze, restart, or stop refreshing for an extended period. This lesson's code does not configure touch functionality, so touch response is not treated as an acceptance item.

image-20260814171639992

6. Key Code Explanation

6.1 Boot Initialization and Knob Protection

on_boot:
  priority: 800
  then:
    - output.turn_on: lcd_power
    - output.turn_on: display_reset
    - delay: 100ms
    - output.turn_off: display_reset
    - delay: 100ms
    - delay: 500ms
    - light.turn_on:
        id: display_backlight
        brightness: 0.5
    - lambda: |-
        id(encoder_ready) = false;
        id(brightness_value) = 50;
        id(knob).publish_state(0);
    - light.turn_on:
        id: display_backlight
        brightness: 0.5
    - lambda: |-
        char buf[10];
        snprintf(buf, sizeof(buf), "%d%%", id(brightness_value));
        lv_label_set_text(id(lbl_brightness), buf);
    - delay: 200ms
    - lambda: |-
        id(encoder_ready) = true;

The startup sequence powers and resets the panel through the PCF8574, initializes the backlight and state to 50%, resets the encoder baseline, updates the percentage label, and only then enables encoder processing. The arc itself starts at 50 in its widget declaration, so the arc, label, and PWM begin in the same state.

6.2 480 x 480 Image Resource

image:
  - file: "bj_light_21_1.png"
    id: img_bg_light
    resize: 480x480
    type: RGB565

ESPHome reads bj_light_21_1.png at compile time, resizes it to the panel resolution, and converts it to RGB565. The file must be available relative to the YAML configuration. A missing file or mismatched name stops compilation before flashing.

6.3 Display Buffer and Refresh

display:
  - platform: st7701s
    id: my_display
    update_interval: 50ms
    dimensions:
      width: 480
      height: 480
    pclk_frequency: 18MHz
    pclk_inverted: true

lvgl:
  displays:
    - my_display
  buffer_size: 50%

The full display section preserves the 2.1-inch board's SPI control pins, RGB data map, porch timing, and ST7701S initialization sequence. LVGL uses half of a full-frame buffer, which relies on the 8 MB Octal PSRAM. The 50 ms display interval corresponds to a 20 Hz update schedule.

6.4 Background, Title, Arc, and Percentage

widgets:
  - image:
      align: CENTER
      id: img_bg
      src: img_bg_light
      width: 480
      height: 480
      antialias: true
  - label:
      align: CENTER
      id: lbl_title
      y: -50
      text_font: montserrat_25
      text: "Brightness"
      text_color: 0xFFFFFF
  - arc:
      id: arc_brightness
      align: CENTER
      width: 360
      height: 360
      start_angle: 135
      end_angle: 45
      min_value: 0
      max_value: 100
      value: 50
      arc_color: 0x1A1A1A
      arc_width: 12
      indicator:
        arc_color: 0x00D2FF
        arc_width: 12
      knob:
        bg_color: 0xFFFFFF
        width: 15
        height: 15
        radius: 7
  - label:
      align: CENTER
      id: lbl_brightness
      text_font: montserrat_50
      text: "50%"
      text_color: 0xFFFFFF

Widgets are drawn in definition order. The 480 x 480 background is placed first, then the title, the 360 x 360 arc, and the central percentage. The arc covers 270 degrees from 135 degrees to 45 degrees. arc_brightness and lbl_brightness are referenced by the encoder Lambda, so their IDs form part of the input-to-UI contract.

6.5 Encoder Input and Synchronized Outputs

static int acc = 0;
acc += delta;

if (abs(acc) >= 1) {
  id(brightness_value) += (acc > 0) ? -5 : 5;
  id(brightness_value) = clamp(id(brightness_value), 0, 100);

  float level = id(brightness_value) / 100.0f;
  id(bl_pwm).set_level(level);
  lv_arc_set_value(id(arc_brightness), id(brightness_value));

  char buf[10];
  snprintf(buf, sizeof(buf), "%d%%", id(brightness_value));
  lv_label_set_text(id(lbl_brightness), buf);
  lv_refr_now(NULL);
  acc = 0;
}

GPIO42 and GPIO4 provide the encoder phases. The callback accumulates movement until it reaches one effective step, changes brightness by 5%, clamps the value, writes GPIO6 PWM, moves the arc, updates the percentage label, and forces an LVGL refresh. All three outputs use the same integer, preventing visual and physical brightness from drifting apart.

7. Experimental Observations

After reset, the 480 x 480 background image, Brightness title, cyan arc, white knob, and 50% value should appear. Rotating the encoder must change the arc, percentage, and physical backlight together in 5% steps. The image should remain stable without missing resources, color corruption, repeated resets, or long refresh pauses. Lesson 04 does not configure touch, so touching the arc is not an acceptance item yet.

LVGL brightness interface

8. Function or Resource Creation Process

Place bj_light_21_1.png in the ESPHome configuration directory beside 21-lvgl-interface.yaml. ESPHome converts it to the compiled resource img_bg_light; LVGL then creates the runtime widget img_bg. These IDs serve different stages and should not be interchanged. For a custom background, use a 480 x 480 image, keep important content away from the circular edge, and re-run Validate to confirm memory use and readability.