AppFS¶
The PocketSprite includes a flash filesystem that allows storing multiple ESP32 applications, as well as other files, in the ESP32 flash. This filesystem is called appfs. Because this filesystem has to be compatible with the way the ESP32 loads applications and maps memory, it is somewhat different from other filesystems: while files have names and sizes as you’d expect, accessing them is more akin to accessing the raw flash underneath it and the file access functions mirror the ESP-IDF flash functions more than they look like standard POSIX file access methods.
An appfs can only be stored in a partition in the SPI flash the ESP32 boots from, and thus has a maximum size of 16MiB. The file system has a sector size of 64KiB which lines up with the 64KiB pages of the ESP32 MMU. This, however, also implies that any file stored on the file system occupies at least 64KiB of flash. With this in mind, please store your data in one large file instead of many smaller ones whenever possible
- Some quirks of this approach are:
- Files cannot be expanded or shrunk. The filesize has to be known on creation and cannot be changed afterwards.
- A write to a file can only reset bits (1->0). In order to overwrite existing data, you need to make sure the region you write in is erased, and if not, erase it first. Note that erasing can only happen on a 4K block.
- The appfs on the PocketSprite is 15.3 MiB in size. With a minimum file size of 64K, this means at maximum there can be 245 files stored in the flash.
Note: PocketSprite software not running on PocketSprite hardware does not have an AppFS filesystem available.
Functions¶
-
esp_err_t
appfsInit
(int type, int subtype)¶ Initialize the appfs code and mount the appfs partition.
Run this before using any of the other AppFs APIs
- Return
- ESP_OK if all OK, an error from the underlying partition or flash code otherwise.
- Parameters
type
: Partition type. Normally you’d pass APPFS_PART_TYPE here.subtype
: Partition subtype. Normally you’d pass APPFS_PART_SUBTYPE here.
-
int
appfsExists
(const char *filename)¶ Check if a file with the given filename exists.
- Return
- 1 if a file with a name which exactly matches filename exists; 0 otherwise.
- Parameters
filename
: Filename to check
-
bool
appfsFdValid
(int fd)¶ Check if a file descriptor is valid.
Because file descriptors are integers which are more-or-less valid over multiple sessions, they can be stored in non-volatile memory and re-used later. When doing this, a sanity check to see if the fd still points at something valid may be useful. This function provides that sanity check.
- Return
- True if fd points to a valid file, false otherwise.
- Parameters
fd
: File descriptor to check
-
appfs_handle_t
appfsOpen
(const char *filename)¶ Open a file on a mounted appfs.
- Return
- The filedescriptor if succesful, APPFS_INVALID_FD if not.
- Parameters
filename
: Filename of the file to open
-
void
appfsClose
(appfs_handle_t handle)¶ Close a file on a mounted appfs.
- Note
- In the current appfs implementation, this is a no-op. This may change in the future, however.
- Parameters
handle
: File descriptor to close
-
esp_err_t
appfsDeleteFile
(const char *filename)¶ Delete a file on the appfs.
- Return
- ESP_OK if file successfully deleted, an error otherwise.
- Parameters
filename
: Name of the file to delete
-
esp_err_t
appfsCreateFile
(const char *filename, size_t size, appfs_handle_t *handle)¶ Create a new file on the appfs.
Initially, the file will have random contents consisting of whatever used the sectors of flash it occupies earlier. Note that this function also opens the file and returns a file descriptor to it if succesful; no need for a separate appfsOpen call.
- Return
- ESP_OK if file successfully deleted, an error otherwise.
- Parameters
filename
: Name of the file to be createdsize
: Size of the file, in byteshandle
: Pointer to an appfs_handle_t which will store the file descriptor of the created file
-
esp_err_t
appfsMmap
(appfs_handle_t fd, size_t offset, size_t len, const void **out_ptr, spi_flash_mmap_memory_t memory, spi_flash_mmap_handle_t *out_handle)¶ Map a file into memory.
This maps a (portion of a) file into memory, where you can access it as if it was an array of bytes in RAM. This uses the MMU and flash cache of the ESP32 to accomplish this effect. The memory is read-only; trying to write to it will cause an exception.
- Return
- ESP_OK if file successfully deleted, an error otherwise.
- Parameters
fd
: File descriptor of the file to map.offset
: Offset into the file where the map startslen
: Lenght of the mapout_ptr
: Pointer to a const void* variable where, if successful, a pointer to the memory is stored.memory
: One of SPI_FLASH_MMAP_DATA or SPI_FLASH_MMAP_INST, where the former does a map to data memory and the latter a map to instruction memory. You’d normally use the first option.out_handle
: Pointer to a spi_flash_mmap_handle_t variable. This variable is needed to later free the map again.
-
void
appfsMunmap
(spi_flash_mmap_handle_t handle)¶ Unmap a previously mmap’ped file.
This unmaps a region previously mapped with appfsMmap
- Parameters
handle
: Handle obtained in the previous appfsMmap call
-
esp_err_t
appfsErase
(appfs_handle_t fd, size_t start, size_t len)¶ Erase a portion of an appfs file.
This sets all bits in the region to be erased to 1, so an appfsWrite can reset selected bits to 0 again.
- Return
- ESP_OK if file successfully deleted, an error otherwise.
- Parameters
fd
: File descriptor of file to erase in.start
: Start offset of file portion to be erased. Must be aligned to 4KiB.len
: Length of file portion to be erased. Must be a multiple of 4KiB.
-
esp_err_t
appfsWrite
(appfs_handle_t fd, size_t start, uint8_t *buf, size_t len)¶ Write to a file in appfs.
Note: Because this maps directly to a write of the underlying flash memory, this call is only able to reset bits in the written area from 1 to 0. If you want to change bits from 0 to 1, call appfsErase on the area to be written before calling this function. This function will return success even if the data in flash is not the same as the data in the buffer due to bits being 1 in the buffer but 0 on flash.
If the above paragraph is confusing, just remember to erase a region before you write to it.
- Return
- ESP_OK if write was successful, an error otherwise.
- Parameters
fd
: File descriptor of file to write tostart
: Offset into file to start writingbuf
: Buffer of bytes to writelen
: Length, in bytes, of data to be written
-
esp_err_t
appfsRead
(appfs_handle_t fd, size_t start, void *buf, size_t len)¶ Read a portion of a file in appfs.
This function reads
len
bytes of file data, starting from offsetstart
, into the bufferbuf
. Note that if you do many reads, it usually is more efficient to map the file into memory and read from it that way instead.- Return
- ESP_OK if read was successful, an error otherwise.
- Parameters
fd
: File descriptor of the filestart
: Offset in the file to start reading frombuf
: Buffer to contain the read datalen
: Length, in bytes, of the data to read
-
esp_err_t
appfsRename
(const char *from, const char *to)¶ Atomically rename a file.
This atomically renames a file. If a file with the target name already exists, it will be deleted. This action is done atomically, so at any point in time, either the original or the new file will fully exist under the target name.
- Return
- ESP_OK if rename was successful, an error otherwise.
- Parameters
from
: Original name of fileto
: Target name of file
-
void
appfsEntryInfo
(appfs_handle_t fd, const char **name, int *size)¶ Get file information.
Given a file descriptor, this returns the name and size of the file. The file descriptor needs to be valid for this to work.
- Parameters
fd
: File descriptorname
: Pointer to a char pointer. This will be pointed at the filename in memory. There is no need to free the pointer memory afterwards. Pointer memory is valid while the file exists / is not deleted. Can be NULL if name information is not wanted.size
: Pointer to an int where the size of the file will be written, or NULL if this information is not wanted.
-
appfs_handle_t
appfsNextEntry
(appfs_handle_t fd)¶ brief Get the next entry in the appfs.
This function can be used to list all the files existing in the appfs. Pass it APPFS_INVALID_FD when calling it for the first time to receive the first file descriptor. Pass it the result of the previous call to get the next file descriptor. When this function returns APPFS_INVALID_FD, all files have been enumerated. You can use
appfsEntryInfo()
to get the file name and size associated with the returned file descriptors- Return
- Next file descriptor, or APPFS_INVALID_FD if all files have been enumerated
- Parameters
fd
: File descriptor returned by previous call, or APPFS_INVALID_FD to get the first file descriptor
-
esp_err_t
appfsGetCurrentApp
(appfs_handle_t *ret_app)¶ Get file descriptor of currently running app.
- Return
- ESP_OK on success, an error when e.g. the currently running code isn’t located in appfs.
- Parameters
ret_app
: Pointer to variable to hold the file descriptor
-
size_t
appfsGetFreeMem
()¶ Get amount of free space in appfs partition.
- Return
- amount of free space, in bytes
-
void
appfsDump
()¶ Debugging function: dump current appfs state.
Prints state of the appfs to stdout.