Raspberry Pi Pico and rp2040 boards: integrated LittleFS filesystem – 2

Spread the love

Raspberry Pi Pico and rp2040 boards: integrated LittleFS filesystem
Raspberry Pi Pico and rp2040 boards: integrated LittleFS filesystem

We continue to explore the rp2040 devices, very interesting microcontrollers. All prototype boards come with integrated SPI Flash. Raspberry Pi selected the LittleFS filesystem to manage this storage, a good compromise between functionality and performance.

Here my selection of rp2040 boards Official Pi Pico - Official Pi Pico W - Waveshare rp2040-zero - WeAct Studio rp2040

LittleFS File System

Raspberry Pi Pico: schematics of SPI Flash memory
Raspberry Pi Pico: schematics of SPI Flash memory

LittleFS is focuses on higher performance and directory support and has reasonable filesystem and per-file overhead (4K minimum file allocation unit).

The LittleFS implementation for the ESP8266 supports filenames of up to 31 characters + terminating zero (i.e. char filename[32]) and as many subdirectories as space permits.

Add files from IDE to LittleFS

This operation without an extension for the Arduino IDE is not so simple, but here we will explain the most straightforward way.

First, you must download the plugin for Arduino IDE here.

Then you must find your Shetckbook folder, so you must go to file –> Preferences; in that screen, you can find at the top of the window the Sketchbook location.

Sketchbook Location, additional file Arduino IDE
Preferences window

Now you must create (if not exist) the folder tools/PicoLittleFS/tool and add the jar file picolittlefs.jar there.

Raspberry Pi Pico: LittleFS Arduino ide plugin
Raspberry Pi Pico: LittleFS Arduino ide plugin

Now restart the IDE and on Tools menu you can find new menu-line Pico LittleFS Data Upload.

Arduino IDE Raspberry Pi Pico rp2040 LittleFS sketch data upload
Arduino IDE Raspberry Pi Pico rp2040 LittleFS sketch data upload

Create data folder

On Arduino IDE, do Ctrl+K to open a file browser on the sketch’s directory.
Create a directory data to put the data you want to upload.

Data folder to upload on file-system
Data folder to upload on file-system

Set the size of LittleFS on Tools --> Flash size this is the size of your Microcontroller filesystem.

Upload your sketch, and then click on Pico LittleFS Data Upload.

Now go to the example sketch to check if all is OK.

Commands

There are some standard commands that you can use with this filesystem

LittreFS.begin()
This method mounts the LittreFS file system and must be called before any other FS APIs are used. Returns true if the file system was mounted successfully; false otherwise.

LittreFS.format()
Formats the file system. Returns true if the formatting was successful.

LittreFS.open(path, mode)
Opens a file. The path should be absolute, starting with a slash (e.g.,/dir/filename.txt). Mode is a string specifying access mode. It can be one of “r,” “w”, and “a”. Meaning of these modes is the same as for fopen C function.
Returns File object. To check whether the file was opened successfully, use the boolean operator.

LittreFS.exists(path)
Returns true if a file with a given path exists; false otherwise.

LittreFS.remove(path): Deletes the file given its absolute path. Returns true if the file was deleted successfully.

LittleFS.rename(pathFrom, pathTo)
Renames file from pathFrom to pathTo. Returns true if the file was renamed successfully. Paths must be absolute.

LittreFS.mkdir(path):
Create a new folder. Returns true if the directory creation succeeded, false otherwise.

LittreFS.rmdir(path):
Remove directory. Returns true if the directory was successfully removed; false otherwise.

LittreFS.info()
Fill all information about the filesystem, and you must pass a structure FSInfo. Returns true if It’s all ok.

struct FSInfo {
    size_t totalBytes;
    size_t usedBytes;
    size_t blockSize;
    size_t pageSize;
    size_t maxOpenFiles;
    size_t maxPathLength;
};

file.seek(offset, mode)
This function behaves like fseek C function. Depending on the value of mode, it moves the current position in a file as follows:

  • if the mode is SeekSet, the position is set to offset bytes from the beginning.
  • if the mode is SeekCur, the current position is moved by offset bytes.
  • if the mode is SeekEnd, the position is set to offset bytes from the end of the file.
  • Returns true if the position was set successfully.

file.position()
Returns the current position inside the file in bytes.

file.size()
Returns file size in bytes.

file.name()
Returns file name as const char*.

file.close()
Close the file.

file.getLastWrite()
time_t time of last write (use the internal time to manage date).

file.getCreationTime()
time_t of creation.

file.isDirectory()
Return if It’s a directory

file.isFile()
Return if It’s a file

file.openNextFile()
Set the next file pointer in the directory.

file.rewindDirectory()
Restart the pointer to the first file of the directory.

dir.isFile()
Returns true if the current file pointed to by the internal iterator is a File.

dir.isDirectory()
Returns true if the current file pointed to by the internal iterator is a Directory.

dir.openFile()
This method takes mode argument, which has the same meaning as for LittleFS.open() function.

dir.fileName()
Return the name of the file currently pointed.

dir.filetime()
Return the last write time of the file currently pointed.

dir.fileCreationTime()
Return the creation time of the file currently pointed.

dir.fileSize()
Return the size of the file currently pointed.

dir.rewind()
Resets the internal pointer to the start of the directory.

dir.next()
Put the internal pointer to the next element of the directory.

Practical examples

Here is a sketch to get info and check all files in your LittleFS.

/*
 *  Raspberry Pi Pico (or generic rp2040)
 *  LittleFS get info, read dir and show all file uploaded
 *  add a data folder to use with Pico LittleFS Data Upload
 *  by Mischianti Renzo <https://mischianti.org>
 *
 *  https://mischianti.org
 *
 */

#include "Arduino.h"
#include "LittleFS.h"

