How to define LittleFS partition, build image and flash it on ESP32?

Working on an ESP32 platform, SPIFFS is not workable for our needs and we want to use LittleFS as the virtual file system for our internal Flash memory. I can’t find information how I would have to define a LittleFS partition in PlatformIO, though I presume it would simply be some custom type.
Also, are there any good tools to create BIN images for LittleFS that PlatformIO can use and how would I tell PIO to use that tool and then flash the respective BIN file to the device?

I can’t seem to find any documentation that explains the path to set up custom partitions, their image creation and upload in PIO, so any suggestions or pointers would be really appreciated.

1 Like

So, I got a custom Python script to build my LittleFS partition image and I can upload it to the device using esptool, after all.
I’m still trying to figure out how to get PlatformIO to call the respective custom scripts, though, to build my image instead of a SPIFFS image when I select “Upload File System Image” and flash it to the device in the respective partition. Thoughts, anyone?

For all those interested, I solved the problem. I am essentially creating a SPIFFS partition and load my own LittleFS image into it instead, fooling PlatformIO into thinking, it’s actually dealing with a SPIFFS image. I am doing this by overloading the MKSPIFFS command.

I created a Python script that I call from platformio.ini

extra_scripts = LittleFSBuilder.py

In this file I replace the MKSPIFFS tool command with my own like this

Import("env")
env.Replace( MKSPIFFSTOOL='python3" "' + env.get("PROJECT_DIR") + '/mklittlefs.py' )

My own tool now uses the command line arguments, builds the LittleFS bin image accordingly and saves it to where PlatformIO tells it to (the last argument in the mkspiffs call is the destination file name).

PlatformIO now believes it has actually created a SPIFFS.BIN file and proceeds to upload it to the device. Done!

3 Likes

Nice! I’ll add an issue to the github mentioning this, as LittleFS is missing from PIO at the moment as an FS option, for both ESP8266 and ESP32. Would you be willing to share your mklittlefs.py script also?

Edit: Er… now I’m confused… the ESP8266 core has LittleFS support as of 2.6.0, but not the ESP32 yet? Are you using a third party like esp32_littleflash or something?

ESP8266: support littlefs for upcoming esp8266 arduino core release · Issue #173 · platformio/platform-espressif8266 · GitHub

I wrote my own Flash layer for LittleFS (GitHub - littlefs-project/littlefs: A little fail-safe filesystem designed for microcontrollers) but the process is the same. Get info about the partition to determine the size, etc and use that info to initialize your LittleFS file system with. Couldn’t be simpler.

As for the mklittlefs.py script, you can find it below. The file copying is a bit awkward but shutil did not work for some reason and locked up, so I decided to really read each file into a buffer instead and save it out from there.

#
# This is a small tool to read files from the PlatformIO data directory and write
# them to a flashable BIN image in the LittleFS file format
#
# This script requires the LittleFS-Python mopackage
# https://pypi.org/project/littlefs-python/
#
#
# Author: Guido Henkel
# Date: 1/2/2020
#
import getopt, sys
from littlefs import LittleFS
from pathlib import Path


print( "Building LittleFS image…" )

argList = sys.argv[1:]
arxx = { argList[i]: argList[i+1] for i in range(0, len(argList)-1, 2) }
print( arxx )

dataPath = arxx["-c"]                                                               # Grab the source path parameter

fileList = [pth for pth in Path(dataPath).iterdir() if pth.stem[0] != '.']          # Create a list of all the files in the directory

fs = LittleFS(block_size=4096, block_count=3072)                                    # Open LittleFS, create a 12MB partition

for curFile in fileList:
    print( "Adding " + curFile.name )
    with open( curFile, 'rb' ) as f:                                                # Read the file into a buffer
        data = f.read()

    with fs.open( curFile.name, 'w') as fh:                                         # Write the file to the LittleFS partition
        fh.write( data )

outName = argList[-1]                                                               # The last argument is the destination file name
with open(outName, 'wb') as fh:                                                     # Dump the filesystem content to a file
    fh.write(fs.context.buffer)
1 Like

Hi,
where exactly do you put these scripts (LittleFSBuilder.py and mklittlefs.py)?

Sorry about the silly question, but I haven’t worked with scripts in Platformio…

Thank you!!!

1 Like

Anywhere it can be accessed! :wink: Keep in mind you need to know where it goes when you add the extra_scripts line in your platformio… so you probably want it in the root of the project folder, or in a extras subfolder… etc.

Thanks for the quick reply. However, I still cannot get it to work…:

So, I put the two files in the root, and trying to upload the FS, I get the following error message:

