O controle Raspberry Pi GPIO MQTT permite acionar um relé, LED ou buzzer de qualquer lugar e ver o estado de botões e contatos em tempo real, sem abrir uma única porta na sua rede. Neste tutorial você vai usar gpiozero e paho-mqtt para ligar e desligar uma saída por comandos MQTT, publicar mudanças nas entradas no momento em que acontecem e reportar tudo como estado retained. Você vai testar com broker.connectmq.com e um cliente de linha de comando e depois migrar para credenciais privadas.
Como as peças se encaixam
O Pi se conecta de saída a um broker MQTT e assina um topic de comando. Qualquer coisa que publique MQTT (um app de celular, o Node-RED, um dashboard web, um script) envia ON ou OFF para esse topic, e o Pi aciona o pino. Quando o pino muda ou um botão é pressionado, o Pi publica o novo estado em um topic de estado separado.
Essa separação entre topics de comando e de estado é a decisão de design mais importante deste tutorial:
| Topic | Direção | Retained | Payload de exemplo |
|---|---|---|---|
.../gpio/led/set |
Controlador para o Pi | Não | ON |
.../gpio/led/state |
Pi para todos | Sim | ON |
.../gpio/button/state |
Pi para todos | Sim | PRESSED |
.../status |
Pi para todos | Sim (Last Will) | online / offline |
Os comandos não são retained, então um "ON" antigo nunca dispara de novo quando o Pi reinicia. Os estados são retained, então qualquer dashboard que se conecte depois vê os valores atuais na hora. Um dashboard deve exibir o topic de estado, e não repetir o que enviou, porque o topic de estado reflete o que realmente aconteceu no hardware.
Ligação
Você precisa de um LED com um resistor de 330 ohms (ou um módulo de relé) e de um botão.
- Anodo do LED (perna mais longa) pelo resistor até o GPIO 17 (pino físico 11), catodo ao terra.
- Botão entre o GPIO 27 (pino físico 13) e o terra. O gpiozero ativa o pull-up interno, então não é preciso resistor.
Os pinos GPIO são de 3,3 V e fornecem apenas alguns miliampères. Acione relés, motores e qualquer coisa em tensão de rede por meio de uma placa driver ou de um módulo de relé adequado, nunca diretamente. O tutorial relé no Raspberry Pi via MQTT mostra como fazer isso com segurança.
Instale as bibliotecas
O gpiozero vem pré-instalado no Raspberry Pi OS e usa o backend lgpio, que funciona em todos os modelos, inclusive no Raspberry Pi 5 com seu chip de E/S RP1. Crie um ambiente virtual que enxergue os pacotes do sistema e adicione a paho-mqtt:
sudo apt install -y python3-gpiozero python3-lgpio
python3 -m venv --system-site-packages ~/gpio-mqtt/.venv
source ~/gpio-mqtt/.venv/bin/activate
pip install "paho-mqtt>=2.0"
python -c "import gpiozero, paho.mqtt; print('ok')"
Usar --system-site-packages evita compilar bibliotecas de GPIO com o pip, que é a causa mais comum de erros de instalação nos modelos de Pi mais novos. Seu usuário precisa estar no grupo gpio, o que já acontece com o usuário padrão.
O script Raspberry Pi GPIO MQTT
import os
import signal
import uuid
import paho.mqtt.client as mqtt
from gpiozero import LED, Button
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"pi-gpio-{uuid.uuid4().hex[:8]}")
PREFIX = os.getenv("TOPIC_PREFIX", "connectmq/demo/g3p6v2/pi1")
led = LED(17)
button = Button(27, bounce_time=0.05)
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=CLIENT_ID)
if USERNAME:
client.username_pw_set(USERNAME, PASSWORD)
if PORT == 8883:
client.tls_set()
client.will_set(f"{PREFIX}/status", "offline", qos=1, retain=True)
client.reconnect_delay_set(1, 30)
def publish_led_state():
client.publish(f"{PREFIX}/gpio/led/state", "ON" if led.is_lit else "OFF", qos=1, retain=True)
def publish_button_state():
state = "PRESSED" if button.is_pressed else "RELEASED"
client.publish(f"{PREFIX}/gpio/button/state", state, qos=1, retain=True)
def on_connect(client, userdata, flags, reason_code, properties):
if reason_code.is_failure:
print("connect failed:", reason_code)
return
print("connected")
client.subscribe(f"{PREFIX}/gpio/+/set", qos=1)
client.publish(f"{PREFIX}/status", "online", qos=1, retain=True)
publish_led_state()
publish_button_state()
def on_message(client, userdata, msg):
target = msg.topic.split("/")[-2]
command = msg.payload.decode(errors="ignore").strip().upper()
if target != "led":
return
if command == "ON":
led.on()
elif command == "OFF":
led.off()
elif command == "TOGGLE":
led.toggle()
else:
print("ignored command:", command)
return
publish_led_state()
button.when_pressed = publish_button_state
button.when_released = publish_button_state
client.on_connect = on_connect
client.on_message = on_message
client.connect_async(BROKER, PORT, keepalive=30)
client.loop_start()
try:
signal.pause()
except KeyboardInterrupt:
pass
finally:
client.publish(f"{PREFIX}/status", "offline", qos=1, retain=True).wait_for_publish(2)
client.loop_stop()
client.disconnect()
led.off()
Alguns detalhes merecem destaque. O gpiozero executa os callbacks when_pressed em uma thread própria, e o publish() da paho é thread-safe, então chamá-lo a partir do callback não tem problema. O bounce_time filtra o repique mecânico do botão, que de outra forma gera rajadas de eventos de pressionar e soltar. O on_connect refaz as assinaturas e republica os estados a cada reconexão, então o broker sempre guarda a verdade atual. Comandos desconhecidos são ignorados em vez de interpretados por suposição.
Teste a partir de outra máquina
Rode o script no Pi e, no seu notebook, abra um terminal para acompanhar tudo e outro para enviar comandos:
# terminal 1: acompanhar todos os topics deste Pi
mosquitto_sub -h broker.connectmq.com -p 1883 -i gpio-watch-g3p6v2 -t "connectmq/demo/g3p6v2/pi1/#" -v
# terminal 2: enviar comandos
mosquitto_pub -h broker.connectmq.com -p 1883 -i gpio-cmd-g3p6v2 -t "connectmq/demo/g3p6v2/pi1/gpio/led/set" -m ON
mosquitto_pub -h broker.connectmq.com -p 1883 -i gpio-cmd-g3p6v2 -t "connectmq/demo/g3p6v2/pi1/gpio/led/set" -m TOGGLE
O LED muda e o terminal 1 mostra o novo led/state. Pressione o botão e o button/state alterna entre PRESSED e RELEASED. Pare o script com Ctrl+C e o status muda para offline; se em vez disso você tirar a energia, o broker publica o Last Will quando o keepalive expira.
O servidor MQTT público gratuito é ideal para este teste, mas lembre-se de que qualquer pessoa pode ler e publicar nos topics públicos dele. Não deixe um relé de verdade controlável por lá.
Expandindo para mais pinos
Para controlar várias saídas, mantenha um dicionário de dispositivos nomeados e localize-os a partir do topic:
from gpiozero import OutputDevice
outputs = {"led": LED(17), "fan": OutputDevice(22), "pump": OutputDevice(23, active_high=False)}
No on_message, troque o LED único por outputs.get(target) e publique o estado para o nome correspondente. Use nomes, e não números de pino, nos topics: um dashboard não deve quebrar quando você refaz a fiação da placa. Para entradas como sensores de temperatura, publique por temporizador em vez de por mudança; o tutorial DHT22 MQTT mostra esse padrão.
Comportamento à prova de falhas para as saídas
Decida com antecedência o que uma saída deve fazer quando algo dá errado, porque a rede vai cair em algum momento. Para uma lâmpada, manter o último estado é aceitável. Para uma bomba, um aquecedor ou uma válvula, geralmente é mais seguro desligar se nenhum comando ou heartbeat chegar por um tempo. Um padrão simples é um timestamp atualizado no on_message e uma verificação no loop principal que desliga a saída após, digamos, dez minutos de silêncio e publica o novo estado quando a conexão voltar.
Pense também na inicialização. O gpiozero coloca as saídas em desligado quando o objeto é criado, então depois de um reboot todas as saídas começam em um estado seguro e conhecido até chegar um comando novo. Mantenha assim, em vez de restaurar o estado anterior a partir de um arquivo, a menos que o processo realmente exija. Se exigir, documente o comportamento no dispositivo e no dashboard para ninguém ser pego de surpresa.
Solução de problemas
GPIO busyoulgpio.error: outro processo está usando o pino. Pare os outros scripts ou serviços que o utilizam e verifique se você não está rodando duas cópias.- No module named lgpio: o venv foi criado sem
--system-site-packages. Recrie-o ou instale opython3-lgpio. - Os comandos não fazem nada: confira o topic exato e se o Pi imprimiu "connected". Assine
#sob o seu prefixo para ver o que realmente está sendo publicado. - O LED acende a cada reboot: alguém publicou uma retained message no topic
set. Limpe-a publicando uma mensagem retained vazia nesse topic commosquitto_pub -r -n. - Eventos aleatórios do botão: aumente o
bounce_timeou encurte a fiação; cabos longos sem blindagem captam ruído.
Configuração para produção
Para qualquer coisa além de uma demonstração, crie um broker privado no ConnectMQ e inicie o script com MQTT_PORT=8883, MQTT_USERNAME=YOUR_USERNAME, MQTT_PASSWORD=YOUR_PASSWORD e MQTT_CLIENT_ID=YOUR_CLIENT_ID obtidos no portal. O TLS impede que os comandos sejam lidos ou forjados em trânsito, e credenciais por dispositivo garantem que só os seus clientes publiquem nos seus topics. Rode-o como serviço seguindo os passos de como rodar um script Python MQTT na inicialização, para que as saídas voltem ao controle depois de uma queda de energia. Com um broker MQTT gerenciado, não há nada para instalar ou manter no Pi além do seu script.
Se você está começando agora, o tutorial MQTT com Raspberry Pi cobre a instalação e o básico, e a documentação do gpiozero lista todas as classes de dispositivos suportadas.
Controle seu Pi de qualquer lugar com o ConnectMQ
Crie um broker Serverless gratuito em app.connectmq.com, sem cartão de crédito. O cliente MQTT no navegador do ConnectMQ permite enviar comandos ON e OFF e acompanhar os topics de estado sem instalar nada, então você pode controlar seu Pi pelo celular ou notebook poucos minutos depois de se cadastrar.
Perguntas frequentes
Posso usar RPi.GPIO em vez de gpiozero?
O RPi.GPIO não suporta o Raspberry Pi 5. O gpiozero com o backend lgpio funciona em todos os modelos atuais e cuida do debounce e das threads para você.
Por que usar retained messages para o estado, mas não para os comandos?
O estado retained permite que novos assinantes vejam o valor atual na hora. Um comando retained seria reproduzido toda vez que o Pi reconectasse, acionando saídas de forma inesperada.
É seguro controlar equipamentos de rede elétrica dessa forma?
Somente por meio de um módulo de relé ou contator devidamente dimensionado, instalado por um profissional qualificado, com TLS e credenciais privadas no broker. Nunca acione a rede elétrica diretamente pelos pinos GPIO.
Com que rapidez os comandos chegam ao Pi?
Em uma conexão saudável, os comandos MQTT normalmente chegam em bem menos de um segundo, o que é mais do que suficiente para lâmpadas, relés e a maioria das tarefas de automação.



