ESP32 Arduino: Updating firmware from the SPIFFS file system

In this tutorial we will learn how to update the firmware of the ESP32 from a binary file on the SPIFFS file system of the device. The tests shown here were performed using an ESP32 board from DFRobot.

Introduction

In this tutorial we will learn how to update the firmware of the ESP32 from a binary file on the SPIFFS file system of the device.

For this tutorial we will need to obtain the binary file of a compiled Arduino Sketch. This previous tutorial explains in detail how to do it.

We will also need to upload it to the SPIFFS file system of the device afterwards, using this Arduino IDE plugin. You can check how to do it here.

The tests shown here were performed using an ESP32 board from DFRobot.

The code of the new program

We will start by writing the code for the program that will be flashed to the ESP32 from the SPIFFS file system. It will be a very simple “Hello World”, for demonstration purposes.

We will start by opening a serial connection on the Arduino setup function, so we can output our message.

Then, in the Arduino loop, we will print a “Hello World” message every second.

The complete code can be seen below and, as mentioned, it is really simple.

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

void loop() {
  Serial.println("Hello world");
  delay(1000);
}

Uploading the binary file to the SPIFFS file system

Now that we have written our final firmware in the previous section, we need to compile it and upload it to the SPIFFS file system of the ESP32.

The first thing we will need to do is obtaining the compiled binary file. This previous tutorial explains in detail how to do it.

In short, we simply need to click the Sketch menu at the top of the Arduino IDE and select the “Export compiled Binary” option, as shown in figure 1.

Exporting the compiled binary for the sketch, using the Arduino IDE
Figure 1 – Exporting the compiled binary, using the Arduino IDE.

When the procedure finishes, the binary file should be available in the sketch folder. You can navigate to the sketch folder by clicking on the Sketch menu and selecting the “Show Sketch Folder” option. This is illustrated in figure 2.

Opening the Arduino Sketch folder.
Figure 2 – Opening the Arduino Sketch folder.

There you should see the .ino file of your sketch and below a .bin file. Your .bin file will have the name of your sketch appended with the name of your board. The content of the folder is shown in figure 3.

Content of the Sketch folder, containing the compiled binary.
Figure 3 – Content of the Sketch folder, containing the compiled binary.

To simplify accessing it later, you can edit the name of the binary file to be called “firmware.bin“. In the code of the next section, when accessing the file in the SPIFFS file system, I’ll be assuming this name. Figure 4 shows how the file should look like after the rename.

Renaming the binary file of the compiled Sketch.
Figure 4 – Renaming the binary file.

So, after renaming the file, we still need to upload it to the ESP32 file system. The easiest way is by using this plugin. You can check here a detailed guide on how to use it.

In short, we simply need to create a folder called data inside our sketch folder (the one where we are currently located). This is shown in figure 5.

Creating the data folder inside the Arduino Sketch folder.
Figure 5 – Creating the data folder.

Then, we need to place there the files we want to upload to the ESP32 SPIFFS file system. In our case, we should place there the “firmware.bin” file, as illustrated in figure 6.

Final content of the data folder.
Figure 6 – Final content of the data folder.

Then we go back to the Arduino IDE and under the Tools menu we just need to select the “ESP32 Sketch Data Upload” option, with the ESP32 connected to the computer. This is shown in figure 7.

Uploading the content of the data folder to the SPIFFS file system, using the Arduino IDE.
Figure 7 – Uploading the content of the data folder to the SPIFFS file system.

Once the procedure finishes, the binary file should be on the device SPIFFS file system.

The code

We will start our code by including the libraries we need. We will need the SPIFFS.h, to be able to access the binary file on the file system, and the Update.h, which exposes the functionality we need to update the firmware of the ESP32.

You can check the header file of the Update.h library here. The implementation file can be seen here.

#include "SPIFFS.h"
#include "Update.h"

Moving on to the Arduino setup, where we will write the rest of the code, we will start by opening a serial connection, to be able to print some messages from our program.