Processing esp12e (platform: espressif8266; framework: arduino; board: esp12e)
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/espressif8266/esp12e.html
PLATFORM: Espressif 8266 2.5.1 > Espressif ESP8266 ESP-12E
HARDWARE: ESP8266 80MHz, 80KB RAM, 4MB Flash
PACKAGES:
 - framework-arduinoespressif8266 3.20701.0 (2.7.1)
 - tool-esptool 1.413.0 (4.13)
 - tool-esptoolpy 1.20800.0 (2.8.0)
 - tool-mkspiffs 1.200.0 (2.0)
 - toolchain-xtensa 2.40802.200502 (4.8.2)
LDF: Library Dependency Finder -> http://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 36 compatible libraries
Scanning dependencies...
Dependency Graph
|-- <ArduinoJson> 6.15.1
|-- <Time> 1.6
|-- <Ticker> 1.0
|-- <Timezone> 1.2.4
|   |-- <Time> 1.6
|-- <PCF8574_ESP> 1.0.10
|   |-- <Wire> 1.0
|-- <EspSoftwareSerial> 6.8.1
|-- <TinyGPSPlus> 1.0.2
|-- <AccelStepper> 1.59
|-- <ESP8266WiFi> 1.0
|-- <ArduinoOTA> 1.0
|   |-- <ESP8266WiFi> 1.0
|   |-- <ESP8266mDNS> 1.2
|   |   |-- <ESP8266WiFi> 1.0
|-- <EEPROM> 1.0
|-- <ESP8266mDNS> 1.2
|   |-- <ESP8266WiFi> 1.0
|-- <ESP8266WebServer> 1.0
|   |-- <ESP8266WiFi> 1.0
|-- <LittleFS(esp8266)> 0.1.0
|-- <PubSubClient> 1.99.1
|-- <SD(esp8266)> 2.0.0
|   |-- <SDFS> 0.1.0
|   |   |-- <ESP8266SdFat> 1.1.0
|   |   |   |-- <SPI> 1.0
|   |   |-- <SPI> 1.0
|-- <SPI> 1.0
|-- <Wire> 1.0
Building in release mode
Building SPIFFS image from 'data' directory to .pio\build\esp12e\spiffs.bin
Python was not found but can be installed from the Microsoft Store: https://go.microsoft.com/fwlink?linkID=2082640scons: *** [.pio\build\esp12e\spiffs.bin] Error 9009
========================================================================================= [FAILED] Took 1.52 seconds =========================================================================================
The terminal process terminated with exit code: 1

Of course, Python IS installed on my system… It is also in my (user) path variable:

python-env

Also, in Visual Studio Code, in the terminal I can access it normally:

Any idea what I might be missing? This is driving me crazy…

BTW: I am using a Windows 10 Pro, if it makes any difference…

What’s the exact content of your platformio.ini?

platformio.ini:

[env:esp12e]
platform = espressif8266
framework = arduino
board = esp12e

extra_scripts = LittleFSBuilder.py

lib_deps =
            ArduinoJson
            Time
            Ticker
            Timezone
            PCF8574_ESP
            EspSoftwareSerial
            1655                #TinyGPSPlus
            AccelStepper

upload_protocol = espota
upload_port = 192.168.1.201

And this is how the project folder looks:
project-struct

But does it also work via python3? If the content of your LittleFSBuild.py is the same from the post above,

Import("env")
env.Replace( MKSPIFFSTOOL='python3" "' + env.get("PROJECT_DIR") + '/mklittlefs.py' )

the script will invoke python3, not python… Though I don’t know why the double quotes are exactly this way, you might have to delete them so that it results in python3 <project_dir>/mklittefs.py (or put the whole path to the script in quotes in case the path contains spaces)

1 Like

I tried the proposed option, but the resulting image (I guess) is not compatible with LittleFSv2 in Arduino.
What worked for me is a similar substitution:

Import (“env”)
env.Replace (MKSPIFFSTOOL = env.get (“PROJECT_DIR”) + ‘/mklittlefs.exe’)

but with - it’s parameters compatible with mkspiffstool so it’s just work

1 Like

I changed python3 to python in the script as you suggested, and now it seems to be running OK, but I get an error message saying that there is not enough space…:

> Executing task in folder stepper-test: C:\Users\Viktor\.platformio\penv\Scripts\platformio.exe run --target uploadfs <


Processing esp12e (platform: espressif8266; framework: arduino; board: esp12e)
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/espressif8266/esp12e.html
PLATFORM: Espressif 8266 2.5.1 > Espressif ESP8266 ESP-12E
HARDWARE: ESP8266 80MHz, 80KB RAM, 4MB Flash
PACKAGES:
 - framework-arduinoespressif8266 3.20701.0 (2.7.1)
 - tool-esptool 1.413.0 (4.13)
 - tool-esptoolpy 1.20800.0 (2.8.0)
 - tool-mkspiffs 1.200.0 (2.0)
 - toolchain-xtensa 2.40802.200502 (4.8.2)
