Loading png image from SD Card on Waveshare ESP32 7 Inch display

Description

How to load png image from SD Card?

What MCU/Processor/Board and compiler are you using?

ESP32 S3 - Waveshare 7 Inch display

What LVGL version are you using?

8.3.11

What do you want to achieve?

Load images from SD Card to recude RAM/Flash Usage.

What have you tried so far?

I tried many many ways… At this moment I mounted correctly SD card on start application. In button press event function i have function to read png files.

Code to reproduce

Add a code snippet which can run in the simulator. It should contain only the relevant code that compiles without errors when separated from your main code base.

The code block(s) should be formatted like:

#include "ui.h"
#include "esp_heap_caps.h"
#include "esp_log.h"

#include "esp_vfs_fat.h"
#include "sdmmc_cmd.h"
#include "sd_test_io.h"
#include "dirent.h"

int counter = 1;
#define MOUNT_POINT "/sdcard"
static const char *SDTAG = "SDLOG";\

void list_files_on_sd_card2() {
    DIR *dir = opendir(MOUNT_POINT); // open main folder on SD Card
    if (dir == NULL) {
        ESP_LOGE(SDTAG, "Failed to open directory");
        return;
    }

    struct dirent *entry;
    while ((entry = readdir(dir)) != NULL) {
        ESP_LOGI(SDTAG, "Found file: %s", entry->d_name);  // Write full file path
    }

    closedir(dir);  // close folder
}

static esp_err_t read_binary_file(const char *path) {
    ESP_LOGI(SDTAG, "Reading binary file: %s", path);
    
    size_t free_psram = heap_caps_get_free_size(MALLOC_CAP_SPIRAM);
	ESP_LOGI("PSRAM", "1 PSRAM Free Heap: %d, w MB: %.2f", free_psram, (float)free_psram / 1048576);
    
    FILE *f = fopen(path, "rb");
    if (f == NULL) {
        ESP_LOGE(SDTAG, "Failed to open file for reading");
        return ESP_FAIL;
    }
    
     // check png file size
    fseek(f, 0, SEEK_END);  // Przesuń wskaźnik na koniec pliku
    long file_size = ftell(f);  // Zapisz rozmiar pliku
    fseek(f, 0, SEEK_SET);  // Przesuń wskaźnik z powrotem na początek pliku

    ESP_LOGI(SDTAG, "File size: %ld bytes", file_size);

    uint8_t *buffer = (uint8_t *)malloc(file_size);
    if (buffer == NULL) {
        ESP_LOGE(SDTAG, "Failed to allocate memory for image");
        fclose(f);
        return ESP_ERR_NO_MEM;
    }
    
    free_psram = heap_caps_get_free_size(MALLOC_CAP_SPIRAM);
	ESP_LOGI("PSRAM", "2 PSRAM Free Heap: %d, w MB: %.2f", free_psram, (float)free_psram / 1048576);
    
    /*size_t bytes_read;
    while ((bytes_read = fread(buffer, 1, sizeof(buffer), f)) > 0) {
        ESP_LOGI(SDTAG, "Read %d bytes", bytes_read);
        // Przetwórz dane w buforze
    }*/
    
    
    // read png file to buffer
    size_t bytes_read = fread(buffer, 1, file_size, f);
    if (bytes_read != file_size) {
        ESP_LOGE(SDTAG, "Failed to read entire file");
        free(buffer);
        fclose(f);
        return ESP_FAIL;
    }
    


    fclose(f);
    
    // image struct of lvgl
    lv_img_dsc_t img_dsc = {0};
    img_dsc.data = buffer;
    img_dsc.header.cf = LV_IMG_CF_TRUE_COLOR_ALPHA;
    img_dsc.header.always_zero = 0;
    img_dsc.header.w = 0;  
    img_dsc.header.h = 0;  

    
    lv_img_set_src(ui_Image1, &img_dsc); // z tym 5.22
    
    free(buffer);
    
    ESP_LOGI(SDTAG, "PNG image loaded successfully");
    
    return ESP_OK;
}