Then we will mount the SPIFFS file system. We always need to mount SPIFFS before we start interacting with it. In case the mounting procedure fails, we print a message to the user and we won’t try to proceed, since we wouldn’t be able to read the file.

Serial.begin(115200);
 
if(!SPIFFS.begin(true)){
     Serial.println("An Error has occurred while mounting SPIFFS");
     return;
}

After mounting the file system, we will open the binary file in reading mode. Recall that our file was called “firmware.bin” and it was uploaded from the root of the data folder. Thus, it should have the same name and it should be located at the root of the file system.

So, to open the file for reading, we simply call the open method on the SPIFFS object, passing as input the path to the file. Note that we don’t need to explicitly indicate the opening mode since it defaults to read.

After this we will do an error check to make sure we were able to open the file in reading mode.

File file = SPIFFS.open("/firmware.bin");
 
if(!file){
    Serial.println("Failed to open file for reading");
    return;
}

In case of success, the next thing we will do is obtaining the file size, to later check if we have enough space to update the firmware. We can obtain the size of the file with a call to the size method on our File object.

size_t fileSize = file.size();

Next we will take care of the uploading procedure. For this, we will call some methods on an extern variable called Update, which becomes available when we import the Update.h library.

The first thing we will do is calling the begin method on the Update object, in order to start the updating procedure.

This method receives as input the size of the binary file, in bytes, and returns a Boolean flag indicating if the procedure can be started or not. One of the checks it will perform is to confirm if there is enough space to do the update.

if(!Update.begin(fileSize)){
      
      Serial.println("Cannot do the update");
      return;
};

Then we will call the writeStream method on the Update extern variable, passing as input our file. Under the hood, this method will call the readBytes method of the stream to obtain the bytes and write them to the flash [1].

Note that, as can be seen here, the File class extends the Stream class, which is why we can directly pass the File object as input of the writeStream method.

Update.writeStream(file);

To complete the procedure, we just need to call the end method on the Update variable. This method will return true if the procedure was completed successfully and false otherwise. We will use this value for error checking.

In case of error, we can obtain the integer representing it by calling the getError method. You can check the list of possible errors here.

if(Update.end()){
      
   Serial.println("Successful update");  
}else{
      
   Serial.println("Error Occurred: " + String(Update.getError()));
   return;
}

In case of success, we will simply close the previously opened binary file and restart the ESP32, so it starts running the new firmware. You can read here more about how to perform a software restart on the ESP32.

file.close();

Serial.println("Reset in 4 seconds...");
delay(4000);

ESP.restart();

The complete code can be seen below.

#include "SPIFFS.h"
#include "Update.h"
 
void setup() {
 
   Serial.begin(115200);
 
   if(!SPIFFS.begin(true)){
        Serial.println("An Error has occurred while mounting SPIFFS");
        return;
   }
  
    File file = SPIFFS.open("/firmware.bin");
 
    if(!file){
        Serial.println("Failed to open file for reading");
        return;
    }
      
    Serial.println("Starting update..");
       

    size_t fileSize = file.size();

    if(!Update.begin(fileSize)){
      
       Serial.println("Cannot do the update");
       return;
    };

    Update.writeStream(file);

    if(Update.end()){
      
      Serial.println("Successful update");  
    }else {
      
      Serial.println("Error Occurred: " + String(Update.getError()));
      return;
    }
    
    file.close();

    Serial.println("Reset in 4 seconds...");
    delay(4000);

    ESP.restart();
}
 
void loop() {}

Testing the code

Assuming that you have already uploaded the binary file of the final firmware to the SPIFFS file system of the ESP32, simply upload the code from the previous section to your device.

When the procedure finishes, open the Arduino IDE serial monitor. You should get an output similar to figure 8.

Figure 8 – Output of the program, before the ESP32 resets.

After the ESP32 resets, it should load the new firmware, as shown in figure 9.

Figure 9 – ESP32 running the final firmware.

References

[1] https://github.com/espressif/arduino-esp32/blob/master/libraries/Update/src/Update.h

Leave a Reply