almacenamiento-guardar-configuracion-flash-nanoframework

Saving Configuration to Flash with nanoFramework

  • 5 min

Persistent storage means saving data even when the microcontroller resets.

Until now, our code lived in RAM. RAM is fast, but volatile: if you remove power, the data disappears.

A device may need to save data permanently:

  • Wi-Fi credentials (SSID/Pass).
  • Authentication tokens (API Keys).
  • User preferences (Alarm thresholds, LED colors).

In Arduino we used to use the EEPROM or Preferences library. In nanoFramework, we level up and use the System.IO namespace.

We’re going to use a familiar file API, applied to the ESP32’s internal flash memory.

For this tutorial you need to install the NuGet package: nanoFramework.System.IO.FileSystem

The Internal File System

The ESP32 has Flash memory (where your code is stored). nanoFramework reserves a partition of that memory so you can use it like a very small “hard drive”.

Depending on how your firmware is configured (though it’s the standard), this internal unit usually mounts on the I:\ drive (for Internal).

This means we can create, read, and write files to I:\config.txt using standard .NET classes.

Saving Plain Text

Let’s start with the basics: saving a simple message.

using System;
using System.Diagnostics;
using System.IO; // <--- Key namespace

public static void WriteFile()
{
    // Path on internal storage
    string path = "I:\\note.txt";

    try
    {
        // Check if it exists
        if (File.Exists(path))
        {
            Debug.WriteLine("File already exists. Deleting...");
            File.Delete(path);
        }

        // Write text
        // WriteAllText opens, writes, and closes the file automatically
        File.WriteAllText(path, "Hello from the past (before reset)");
        
        Debug.WriteLine("File saved successfully.");
    }
    catch (Exception ex)
    {
        Debug.WriteLine($"Error writing file: {ex.Message}");
    }
}

public static void ReadFile()
{
    string path = "I:\\note.txt";

    if (File.Exists(path))
    {
        string content = File.ReadAllText(path);
        Debug.WriteLine($"Read from disk: {content}");
    }
    else
    {
        Debug.WriteLine("The file does not exist.");
    }
}
Copied!

If you run WriteFile(), reset your ESP32 (cutting power), and then run ReadFile(), you’ll see the message is still there. Exactly what we wanted.

Saving JSON Configuration

For configuration with multiple fields, we can combine System.IO with nanoFramework.Json. Remember the JSON lesson? We’ll combine System.IO with nanoFramework.Json to create a professional configuration system.

Define the Configuration Class

public class AppConfig
{
    public string Ssid { get; set; } = "MyWifi";
    public string Password { get; set; } = "1234";
    public int ReadInterval { get; set; } = 60; // Seconds
    public string DeviceName { get; set; } = "Sensor_01";
}
Copied!

Create the Configuration Manager

Let’s create a static class that handles loading and saving this configuration.

using System.IO;
using nanoFramework.Json;

public static class ConfigurationManager
{
    private const string ConfigPath = "I:\\appsettings.json";

    public static void Save(AppConfig config)
    {
        // 1. Serialize the object to JSON text
        string json = JsonConvert.SerializeObject(config);

        // 2. Save to disk
        File.WriteAllText(ConfigPath, json);
    }

    public static AppConfig Load()
    {
        // If the file does not exist, return a default config
        if (!File.Exists(ConfigPath))
        {
            return new AppConfig();
        }

        // 1. Read from disk
        string json = File.ReadAllText(ConfigPath);

        // 2. Deserialize
        try 
        {
            return (AppConfig)JsonConvert.DeserializeObject(json, typeof(AppConfig));
        }
        catch
        {
            // If the file is corrupted, return default
            return new AppConfig();
        }
    }
}
Copied!

Using it from Main

Now your main program is much cleaner and more robust.

public static void Main()
{
    // Load configuration at startup
    AppConfig myConfig = ConfigurationManager.Load();

    Debug.WriteLine($"Starting device: {myConfig.DeviceName}");
    Debug.WriteLine($"Connecting to Wi-Fi: {myConfig.Ssid}...");

    // ... connection logic ...

    // Suppose we receive an order via MQTT to change the interval
    // ...
    myConfig.ReadInterval = 120;
    
    // Save so the change persists after a reset
    ConfigurationManager.Save(myConfig);
    Debug.WriteLine("Updated configuration saved.");

    Thread.Sleep(Timeout.Infinite);
}
Copied!

Flash Memory Wears Out

It’s very important to understand the underlying hardware. The Flash memory on an ESP32 has a limited number of write cycles (typically between 10,000 and 100,000).

  • Reading: Infinite. You can call Load() as many times as you want.
  • Writing: Limited.

Bad practice: Saving configuration inside a loop every second. You will destroy your chip’s Flash memory within weeks. Good practice: Save only when something changes (e.g., the user changes a setting from the web) or at very long intervals (e.g., accumulated counters once every hour).

Do not save passwords or tokens as JSON in plain text without analyzing the risk. The file system provides persistence, not encryption.

SD Cards for More Capacity

What if we want to save a Data Log (Datalogger)? If we want to save the temperature every 10 seconds for a year, the internal 4MB memory will fill up quickly.

For that, we use SD cards. nanoFramework supports SD cards connected via SPI or SDMMC.

The code is almost identical, only the drive changes. When mounting the SD card, it will usually appear as D:\ (or the letter assigned by the system).

// Write to the SD card
File.AppendAllText("D:\\datalog.csv", $"{DateTime.UtcNow},24.5\r\n");
Copied!

Note: Using the SD card requires additional initialization code to mount the unit (StorageEventManager), but the read/write logic is the same System.IO.