void printDirectory(File dir, int numTabs = 3);

void setup()
{
	Serial.begin(115200);

	while (!Serial) {delay(100);}

	Serial.println(F("Inizializing FS..."));
	if (LittleFS.begin()){
		Serial.println(F("done."));
	}else{
		Serial.println(F("fail."));
	}

	// To format all space in LittleFS
	// LittleFS.format()

	// Get all information of your LittleFS
	FSInfo *info;
	LittleFS.info(*info);

	unsigned int totalBytes = info->totalBytes;
	unsigned int usedBytes = info->usedBytes;
	unsigned int freeBytes  = totalBytes-usedBytes;

	unsigned int maxPath = info->maxPathLength;

	Serial.println("File sistem info.");

	Serial.print("Total space:      ");
	Serial.print(totalBytes);
	Serial.println("byte");

	Serial.print("Total space used: ");
	Serial.print(usedBytes);
	Serial.println("byte");

	Serial.print("Total space free: ");
	Serial.print(freeBytes);
	Serial.println("byte");

	Serial.print("Max path lenght: ");
	Serial.print(maxPath);
	Serial.println("");

	Serial.println();

	// Open dir folder
	File dir = LittleFS.open("/", "r");
	// Cycle all the content
	printDirectory(dir);
}

void loop()
{

}

void printDirectory(File dir, int numTabs) {
  while (true) {

    File entry =  dir.openNextFile();
    if (! entry) {
      // no more files
      break;
    }
    for (uint8_t i = 0; i < numTabs; i++) {
      Serial.print('\t');
    }
    Serial.print(entry.name());
    if (entry.isDirectory()) {
      Serial.println("/");
      printDirectory(entry, numTabs + 1);
    } else {
      // files have sizes, directories do not
      Serial.print("\t\t");
      Serial.print(entry.size(), DEC);
      Serial.print("\t");
      Serial.println("byte");

    }
    entry.close();
  }
}

The result is:

Inizializing FS...
done.
File sistem info.
Total space:      537140992byte
Total space used: 239byte
Total space free: 537140753byte
Max path lenght: 13107322

			file1.txt		62	byte
			folderTest/
				file2.txt		62	byte
				file3.txt		62	byte

Here is a sketch with more valuable commands, write a string in a file, read all file content, position on the 9 bytes of the file, and read from there the data.

/*
 *  Raspberry Pi Pico (or generic rp2040)
 *  LittleFS write, read and seek file
 *  by Mischianti Renzo <https://mischianti.org>
 *
 *  https://mischianti.org/
 *
 */
#include "Arduino.h"
#include "LittleFS.h"

void setup()
{
  Serial.begin(115200);

  while (!Serial) {delay(100);}

  Serial.println(F("Inizializing FS..."));
  if (LittleFS.begin()){
    Serial.println(F("done."));
  }else{
    Serial.println(F("fail."));
  }

  // To remove previous test
  // LittleFS.remove(F("/testCreate.txt"));

  File testFile = LittleFS.open(F("/testCreate.txt"), "w");

  if (testFile){
    Serial.println("Write file content!");
    testFile.print("Here the test text www.mischianti.org!!");

    testFile.close();
  }else{
    Serial.println("Problem on create file!");
  }

  testFile = LittleFS.open(F("/testCreate.txt"), "r");
  if (testFile){
    Serial.println("Read file content!");
    /**
     * File derivate from Stream so you can use all Stream method
     * readBytes, findUntil, parseInt, println etc
     */
    Serial.println(testFile.readString());
    testFile.close();
  }else{
    Serial.println("Problem on read file!");
  }

  testFile = LittleFS.open(F("/testCreate.txt"), "r");
  if (testFile){
    /**
     * mode is SeekSet, position is set to offset bytes from the beginning.
     * mode is SeekCur, current position is moved by offset bytes.
     * mode is SeekEnd, position is set to offset bytes from the end of the file.
     * Returns true if position was set successfully.
     */
    Serial.println("Position inside the file at 19 byte!");
    testFile.seek(19, SeekSet);

    Serial.println("Read file content!");
    Serial.println(testFile.readString());
    testFile.close();
  }else{
    Serial.println("Problem on read file!");
  }
}

void loop()
{

}

Here is the result of the sketch.

Inizializing FS...
done.
Write file content!
Read file content!
Here the test text www.mischianti.org!!
Position inside the file at 19 byte!
Read file content!
www.mischianti.org!!

Thanks

  1. Raspberry Pi Pico and rp2040 boards: pinout, specs, and Arduino IDE configuration
  2. Raspberry Pi Pico and rp2040 boards: integrated LittleFS filesystem
  3. Raspberry Pi Pico and rp2040 board: ethernet w5500 with plain (HTTP) and SSL (HTTPS) requests
  4. Raspberry Pi Pico and rp2040 boards: WiFiNINA with ESP32 WiFi Co-Processor
  5. Raspberry Pi Pico and rp2040 boards: how to use SD card


Spread the love

4 Responses

  1. Fernando Antonio Camargo says:

    Is it works with Arduino IDE 2.0?
    On tools’ menu, the option “Pico LittleFS Upload” never show up.
    I tried to execute your example, but the LittleFS.begin() always fails.
    Can you help me?

  2. Brad says:

    Great article. I was able to get this to work. I put in about 300K of data files into a 1M LFS partition.

    However, in both your example and in mine, the reported disk space is incorrect. The numbers are too large. (totalBytes, usedBytes)

    The freeBytes number is too small and also makes no sense.

    maxPathLength is the value that seems to make some sense as it’s close to the 1M I allocated for data.

    However, the files are listed correctly along with their size.

    Am I missing something?

Leave a Reply

Your email address will not be published. Required fields are marked *