ESP32 development learning LVGL Littlevgl using file system

LVGL has a "file system" abstraction module that enables you to attach any type of file system. The file system is identified by a drive letter. For example, if the SD card is associated with the letter "S", you can access a file similar to "S:path/to/file.txt".

The "file system" abstraction module is officially provided for the upper GUI to read files, pictures, fonts and other operations. You can use the file system API of LVGL like FatFS. When LVGL uses the resources of external storage devices, it will be called automatically. For example, the functions listed below are LVGL functions. After docking the file system, these functions will automatically read the files in the file system. As long as their corresponding decoding library is added, the picture can be displayed

lv_img_set_src(img, "P:lv_lib_lodepng/png_decoder_test.png");
lv_img_set_src(img2,  "S.\\lv_lib_split_jpg\\example\\images\\small_image.sjpg");
lv_img_set_src(img, "S/path/to/image.bmp");
lv_obj_t * img = lv_gif_create_from_file(parent, "S/path/to/example.gif");

Development environment of this paper:

Visual Studio Code V1.58.2

LVGL version v7 ten

Chip platform: ESP32

IDF library version: 4.3.0

IDF TOOLS compilation tool chain version: 2.9

The software in this paper is based on the ESP32 project LV officially provided by LVGL_ port_ Modified from ESP32

LVGL ESP32 official address:

The development board platform is based on HelloBug ESP32 development board

Development board purchase link:

1, lvgl file system interface migration

There is no interface code in the official ESP32 project of LVGL, but it can be downloaded from the official git.


After downloading, directly copy it to the components folder in the project. The directory structure is as follows

It's not enough to copy the interface file. To take it as a module of LVGL, you have to write and add a cmakelists Txt on LV_ fs_ Under the if folder, the content and directory structure are as follows:

Include all under this folder c file. In addition, it refers to the file system in the idf library and the underlying SD card driver_ fs_ If is registered to the idf component, and finally two components lvgl and my are added_ sd_ FatFs component and lvgl are known to be the core components of lvgl, my_sd_fatfs is written by itself according to its name. It is used to initialize the underlying SD card and mount the underlying file system. How to write this will be discussed later.

2, Modify configuration

Open LV in the LVGL directory in the project directory_ conf.h. \components\lvgl\lv_conf.h, in H file, add the following configuration to enable the file system of LVGL

// File system interface
#define LV_USE_FS_IF	1
#define LV_FS_IF_FATFS    'S'
#define LV_FS_IF_PC       '\0'
#define LV_FS_IF_POSIX    '\0'

III. initialize the underlying SD card driver

To connect to the lvgl file system, you must first initialize the underlying memory and mount the file system.

Create a folder in the project directory components_ sd_ FATFS, which you can name casually, means to initialize the source directory of the underlying FATFS, then create the include folder, and then create cmakelists txt,,my_sd_fatfs.c,my_sd_fatfs.h. The folder structure is as follows:

 CMakeLists.txt file is mainly used to add files and other components for this component. Its contents are as follows:

set(mysrcs "my_sd_fatfs.c")

idf_component_register(SRCS "${mysrcs}"
                       INCLUDE_DIRS "include"
					   REQUIRES fatfs) file content


my_sd_fatfs.h) document content

#ifndef MY_SD_FATFS_H
#define MY_SD_FATFS_H

