nodemcu / nodemcu-firmware

Lua based interactive firmware for ESP8266, ESP8285 and ESP32
https://nodemcu.readthedocs.io
MIT License
7.65k stars 3.12k forks source link

RFD - SD Card support #1321

Closed devsaurus closed 8 years ago

devsaurus commented 8 years ago

I'm currently looking into the bits and pieces that are needed to add support for SD Cards to the NodemMCU firmware. Even though that the topic is quite clear, I'd like to understand the requirements of potential use cases a bit better before starting with code. So please feel free to feed back to my specific questions or update the topic with additional items.

Low level driver

This will be based on SPI mode as I don't feel like venturing into SD protocol topics on undocumented hardware. It's a drawback performance-wise but common use in the Arduino world. A good start for a driver code is SdFat which implements all SD related functions in one file, supporting

Reading and writing raw blocks of the card could be exposed as Lua API, but I don't see a need for that at the moment. Is there any interest in accessing the card without a file system?

File system

Most prominent will be support for FAT16/FAT32 since it allows for simple data exchange with PCs. There appear to be different shades of FAT support available, trading in memory consumption for features. Thus the most interesting items for selecting an fs implementation are lurking here. How important are the following aspects for your use case?

FatFs would cover all the items mentioned above but I'm open for other proposals.

What about other file systems like ext2/3/4?

devyte commented 8 years ago

For what it's worth, here's what would be my usage:

pjsg commented 8 years ago

Large card sizes are a must. creating directories: yes fat32 is good enough.

It would be nice to have an API that was compatible with the SPIFFS API (or at least a way to build a compatible API).

devsaurus commented 8 years ago

I've been working on a prototype during the last days and have basic SD access in place now. While incrementally adding FAT file system functions I get a better view on the Lua API thus it's time for an update and call for feedback.

SPI layer

The user is supposed to initialize SPI upfront. This allows some degree of freedom regarding the system configuration:

SD card access

The SD card is identified by its SS/CS pin and can theoretically coexist with other slaves on the same bus. The driver will temporarily reduce SPI clock to 400 kHz during card initialization and switches back to the user configuration.

File system layer

This is based on ChaN's FatFS R0.12 code.

I see the possibility to have a moderate hierarchical object design while addressing a file or folder is done in a flat way with paths across volumes and directories. This feels more like the traditional approach and avoids artificial mappings between an object tree and FatFS' path definition.

Volume object

Directory object

File object

Info object

BTW I'm developing this on a Wemos D1 Mini with Micro SD shield - perfect match :wink:.

devyte commented 8 years ago

Would it make sense to try to mimic the Lua api a bit closer? What I mean is: Lua 5.1 manual

If not, it's ok, I don't think this is a big deal. It's just that I think it's best to stick to standards where possible :)

devsaurus commented 8 years ago

Thanks for the feedback @devyte.

add file:flush(), if it makes sense

Ok

file:lseek() -> file:seek()

Ok

maybe add file:setvbuf(), if it makes sense

I don't see a way atm to influence FatFS' buffering strategy.

os.remove()/.rename()

unlink() -> rename() is a good point. It's also in line with the spiffs module.

I've updated my previous post accordingly.

devsaurus commented 8 years ago

FAT stores modification time in year/month/day etc. format. Support for correct timestamps requires date&time info to be broken down into these components. My current idea is to provide a callback hook that gets fired when the filesystem needs current time. User is then free to choose how to generate this info. Preferably from rtctime.get(), but this one delivers seconds since epoch - not y/m/d/h/m/s. I've opened #1370 to enable easy conversion from rtctime to FAT compatible format.

devsaurus commented 8 years ago

Pushed a first version to my fatfs branch. Some more clean-up and testing required. Please feel free to feed back with any comments (no docs yet).

marcelstoer commented 8 years ago

User is then free to choose how to generate this info. Preferably from rtctime.get()...

You say "(potentially) dependent modules" and I say #386 😉

devsaurus commented 8 years ago

The filesystem functions can hit a couple of blocking points and will report these as result codes to the Lua wrapper. This information is currently propagated to Lua land and the script is responsible for any error detection. Eg. fatfs.open("file", "r") returns nil instead of the file object in case the file doesn't exist. It could, on the other hand, also throw an error().

Is there a preferred way how to report these errors?

marcelstoer commented 8 years ago

I read somewhere about Lua

The usual practice is to throw errors when the program cannot recover (bad file handle for instance) and signal errors when it can recover (file not found for instance)

Returning nil would be considered signaling errors in this case, right?

To me it also depends on whether there's some kind of "does-file-exist" function available. If clients have the chance to check for a file's existence prior to opening it it may be ok to throw an error if they don't and the file is missing indeed. If clients have no chance to verify first it'd be a bit "unfair" to throw an error.

eku commented 8 years ago

@marcelstoer I agree with you, but this applies only to single-threaded environments. The result of the test may be invalid immediately after the call.

marcelstoer commented 8 years ago

this applies only to single-threaded environments

Yes, and even then there's no 100% guarantee although for all practical purposes it can be assumed.

devsaurus commented 8 years ago

Thanks for the feedback. I tend to stick to the paradigm "throw errors when the program cannot recover and signal errors when it can recover" for the time being.

devsaurus commented 8 years ago

The more this module evolves, the more I get the impression that the whole file system topic should be more generic. The separation between FAT SD cards and SPIFFS flash is something which feels more and more like a suboptimal approach. Unifying both is quite a task in itself, thus I propose the following roadmap:

~~1. Rename this module to ufs - "Unified File System" - and continue with a PR which adds FatFs as the first implementation for ufs.

  1. PR to Integrate SPIFFS into ufs. There are a couple of features that are not supported with the current file API; eg. opening multiple files. This will deprecate file in the long term.
  2. PR to push the unified system from Lua down to the C layer. This opens access to SD card storage for other modules and will finally enable to execute scripts from SD card.~~

The ufs API will increment with moderate, back-ward compatible adjustments along the path, I assume. Let me know if you see any issues with this approach.

eku commented 8 years ago

@devsaurus vfs is a common name for the file system abstraction layer.

marcelstoer commented 8 years ago

Thanks Arnim for the well thought-out plan 👍. However, just as Erik I also think that "Unified File System" is not the correct term. You're a bstracting, g eneralizing or v irtualizing the file system.

devsaurus commented 8 years ago

Thanks @eku and @marcelstoer. vfs seems to be a suitable name.

devsaurus commented 8 years ago

This will deprecate file in the long term.

I've been contemplating this aspect a while now - it just feels wrong from whatever point of view. A better approach IMO is to incrementally enhance the file module itself:

  1. Encapsulate spiffs and fatfs functions by a vfs layer on C level.
  2. Use vfs layer from within the file module.
  3. Add object-oriented methods to file while keeping the existing flat API.
  4. Use vfs layer in all other C sources (mainly the Lua part itself) -> dofile() etc from SD card
  5. Optional: deprecate the flat file API after some time.

The term flat API refers to the module functions that restrict Lua land to 1 open file at a time:

Steps 1.-4. keep full backwards compatibility (with single-file restriction for both spiffs and fatfs). While 3. adds API methods that enable multiple open files and directory handling.