ESP32: Write Data to a File (LittleFS) – Arduino IDE

In this guide, you’ll learn how to write and save data permanently to a file saved on the ESP32 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.

ESP32 Write Data to a File LittleFS Arduino IDE

We have a similar tutorial for ESP8266 boards: ESP8266 NodeMCU: Write Data to a File (LittleFS) – Arduino IDE.

Table of Contents

Throughout this tutorial, we’ll cover the following topics:

Prerequisites

We’ll program the ESP32 board using Arduino IDE. So, make sure you have the ESP32 add-on installed. Follow the next tutorial if you haven’t already:

Additionally, make sure you’re running the latest version of the ESP32 add-on. Go to Tools > Board > Boards Manager, search for ESP32, 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 ESP32 boards is especially useful to:

You may also like reading: ESP32: Upload Files to LittleFS using Arduino IDE.

ESP32 with LittleFS – Handling Files and Folders

Before showing you how to write data to a file on LittleFS with the ESP32, let’s take a look at an example that shows how to do practically any task that you may need when dealing with files and folders.

The following code was adapted from the official example.

// Adapted from: https://github.com/espressif/arduino-esp32/blob/master/libraries/LittleFS/examples/LITTLEFS_test/LITTLEFS_test.ino
// Project details: https://RandomNerdTutorials.com/esp32-write-data-littlefs-arduino/

#include <Arduino.h>
#include "FS.h"
#include <LittleFS.h>

//  You only need to format LittleFS the first time you run a
//  test or else use the LITTLEFS plugin to create a partition 
//  https://github.com/lorol/arduino-esp32littlefs-plugin

#define FORMAT_LITTLEFS_IF_FAILED true

void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
    Serial.printf("Listing directory: %s\r\n", dirname);

    File root = fs.open(dirname);
    if(!root){
        Serial.println("- failed to open directory");
        return;
    }
    if(!root.isDirectory()){
        Serial.println(" - not a directory");
        return;
    }

    File file = root.openNextFile();
    while(file){
        if(file.isDirectory()){
            Serial.print("  DIR : ");
            Serial.println(file.name());
            if(levels){
                listDir(fs, file.path(), levels -1);
            }
        } else {
            Serial.print("  FILE: ");
            Serial.print(file.name());
            Serial.print("\tSIZE: ");
            Serial.println(file.size());
        }
        file = root.openNextFile();
    }
}

void createDir(fs::FS &fs, const char * path){
    Serial.printf("Creating Dir: %s\n", path);
    if(fs.mkdir(path)){
        Serial.println("Dir created");
    } else {
        Serial.println("mkdir failed");
    }
}

void removeDir(fs::FS &fs, const char * path){
    Serial.printf("Removing Dir: %s\n", path);
    if(fs.rmdir(path)){
        Serial.println("Dir removed");
    } else {
        Serial.println("rmdir failed");
    }
}

void readFile(fs::FS &fs, const char * path){
    Serial.printf("Reading file: %s\r\n", path);

    File file = fs.open(path);
    if(!file || file.isDirectory()){
        Serial.println("- failed to open file for reading");
        return;
    }

    Serial.println("- read from file:");
    while(file.available()){
        Serial.write(file.read());
    }
    file.close();
}

void writeFile(fs::FS &fs, const char * path, const char * message){
    Serial.printf("Writing file: %s\r\n", path);

    File file = fs.open(path, FILE_WRITE);
    if(!file){
        Serial.println("- failed to open file for writing");
        return;
    }
    if(file.print(message)){
        Serial.println("- file written");
    } else {
        Serial.println("- write failed");
    }
    file.close();
}