void changeIMG(lv_event_t * e)
{
	counter++;
	
	size_t free_psram = heap_caps_get_free_size(MALLOC_CAP_SPIRAM);
	ESP_LOGI("PSRAM", " Przed PSRAM Free Heap: %d, w MB: %.2f", free_psram, (float)free_psram / 1048576);
	
	
	
	//list_files_on_sd_card2();
	
	//read_binary_file("/sdcard/001.png");
	
	if(counter > 4){
		counter = 1;
	}
	
	switch(counter){
		case 1:
			lv_img_set_src(ui_Image1, &ui_img_1_png); // z tym 5.66
			
			ESP_LOGI("IMG1", "buffer: %d", &ui_img_1_png.data);
			break;
		case 2:
			//lv_img_set_src(ui_Image1, &ui_img_2_png); // z tym 5.22
			read_binary_file("/sdcard/001.png");
			break;
		case 3:
			//lv_img_set_src(ui_Image1, &ui_img_3_png); // z tym 4.72
			read_binary_file("/sdcard/002.png");
			break;
		case 4:
			read_binary_file("/sdcard/003.png");
			//lv_img_set_src(ui_Image1, &ui_img_4_png); // z tym 4.28
			break;
	}
	
	ESP_LOGI("PSRAM", " Po PSRAM Free Heap: %d, w MB: %.2f", free_psram, (float)free_psram / 1048576);
}

Screenshot and/or video

If possible, add screenshots and/or videos about the current state.

Ok so in the another way, i read about creating and register file system driver in LVGL. So i created sd_lvlg.c, this look like:

/*
 * sd_lvgl.c
 *
 *  Created on: 14 sty 2025
 *      Author: apacz
 */

#include "esp_vfs_fat.h"
#include "sdmmc_cmd.h"
#include "sd_test_io.h"
#include "dirent.h"
#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include "driver/gpio.h"
#include "esp_err.h"
#include "esp_log.h"
#include "lvgl.h"

#include "driver/i2c.h"


static const char *SDTAG = "SDLOG";
static const char *SD_MOUNT_POINT = "/sdcard";

#define PIN_NUM_MISO  13
#define PIN_NUM_MOSI  11
#define PIN_NUM_CLK   12
#define PIN_NUM_CS    -1

////////////////////////////////////////////////////////////////

#define I2C_MASTER_SCL_IO           9       /*!< GPIO number used for I2C master clock */
#define I2C_MASTER_SDA_IO           8       /*!< GPIO number used for I2C master data  */
#define I2C_MASTER_NUM              0       /*!< I2C master i2c port number, the number of i2c peripheral interfaces available will depend on the chip */
#define I2C_MASTER_FREQ_HZ          400000                     /*!< I2C master clock frequency */
#define I2C_MASTER_TX_BUF_DISABLE   0                          /*!< I2C master doesn't need buffer */
#define I2C_MASTER_RX_BUF_DISABLE   0                          /*!< I2C master doesn't need buffer */
#define I2C_MASTER_TIMEOUT_MS       1000

