Automatización IoT

Abridor de puerta de garaje MQTT casero con ESP32, relé y sensor reed

Convierte el motor de tu puerta de garaje en uno inteligente con un ESP32, un relé que pulsa por ti el botón de pared y un sensor reed que informa del estado real de la puerta como mensaje MQTT retenido.

Abridor de puerta de garaje MQTT construido con un ESP32, un módulo de relé y un sensor reed magnético en el marco de la puerta
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

Un abridor de puerta de garaje MQTT te permite abrir, cerrar y, sobre todo, comprobar tu puerta de garaje desde cualquier lugar usando el motor que ya tienes. En este tutorial conectarás un ESP32 a un relé que "pulsa" el botón de pared y a un sensor reed que informa de si la puerta está realmente cerrada, y publicarás ese estado como mensaje MQTT retenido para que cualquier aplicación vea la verdad en el momento en que se conecta.

Cómo funciona el abridor

Casi todos los motores de puerta de garaje residenciales tienen bornes para un pulsador de pared. Puentear esos dos bornes brevemente equivale a pulsar el botón: la puerta arranca, se detiene o invierte el sentido según dónde se encuentre. Eso hace que la parte de hardware sea sencilla y no invasiva:

  • Un relé conectado en paralelo con el botón de pared da una "pulsación" de 500 ms.
  • Un sensor reed en el marco, con un imán en la puerta, se cierra cuando la puerta está completamente cerrada.
  • El ESP32 escucha los comandos, activa el relé y publica el estado del sensor reed.

La decisión de diseño clave es que el abridor nunca supone lo que ha hecho la puerta. Publica lo que ve el sensor. Un comando es una petición; el sensor reed es el hecho.

Lista de materiales

Pieza Función
ESP32 DevKit Wi-Fi y cliente MQTT
Módulo de relé de 1 canal compatible con 3,3 V (o módulo de 5 V con optoacoplador) Puentea los bornes del botón de pared
Sensor reed magnético, normalmente abierto, de superficie Detecta la puerta completamente cerrada
Segundo sensor reed opcional Detecta la puerta completamente abierta
Fuente USB de 5 V y caja Alimentación y protección contra el polvo
Cable de timbre de dos hilos Del relé a los bornes del motor y del reed al ESP32

Antes de cablear nada, consulta el manual de tu motor. Algunos motores modernos usan un panel de pared digital en lugar de un contacto seco; necesitan una interfaz compatible o un mando a distancia de repuesto con su botón conectado al relé.

Cableado

Desde Hasta Notas
Relé IN GPIO 26 El nivel activo depende de tu módulo
Relé VCC / GND 5V / GND Los módulos con optoacoplador pueden necesitar un JD-VCC aparte
Relé COM y NO Bornes del botón de pared en el motor En paralelo con el botón existente
Sensor reed (posición cerrada) GPIO 27 y GND Usa la resistencia pull-up interna
Sensor reed (posición abierta, opcional) GPIO 25 y GND Usa la resistencia pull-up interna

Con INPUT_PULLUP, un sensor reed cerrado lee LOW. Monta el reed de posición cerrada de forma que el imán quede a menos de un centímetro solo cuando la puerta esté completamente bajada.

Diseño de topics para el abridor de puerta de garaje MQTT

Siguiendo las convenciones de nuestra guía sobre la estructura de topics MQTT para el hogar inteligente:

<base>/garage/door/state          retained: "closed", "open", "opening/closing", "unknown"
<base>/garage/door/state/set      command: "OPEN", "CLOSE", "PRESS" (never retained)
<base>/garage/door/availability   retained: "online" / "offline" via last will

Retener el topic de estado hace que el dashboard de tu móvil muestre "closed" en cuanto se conecta, en lugar de esperar al siguiente cambio. No retener nunca el topic de comandos hace que un corte de luz no pueda reproducir un OPEN antiguo y abrir la puerta por sí solo.

El sketch del ESP32

El sketch usa PubSubClient. Pon BENCH_TEST a true mientras pruebas en el servidor MQTT público gratuito de broker.connectmq.com con un LED en lugar del motor; ponlo a false para tu broker privado con TLS.

#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>

#define BENCH_TEST true

const char* WIFI_SSID = "YOUR_WIFI";
const char* WIFI_PASS = "YOUR_WIFI_PASSWORD";
const char* BROKER    = "broker.connectmq.com";

#if BENCH_TEST
  const char* BASE = "connectmq/demo/g4k9t2";   // prefijo aleatorio, área pública
  const char* CLIENT_ID = "garage-g4k9t2";
  const char* MQTT_USER = nullptr;
  const char* MQTT_PASS = nullptr;
  WiFiClient net;
  const uint16_t PORT = 1883;
#else
  const char* BASE = "home1";
  const char* CLIENT_ID = "YOUR_CLIENT_ID";
  const char* MQTT_USER = "YOUR_USERNAME";
  const char* MQTT_PASS = "YOUR_PASSWORD";
  const char* ROOT_CA = R"EOF(