void appendFile(fs::FS &fs, const char * path, const char * message){
    Serial.printf("Appending to file: %s\r\n", path);

    File file = fs.open(path, FILE_APPEND);
    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(fs::FS &fs, const char * path1, const char * path2){
    Serial.printf("Renaming file %s to %s\r\n", path1, path2);
    if (fs.rename(path1, path2)) {
        Serial.println("- file renamed");
    } else {
        Serial.println("- rename failed");
    }
}

void deleteFile(fs::FS &fs, const char * path){
    Serial.printf("Deleting file: %s\r\n", path);
    if(fs.remove(path)){
        Serial.println("- file deleted");
    } else {
        Serial.println("- delete failed");
    }
}

void testFileIO(fs::FS &fs, const char * path){
    Serial.printf("Testing file I/O with %s\r\n", path);

    static uint8_t buf[512];
    size_t len = 0;
    File file = fs.open(path, FILE_WRITE);
    if(!file){
        Serial.println("- failed to open file for writing");
        return;
    }

    size_t i;
    Serial.print("- writing" );
    uint32_t start = millis();
    for(i=0; i<2048; i++){
        if ((i & 0x001F) == 0x001F){
          Serial.print(".");
        }
        file.write(buf, 512);
    }
    Serial.println("");
    uint32_t end = millis() - start;
    Serial.printf(" - %u bytes written in %u ms\r\n", 2048 * 512, end);
    file.close();

    file = fs.open(path);
    start = millis();
    end = start;
    i = 0;
    if(file && !file.isDirectory()){
        len = file.size();
        size_t flen = len;
        start = millis();
        Serial.print("- reading" );
        while(len){
            size_t toRead = len;
            if(toRead > 512){
                toRead = 512;
            }
            file.read(buf, toRead);
            if ((i++ & 0x001F) == 0x001F){
              Serial.print(".");
            }
            len -= toRead;
        }
        Serial.println("");
        end = millis() - start;
        Serial.printf("- %u bytes read in %u ms\r\n", flen, end);
        file.close();
    } else {
        Serial.println("- failed to open file for reading");
    }
}

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

    if(!LittleFS.begin(FORMAT_LITTLEFS_IF_FAILED)){
        Serial.println("LittleFS Mount Failed");
        return;
    }

    createDir(LittleFS, "/mydir"); // Create a mydir folder
    writeFile(LittleFS, "/mydir/hello1.txt", "Hello1"); // Create a hello1.txt file with the content "Hello1"
    listDir(LittleFS, "/", 1); // List the directories up to one level beginning at the root directory
    deleteFile(LittleFS, "/mydir/hello1.txt"); //delete the previously created file
    removeDir(LittleFS, "/mydir"); //delete the previously created folder
    listDir(LittleFS, "/", 1); // list all directories to make sure they were deleted
    
    writeFile(LittleFS, "/hello.txt", "Hello "); //Create and write a new file in the root directory
    appendFile(LittleFS, "/hello.txt", "World!\r\n"); //Append some text to the previous file
    readFile(LittleFS, "/hello.txt"); // Read the complete file
    renameFile(LittleFS, "/hello.txt", "/foo.txt"); //Rename the previous file
    readFile(LittleFS, "/foo.txt"); //Read the file with the new name
    deleteFile(LittleFS, "/foo.txt"); //Delete the file
    testFileIO(LittleFS, "/test.txt"); //Testin
    deleteFile(LittleFS, "/test.txt"); //Delete the file
  
    Serial.println( "Test complete" ); 
}

void loop(){

}

View raw code

This code covers the following:

How the Code Works

First, you need to include the following libraries: FS.h to handle files, and LittleFS.h to create and access the filesystem.

#include "FS.h"
#include <LittleFS.h>

The first time you use LittleFS on the ESP32, you need to format it so that it creates a partition dedicated to that filesystem. To do that, we have the following boolean variable to control whether we want to format the filesystem or not.

#define FORMAT_LITTLEFS_IF_FAILED true

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 filesystem (LittleFs), the main directory’s name, and the levels to go into the directory.

void listDir(fs::FS &fs, const char * dirname, uint8_t levels){
    Serial.printf("Listing directory: %s\r\n", dirname);

    File root = fs.open(dirname);
    if(!root){
        Serial.println("- failed to open directory");
        return;
    }
    if(!root.isDirectory()){
        Serial.println(" - not a directory");
        return;
    }

    File file = root.openNextFile();
    while(file){
        if(file.isDirectory()){
            Serial.print("  DIR : ");
            Serial.println(file.name());
            if(levels){
                listDir(fs, file.path(), levels -1);
            }
        } else {
            Serial.print("  FILE: ");
            Serial.print(file.name());
            Serial.print("\tSIZE: ");
            Serial.println(file.size());
        }
        file = root.openNextFile();
    }
}

Here’s an example of how to call this function. The / corresponds to the root directory. The following command will list all the directories up to one level beginning at the root directory.

listDir(LittleFS, "/", 1); // List the directories up to one level beginning at the root directory

Create a Directory

The createDir() function creates a new directory. Pass as an argument the LittleFS filesystem and the directory name path.

