Automação IoT

Abridor de Portão de Garagem MQTT Caseiro com ESP32, Relé e Sensor Reed

Transforme o motor do seu portão de garagem em um motor inteligente com um ESP32, um relé que aperta o botão de parede por você e um sensor reed que informa o estado real do portão como retained message MQTT.

Abridor de portão de garagem MQTT montado com um ESP32, um módulo de relé e um sensor reed magnético no batente do portão
Rode isso no seu próprio broker MQTTTenha um broker MQTT privado e gerenciado no plano Free e comece com exemplos de código prontos.
Teste grátis Exemplos de código Sem cartão de crédito

Um abridor de portão de garagem MQTT permite abrir, fechar e, principalmente, conferir o seu portão de garagem de qualquer lugar, usando o motor que você já tem. Neste tutorial você vai ligar um ESP32 a um relé que "aperta" o botão de parede e a um sensor reed que informa se o portão está realmente fechado, e depois publicar esse estado como uma retained message MQTT, para que todo aplicativo veja a verdade no momento em que se conecta.

Como o abridor funciona

Quase todo motor de portão de garagem residencial tem terminais para um botão de parede. Fechar esses dois terminais por um instante equivale a apertar o botão: o portão começa a se mover, para ou inverte o sentido, dependendo de onde está. Isso torna a parte de hardware simples e não invasiva:

  • Um relé ligado em paralelo com o botão de parede dá um "aperto" de 500 ms.
  • Um sensor reed no batente, com um ímã no portão, fecha o contato quando o portão está totalmente fechado.
  • O ESP32 escuta os comandos, pulsa o relé e publica o estado do sensor reed.

A principal escolha de design é que o abridor nunca presume o que o portão fez. Ele publica o que o sensor vê. Um comando é um pedido; o sensor reed é o fato.

Lista de componentes

Componente Finalidade
ESP32 DevKit Wi-Fi e client MQTT
Módulo de relé de 1 canal compatível com 3,3 V (ou módulo de 5 V com optoacoplador) Fecha os terminais do botão de parede
Sensor reed magnético normalmente aberto, de sobrepor Detecta o portão totalmente fechado
Segundo sensor reed opcional Detecta o portão totalmente aberto
Fonte USB de 5 V e caixa Alimentação e proteção contra poeira
Fio paralelo de campainha Relé até os terminais do motor, reed até o ESP32

Antes de ligar qualquer coisa, consulte o manual do seu motor. Alguns motores mais novos usam um painel de parede digital em vez de um contato seco; esses precisam de uma interface compatível ou de um controle remoto reserva com o botão ligado ao relé.

Ligações

De Para Observações
IN do relé GPIO 26 O nível ativo depende do seu módulo
VCC / GND do relé 5V / GND Módulos com optoacoplador podem precisar de um JD-VCC separado
COM e NO do relé Terminais do botão de parede no motor Em paralelo com o botão existente
Sensor reed (posição fechada) GPIO 27 e GND Usa o pull-up interno
Sensor reed (posição aberta, opcional) GPIO 25 e GND Usa o pull-up interno

Com INPUT_PULLUP, um sensor reed fechado lê LOW. Instale o reed da posição fechada de modo que o ímã fique a cerca de um centímetro dele somente quando o portão estiver totalmente abaixado.

Design de topics para o abridor de portão de garagem MQTT

Seguindo as convenções do nosso guia de estrutura de topics MQTT para casa 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

Reter o topic de estado faz com que o dashboard do seu celular mostre "closed" imediatamente ao conectar, em vez de esperar pela próxima mudança. Nunca reter o topic de comando significa que uma queda de energia não consegue reproduzir um OPEN antigo e abrir o portão sozinha.

O sketch do ESP32

O sketch usa a PubSubClient. Defina BENCH_TEST como true enquanto testa no servidor MQTT público (broker.connectmq.com) com um LED no lugar do motor; defina como false para o seu broker MQTT gerenciado privado com 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";   // prefixo aleatório, á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;            // mude para LOW em módulos ativos em nível baixo
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";   // parado no meio do caminho ou sem o sensor de aberto
}

void pressButton() {
  if (millis() - lastPressAt < 2000) return;   // ignora repetições 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 com o portão aberto ou CLOSE com ele fechado é ignorado de 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 = "";   // força um novo estado retido
    } 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 retido
      lastState = s;
    }
  }
}

Alguns detalhes que merecem atenção:

  • OPEN só pulsa o relé quando o portão lê closed, e CLOSE só quando lê open. Como um aperto alterna o motor, enviar OPEN para um portão aberto acabaria fechando-o.
  • Apertos com menos de dois segundos de intervalo são ignorados, o que impede que um toque duplo no botão ou uma mensagem QoS 1 reenviada inverta o portão.
  • A leitura a cada 200 ms funciona como um debounce simples para o sensor reed.

Testando na bancada

Ligue um LED com resistor na saída do relé (ou apenas escute o clique do relé), mantenha BENCH_TEST como true e grave a placa. Depois, em um 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

