Tema
Sesión 1 · Fundamentos de Node-RED y flujos de control
Objetivos de la sesión
- Instalar Node-RED de forma nativa con npm, identificar los ficheros de su directorio de usuario y levantar después el entorno del curso con Docker.
- Explicar la programación basada en flujos y la estructura del objeto
msg. - Construir flujos con los nodos
inject,debug,change,switchyfunction. - Recibir por MQTT una magnitud del invernadero simulado y convertirla a un valor numérico.
- Programar una decisión de control basada en umbrales guardados en el contexto de flujo.
Conexión con el sistema del invernadero
Esta sesión construye la lógica de procesado: la pieza que recibe una lectura, la interpreta y decide qué hacer. En las sesiones siguientes esa decisión se enriquecerá con la previsión meteorológica (S2), actuará sobre la bomba (S3), se verá en el panel (S4), quedará registrada (S5) y generará alertas (S6).
Este es el flujo que construirás en la práctica guiada. Los recuadros discontinuos son el contexto de flujo: los umbrales y el último estado se guardan ahí, no en los nodos.
Conceptos clave
Programación basada en flujos
En Node-RED un programa es un flujo: un grafo de nodos unidos por cables. Cada nodo recibe un mensaje, hace una cosa y pasa uno o más mensajes al siguiente. No hay un bucle principal: el motor (Node.js) ejecuta cada nodo cuando le llega un mensaje.
Ya conoces la idea desde Python:
Script Python con paho-mqtt | Flujo de Node-RED |
|---|---|
client.connect(...) y loop_forever() | Nodo de configuración mqtt-broker: Node-RED mantiene la conexión |
client.subscribe("invernadero/suelo/humedad") | Nodo mqtt in con ese topic |
Callback on_message(client, userdata, msg) | Todo lo que cuelga del cable de salida del mqtt in |
if valor < umbral: ... | Nodo switch |
print(...) | Nodo debug |
La diferencia está en lo que viene después: en Node-RED añadir un panel, una base de datos o un bot de Telegram es añadir nodos al mismo flujo, no reescribir el script.
Repasar MQTT
Si no tienes claro qué son el broker, los topics, el QoS o el retain, el anexo MQTT en lo que necesitas lo resume con ejemplos del invernadero.
El mensaje msg
Todo lo que circula por un cable es un objeto JavaScript llamado msg:
| Propiedad | Contenido |
|---|---|
msg.payload | El dato principal. Con MQTT, el contenido del mensaje: "42.5" |
msg.topic | El topic MQTT o una etiqueta libre: "invernadero/suelo/humedad" |
msg._msgid | Identificador único que asigna Node-RED |
| cualquier otra | Puedes añadir las tuyas: msg.estado, msg.unidad… |
Documentación: Working with messages.
Nodos de esta sesión
| Nodo | Para qué |
|---|---|
inject | Genera un mensaje: al pulsar, al desplegar o periódicamente |
debug | Muestra el mensaje en la barra lateral de depuración |
change | Modifica propiedades sin escribir código: asignar, borrar, mover, expresiones JSONata |
switch | Enruta el mensaje a una salida u otra según reglas |
function | Código JavaScript con acceso a msg, al contexto y al estado del nodo |
mqtt in | Suscripción a un topic MQTT |
Documentación: Core nodes y Writing functions.
Contexto
Los mensajes no se acuerdan de nada. Para guardar estado entre mensajes (el último valor, unos umbrales, un contador) Node-RED ofrece el contexto en tres ámbitos: nodo (context), flujo (flow, compartido por los nodos de la pestaña) y global (global, todo Node-RED). Documentación: Working with context.
Práctica guiada paso a paso
Cada paso tiene la misma estructura: para qué sirve, qué hacer (con los nombres exactos que verás en el editor, que está en inglés), qué debes ver al terminar y si no funciona. Las operaciones básicas del editor se explican con detalle la primera vez que aparecen; después solo se nombran.
Paso 1 · Instalar Node-RED en tu PC (40 min)
Para qué sirve. Antes de usar el entorno del curso vas a instalar Node-RED a mano. Así verás qué es realmente (una aplicación de Node.js), dónde guarda tu trabajo y cómo se maneja el editor.
1.1 · Instala Node.js en su versión LTS. Node-RED 4 necesita Node.js 18 o superior.
powershell
winget install OpenJS.NodeJS.LTS
# o descarga el instalador LTS de https://nodejs.orgbash
brew install nodebash
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt-get install -y nodejsCierra la terminal, abre una nueva (para que reconozca los comandos recién instalados) y comprueba las versiones:
bash
node -v # por ejemplo v22.x
npm -v # por ejemplo 10.x1.2 · Instala Node-RED como paquete global de npm. Tarda uno o dos minutos:
powershell
npm install -g --unsafe-perm node-redbash
sudo npm install -g --unsafe-perm node-red1.3 · Arráncalo escribiendo node-red en la terminal. Deja esa terminal abierta: Node-RED funciona mientras el comando siga en marcha. Verás un log como este (las rutas y versiones dependen de tu equipo):
text
Welcome to Node-RED
===================
27 Sep 19:05:34 - [info] Node-RED version: v4.1.15
27 Sep 19:05:34 - [info] Node.js version: v22.x
27 Sep 19:05:41 - [info] Settings file : C:\Users\tu-usuario\.node-red\settings.js
27 Sep 19:05:41 - [info] Context store : 'default' [module=memory]
27 Sep 19:05:41 - [info] User directory : C:\Users\tu-usuario\.node-red
27 Sep 19:05:41 - [info] Flows file : C:\Users\tu-usuario\.node-red\flows.json
27 Sep 19:05:41 - [info] Server now running at http://127.0.0.1:1880/
27 Sep 19:05:41 - [info] Starting flowsLocaliza en tu log las cuatro líneas importantes: User directory (dónde guarda todo), Settings file (su configuración), Flows file (tus flujos) y Server now running (la dirección del editor).
1.4 · Abre el editor en http://localhost:1880. Estas son sus partes:

| # | Zona | Para qué sirve |
|---|---|---|
| 1 | Paleta | Todos los nodos disponibles, agrupados por secciones (common, function, network…). Se arrastran de aquí al lienzo. El buscador filter nodes de arriba los filtra por nombre |
| 2 | Lienzo | Donde construyes el flujo. Cada pestaña de arriba es un flujo distinto |
| 3 | Deploy | Envía los cambios al motor. Hasta que no pulsas Deploy, nada de lo que dibujas se ejecuta |
| 4 | Barra lateral | Varias pestañas: info, ayuda, debug (el insecto, donde aparecen los mensajes) y ⚙ (configuración). Si no la ves, pulsa Ctrl+Espacio |
| 5 | Botón del inject | El cuadradito a la izquierda del nodo: al pulsarlo, el nodo envía un mensaje |
1.5 · Tu primer flujo. Las tres operaciones básicas del editor:
- Añadir un nodo: en la paleta, sección common, arrastra el nodo inject al lienzo y suéltalo. Haz lo mismo con el nodo debug, a su derecha.
- Unir dos nodos: cada nodo tiene un puerto de salida (cuadradito gris a la derecha) y/o un puerto de entrada (a la izquierda). Pincha en el puerto de salida del
injecty, sin soltar, arrastra hasta el puerto de entrada deldebug. Aparece un cable gris. - Desplegar: pulsa Deploy. El botón se pone gris hasta que vuelvas a cambiar algo.
Ahora pulsa el botón del inject (zona 5) y abre la pestaña debug de la barra lateral.
Qué debes ver. Un mensaje con la fecha, node: … y un número largo en azul, como 1790528874059: es la marca de tiempo (milisegundos desde 1970) que el inject envía por defecto.
Si no funciona.
- No aparece nada en debug: falta pulsar Deploy, o el cable no llega al puerto de entrada del
debug. Un cable bien conectado se ve continuo de un puerto a otro. - El nodo tiene un triángulo rojo o un punto azul encima: el triángulo indica un error de configuración y el punto azul, cambios sin desplegar.
1.6 · Instala un nodo desde la paleta. Node-RED se amplía con nodos que publica la comunidad.
- Abre el menú ☰ (arriba a la derecha) → Manage palette.
- Pestaña Install. En el buscador escribe
node-red-node-random. - Pulsa install en el resultado y confirma.