#ifdef __cplusplus
extern "C" {

#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include "esp_vfs_fat.h"
#include "driver/sdspi_host.h"
#include "driver/spi_common.h"
#include "sdmmc_cmd.h"
#include "driver/sdmmc_host.h"

void SD_Fatfs_Init(void);

#endif /* LVGL_HELPERS_H */

my_sd_fatfs.c. document content:

#include "my_sd_fatfs.h"

static const char *TAG = "TFCardFat";

#define MOUNT_POINT 		 "/sdcard" 		//  The first byte must be '/'
#define SPI_DMA_CHAN    2

//When testing SD and SPI modes, remember that once the card is initialized in SPI mode, it cannot be reinitialized in SD mode without turning on the card power.
#define PIN_NUM_MISO 19
#define PIN_NUM_MOSI 23
#define PIN_NUM_CLK  18
#define PIN_NUM_CS   5

static void get_fatfs_usage(size_t* out_total_bytes, size_t* out_free_bytes);

void SD_Fatfs_Init(void)
	esp_err_t ret;								// ESP error definition
	sdmmc_card_t* card;							// SD / MMC card information structure
	const char mount_point[] = MOUNT_POINT;		// root directory
	char ReadFileBuff[64];

	esp_vfs_fat_sdmmc_mount_config_t mount_config = {	// File system mount configuration
		.format_if_mount_failed = false,				// If the mount fails: true will repartition and format / false will not repartition and format
		.max_files = 5,									// Maximum number of open files
		.allocation_unit_size = 16 * 1024

	ESP_LOGI(TAG,"->Initializing SD card");
	ESP_LOGI(TAG,"->Using SPI peripheralr");
	sdmmc_host_t host = SDSPI_HOST_DEFAULT();
	spi_bus_config_t bus_cfg = {
		.mosi_io_num = PIN_NUM_MOSI,
		.miso_io_num = PIN_NUM_MISO,
		.sclk_io_num = PIN_NUM_CLK,
		.quadwp_io_num = -1,
		.quadhd_io_num = -1,
		.max_transfer_sz = 4 * 1024 * sizeof(uint8_t),
	host.slot = 2;
	// SPI bus initialization
	ret = spi_bus_initialize(host.slot, &bus_cfg, SPI_DMA_CHAN);
	if (ret != ESP_OK) {
		printf("%s->Failed to initialize bus.\r\n",TAG);
	// This initializes slots without card detection (CD) and write protect (WP) signals.
	// If your motherboard has these signals, please modify the slot_config.gpio_cd and slot_config.gpio_wp. 
	sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();// SPI2_HOST  1
	slot_config.gpio_cs = PIN_NUM_CS;
	slot_config.host_id = host.slot;
	// Mount file system
	ret = esp_vfs_fat_sdspi_mount(mount_point, &host, &slot_config, &mount_config, &card);
	if (ret != ESP_OK) {
		if (ret == ESP_FAIL) {
			printf("%s->Failed to mount filesystem.%s\r\n",TAG,
				"If you want the card to be formatted, set the EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.");
		} else {
			printf("%s->Failed to initialize the card %s  (%s). ",TAG,
				"Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret));
	// TF card has been initialized. Print TF Card properties
	sdmmc_card_print_info(stdout, card);

	// Print FAT FS size information
	size_t bytes_total, bytes_free;
	get_fatfs_usage(&bytes_total, &bytes_free);
	printf("%s->FAT FS Total: %d MB, Free: %d MB \r\n",TAG, bytes_total / 1024, bytes_free / 1024);

At this time, POSIX and C standard library functions can be used to perform various operations on SD card files. The next step is to connect C standard functions with LVGL file system functions.

4, Docking file operation function

The file operation function of LVGL is in components\lvgl\lvgl\src\lv_misc\lv_fs.h file has definitions. Here, the functions to be docked are listed as follows: (do not modify this file!!)

lv_fs_res_t lv_fs_open(lv_fs_file_t * file_p, const char * path, lv_fs_mode_t mode);
lv_fs_res_t lv_fs_close(lv_fs_file_t * file_p);
lv_fs_res_t lv_fs_remove(const char * path);
lv_fs_res_t lv_fs_read(lv_fs_file_t * file_p, void * buf, uint32_t btr, uint32_t * br);
lv_fs_res_t lv_fs_write(lv_fs_file_t * file_p, const void * buf, uint32_t btw, uint32_t * bw);
lv_fs_res_t lv_fs_seek(lv_fs_file_t * file_p, uint32_t pos);
lv_fs_res_t lv_fs_tell(lv_fs_file_t * file_p, uint32_t * pos);
lv_fs_res_t lv_fs_trunc(lv_fs_file_t * file_p);
lv_fs_res_t lv_fs_size(lv_fs_file_t * file_p, uint32_t * size);
lv_fs_res_t lv_fs_rename(const char * oldname, const char * newname);
lv_fs_res_t lv_fs_dir_open(lv_fs_dir_t * rddir_p, const char * path);
lv_fs_res_t lv_fs_dir_read(lv_fs_dir_t * rddir_p, char * fn);
lv_fs_res_t lv_fs_dir_close(lv_fs_dir_t * rddir_p);
lv_fs_res_t lv_fs_free_space(char letter, uint32_t * total_p, uint32_t * free_p);
char * lv_fs_get_letters(char * buf);
const char * lv_fs_get_ext(const char * fn);
char * lv_fs_up(char * path);
const char * lv_fs_get_last(const char * path);

In fact, it is not necessary to realize the docking of all functions, and the most commonly used functions can be realized. I just look at how a LV file operation function is called to the bottom layer step by step. For example, components \ lvgl \ lvgl \ SRC \ LV_ misc\lv_ fs. LV in C_ fs_ The prototype of the open function is as follows: (do not modify this file)

lv_fs_res_t lv_fs_open(lv_fs_file_t * file_p, const char * path, lv_fs_mode_t mode)
    file_p->drv    = NULL;
    file_p->file_d = NULL;
    if(path == NULL) return LV_FS_RES_INV_PARAM;
    char letter = path[0];
    file_p->drv = lv_fs_get_drv(letter);
    if(file_p->drv == NULL) {
        return LV_FS_RES_NOT_EX;
    if(file_p->drv->ready_cb != NULL) {
        if(file_p->drv->ready_cb(file_p->drv) == false) {
            file_p->drv = NULL;
            return LV_FS_RES_HW_ERR;
    if(file_p->drv->open_cb == NULL) {
        file_p->drv = NULL;
        return LV_FS_RES_NOT_IMP;
    const char * real_path = lv_fs_get_real_path(path);
    if(file_p->drv->file_size == 0) {  /*Is file_d zero size?*/
        /*Pass file_d's address to open_cb, so the implementor can allocate memory byself*/
        return file_p->drv->open_cb(file_p->drv, &file_p->file_d, real_path, mode);
    file_p->file_d = lv_mem_alloc(file_p->drv->file_size);
    if(file_p->file_d == NULL) {
        file_p->drv = NULL;
        return LV_FS_RES_OUT_OF_MEM; /* Out of memory */
    lv_fs_res_t res = file_p->drv->open_cb(file_p->drv, file_p->file_d, real_path, mode);
    if(res != LV_FS_RES_OK) {
        file_p->file_d = NULL;
        file_p->drv    = NULL;
    return res;

The function is preceded by various checks and the necessary memory to apply for opening files. It mainly depends on this sentence. The parameters are device operation pointer, file pointer, real directory and operation mode.

open_cb(file_p->drv, &file_p->file_d, real_path, mode);

The function pointer of this function points to components \ lv_fs_if\lv_ fs_ fatfs. FS in C_ Open function. lv_ fs_ The if component is the file system component that we downloaded from LVGL in the first step_ LV configured in #define. H_ USE_ FS_ If # 1 switch to point here.

To achieve docking, you need to change components\lv_fs_if\lv_fs_fatfs.c each file operation function in this file. LV in this file_ fs_ if_ fatfs_ The functions to be implemented are listed in the init function

    fs_drv.open_cb = fs_open;
    fs_drv.close_cb = fs_close;
    fs_drv.read_cb = fs_read;
    fs_drv.write_cb = fs_write;
    fs_drv.seek_cb = fs_seek;
    fs_drv.tell_cb = fs_tell;
    fs_drv.free_space_cb = fs_free;
    fs_drv.size_cb = fs_size;
    fs_drv.remove_cb = fs_remove;
    fs_drv.rename_cb = fs_rename;
    fs_drv.trunc_cb = fs_trunc;

    fs_drv.rddir_size = sizeof(dir_t);
    fs_drv.dir_close_cb = fs_dir_close;
    fs_drv.dir_open_cb = fs_dir_open;
    fs_drv.dir_read_cb = fs_dir_read;

This c file has written out all the functions to be implemented, but they all return directly

lv_fs_res_t res = LV_FS_RES_NOT_IMP;

The first step is to modify the initialization function. Here, we call the function of initializing the underlying SD card driver in the third step to initialize the underlying file system.

/* Initialize your Storage device and File system. */
static void fs_init(void)
	/* Initialize the SD card and FatFS itself.
	* Better to do it in your code to keep this library utouched for easy updating*/
	// Initialize the SD card and FatFS itself.
	// It's best to do this in your code to keep the library unchanged for easy updating

Next, let's modify all the file operation functions.


static lv_fs_res_t fs_open(lv_fs_drv_t * drv, void * file_p, const char * path, lv_fs_mode_t mode)
	// Open file
	const char * flags = "";
	if(mode == LV_FS_MODE_WR) flags = "wb";
	else if(mode == LV_FS_MODE_RD) flags = "rb";
	else if(mode == (LV_FS_MODE_WR | LV_FS_MODE_RD)) flags = "rb+";
	sprintf(f_path, "/sdcard/%s", path);
	file_t f = fopen(f_path, flags);
	if (NULL == f) {
		return LV_FS_RES_NOT_EX;
	fseek(f, 0, SEEK_SET);

	/* 'file_p' Is a pointer to the file descriptor. We need to store our file descriptor here*/
	file_t * fp = file_p;
	*fp = f;
	return LV_FS_RES_OK;


static lv_fs_res_t fs_close (lv_fs_drv_t * drv, void * file_p)
	// Close file
	(void) drv;		/*Unused*/
	(void) drv;		/*Unused*/
	file_t * fp = file_p;	// Fetch file operation handle
	if (NULL == fp) {
		ESP_LOGE(TAG, "NULL file pointer");
		return LV_FS_RES_NOT_EX;
	if (EOF == fclose(fp)) {
		ESP_LOGE(TAG, "Failed close file pointer");
		return LV_FS_RES_NOT_EX;
	return LV_FS_RES_OK;


static lv_fs_res_t fs_read (lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br)
	// read file
	(void) drv;		/*Unused*/
	file_t * fp = file_p;// Fetch file operation handle
	*br = fread(buf, 1, btr, (FILE *)file_p);
	return LV_FS_RES_OK;


static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw)
	// Write data to operation file
	(void) drv;		/*Unused*/
	file_t * fp = file_p;// Fetch file operation handle
	*bw = fwrite(buf, 1, btw, (FILE *)file_p);
	return LV_FS_RES_OK;


static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos, int whence)
	// Set file operation pointer offset
	(void) drv;		/*Unused*/
	fseek((FILE *)file_p, pos, SEEK_SET);
	return LV_FS_RES_OK;


static lv_fs_res_t fs_size(lv_fs_drv_t * drv, void * file_p, uint32_t * size_p)
	// Get current file size (Byte)
	(void) drv;		/*Unused*/
	file_t * fp = file_p;// Fetch file operation handle
	uint32_t cur = ftell((FILE *)file_p);
	fseek((FILE *)file_p, 0L, SEEK_END);
	*size_p = ftell((FILE *)file_p);
	fseek((FILE *)file_p, cur, SEEK_SET);// Recover file pointer
	return LV_FS_RES_OK;


static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p)
	// Gets the current operation pointer position of the file
	(void) drv;		/*Unused*/
	*pos_p = ftell((FILE *)file_p);
	return LV_FS_RES_OK;


static lv_fs_res_t fs_remove(lv_fs_drv_t * drv, const char *path)
	char * pathbuf = lv_mem_alloc(128);
	lv_fs_res_t res = LV_FS_RES_UNKNOWN;
		return LV_FS_RES_OK;
		return res;


static lv_fs_res_t fs_rename (lv_fs_drv_t * drv, const char * oldname, const char * newname)
	(void) drv;		/*Unused*/
	char * oldnamebuf = lv_mem_alloc(128);
	char * newnamebuf = lv_mem_alloc(128);
	lv_fs_res_t res = LV_FS_RES_UNKNOWN;
		return LV_FS_RES_OK;
		return res;


static lv_fs_res_t fs_free(lv_fs_drv_t * drv, uint32_t * total_p, uint32_t * free_p)
	lv_fs_res_t res = LV_FS_RES_NOT_IMP;

	size_t total_sectors = 0,free_sectors = 0;
	size_t sd_total = 0,sd_total_KB = 0,sd_total_MB = 0;
	size_t sd_free = 0,sd_free_KB = 0,sd_free_MB = 0;

	FATFS *fs;
	size_t free_clusters;
	FRESULT fres = f_getfree("0:", &free_clusters, &fs);
	if(fres == FR_OK){
		total_sectors = (fs->n_fatent - 2) * fs->csize;
		free_sectors = free_clusters * fs->csize;

		sd_total = total_sectors/1024;
		sd_total_KB = sd_total*fs->ssize;
		sd_total_MB = (sd_total*fs->ssize)/1024;
		sd_free = free_sectors/1024;
		sd_free_KB = sd_free*fs->ssize;
		sd_free_MB = (sd_free*fs->ssize)/1024;
		if (total_p != NULL) {
			*total_p = sd_total_KB;
		if (free_p != NULL) {
			*free_p = sd_free_KB;
		return LV_FS_RES_OK;
		*total_p = sd_total_KB;
		*free_p = sd_free_KB;
	//printf("SD Cart sd_total_KB %d KByte\r\n ", sd_total_KB);	
	// printf("SD Cart sd_total_MB %d MByte\r\n ", sd_total_MB);	
	// printf("SD Cart sd_free_KB %d KByte\r\n ", sd_free_KB);	
	// printf("SD Cart sd_free_MB %d MByte\r\n ", sd_free_MB);	
	// Assuming that the total size is less than 4GiB, flash should be true for SPI


static lv_fs_res_t fs_dir_open (lv_fs_drv_t * drv, void * dir_p, const char *path)

	dir_t file_dir = NULL;
 	//file_dir = opendir("/sdcard/");

	(void) drv;		/*Unused*/
	char * pathbuf = lv_mem_alloc(128);
	printf("fs_dir_open   %s\r\n",pathbuf);
	file_dir = opendir(pathbuf);
	if (NULL == file_dir) {
		return LV_FS_RES_FS_ERR;
		/* 'file_p' Is a pointer to the file descriptor. We need to store our file descriptor here*/
	dir_t * fd = dir_p;
	*fd = file_dir;
	return LV_FS_RES_OK;


static lv_fs_res_t fs_dir_read (lv_fs_drv_t * drv, void * dir_p, char *fn)
	struct dirent *entry;
	dir_t *mdir_p = dir_p;
	do {
		entry = readdir(*mdir_p);
		if(entry) {
			if(entry->d_type == DT_DIR) sprintf(fn, "/%s", entry->d_name);
			else strcpy(fn, entry->d_name);
		} else {
			strcpy(fn, "");
	} while(strcmp(fn, "/.") == 0 || strcmp(fn, "/..") == 0);
	return LV_FS_RES_OK;


static lv_fs_res_t fs_dir_close (lv_fs_drv_t * drv, void * dir_p)
	(void) drv;		/*Unused*/
	return LV_FS_RES_OK;

5, Test code

The next step is to use the functions prompted by lvgl to actually operate the file and test it. Before that, you should first initialize the lvgl file system in the main function_ After the init function, the lv_ is called. fs_ if_ init(); Initialize the file system and define some things to be used later, such as file operation handle, folder operation handle, return value, quantity variable, read content array, etc

	lv_init();										// Initialize LittlevGL
	lvgl_driver_init();								// Initializing the LCD touch chip SPI/IIC driver


	lv_fs_file_t f;
	lv_fs_dir_t d;
	lv_fs_res_t res;
	uint32_t resCount;
	uint8_t buf[64];

Write operation function

The specific operation instructions are as follows:

Open s: / Hello. In read-only mode Txt file to test whether the file exists
Open s: / Hello. By writing Txt file, write hellobug esp32 string, and then close the file
Open s: / Hello. In read-only mode Txt file, read the contents and print, and then close the file
Delete s: / bug Txt (must not exist when running for the first time)
Put hello Txt to bug txt

Open bug in read-only mode Txt, get the file size
Read bug Txt all contents and print
Move the operation file pointer to the 7th byte
Gets the current pointer position of the file
Read the contents of the file from the current position of the pointer and print it
Close file

Open root directory
Cycle to read all directories and files under the directory
Close directory

Then add the following test code. Basically, all the interfaces just rewritten are used.

	// Open a file in read-only mode to judge whether the file exists
	res = lv_fs_open(&f,"S:/hello.txt", LV_FS_MODE_RD);
	if (res == LV_FS_RES_OK) {	// File exists
		lv_fs_close(&f);		// Close file
		ESP_LOGI(TAG,"'hello.txt' file is existsres : %d",res);
	}else if(res == LV_FS_RES_NOT_EX){// file does not exist
		ESP_LOGI(TAG,"'hello.txt' file is not exists res : %d",res);

	/Create a file and write its contents
	res = lv_fs_open(&f,"S:/hello.txt", LV_FS_MODE_WR);// Create a file
	if (res == LV_FS_RES_OK) {
		ESP_LOGI(TAG,"Create file 'hello.txt' Success res : %d",res);
		res = lv_fs_write(&f,"hellobug esp32", sizeof("hellobug esp32"), &resCount);// Create a file
		if (res == LV_FS_RES_OK) {
			ESP_LOGI(TAG,"Write file 'hello.txt' Success res : %d WriteByte: %d",res,resCount);

	/Read the file just created and print the contents
	res = lv_fs_open(&f,"S:/hello.txt", LV_FS_MODE_RD);// Open a file read-only
	if(res == LV_FS_RES_OK) {
		ESP_LOGI(TAG,"lv_fs_open->Success %d",res);
		res = lv_fs_read(&f, buf, 64, &resCount);
		if(res != LV_FS_RES_OK) {
			ESP_LOGI(TAG,"lv_fs_read->Failed %d",res);
			ESP_LOGI(TAG,"lv_fs_read->Success %d",res);
		ESP_LOGI(TAG,"lv_fs_read ReadByte : %d Content : %s",resCount,buf);
		ESP_LOGI(TAG,"lv_fs_open->Failed %d",res);

	// Delete file
	res = lv_fs_remove("S:/bug.txt");
	ESP_LOGI(TAG,"lv_fs_remove-> bug.txt   res:%d",res);

	// rename file
	res = lv_fs_rename("S:/hello.txt","S:/bug.txt");
	ESP_LOGI(TAG,"lv_fs_rename-> oldname:hello.txt     newname:bug.txt   res:%d",res);

	/Read the file just renamed and print the content
	res = lv_fs_open(&f,"S:/bug.txt", LV_FS_MODE_RD);// Open a file read-only
	if(res == LV_FS_RES_OK) {
		// Get file size
		res = lv_fs_size(&f,&resCount);
		ESP_LOGI(TAG,"lv_fs_size-> bug.txt   file size : %d Byte",resCount);

		ESP_LOGI(TAG,"lv_fs_open-> S:/bug.txt Success %d",res);
		res = lv_fs_read(&f, buf, 64, &resCount);
		if(res != LV_FS_RES_OK) {
			ESP_LOGI(TAG,"lv_fs_read-> S:/bug.txt Failed %d",res);
			ESP_LOGI(TAG,"lv_fs_read-> S:/bug.txt Success %d",res);
		ESP_LOGI(TAG,"lv_fs_read S:/bug.txt ReadByte : %d Content : %s",resCount,buf);

		// Example of locating a file operation pointer
		res = lv_fs_seek(&f, 7);
		ESP_LOGI(TAG,"lv_fs_seek-> S:/bug.txt res %d",res);
		// Get file operation pointer example
		res = lv_fs_tell(&f, &resCount);
		ESP_LOGI(TAG,"lv_fs_tell-> S:/bug.txt res %d   index: %d",res,resCount);

		res = lv_fs_read(&f, buf, 64, &resCount);
		ESP_LOGI(TAG,"lv_fs_read seek 7Byte S:/bug.txt ReadByte : %d Content : %s",resCount,buf);

		ESP_LOGI(TAG,"lv_fs_open-> S:/bug.txt Failed %d",res);

	res = lv_fs_dir_open(&d,"S:/");
	ESP_LOGI(TAG,"lv_fs_dir_open-> res %d",res);

		res = lv_fs_dir_read(&d,(char *)buf);
		if(buf[0] == '\0'){
		ESP_LOGI(TAG,"lv_fs_dir_read   res : %d Content : %s",res,buf);
	res = lv_fs_dir_close(&d);
	ESP_LOGI(TAG,"lv_fs_dir_close   res : %d ",res);

Test result printing:


Keywords: ESP32 LVGL

Added by etsauer on Mon, 10 Jan 2022 22:10:35 +0200