void createDir(fs::FS &fs, const char * path){
  Serial.printf("Creating Dir: %s\n", path);
  if(fs.mkdir(path)){
    Serial.println("Dir created");
  } else {
    Serial.println("mkdir failed");
  }
}

For example, the following command creates a new directory (folder) on the root called mydir.

createDir(LittleFS, "/mydir"); // Create a mydir folder

Remove a Directory

To remove a directory from the filesystemca, use the removeDir() function and pass as an argument the LittleFS filesystem and the directory name path.

void removeDir(fs::FS &fs, const char * path){
  Serial.printf("Removing Dir: %s\n", path);
  if(fs.rmdir(path)){
    Serial.println("Dir removed");
  } else {
    Serial.println("rmdir failed");
  }
}

Here is an example that deletes the mydir folder.

removeDir(LittleFS, "/mydir");

Read File Content

The readFile() function reads the content of a file and prints the content in the Serial Monitor. As with previous functions, pass as an argument the LittleFs filesystem and the file path.

void readFile(fs::FS &fs, const char * path){
  Serial.printf("Reading file: %s\r\n", path);

  File file = fs.open(path);
  if(!file || file.isDirectory()){
    Serial.println("- failed to open file for reading");
    return;
  }

  Serial.println("- 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(LittleFS, "/hello.txt"); // Read the complete file

Write Content to a File

To write content to a file, you can use the writeFile() function. Pass as an argument, the LittleFS filesystem, the file path, and the message (as a const char variable).

void writeFile(fs::FS &fs, const char * path, const char * message){
  Serial.printf("Writing file: %s\r\n", path);

  File file = fs.open(path, FILE_WRITE);
  if(!file){
    Serial.println("- failed to open file for writing");
    return;
  }
  if(file.print(message)){
    Serial.println("- file written");
  } else {
    Serial.println("- write failed");
  }
  file.close();
}

The following line writes Hello in the hello.txt file.

writeFile(LittleFS, "/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(fs::FS &fs, const char * path, const char * message){
  Serial.printf("Appending to file: %s\r\n", path);

  File file = fs.open(path, FILE_APPEND);
  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 \r\n means that the next time you write something to the file, it will be written in a new line.

appendFile(LittleFS, "/hello.txt", "World!\r\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(fs::FS &fs, const char * path1, const char * path2){
  Serial.printf("Renaming file %s to %s\r\n", path1, path2);
  if (fs.rename(path1, path2)) {
    Serial.println("- file renamed");
  } else {
    Serial.println("- rename failed");
  }
}

The following line renames the hello.txt file to foo.txt.

renameFile(LittleFS, "/hello.txt", "/foo.txt");

Delete a File

Use the deleteFile() function to delete a file. Pass as an argument the LittleFS filesystem and the file path of the file you want to delete.

void deleteFile(fs::FS &fs, const char * path){
  Serial.printf("Deleting file: %s\r\n", path);
  if(fs.remove(path)){
    Serial.println("- file deleted");
  } else {
    Serial.println("- delete failed");
  }
}

The following line deletes the foo.txt file from the filesystem.

deleteFile(LittleFS, "/foo.txt");

Test a File

The testFileIO() function shows how long it takes to read the content of a file.

void testFileIO(fs::FS &fs, const char * path){
  Serial.printf("Testing file I/O with %s\r\n", path);

  static uint8_t buf[512];
  size_t len = 0;
  File file = fs.open(path, FILE_WRITE);
  if(!file){
    Serial.println("- failed to open file for writing");
    return;
  }

  size_t i;
  Serial.print("- writing" );
  uint32_t start = millis();
  for(i=0; i<2048; i++){
    if ((i & 0x001F) == 0x001F){
      Serial.print(".");
     }
    file.write(buf, 512);
  }
  Serial.println("");
  uint32_t end = millis() - start;
  Serial.printf(" - %u bytes written in %u ms\r\n", 2048 * 512, end);
  file.close();

  file = fs.open(path);
  start = millis();
  end = start;
  i = 0;
  if(file && !file.isDirectory()){
    len = file.size();
    size_t flen = len;
    start = millis();
    Serial.print("- reading" );
    while(len){
      size_t toRead = len;
      if(toRead > 512){
        toRead = 512;
       }
       file.read(buf, toRead);
       if ((i++ & 0x001F) == 0x001F){
         Serial.print(".");
       }
       len -= toRead;
      }
    Serial.println("");
    end = millis() - start;
    Serial.printf("- %u bytes read in %u ms\r\n", flen, end);
    file.close();
  } else {
    Serial.println("- failed to open file for reading");
  }
}

The following function tests the test.txt file.

testFileIO(LittleFS, "/test.txt"); 

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.

You can pass true or false as an argument to the begin() method. If you pass true it will format the LittleFS filesystem if the initialization fails. Because this is the first test we’re running, we set the FORMAT_LITTLEFS_IF_FAILED variable to true.

Testing the Filesystem

The following lines call all the functions we’ve seen previously.

createDir(LittleFS, "/mydir"); // Create a mydir folder
writeFile(LittleFS, "/mydir/hello1.txt", "Hello1"); // Create a hello1.txt file with the content "Hello1"
listDir(LittleFS, "/", 1); // List the directories up to one level beginning at the root directory
deleteFile(LittleFS, "/mydir/hello1.txt"); //delete the previously created file
removeDir(LittleFS, "/mydir"); //delete the previously created folder
listDir(LittleFS, "/", 1); // list all directories to make sure they were deleted
    
writeFile(LittleFS, "/hello.txt", "Hello "); //Create and write a new file in the root directory
appendFile(LittleFS, "/hello.txt", "World!\r\n"); //Append some text to the previous file
readFile(LittleFS, "/hello.txt"); // Read the complete file
renameFile(LittleFS, "/hello.txt", "/foo.txt"); //Rename the previous file
readFile(LittleFS, "/foo.txt"); //Read the file with the new name
deleteFile(LittleFS, "/foo.txt"); //Delete the file
testFileIO(LittleFS, "/test.txt"); //Testin
deleteFile(LittleFS, "/test.txt"); //Delete the file

Demonstration

Upload the previous sketch to your ESP32 board. After that, open the Serial Monitor and press the ESP32 on-board RST button. If the initialization succeeds, you’ll get similar messages on the Serial Monitor.

ESP32 Testing LittleFS Examples

ESP32 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 and folders 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.

// Project details: https://RandomNerdTutorials.com/esp32-write-data-littlefs-arduino/

#include <Arduino.h>
#include "FS.h"
#include <LittleFS.h>

#define FORMAT_LITTLEFS_IF_FAILED true

int mydata;

void writeFile(fs::FS &fs, const char * path, const char * message){
    Serial.printf("Writing file: %s\r\n", path);

    File file = fs.open(path, FILE_WRITE);
    if(!file){
        Serial.println("- failed to open file for writing");
        return;
    }
    if(file.print(message)){
        Serial.println("- file written");
    } else {
        Serial.println("- write failed");
    }
    file.close();
}

void appendFile(fs::FS &fs, const char * path, const char * message){
    Serial.printf("Appending to file: %s\r\n", path);

    File file = fs.open(path, FILE_APPEND);
    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 readFile(fs::FS &fs, const char * path){
    Serial.printf("Reading file: %s\r\n", path);

    File file = fs.open(path);
    if(!file || file.isDirectory()){
        Serial.println("- failed to open file for reading");
        return;
    }

    Serial.println("- read from file:");
    while(file.available()){
        Serial.write(file.read());
    }
    file.close();
}


void setup() {
  Serial.begin(115200);
  if(!LittleFS.begin(FORMAT_LITTLEFS_IF_FAILED)){
      Serial.println("LittleFS Mount Failed");
      return;
   }
   else{
       Serial.println("Little FS Mounted Successfully");
   }
   writeFile(LittleFS, "/data.txt", "MY ESP32 DATA \r\n");
}

void loop() {
  mydata = random (0, 1000);
  appendFile(LittleFS, "/data.txt", (String(mydata)+ "\r\n").c_str()); //Append data to the file
  readFile(LittleFS, "/data.txt"); // Read the contents of the file
  delay(30000);
}

View raw code

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 writeFile(fs::FS &fs, const char * path, const char * message){
  Serial.printf("Writing file: %s\r\n", path);

  File file = fs.open(path, FILE_WRITE);
  if(!file){
    Serial.println("- failed to open file for writing");
    return;
  }
  if(file.print(message)){
    Serial.println("- file written");
  } else {
    Serial.println("- write failed");
  }
  file.close();
}

void appendFile(fs::FS &fs, const char * path, const char * message){
  Serial.printf("Appending to file: %s\r\n", path);

  File file = fs.open(path, FILE_APPEND);
  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 readFile(fs::FS &fs, const char * path){
  Serial.printf("Reading file: %s\r\n", path);

  File file = fs.open(path);
  if(!file || file.isDirectory()){
    Serial.println("- failed to open file for reading");
    return;
  }

 Serial.println("- read from file:");
 while(file.available()){
   Serial.write(file.read());
  }
  file.close();
}

In the setup(), we initialize the Serial Monitor for debugging purposes.

Serial.begin(115200);

And we initialize the LittleFS filesystem:

if(!LittleFS.begin(FORMAT_LITTLEFS_IF_FAILED)){
  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 ESP32 DATA:

writeFile(LittleFS, "/data.txt", "MY ESP32 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(LittleFS, "/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(LittleFS, "/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 your ESP32 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.

ESP32 Save Data to File LittleFS Example Serial Monitor

Notice that if you restart your board, you’ll lose all your previous data. Why is that happening?

ESP32 Save Data to File LittleFS Example Serial Monitor

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.

ESP32 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 ESP32 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(LittleFS, "/data.txt", "MY ESP32 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(LittleFS, "/data.txt", "MY ESP32 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.

// Project details: https://RandomNerdTutorials.com/esp32-write-data-littlefs-arduino/

#include <Arduino.h>
#include "FS.h"
#include <LittleFS.h>

/* You only need to format LittleFS the first time you run a
   test or else use the LITTLEFS plugin to create a partition
   https://github.com/lorol/arduino-esp32littlefs-plugin
 */
#define FORMAT_LITTLEFS_IF_FAILED true

int mydata;

void writeFile(fs::FS &fs, const char * path, const char * message){
    Serial.printf("Writing file: %s\r\n", path);

    File file = fs.open(path, FILE_WRITE);
    if(!file){
        Serial.println("- failed to open file for writing");
        return;
    }
    if(file.print(message)){
        Serial.println("- file written");
    } else {
        Serial.println("- write failed");
    }
    file.close();
}

void appendFile(fs::FS &fs, const char * path, const char * message){
    Serial.printf("Appending to file: %s\r\n", path);

    File file = fs.open(path, FILE_APPEND);
    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 readFile(fs::FS &fs, const char * path){
    Serial.printf("Reading file: %s\r\n", path);

    File file = fs.open(path);
    if(!file || file.isDirectory()){
        Serial.println("- failed to open file for reading");
        return;
    }

    Serial.println("- read from file:");
    while(file.available()){
        Serial.write(file.read());
    }
    file.close();
}


void setup() {
  Serial.begin(115200);
  if(!LittleFS.begin(FORMAT_LITTLEFS_IF_FAILED)){
      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(LittleFS, "/data.txt", "MY ESP32 DATA \r\n");
   }
   else {
       Serial.println("File already exists");
   }
}

void loop() {
  mydata = random (0, 1000);
  appendFile(LittleFS, "/data.txt", (String(mydata)+ "\r\n").c_str()); //Append data to the file
  readFile(LittleFS, "/data.txt"); // Read the contents of the file
  delay(30000);
}

View raw code

If you test this example, you’ll see that the file keeps all data even after a restart.

ESP32 Save Data to File LittleFS Example Serial Monitor - prevent overwrite

Wrapping Up

With this tutorial, you learned how to save data permanently on a file in the ESP32 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 ESP32, 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 ESP32 filesystem: ESP32: Upload Files to LittleFS using Arduino IDE.

We hope you found this tutorial useful. Learn more about the ESP32 with our resources:



Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »
Learn how to build a home automation system and we’ll cover the following main subjects: Node-RED, Node-RED Dashboard, Raspberry Pi, ESP32, ESP8266, MQTT, and InfluxDB database DOWNLOAD »

Recommended Resources

Build a Home Automation System from Scratch » With Raspberry Pi, ESP8266, Arduino, and Node-RED.

Home Automation using ESP8266 eBook and video course » Build IoT and home automation projects.

Arduino Step-by-Step Projects » Build 25 Arduino projects with our course, even with no prior experience!

What to Read Next…


Enjoyed this project? Stay updated by subscribing our newsletter!

24 thoughts on “ESP32: Write Data to a File (LittleFS) – Arduino IDE”

  1. Hi Sara,
    This is great!
    Now all I have to do to save data to a LittleFS file for Wifi SSID & Password, or ThingSpeak is save the data to a file on the ESP32, etc.
    Maybe I missed it in the tutorial, but, when I flash another program to the ESP32, how do I keep the data file in flash memory from being wiped out, so I can use the data file for other ESP programs?
    Thanks,
    Joe

    Reply
    • Hi.
      Flashing a new program to the ESP32’s flash memory will not automatically wipe out the LittleFS data files unless you explicitly instruct it to do so.
      Your data files should be preserved across program updates unless you do something to remove them.
      Regards,
      Sara

      Reply
  2. Excellent tutorial, once again! Right down to every detail I need, and with clear examples.
    You guys are educators, first and for sure.
    Walter

    Reply
  3. Good project. I will surely try it out. Please i want to ask,
    What is the write cycle endurance. I know eeprom is around 10,000 to 100,000 cycle.
    Also, what is the size of data that can be written to this FS. Does it depend on the size of the main code?

    Reply
  4. Hi Sara,
    I guess this is a dumb question, but when I flash a program to the ESP32, why does this not wipe out the Flash Memory storing the LittleFS file? I know you say “save data permanently on a file in the ESP32 LittleFS filesystem”. What happens to the LittleFS data file when the Arduino IDE flashes a new program to ESP32 Flash memory?
    Thanks,
    Joe

    Reply
    • The LittleFS file will remain there even after uploading a new code.
      When you flash a new program to an ESP32 using the Arduino IDE or any other development environment, you are overwriting the program code stored in the flash memory. However, this process typically doesn’t erase the entire flash memory, including the LittleFS filesystem, unless you explicitly choose to do so.
      Regards,
      Sara

      Reply
  5. Hi Sara and Rui – Thanks for this article really clear article. I have used LFS with variables and just convert data to text using String(mydata) and then saved as text file without using .c_str(). Can you tell me what .c_str() does?
    The method seems to work without it so is it necessary?

    Reply
    • Hi.
      Are you using the same functions we have in the code?
      The c_str() method converts a string to an array of characters with a null character at the end. The functions we define expect an argument of that type (const char * message).
      If it’s not necessary, maybe something is doing the conversion automatically in the background.

      Regards,
      Sara

      Reply
  6. Hello,
    Great examples.
    If you check your arduino settings, you set the amount of flash space for the code and for the littlefs. So the code will not overwrite the littlefs normally. Make sure you do not tweak the settings and get rid of the littlefs or these programs with not work right.
    There is a button that allows for overwriting the whole flash before upload though. This MIGHT erase the littlefs, be sure to check this or just leave the button set to not overwrite.
    You can add some smarts to the program to check and create a new file with a different name if the requested name exists. I do this and it works great to avoid accidentally deleting something important.
    Keep up the good work.
    DrC

    Reply
  7. Hi Sara,
    Thank you for the tutorial.
    I use Arduino IDE 2.2.1 with ESP32 S3P2 and so far I do not see a predefined partition scheme that incorporates the littlefs SubType for the 16MB flash size, only for the 32MB one which actually has spiffs defined in it, as I found when I inspected it.
    To overcome this I created a custom partition table in a csv file called “partition.csv” and placed it into the root directory of the project. The problem I am facing is the compiler fails to recognise the littlefs SubType and throws that error message referring to the line number wher that is defined. The documentation here https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/partition-tables.html clearly shows that the this SubType exists so I expect it to compile but it does not.
    Any ideas why that happens, and can we use the spiffs partition as littlefs partition without a drawback?
    Regards,
    Andrew

    Reply
    • It’s included by default.
      No need to download.
      Just make sure you have an ESP32 selected in Tools > Board.
      Regards,
      Sara

      Reply
  8. Hi Sara,

    Thanks for this great tutorial! Unfortunately, I’m still having trouble getting it to work. Do you (or anyone else) have any idea why the mounting might not be working?

    I’ve tried almost everything I can think of, but I’m out of ideas at this point. I always end up in an error message “LittleFS Mount Failed”. I’m using a simple Arduino Nano ESP32, connected to my laptop with a regular USB-C cable. I usualle program in VS code with PlatformIO, but I tested everything also with ArduinoIDE.

    Thanks in advance for any help you can provide. It would help me a lot.
    Regards, Patrick

    Reply

Leave a Comment

Download Our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.