File System Callback Questions

Description

I’m trying to implement some File System callbacks on my ESP32 using SPIFFS, but I’m having a hard time finding any examples to show me how to implement the callback properly.

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

Generic ESP32 Dev board, Arduino using PlatformIO

What LVGL version are you using?

7+

What do you want to achieve?

Write a proper file_open callback for my file system driver

What have you tried so far?

  • I have implemented the File System Driver (working)
  • But I cannot get a proper callback to compile.

The problem is lack of examples available. The documentation covers how to create the File system driver, but does not cover an examples of the needed callbacks, not is there any documentation explaining the arguments passed to the callbacks.

I’ve found some forum posts that cover this topic, but the results either do not compile for me, or the thread dies without a proper conclusion.

I’d love to see some full “official” examples of how to implement these callbacks.

Code to reproduce

  • This may be a simple fix, but my lack of C/C++ has me stumped.

Following this thread here on Github, I tried to implement kisvegabor’s final suggestion in the thread (April 20th), but the line (File * fp = file_p;) fails to compile with the following error:

error: invalid conversion from 'void*' to 'fs::File*' [-fpermissive]

As a test, I wrote my file_open callback to simply print the path it was handed, and that works, so I know the callback is being called.

The code block(s) should be formatted like:

int my_file_object = 1000;
int my_dir_object = 1000;

lv_fs_res_t open_SPIFFS_file(struct _lv_fs_drv_t * drv, void * file_p, const char * path, lv_fs_mode_t mode) 
{
    (void) drv; /*Unused*/

    File * fp = file_p; // <-- THIS LINE FAILS TO COMPILE
    if (*fp) {
        return LV_FS_RES_OK;
    }    

    char fullPath[64];
    sprintf(fullPath,"/%s", path);

    (*fp) = SPIFFS.open(fullPath, mode == LV_FS_MODE_WR ? FILE_WRITE : FILE_READ);

    if(!(*fp) || (*fp).isDirectory()){
        return LV_FS_RES_UNKNOWN;
    } else{
        return LV_FS_RES_OK;
    }
}

bool init_file_system_driver() 
{
    lv_fs_drv_t drv;
    lv_fs_drv_init(&drv);                     /*Basic initialization*/

    drv.letter = 'S';                         /*An uppercase letter to identify the drive */
    drv.file_size = sizeof(my_file_object);   /*Size required to store a file object*/
    drv.rddir_size = sizeof(my_dir_object);   /*Size required to store a directory object (used by dir_open/close/read)*/
    drv.ready_cb = NULL; //my_ready_cb;               /*Callback to tell if the drive is ready to use */
    drv.open_cb = open_SPIFFS_file;                 /*Callback to open a file */
    drv.close_cb = NULL; //my_close_cb;               /*Callback to close a file */
    drv.read_cb = NULL; //my_read_cb;                 /*Callback to read a file */
    drv.write_cb = NULL; //my_write_cb;               /*Callback to write a file */
    drv.seek_cb = NULL; //my_seek_cb;                 /*Callback to seek in a file (Move cursor) */
    drv.tell_cb = NULL; //my_tell_cb;                 /*Callback to tell the cursor position  */
    drv.trunc_cb = NULL; //my_trunc_cb;               /*Callback to delete a file */
    drv.size_cb = NULL; //my_size_cb;                 /*Callback to tell a file's size */
    drv.rename_cb = NULL; //my_rename_cb;             /*Callback to rename a file */

    drv.dir_open_cb = NULL; //my_dir_open_cb;         /*Callback to open directory to read its content */
    drv.dir_read_cb = NULL; //my_dir_read_cb;         /*Callback to read a directory's content */
    drv.dir_close_cb = NULL; //my_dir_close_cb;       /*Callback to close a directory */

    drv.free_space_cb = NULL; //my_free_space_cb;     /*Callback to tell free space on the drive */

    // drv.user_data = my_user_data;             /*Any custom data if required*/

    lv_fs_drv_register(&drv);                 /*Finally register the drive*/

    if (!SPIFFS.begin(true)) {
        Serial.println("An Error has occurred while mounting SPIFFS");
        return false;
    }    

    return true;
}