Passe um ímã perto do sensor reed e veja o estado mudar. Desconecte o ESP32 e, depois do tempo limite do keep-alive, o broker publica offline no topic de disponibilidade.

Não conecte o motor real do portão enquanto estiver usando o servidor MQTT público. Ele serve apenas para testes e protótipos, não para dados de produção. Qualquer pessoa que adivinhe o seu topic pode ler e publicar nele. Mude primeiro para credenciais privadas.

Entrando em operação em um broker privado

Defina BENCH_TEST como false, cole o certificado ISRG Root X1 (a raiz do Let's Encrypt) e preencha o usuário, a senha e o client ID gerados no portal do ConnectMQ. Agora o abridor se conecta via TLS na porta 8883 e apenas clients com as suas credenciais podem comandá-lo. Nosso artigo de boas práticas de segurança MQTT explica por que credenciais e TLS são inegociáveis para qualquer coisa que abra uma porta.

Para controlá-lo pelo Home Assistant, adicione uma entidade MQTT cover apontando para os mesmos topics ou siga o guia de MQTT no Home Assistant:

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 segurança

  1. Mantenha os recursos de segurança do próprio motor (fotocélulas, reversão automática) conectados e funcionando.
  2. Nunca retenha mensagens de comando.
  3. Só aja em OPEN/CLOSE quando o sensor confirmar o estado oposto.
  4. Use TLS e credenciais privadas antes de conectar o motor.
  5. Adicione uma notificação para quando o portão ficar open por mais tempo do que o esperado.
  6. Instale o relé dentro de uma caixa, longe das partes móveis.
  7. Teste o que acontece quando o Wi-Fi cai no meio do percurso: o portão deve simplesmente terminar o movimento.

Para a notificação de "portão esquecido aberto", as regras de alerta por limite do portal do ConnectMQ podem sinalizar valores em um topic, ou uma automação no Home Assistant ou no Node-RED pode observar se o open dura mais de dez minutos.

Solução de problemas

O relé clica, mas o portão não se move. Seu motor provavelmente usa um painel de parede digital. Ligue o relé no botão de um controle remoto reserva.

O estado oscila entre closed e unknown. O ímã está longe demais do sensor reed ou o portão trepida. Aproxime-os ou aumente o tempo de debounce.

O portão inverte o sentido quando você envia OPEN. Você está enviando PRESS ou o sensor de fechado não está lendo LOW. Verifique a ligação do reed com o monitor serial.

O dispositivo nunca volta a ficar online. Verifique se o client ID é único; dois dispositivos com o mesmo ID derrubam um ao outro. Em um broker privado, confirme também que você está usando exatamente o client ID emitido com as suas credenciais, porque os client IDs ficam vinculados a elas.

Os comandos chegam atrasados ou duas vezes. O QoS 1 garante entrega pelo menos uma vez, então uma mensagem reenviada pode chegar depois de uma reconexão. O bloqueio de dois segundos entre apertos e as verificações de estado em onMessage tornam as duplicatas inofensivas.

Controle sua garagem com segurança com o ConnectMQ

Um abridor de portão merece um broker privado e criptografado, e não o servidor MQTT público. O ConnectMQ é um broker MQTT gerenciado com TLS, credenciais geradas e um explorador de topics ao vivo para acompanhar o estado do seu portão. Crie um broker Serverless gratuito em app.connectmq.com, sem cartão de crédito, mude BENCH_TEST para false e pronto, está em operação. Para entender a hospedagem, leia sobre o broker MQTT na nuvem e veja mais projetos, como detecção de vazamento de água com IoT, no nosso guia de automação IoT.

Perguntas frequentes

Isso funciona com qualquer motor de portão de garagem?

Funciona com motores que aceitam um botão de parede simples de contato seco, o que abrange a maioria dos abridores residenciais. Motores com painéis de parede digitais precisam de um controle remoto reserva ligado ao relé.

Por que usar um sensor reed em vez de acompanhar os comandos?

Os comandos podem falhar, o portão pode ser parado por um obstáculo ou alguém pode usar o controle remoto comum. Só um sensor diz onde o portão realmente está.

É seguro controlar um portão de garagem via MQTT?

É razoável quando você usa TLS, credenciais privadas, comandos não retidos e mantém a reversão de segurança embutida no motor. Nunca conecte um portão real ao servidor MQTT público, que é só para testes.

Posso adicionar um segundo sensor para a posição totalmente aberta?

Sim. O sketch já lê um reed opcional de posição aberta no GPIO 25, o que transforma "unknown" em um estado "open" claro.

Artigos relacionados

Guia de Configuração do Zigbee2MQTT: do Coordenador ao Painel

Instale o Zigbee2MQTT com Docker, configure o coordenador e a conexão MQTT, pareie e renomeie dispositivos, controle-os via MQTT e veja seus estados em um painel, testando primeiro no servidor MQTT público (gratuito).

8 min de leitura

Todos os artigos →

Seu broker MQTT privado está a poucos cliques de distância

Crie uma conta, receba as credenciais do seu broker e conecte seu primeiro dispositivo — sem servidores para instalar, atualizar ou escalar.