L'intégration ChirpStack MQTT publie chaque événement LoRaWAN de votre propre serveur de réseau vers un broker MQTT, ce qui en fait le point de départ naturel pour amener les données des capteurs de terrain vers les tableaux de bord et l'automatisation. Dans ce tutoriel, vous allez configurer l'intégration dans ChirpStack v4, décoder les payloads avec le codec d'un profil d'appareil, puis transférer les uplinks décodés vers un broker cloud de deux façons : un pont Mosquitto sans code et un relais Node.js qui remet en forme les données dans votre propre arborescence de topics.
Comment ChirpStack utilise MQTT
ChirpStack est un serveur de réseau LoRaWAN open source. Dans une installation typique (par exemple la configuration Docker Compose officielle), MQTT intervient deux fois :
- Backend des passerelles. Les ponts des passerelles publient les trames radio brutes vers un broker Mosquitto local, auquel ChirpStack s'abonne.
- Intégration MQTT. Après déduplication, déchiffrement et décodage, ChirpStack publie les événements applicatifs (uplinks, joins, acquittements, erreurs) sur des topics MQTT du même broker local.
Ce sont les événements de l'intégration qui vous intéressent. Ils résident sur un broker situé dans le réseau du serveur LoRaWAN, généralement inaccessible depuis les tableaux de bord, les téléphones ou d'autres sites. Les transférer vers un broker cloud règle ce problème sans exposer le serveur de réseau lui-même.
Sensors ~~LoRa~~> Gateway --> local Mosquitto <--> ChirpStack
|
application/.../event/up
|
Mosquitto bridge or Node.js forwarder
| (TLS)
v
Cloud MQTT broker --> Node-RED, dashboards, alerts
Étape 1 : activer l'intégration ChirpStack MQTT
Dans chirpstack.toml, la section d'intégration se présente ainsi dans une installation standard :
[integration]
enabled = ["mqtt"]
[integration.mqtt]
server = "tcp://mosquitto:1883/"
json = true
Avec json = true, les événements sont en JSON plutôt qu'en Protobuf, ce qui est bien plus simple à exploiter. Le topic d'événement par défaut est :
application/<application_id>/device/<dev_eui>/event/<event>
où <event> vaut up, join, ack, txack, log, status ou location. Redémarrez ChirpStack après avoir modifié le fichier, puis observez le broker local sur le serveur :
mosquitto_sub -h localhost -p 1883 -t 'application/+/device/+/event/up' -v
Étape 2 : décoder les payloads avec un codec
Les payloads LoRaWAN bruts sont des octets, livrés encodés en base64 dans le champ data. Ajoutez un codec JavaScript au profil d'appareil (Device profiles > votre profil > Codec, type JavaScript) pour que ChirpStack ajoute un champ object décodé à chaque uplink :
function decodeUplink(input) {
var b = input.bytes;
if (input.fPort !== 1 || b.length < 6) {
return { errors: ["unexpected fPort or length"] };
}
var temp = (b[2] << 8) | b[3];
if (temp & 0x8000) temp -= 0x10000;
return {
data: {
moisture_pct: ((b[0] << 8) | b[1]) / 10,
soil_temp_c: temp / 10,
battery_v: ((b[4] << 8) | b[5]) / 1000
}
};
}
Un événement d'uplink contient alors, entre autres, deviceInfo.deviceName, deviceInfo.devEui, fCnt, fPort, time, object (vos valeurs décodées) et rxInfo (une entrée par passerelle avec rssi et snr).
Option A : transférer avec un pont Mosquitto
Si ChirpStack tourne déjà avec Mosquitto, un pont est la voie la plus rapide : quelques lignes de configuration et Mosquitto transfère les topics correspondants vers le broker cloud, en se reconnectant automatiquement. Créez /etc/mosquitto/conf.d/cloud-bridge.conf (ou l'équivalent dans votre volume Docker) :
connection connectmq
address broker.connectmq.com:1883
remote_clientid cs-bridge-c3h7k5
cleansession true
try_private false
bridge_protocol_version mqttv311
notifications true
notification_topic connectmq/demo/c3h7k5/bridge/state
# local: application/<app>/device/<eui>/event/up
# remote: connectmq/demo/c3h7k5/<app>/device/<eui>/event/up
topic +/device/+/event/up out 1 application/ connectmq/demo/c3h7k5/
La ligne topic prend un motif, une direction, une QoS et deux préfixes. Mosquitto retire le préfixe local application/ et ajoute le préfixe distant, ce qui maintient le topic distant à huit niveaux, le maximum autorisé sur le serveur MQTT public gratuit. Remplacez c3h7k5 par votre propre chaîne aléatoire : ce serveur, réservé aux tests et aux prototypes, est partagé et lisible par tous. try_private false est recommandé lorsque le broker distant n'est pas Mosquitto.
Redémarrez Mosquitto et abonnez-vous depuis n'importe où :
mosquitto_sub -h broker.connectmq.com -p 1883 -i watch-c3h7k5 \
-t 'connectmq/demo/c3h7k5/#' -v
En production, passez au TLS et aux identifiants délivrés avec votre broker privé :
connection connectmq
address broker.connectmq.com:8883
bridge_cafile /etc/ssl/certs/ca-certificates.crt
remote_clientid YOUR_CLIENT_ID
remote_username YOUR_USERNAME
remote_password YOUR_PASSWORD
cleansession true
try_private false
notifications true
notification_topic farm1/lorawan/bridge/state
topic +/device/+/event/up out 1 application/ farm1/lorawan/
La limite d'un pont est que les payloads sont transférés tels quels. Les événements ChirpStack sont verbeux, avec des données en base64, des informations d'émission et toutes les métadonnées des passerelles, et les topics utilisent des UUID d'application et des DevEUI au lieu de noms lisibles.
Option B : un relais ChirpStack MQTT en Node.js
Lorsque les consommateurs doivent voir des données compactes et nommées, un petit relais est préférable. Celui-ci utilise MQTT.js v5, s'abonne aux uplinks sur le broker local et republie un enregistrement propre par appareil. Installez avec npm install mqtt.
// forwarder.mjs
import mqtt from "mqtt";
const LOCAL_URL = process.env.LOCAL_URL ?? "mqtt://localhost:1883";
const OUT_URL = process.env.OUT_URL ?? "mqtt://broker.connectmq.com:1883";
const OUT_PREFIX = process.env.OUT_PREFIX ?? "connectmq/demo/c3h7k5/farm";
const out = mqtt.connect(OUT_URL, {
clientId: process.env.OUT_CLIENT_ID ?? "cs-forwarder-c3h7k5",
username: process.env.OUT_USER,
password: process.env.OUT_PASS,
protocolVersion: 5,
will: { topic: `${OUT_PREFIX}/forwarder/status`, payload: "offline", qos: 1, retain: true },
});
out.on("connect", () => {
console.log("cloud broker connected");
out.publish(`${OUT_PREFIX}/forwarder/status`, "online", { qos: 1, retain: true });
});
out.on("error", (err) => console.error("cloud:", err.message));
const local = mqtt.connect(LOCAL_URL, { clientId: "cs-forwarder-local-c3h7k5" });
local.on("connect", () => {
local.subscribe("application/+/device/+/event/up", { qos: 1 });
console.log("local broker connected");
});
local.on("message", (topic, payload) => {
let ev;
try {
ev = JSON.parse(payload.toString());
} catch {
return console.warn("non-JSON event on", topic);
}
if (!ev.object) {
return console.warn(`${ev.deviceInfo?.deviceName}: no decoded object, check the codec`);
}
const rx = ev.rxInfo ?? [];
const best = rx.reduce((a, g) => ((g.rssi ?? -999) > (a.rssi ?? -999) ? g : a), {});
const name = ev.deviceInfo.deviceName.replace(/[^A-Za-z0-9_-]/g, "_");
const record = {
ts: ev.time,
dev_eui: ev.deviceInfo.devEui,
...ev.object,
f_cnt: ev.fCnt,
rssi: best.rssi ?? null,
snr: best.snr ?? null,
gateways: rx.length,
};
out.publish(`${OUT_PREFIX}/${name}/telemetry`, JSON.stringify(record), { qos: 1, retain: true });
console.log(name, record);
});
Exécutez-le à côté de ChirpStack :
node forwarder.mjs
# production
OUT_URL=mqtts://broker.connectmq.com:8883 OUT_USER=YOUR_USERNAME \
OUT_PASS=YOUR_PASSWORD OUT_CLIENT_ID=YOUR_CLIENT_ID OUT_PREFIX=farm1/north \
node forwarder.mjs
Les noms d'appareils sont assainis avant d'être placés dans le topic, car des caractères comme +, # et / casseraient la hiérarchie. Les enregistrements sont retained afin que les tableaux de bord voient immédiatement la dernière mesure de chaque capteur, même si les appareils LoRaWAN n'émettent parfois que toutes les 15 minutes.
Choisir entre les deux
| Critère | Pont Mosquitto | Relais Node.js |
|---|---|---|
| Effort de mise en place | Un fichier de configuration | Un petit script et un service |
| Payload | Événement ChirpStack complet | Enregistrement compact que vous définissez |
| Noms des topics | ID d'application et DevEUI | Noms d'appareils lisibles, votre hiérarchie |
| Éléments à gérer | Rien d'autre que Mosquitto | Un processus supplémentaire à surveiller |
| Downlinks | Ajouter une ligne de topic in |
Ajouter un abonnement et une publication |
De nombreuses installations commencent par le pont pour faire circuler les données, puis passent à un relais lorsque les consommateurs se multiplient. Le tutoriel The Things Network MQTT applique la même idée de relais en Python, ce qui est utile si votre exploitation combine réseaux LoRaWAN communautaires et privés.
Envoyer des downlinks via ChirpStack
Pour mettre un downlink en file d'attente, publiez sur le topic de commande de l'appareil sur le broker local :
mosquitto_pub -h localhost -p 1883 \
-t 'application/YOUR_APP_ID/device/0004a30b001c1234/command/down' \
-m '{"devEui":"0004a30b001c1234","confirmed":false,"fPort":2,"data":"Hg=="}'
data est en base64 (Hg== correspond à l'octet unique 30, par exemple un intervalle d'émission en minutes). Les appareils de classe A le reçoivent après leur prochain uplink. Ne transférez des commandes de downlink depuis le cloud que si vous faites confiance à chaque client capable d'y publier, et validez-les dans le relais.
Dépannage
- Rien sur
event/up. Vérifiez que l'appareil a rejoint le réseau (cherchezevent/join), que l'intégration est activée et quejson = trueest défini si vous attendez du JSON. objectabsent. Le codec est manquant, a échoué ou a renvoyé des erreurs ; les messagesevent/logaffichent les erreurs de codec.- Le pont se connecte puis décroche.
remote_clientiden double, identifiants erronés ou fichier CA manquant pour le port 8883. Le journal de Mosquitto en donne la raison. - Messages rejetés sur le serveur MQTT public gratuit. Le topic dépasse huit niveaux ou le client dépasse dix messages par seconde ; utilisez des préfixes plus courts et n'y transférez pas les événements
log.
Surveiller le chemin de transfert
Une chaîne LoRaWAN comporte plusieurs points de défaillance silencieuse : une passerelle perd son alimentation, ChirpStack cesse de décoder après une modification du codec, ou le relais s'arrête. Surveillez chaque couche séparément. L'interface web de ChirpStack affiche l'heure de dernière activité des passerelles et les trames par appareil. Le topic status retained du relais, appuyé par son last will, indique si le relais lui-même est vivant, et le topic de notification du pont fait de même pour l'option A. Enfin, l'âge du message telemetry retained de chaque appareil vous dit si les données circulent encore de bout en bout.
Côté cloud, l'explorateur de topics en direct de ConnectMQ est le moyen le plus rapide de confirmer que les enregistrements arrivent avec la forme attendue après une modification du codec, et une règle d'alerte à seuil sur un champ comme battery_v prévient avant qu'un capteur ne devienne muet.
Notes pour la production
Gardez le serveur de réseau et le broker des passerelles privés, et faites du relais ou du pont la seule voie de sortie. Un broker MQTT managé devient alors le point central commun aux données LoRaWAN, aux appareils Wi-Fi et aux contrôleurs, et l'article sur le broker MQTT dans le cloud compare les options d'hébergement. Pour savoir quoi faire des données, le guide agriculture intelligente IoT et le montage système d'arrosage automatique présentent des usages concrets, et le guide de l'automatisation IoT offre une vue d'ensemble.
Transférez vos données ChirpStack vers ConnectMQ
ConnectMQ offre à vos données LoRaWAN un broker MQTT privé sécurisé par TLS, avec un explorateur de topics en direct, un tableau de bord et des règles d'alerte à seuil pour des valeurs comme une batterie faible ou le gel. Créez un broker Serverless gratuit sur app.connectmq.com, sans carte bancaire, et dirigez-y votre pont ou votre relais.
Questions fréquentes
Quel topic MQTT ChirpStack v4 utilise-t-il pour les uplinks ?
application/<application_id>/device/<dev_eui>/event/up par défaut, avec des payloads JSON lorsque json = true est défini dans l'intégration MQTT.
ChirpStack peut-il publier directement vers un broker cloud ?
Vous pouvez faire pointer le server de l'intégration vers un autre broker, mais le garder en local et transférer des topics choisis vous donne le contrôle sur ce qui sort du réseau.
Pourquoi les payloads ChirpStack sont-ils en base64 ?
Le champ data transporte les octets LoRaWAN bruts encodés en base64. Ajoutez un codec au profil d'appareil pour obtenir les valeurs décodées dans le champ object.
Le pont Mosquitto met-il les messages en mémoire tampon quand internet est coupé ?
Avec cleansession false et la QoS 1, Mosquitto peut mettre en file les messages destinés au pont pendant la coupure, dans les limites de file configurées. Avec cleansession true, les messages publiés pendant la coupure ne sont pas transférés.



