Raspberry Pi

Piloter des relais Raspberry Pi via MQTT : commander une carte multicanal depuis n'importe où

Câblez une carte 4 relais sur un Raspberry Pi, pilotez chaque canal depuis des topics MQTT avec Python et gpiozero, et utilisez les retained messages pour que chaque relais retrouve le bon état après un redémarrage ou une coupure de courant.

Raspberry Pi câblé à une carte quatre relais avec LED témoins, piloté par des messages MQTT depuis le cloud
Essayez-le sur votre propre broker MQTTObtenez un broker MQTT privé et managé avec l’offre Free et partez d’exemples de code prêts à l’emploi.
Essayer gratuitement Exemples de code Aucune carte bancaire requise

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 :

  1. Lorsque votre tableau de bord publie ON sur relays/1/set avec 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.
  2. Lorsque le Pi publie relays/1/state avec 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

  1. 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.
  2. Les LED de la carte s'allument mais le relais ne clique pas : l'alimentation des bobines est trop faible ; alimentez JD-VCC depuis une source 5 V séparée.
  3. 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.
  4. 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.
  5. 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.

Articles liés

Journal d'accès RFID MQTT sur Raspberry Pi avec le lecteur RC522

Câblez un lecteur RFID RC522 sur un Raspberry Pi, lisez les badges en Python, vérifiez-les dans une liste blanche locale et publiez chaque tentative d'accès sous forme d'événement MQTT exploitable par vos outils de journalisation, tableaux de bord et alertes.

8 min de lecture

Installer Mosquitto sur Raspberry Pi comme broker MQTT local sécurisé

Installez Mosquitto sur un Raspberry Pi, sécurisez-le avec des mots de passe et une configuration de listener raisonnable, testez-le avec mosquitto_pub et mosquitto_sub, et découvrez quand un broker local doit faire un pont vers un broker cloud.

9 min de lecture

Tous les articles →

Votre broker MQTT privé, à quelques clics

Créez un compte, récupérez les identifiants de votre broker et connectez votre premier appareil — aucun serveur à installer, mettre à jour ou dimensionner.