Node-RED

Histórico de gráficos en Node-RED: conserva los datos MQTT de tus gráficos tras reiniciar

Los gráficos del dashboard lo olvidan todo cuando Node-RED se reinicia. Esta guía muestra dos formas fiables de conservar el histórico de gráficos en Node-RED para datos MQTT: almacenamiento de contexto en archivo y una pequeña tabla SQLite, ambos reproducidos en Dashboard 2.0 al arrancar.

Gráfico de líneas en Node-RED Dashboard 2.0 que recupera 24 horas de datos de temperatura MQTT para mostrar el histórico de gráficos en Node-RED tras un reinicio
Pruébalo en tu propio broker MQTTConsigue un broker MQTT privado y gestionado con el plan Free y empieza con ejemplos de código listos para usar.
Pruébalo gratis Ejemplos de código Sin tarjeta

El histórico de gráficos en Node-RED desaparece cada vez que Node-RED se reinicia, porque los gráficos del dashboard guardan sus puntos en memoria. Este tutorial muestra dos formas prácticas de conservar ese histórico para datos MQTT: un almacén de contexto en archivo para conjuntos de datos pequeños y una tabla SQLite cuando quieres consultas y una retención más larga. En ambos casos reproducirás los datos almacenados en un gráfico de Dashboard 2.0 al arrancar, para que los operadores vean las últimas 24 horas en lugar de una cuadrícula vacía.

Por qué los gráficos de Node-RED pierden su histórico

Un nodo ui-chart de Dashboard 2.0 guarda los puntos que ha recibido en un almacén de datos del lado del servidor, por eso una nueva pestaña del navegador sigue mostrando la línea existente. Ese almacén vive en la memoria del proceso de Node-RED. Si reinicias Node-RED, reinicias la Raspberry Pi en la que se ejecuta o actualizas un paquete y reinicias, el gráfico vuelve a empezar desde cero.

MQTT no llena ese hueco por sí solo. Un retained message conserva solo el último valor de cada topic, así que un suscriptor que se reconecta recibe un punto, no una tendencia. Un broker es un enrutador de mensajes, no una base de datos. Si quieres entender exactamente dónde está ese límite, lee qué hace un broker MQTT. El histórico tiene que almacenarse en algún lugar que controles, y Node-RED es un sitio cómodo para hacerlo.

Elegir un método de almacenamiento para el histórico de gráficos en Node-RED

Método Adecuado para Retención Instalación adicional Inconvenientes
Contexto en archivo (localfilesystem) Una o pocas series, un punto por minuto De horas a unos pocos días Ninguna El array completo se reescribe en cada volcado; no admite consultas
SQLite (node-red-node-sqlite) Muchas series, filtrado por rango de tiempo De semanas a meses Un nodo de la paleta Tú gestionas la limpieza de la tabla
Base de datos de series temporales (InfluxDB) Muchos dispositivos, agregación, Grafana De meses a años Servidor aparte Más piezas que mantener

El resto del artículo implementa los dos primeros. Para el tercero, sigue el tutorial de Node-RED con InfluxDB, que usa la misma entrada MQTT y una base de datos creada para este trabajo.

Datos de prueba desde un broker gratuito

Necesitas un flujo de lecturas. Publica una temperatura cada pocos segundos en broker.connectmq.com, el servidor MQTT público gratuito, con un prefijo de topic único. Sustituye a7f3k2 por tu propia cadena aleatoria; cualquiera puede leer lo que hay en el servidor público, así que úsalo solo para pruebas, nunca con datos de producción.

# Publica una temperatura aleatoria cada 5 segundos (Ctrl+C para detener)
while true; do
  T=$(awk -v r=$RANDOM 'BEGIN { printf "%.2f", 20 + (r % 800) / 100 }')
  mosquitto_pub -h broker.connectmq.com -p 1883 -i pub-hist-a7f3k2 \
    -t connectmq/demo/a7f3k2/room1/telemetry -m "{\"temperature\": $T}"
  sleep 5
done

En Node-RED, añade un nodo mqtt in en connectmq/demo/a7f3k2/room1/telemetry, con salida "a parsed JSON object", usando un nodo de configuración del broker con un client ID único.

Método 1: almacenamiento de contexto en archivo

Activa un almacén de contexto persistente

Por defecto, el contexto de Node-RED vive en memoria. Edita settings.js en tu directorio de usuario de Node-RED (normalmente ~/.node-red/settings.js) y define un segundo almacén que escriba en disco:

// ~/.node-red/settings.js (dentro de module.exports = { ... })
contextStorage: {
    default: { module: "memory" },
    file: {
        module: "localfilesystem",
        config: { flushInterval: 30 }   // segundos entre escrituras en disco
    }
},

