Porting MicroPython’s LVGL Bindings to ESP32

I am not sure why nobody provides ready-made ESP32 firmware with MicroPython’s LVGL bindings, so I compiled one and recorded the steps.

Steps

All the steps below were done on a Linux system; other systems are similar, so I will not elaborate.

Switch the ESP-IDF version

The official LVGL bindings support up to v4.4; see Setting up ESP-IDF and the build environment.

The ESP-IDF changes quickly and MicroPython only supports certain versions. Currently MicroPython supports v4.0.2, v4.1.1, v4.2.2, v4.3.2 and v4.4, although other IDF v4 versions may also work.

If you have already installed the IDF, there is no need to download it again. Enter esp/esp-idf — it is a Git repository — and run the following command to switch to the version branch:

1
git switch release/v4.4

After switching branches, you need to re-initialize the Git submodules:

1
git submodule update --init --recursive

Once that is done, running git status will show some files with changes — these are usually leftovers from the previous version. Remove those changes.

Here is what I ran:

1
2
rm -rf $(git status -s | cut -b 4-255) # 删除未加入Git的变更
git restore components/esptool_py/esptool # 恢复Git管理的变更

Check git status again to make sure there are no changes.

Reinstall the IDF:

1
esp/esp-idf/install.sh

Build lv_micropython

First clone the repository:

1
git clone https://github.com/lvgl/lv_micropython.git

Enter the repository and activate the IDF environment:

1
source export.sh

Run the following commands to compile for the ESP32:

1
2
make -C mpy-cross
make -C ports/esp32 LV_CFLAGS="-DLV_COLOR_DEPTH=16" BOARD=GENERIC submodules deploy

When using IL9341 driver, the color depth need to be set to match ILI9341. This can be done from the command line.

  • LV_CFLAGS are used to override color depth, for ILI9341 compatibility.

    • LV_COLOR_DEPTH=16 is needed if you plan to use the ILI9341 driver.
  • BOARD - I use WROVER board with SPIRAM. You can choose other boards from ports/esp32/boards/ directory.

  • deploy - make command will create ESP32 port of Micropython, and will try to deploy it through USB-UART bridge.

The compilation will report errors because some Git submodules are missing; initialize them:

1
2
git submodule update --init --recursive lib/lv_bindings
git submodule update --init --recursive lib/berkeley-db-1.xx

If it still reports another one missing, initialize that one as well. In my case these two were the ones that errored.

Compile again — this time it fails with a static assertion error in lv_micropython/ports/esp32/network_common.c:

1
2
3
4
5
6
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 3, 0)
#define TEST_WIFI_AUTH_MAX 9
#else
#define TEST_WIFI_AUTH_MAX 8
#endif
_Static_assert(WIFI_AUTH_MAX == TEST_WIFI_AUTH_MAX, "Synchronize WIFI_AUTH_XXX constants with the ESP-IDF. Look at esp-idf/components/esp_wifi/include/esp_wifi_types.h");

Looking at esp-idf/components/esp_wifi/include/esp_wifi_types.h, it turns out to be an enum:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
/* Strength of authmodes */
/* OPEN < WEP < WPA_PSK < WPA2_PSK = WPA_WPA2_PSK < WAPI_PSK < WPA3_PSK = WPA2_WPA3_PSK */
typedef enum {
    WIFI_AUTH_OPEN = 0,         /**< authenticate mode : open */
    WIFI_AUTH_WEP,              /**< authenticate mode : WEP */
    WIFI_AUTH_WPA_PSK,          /**< authenticate mode : WPA_PSK */
    WIFI_AUTH_WPA2_PSK,         /**< authenticate mode : WPA2_PSK */
    WIFI_AUTH_WPA_WPA2_PSK,     /**< authenticate mode : WPA_WPA2_PSK */
    WIFI_AUTH_ENTERPRISE,       /**< authenticate mode : WiFi EAP security */
    WIFI_AUTH_WPA2_ENTERPRISE = WIFI_AUTH_ENTERPRISE,  /**< authenticate mode : WiFi EAP security */
    WIFI_AUTH_WPA3_PSK,         /**< authenticate mode : WPA3_PSK */
    WIFI_AUTH_WPA2_WPA3_PSK,    /**< authenticate mode : WPA2_WPA3_PSK */
    WIFI_AUTH_WAPI_PSK,         /**< authenticate mode : WAPI_PSK */
    WIFI_AUTH_WPA3_ENT_192,     /**< authenticate mode : WPA3_ENT_SUITE_B_192_BIT */
    WIFI_AUTH_MAX
} wifi_auth_mode_t;

I counted and WIFI_AUTH_MAX equals 10, so I changed TEST_WIFI_AUTH_MAX to 10 everywhere, and it compiles.

Now the build succeeds, and it auto-flashes after compiling.

Customize the ESP32 board

The default board has only 4MB of flash, but the board I have on hand uses 16MB, so I needed a custom board configuration.

For the specific steps, refer to Defining a custom ESP32 board.

Straight to the commands — copy a board configuration template as GENERIC_16M:

1
cp boards/GENERIC boards/GENERIC_16M -r

Copy an sdkconfig.board from the SIL_WESP32 board into the GENERIC_16M folder.

Modify its contents as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# 16 MB flash

CONFIG_ESPTOOLPY_FLASHSIZE_4MB=
CONFIG_ESPTOOLPY_FLASHSIZE_8MB=
CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
CONFIG_ESPTOOLPY_FLASHSIZE="16MB"

# Fast flash

CONFIG_FLASHMODE_QIO=y
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
CONFIG_ESP32_REV_MIN_1=y

# OTA

CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-16MiB-ota.csv"

Edit mpconfigboard.cmake, modifying its contents as follows:

1
2
3
4
5
set(SDKCONFIG_DEFAULTS
    boards/sdkconfig.base
    boards/sdkconfig.ble
    boards/GENERIC_16M/sdkconfig.board
)

Recompile:

1
make -C ports/esp32 LV_CFLAGS="-DLV_COLOR_DEPTH=16" BOARD=GENERIC submodules deploy

Firmware download

Firmware download

References