devicescript-settings-flash-persistencia

Saving Data to Flash with DeviceScript Settings

  • 5 min

DeviceScript Settings allow you to save values in flash memory so they can be retrieved after a reboot.

This is necessary because RAM is volatile. In almost any real-world (“production”) project, we need to permanently store certain data:

  • A counter for how many times the machine has been powered on.
  • The user’s preferred brightness setting.
  • The last relay state to restore it when power returns.

In the classic Arduino world, we used the EEPROM library or, more recently on ESP32, Preferences.

In DeviceScript, we have a unified and much friendlier mechanism: the Settings package.

What are Settings

DeviceScript reserves a small partition of the microcontroller’s Flash memory (the same one where the program is stored) to hold data.

Unlike the old EEPROM, where we saved bytes at memory addresses (write(0, 255)), the Settings service works like a Key-Value Dictionary.

  • Key: A text string (string) to identify the data. E.g., "brightness".
  • Value: The data we want to save (Numbers, Strings, or Buffers).

This is very similar to the browser’s localStorage. It’s simple, direct, and doesn’t require managing memory addresses.

Reading and Writing Values

To use it, we need to import readSetting and writeSetting from @devicescript/settings.

Setting keys should be short. The official documentation recommends less than 14 characters, so bright is better than brightness_super_detailed.

Writing Data

import { writeSetting } from "@devicescript/settings"

// Save a number
await writeSetting("bright", 80)

// Save a string
await writeSetting("name", "Luis")
Copied!

The call uses await because writing to flash is an asynchronous operation.

Reading Data

Here’s an important detail: What if the data doesn’t exist? (For example, the first time we power up the board). We can pass a default value to readSetting.

import { readSetting } from "@devicescript/settings"

// Read the value
const savedBrightness = (await readSetting<number>("bright")) ?? 50

console.log(`Retrieved value: ${savedBrightness}`) 
Copied!

Example: Reboot Counter

The “Hello World” of persistence is counting how many times our device has started. This proves the data survives a power cycle.

import { readSetting, writeSetting } from "@devicescript/settings"
import { delay } from "@devicescript/core"

async function manageCounter() {
    console.log("Starting system...")

    // 1. Read the current value. If it doesn't exist, start at 0.
    let counter = (await readSetting<number>("boot_count")) ?? 0

    console.log(`This device has been rebooted ${counter} times.`)

    // 2. Increment and save for next time
    counter++
    await writeSetting("boot_count", counter)
    
    console.log("New value saved. Reboot me!")
}

manageCounter()
Copied!

Load the code and note the counter. Press RESET or disconnect power; on the next boot, the value should increase without returning to zero.

Saving Objects as JSON

The Settings service saves basic types. But what if we want to save our entire device configuration at once?

const config = {
    mode: "auto",
    thresholds: { min: 20, max: 30 },
    active: true
}
Copied!

We can’t save a JS object directly. But we have an old friend to help us: JSON.

We convert the object to text (stringify) to save it, and convert it back to an object (parse) when reading.

import { readSetting, writeSetting } from "@devicescript/settings"

const CONFIG_KEY = "config"

// --- SAVE ---
async function saveConfig(cfg: any) {
    const text = JSON.stringify(cfg)
    await writeSetting(CONFIG_KEY, text)
    console.log("Configuration saved.")
}

// --- LOAD ---
async function loadConfig() {
    const text = (await readSetting<string>(CONFIG_KEY)) ?? ""
    
    if (!text) {
        // Return a default configuration if nothing is saved
        return { mode: "manual", thresholds: { min: 0, max: 100 } }
    }
    
    // Convert the text back to an object
    return JSON.parse(text + "")
}
Copied!

This pattern is the most recommended. Instead of having 20 separate keys ("mode", "threshold_min", "threshold_max"…), save a single JSON object. It’s cleaner and easier to maintain.

Deleting Data

Sometimes we need a “Factory Reset” and erase everything. For that, the easiest way is to use the VS Code extension itself.

Run DeviceScript: Clear Device Settings... to clear the settings on the connected device.

Avoiding Flash Wear

It’s crucial to remember that we are writing to Flash memory.

Wear

Flash memory has a limited number of erase/write cycles. Do not call writeSetting() in a fast loop.

  • Bad: Saving the temperature every second.
  • Good: Saving the configuration only when the user changes it.
Speed

Writing to Flash takes time (milliseconds). It is not instantaneous.

Space

It is not a hard drive. You have a few KB available (typically 4KB - 16KB depending on the partition). Do not try to save images or huge logs here.

Difference Between Settings and Secrets

An important distinction in DeviceScript:

  • Settings: For your application data (colors, counters, preferences). They can be easily read and written from code.
  • WiFi or MQTT Secrets: Go in .env.local, are not uploaded to Git, and are transferred to the device during deployment. Do not display them in the console.