In this guide, you’ll learn how to write and save data permanently to a file saved on the ESP8266 filesystem (LittleFS). LittleFS is a lightweight filesystem created for microcontrollers that lets you access the flash memory like you would do in a standard file system on your computer, but simple and more limited.
We have a similar tutorial for ESP32 boards: ESP32: Write Data to a File (LittleFS) – Arduino IDE.
Table of Contents
Throughout this tutorial, we’ll cover the following topics:
- Introducing LittleFS
- ESP8266 with LittleFS – Handling Files and Folders
- ESP8266 with LittleFS – How to Save Variables’ Values to a File
- ESP8266 with LittleFS – Check if a file already exists
Prerequisites
We’ll program the ESP8266 board using Arduino IDE. So, make sure you have the ESP8266 add-on installed. Follow the next tutorial if you haven’t already:
Additionally, make sure you’re running the latest version of the ESP8266 add-on. Go to Tools > Board > Boards Manager, search for ESP8266, and check that you’re running the latest version.
Introducing LittleFS
LittleFS is a lightweight filesystem created for microcontrollers that lets you access the flash memory like you would do in a standard file system on your computer, but it’s simpler and more limited. You can read, write, close, and delete files and folders. Using a filesystem with the ESP8266 boards is especially useful to:
- Create configuration files with settings;
- Save data permanently;
- Create files to save small amounts of data instead of using a microSD card;
- Save HTML, CSS, and JavaScript files to build a web server;
- Save images, figures, and icons;
- And much more.
You may also like reading: Install ESP8266 NodeMCU LittleFS Filesystem Uploader in Arduino IDE.
ESP8266 with LittleFS – Handling Files and Folders
Before showing you how to write data to a file on LittleFS with the ESP8266, let’s take a look at an example that shows how to do practically any task that you may need when dealing with files using LittleFS.
This is the official example from the ESP8266 core.
/* Example showing timestamp support in LittleFS - Released into the public domain.
Earle F. Philhower, III <[email protected]> Official example: https://github.com/esp8266/Arduino/blob/master/libraries/LittleFS/examples/LittleFS_Timestamp/LittleFS_Timestamp.ino
*/
// Project details: https://RandomNerdTutorials.com/esp8266-nodemcu-write-data-littlefs-arduino/
#include <FS.h>
#include <LittleFS.h>
#include <time.h>
#include <ESP8266WiFi.h>
#ifndef STASSID
#define STASSID "REPLACE_WITH_YOUR_SSID"
#define STAPSK "REPLACE_WITH_YOUR_PASSWORD"
#endif
const char *ssid = STASSID;
const char *pass = STAPSK;
long timezone = 0;
byte daysavetime = 1;
void listDir(const char *dirname) {
Serial.printf("Listing directory: %s\n", dirname);
Dir root = LittleFS.openDir(dirname);
while (root.next()) {
File file = root.openFile("r");
Serial.print(" FILE: ");
Serial.print(root.fileName());
Serial.print(" SIZE: ");
Serial.print(file.size());
time_t cr = file.getCreationTime();
time_t lw = file.getLastWrite();
file.close();
struct tm *tmstruct = localtime(&cr);
Serial.printf(" CREATION: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec);
tmstruct = localtime(&lw);
Serial.printf(" LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec);
}
}
void readFile(const char *path) {
Serial.printf("Reading file: %s\n", path);
File file = LittleFS.open(path, "r");
if (!file) {
Serial.println("Failed to open file for reading");
return;
}
Serial.print("Read from file: ");
while (file.available()) { Serial.write(file.read()); }
file.close();
}
void writeFile(const char *path, const char *message) {
Serial.printf("Writing file: %s\n", path);
File file = LittleFS.open(path, "w");
if (!file) {
Serial.println("Failed to open file for writing");
return;
}
if (file.print(message)) {
Serial.println("File written");
} else {
Serial.println("Write failed");
}
delay(2000); // Make sure the CREATE and LASTWRITE times are different
file.close();
}
void appendFile(const char *path, const char *message) {
Serial.printf("Appending to file: %s\n", path);
File file = LittleFS.open(path, "a");
if (!file) {
Serial.println("Failed to open file for appending");
return;
}
if (file.print(message)) {
Serial.println("Message appended");
} else {
Serial.println("Append failed");
}
file.close();
}
void renameFile(const char *path1, const char *path2) {
Serial.printf("Renaming file %s to %s\n", path1, path2);
if (LittleFS.rename(path1, path2)) {
Serial.println("File renamed");
} else {
Serial.println("Rename failed");
}
}
void deleteFile(const char *path) {
Serial.printf("Deleting file: %s\n", path);
if (LittleFS.remove(path)) {
Serial.println("File deleted");
} else {
Serial.println("Delete failed");
}
}
void setup() {
Serial.begin(115200);
// We start by connecting to a WiFi network
Serial.println();
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, pass);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
Serial.println("Contacting Time Server");
configTime(3600 * timezone, daysavetime * 3600, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org");
struct tm tmstruct;
delay(2000);
tmstruct.tm_year = 0;
getLocalTime(&tmstruct, 5000);
Serial.printf("\nNow is : %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct.tm_year) + 1900, (tmstruct.tm_mon) + 1, tmstruct.tm_mday, tmstruct.tm_hour, tmstruct.tm_min, tmstruct.tm_sec);
Serial.println("");
Serial.println("Formatting LittleFS filesystem");
LittleFS.format();
Serial.println("Mount LittleFS");
if (!LittleFS.begin()) {
Serial.println("LittleFS mount failed");
return;
}
listDir("/");
deleteFile("/hello.txt");
writeFile("/hello.txt", "Hello ");
appendFile("/hello.txt", "World!\n");
listDir("/");
Serial.println("The timestamp should be valid above");
Serial.println("Now unmount and remount and perform the same operation.");
Serial.println("Timestamp should be valid, data should be good.");
LittleFS.end();
Serial.println("Now mount it");
if (!LittleFS.begin()) {
Serial.println("LittleFS mount failed");
return;
}
readFile("/hello.txt");
listDir("/");
}
void loop() {
}
This code covers the following:
- List a directory;
- Read a file’s content;
- Write content to a file;
- Append content to a file;
- Rename a file;
- Delete a file;
- Initialize the LittleFS filesystem.
This particular example also displays when a file was created and what was the last time it was written. So, you’ll need to insert your network credentials so that we can get the current time from the internet.
How the Code Works
First, you need to include the following libraries: FS.h to handle files, LittleFS.h to create and access the filesystem, the time.h to deal with time functions and the ESP8266WiFi.h so that we can connect to the internet to retrieve the current time.
#include <FS.h>
#include <LittleFS.h>
#include <time.h>
#include <ESP8266WiFi.h>
You need to insert your network credentials on the following lines:
#define STASSID "REPLACE_WITH_YOUR_SSID"
#define STAPSK "REPLACE_WITH_YOUR_PASSWORD"
Adjust your timezone in the following parameters. In the timezone variable insert a number accordingly to your timezone in relation to GMT. Additionally, add if your timezone uses daylight saving time.
long timezone = 0;
byte daysavetime = 1;
The example provides several functions to handle files on the LittleFS filesystem. Let’s take a look at them.
List a directory
The listDir() function lists the directories on the filesystem. This function accepts as arguments the directory’s file path. This will output all files and folders inside that path and the date of creation plus the last time they were modified.
void listDir(const char *dirname) {
Serial.printf("Listing directory: %s\n", dirname);
Dir root = LittleFS.openDir(dirname);
while (root.next()) {
File file = root.openFile("r");
Serial.print(" FILE: ");
Serial.print(root.fileName());
Serial.print(" SIZE: ");
Serial.print(file.size());
time_t cr = file.getCreationTime();
time_t lw = file.getLastWrite();
file.close();
struct tm *tmstruct = localtime(&cr);
Serial.printf(" CREATION: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec);
tmstruct = localtime(&lw);
Serial.printf(" LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec);
}
}
Here’s an example of how to call this function. The / corresponds to the root directory. The following command will list all the directories and files in the root directory.
listDir("/");
Read File Content
The readFile() function reads the content of a file and prints the content in the Serial Monitor. Pass as an argument the file path.
void readFile(const char *path) {
Serial.printf("Reading file: %s\n", path);
File file = LittleFS.open(path, "r");
if (!file) {
Serial.println("Failed to open file for reading");
return;
}
Serial.print("Read from file: ");
while (file.available()) { Serial.write(file.read()); }
file.close();
}
For example, the following line reads the content of the hello.txt file.
readFile("/hello.txt");
Write Content to a File
To write content to a file, you can use the writeFile() function. Pass as an argument: the file path, and the message/data (as a const char variable).
void writeFile(const char *path, const char *message) {
Serial.printf("Writing file: %s\n", path);
File file = LittleFS.open(path, "w");
if (!file) {
Serial.println("Failed to open file for writing");
return;
}
if (file.print(message)) {
Serial.println("File written");
} else {
Serial.println("Write failed");
}
delay(2000); // Make sure the CREATE and LASTWRITE times are different
file.close();
}
The following line writes Hello in the hello.txt file.
writeFile("/hello.txt", "Hello ");
Append Content to a File
Similarly, you can append content to a file (without overwriting previous content) using the appendFile() function.
void appendFile(const char *path, const char *message) {
Serial.printf("Appending to file: %s\n", path);
File file = LittleFS.open(path, "a");
if (!file) {
Serial.println("Failed to open file for appending");
return;
}
if (file.print(message)) {
Serial.println("Message appended");
} else {
Serial.println("Append failed");
}
file.close();
}
The following line appends the message World!\r\n in the hello.txt file. The \n means that the next time you write something to the file, it will be written in a new line.
appendFile("/hello.txt", "World!\n");
Rename a File
You can rename a file using the renameFile() function. Pass as arguments the LittleFS filesystem, the original filename, and the new filename.
void renameFile(const char *path1, const char *path2) {
Serial.printf("Renaming file %s to %s\n", path1, path2);
if (LittleFS.rename(path1, path2)) {
Serial.println("File renamed");
} else {
Serial.println("Rename failed");
}
}
The following line renames the hello.txt file to foo.txt.
renameFile("/hello.txt", "/foo.txt");
Delete a File
Use the deleteFile() function to delete a file. Pass as an argument the file path of the file you want to delete.
void deleteFile(const char *path) {
Serial.printf("Deleting file: %s\n", path);
if (LittleFS.remove(path)) {
Serial.println("File deleted");
} else {
Serial.println("Delete failed");
}
}
The following line deletes the hello.txt file from the filesystem.
deleteFile("/hello.txt");
Initialize Wi-Fi and Time
In the setup(), we start by initializing Wi-Fi.
WiFi.begin(ssid, pass);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
After initializing Wi-Fi, we can set up the time.
Serial.println("Contacting Time Server");
configTime(3600 * timezone, daysavetime * 3600, "time.nist.gov", "0.pool.ntp.org", "1.pool.ntp.org");
struct tm tmstruct;
delay(2000);
tmstruct.tm_year = 0;
getLocalTime(&tmstruct, 5000);
Serial.printf("\nNow is : %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct.tm_year) + 1900, (tmstruct.tm_mon) + 1, tmstruct.tm_mday, tmstruct.tm_hour, tmstruct.tm_min, tmstruct.tm_sec);
Serial.println("");
To learn more about time functions with the ESP8266, check the following tutorials:
- ESP8266 NodeMCU NTP Client-Server: Get Date and Time (Arduino IDE)
- Get Epoch/Unix Time with the ESP8266 NodeMCU (Arduino)
Initialize the Filesystem
In the setup(), the following lines initialize the LittleFS filesystem.
if(!LittleFS.begin(FORMAT_LITTLEFS_IF_FAILED)){
Serial.println("LittleFS Mount Failed");
return;
}
The LittleFS.begin() function returns true if the filesystem is initialized successfully or false if it isn’t.
Testing the Filesystem
The following lines call all the functions we’ve seen previously and mount and unmount the filesystem for testing purposes
listDir("/");
deleteFile("/hello.txt");
writeFile("/hello.txt", "Hello ");
appendFile("/hello.txt", "World!\n");
listDir("/");
Serial.println("The timestamp should be valid above");
Serial.println("Now unmount and remount and perform the same operation.");
Serial.println("Timestamp should be valid, data should be good.");
LittleFS.end();
Serial.println("Now mount it");
if (!LittleFS.begin()) {
Serial.println("LittleFS mount failed");
return;
}
readFile("/hello.txt");
listDir("/");
Demonstration
Upload the previous sketch to your ESP8266 board. After that, open the Serial Monitor and press and reset your board. If the initialization succeeds, you’ll get similar messages on the Serial Monitor.
As you can see, when listing files, it shows the time of creation and the last time it was written.
ESP8266 with LittleFS – How to Save Variables’ Values to a File
The previous example illustrated almost all the operations you might need to do when dealing with files on the filesystem. In this section, we’ll take a look at a more simple and specific example: how to save the content of a variable to the filesystem.
Let’s take a look at the following code.
/* Example showing timestamp support in LittleFS - Released into the public domain.
Earle F. Philhower, III <[email protected]> Official example: https://github.com/esp8266/Arduino/blob/master/libraries/LittleFS/examples/LittleFS_Timestamp/LittleFS_Timestamp.ino
*/
// Project details: https://RandomNerdTutorials.com/esp8266-nodemcu-write-data-littlefs-arduino/
#include <FS.h>
#include <LittleFS.h>
int mydata;
void readFile(const char *path) {
Serial.printf("Reading file: %s\n", path);
File file = LittleFS.open(path, "r");
if (!file) {
Serial.println("Failed to open file for reading");
return;
}
Serial.print("Read from file: ");
while (file.available()) { Serial.write(file.read()); }
file.close();
}
void writeFile(const char *path, const char *message) {
Serial.printf("Writing file: %s\n", path);
File file = LittleFS.open(path, "w");
if (!file) {
Serial.println("Failed to open file for writing");
return;
}
if (file.print(message)) {
Serial.println("File written");
} else {
Serial.println("Write failed");
}
delay(2000); // Make sure the CREATE and LASTWRITE times are different
file.close();
}
void appendFile(const char *path, const char *message) {
Serial.printf("Appending to file: %s\n", path);
File file = LittleFS.open(path, "a");
if (!file) {
Serial.println("Failed to open file for appending");
return;
}
if (file.print(message)) {
Serial.println("Message appended");
} else {
Serial.println("Append failed");
}
file.close();
}
void setup() {
Serial.begin(115200);
Serial.println("Mount LittleFS");
if (!LittleFS.begin()) {
Serial.println("LittleFS mount failed");
return;
}
else{
Serial.println("Little FS Mounted Successfully");
}
writeFile("/data.txt", "MY ESP8266 DATA \r\n");
}
void loop() {
mydata = random (0, 1000);
appendFile("/data.txt", (String(mydata)+ "\r\n").c_str()); //Append data to the file
readFile("/data.txt"); // Read the contents of the file
delay(30000);
}
For this example, we’ll continuously save the value of a variable to the filesystem. As an example, we’ll save a random number, but this can be easily adjusted to save sensor readings, for example.
We start by creating a variable that will hold the random number called mydata.
int mydata;
For this particular example, we just need to use the writeFile(), appendFile(), and readFile() functions. So, we have those functions defined before the setup():
void readFile(const char *path) {
Serial.printf("Reading file: %s\n", path);
File file = LittleFS.open(path, "r");
if (!file) {
Serial.println("Failed to open file for reading");
return;
}
Serial.print("Read from file: ");
while (file.available()) { Serial.write(file.read()); }
file.close();
}
void writeFile(const char *path, const char *message) {
Serial.printf("Writing file: %s\n", path);
File file = LittleFS.open(path, "w");
if (!file) {
Serial.println("Failed to open file for writing");
return;
}
if (file.print(message)) {
Serial.println("File written");
} else {
Serial.println("Write failed");
}
delay(2000); // Make sure the CREATE and LASTWRITE times are different
file.close();
}
void appendFile(const char *path, const char *message) {
Serial.printf("Appending to file: %s\n", path);
File file = LittleFS.open(path, "a");
if (!file) {
Serial.println("Failed to open file for appending");
return;
}
if (file.print(message)) {
Serial.println("Message appended");
} else {
Serial.println("Append failed");
}
file.close();
}
In the setup(), we initialize the Serial Monitor for debugging purposes.
Serial.begin(115200);
And we initialize the LittleFS filesystem:
Serial.println("Mount LittleFS");
if (!LittleFS.begin()) {
Serial.println("LittleFS mount failed");
return;
}
else{
Serial.println("Little FS Mounted Successfully");
}
Then, we create a file called data.txt with the following text inside MY ESP8266 DATA:
writeFile("/data.txt", "MY ESP8266 DATA \r\n");
Something to notice about the writeFile() function: it creates a file (if it doesn’t exist) called data.txt with the text we define inside.
If that file already exists, the writeFile() function will overwrite any existing contents inside that file. So, if you want to continuously add new data without replacing it, you should use the appendFile() function after creating the file. If you want to replace the content of the file, you should use the writeFile().
In the loop(), we start by attributing a random value between 0 and 1000 to the mydata variable.
mydata = random (0, 1000);
Then, we append data to the file by calling the appendFile() function.
appendFile("/data.txt", (String(mydata)+ "\r\n").c_str()); // Append data to the file
Notice that we concatenate the mydata variable with “\r\n” so that subsequent data is written on the next line. Because our variable is of int type, we need to convert it to a String before concatenating.
String(mydata)
Additionally, then, we need to convert it to a const char using the c_str() method:
String(mydata)+ "\r\n").c_str()
After appending data to the file, we’ll read its content by calling the readFile() function.
readFile("/data.txt"); // Read the contents of the file
New random values are generated and added to the file every 30 seconds.
delay(30000);
Demonstration
Upload the code to the ESP8266 board. Open the Serial Monitor at a baud rate of 115200.
It should initialize the filesystem, create the file and start adding a new random number to the file every 30 seconds.
Notice that if you restart your board, you’ll lose all your previous data. Why is that happening?
That happens because we’re calling the writeFile() function in the setup(). As we’ve explained previously, it will create a new file if it doesn’t exist, or overwrite an already existing file with the same name. To prevent that, we can add some lines to the setup() to check whether the file already exists.
ESP8266 with LittleFS – Check if a file already exists
To check if a file already exists in the filesystem, we can use the exists() method and pass as an argument the file path. You can add the following lines to the setup() to prevent overwriting when the ESP8266 restarts:
// Check if the file already exists to prevent overwritting existing data
bool fileexists = LittleFS.exists("/data.txt");
Serial.print(fileexists);
if(!fileexists) {
Serial.println("File doesn’t exist");
Serial.println("Creating file...");
// Create File and add header
writeFile("/data.txt", "MY ESP8266 DATA \r\n");
}
else {
Serial.println("File already exists");
}
It uses the exists() method to check if the file already exists:
bool fileexists = LittleFS.exists("/data.txt");
It will return true if the file already exists or false if it doesn’t.
If it doesn’t exist, it will create the file with the content with define.
if(!fileexists) {
Serial.println("File doesn’t exist");
Serial.println("Creating file...");
// Create File and add header
writeFile("/data.txt", "MY ESP8266 DATA \r\n");
}
If it already exists, it simply writes File already exists in the Serial Monitor.
else {
Serial.println("File already exists");
}
Here’s the complete example that checks if the file already exists.
/* Example showing timestamp support in LittleFS - Released into the public domain.
Earle F. Philhower, III <[email protected]> Official example: https://github.com/esp8266/Arduino/blob/master/libraries/LittleFS/examples/LittleFS_Timestamp/LittleFS_Timestamp.ino
*/
// Project details: https://RandomNerdTutorials.com/esp8266-nodemcu-write-data-littlefs-arduino/
#include <FS.h>
#include <LittleFS.h>
int mydata;
void readFile(const char *path) {
Serial.printf("Reading file: %s\n", path);
File file = LittleFS.open(path, "r");
if (!file) {
Serial.println("Failed to open file for reading");
return;
}
Serial.print("Read from file: ");
while (file.available()) { Serial.write(file.read()); }
file.close();
}
void writeFile(const char *path, const char *message) {
Serial.printf("Writing file: %s\n", path);
File file = LittleFS.open(path, "w");
if (!file) {
Serial.println("Failed to open file for writing");
return;
}
if (file.print(message)) {
Serial.println("File written");
} else {
Serial.println("Write failed");
}
delay(2000); // Make sure the CREATE and LASTWRITE times are different
file.close();
}
void appendFile(const char *path, const char *message) {
Serial.printf("Appending to file: %s\n", path);
File file = LittleFS.open(path, "a");
if (!file) {
Serial.println("Failed to open file for appending");
return;
}
if (file.print(message)) {
Serial.println("Message appended");
} else {
Serial.println("Append failed");
}
file.close();
}
void setup() {
Serial.begin(115200);
Serial.println("Mount LittleFS");
if (!LittleFS.begin()) {
Serial.println("LittleFS mount failed");
return;
}
else{
Serial.println("Little FS Mounted Successfully");
}
// Check if the file already exists to prevent overwritting existing data
bool fileexists = LittleFS.exists("/data.txt");
Serial.print(fileexists);
if(!fileexists) {
Serial.println("File doesn’t exist");
Serial.println("Creating file...");
// Create File and add header
writeFile("/data.txt", "MY ESP8266 DATA \r\n");
}
else {
Serial.println("File already exists");
}
}
void loop() {
mydata = random (0, 1000);
appendFile("/data.txt", (String(mydata)+ "\r\n").c_str()); //Append data to the file
readFile("/data.txt"); // Read the contents of the file
delay(30000);
}
If you test this example, you’ll see that the file keeps all data even after a restart.
Wrapping Up
With this tutorial, you learned how to save data permanently on a file in the ESP8266 LittleFS filesystem. You learn how to create a file, append data, and read the contents of a file.
If you have a file with content that you want to save to the ESP8266, and you don’t need to add data during runtime, you may want to use the LittleFS plugin instead. It allows you to save files that you have on your sketch folder directly to the ESP8266 filesystem: Install ESP8266 NodeMCU LittleFS Filesystem Uploader in Arduino IDE
We hope you found this tutorial useful. Learn more about the ESP8266 with our resources:
Muy buen artÃculo lastima que no puedo comprar el libro
Gracias 🙂
Thank you very much Sara for your helpful guide.
I want to ask if it’s possible to limit the text file in size to prevent being out of memory and overwrite the file when it reaches that size?
Thank you in advance
It didn’t work. returns LittleFS mount failed error without me having changed any of the code
Reset the board two to three times and see if it mounts successfully.
Regards,
Sara
I have a new problem now, I use the TTGO LORa32 SX1276 Oled board called in your application TTGO LoRa32 SX1276 OLED Board: Getting Started with Arduino IDE
To get LittleFS running I have to update the Espressif library from version 1.0.6 to the latest version (2.0.13) but now I miss the reference to my board TTGO LORa32 SX1276 Oled
Now I reset the library back to version 1,0,6 to get my project working, but I can’t use LittleFS
Hi.
Check what is the chip of your ESP32 board. You probably can use other more generic board option.
Regards,
Sara
Hi Sara!
How to read variable “mydata” from the spiffs not as a string , but as an integer ?