void setup() 
{
    init_file_system_driver();
}

Some other important information that I forgot to include:

  • I’m trying to load image files from the SPIFFS file system.
  • These images are converted into *.bin files using the online tool.
  • I’m trying to open and then display these images on my display using:
    lv_obj_t * icon = lv_img_create(lv_scr_act(), NULL);
    lv_img_set_src(icon, "S:myImageFilename.bin");
    lv_obj_align(icon, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 0);

Try this:

lv_fs_res_t open_SPIFFS_file(struct _lv_fs_drv_t * drv, void * file_p, const char * path, lv_fs_mode_t mode) 
{
    (void) drv; /*Unused*/

    File * fp = (File *)file_p; // embeddedt: C++ needs explicit casts
    if (*fp) {
        return LV_FS_RES_OK;
    }    

    char fullPath[64];
    sprintf(fullPath,"/%s", path);

    (*fp) = SPIFFS.open(fullPath, mode == LV_FS_MODE_WR ? FILE_WRITE : FILE_READ);

    if(!(*fp) || (*fp).isDirectory()){
        return LV_FS_RES_UNKNOWN;
    } else{
        return LV_FS_RES_OK;
    }
}

@embeddedt, thanks for the quick reply.

This solved my compile issue. (I actually tried your suggestion but forgot the ‘*’ in the cast)

My issue now is that the image does not show up on my display when I use the following code in my setup function

    lv_obj_t * icon = lv_img_create(lv_scr_act(), NULL);
    lv_img_set_src(icon, "S:image01.bin");
    lv_obj_align(icon, NULL, LV_ALIGN_CENTER, 0, 0);

There are no error messages and I’ve verified that the file exists on SPIFFS.
Am I missing any important steps?

Previously, where I expected my image to show up, I saw the words “no data” when it couldn’t find any image data. But now, it appears as if the image is transparent, or possibly off screen somewhere?

You need a read_cb for your filesystem as well. Do you have that?

I did not have either, or the seek and tell callback. So I added those like below, but I’m still seeing no image, or any errors.

lv_fs_res_t read_SPIFFS_file(struct _lv_fs_drv_t *drv, void *file_p, void *buf, uint32_t btr, uint32_t *br)
{
    (void) drv; /*Unused*/

    File * fp = (File *)file_p; // embeddedt: C++ needs explicit casts
    if (*fp) {
        return LV_FS_RES_OK;
    }  

    *br = (*fp).read((uint8_t*)buf, btr);
    return LV_FS_RES_OK;
}

lv_fs_res_t close_SPIFFS_file(struct _lv_fs_drv_t *drv, void *file_p)
{
    (void) drv; /*Unused*/

    File * fp = (File *)file_p; // embeddedt: C++ needs explicit casts
    if (*fp) {
        return LV_FS_RES_OK;
    }  

    (*fp).close();
    return LV_FS_RES_OK;
}

lv_fs_res_t seek_SPIFFS_file(lv_fs_drv_t *drv, void *file_p, uint32_t pos)
{
    (void) drv; /*Unused*/

    File * fp = (File *)file_p; // embeddedt: C++ needs explicit casts
    if (*fp) {
        return LV_FS_RES_OK;
    }  

    (*fp).seek(pos);

    return LV_FS_RES_OK;
}

lv_fs_res_t tell_SPIFFS_file(lv_fs_drv_t *drv, void *file_p, uint32_t *pos_p)
{
    (void) drv; /*Unused*/

    File * fp = (File *)file_p; // embeddedt: C++ needs explicit casts
    if (*fp) {
        return LV_FS_RES_OK;
    }  
    
    uint32_t tmp = (*fp).position();
    pos_p = &tmp;
  
    return LV_FS_RES_OK;
}

