diff --git a/lib/lv_fs_if/LICENSE b/lib/lv_fs_if/LICENSE new file mode 100644 index 00000000..e418490b --- /dev/null +++ b/lib/lv_fs_if/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Littlev Graphics Library + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/lv_fs_if/README.md b/lib/lv_fs_if/README.md new file mode 100644 index 00000000..f2c09521 --- /dev/null +++ b/lib/lv_fs_if/README.md @@ -0,0 +1,23 @@ +# File System Interface + +LittlevGL has a [File system](https://docs.littlevgl.com/en/html/overview/file-system.html) module to attach memories which can manipulate with files. Here you can find interfaces to +- FATFS +- PC (Linux and Windows) +file systems. + +You still need to provide the drivers and libraries, this repo gives "only" the bridge between FATFS/PC/etc and LittlevGL. + +## Usage +1. Add these lines to you `lv_conf.h`: +```c +/*File system interface*/ +#define LV_USE_FS_IF 1 +#if LV_USE_FS_IF +# define LV_FS_IF_FATFS '\0' +# define LV_FS_IF_PC '\0' +#endif /*LV_USE_FS_IF*/ +``` + +2. Enable an interface you need by changing `'\0'` to letter you want to use for that drive. E.g. `'S'` for SD card with FATFS. + +3. Call `lv_fs_if_init()` to register the enabled interfaces. diff --git a/lib/lv_fs_if/lv_fs_fatfs.c b/lib/lv_fs_if/lv_fs_fatfs.c new file mode 100644 index 00000000..4279b2b7 --- /dev/null +++ b/lib/lv_fs_if/lv_fs_fatfs.c @@ -0,0 +1,342 @@ +/** + * @file lv_fs_fatfs.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_fs_if.h" + +#if LV_USE_FS_IF +#if LV_FS_IF_FATFS != '\0' +#include "ff.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/* Create a type to store the required data about your file.*/ +typedef FIL file_t; + +/*Similarly to `file_t` create a type for directory reading too */ +typedef DIR dir_t; + +/********************** + * STATIC PROTOTYPES + **********************/ +static void fs_init(void); + +static lv_fs_res_t fs_open (lv_fs_drv_t * drv, void * file_p, const char * path, lv_fs_mode_t mode); +static lv_fs_res_t fs_close (lv_fs_drv_t * drv, void * file_p); +static lv_fs_res_t fs_read (lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br); +static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw); +static lv_fs_res_t fs_seek (lv_fs_drv_t * drv, void * file_p, uint32_t pos); +static lv_fs_res_t fs_size (lv_fs_drv_t * drv, void * file_p, uint32_t * size_p); +static lv_fs_res_t fs_tell (lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p); +static lv_fs_res_t fs_remove (lv_fs_drv_t * drv, const char *path); +static lv_fs_res_t fs_trunc (lv_fs_drv_t * drv, void * file_p); +static lv_fs_res_t fs_rename (lv_fs_drv_t * drv, const char * oldname, const char * newname); +static lv_fs_res_t fs_free (lv_fs_drv_t * drv, uint32_t * total_p, uint32_t * free_p); +static lv_fs_res_t fs_dir_open (lv_fs_drv_t * drv, void * dir_p, const char *path); +static lv_fs_res_t fs_dir_read (lv_fs_drv_t * drv, void * dir_p, char *fn); +static lv_fs_res_t fs_dir_close (lv_fs_drv_t * drv, void * dir_p); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_fs_if_fatfs_init(void) +{ + /*---------------------------------------------------- + * Initialize your storage device and File System + * -------------------------------------------------*/ + fs_init(); + + /*--------------------------------------------------- + * Register the file system interface in LittlevGL + *--------------------------------------------------*/ + + /* Add a simple drive to open images */ + lv_fs_drv_t fs_drv; /*A driver descriptor*/ + lv_fs_drv_init(&fs_drv); + + /*Set up fields...*/ + fs_drv.file_size = sizeof(file_t); + fs_drv.letter = LV_FS_IF_FATFS; + 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; + + lv_fs_drv_register(&fs_drv); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/* 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*/ +} + +/** + * Open a file + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable + * @param path path to the file beginning with the driver letter (e.g. S:/folder/file.txt) + * @param mode read: FS_MODE_RD, write: FS_MODE_WR, both: FS_MODE_RD | FS_MODE_WR + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_open (lv_fs_drv_t * drv, void * file_p, const char * path, lv_fs_mode_t mode) +{ + uint8_t flags = 0; + + if(mode == LV_FS_MODE_WR) flags = FA_WRITE | FA_OPEN_ALWAYS; + else if(mode == LV_FS_MODE_RD) flags = FA_READ; + else if(mode == (LV_FS_MODE_WR | LV_FS_MODE_RD)) flags = FA_READ | FA_WRITE | FA_OPEN_ALWAYS; + + FRESULT res = f_open(file_p, path, flags); + + if(res == FR_OK) { + f_lseek(file_p, 0); + return LV_FS_RES_OK; + } else { + return LV_FS_RES_UNKNOWN; + } +} + + +/** + * Close an opened file + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable. (opened with lv_ufs_open) + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_close (lv_fs_drv_t * drv, void * file_p) +{ + f_close(file_p); + return LV_FS_RES_OK; +} + +/** + * Read data from an opened file + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable. + * @param buf pointer to a memory block where to store the read data + * @param btr number of Bytes To Read + * @param br the real number of read bytes (Byte Read) + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_read (lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br) +{ + FRESULT res = f_read(file_p, buf, btr, (UINT*)br); + if(res == FR_OK) return LV_FS_RES_OK; + else return LV_FS_RES_UNKNOWN; +} + +/** + * Write into a file + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable + * @param buf pointer to a buffer with the bytes to write + * @param btr Bytes To Write + * @param br the number of real written bytes (Bytes Written). NULL if unused. + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw) +{ + FRESULT res = f_write(file_p, buf, btw, (UINT*)bw); + if(res == FR_OK) return LV_FS_RES_OK; + else return LV_FS_RES_UNKNOWN; +} + +/** + * Set the read write pointer. Also expand the file size if necessary. + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable. (opened with lv_ufs_open ) + * @param pos the new position of read write pointer + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_seek (lv_fs_drv_t * drv, void * file_p, uint32_t pos) +{ + f_lseek(file_p, pos); + return LV_FS_RES_OK; +} + +/** + * Give the size of a file bytes + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable + * @param size pointer to a variable to store the size + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_size (lv_fs_drv_t * drv, void * file_p, uint32_t * size_p) +{ + (*size_p) = f_size(((file_t *)file_p)); + return LV_FS_RES_OK; +} + +/** + * Give the position of the read write pointer + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable. + * @param pos_p pointer to to store the result + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_tell (lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p) +{ + *pos_p = f_tell(((file_t *)file_p)); + return LV_FS_RES_OK; +} + +/** + * Delete a file + * @param drv pointer to a driver where this function belongs + * @param path path of the file to delete + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_remove (lv_fs_drv_t * drv, const char *path) +{ + lv_fs_res_t res = LV_FS_RES_NOT_IMP; + + /* Add your code here*/ + + return res; +} + +/** + * Truncate the file size to the current position of the read write pointer + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to an 'ufs_file_t' variable. (opened with lv_fs_open ) + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_trunc (lv_fs_drv_t * drv, void * file_p) +{ + f_sync(file_p); /*If not syncronized fclose can write the truncated part*/ + f_truncate(file_p); + return LV_FS_RES_OK; +} + +/** + * Rename a file + * @param drv pointer to a driver where this function belongs + * @param oldname path to the file + * @param newname path with the new name + * @return LV_FS_RES_OK or any error from 'fs_res_t' + */ +static lv_fs_res_t fs_rename (lv_fs_drv_t * drv, const char * oldname, const char * newname) +{ + + FRESULT res = f_rename(oldname, newname); + + if(res == FR_OK) return LV_FS_RES_OK; + else return LV_FS_RES_UNKNOWN; +} + +/** + * Get the free and total size of a driver in kB + * @param drv pointer to a driver where this function belongs + * @param letter the driver letter + * @param total_p pointer to store the total size [kB] + * @param free_p pointer to store the free size [kB] + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +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; + + /* Add your code here*/ + + return res; +} + +/** + * Initialize a 'fs_read_dir_t' variable for directory reading + * @param drv pointer to a driver where this function belongs + * @param dir_p pointer to a 'fs_read_dir_t' variable + * @param path path to a directory + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_dir_open (lv_fs_drv_t * drv, void * dir_p, const char *path) +{ + FRESULT res = f_opendir(dir_p, path); + if(res == FR_OK) return LV_FS_RES_OK; + else return LV_FS_RES_UNKNOWN; +} + +/** + * Read the next filename form a directory. + * The name of the directories will begin with '/' + * @param drv pointer to a driver where this function belongs + * @param dir_p pointer to an initialized 'fs_read_dir_t' variable + * @param fn pointer to a buffer to store the filename + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_dir_read (lv_fs_drv_t * drv, void * dir_p, char *fn) +{ + FRESULT res; + FILINFO fno; + fn[0] = '\0'; + + do { + res = f_readdir(dir_p, &fno); + if(res != FR_OK) return LV_FS_RES_UNKNOWN; + + if(fno.fattrib & AM_DIR) { + fn[0] = '/'; + strcpy(&fn[1], fno.fname); + } + else strcpy(fn, fno.fname); + + } while(strcmp(fn, "/.") == 0 || strcmp(fn, "/..") == 0); + + return LV_FS_RES_OK; +} + +/** + * Close the directory reading + * @param drv pointer to a driver where this function belongs + * @param dir_p pointer to an initialized 'fs_read_dir_t' variable + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_dir_close (lv_fs_drv_t * drv, void * dir_p) +{ + f_closedir(dir_p); + return LV_FS_RES_OK; +} + +#endif /*LV_USE_FS_IF*/ +#endif /*LV_FS_IF_FATFS*/ diff --git a/lib/lv_fs_if/lv_fs_if.c b/lib/lv_fs_if/lv_fs_if.c new file mode 100644 index 00000000..0b4c8fcc --- /dev/null +++ b/lib/lv_fs_if/lv_fs_if.c @@ -0,0 +1,70 @@ +/** + * @file lv_fs_if.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_fs_if.h" + +#if LV_USE_FS_IF + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +#if LV_FS_IF_FATFS != '\0' +void lv_fs_if_fatfs_init(void); +#endif + +#if LV_FS_IF_PC != '\0' +void lv_fs_if_pc_init(void); +#endif + +#if LV_FS_IF_SPIFFS != '\0' +#include "lv_fs_spiffs.h" +#endif + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Register driver(s) for the File system interface + */ +void lv_fs_if_init(void) +{ +#if LV_FS_IF_FATFS != '\0' + lv_fs_if_fatfs_init(); +#endif + +#if LV_FS_IF_PC != '\0' + lv_fs_if_pc_init(); +#endif + +#if LV_FS_IF_SPIFFS != '\0' + lv_fs_if_spiffs_init(); +#endif +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif diff --git a/lib/lv_fs_if/lv_fs_if.h b/lib/lv_fs_if/lv_fs_if.h new file mode 100644 index 00000000..cc69dc83 --- /dev/null +++ b/lib/lv_fs_if/lv_fs_if.h @@ -0,0 +1,47 @@ +/** + * @file lv_fs_if.h + * + */ + +#ifndef LV_FS_IF_H +#define LV_FS_IF_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#include "lvgl.h" + +#if LV_USE_FS_IF + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Register driver(s) for the File system interface + */ +void lv_fs_if_init(void); + +/********************** + * MACROS + **********************/ + +#endif /*LV_USE_FS_IF*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_FS_IF_H*/ diff --git a/lib/lv_fs_if/lv_fs_pc.c b/lib/lv_fs_if/lv_fs_pc.c new file mode 100644 index 00000000..9444ebe5 --- /dev/null +++ b/lib/lv_fs_if/lv_fs_pc.c @@ -0,0 +1,472 @@ +/** + * @file lv_fs_pc.c + * + */ + + +/********************* + * INCLUDES + *********************/ +#include "lv_fs_if.h" +#if LV_USE_FS_IF +#if LV_FS_IF_PC != '\0' + +#include +#include +#include +#include +#ifdef WIN32 +#include +#endif + +/********************* + * DEFINES + *********************/ +#ifndef LV_FS_PC_PATH +# ifndef WIN32 +# define LV_FS_PC_PATH "./" /*Projet root*/ +# else +# define LV_FS_PC_PATH ".\\" /*Projet root*/ +# endif +#endif /*LV_FS_PATH*/ + +/********************** + * TYPEDEFS + **********************/ + +/* Create a type to store the required data about your file. */ +typedef FILE * file_t; + +/*Similarly to `file_t` create a type for directory reading too */ +#ifndef WIN32 +typedef DIR * dir_t; +#else +typedef HANDLE dir_t; +#endif + + +/********************** + * STATIC PROTOTYPES + **********************/ +static lv_fs_res_t fs_open (lv_fs_drv_t * drv, void * file_p, const char * path, lv_fs_mode_t mode); +static lv_fs_res_t fs_close (lv_fs_drv_t * drv, void * file_p); +static lv_fs_res_t fs_read (lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br); +static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw); +static lv_fs_res_t fs_seek (lv_fs_drv_t * drv, void * file_p, uint32_t pos); +static lv_fs_res_t fs_size (lv_fs_drv_t * drv, void * file_p, uint32_t * size_p); +static lv_fs_res_t fs_tell (lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p); +static lv_fs_res_t fs_remove (lv_fs_drv_t * drv, const char *path); +static lv_fs_res_t fs_trunc (lv_fs_drv_t * drv, void * file_p); +static lv_fs_res_t fs_rename (lv_fs_drv_t * drv, const char * oldname, const char * newname); +static lv_fs_res_t fs_free (lv_fs_drv_t * drv, uint32_t * total_p, uint32_t * free_p); +static lv_fs_res_t fs_dir_open (lv_fs_drv_t * drv, void * dir_p, const char *path); +static lv_fs_res_t fs_dir_read (lv_fs_drv_t * drv, void * dir_p, char *fn); +static lv_fs_res_t fs_dir_close (lv_fs_drv_t * drv, void * dir_p); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Register a driver for the File system interface + */ +void lv_fs_if_pc_init(void) +{ + /*--------------------------------------------------- + * Register the file system interface in LittlevGL + *--------------------------------------------------*/ + + /* Add a simple drive to open images */ + lv_fs_drv_t fs_drv; /*A driver descriptor*/ + lv_fs_drv_init(&fs_drv); + + /*Set up fields...*/ + fs_drv.file_size = sizeof(file_t); + fs_drv.letter = LV_FS_IF_PC; + 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; + + lv_fs_drv_register(&fs_drv); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Open a file + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable + * @param path path to the file beginning with the driver letter (e.g. S:/folder/file.txt) + * @param mode read: FS_MODE_RD, write: FS_MODE_WR, both: FS_MODE_RD | FS_MODE_WR + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_open (lv_fs_drv_t * drv, void * file_p, const char * path, lv_fs_mode_t mode) +{ + (void) drv; /*Unused*/ + errno = 0; + + 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+"; + + /*Make the path relative to the current directory (the projects root folder)*/ + +#ifndef WIN32 + char buf[256]; + sprintf(buf, LV_FS_PC_PATH "/%s", path); +#else + char buf[256]; + sprintf(buf, LV_FS_PC_PATH "\\%s", path); +#endif + + file_t f = fopen(buf, flags); + if(f == NULL) return LV_FS_RES_UNKNOWN; + + /*Be sure we are the beginning of the file*/ + fseek(f, 0, SEEK_SET); + + /* 'file_p' is pointer to a file descriptor and + * we need to store our file descriptor here*/ + file_t * fp = file_p; /*Just avoid the confusing casings*/ + *fp = f; + + return LV_FS_RES_OK; +} + + +/** + * Close an opened file + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable. (opened with lv_ufs_open) + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_close (lv_fs_drv_t * drv, void * file_p) +{ + (void) drv; /*Unused*/ + file_t * fp = file_p; /*Just avoid the confusing casings*/ + fclose(*fp); + return LV_FS_RES_OK; +} + +/** + * Read data from an opened file + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable. + * @param buf pointer to a memory block where to store the read data + * @param btr number of Bytes To Read + * @param br the real number of read bytes (Byte Read) + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_read (lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br) +{ + (void) drv; /*Unused*/ + file_t * fp = file_p; /*Just avoid the confusing casings*/ + *br = fread(buf, 1, btr, *fp); + return LV_FS_RES_OK; +} + +/** + * Write into a file + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable + * @param buf pointer to a buffer with the bytes to write + * @param btr Bytes To Write + * @param br the number of real written bytes (Bytes Written). NULL if unused. + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw) +{ + (void) drv; /*Unused*/ + file_t * fp = file_p; /*Just avoid the confusing casings*/ + *bw = fwrite(buf, 1, btw, *fp); + return LV_FS_RES_OK; +} + +/** + * Set the read write pointer. Also expand the file size if necessary. + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable. (opened with lv_ufs_open ) + * @param pos the new position of read write pointer + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_seek (lv_fs_drv_t * drv, void * file_p, uint32_t pos) +{ + (void) drv; /*Unused*/ + file_t * fp = file_p; /*Just avoid the confusing casings*/ + fseek(*fp, pos, SEEK_SET); + return LV_FS_RES_OK; +} + +/** + * Give the size of a file bytes + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable + * @param size pointer to a variable to store the size + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_size (lv_fs_drv_t * drv, void * file_p, uint32_t * size_p) +{ + (void) drv; /*Unused*/ + file_t * fp = file_p; /*Just avoid the confusing casings*/ + + uint32_t cur = ftell(*fp); + + fseek(*fp, 0L, SEEK_END); + *size_p = ftell(*fp); + + /*Restore file pointer*/ + fseek(*fp, cur, SEEK_SET); + + return LV_FS_RES_OK; +} +/** + * Give the position of the read write pointer + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable. + * @param pos_p pointer to to store the result + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_tell (lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p) +{ + (void) drv; /*Unused*/ + file_t * fp = file_p; /*Just avoid the confusing casings*/ + *pos_p = ftell(*fp); + return LV_FS_RES_OK; +} + +/** + * Delete a file + * @param drv pointer to a driver where this function belongs + * @param path path of the file to delete + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_remove (lv_fs_drv_t * drv, const char *path) +{ + (void) drv; /*Unused*/ + lv_fs_res_t res = LV_FS_RES_NOT_IMP; + + /* Add your code here*/ + + return res; +} + +/** + * Truncate the file size to the current position of the read write pointer + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to an 'ufs_file_t' variable. (opened with lv_fs_open ) + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_trunc (lv_fs_drv_t * drv, void * file_p) +{ + (void) drv; /*Unused*/ + file_t * fp = file_p; /*Just avoid the confusing casings*/ + + fflush(*fp); /*If not syncronized fclose can write the truncated part*/ + uint32_t p = ftell(*fp); + ftruncate(fileno(*fp), p); + return LV_FS_RES_OK; +} + +/** + * Rename a file + * @param drv pointer to a driver where this function belongs + * @param oldname path to the file + * @param newname path with the new name + * @return LV_FS_RES_OK or any error from 'fs_res_t' + */ +static lv_fs_res_t fs_rename (lv_fs_drv_t * drv, const char * oldname, const char * newname) +{ + (void) drv; /*Unused*/ + static char new[512]; + static char old[512]; + + sprintf(old, LV_FS_PC_PATH "/%s", oldname); + sprintf(new, LV_FS_PC_PATH "/%s", newname); + + int r = rename(old, new); + + if(r == 0) return LV_FS_RES_OK; + else return LV_FS_RES_UNKNOWN; +} + +/** + * Get the free and total size of a driver in kB + * @param drv pointer to a driver where this function belongs + * @param letter the driver letter + * @param total_p pointer to store the total size [kB] + * @param free_p pointer to store the free size [kB] + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_free (lv_fs_drv_t * drv, uint32_t * total_p, uint32_t * free_p) +{ + (void) drv; /*Unused*/ + lv_fs_res_t res = LV_FS_RES_NOT_IMP; + + /* Add your code here*/ + + return res; +} + + +#ifdef WIN32 +static char next_fn[256]; +#endif + +/** + * Initialize a 'fs_read_dir_t' variable for directory reading + * @param drv pointer to a driver where this function belongs + * @param dir_p pointer to a 'fs_read_dir_t' variable + * @param path path to a directory + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_dir_open (lv_fs_drv_t * drv, void * dir_p, const char *path) +{ + (void) drv; /*Unused*/ + dir_t d; +#ifndef WIN32 + /*Make the path relative to the current directory (the projects root folder)*/ + char buf[256]; + sprintf(buf, LV_FS_PC_PATH "/%s", path); + if ((d = opendir(buf)) == NULL) { + return LV_FS_RES_FS_ERR; + } else { + /* 'dir_p' is pointer to a file descriptor and + * we need to store our file descriptor here*/ + dir_t * dp = dir_p; /*Just avoid the confusing casings*/ + *dp = d; + } +#else + d = INVALID_HANDLE_VALUE; + WIN32_FIND_DATA fdata; + + /*Make the path relative to the current directory (the projects root folder)*/ + char buf[256]; + sprintf(buf, LV_FS_PC_PATH "\\%s\\*", path); + + strcpy(next_fn, ""); + d = FindFirstFile(buf, &fdata); + do { + if (strcmp(fdata.cFileName, ".") == 0 || strcmp(fdata.cFileName, "..") == 0) { + continue; + } else { + + if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + sprintf(next_fn, "/%s", fdata.cFileName); + } else { + sprintf(next_fn, "%s", fdata.cFileName); + } + break; + } + } while(FindNextFileA(d, &fdata)); + + dir_t * dp = dir_p; /*Just avoid the confusing casings*/ + *dp = d; + +#endif + + return LV_FS_RES_OK; +} + +/** + * Read the next filename form a directory. + * The name of the directories will begin with '/' + * @param drv pointer to a driver where this function belongs + * @param dir_p pointer to an initialized 'fs_read_dir_t' variable + * @param fn pointer to a buffer to store the filename + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_dir_read (lv_fs_drv_t * drv, void * dir_p, char *fn) +{ + (void) drv; /*Unused*/ + dir_t * dp = dir_p; /*Just avoid the confusing casings*/ + +#ifndef WIN32 + struct dirent *entry; + do { + entry = readdir(*dp); + + 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); +#else + strcpy(fn, next_fn); + + strcpy(next_fn, ""); + WIN32_FIND_DATA fdata; + + if(FindNextFile(*dp, &fdata) == false) return LV_FS_RES_OK; + do { + if (strcmp(fdata.cFileName, ".") == 0 || strcmp(fdata.cFileName, "..") == 0) { + continue; + } else { + + if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + sprintf(next_fn, "/%s", fdata.cFileName); + } else { + sprintf(next_fn, "%s", fdata.cFileName); + } + break; + } + } while(FindNextFile(*dp, &fdata)); + +#endif + return LV_FS_RES_OK; +} + +/** + * Close the directory reading + * @param drv pointer to a driver where this function belongs + * @param dir_p pointer to an initialized 'fs_read_dir_t' variable + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_dir_close (lv_fs_drv_t * drv, void * dir_p) +{ + (void) drv; /*Unused*/ + dir_t * dp = dir_p; +#ifndef WIN32 + closedir(*dp); +#else + FindClose(*dp); + *dp = INVALID_HANDLE_VALUE; +#endif + return LV_FS_RES_OK; +} + +#endif /*LV_USE_FS_IF*/ +#endif /*LV_FS_IF_FATFS*/ diff --git a/lib/lv_fs_if/lv_fs_spiffs.cpp b/lib/lv_fs_if/lv_fs_spiffs.cpp new file mode 100644 index 00000000..c3670293 --- /dev/null +++ b/lib/lv_fs_if/lv_fs_spiffs.cpp @@ -0,0 +1,429 @@ +/** + * @file lv_fs_spiffs.c + * + */ + +/********************* + * INCLUDES + *********************/ +//#include +#include "lv_fs_if.h" +#include "lv_fs_spiffs.h" + +#if LV_USE_FS_IF +#if LV_FS_IF_SPIFFS != '\0' + +#if defined(ARDUINO_ARCH_ESP32) +#include "SPIFFS.h" +#endif +#include "FS.h" // Include the SPIFFS library + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/* Create a type to store the required data about your file.*/ +typedef File lv_spiffs_file_t; + +/*Similarly to `file_t` create a type for directory reading too */ +typedef File lv_spiffs_dir_t; + +/********************** + * STATIC PROTOTYPES + **********************/ +static void fs_init(void); + +static lv_fs_res_t fs_open(lv_fs_drv_t * drv, void * file_p, const char * path, lv_fs_mode_t mode); +static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p); +static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br); +static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw); +static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos); +static lv_fs_res_t fs_size(lv_fs_drv_t * drv, void * file_p, uint32_t * size_p); +static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p); +static lv_fs_res_t fs_remove(lv_fs_drv_t * drv, const char * path); +static lv_fs_res_t fs_trunc(lv_fs_drv_t * drv, void * file_p); +static lv_fs_res_t fs_rename(lv_fs_drv_t * drv, const char * oldname, const char * newname); +static lv_fs_res_t fs_free(lv_fs_drv_t * drv, uint32_t * total_p, uint32_t * free_p); +static lv_fs_res_t fs_dir_open(lv_fs_drv_t * drv, void * dir_p, const char * path); +static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn); +static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_fs_if_spiffs_init(void) +{ + /*---------------------------------------------------- + * Initialize your storage device and File System + * -------------------------------------------------*/ + fs_init(); + + /*--------------------------------------------------- + * Register the file system interface in LittlevGL + *--------------------------------------------------*/ + + /* Add a simple drive to open images */ + lv_fs_drv_t fs_drv; /*A driver descriptor*/ + lv_fs_drv_init(&fs_drv); + + /*Set up fields...*/ + fs_drv.file_size = sizeof(lv_spiffs_file_t); + fs_drv.letter = LV_FS_IF_SPIFFS; + 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(lv_spiffs_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; + + lv_fs_drv_register(&fs_drv); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/* Initialize your Storage device and File system. */ +static void fs_init(void) +{ + SPIFFS.begin(); +} + +/** + * Open a file + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable + * @param path path to the file beginning with the driver letter (e.g. S:/folder/file.txt) + * @param mode read: FS_MODE_RD, write: FS_MODE_WR, both: FS_MODE_RD | FS_MODE_WR + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_open(lv_fs_drv_t * drv, void * file_p, const char * path, lv_fs_mode_t mode) +{ + Serial.print("Opening: "); + Serial.println(path); + + String fullpath = "/" + String(path); + Serial.println(fullpath); + + const char * fullfilename = fullpath.c_str(); + Serial.println(fullfilename); + Serial.flush(); + + if(!SPIFFS.exists(fullfilename)) { + Serial.println("File does not exist"); + return LV_FS_RES_NOT_EX; + } + // return LV_FS_RES_UNKNOWN; + + static lv_spiffs_file_t file; + if(mode == LV_FS_MODE_WR) + file = SPIFFS.open(fullfilename, "a"); + else if(mode == LV_FS_MODE_RD) + file = SPIFFS.open(fullfilename, "r"); + else if(mode == (LV_FS_MODE_WR | LV_FS_MODE_RD)) + file = SPIFFS.open(fullfilename, "r+"); + + if(file) { + + lv_spiffs_file_t * fp = (lv_spiffs_file_t *)file_p; /*Just avoid the confusing casings*/ + *fp = file; + + Serial.println("Opened OK"); + Serial.flush(); + // return LV_FS_RES_UNKNOWN; + + return LV_FS_RES_OK; + } else { + Serial.println("Open failed"); + return LV_FS_RES_UNKNOWN; + } +} + +/** + * Close an opened file + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable. (opened with lv_ufs_open) + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_close(lv_fs_drv_t * drv, void * file_p) +{ + // File file = *(File *)(file_p); + lv_spiffs_file_t * file = (lv_spiffs_file_t *)file_p; /*Convert type*/ + + char * msg; + sprintf(msg, "Closing: %s", file->name()); + Serial.println(msg); + Serial.flush(); + file->close(); + + return LV_FS_RES_OK; +} + +/** + * Read data from an opened file + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable. + * @param buf pointer to a memory block where to store the read data + * @param btr number of Bytes To Read + * @param br the real number of read bytes (Byte Read) + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_read(lv_fs_drv_t * drv, void * file_p, void * buf, uint32_t btr, uint32_t * br) +{ + lv_spiffs_file_t * file = (lv_spiffs_file_t *)file_p; /*Convert type*/ + // lv_spiffs_file_t file = *(lv_spiffs_file_t *)(file_p); + + char * msg; + sprintf(msg, "Reading: %s", file->name()); + Serial.println(msg); + Serial.flush(); + + if(!*file || file->isDirectory()) { + return LV_FS_RES_UNKNOWN; + } + + return LV_FS_RES_UNKNOWN; + *br = file->readBytes((char *)buf, btr); + return LV_FS_RES_OK; +} + +/** + * Write into a file + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable + * @param buf pointer to a buffer with the bytes to write + * @param btr Bytes To Write + * @param br the number of real written bytes (Bytes Written). NULL if unused. + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_write(lv_fs_drv_t * drv, void * file_p, const void * buf, uint32_t btw, uint32_t * bw) +{ + return LV_FS_RES_NOT_IMP; + /* + File file = SPIFFS.open((char *)file_p, "w"); + if(!file) { + return LV_FS_RES_UNKNOWN; + } + char * message; + strncpy(buf, message, btw); + if(file.print(message)) { + *bw = (uint32_t)sizeof(message); + return LV_FS_RES_OK; + } else { + bw = 0; + return LV_FS_RES_UNKNOWN; + }*/ +} + +/** + * Set the read write pointer. Also expand the file size if necessary. + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable. (opened with lv_ufs_open ) + * @param pos the new position of read write pointer + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_seek(lv_fs_drv_t * drv, void * file_p, uint32_t pos) +{ + lv_spiffs_file_t * file = (lv_spiffs_file_t *)file_p; /*Convert type*/ + // lv_spiffs_file_t file = *(lv_spiffs_file_t *)(file_p); + char * msg; + sprintf(msg, "Seeking: %s", file->name()); + Serial.println(msg); + Serial.flush(); + + if(file->seek(pos, SeekSet)) { + return LV_FS_RES_OK; + } else { + return LV_FS_RES_UNKNOWN; + } +} + +/** + * Give the size of a file bytes + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable + * @param size pointer to a variable to store the size + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_size(lv_fs_drv_t * drv, void * file_p, uint32_t * size_p) +{ + lv_spiffs_file_t * file = (lv_spiffs_file_t *)file_p; /*Convert type*/ + // lv_spiffs_file_t file = *(lv_spiffs_file_t *)(file_p); + char * msg; + sprintf(msg, "Filesize: %s", file->name()); + Serial.println(msg); + Serial.flush(); + + *size_p = file->size(); + return LV_FS_RES_OK; +} + +/** + * Give the position of the read write pointer + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to a file_t variable. + * @param pos_p pointer to to store the result + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_tell(lv_fs_drv_t * drv, void * file_p, uint32_t * pos_p) +{ + lv_spiffs_file_t * file = (lv_spiffs_file_t *)file_p; /*Convert type*/ + // lv_spiffs_file_t file = *(lv_spiffs_file_t *)(file_p); + char * msg; + sprintf(msg, "Telling: %s", file->name()); + Serial.println(msg); + Serial.flush(); + + uint32_t pos = file->position(); + pos_p = &pos; + return LV_FS_RES_OK; +} + +/** + * Delete a file + * @param drv pointer to a driver where this function belongs + * @param path path of the file to delete + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_remove(lv_fs_drv_t * drv, const char * path) +{ + Serial.println("Deleteing: " + (String)path); + Serial.flush(); + + if(SPIFFS.remove(path)) { + return LV_FS_RES_OK; + } else { + return LV_FS_RES_UNKNOWN; + } +} + +/** + * Truncate the file size to the current position of the read write pointer + * @param drv pointer to a driver where this function belongs + * @param file_p pointer to an 'ufs_file_t' variable. (opened with lv_fs_open ) + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_trunc(lv_fs_drv_t * drv, void * file_p) +{ + return LV_FS_RES_NOT_IMP; +} + +/** + * Rename a file + * @param drv pointer to a driver where this function belongs + * @param oldname path to the file + * @param newname path with the new name + * @return LV_FS_RES_OK or any error from 'fs_res_t' + */ +static lv_fs_res_t fs_rename(lv_fs_drv_t * drv, const char * oldname, const char * newname) +{ + if(SPIFFS.rename(oldname, newname)) { + return LV_FS_RES_OK; + } else { + return LV_FS_RES_UNKNOWN; + } +} + +/** + * Get the free and total size of a driver in kB + * @param drv pointer to a driver where this function belongs + * @param letter the driver letter + * @param total_p pointer to store the total size [kB] + * @param free_p pointer to store the free size [kB] + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +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; + + /* Add your code here*/ + + return res; +} + +/** + * Initialize a 'fs_read_dir_t' variable for directory reading + * @param drv pointer to a driver where this function belongs + * @param dir_p pointer to a 'fs_read_dir_t' variable + * @param path path to a directory + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_dir_open(lv_fs_drv_t * drv, void * dir_p, const char * path) +{ + lv_spiffs_dir_t * dir = (lv_spiffs_file_t *)dir_p; /*Convert type*/ + + Serial.print("Open directory: "); + Serial.println(path); + Serial.flush(); + + *dir = SPIFFS.open(path, "r"); + if(*dir) { + dir_p = &dir; + return LV_FS_RES_OK; + } else { + return LV_FS_RES_UNKNOWN; + } +} + +/** + * Read the next filename form a directory. + * The name of the directories will begin with '/' + * @param drv pointer to a driver where this function belongs + * @param dir_p pointer to an initialized 'fs_read_dir_t' variable + * @param fn pointer to a buffer to store the filename + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_dir_read(lv_fs_drv_t * drv, void * dir_p, char * fn) +{ + lv_spiffs_dir_t * dir = (lv_spiffs_file_t *)dir_p; /*Convert type*/ + lv_spiffs_file_t file = dir->openNextFile(); + + if(file) { + strcpy(fn, file.name()); + return LV_FS_RES_OK; + } else { + return LV_FS_RES_UNKNOWN; + } +} + +/** + * Close the directory reading + * @param drv pointer to a driver where this function belongs + * @param dir_p pointer to an initialized 'fs_read_dir_t' variable + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +static lv_fs_res_t fs_dir_close(lv_fs_drv_t * drv, void * dir_p) +{ + return LV_FS_RES_OK; +} + +#endif /*LV_USE_FS_IF*/ +#endif /*LV_FS_IF_SPIFFS*/ diff --git a/lib/lv_fs_if/lv_fs_spiffs.h b/lib/lv_fs_if/lv_fs_spiffs.h new file mode 100644 index 00000000..38ed9f1a --- /dev/null +++ b/lib/lv_fs_if/lv_fs_spiffs.h @@ -0,0 +1,19 @@ +/** + * @file lv_fs_if.h + * + */ + +#ifndef LV_FS_IF_SPIFFS_H +#define LV_FS_IF_SPIFFS_H + +#ifdef __cplusplus +extern "C" { +#endif + +void lv_fs_if_spiffs_init(void); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif \ No newline at end of file