SingleFileDisk allows for exporting a file from the onboard LittleFS filesystem to a PC through an emulated FAT drive when connected. The PC can open and copy the file, as well as delete it, but the PC has no access to the main onboard LittleFS and no actual on-flash FAT structures are used. This is handy for things like data loggers. They can run connected to USB power for some time, and then connected to a PC to dowmload the CSV log recorded. It's almost 2023, allow LFN (long file names) on the emulated USB disk. Reduce the disk buffer size to 64 bytes. The buffer is statically allocated so it's always present, even in non-USB disk mode, meaning all apps will pay the RAM price for it. 64 bytes is slower to read but works and saves ~1/2KB of heap for all apps.
82 lines
3.1 KiB
ReStructuredText
82 lines
3.1 KiB
ReStructuredText
SingleFileDrive
|
|
===============
|
|
|
|
USB drive mode is supported through the ``SingleFileDrive`` class which
|
|
allows the Pico to emulate a FAT-formatted USB stick while preserving the
|
|
onboard LittleFS filesystem. A single file can be exported this way without
|
|
needing to use FAT as the onboard filesystem (FAT is not appropriate for
|
|
flash-based devices without complicated wear leveling because of the update
|
|
frequency of the FAT tables).
|
|
|
|
This emulation is very simple and only allows for the reading of the single
|
|
file, and deleting it.
|
|
|
|
Callbacks, Interrupt Safety, and File Operations
|
|
------------------------------------------------
|
|
|
|
The ``SingleFileDrive`` library allows your application to get a callback
|
|
when a PC attempts to mount or unmount the Pico as a drive. Your app can
|
|
also get a callback if the user attempts to delete the file (but your
|
|
sketch does not actually need to delete the file, it's up to you).
|
|
|
|
Note that when the USB drive is mounted by a PC it is not safe for your
|
|
main sketch to make changes to the LittleFS filesystem or the file being
|
|
exported. So, normally, your ``onPlug`` callback will set a flag letting
|
|
your application know not to touch the filesystem, with the ``onUnplug``
|
|
callback clearing this flag.
|
|
|
|
Also, because the USB port can be connected at any time, it is important
|
|
to disable interrupts using ``noInterrupts()`` before writing to a file
|
|
you will be exporting (and restoring them with ``interrupts()`` afterwards).
|
|
It is also important to ``close()`` the file after each update, or the
|
|
on-flash version the ``SingleFileDrive`` will attempt to export may not be
|
|
up to date causing issues later on.
|
|
|
|
See the included ``DataLoggerUSB`` sketch for an example of working with
|
|
these limitations.
|
|
|
|
Using SingleFileDrive
|
|
---------------------
|
|
|
|
Implementing the drive requires including the header file, starting LittleFS,
|
|
defining your callbacks, and telling the library what file to export. No
|
|
polling or other calls are required outside of your ``setup()``. (Note that
|
|
the callback routines allow for a parameter to be passed to them, but in most
|
|
cases this can be safely ignored.)
|
|
|
|
.. code:: cpp
|
|
|
|
#include <LittleFS.h>
|
|
#include <SingleFileDrive.h>
|
|
|
|
void myPlugCB(uint32_t data) {
|
|
// Tell my app not to write to flash, we're connected
|
|
}
|
|
|
|
void myUnplugCB(uint32_t data) {
|
|
// I can start writing to flash again
|
|
}
|
|
|
|
void myDeleteDB(uint32_t data) {
|
|
// Maybe LittleFS.remove("myfile.txt")? or do nothing
|
|
}
|
|
|
|
void setup() {
|
|
LittleFS.begin();
|
|
singleFileDrive.onPlug(myPlugCB);
|
|
singleFileDrive.onUnplug(myUnplugCB);
|
|
singleFileDrive.onDelete(myDeleteCB);
|
|
singleFileDrive.begin("littlefsfile.csv", "Data Recorder.csv");
|
|
// ... rest of setup ...
|
|
}
|
|
|
|
void loop() {
|
|
// Take some measurements, delay, etc.
|
|
if (okay-to-write) {
|
|
noInterrupts();
|
|
File f = LittleFS.open("littlefsfile.csv", "a");
|
|
f.printf("%d,%d,%d\n", data1, data2, data3);
|
|
f.close();
|
|
interrupts();
|
|
}
|
|
}
|