LDF: Library Dependency Finder -> http://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 36 compatible libraries
Scanning dependencies...
Dependency Graph
|-- <ArduinoJson> 6.15.1
|-- <Time> 1.6
|-- <Ticker> 1.0
|-- <Timezone> 1.2.4
|   |-- <Time> 1.6
|-- <PCF8574_ESP> 1.0.10
|   |-- <Wire> 1.0
|-- <EspSoftwareSerial> 6.8.1
|-- <TinyGPSPlus> 1.0.2
|-- <AccelStepper> 1.59
|-- <ESP8266WiFi> 1.0
|-- <ArduinoOTA> 1.0
|   |-- <ESP8266WiFi> 1.0
|   |-- <ESP8266mDNS> 1.2
|   |   |-- <ESP8266WiFi> 1.0
|-- <EEPROM> 1.0
|-- <ESP8266mDNS> 1.2
|   |-- <ESP8266WiFi> 1.0
|-- <ESP8266WebServer> 1.0
|   |-- <ESP8266WiFi> 1.0
|-- <LittleFS(esp8266)> 0.1.0
|-- <PubSubClient> 1.99.1
|-- <SD(esp8266)> 2.0.0
|   |-- <SDFS> 0.1.0
|   |   |-- <ESP8266SdFat> 1.1.0
|   |   |   |-- <SPI> 1.0
|   |   |-- <SPI> 1.0
|-- <SPI> 1.0
|-- <Wire> 1.0
Building in release mode
Building SPIFFS image from 'data' directory to .pio\build\esp12e\spiffs.bin
Building LittleFS image…
{'-c': 'data', '-p': '256', '-b': '8192', '-s': '1024000'}
Adding badrequest.html
Adding favicon.ico
Adding generalsettings.html
Adding index.html
Adding login.html
Adding networksettings.html
Adding pageheader.html
Adding sensors.html
Adding status.html
Adding tools.html
Uploading .pio\build\esp12e\spiffs.bin
11:11:49 [DEBUG]: Options: {'esp_ip': '192.168.1.201', 'host_ip': '0.0.0.0', 'esp_port': 8266, 'host_port': 57875, 'auth': '', 'image': '.pio\\build\\esp12e\\spiffs.bin', 'spiffs': True, 'debug': True, 'progress': True}
11:11:49 [INFO]: Starting on 0.0.0.0:57875
11:11:49 [INFO]: Upload size: 12582912
11:11:49 [INFO]: Sending invitation to: 192.168.1.201
11:11:49 [ERROR]: Bad Answer: ERR: ERROR[4]: Not Enough Space


*** [uploadfs] Error 1
========================================================================================= [FAILED] Took 3.35 seconds =========================================================================================
The terminal process terminated with exit code: 1

Note, that before the conversion to LittleFS the files fit without a problem. The total size of files is around 30 kBytes.

I tried your proposal and it finally went through without a problem. However, none of the web pages (in the data folder) get displayed. This leads me to believe that I haven’t made sufficient changes in my code (for the ESP).

As I read that LittleFS is “drop-in compatible” with SPIFFS, so far I only changed all occurrences of SPIFFS to LittleFS, like this:
fs::File f = LittleFS.open("/pageheader.html", "r");

Is there anything else I need to be aware of?

Worth noting, that any existing code that creates/modifies/reads files on the fly work. The problem is only with the file system I upload.

This is the log of the succeeded upload:

> Executing task in folder stepper-test: C:\Users\Viktor\.platformio\penv\Scripts\platformio.exe run --target uploadfs <

Processing esp12e (platform: espressif8266; framework: arduino; board: esp12e)
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/espressif8266/esp12e.html
PLATFORM: Espressif 8266 2.5.1 > Espressif ESP8266 ESP-12E
HARDWARE: ESP8266 80MHz, 80KB RAM, 4MB Flash
PACKAGES:
 - framework-arduinoespressif8266 3.20701.0 (2.7.1)
 - tool-esptool 1.413.0 (4.13)
 - tool-esptoolpy 1.20800.0 (2.8.0)
 - tool-mkspiffs 1.200.0 (2.0)
 - toolchain-xtensa 2.40802.200502 (4.8.2)
