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
- Elige un periodo de retención y aplícalo en el código o en SQL, no a base de esperanza.
- Reduce la resolución antes de almacenar si solo muestras promedios.
- 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.
- Vacía el gráfico antes de reproducir para evitar líneas duplicadas tras los deploys.
- Haz copia de seguridad del directorio de contexto o del archivo de base de datos junto con el resto de tu proyecto Node-RED.
- 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.