Reinicia Node-RED. Mantener la memoria como almacén por defecto y nombrar explícitamente el almacén en archivo significa que solo se escriben en disco los datos que elijas, algo importante en las tarjetas SD. Los valores se vuelcan cada 30 segundos, así que un corte brusco de alimentación puede perder hasta esa ventana; un apagado limpio lo vuelca todo. La guía de contexto de Node-RED explica con más detalle los ámbitos y los almacenes.

Registra un punto por minuto

Guardar cada lectura en bruto produce rápidamente un array grande que se reescribe en cada volcado. En su lugar, promedia las lecturas por minuto. Conecta el nodo mqtt in a este nodo function y conecta su salida al gráfico:

// Nodo function "record history" - 1 salida
const RETENTION_MS = 24 * 60 * 60 * 1000;   // conservar 24 horas
const value = Number(msg.payload.temperature);
if (Number.isNaN(value)) {
    return null;
}

const now = Date.now();
const minute = Math.floor(now / 60000);
let acc = context.get("acc") || { minute: minute, sum: 0, n: 0 };

if (acc.minute !== minute) {
    if (acc.n > 0) {
        let history = flow.get("tempHistory", "file") || [];
        history.push({
            x: acc.minute * 60000,
            y: Math.round((acc.sum / acc.n) * 100) / 100
        });
        const cutoff = now - RETENTION_MS;
        history = history.filter(p => p.x >= cutoff);
        flow.set("tempHistory", history, "file");
    }
    acc = { minute: minute, sum: 0, n: 0 };
}

acc.sum += value;
acc.n += 1;
context.set("acc", acc);

// Punto en vivo para el gráfico
return { topic: "Temperature", payload: { x: now, y: value } };

Con un punto por minuto, un día de histórico son 1.440 objetos pequeños por serie, muy por debajo de lo que un archivo de contexto maneja con comodidad.

Configura el gráfico

En el nodo ui-chart, elige un gráfico de líneas, deja Series como msg.topic, ajusta X a la clave x e Y a la clave y, y limita el gráfico a 24 horas de puntos para que coincida con tu retención.

Reproduce el histórico al arrancar

Añade un nodo inject configurado como Inject once after 1 second, sin repetición, y conéctalo a este nodo function y después al mismo gráfico:

// Nodo function "replay history" - 1 salida
const history = flow.get("tempHistory", "file") || [];
node.status({ fill: "green", shape: "dot", text: history.length + " points" });

return [[
    { topic: "Temperature", payload: [] },        // primero, vaciar el gráfico
    { topic: "Temperature", payload: history }    // después, enviar el array almacenado
]];

Enviar un array vacío borra el gráfico, lo que evita líneas duplicadas tras un redeploy. Enviar un array de objetos {x, y} los añade todos de una vez. Reinicia Node-RED y el gráfico vuelve con el último día de promedios y continúa después con los puntos en vivo.

Método 2: SQLite para una retención más larga

Cuando tienes varias series, quieres una semana o más, o quieres filtrar por tiempo, una base de datos es más limpia. Instala node-red-node-sqlite desde el gestor de paleta. En algunas plataformas compila un módulo nativo, así que dale un minuto.

Crea la tabla

Añade un nodo inject (inject once al arrancar) con msg.topic establecido en la sentencia siguiente y conéctalo a un nodo sqlite con el archivo de base de datos /home/pi/.node-red/history.db (ajusta la ruta) y el modo de consulta SQL Via msg.topic.