Qué debes ver. Al terminar, el nodo random aparece en la sección function de la paleta. Pruébalo: inject → random → debug, Deploy, pulsa el inject y en debug sale un número entre 1 y 10.
1.7 · Explora el directorio de usuario desde otra terminal, sin parar Node-RED:
powershell
dir $HOME\.node-red
type $HOME\.node-red\package.jsonbash
ls -la ~/.node-red
cat ~/.node-red/package.jsonQué debes ver. Los ficheros settings.js, flows.json, flows_cred.json (si ya hay credenciales), package.json y la carpeta node_modules. En package.json aparece node-red-node-random: lo que instalas desde la paleta se guarda en tu directorio de usuario, no junto a Node-RED. Qué es cada fichero lo tienes en Node-RED como plataforma.
1.8 · Comprueba que el trabajo persiste. En la terminal donde corre Node-RED pulsa Ctrl+C (lo para) y vuelve a escribir node-red. Recarga el editor: tu flujo sigue ahí. Abre flows.json con un editor de texto: es un JSON con tus nodos, el mismo formato que obtienes al exportar un flujo.
1.9 · Para Node-RED con Ctrl+C antes de seguir. Si quieres conservar el flujo, antes selecciónalo todo en el lienzo (Ctrl+A), pulsa Ctrl+E (Export) y Download.
Puerto 1880 ocupado
El Node-RED nativo y el de Docker usan el mismo puerto. Si en el paso 2 docker compose up falla con port is already allocated, o el editor que abres no es el que esperas, es que el nativo sigue en marcha: páralo con Ctrl+C en su terminal.
Sin permisos de administrador
Si no puedes instalar paquetes globales, ejecuta Node-RED sin instalarlo con npx node-red.
Paso 2 · Pasar al entorno del curso con Docker (20 min)
Para qué sirve. Una instalación nativa sirve para un Node-RED. El sistema del invernadero necesita además un broker, una base de datos y el simulador, con las mismas versiones en todos los PCs. Docker Compose levanta todo eso igual en cada máquina con un solo comando.
Qué hacer.
Descarga el entorno: entorno-docker.zip. Descomprímelo donde quieras trabajar (por ejemplo, en el Escritorio): se crea una carpeta
docker. Abre una terminal dentro de ella (en el explorador de Windows: clic derecho en la carpeta → Abrir en Terminal).Crea tu fichero de configuración copiando la plantilla:
bashcp .env.example .envEn PowerShell funciona igual; también puedes copiar el fichero y renombrarlo a mano.
No hace falta cambiar nada del
.env, pero ábrelo con un editor de texto (el Bloc de notas vale) para ver qué contiene. Las primeras líneas eligen el broker al que se conecta Node-RED: elmosquittolocal, donde publica el simulador del invernadero.ini# Broker local + simulador del invernadero COMPOSE_PROFILES=simulador MQTT_HOST=mosquittoEl resto son las contraseñas del broker, la velocidad del simulador y los datos de las sesiones siguientes. Debajo hay un segundo bloque, comentado, para conectarse al invernadero real del laboratorio; todo el módulo se hace con el simulador, y si algún día usas los dispositivos físicos, Trabajar con el invernadero real explica qué cambiar.
Arranca el entorno. La primera vez tarda unos minutos, porque descarga y construye las imágenes:
bashdocker compose up -d --build docker compose psAbre http://localhost:1880.
Qué debes ver.
docker compose pslistanode-red,influxdb,mosquittoysimulador, todos con estado running o Up.El editor está vacío: es otra instalación, con su propio directorio de usuario. Compruébalo con:
bashdocker compose exec node-red ls -la /dataVerás los mismos tipos de fichero que en
~/.node-red. Si exportaste el flujo del paso 1, impórtalo aquí: menú ☰ → Import (o Ctrl+I) → select a file to import → Import.
Si no funciona.
unknown shorthand flag: 'd' in -dodocker: unknown command: docker compose: tienes el clientedockerpero no el plugin Compose. Pasa, por ejemplo, si instalaste solo el paquete Docker CLI conwinget. Ese paquete tampoco trae el motor que ejecuta contenedores Linux, que son los que usa el curso. Instala Docker Desktop (siguiente punto) y desinstala el otro paquete para que no se mezclen:winget uninstall Docker.DockerCLI.docker composeno se reconoce ofailed to connect to the docker API: Docker Desktop no está instalado o no está arrancado. En Windows, instálalo conwinget install Docker.DockerDesktop(necesita permisos de administrador y WSL 2, y puede pedir reiniciar), ábrelo y espera a que el icono de la ballena diga Engine running. Comprueba que todo está en orden condocker compose versionydocker run --rm hello-world.ports are not available: exposing port TCP 0.0.0.0:1883…Only one usage of each socket address: otro programa usa ya el puerto 1883. Lo habitual es un Mosquitto que instalaste en tu PC en la práctica anterior. En Windows, averigua cuál es conGet-NetTCPConnection -LocalPort 1883 -State Listen(la columna OwningProcess es su PID) y detenlo: si es el servicio de Mosquitto, conStop-Service mosquittoen una terminal de administrador. Si pasa con el 1880, es el Node-RED nativo del paso 1. Después repitedocker compose up -d.- El editor no carga: espera un minuto y mira el log con
docker compose logs node-red.
El simulador
El simulador reproduce el invernadero: publica los mismos topics y con el mismo formato que sus sensores, y responde a los comandos de la bomba y el ventilador. SIM_VELOCIDAD=60 hace que un minuto real equivalga a una hora simulada: verás secarse el suelo en pocos minutos. Mira qué publica con docker compose exec mosquitto mosquitto_sub -u alumno -P alumno -t "invernadero/#" -v (sal con Ctrl+C). También tiene un panel web en http://localhost:8090 para ver el invernadero en tiempo real y provocar situaciones como una helada o una tormenta. La guía del simulador explica qué valores publica, cómo encender la bomba y el ventilador y cómo provocar fallos.
Paso 3 · Anatomía de msg (15 min)
Para qué sirve. Todo lo que viaja por un cable es un objeto msg. En este paso vas a ver qué contiene, a cambiar sus valores y a añadirle una propiedad tuya. Es la base de todo lo demás.
3.1 · Configura el inject. Ya en el Node-RED de Docker, crea un inject unido a un debug, como en el paso 1.5. Después haz doble clic en el inject: se abre su diálogo de configuración en un panel a la derecha.