bool init_file_system_driver() 
{
    lv_fs_drv_t drv;
    lv_fs_drv_init(&drv);                     /*Basic initialization*/

    drv.letter = 'S';                         /*An uppercase letter to identify the drive */
    drv.file_size = sizeof(my_file_object);   /*Size required to store a file object*/
    drv.rddir_size = sizeof(my_dir_object);   /*Size required to store a directory object (used by dir_open/close/read)*/
    drv.ready_cb = NULL; //my_ready_cb;               /*Callback to tell if the drive is ready to use */
    drv.open_cb = open_SPIFFS_file;                 /*Callback to open a file */
    drv.close_cb = close_SPIFFS_file;                /*Callback to close a file */
    drv.read_cb = read_SPIFFS_file;                 /*Callback to read a file */
    drv.write_cb = NULL; //my_write_cb;               /*Callback to write a file */
    drv.seek_cb = seek_SPIFFS_file;                 /*Callback to seek in a file (Move cursor) */
    drv.tell_cb = tell_SPIFFS_file;                 /*Callback to tell the cursor position  */
    drv.trunc_cb = NULL; //my_trunc_cb;               /*Callback to delete a file */
    drv.size_cb = NULL; //my_size_cb;                 /*Callback to tell a file's size */
    drv.rename_cb = NULL; //my_rename_cb;             /*Callback to rename a file */

    drv.dir_open_cb = NULL; //my_dir_open_cb;         /*Callback to open directory to read its content */
    drv.dir_read_cb = NULL; //my_dir_read_cb;         /*Callback to read a directory's content */
    drv.dir_close_cb = NULL; //my_dir_close_cb;       /*Callback to close a directory */

    drv.free_space_cb = NULL; //my_free_space_cb;     /*Callback to tell free space on the drive */

    // drv.user_data = my_user_data;             /*Any custom data if required*/

    lv_fs_drv_register(&drv);                 /*Finally register the drive*/

    if (!SPIFFS.begin(true)) {
        Serial.println("An Error has occurred while mounting SPIFFS");
        return false;
    }    

    return true;
}

I should have added to my last post that I’m not sure if I implemented those callbacks properly. I’m hoping somebody can take a look and let me know.

I doubt this will fix your problem but in tell_SPIFFS_file you should assign pos_p like this:

*pos_p = tmp;

With your current syntax it’s just changing the value of pos_p itself.

I had a crack at a SPIFFS filesystem driver for lvgl a few months ago. It kinda worked as I could get an image to display properly, but it still crashed sometimes… so expect it to be buggy.

You might be able to use part of it for your own implementation or maybe someone can help find some of the remaining bugs. I have it on GitHub here https://github.com/fvanroie/hasp-lvgl/blob/0.1.0-dev/lib/lv_fs_if/lv_fs_spiffs.cpp

It’s been a while, but I want to re-investigate the driver too, as this question comes up regularly. Hopefully we can join forces and create a working reference driver for ESP32.

1 Like

@fvanroie, thanks so much for your repository. Following your code I was able to get image displaying working with the following implementation below.

The one thing that might have been my issue, was I was assigning the driver’s file_size to an int, where as you were assigning it to type lv_spiffs_file_t, which is of File type. that might have been my issue as I thought that was meant to be a size value.

/* Create a type to store the required data about your file.*/
typedef File lv_spiffs_file_t;
typedef File lv_spiffs_dir_t;


lv_fs_res_t open_SPIFFS_file(struct _lv_fs_drv_t * drv, void * file_p, const char * path, lv_fs_mode_t mode) 
{
    (void) drv; /*Unused*/

    char filename[32];
    snprintf_P(filename, sizeof(filename), PSTR("/%s"), path);    

    File file = SPIFFS.open(filename, mode == LV_FS_MODE_WR ? FILE_WRITE : FILE_READ);

    if(!file) {
        return LV_FS_RES_NOT_EX;

    } else if(file.isDirectory()) {
        return LV_FS_RES_UNKNOWN;

    } else {
        lv_spiffs_file_t * fp = (lv_spiffs_file_t *)file_p; /*Just avoid the confusing casings*/
        if (fp != NULL) (*fp) = file;
        return LV_FS_RES_OK;
    }
}