-----BEGIN CERTIFICATE-----
...paste the ISRG Root X1 certificate here...
-----END CERTIFICATE-----
)EOF";
  WiFiClientSecure net;
  const uint16_t PORT = 8883;
#endif

const int RELAY_PIN = 26;
const int REED_CLOSED_PIN = 27;
const int REED_OPEN_PIN = 25;
const bool RELAY_ACTIVE = HIGH;            // cambia a LOW para módulos activos a nivel bajo
const unsigned long PRESS_MS = 500;
const unsigned long TRAVEL_TIMEOUT_MS = 30000;

PubSubClient mqtt(net);
String tState, tSet, tAvail;
String lastState = "";
unsigned long lastPressAt = 0;

String readDoor() {
  bool closed = digitalRead(REED_CLOSED_PIN) == LOW;
  bool open = digitalRead(REED_OPEN_PIN) == LOW;
  if (closed) return "closed";
  if (open) return "open";
  if (millis() - lastPressAt < TRAVEL_TIMEOUT_MS) return "opening/closing";
  return "unknown";   // detenida a medio camino o falta el sensor de apertura
}

void pressButton() {
  if (millis() - lastPressAt < 2000) return;   // ignora repeticiones rápidas
  digitalWrite(RELAY_PIN, RELAY_ACTIVE);
  delay(PRESS_MS);
  digitalWrite(RELAY_PIN, !RELAY_ACTIVE);
  lastPressAt = millis();
}

void onMessage(char* topic, byte* payload, unsigned int len) {
  String cmd;
  for (unsigned int i = 0; i < len; i++) cmd += (char)payload[i];
  cmd.trim();
  cmd.toUpperCase();
  String door = readDoor();
  if (cmd == "OPEN" && door == "closed") pressButton();
  else if (cmd == "CLOSE" && door == "open") pressButton();
  else if (cmd == "PRESS") pressButton();
  // OPEN con la puerta abierta o CLOSE con la puerta cerrada se ignoran a propósito
}

void connectMqtt() {
  while (!mqtt.connected()) {
    if (mqtt.connect(CLIENT_ID, MQTT_USER, MQTT_PASS, tAvail.c_str(), 1, true, "offline")) {
      mqtt.publish(tAvail.c_str(), "online", true);
      mqtt.subscribe(tSet.c_str(), 1);
      lastState = "";   // fuerza un estado retenido nuevo
    } else {
      delay(5000);
    }
  }
}

void setup() {
  pinMode(RELAY_PIN, OUTPUT);
  digitalWrite(RELAY_PIN, !RELAY_ACTIVE);
  pinMode(REED_CLOSED_PIN, INPUT_PULLUP);
  pinMode(REED_OPEN_PIN, INPUT_PULLUP);

  tState = String(BASE) + "/garage/door/state";
  tSet   = tState + "/set";
  tAvail = String(BASE) + "/garage/door/availability";

  WiFi.begin(WIFI_SSID, WIFI_PASS);
  while (WiFi.status() != WL_CONNECTED) delay(500);
#if !BENCH_TEST
  net.setCACert(ROOT_CA);
#endif
  mqtt.setServer(BROKER, PORT);
  mqtt.setCallback(onMessage);
}

void loop() {
  if (!mqtt.connected()) connectMqtt();
  mqtt.loop();

  static unsigned long lastCheck = 0;
  if (millis() - lastCheck > 200) {
    lastCheck = millis();
    String s = readDoor();
    if (s != lastState) {
      mqtt.publish(tState.c_str(), s.c_str(), true);   // estado retenido
      lastState = s;
    }
  }
}

Algunos detalles que conviene observar:

  • OPEN solo activa el relé cuando la puerta lee closed, y CLOSE solo cuando lee open. Como una pulsación invierte el motor, enviar OPEN a una puerta abierta la cerraría si no fuera así.
  • Se ignoran las pulsaciones separadas por menos de dos segundos, lo que impide que una doble pulsación del botón o un mensaje QoS 1 reintentado inviertan la puerta.
  • El sondeo cada 200 ms actúa como un antirrebote sencillo para el sensor reed.

Pruebas en el banco

Conecta un LED con una resistencia a la salida del relé (o simplemente escucha el clic del relé), deja BENCH_TEST en true y graba la placa. Después, desde una terminal:

B=connectmq/demo/g4k9t2
mosquitto_sub -h broker.connectmq.com -p 1883 -i mon-g4k9t2 -t "$B/garage/#" -v
mosquitto_pub -h broker.connectmq.com -p 1883 -i cmd-g4k9t2 -q 1 -t $B/garage/door/state/set -m PRESS

Pasa un imán junto al sensor reed y observa cómo cambia el estado. Desenchufa el ESP32 y, cuando venza el tiempo de keep-alive, el broker publicará offline en el topic de disponibilidad.

No conectes el motor real de la puerta mientras uses el broker público. Cualquiera que adivine tu topic puede leer y publicar en él. Cambia primero a credenciales privadas.