- En la fila msg.payload, pincha en la flechita ▾ del desplegable de tipo (a la izquierda del valor) y elige string (icono az). Escribe
42.5como valor. - En la fila msg.topic, escribe
invernadero/suelo/humedad. Si esa fila no existe, añádela con el botón +add de debajo de la lista. - En Name, escribe
humedad 42.5: el nombre es lo que se ve en el lienzo. - Pulsa Done (arriba a la derecha del diálogo) para aceptar. Cancel descarta los cambios.
3.2 · Configura el debug para ver el mensaje entero. Doble clic en el debug:

- En Output, abre el desplegable y elige complete msg object. Por defecto solo muestra
msg.payload. - En Name, escribe
mensaje completo. - Done, Deploy y pulsa el botón del
inject.
Qué debes ver. En la pestaña debug, un mensaje con : msg : Object. Pincha en la flechita ▸ para desplegarlo: aparecen _msgid, payload: "42.5" y topic: "invernadero/suelo/humedad".
3.3 · Añade una propiedad con un nodo change. El nodo change modifica el mensaje sin escribir código. Vas a añadir msg.unidad con el valor %.
Insértalo en el cable: busca change en la paleta (sección function, color amarillo), arrástralo y suéltalo encima del cable que une el
injectcon eldebug. Cuando el cable se vuelva discontinuo, suelta: el nodo queda insertado entre los dos.Otra forma: pincha en el cable (se resalta), pulsa Supr para borrarlo, y cablea
inject→change→debug.
Configúralo: doble clic en el
change. Trae una regla, que se lee como una frase: Setmsg.payloadto the value …
- En el campo de la derecha de msg., borra
payloady escribeunidad. - En to the value, deja el tipo az (texto) y escribe
%. - En Name, escribe
añadir unidad. - Done.
- En el campo de la derecha de msg., borra
Deploy y pulsa el
inject.
Qué debes ver. El mensaje llega con una propiedad más, unidad: "%":