void mount_sd_card(){
	
	ESP_LOGI(SDTAG, "Start mountSDCard");
	esp_err_t ret;
    
    gpio_pullup_en(GPIO_NUM_12); // CLK
	gpio_pullup_en(GPIO_NUM_11); // MOSI
	gpio_pullup_en(GPIO_NUM_13); // MISO


    
    
    uint8_t write_buf = 0x01;
    i2c_master_write_to_device(I2C_MASTER_NUM, 0x24, &write_buf, 1, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
    write_buf = 0x0A;
    i2c_master_write_to_device(I2C_MASTER_NUM, 0x38, &write_buf, 1, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
    

    esp_vfs_fat_sdmmc_mount_config_t mount_config = {
        .format_if_mount_failed = false,
        .max_files = 2,
        .allocation_unit_size = 16 * 1024
    };
    sdmmc_card_t *card;
    //const char mount_point[] = SD_MOUNT_POINT;
    ESP_LOGI(SDTAG, "Initializing SD card");

    // Use settings defined above to initialize SD card and mount FAT filesystem.
    // Note: esp_vfs_fat_sdmmc/sdspi_mount is all-in-one convenience functions.
    // Please check its source code and implement error recovery when developing
    // production applications.
    ESP_LOGI(SDTAG, "Using SPI peripheral");

    // By default, SD card frequency is initialized to SDMMC_FREQ_DEFAULT (20MHz)
    // For setting a specific frequency, use host.max_freq_khz (range 400kHz - 20MHz for SDSPI)
    // Example: for fixed frequency of 10MHz, use host.max_freq_khz = 10000;
    sdmmc_host_t host = SDSPI_HOST_DEFAULT();
    host.max_freq_khz = 20000; // Ustawienie prędkości na 10 MHz (10000 kHz)

    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 = 4000,
    };
    ret = spi_bus_initialize(host.slot, &bus_cfg, SDSPI_DEFAULT_DMA);
    if (ret != ESP_OK) {
        ESP_LOGE(SDTAG, "Failed to initialize bus.");
        goto K1;
    }

    // This initializes the slot without card detect (CD) and write protect (WP) signals.
    // Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
    sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
    slot_config.gpio_cs = PIN_NUM_CS;
    slot_config.host_id = host.slot;


    ESP_LOGI(SDTAG, "Mounting filesystem");
    ret = esp_vfs_fat_sdspi_mount(SD_MOUNT_POINT, &host, &slot_config, &mount_config, &card);

    if (ret != ESP_OK) {
        if (ret == ESP_FAIL) {
            ESP_LOGE(SDTAG, "Failed to mount filesystem. "
                     "If you want the card to be formatted, set the CONFIG_EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.");
        } else {
            ESP_LOGE(SDTAG, "Failed to initialize the card (%s). "
                     "Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret));
        }
        goto K1;
    }
    
    ESP_LOGI(SDTAG, "Filesystem mounted");
    
    K1:
}

// Callback do otwierania plików
static void *sd_open_cb(lv_fs_drv_t *drv, const char *path, lv_fs_mode_t mode) {
	
	ESP_LOGI(SDTAG, "Open file: %s", path);
	
    char full_path[128];
    snprintf(full_path, sizeof(full_path), "%s/%s", SD_MOUNT_POINT, path);

    const char *mode_str = (mode == LV_FS_MODE_WR) ? "wb" : "rb";
    return fopen(full_path, mode_str);
}

// Callback do zamykania plików
static lv_fs_res_t sd_close_cb(lv_fs_drv_t *drv, void *file_p) {
    fclose((FILE *)file_p);
    return LV_FS_RES_OK;
}

// Callback do czytania plików
static lv_fs_res_t sd_read_cb(lv_fs_drv_t *drv, void *file_p, void *buf, uint32_t btr, uint32_t *br) {
    *br = fread(buf, 1, btr, (FILE *)file_p);
    return (*br > 0) ? LV_FS_RES_OK : LV_FS_RES_UNKNOWN;
}

// Callback do wyszukiwania pozycji w pliku
static lv_fs_res_t sd_seek_cb(lv_fs_drv_t *drv, void *file_p, uint32_t pos, lv_fs_whence_t whence) {
    fseek((FILE *)file_p, pos, (whence == LV_FS_SEEK_SET) ? SEEK_SET :
                            (whence == LV_FS_SEEK_CUR) ? SEEK_CUR : SEEK_END);
    return LV_FS_RES_OK;
}

// Callback do uzyskiwania pozycji w pliku
static lv_fs_res_t sd_tell_cb(lv_fs_drv_t *drv, void *file_p, uint32_t *pos_p) {
    *pos_p = ftell((FILE *)file_p);
    return LV_FS_RES_OK;
}

void lv_fs_register_sd(void) {
	
	ESP_LOGI(SDTAG, "Rejestrowanie drivera SD LVGL");
	
    lv_fs_drv_t fs_drv;
    lv_fs_drv_init(&fs_drv);

    fs_drv.letter = 'A';               // Litera oznaczająca system plików
    fs_drv.cache_size = 0;   // Rozmiar uchwytu pliku
    fs_drv.open_cb = sd_open_cb;
    fs_drv.close_cb = sd_close_cb;
    fs_drv.read_cb = sd_read_cb;
    fs_drv.seek_cb = sd_seek_cb;
    fs_drv.tell_cb = sd_tell_cb;

    lv_fs_drv_register(&fs_drv);
}

I have created lv_fs_register_sd() function and this is called after lv_init();
The SD card mount correctly. The read, seek ect. functions I will write later,
now I have problem with find ff.h in lv_fs_fatfs.c

C:/Projekty/ESP-IDE/SD_LVGL/components/lvgl__lvgl/src/extra/libs/fsdrv/lv_fs_fatfs.c:12:10: fatal error: ff.h: No such file or directory

How can I fix this in ESP-IDE evn?