devicescript-settings-flash-persistencia

Guardar datos en flash con Settings de DeviceScript

  • 5 min

Los settings de DeviceScript permiten guardar valores en la memoria flash para recuperarlos después de un reinicio.

Esto es normal, ya que la memoria RAM es volátil. Pero en casi cualquier proyecto real (“en producción”), necesitamos guardar ciertos datos de forma permanente:

  • Un contador de cuántas veces se ha encendido la máquina.
  • La configuración de brillo preferida por el usuario.
  • El último estado del relé para recuperarlo al volver la luz.

En el mundo Arduino clásico, usábamos la librería EEPROM o, más recientemente en ESP32, Preferences.

En DeviceScript, tenemos un mecanismo unificado y mucho más amigable: el paquete Settings.

Qué son los settings

DeviceScript reserva una pequeña partición de la memoria Flash del microcontrolador (la misma donde se guarda el programa) para almacenar datos.

A diferencia de la EEPROM antigua, donde guardábamos bytes en direcciones de memoria (write(0, 255)), el servicio Settings funciona como un Diccionario Clave-Valor.

  • Clave (Key): Un nombre de texto (string) para identificar el dato. Ej: "brillo_pantalla".
  • Valor (Value): El dato que queremos guardar (Números, Strings o Buffers).

Esto se parece mucho al localStorage del navegador web. Es sencillo, directo y no requiere gestionar direcciones de memoria.

Leer y escribir valores

Para usarlo, necesitamos importar readSetting y writeSetting desde @devicescript/settings.

Las claves de settings deben ser cortas. La documentación oficial recomienda menos de 14 caracteres, así que mejor brillo que brillo_pantalla_super_detallado.

Escribir un dato

import { writeSetting } from "@devicescript/settings"

// Guardamos un número
await writeSetting("brillo", 80)

// Guardamos un texto
await writeSetting("nombre", "Luis")
Copied!

La llamada lleva await porque escribir en flash es una operación asíncrona.

Leer un dato

Aquí hay un detalle importante: ¿Qué pasa si el dato no existe? (Por ejemplo, la primera vez que encendemos la placa). Podemos pasar un valor por defecto a readSetting.

import { readSetting } from "@devicescript/settings"

// Leemos el valor
const brilloGuardado = (await readSetting<number>("brillo")) ?? 50

console.log(`Valor recuperado: ${brilloGuardado}`) 
Copied!

Ejemplo: contador de reinicios

El “Hola Mundo” de la persistencia es contar cuántas veces ha arrancado nuestro dispositivo. Esto nos demuestra que el dato sobrevive al apagado.

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

async function gestionarContador() {
    console.log("Iniciando sistema...")

    // 1. Leemos el valor actual. Si no existe, empieza en 0.
    let contador = (await readSetting<number>("boot_count")) ?? 0

    console.log(`Este dispositivo se ha reiniciado ${contador} veces.`)

    // 3. Incrementamos y guardamos para la próxima
    contador++
    await writeSetting("boot_count", contador)
    
    console.log("Nuevo valor guardado. ¡Reiníciame!")
}

gestionarContador()
Copied!

Carga el código y anota el contador. Pulsa RESET o desconecta la alimentación; en el siguiente arranque, el valor debería aumentar sin volver a cero.

Guardar objetos como JSON

El servicio de Settings guarda tipos básicos. Pero, ¿y si quiero guardar toda la configuración de mi dispositivo de golpe?

const config = {
    modo: "auto",
    umbrales: { min: 20, max: 30 },
    activo: true
}
Copied!

No podemos guardar un objeto JS directamente. Pero tenemos un viejo amigo que nos ayuda: JSON.

Convertimos el objeto a texto (stringify) para guardar, y lo reconvertimos a objeto (parse) al leer.

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

const CLAVE_CONFIG = "config"

// --- GUARDAR ---
async function guardarConfig(cfg: any) {
    const texto = JSON.stringify(cfg)
    await writeSetting(CLAVE_CONFIG, texto)
    console.log("Configuración guardada.")
}

// --- CARGAR ---
async function cargarConfig() {
    const texto = (await readSetting<string>(CLAVE_CONFIG)) ?? ""
    
    if (!texto) {
        // Devolvemos una configuración por defecto si no hay nada guardado
        return { modo: "manual", umbrales: { min: 0, max: 100 } }
    }
    
    // Convertimos el texto de vuelta a objeto
    return JSON.parse(texto + "")
}
Copied!

Este patrón es el más recomendado. En lugar de tener 20 claves sueltas ("modo", "umbral_min", "umbral_max"…), guardad un solo objeto JSON. Es más limpio y fácil de mantener.

Borrar datos

A veces necesitamos hacer un “Factory Reset” y borrar todo. Para eso, lo más cómodo es usar la propia extensión de VS Code.

Ejecuta DeviceScript: Clear Device Settings... para borrar los ajustes del dispositivo conectado.

Evitar el desgaste de la flash

Es vital recordar que estamos escribiendo en memoria Flash.

Desgaste

La memoria flash admite un número limitado de ciclos de borrado y escritura. No llames a writeSetting() desde un bucle rápido.

  • Mal: Guardar la temperatura cada segundo.
  • Bien: Guardar la configuración solo cuando el usuario la cambia.
Velocidad

Escribir en Flash toma tiempo (milisegundos). No es instantáneo.

Espacio

No es un disco duro. Disponéis de unos pocos KB (normalmente 4KB - 16KB dependiendo de la partición). No intentéis guardar imágenes o logs gigantescos aquí.

Diferencia entre settings y secretos

Una distinción importante en DeviceScript:

  • Settings: Para datos de tu aplicación (colores, contadores, preferencias). Se pueden leer y escribir desde código fácilmente.
  • Secretos de WiFi o MQTT: Van en .env.local, no se suben a Git y se transfieren al dispositivo al desplegar. No los muestres en la consola.