Si no funciona.
- Solo sale
"42.5": eldebugsigue en msg.payload; cambia Output a complete msg object. - No aparece
unidad: revisa que la regla es Set (no Change ni Delete) y que no hay un espacio antes o después deunidad.
Paso 4 · Conectar con el invernadero: mqtt in (30 min)
Para qué sirve. Recibir una lectura del invernadero, igual que hacía tu script Python con subscribe. Aquí aparece un concepto nuevo: el nodo de configuración, que guarda los datos de conexión al broker y comparten todos los nodos MQTT.
4.1 · Añade el mqtt in. En la paleta, sección network, arrastra mqtt in al lienzo y haz doble clic en él.

4.2 · Crea el nodo de configuración del broker. En Server aparece Add new mqtt-broker...: pulsa el lápiz de su derecha. Se abre un segundo diálogo encima del primero.
En la pestaña Connection:

| Campo | Valor | Por qué |
|---|---|---|
| Name | Broker invernadero | Es el nombre con el que lo elegirás en otros nodos |
| Server | ${MQTT_HOST} | Se sustituye por la variable del .env: si cambia el broker, cambias el .env, no el flujo |
| Port | ${MQTT_PORT} | Igual, normalmente 1883 |
| Client ID | vacío | Node-RED genera uno aleatorio. Si dos alumnos usan el mismo, el broker los expulsa en bucle |
En la pestaña Security:

- Username:
alumno. - Password: la de
MQTT_ALUMNO_PASSen tu.env(alumnosi no la cambiaste).
Pulsa Add (arriba a la derecha). Vuelves al diálogo del mqtt in, con Broker invernadero ya elegido en Server.
4.3 · Configura la suscripción en el mqtt in:
| Campo | Valor |
|---|---|
| Action | Subscribe to single topic |
| Topic | invernadero/suelo/humedad |
| QoS | 0 |
| Output | a String: queremos el texto tal cual llega |
| Name | humedad del suelo |
Done. Une su salida a un debug nuevo y pulsa Deploy.
Qué debes ver. Bajo el mqtt in, un cuadradito verde con el texto connected. En debug, una lectura cada pocos segundos (por ejemplo "50.1"). La primera llega nada más desplegar: es el valor retenido (retain) que guarda el broker (qué es el retain).
Si no funciona.
- Pone
connectingodisconnected: comprueba queMQTT_ALUMNO_PASSdel.envcoincide con la contraseña que has puesto, y quedocker compose psmuestramosquittoysimulador. - Pone
connectedpero no llega nada: revisa el topic letra a letra; MQTT distingue mayúsculas y minúsculas. - El estado alterna entre
connectedydisconnectedcada pocos segundos: otro cliente usa tu mismo Client ID y el broker os expulsa por turnos. Abre el nodoBroker invernaderoy deja Client ID vacío, para que Node-RED genere uno único.
Topics disponibles (todos con payload numérico en texto):
| Topic | Magnitud |
|---|---|
invernadero/suelo/humedad | Humedad del suelo (%) |
invernadero/dht22/temperatura · invernadero/dht11/temperatura | Temperatura ambiente (ºC) |
invernadero/dht22/humedad · invernadero/dht11/humedad | Humedad ambiente (%) |
invernadero/ultrasonidos/distancia | Distancia al agua del depósito (cm) |
invernadero/balanza/peso | Peso (g) |
invernadero/tds/tds | Sólidos disueltos en el agua (ppm) |
Paso 5 · De texto a número con change (15 min)
Para qué sirve. Los ESP32 publican texto: "50.1", no 50.1. Si comparas texto con un número ("9" < "25" es false porque compara carácter a carácter), obtendrás resultados erróneos sin ningún aviso. Hay que convertirlo.
Qué hacer.
- Inserta un
changeentre elmqtt iny eldebug(como en el paso 3.3) y ábrelo. - En la regla deja Set
msg.payload. - En to the value, abre el desplegable de tipo y elige expression (icono J:). Escribe
$number(payload). - Name:
texto → número. Done y Deploy.

