Un contrôleur de relais Raspberry Pi MQTT vous permet de commuter des lampes, des pompes, des ventilateurs ou des contacteurs depuis un téléphone, un tableau de bord ou un script d'automatisation, sans ouvrir de ports sur votre box. Dans ce tutoriel, vous allez câbler une carte relais multicanal, écrire un service Python qui associe des topics MQTT aux canaux de relais, et utiliser les retained messages pour que chaque relais revienne dans le bon état après un redémarrage. C'est une brique de base idéale pour une maison connectée.
Ce dont vous avez besoin
| Élément | Remarques |
|---|---|
| Raspberry Pi (3B+, 4, 5 ou Zero 2 W) | Raspberry Pi OS Bookworm, connecté au réseau |
| Carte 2, 4 ou 8 relais | Bobine 5 V avec optocoupleurs, entrées compatibles 3,3 V |
| Câbles Dupont femelle-femelle | Un par canal, plus l'alimentation et la masse |
Python 3 avec gpiozero et paho-mqtt 2.x |
Installés dans un environnement virtuel |
| Un broker MQTT | broker.connectmq.com pour les tests, des identifiants privés pour la production |
Une remarque de sécurité avant tout : une carte relais commute tout ce que vous y raccordez. Si vous pilotez du 230 V, le câblage doit être réalisé dans un boîtier adapté par une personne qualifiée, avec des relais dimensionnés pour la charge. Pour apprendre, commutez plutôt un ruban LED 12 V ou un petit ventilateur à courant continu.
Câbler la carte relais
La plupart des modules relais grand public disposent d'un connecteur marqué VCC, GND et IN1 à IN4. Raccordez-les ainsi :
| Broche relais | Broche Raspberry Pi |
|---|---|
| VCC | 5 V (broche physique 2) |
| GND | GND (broche physique 6) |
| IN1 | GPIO17 (broche physique 11) |
| IN2 | GPIO27 (broche physique 13) |
| IN3 | GPIO22 (broche physique 15) |
| IN4 | GPIO23 (broche physique 16) |
Beaucoup de ces cartes sont actives à l'état bas : mettre une entrée à la masse alimente la bobine. C'est pourquoi le code ci-dessous utilise active_high=False. Si votre carte s'enclenche quand vous attendez l'inverse, changez cette option.
Les cartes équipées d'un cavalier JD-VCC permettent d'alimenter les bobines depuis une alimentation 5 V séparée pour une isolation complète. Retirez le cavalier, alimentez JD-VCC depuis la source externe, laissez VCC sur le 3,3 V ou le 5 V du Pi selon la documentation de la carte, et ne reliez les masses que si le manuel l'exige.
Un autre détail matériel compte pour la fiabilité. Avant le lancement de votre script, les broches GPIO sont des entrées avec leurs résistances de tirage par défaut : une carte active à l'état bas peut donc commuter brièvement au démarrage. Testez le comportement de vos relais lors d'une coupure et d'une remise sous tension. Si un tel à-coup peut être dangereux, choisissez des broches dont le tirage par défaut correspond au niveau « arrêt » ou ajoutez des résistances de tirage externes.
Concevoir le contrat de topics
Une organisation de topics claire rend le contrôleur de relais facile à utiliser depuis n'importe quel client, qu'il s'agisse de Node-RED, de Home Assistant ou d'un script. Jeedom et Domoticz savent aussi communiquer en MQTT et peuvent donc piloter les mêmes topics.
| Topic | Sens | Payload | Retained |
|---|---|---|---|
connectmq/demo/a91c4e/relays/<n>/set |
Contrôleur vers Pi | ON, OFF (ou TOGGLE, non retained) |
Oui pour ON/OFF |
connectmq/demo/a91c4e/relays/<n>/state |
Pi vers tout le monde | ON ou OFF |
Oui |
connectmq/demo/a91c4e/relays/status |
Pi (et last will) | online ou offline |
Oui |
Remplacez a91c4e par votre propre chaîne aléatoire. Sur un broker public partagé, chaque topic est lisible par tous : un préfixe unique isole votre trafic de test. Si vous voulez revoir pourquoi des topics set et state séparés sont une bonne pratique, le guide GPIO du Raspberry Pi via MQTT détaille le modèle commande/état.
Comment les états retained survivent à un redémarrage
Les retained messages sont la clé d'un contrôleur de relais qui se comporte correctement après une coupure de courant. Le broker conserve le dernier message retained de chaque topic et le transmet immédiatement à tout nouvel abonné. Cela offre deux comportements utiles :
- Lorsque votre tableau de bord publie
ONsurrelays/1/setavec le drapeau retain, le broker le conserve. Après le redémarrage du Pi et son abonnement àrelays/+/set, il reçoit l'état souhaité pour chaque canal et le restaure. - Lorsque le Pi publie
relays/1/stateavec retain, tout tableau de bord ouvert plus tard voit immédiatement l'état réel au lieu d'attendre le prochain changement.
Il y a un piège : ne rendez jamais TOGGLE retained. Un toggle retained serait rejoué à chaque reconnexion et inverserait le relais à chaque fois. Le script ci-dessous ignore donc les toggles retained. Pour approfondir le mécanisme, lisez notre explication des retained messages MQTT.
Script Python de relais Raspberry Pi MQTT
Créez un dossier de projet et un environnement virtuel :
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
L'option --system-site-packages permet à l'environnement virtuel d'utiliser le backend lgpio dont gpiozero a besoin sur le Raspberry Pi 5, et qui fonctionne aussi sur les modèles plus anciens. Enregistrez le code suivant sous 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 # la plupart des cartes optocouplées grand public sont actives à l'état bas
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)
Lancez-le avec python relay_mqtt.py. Le script coupe tous les relais lors d'un arrêt propre ; si vous préférez que les charges restent alimentées quand le service s'arrête, supprimez la boucle dans shutdown.
Tester depuis votre ordinateur portable
Ouvrez un second terminal sur n'importe quelle machine dotée des clients Mosquitto et observez les topics d'état :
mosquitto_sub -h broker.connectmq.com -p 1883 -i laptop-a91c4e \
-t 'connectmq/demo/a91c4e/relays/#' -v
Puis activez le canal 1 avec une commande retained et inversez le 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
Vous devez entendre les clics et voir apparaître relays/1/state ON. Redémarrez maintenant le Pi. Lorsque le script se reconnecte, le canal 1 repasse à ON car la commande retained est toujours sur le broker, tandis que le canal 2 démarre à OFF car les toggles ne sont jamais retained. Pour effacer une commande retained, publiez un message retained vide : mosquitto_pub ... -t '.../relays/1/set' -r -n.
L'exécuter en tant que service
Un contrôleur de relais doit démarrer au boot et redémarrer après un plantage. Créez /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
Activez-le avec sudo systemctl daemon-reload && sudo systemctl enable --now relay-mqtt. L'explication complète des fichiers de service, de la journalisation et des fichiers d'environnement se trouve dans lancer un script Python MQTT au démarrage. Adaptez le nom d'utilisateur si le vôtre n'est pas pi.
Passer du serveur de test à la production
Le serveur MQTT public gratuit est parfait pour la première heure, mais quiconque devine votre topic pourrait commuter vos relais. Pour tout ce qui est relié à de vrais équipements, utilisez un broker privé avec authentification et TLS. Avec un broker MQTT managé comme ConnectMQ, vous créez un broker dans le portail, copiez le nom d'utilisateur, le mot de passe et le client ID générés dans le fichier de service ci-dessus, et réglez le port sur 8883. Le client ID est lié aux identifiants émis : un autre appareil ne peut donc pas usurper votre contrôleur. Le script, lui, ne change pas du tout.
Le portail ConnectMQ aide aussi pendant la mise en service : son client MQTT intégré au navigateur peut publier ON et OFF sur chaque canal, et l'explorateur de topics en direct affiche d'un coup d'œil les états retained et les valeurs de statut. Si vous cherchez une alternative matérielle au Pi pour la même tâche, comparez avec le montage relais ESP32 via MQTT.
Dépannage
- Les relais s'enclenchent au démarrage et se coupent quand on les commande à ON : votre carte est active à l'état haut ; réglez
ACTIVE_HIGH = True. - Les LED de la carte s'allument mais le relais ne clique pas : l'alimentation des bobines est trop faible ; alimentez
JD-VCCdepuis une source 5 V séparée. Connection refused: Not authorized: vérifiez le nom d'utilisateur, le mot de passe et le client ID, ou supprimez-les si vous utilisez le serveur public anonyme.- Le relais restaure un ancien état après redémarrage : une commande retained périmée se trouve sur le broker ; effacez-la avec une publication retained vide.
- Erreurs
GPIO busy: un autre script ou service utilise les mêmes broches ; arrêtez-le avant de lancer celui-ci.
Démarrer avec ConnectMQ
Prêt à piloter de vrais équipements ? Inscrivez-vous à l'offre gratuite Serverless (Free), sans carte bancaire, créez un broker et faites pointer votre service de relais dessus en TLS. Vous disposez de MQTT 3.1.1 et 5.0, des retained messages et d'un portail pour suivre chaque commutation. Vous découvrez ces notions ? Le tutoriel MQTT sur Raspberry Pi couvre les bases, et notre présentation du broker MQTT cloud explique quand l'hébergement l'emporte sur Mosquitto installé sur le Pi.
Questions fréquentes
Pourquoi mon relais change-t-il d'état après le redémarrage du Raspberry Pi ?
Il rejoue une commande retained stockée sur le broker, ce qui est généralement le comportement souhaité. Si l'état est incorrect, publiez la bonne valeur avec retain ou effacez le topic avec un message retained vide.
Puis-je utiliser la QoS 1 pour les commandes de relais ?
Oui, et c'est même recommandé. La QoS 1 garantit que la commande arrive au moins une fois, et comme ON et OFF sont idempotents, une livraison en double est sans conséquence.
Combien de relais un Raspberry Pi peut-il piloter ?
Autant que vous avez de broches GPIO libres, généralement jusqu'à huit sur une carte grand public. Pour davantage de canaux, utilisez un extenseur de GPIO I2C comme le MCP23017 en conservant la même organisation de topics.
Est-il sûr de piloter des appareils sur secteur de cette façon ?
Uniquement avec des relais dimensionnés pour la charge, un boîtier adapté et un câblage conforme aux normes électriques locales. Utilisez toujours des connexions TLS authentifiées pour tout ce qui commute de vrais équipements.