lv_fs_res_t read_SPIFFS_file(struct _lv_fs_drv_t *drv, void *file_p, void *buf, uint32_t btr, uint32_t *br)
{
    (void) drv; /*Unused*/

    lv_spiffs_file_t *fp = (lv_spiffs_file_t *)file_p;
    lv_spiffs_file_t file = *fp;

    if (!file) {
        return LV_FS_RES_NOT_EX;

    } else {
        *br = (uint32_t)file.readBytes((char *)buf, btr);
        return LV_FS_RES_OK;
    }    
}

lv_fs_res_t close_SPIFFS_file(struct _lv_fs_drv_t *drv, void *file_p)
{
    (void) drv; /*Unused*/

    lv_spiffs_file_t file = *(lv_spiffs_file_t *)file_p;

    if (!file) {
        return LV_FS_RES_NOT_EX;

    } else if(file.isDirectory()) {
        return LV_FS_RES_UNKNOWN;

    } else {
        file.close();
        return LV_FS_RES_OK;
    }    
}

lv_fs_res_t seek_SPIFFS_file(lv_fs_drv_t *drv, void *file_p, uint32_t pos)
{
    (void) drv; /*Unused*/

    lv_spiffs_file_t file = *(lv_spiffs_file_t *)file_p;

    if (!file) {
        return LV_FS_RES_NOT_EX;

    } else {
        file.seek(pos, SeekSet);
        return LV_FS_RES_OK;
    }
}

lv_fs_res_t tell_SPIFFS_file(lv_fs_drv_t *drv, void *file_p, uint32_t *pos_p)
{
    (void) drv; /*Unused*/

    lv_spiffs_file_t file = *(lv_spiffs_file_t *)file_p;

    if (!file) {
        return LV_FS_RES_NOT_EX;

    } else {
        *pos_p = (uint32_t)file.position();
        return LV_FS_RES_OK;
    }    
}

bool init_file_system_driver() 
{
    lv_fs_drv_t drv;
    lv_fs_drv_init(&drv);                     /*Basic initialization*/

    drv.letter = 'S';                         /*An uppercase letter to identify the drive */
    drv.file_size = sizeof(lv_spiffs_file_t);   /*Size required to store a file object*/
    drv.rddir_size = sizeof(lv_spiffs_dir_t);   /*Size required to store a directory object (used by dir_open/close/read)*/
    drv.ready_cb = NULL; //my_ready_cb;               /*Callback to tell if the drive is ready to use */
    drv.open_cb = open_SPIFFS_file;                 /*Callback to open a file */
    drv.close_cb = close_SPIFFS_file;                /*Callback to close a file */
    drv.read_cb = read_SPIFFS_file;                 /*Callback to read a file */
    drv.write_cb = NULL; //my_write_cb;               /*Callback to write a file */
    drv.seek_cb = seek_SPIFFS_file;                 /*Callback to seek in a file (Move cursor) */
    drv.tell_cb = tell_SPIFFS_file;                 /*Callback to tell the cursor position  */
    drv.trunc_cb = NULL; //my_trunc_cb;               /*Callback to delete a file */
    drv.size_cb = NULL; //my_size_cb;                 /*Callback to tell a file's size */
    drv.rename_cb = NULL; //my_rename_cb;             /*Callback to rename a file */

    drv.dir_open_cb = NULL; //my_dir_open_cb;         /*Callback to open directory to read its content */
    drv.dir_read_cb = NULL; //my_dir_read_cb;         /*Callback to read a directory's content */
    drv.dir_close_cb = NULL; //my_dir_close_cb;       /*Callback to close a directory */

    drv.free_space_cb = NULL; //my_free_space_cb;     /*Callback to tell free space on the drive */

    // drv.user_data = my_user_data;             /*Any custom data if required*/

    lv_fs_drv_register(&drv);                 /*Finally register the drive*/

    if (!SPIFFS.begin(true)) {
        Serial.println("An Error has occurred while mounting SPIFFS");
        return false;
    }    

    return true;
}

As I use this image loading code, I’ll keep an eye on performance and capability and report back any info that I think might be useful to improving your implementation.

1 Like

Happy to help and good to know it worked out. I"ll compare your notes and see if anything pops out too.