$number(...) es una función de JSONata, un lenguaje de expresiones que Node-RED usa en muchos campos. Dentro de la expresión, payload significa msg.payload.
Qué debes ver. Para comprobar la diferencia, deja un debug directamente en la salida del mqtt in y otro detrás del change:


El texto aparece en rojo, entre comillas y con tipo string; el número, en azul, sin comillas y con tipo number.
Si no funciona. Sale NaN o un error: el tipo del valor no es expression (con az estarías guardando literalmente el texto $number(payload)).
Paso 6 · Probar sin sensores (10 min)
Para qué sirve. Probar la lógica con valores concretos, sin depender de lo que marque el sensor en ese momento. Son tus casos de prueba: los usarás en los pasos siguientes y en el entregable.
Qué hacer.
- Añade tres
inject. En cada uno: msg.payload de tipo string con los valores20.0,30.0y50.0, y Nameprueba: 20 %,prueba: 30 %,prueba: 50 %. - Une la salida de los tres a la entrada del
changetexto → número. Una entrada puede recibir tantos cables como quieras. - Deploy.
Qué debes ver. Al pulsar cada inject, el debug de detrás del change muestra 20, 30 o 50 como número. Es exactamente el mismo camino que recorren las lecturas reales.
Paso 7 · Umbrales en el contexto de flujo (20 min)
Para qué sirve. Los umbrales no deben estar escritos a fuego dentro de los nodos: se cargan una vez en el contexto de flujo (una memoria compartida por los nodos de la pestaña) y todos los nodos los leen de ahí. Así, cambiar un umbral es cambiar un solo sitio.
7.1 · Un inject que se dispara solo al desplegar. Añade un inject y ábrelo:

Name:
cargar umbrales.En msg.payload, elige el tipo JSON (icono {}) y escribe:
json{ "suelo": { "critico": 25, "aviso": 35 } }El botón … de la derecha abre un editor más cómodo que avisa si el JSON está mal escrito.
Si hay fila msg.topic, bórrala con su ×: no hace falta.
Más abajo, marca Inject once after
0.1seconds, then y deja Repeat en none: se disparará una vez, justo después de cada Deploy.
7.2 · Guardarlo en el contexto. Une el inject a un change con la regla Set flow. umbrales to the value msg. payload. Para escribir flow. en el primer campo, abre su desplegable de tipo y elige flow.; en el segundo, elige msg.. Name: flow.umbrales = payload. Done y Deploy.
Qué debes ver. Abre la pestaña Context Data de la barra lateral (icono de base de datos; si no aparece, búscala en la flechita ▾ del extremo derecho de las pestañas). En la sección Flow, pulsa el botón de refrescar (↻):

umbrales aparece con suelo.critico: 25 y suelo.aviso: 35. estadoSuelo y ultimaHumedadSuelo aparecerán cuando completes el paso 9.
Si no funciona. umbrales no aparece: pulsa ↻ (esta pestaña no se refresca sola), y comprueba que el inject tiene marcado Inject once after.
Paso 8 · Clasificar con switch (20 min)
Para qué sirve. El nodo switch es el if de Node-RED: envía cada mensaje por una salida u otra según unas reglas. Los dos umbrales dividen el rango de humedad en tres bandas. Así se clasificaría un día sin riego:
8.1 · Configura el switch. Añade un switch (sección function) detrás del change texto → número y ábrelo:

Name:
clasificar. Property:msg.payload(ya viene así).La primera regla ya existe. En su primer desplegable elige <. En el desplegable de tipo del valor elige flow. y escribe
umbrales.suelo.critico.Pulsa +add (bajo la lista) para una segunda regla: <, tipo flow.,
umbrales.suelo.aviso.Pulsa +add otra vez y en el primer desplegable elige otherwise (el "en otro caso").
Abajo del todo, cambia checking all rules por stopping after first match:
Sin esta opción, un 20 % cumpliría las dos primeras reglas (20 < 25 y 20 < 35) y saldría por las dos salidas.
Done. El nodo tiene ahora tres puertos de salida, numerados de arriba abajo; si pasas el ratón por uno, ves su número.
8.2 · Etiqueta cada salida. Añade tres change, uno por salida, cada uno con la regla Set msg. estado to the value (tipo az) crítico, aviso o normal. Llámalos estado = crítico, estado = aviso y estado = normal, y une la salida 1 al primero, la 2 al segundo y la 3 al tercero.
Qué debes ver. Conecta un debug en complete msg object a cada change, Deploy, y pulsa los tres inject de prueba: 20 llega con estado: "crítico", 30 con "aviso" y 50 con "normal".
Si no funciona. Todo sale como normal: los umbrales no están en el contexto (repasa el paso 7) o el tipo del valor de las reglas quedó en az en lugar de flow., con lo que compara con el texto "umbrales.suelo.critico".
Paso 9 · Decidir con function (25 min)
Para qué sirve. Cuando la lógica no cabe en un switch o un change, el nodo function permite escribir JavaScript. Aquí decidirá si hay que regar y guardará el último estado.
Qué hacer.
Añade un function (sección function, naranja) y une a su entrada las salidas de los tres
changede estado.Ábrelo. Name:
decidir riego. En la pestaña On Message borra lo que haya y pega el código:
js// Al arrancar pueden llegar lecturas retenidas antes que los umbrales: // sin umbrales, el switch no ha podido clasificar bien la lectura if (!flow.get("umbrales")) { node.warn("Lectura ignorada: los umbrales aún no están cargados"); return null; } // Guarda el último estado en el contexto de flujo y decide la acción const anterior = flow.get("estadoSuelo"); flow.set("estadoSuelo", msg.estado); flow.set("ultimaHumedadSuelo", msg.payload); const accion = msg.estado === "crítico" ? "regar" : "no regar"; msg.payload = { humedad: msg.payload, estado: msg.estado, accion: accion, cambio: msg.estado !== anterior // true solo cuando cambia el estado }; node.status({ fill: msg.estado === "normal" ? "green" : msg.estado === "aviso" ? "yellow" : "red", shape: "dot", text: `${msg.payload.humedad} % → ${accion}` }); return msg;Done. Une la salida del
functiona undebugllamadodecisión(en msg.payload) y pulsa Deploy.
Qué hace el código:
flow.get(...)/flow.set(...)leen y escriben el contexto de flujo del paso 7.return msgenvía el mensaje por la salida;return nullno envía nada.- La comprobación del principio evita una condición de carrera. Los topics del invernadero llevan retain, así que al desplegar llega enseguida la última lectura, a veces antes de que el
injecthaya cargado los umbrales. Sin ellos, elswitchcompara conundefined, todas las reglas fallan y la lectura sale por otherwise: se clasificaría comonormalsin avisar. node.status(...)muestra un indicador bajo el nodo: útil para ver el estado sin abrir la depuración.cambioservirá en la sesión 6 para avisar solo cuando el estado cambia, no con cada lectura.
Qué debes ver. Pulsa los tres inject de prueba. En debug, una decisión por cada uno, y bajo el nodo decidir riego un punto de color con el último valor:

| Entrada | estado | accion |
|---|---|---|
| 20 | crítico | regar |
| 30 | aviso | no regar |
| 50 | normal | no regar |
Después deja correr los datos reales: cada lectura del sensor produce una decisión.
Si no funciona.
- Aparece
Lectura ignorada…en debug: es el aviso de la condición de carrera. Es normal justo tras un Deploy; si se repite siempre, elinjectcargar umbralesno se está disparando. - Error
SyntaxErroral pulsar Done: falta o sobra un paréntesis o una llave; el editor delfunctionmarca la línea con una cruz roja.
Si te atascas, puedes descargar el flujo de referencia de la práctica guiada: s1-control-riego.json. Impórtalo con menú ☰ → Import, abre el nodo Broker invernadero (doble clic en el mqtt in → lápiz de Server) y escribe el usuario y la contraseña en Security: las credenciales nunca se exportan.
Node-RED como plataforma
Dentro de una instalación de Node-RED
Todo lo que distingue a tu Node-RED de otro está en su directorio de usuario: ~/.node-red en la instalación nativa y /data en el contenedor.
| Fichero | Contenido |
|---|---|
settings.js | Configuración del runtime: puerto, seguridad del editor, almacenamiento del contexto… |
flows.json | Tus flujos: lo que guardas al pulsar Deploy |
flows_cred.json | Las credenciales de los nodos (contraseñas, tokens), cifradas y separadas de los flujos |
package.json y node_modules/ | Los nodos instalados desde la paleta |
.config.*.json | Estado interno del editor; no lo toques |
Hacer copia de seguridad de un Node-RED es copiar ese directorio. En la sesión 6 editarás settings.js para proteger el editor y cifrar las credenciales.
Contexto: dónde vive el estado
- La pestaña Context Data muestra el contexto de nodo, flujo y global. Úsala para depurar.
- Por defecto el contexto está en memoria: se pierde al reiniciar Node-RED. Por eso los umbrales se cargan con un
injectonce. - Usa
flowpara lo que comparten los nodos de una pestaña yglobalsolo para lo que necesiten varias pestañas.
Organizar el flujo
- Nombres: cada nodo con un nombre que diga qué hace (
texto → número, nochange). - Comentarios: nodos
commentcomo títulos de sección. Su pestaña info admite Markdown.
Importar y exportar
- Ctrl+E exporta la selección, la pestaña o todos los flujos a JSON. Ctrl+I importa.
- El JSON incluye los nodos de configuración (el broker) pero no las credenciales: por eso puedes compartir flujos sin filtrar contraseñas.
- Exportar es la forma de entregar tu trabajo y de guardar copias: hazlo al final de cada sesión.
Entregable y evidencias para el informe
Qué debe funcionar
Amplía el flujo de la práctica guiada para que controle también la temperatura:
Suscríbete además a
invernadero/dht22/temperaturay conviértela a número.Añade al
injectde umbrales los de temperatura, por ejemplo:json{ "suelo": { "critico": 25, "aviso": 35 }, "temperatura": { "aviso": 30, "critico": 35 } }Clasifica cada temperatura como
normal,avisoocrítico. Cuidado: aquí el peligro está en los valores altos.Una única función de decisión que, con la última lectura de cada sensor guardada en el contexto, produzca:
json{ "regar": true, "ventilar": false, "suelo": "crítico", "temperatura": "normal" }Criterio mínimo: regar si el suelo está en estado crítico y ventilar si la temperatura está en aviso o crítico.
Casos de prueba con
injectque cubran los tres estados de cada magnitud.
Cómo se comprueba
- Con los
injectde prueba, cada combinación produce la decisión esperada. - Con los datos del simulador, la decisión se actualiza al llegar cada lectura.
- Cambiar un umbral en el
injecty volver a pulsarlo cambia el comportamiento sin tocar ningún otro nodo.
Criterios de calidad: umbrales solo en el contexto (ningún número "mágico" dentro de switch o function), nodos con nombre y flujo organizado con comentarios.
Evidencias que debes guardar
- [ ] Captura de la terminal con Node-RED nativo arrancado (versiones y User directory) y del listado de tu
~/.node-red. - [ ] Captura del flujo completo.
- [ ] Captura de la depuración con al menos un caso de cada estado para cada magnitud.
- [ ] Captura de Context Data con los umbrales y los últimos valores guardados.
- [ ] Flujo exportado:
s1-<tu-apellido>.json. - [ ] En el informe, respuesta breve a:
- ¿Dónde se guardan tus flujos en la instalación nativa y en Docker? ¿Qué ganas usando Docker?
- ¿Qué ventajas e inconvenientes ves frente a tu script Python de la práctica anterior?
- ¿Por qué hay que convertir el payload a número y qué pasaría si no lo hicieras?
- ¿Por qué el
switchdebe detenerse en la primera coincidencia?