CREATE TABLE IF NOT EXISTS readings (
  ts     INTEGER NOT NULL,
  series TEXT    NOT NULL,
  value  REAL    NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_readings ON readings (series, ts);

Si tu versión del nodo solo ejecuta una sentencia por mensaje, envía las dos sentencias con dos inject separados.

Inserta cada lectura

Usa un segundo nodo sqlite en modo Prepared Statement con este SQL:

INSERT INTO readings (ts, series, value) VALUES ($ts, $series, $value)

Aliméntalo desde un nodo function que construya los parámetros:

// Nodo function "to sqlite params"
const value = Number(msg.payload.temperature);
if (Number.isNaN(value)) return null;
msg.params = { $ts: Date.now(), $series: "Temperature", $value: value };
return msg;

Consulta al arrancar y limpia a diario

Al arrancar, consulta las últimas 24 horas y envía las filas al gráfico. Un nodo sqlite en modo Via msg.topic devuelve las filas como un array en msg.payload:

// Nodo function "history query" - disparado por un inject once al arrancar
msg.topic = "SELECT ts AS x, value AS y FROM readings " +
            "WHERE series = 'Temperature' AND ts > " + (Date.now() - 86400000) +
            " ORDER BY ts";
return msg;

Pasa el resultado por un nodo change que establezca msg.topic en Temperature y después llévalo al gráfico. Añade un inject diario que ejecute DELETE FROM readings WHERE ts < <cutoff> con el límite calculado de la misma forma, para que el archivo no crezca indefinidamente.

Lista de comprobación antes de confiar en ello

  1. Elige un periodo de retención y aplícalo en el código o en SQL, no a base de esperanza.
  2. Reduce la resolución antes de almacenar si solo muestras promedios.
  3. Asegúrate de que el límite de puntos del gráfico coincide con la retención, o los puntos antiguos desaparecerán antes de tiempo.
  4. Vacía el gráfico antes de reproducir para evitar líneas duplicadas tras los deploys.
  5. Haz copia de seguridad del directorio de contexto o del archivo de base de datos junto con el resto de tu proyecto Node-RED.
  6. Vigila el uso de disco en las tarjetas SD; para datos de alta frecuencia, prefiere SQLite o una base de datos externa.

De dónde deben venir los datos en producción

El histórico solo es tan bueno como el flujo que lo alimenta. Si el broker se cae, aparecen huecos en el gráfico, y en un broker público compartido cualquiera puede leer tus topics de demostración o publicar en ellos. Para instalaciones reales, apunta el nodo mqtt in a un broker privado con credenciales y TLS. Con ConnectMQ, el portal emite un username, una password y un client ID para cada broker, y el cambio en el flujo se limita al nodo de configuración: servidor, puerto 8883, TLS activado y las credenciales.

Un broker MQTT gestionado también mantiene a los dispositivos publicando mientras reinicias o actualizas Node-RED. Combínalo con una sesión MQTT persistente y QoS 1 en la suscripción, y los reinicios breves de Node-RED pueden cubrirse gracias a que el broker encola los mensajes para la sesión, sujeto a la expiración de sesión de tu plan. Para los dashboards en sí, el tutorial de Dashboard 2.0 cubre indicadores, controles y diseño, y el tutorial de Node-RED con MQTT cubre los fundamentos del broker. Si más adelante quieres ver esos mismos datos sobre un modelo de la instalación, el mismo flujo MQTT puede alimentar un gemelo digital 3D.

Mantén tus datos de Node-RED en movimiento con ConnectMQ

Regístrate en app.connectmq.com y empieza con el plan gratuito Serverless, sin tarjeta de crédito. Crea un broker, pega las credenciales en el nodo de configuración de Node-RED y tus gráficos se alimentarán de un flujo privado y cifrado con TLS. El explorador de topics en vivo de ConnectMQ te ayuda a confirmar que las lecturas llegan antes de empezar a depurar el almacenamiento.

Preguntas frecuentes

¿Guarda Node-RED Dashboard 2.0 los datos de los gráficos automáticamente?

Mantiene los datos del gráfico en memoria para que las nuevas pestañas del navegador vean los puntos existentes, pero no escribe nada en disco. Tras un reinicio de Node-RED, el gráfico queda vacío a menos que reproduzcas los datos almacenados.

¿Pueden los retained messages de MQTT almacenar el histórico de un gráfico?

No. Un retained message guarda solo el último valor de un topic. Es útil para mostrar el estado actual de inmediato, pero para una tendencia necesitas almacenamiento de contexto o una base de datos.

¿Cuánto histórico puedo guardar en el contexto de Node-RED?

El contexto en archivo funciona bien con unas pocas series reducidas a un punto por minuto durante un día o unos pocos días. Para volúmenes mayores, usa SQLite o una base de datos de series temporales como InfluxDB.

¿Cómo evito las líneas duplicadas tras un deploy?

Envía un array vacío al gráfico antes de reproducir los puntos almacenados. Así se vacía el gráfico y el histórico se dibuja una sola vez.

Artículos relacionados

Cliente OPC UA en Node-RED hacia MQTT con topics ISA-95

Usa node-red-contrib-opcua para suscribirte a variables OPC UA de un PLC o de un servidor de simulación, asignar los node IDs a topics MQTT de estilo ISA-95 y publicar los valores con calidad y marcas de tiempo.

10 min de lectura

Node-RED Dashboard 2.0 con MQTT: indicadores, gráficos y controles

Un tutorial práctico de Node-RED Dashboard 2.0: suscríbete a telemetría MQTT en directo, muéstrala en indicadores y en un gráfico de líneas, y controla un dispositivo con un interruptor que publica comandos y refleja el estado real.

9 min de lectura

Todos los artículos →

Tu broker MQTT privado, a unos pocos clics

Crea una cuenta, obtén las credenciales de tu broker y conecta tu primer dispositivo, sin servidores que instalar, parchear ni escalar.