La automatización de invernaderos mantiene la temperatura y la humedad en el rango que necesitan tus plantas conmutando ventiladores, calefactores y nebulizadores de forma automática, en lugar de depender de que alguien abra las ventanas en el momento justo. En este tutorial diseñarás los topics MQTT, escribirás un flujo de Node-RED que controla tres actuadores con histéresis y enclavamientos, añadirás un watchdog para datos obsoletos y harás que los nodos de relés sean seguros por sí mismos si se cae la red.
La arquitectura
El sistema tiene tres partes que solo se comunican a través de un broker MQTT:
Climate sensor (ESP32 + SHT31/BME280)
| publishes telemetry every 30 s
v
MQTT broker <----> Node-RED (decision logic)
^ |
| publishes /set |
+---------------------+
|
Relay node (ESP32 + 3 relays): fan, heater, mister
publishes /state after acting
Separar la medición, la lógica y la actuación tiene ventajas reales. Puedes sustituir el sensor sin tocar la lógica, cambiar las consignas desde un móvil sin volver a flashear nada y añadir más adelante un dashboard o un registrador de datos como un suscriptor más. Si necesitas repasar qué es un broker, lee primero sobre qué hace un broker MQTT.
Diseño de topics para la automatización de invernaderos
Usa el servidor MQTT público gratuito mientras construyes el sistema; sirve para pruebas y prototipos, no para datos de producción. Sustituye g2w6n3 por tu propia cadena aleatoria, porque el área pública es compartida y cualquiera puede leerla.
| Topic | Dirección | Payload | Retenido |
|---|---|---|---|
connectmq/demo/g2w6n3/greenhouse/climate |
sensor a todos | {"temp":27.4,"hum":58.2} |
sí |
connectmq/demo/g2w6n3/greenhouse/setpoints |
móvil/dashboard a Node-RED | {"heat_on":12,"vent_on":28,"mist_on":45} |
sí |
connectmq/demo/g2w6n3/greenhouse/fan/set |
Node-RED al relé | ON / OFF |
no |
connectmq/demo/g2w6n3/greenhouse/heater/set |
Node-RED al relé | ON / OFF |
no |
connectmq/demo/g2w6n3/greenhouse/mister/set |
Node-RED al relé | ON / OFF |
no |
connectmq/demo/g2w6n3/greenhouse/<actuator>/state |
relé a todos | ON / OFF |
sí |
connectmq/demo/g2w6n3/greenhouse/alarm |
Node-RED a todos | {"type":"stale_sensor"} |
no |
Las consignas son retenidas para que Node-RED reciba los valores actuales justo después de un reinicio. Los comandos no son retenidos, así que un relé que se reinicia no vuelve a ejecutar una instrucción antigua.
Construir el flujo de Node-RED
Crea el flujo con estos nodos. Si Node-RED es nuevo para ti, el tutorial de Node-RED MQTT cubre los conceptos básicos de configuración del broker.
| Nodo | Ajustes |
|---|---|
Configuración mqtt-broker |
Servidor broker.connectmq.com, puerto 1883, client ID nodered-gh-g2w6n3, protocolo MQTT 3.1.1 |
mqtt in "climate" |
Topic connectmq/demo/g2w6n3/greenhouse/climate, salida "a parsed JSON object" |
mqtt in "setpoints" |
Topic connectmq/demo/g2w6n3/greenhouse/setpoints, salida "a parsed JSON object" |
function "climate logic" |
Código más abajo, 1 salida, conectado desde ambos nodos mqtt in |
mqtt out "actuators" |
Topic vacío (se toma de msg.topic), QoS 1, retain false |
inject "watchdog tick" |
Repetir cada 60 segundos |
function "watchdog" |
Código más abajo, conectado al mismo mqtt out |
La función de lógica de clima
const BASE = "connectmq/demo/g2w6n3/greenhouse";
// Valores por defecto, sobrescritos por el mensaje retenido de consignas
const sp = flow.get("sp") || { heat_on: 12, heat_off: 14, vent_on: 28, vent_off: 25, mist_on: 45, mist_off: 55, mist_max_temp: 35 };
if (msg.topic.endsWith("/setpoints")) {
const p = msg.payload || {};
if (p.heat_on !== undefined) { sp.heat_on = p.heat_on; sp.heat_off = p.heat_on + 2; }
if (p.vent_on !== undefined) { sp.vent_on = p.vent_on; sp.vent_off = p.vent_on - 3; }
if (p.mist_on !== undefined) { sp.mist_on = p.mist_on; sp.mist_off = p.mist_on + 10; }
flow.set("sp", sp);
node.status({ fill: "blue", shape: "ring", text: "setpoints " + JSON.stringify(p) });
return null;
}
const t = Number(msg.payload.temp);
const h = Number(msg.payload.hum);
if (!Number.isFinite(t) || !Number.isFinite(h) || t < -20 || t > 60 || h < 0 || h > 100) {
node.warn("Implausible reading ignored: " + JSON.stringify(msg.payload));
return null;
}
flow.set("lastSeen", Date.now());
const st = flow.get("state") || { fan: "OFF", heater: "OFF", mister: "OFF" };
const next = Object.assign({}, st);
// Calefacción con histéresis
if (t <= sp.heat_on) next.heater = "ON";
else if (t >= sp.heat_off) next.heater = "OFF";
// Ventilación con histéresis
if (t >= sp.vent_on) next.fan = "ON";
else if (t <= sp.vent_off) next.fan = "OFF";
// Nebulizar solo cuando hace suficiente calor para evaporar y sin enfriar el cultivo
if (h <= sp.mist_on && t > 18 && t < sp.mist_max_temp) next.mister = "ON";
else if (h >= sp.mist_off || t <= 18) next.mister = "OFF";
// Enclavamientos: nunca calentar y ventilar a la vez, nunca nebulizar mientras se calienta
if (next.heater === "ON") { next.fan = "OFF"; next.mister = "OFF"; }
flow.set("state", next);
const out = [];
for (const a of ["fan", "heater", "mister"]) {
if (next[a] !== st[a] || msg.forceSend) {
out.push({ topic: `${BASE}/${a}/set`, payload: next[a] });
}
}
node.status({ fill: "green", shape: "dot", text: `${t}°C ${h}% F:${next.fan} H:${next.heater} M:${next.mister}` });
return [out];
Devolver [out] envía cada mensaje del array, uno tras otro, por la única salida hacia el nodo mqtt out. Los comandos solo se envían cuando cambia un estado, lo que mantiene bajo el tráfico y legibles los logs del relé.
La función watchdog
const BASE = "connectmq/demo/g2w6n3/greenhouse";
const last = flow.get("lastSeen") || 0;
const ageMin = (Date.now() - last) / 60000;
if (last && ageMin > 5 && !flow.get("staleAlarm")) {
flow.set("staleAlarm", true);
flow.set("state", { fan: "OFF", heater: "OFF", mister: "OFF" });
return [[
{ topic: `${BASE}/heater/set`, payload: "OFF" },
{ topic: `${BASE}/mister/set`, payload: "OFF" },
{ topic: `${BASE}/alarm`, payload: JSON.stringify({ type: "stale_sensor", minutes: Math.round(ageMin) }) }
]];
}
if (ageMin <= 5) flow.set("staleAlarm", false);
return null;
Si el sensor deja de reportar, el watchdog apaga el calefactor y el nebulizador, porque actuar con datos antiguos es peor que no hacer nada. El ventilador no se toca; sin datos, decide la protección propia del relé (ver más abajo).
Probar el flujo
Simula una tarde calurosa y seca y después una noche fría:
mosquitto_pub -h broker.connectmq.com -p 1883 -i sim-g2w6n3 -r \
-t 'connectmq/demo/g2w6n3/greenhouse/climate' -m '{"temp":30.2,"hum":40}'
mosquitto_pub -h broker.connectmq.com -p 1883 -i sim-g2w6n3 -r \
-t 'connectmq/demo/g2w6n3/greenhouse/climate' -m '{"temp":10.5,"hum":80}'
mosquitto_sub -h broker.connectmq.com -p 1883 -i mon-g2w6n3 \
-t 'connectmq/demo/g2w6n3/greenhouse/+/set' -v
El primer mensaje debería encender el ventilador y el nebulizador; el segundo debería apagar ambos y encender el calefactor. Cambia las consignas en vivo publicando {"vent_on":26} en el topic de consignas.
Hacer que el nodo de relés sea seguro por sí mismo
Node-RED decide, pero el nodo de relés nunca debe confiar ciegamente en la red. Incorpora estas reglas en su firmware:
- Tiempo máximo de funcionamiento por actuador. El nebulizador se apaga solo tras, por ejemplo, cinco minutos aunque no llegue ningún
OFF. Una válvula atascada puede encharcar un cultivo. - Corte local por sobretemperatura. Si el nodo de relés tiene su propia sonda de temperatura, apaga el calefactor por encima de un límite fijo, independientemente de los comandos.
- Estado seguro al desconectarse. Cuando MQTT se cae durante más de un minuto, el calefactor y el nebulizador se apagan. Decide si el ventilador debe encenderse o apagarse según tu clima; en regiones cálidas, ventilar suele ser la opción por defecto más segura.
- Publicar el estado tras actuar. Los topics
/statemuestran lo que realmente ocurrió, lo que permite a Node-RED y a los dashboards detectar un relé que ignora los comandos. - Last will. Conéctate con un will
offlineretenido en un topic de estado para que la ausencia de un relé se vea al instante.
Para el cableado de los relés y un sketch de ESP32 que se suscribe a los topics de comandos, consulta el tutorial de relé ESP32 con MQTT. Los calefactores y ventiladores a tensión de red deben conmutarse mediante contactores adecuados, instalados por personal cualificado.
Consejos de ajuste
- Márgenes de histéresis de 2 a 3 °C evitan los ciclos cortos, que desgastan relés y motores.
- La ubicación del sensor importa más que su precisión: protégelo del sol directo, móntalo a la altura de las plantas y dale circulación de aire.
- Las consignas de día y de noche son fáciles de añadir: un nodo
injectal amanecer y al atardecer puede publicar nuevas consignas retenidas. - Regístralo todo. Un flujo de Node-RED o una instancia de Telegraf suscritos a
greenhouse/#te dan el historial para ajustar las consignas a lo largo de una campaña, ya sea de flores en la Sabana de Bogotá o de hortalizas en Almería.
Para una visión más amplia de sensores, conectividad y diseño de topics en toda una explotación, lee la guía de agricultura inteligente IoT, y combina este invernadero con el sistema de riego automático para el riego.
Vigilar el invernadero a distancia
La automatización solo es fiable si puedes ver lo que está haciendo. Tres vistas cubren la mayoría de las necesidades. Una página de Node-RED Dashboard con indicadores de temperatura y humedad, interruptores que muestran los topics /state de los actuadores y deslizadores que publican nuevas consignas ofrece a los productores un panel de control sencillo en el móvil. Un historiador, como Telegraf escribiendo en InfluxDB, te permite comparar las noches de esta semana con las de la anterior y detectar un calefactor que funciona más tiempo que antes. Y una vista de topics en vivo en el lado del broker muestra cada mensaje de comando y de estado en el momento en que se produce.
Cuando el broker es ConnectMQ, el explorador de topics en vivo y el dashboard del portal ofrecen esta última vista sin instalar nada, y el cliente MQTT del navegador es práctico para publicar una consigna de prueba desde un PC dentro del invernadero. Uses la herramienta que uses, compara regularmente /set con /state: un comando que nunca se confirma apunta a un relé que falla, un cable suelto o un nodo que ha perdido el Wi-Fi.
Pasar a producción
Cuando la lógica funcione, sal del área pública de pruebas. Crea un broker privado, cambia el nodo mqtt-broker al puerto 8883 con TLS activado e introduce el usuario, la contraseña y el client ID emitidos. Cada dispositivo recibe sus propias credenciales. Un broker MQTT gestionado mantiene el broker en marcha aunque falle el PC del invernadero y te permite consultar el clima desde cualquier lugar. La guía de automatización IoT muestra cómo este patrón se extiende a hogares y edificios.
Gestiona tu invernadero con ConnectMQ
ConnectMQ da a tu invernadero un broker privado con TLS, un explorador de topics en vivo para observar comandos y estados, y reglas de alerta por umbral como alarma de respaldo frente a heladas o sobrecalentamiento. Crea un broker Serverless gratuito en app.connectmq.com, sin tarjeta de crédito, y apunta a él Node-RED y tus nodos ESP32.
Preguntas frecuentes
¿Puede funcionar la automatización de invernaderos sin internet?
Sí, si el broker y Node-RED están en la red local. Con un broker en la nube ganas acceso remoto, así que coloca las protecciones críticas en el firmware de los relés para que el invernadero siga seguro durante un corte.
¿Qué sensor de temperatura es mejor para un invernadero?
Un SHT31 o SHT4x soporta mejor la humedad alta que un DHT22. Elijas el que elijas, protégelo del sol directo con una garita de radiación ventilada.
¿Por qué mi ventilador se enciende y se apaga continuamente?
La diferencia entre los umbrales de encendido y apagado es demasiado pequeña, o el sensor está en la corriente de aire del ventilador. Amplía la histéresis y cambia el sensor de sitio.
¿Debe tomar las decisiones de control Node-RED o el ESP32?
Usa Node-RED para la lógica flexible y las consignas, y mantén límites de seguridad sencillos en el ESP32 para que los actuadores pasen a un estado seguro si se pierde la conexión.



