Um controlador de relé Raspberry Pi MQTT permite acionar lâmpadas, bombas, ventiladores ou contatores a partir de um celular, de um dashboard ou de um script de automação, sem abrir portas no seu roteador. Neste tutorial você vai ligar uma placa de relés multicanal, escrever um serviço Python que associa topics MQTT a canais de relé e usar retained messages para que cada relé volte ao estado correto depois de um reboot.
O que você precisa
| Item | Observações |
|---|---|
| Raspberry Pi (3B+, 4, 5 ou Zero 2 W) | Raspberry Pi OS Bookworm, conectado à rede |
| Placa de relés de 2, 4 ou 8 canais | Bobina de 5 V com optoacopladores, entradas compatíveis com 3,3 V |
| Jumpers fêmea-fêmea | Um por canal, mais alimentação e terra |
Python 3 com gpiozero e paho-mqtt 2.x |
Instalados em um ambiente virtual |
| Um broker MQTT | broker.connectmq.com para testes, credenciais privadas para produção |
Uma observação de segurança antes de tudo: placas de relés acionam o que quer que você ligue a elas. Se você controlar tensão de rede, a fiação precisa ser feita dentro de um quadro adequado, por um profissional qualificado, com o relé dimensionado para a carga. Para aprender, acione uma fita de LED de 12 V ou um pequeno ventilador CC.
Ligando a placa de relés
A maioria dos módulos de relé para hobby tem um conector identificado como VCC, GND e IN1 a IN4. Ligue assim:
| Pino do relé | Pino do Raspberry Pi |
|---|---|
| VCC | 5 V (pino físico 2) |
| GND | GND (pino físico 6) |
| IN1 | GPIO17 (pino físico 11) |
| IN2 | GPIO27 (pino físico 13) |
| IN3 | GPIO22 (pino físico 15) |
| IN4 | GPIO23 (pino físico 16) |
Muitas dessas placas são ativas em nível baixo: levar uma entrada ao terra energiza a bobina. É por isso que o código abaixo usa active_high=False. Se a sua placa liga quando você espera que desligue, inverta essa flag.
Placas com um jumper JD-VCC permitem alimentar as bobinas com uma fonte de 5 V separada, para isolamento total. Remova o jumper, alimente o JD-VCC pela fonte externa, mantenha o VCC nos 3,3 V ou 5 V do Pi conforme a documentação da placa e compartilhe o terra apenas se o manual exigir.
Mais um detalhe de hardware importa para a confiabilidade. Antes de o seu script iniciar, os pinos GPIO são entradas com resistores de pull padrão, então uma placa ativa em nível baixo pode acionar por um instante no boot. Teste o que os seus relés fazem durante um ciclo de energia. Se um acionamento indevido puder causar dano, escolha pinos cujo pull padrão corresponda ao nível "desligado" ou adicione resistores de pull externos.
Definindo o contrato de topics
Uma estrutura de topics limpa torna o controlador de relés fácil de usar a partir de qualquer cliente, seja o Node-RED, o Home Assistant ou um script:
| Topic | Direção | Payload | Retained |
|---|---|---|---|
connectmq/demo/a91c4e/relays/<n>/set |
Controlador para o Pi | ON, OFF (ou TOGGLE, não retained) |
Sim para ON/OFF |
connectmq/demo/a91c4e/relays/<n>/state |
Pi para todos | ON ou OFF |
Sim |
connectmq/demo/a91c4e/relays/status |
Pi (e last will) | online ou offline |
Sim |
Substitua a91c4e por uma string aleatória sua. Em um broker público compartilhado, todos os topics podem ser lidos por todos, então um prefixo único mantém o seu tráfego de teste separado. Se quiser relembrar por que topics set e state separados são uma boa prática, o guia GPIO do Raspberry Pi via MQTT explica em detalhes o padrão de comando e estado.
Como os estados retained sobrevivem a um reboot
As retained messages são a chave para um controlador de relés que se comporta bem depois de uma queda de energia. O broker guarda a última mensagem retained de cada topic e a entrega imediatamente a qualquer novo assinante. Isso traz dois comportamentos úteis:
- Quando o seu dashboard publica
ONemrelays/1/setcom a flag retain, o broker a guarda. Depois que o Pi reinicia e assinarelays/+/set, ele recebe o estado desejado de cada canal e o restaura. - Quando o Pi publica
relays/1/statecom retain, qualquer dashboard aberto mais tarde vê o estado real na hora, em vez de esperar pela próxima mudança.
Há uma armadilha: nunca use retain com TOGGLE. Um toggle retained seria reproduzido a cada reconexão e inverteria o relé toda vez. Por isso, o script abaixo ignora toggles retained. Para entender melhor o mecanismo, leia nosso artigo sobre retained messages no MQTT.
Script Raspberry Pi relé MQTT em Python
Crie uma pasta de projeto e um ambiente virtual:
sudo apt update
sudo apt install -y python3-venv python3-lgpio
mkdir -p ~/relay-mqtt && cd ~/relay-mqtt
python3 -m venv --system-site-packages .venv
source .venv/bin/activate
pip install "paho-mqtt>=2.0" gpiozero
A flag --system-site-packages permite que o ambiente virtual use o backend lgpio, de que o gpiozero precisa no Raspberry Pi 5 e que também funciona nos modelos mais antigos. Salve o seguinte como relay_mqtt.py:
#!/usr/bin/env python3
"""Control a multi-channel relay board from MQTT topics."""
import os
import signal
import sys
import uuid
import paho.mqtt.client as mqtt
from gpiozero import OutputDevice
BROKER = os.getenv("MQTT_HOST", "broker.connectmq.com")
PORT = int(os.getenv("MQTT_PORT", "1883"))
USERNAME = os.getenv("MQTT_USERNAME")
PASSWORD = os.getenv("MQTT_PASSWORD")
CLIENT_ID = os.getenv("MQTT_CLIENT_ID", f"rpi-relay-{uuid.uuid4().hex[:8]}")
PREFIX = os.getenv("MQTT_PREFIX", "connectmq/demo/a91c4e/relays")
RELAY_PINS = {1: 17, 2: 27, 3: 22, 4: 23}
ACTIVE_HIGH = False # a maioria das placas optoacopladas para hobby é ativa em nível baixo
relays = {
n: OutputDevice(pin, active_high=ACTIVE_HIGH, initial_value=False)
for n, pin in RELAY_PINS.items()
}
def publish_state(client, n):
state = "ON" if relays[n].value else "OFF"
client.publish(f"{PREFIX}/{n}/state", state, qos=1, retain=True)
def on_connect(client, userdata, flags, reason_code, properties):
if reason_code.is_failure:
print(f"Connection refused: {reason_code}")
return
print(f"Connected as {CLIENT_ID}")
client.publish(f"{PREFIX}/status", "online", qos=1, retain=True)
client.subscribe(f"{PREFIX}/+/set", qos=1)
for n in relays:
publish_state(client, n)
def on_message(client, userdata, msg):
try:
n = int(msg.topic.split("/")[-2])
except ValueError:
return
if n not in relays:
print(f"Ignoring unknown channel {n}")
return
command = msg.payload.decode(errors="ignore").strip().upper()
if command in ("ON", "1", "TRUE"):
relays[n].on()
elif command in ("OFF", "0", "FALSE"):
relays[n].off()
elif command == "TOGGLE":
if msg.retain:
print(f"Ignoring retained TOGGLE on channel {n}")
return
relays[n].toggle()
else:
print(f"Unknown command {command!r} for channel {n}")
return
print(f"Relay {n} -> {'ON' if relays[n].value else 'OFF'}")
publish_state(client, n)
def on_disconnect(client, userdata, flags, reason_code, properties):
print(f"Disconnected: {reason_code}; paho will reconnect")
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=CLIENT_ID)
client.on_connect = on_connect
client.on_message = on_message
client.on_disconnect = on_disconnect
client.will_set(f"{PREFIX}/status", "offline", qos=1, retain=True)
client.reconnect_delay_set(min_delay=1, max_delay=60)
if USERNAME:
client.username_pw_set(USERNAME, PASSWORD)
if PORT == 8883:
client.tls_set()
def shutdown(signum, frame):
for relay in relays.values():
relay.off()
client.publish(f"{PREFIX}/status", "offline", qos=1, retain=True)
client.disconnect()
sys.exit(0)
signal.signal(signal.SIGTERM, shutdown)
signal.signal(signal.SIGINT, shutdown)
client.connect(BROKER, PORT, keepalive=30)
client.loop_forever(retry_first_connection=True)
Rode com python relay_mqtt.py. O script desliga todos os relés em um encerramento limpo; se você preferir manter as cargas ligadas quando o serviço parar, remova o loop em shutdown.
Testando a partir do seu notebook
Abra um segundo terminal em qualquer máquina com os clientes Mosquitto instalados e acompanhe os topics de estado:
mosquitto_sub -h broker.connectmq.com -p 1883 -i laptop-a91c4e \
-t 'connectmq/demo/a91c4e/relays/#' -v
Depois ligue o canal 1 com um comando retained e alterne o canal 2:
mosquitto_pub -h broker.connectmq.com -p 1883 -i cmd-a91c4e \
-t 'connectmq/demo/a91c4e/relays/1/set' -m 'ON' -q 1 -r
mosquitto_pub -h broker.connectmq.com -p 1883 -i cmd-a91c4e \
-t 'connectmq/demo/a91c4e/relays/2/set' -m 'TOGGLE' -q 1
Você deve ouvir os cliques e ver relays/1/state ON aparecer. Agora reinicie o Pi. Quando o script se reconectar, o canal 1 volta a ON porque o comando retained continua no broker, enquanto o canal 2 começa em OFF porque toggles nunca são retained. Para limpar um comando retained, publique uma mensagem retained vazia: mosquitto_pub ... -t '.../relays/1/set' -r -n.
Rode como serviço
Um controlador de relés precisa iniciar no boot e reiniciar depois de uma falha. Crie /etc/systemd/system/relay-mqtt.service:
[Unit]
Description=Relay MQTT controller
After=network-online.target
Wants=network-online.target
[Service]
User=pi
WorkingDirectory=/home/pi/relay-mqtt
Environment=MQTT_HOST=broker.connectmq.com
Environment=MQTT_PORT=8883
Environment=MQTT_USERNAME=YOUR_USERNAME
Environment=MQTT_PASSWORD=YOUR_PASSWORD
Environment=MQTT_CLIENT_ID=YOUR_CLIENT_ID
Environment=MQTT_PREFIX=home/garage/relays
ExecStart=/home/pi/relay-mqtt/.venv/bin/python relay_mqtt.py
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
Ative com sudo systemctl daemon-reload && sudo systemctl enable --now relay-mqtt. A explicação completa sobre arquivos de serviço, logs e arquivos de ambiente está em como rodar um script Python MQTT na inicialização. Ajuste o nome de usuário se o seu não for pi.
Do servidor de testes para a produção
O servidor MQTT público é perfeito para a primeira hora, mas qualquer pessoa que adivinhe o seu topic poderia acionar os seus relés. Para qualquer coisa ligada a equipamentos reais, use um broker privado com autenticação e TLS. Com um broker MQTT gerenciado como o ConnectMQ, você cria um broker no portal, copia o usuário, a senha e o client ID gerados para o arquivo de serviço acima e define a porta como 8883. O client ID fica vinculado às credenciais emitidas, então outro dispositivo não consegue se passar pelo seu controlador. O script não muda em nada.
O portal ConnectMQ também ajuda durante o comissionamento: o cliente MQTT no navegador pode publicar ON e OFF em cada canal, e o explorador de topics ao vivo mostra os valores retained de estado e status em um relance. Se você quiser uma alternativa de hardware ao Pi para a mesma tarefa, compare com o projeto relé com ESP32 via MQTT.
Solução de problemas
- Os relés ligam no início e desligam quando você manda ligar: a sua placa é ativa em nível alto; defina
ACTIVE_HIGH = True. - Os LEDs da placa acendem, mas o relé não estala: a alimentação das bobinas está fraca; alimente o
JD-VCCcom uma fonte de 5 V separada. Connection refused: Not authorized: confira usuário, senha e client ID, ou remova-os ao usar o broker público anônimo.- O relé restaura um estado antigo após o reboot: há um comando retained antigo no broker; limpe-o com uma publicação retained vazia.
- Erros
GPIO busy: outro script ou serviço está usando os mesmos pinos; pare-o antes de iniciar este.
Comece com o ConnectMQ
Pronto para controlar equipamentos reais? Cadastre-se no plano Serverless gratuito, sem cartão de crédito, crie um broker e aponte o seu serviço de relés para ele via TLS. Você tem MQTT 3.1.1 e 5.0, retained messages e um portal para acompanhar cada acionamento. Novo nos conceitos? O tutorial MQTT com Raspberry Pi cobre os fundamentos, e nossa visão geral de um broker MQTT na nuvem explica quando a hospedagem vale mais do que rodar o Mosquitto no Pi.
Perguntas frequentes
Por que meu relé muda de estado depois que o Raspberry Pi reinicia?
Ele está reproduzindo um comando retained do broker, o que normalmente é o que você quer. Se o estado estiver errado, publique o valor correto com retain ou limpe o topic com uma mensagem retained vazia.
Posso usar QoS 1 para os comandos do relé?
Sim, e deve. O QoS 1 garante que o comando chegue pelo menos uma vez e, como ON e OFF são idempotentes, uma entrega duplicada não causa dano.
Quantos relés um Raspberry Pi consegue controlar?
Tantos quantos pinos GPIO livres você tiver, normalmente até oito em uma placa para hobby. Para mais canais, use um expansor de GPIO I2C como o MCP23017 e mantenha a mesma estrutura de topics.
É seguro controlar equipamentos da rede elétrica dessa forma?
Somente com relés dimensionados para a carga, um quadro adequado e fiação feita conforme as normas elétricas locais. Sempre use conexões TLS autenticadas para qualquer coisa que acione equipamentos reais.