LDF: Library Dependency Finder -> http://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 36 compatible libraries
Scanning dependencies...
Dependency Graph
|-- <ArduinoJson> 6.15.1
|-- <Time> 1.6
|-- <Ticker> 1.0
|-- <Timezone> 1.2.4
|   |-- <Time> 1.6
|-- <PCF8574_ESP> 1.0.10
|   |-- <Wire> 1.0
|-- <EspSoftwareSerial> 6.8.1
|-- <TinyGPSPlus> 1.0.2
|-- <AccelStepper> 1.59
|-- <ESP8266WiFi> 1.0
|-- <ArduinoOTA> 1.0
|   |-- <ESP8266WiFi> 1.0
|   |-- <ESP8266mDNS> 1.2
|   |   |-- <ESP8266WiFi> 1.0
|-- <EEPROM> 1.0
|-- <ESP8266mDNS> 1.2
|   |-- <ESP8266WiFi> 1.0
|-- <ESP8266WebServer> 1.0
|   |-- <ESP8266WiFi> 1.0
|-- <LittleFS(esp8266)> 0.1.0
|-- <PubSubClient> 1.99.1
|-- <SD(esp8266)> 2.0.0
|   |-- <SDFS> 0.1.0
|   |   |-- <ESP8266SdFat> 1.1.0
|   |   |   |-- <SPI> 1.0
|   |   |-- <SPI> 1.0
|-- <SPI> 1.0
|-- <Wire> 1.0
Building in release mode
Building SPIFFS image from 'data' directory to .pio\build\esp12e\spiffs.bin
/badrequest.html
/favicon.ico
/generalsettings.html
/index.html
/login.html
/networksettings.html
/pageheader.html
/sensors.html
/status.html
/tools.html
Uploading .pio\build\esp12e\spiffs.bin
14:51:36 [DEBUG]: Options: {'esp_ip': '192.168.1.201', 'host_ip': '0.0.0.0', 'esp_port': 8266, 'host_port': 10654, 'auth': '', 'image': '.pio\\build\\esp12e\\spiffs.bin', 'spiffs': True, 'debug': True, 'progress': True}
14:51:36 [INFO]: Starting on 0.0.0.0:10654
14:51:36 [INFO]: Upload size: 1024000
14:51:36 [INFO]: Sending invitation to: 192.168.1.201
14:51:36 [INFO]: Waiting for device...

Uploading: [                                                            ] 0% 
.
.
.
.

Uploading: [============================================================] 99%
Uploading: [============================================================] 100% Done...


14:51:54 [INFO]: Waiting for result...
14:51:55 [INFO]: Result: OK
======================================================================================== [SUCCESS] Took 20.28 seconds ========================================================================================

Try this (platformio.ini)
platform = GitHub - platformio/platform-espressif8266: Espressif 8266: development platform for PlatformIO
platform_packages = GitHub - esp8266/Arduino: ESP8266 core for Arduino
And i diid’t check ota updates… only by wire, but why it’s should differ… Your log looks right.

ok, so finally I managed to get it to work. Probably last night i was tired, as now, when I tried it it worked for the first try.

At the moment this is the only line added to my platformio.ini:

extra_scripts = ../_common/tools/LittleFS/LittleFSBuilder.py

and LittleFSBuilder.py looks like this:

Import("env")
env.Replace (MKSPIFFSTOOL = "../_common/tools/LittleFs/mklittlefs.exe")

This works.

However, I would still like to find out and understand why the python version doesn’t work:

Import("env")
env.Replace( MKSPIFFSTOOL='python" "' + '../_common/tools/LittleFs/mklittlefs.py' )

It still gives me the error:

12:06:32 [DEBUG]: Options: {'esp_ip': '192.168.1.53', 'host_ip': '0.0.0.0', 'esp_port': 8266, 'host_port': 56868, 'auth': '', 'image': '.pio\\build\\esp12e\\spiffs.bin', 'spiffs': True, 'debug': True, 'progress': True}
12:06:32 [INFO]: Starting on 0.0.0.0:56868
12:06:32 [INFO]: Upload size: 12582912
12:06:32 [INFO]: Sending invitation to: 192.168.1.53
12:06:35 [ERROR]: Bad Answer: ERR: ERROR[4]: Not Enough Space

Thank you for all of you sticking with me!

If you copied it from above the script does…

fs = LittleFS(block_size=4096, block_count=3072)                                    # Open LittleFS, create a 12MB partition

a 12MB partition. What’s the size of your ESP8266 flash? Keep in mind that this thread is with regards to the ESP32 originally.

Well, this makes sense… :slight_smile:

True, I am using an ESP-12F module…

Do you know what block size and count should I use for the ESP12-F?

I reduced the block_count to 250 to make the image the same size as the other method creates, it uploads fine, but the pages are not loadable… (I don’t get any error messages, simply nothing is served to the client). Again, the file system seems to be OK, because my software can read and write a new file.

I’m not an expert on how littlefs-python · PyPI creates the LittleFS exactly and what kind of debugging should be undertaken to find out what’s going on behind the curtain. As a general debugging step I’d recommend trying to unpack / list the contents of the generated spiffs.bin (actually LittelFS) with the tools given above, like mklittlefs. You should also try to mount and enumerate the LittleFS filesystem (there’s got to be some functions which tells you how big the mounted LittleFS partition is and a directory/file crawler enumerating all files).

For reproduction I’d also recommend upload your project (e.g. to github) so that other people can reproduce your project and problem 1:1.

1 Like