Puesta en marcha en un broker privado

Pon BENCH_TEST a false, pega el certificado ISRG Root X1 (la raíz de Let's Encrypt) y rellena el usuario, la contraseña y el client ID generados en el portal de ConnectMQ. Ahora el abridor se conecta por TLS en el puerto 8883 y solo los clientes con tus credenciales pueden enviarle comandos. Nuestro artículo sobre buenas prácticas de seguridad MQTT explica por qué las credenciales y TLS son innegociables para cualquier cosa que abra una puerta.

Para controlarlo desde Home Assistant, añade una entidad MQTT de tipo cover que apunte a los mismos topics, o sigue la guía de Home Assistant MQTT:

mqtt:
  cover:
    - name: "Garage Door"
      command_topic: "home1/garage/door/state/set"
      state_topic: "home1/garage/door/state"
      availability_topic: "home1/garage/door/availability"
      payload_open: "OPEN"
      payload_close: "CLOSE"
      payload_stop: "PRESS"
      state_open: "open"
      state_closed: "closed"
      state_opening: "opening/closing"
      qos: 1

Checklist de seguridad

  1. Mantén conectadas y en funcionamiento las funciones de seguridad propias del motor (fotocélulas, inversión automática).
  2. Nunca retengas los mensajes de comando.
  3. Actúa ante OPEN/CLOSE solo cuando el sensor confirme el estado contrario.
  4. Usa TLS y credenciales privadas antes de conectar el motor.
  5. Añade una notificación cuando la puerta lleve open más tiempo del esperado.
  6. Monta el relé dentro de una caja, lejos de las piezas móviles.
  7. Prueba qué ocurre si el Wi-Fi se cae en mitad del recorrido: la puerta simplemente debe terminar su movimiento.

Para la notificación de "puerta abierta", las reglas de alerta por umbral del portal de ConnectMQ pueden señalar valores en un topic, o una automatización en Home Assistant o Node-RED puede vigilar que open dure más de diez minutos.

Solución de problemas

El relé hace clic pero la puerta no se mueve. Probablemente tu motor usa un panel de pared digital. Conecta el relé al botón de un mando a distancia de repuesto.

El estado oscila entre closed y unknown. El imán está demasiado lejos del sensor reed o la puerta vibra. Acércalos o añade un antirrebote más largo.

La puerta invierte el sentido cuando envías OPEN. Estás enviando PRESS, o el sensor de cierre no lee LOW. Comprueba el cableado del reed con el monitor serie.

El dispositivo nunca vuelve a estar online. Comprueba que el client ID es único; dos dispositivos con el mismo ID se expulsan mutuamente. En un broker privado, confirma también que usas exactamente el client ID emitido con tus credenciales, porque los client IDs están vinculados a ellas.

Los comandos llegan tarde o dos veces. QoS 1 garantiza una entrega de al menos una vez, así que un mensaje reintentado puede llegar tras una reconexión. El bloqueo de dos segundos entre pulsaciones y las comprobaciones de estado de onMessage hacen que los duplicados sean inofensivos.

Controla tu garaje con seguridad con ConnectMQ

Un abridor de puertas merece un broker privado y cifrado en lugar de uno público. ConnectMQ es un broker MQTT gestionado con TLS, credenciales generadas y un explorador de topics en vivo para observar el estado de la puerta. Crea un broker Serverless gratuito en app.connectmq.com, sin tarjeta de crédito, cambia BENCH_TEST a false y estarás en marcha. Para más contexto sobre el alojamiento, lee sobre el broker MQTT en la nube, y descubre más proyectos, como la detección de fugas de agua con IoT, en nuestra guía de automatización IoT.

Preguntas frecuentes

¿Funcionará con cualquier motor de puerta de garaje?

Funciona con motores que admiten un pulsador de pared de contacto seco sencillo, lo que cubre la mayoría de los abridores residenciales. Los motores con paneles de pared digitales necesitan en su lugar un mando a distancia de repuesto conectado al relé.

¿Por qué usar un sensor reed en lugar de seguir los comandos?

Los comandos pueden fallar, un obstáculo puede detener la puerta o alguien puede usar el mando a distancia normal. Solo un sensor te dice dónde está realmente la puerta.

¿Es seguro controlar una puerta de garaje por MQTT?

Es razonable si usas TLS, credenciales privadas y comandos no retenidos, y mantienes la inversión de seguridad integrada del motor. Nunca conectes una puerta real a un servidor MQTT público de pruebas.

¿Puedo añadir un segundo sensor para la posición completamente abierta?

Sí. El sketch ya lee un reed opcional de posición abierta en el GPIO 25, que convierte "unknown" en un estado "open" claro.

Artículos relacionados

Guía para configurar Zigbee2MQTT: del coordinador al panel

Instala Zigbee2MQTT con Docker, configura el coordinador y la conexión MQTT, empareja y renombra dispositivos, contrólalos por MQTT y consulta sus estados en un panel, probando primero en un servidor MQTT público gratuito.

8 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.