MQTT-Fahrzeugortung funktioniert gut, weil das Protokoll genau für diese Art von Verbindung entworfen wurde: kleine Geräte, die häufig kleine Nachrichten über unzuverlässige, nach Volumen abgerechnete Netze senden. In diesem Leitfaden entwerfen Sie eine Topic-Struktur für eine Flotte, wählen kompakte GPS-Payloads, stimmen Keep Alive und Sessions für Mobilfunk-Tracker ab, erkennen Fahrzeuge, die offline gehen, und bauen eine Live-Karte im Browser, die sich direkt aus MQTT-Abonnements auf ConnectMQ aktualisiert.
Warum MQTT zur Flottentelematik passt
Ein Mobilfunk-Tracker in einem Transporter verbringt sein Leben damit, Funkzellen zu wechseln, in Tunnel zu fahren und in Parkhäusern den Empfang zu verlieren. Jeder Reconnect kostet Zeit, Energie und Datenvolumen. MQTT hilft auf mehreren Wegen:
- Eine persistente Verbindung statt einer neuen HTTP-Anfrage pro Position, sodass Header und Handshakes nur einmal anfallen.
- Geringer fester Overhead pro Nachricht, was zählt, wenn jedes Kilobyte abgerechnet wird.
- Keep Alive und Last Will geben dem Server ein klares Signal, wenn ein Fahrzeug verstummt.
- QoS 1 mit persistenten Sessions sorgt dafür, dass Befehle ein Fahrzeug erreichen, das kurz keinen Empfang hatte.
- Fan-out über Abonnements: Dieselbe Position speist eine Disponentenkarte, einen Geofencing-Service und einen Historienschreiber, ohne dass der Tracker etwas extra tun muss.
Dasselbe Muster trägt auch in der Industrie: Ein Maschinenbauer kann seine Servicefahrzeuge orten, ein Werk mit mehreren Hallen die Transporte zwischen Lager und Montage verfolgen. Zur Auffrischung der Zustellgarantien lesen Sie MQTT-QoS-Stufen erklärt.
Topic-Design für die Fahrzeugortung
Beginnen Sie mit einer Hierarchie, die zu den Abfragen der Nutzer passt: nach Flotte, dann nach Fahrzeug, dann nach Datentyp.
fleet/{fleetId}/vehicle/{vehicleId}/position live GPS fixes
fleet/{fleetId}/vehicle/{vehicleId}/status online/offline (retained)
fleet/{fleetId}/vehicle/{vehicleId}/event ignition, harsh braking, door open
fleet/{fleetId}/vehicle/{vehicleId}/diag battery voltage, signal quality
fleet/{fleetId}/vehicle/{vehicleId}/cmd commands to the tracker
So kann eine Disponentenkarte fleet/north/vehicle/+/position abonnieren, ein Alarmdienst fleet/+/vehicle/+/event und ein Betriebs-Dashboard fleet/+/vehicle/+/status. Halten Sie Bezeichner kurz und stabil: eine Fahrzeug-ID wie v0142 statt eines vollständigen Kennzeichens mit Leerzeichen. Vermeiden Sie personenbezogene Daten wie den Namen des Fahrers in Topic-Namen.
Payload-Design
Positionsnachrichten machen den Großteil Ihres Datenverkehrs aus und verdienen daher Aufmerksamkeit.
| Feld | Schlüssel | Beispiel | Hinweise |
|---|---|---|---|
| Zeitstempel | t |
1792137600 |
Unix-Sekunden aus der GNSS-Zeit, nicht die Empfangszeit des Servers |
| Breitengrad | la |
41.01224 |
5 Nachkommastellen entsprechen etwa einem Meter |
| Längengrad | lo |
28.97601 |
Gleiche Genauigkeit |
| Geschwindigkeit | s |
48 |
km/h, Ganzzahl |
| Fahrtrichtung | h |
270 |
Grad, Ganzzahl |
| Fix-Qualität | q |
3 |
0 = kein Fix, 2 = 2D, 3 = 3D |
| Satelliten | n |
9 |
Nützlich, um schlechte Fixes herauszufiltern |
Eine daraus resultierende Payload ist etwa 90 Bytes groß:
{"t":1792137600,"la":41.01224,"lo":28.97601,"s":48,"h":270,"q":3,"n":9}
Praktische Regeln:
- Immer den Gerätezeitstempel mitsenden. Nachrichten, die während einer Empfangslücke gepuffert wurden, kommen verspätet an; Karte und Historie müssen nach Fix-Zeit sortieren.
- Beim Puffern bündeln. Publizieren Sie nach einem Tunnel eine Nachricht mit einem Array gepufferter Fixes statt Hunderter Einzelnachrichten.
- Adaptiv melden. Alle 5 bis 15 Sekunden während der Fahrt, alle paar Minuten im Stand und sofort bei deutlichen Richtungswechseln.
- Schlechte Fixes auf dem Gerät verwerfen. Ist die Fix-Qualität 0, publizieren Sie keine Position.
Keep Alive, Sessions und QoS für Mobilfunkverbindungen
- Keep Alive: 60 bis 120 Sekunden sind im Mobilfunk ein vernünftiger Ausgangspunkt. Kürzere Werte erkennen Abbrüche schneller, halten aber das Modem wach; NAT-Timeouts der Netzbetreiber können zudem untätige Verbindungen stillschweigend beenden, wählen Sie den Wert also nicht so lang, dass die NAT-Zuordnung vorher abläuft.
- QoS: QoS 0 ist für hochfrequente Positionen akzeptabel, bei denen der nächste Fix den letzten ersetzt; QoS 1 für Ereignisse und Befehle, die nicht verloren gehen dürfen.
- Persistente Sessions: Verbinden Sie sich mit Clean Start auf false und einer Session Expiry (MQTT 5), zum Beispiel 10 bis 30 Minuten, damit Befehle, die während eines kurzen Ausfalls gesendet werden, beim Reconnect zugestellt werden.
- Message Expiry: Setzen Sie bei Befehlen wie „Türen entriegeln“ ein Ablaufdatum, damit ein Fahrzeug, das eine Stunde später zurückkehrt, keine alte Anweisung ausführt.
- Last Will: Registrieren Sie einen retained Last Will
offlineauf dem Status-Topic und publizieren Sie nach dem Verbinden ein retainedonline.
Einen Tracker in Python simulieren
Bevor die Hardware eintrifft, simulieren Sie ein Fahrzeug. Dieses Skript fährt eine Strecke ab, nutzt einen Last Will und publiziert Positionen mit paho-mqtt 2.x. Es zielt auf den öffentlichen MQTT-Broker, damit Sie es sofort ausführen können; wechseln Sie für den echten Einsatz zu Ihren eigenen Zugangsdaten und Port 8883.
import json
import math
import time
import uuid
import paho.mqtt.client as mqtt
HOST = "broker.connectmq.com"
PORT = 1883
PREFIX = f"connectmq/demo/{uuid.uuid4().hex[:6]}"
VEHICLE = "v0142"
BASE = f"{PREFIX}/fleet/north/vehicle/{VEHICLE}"
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
client_id=f"sim-{VEHICLE}-{uuid.uuid4().hex[:6]}",
protocol=mqtt.MQTTv5)
client.will_set(f"{BASE}/status", "offline", qos=1, retain=True)
def on_connect(client, userdata, flags, reason_code, properties):
print("Connected:", reason_code, "| topic prefix:", PREFIX)
client.publish(f"{BASE}/status", "online", qos=1, retain=True)
client.on_connect = on_connect
client.connect(HOST, PORT, keepalive=90)
client.loop_start()
lat, lon, heading = 41.0100, 28.9700, 45.0
try:
while True:
lat += 0.0003 * math.cos(math.radians(heading))
lon += 0.0003 * math.sin(math.radians(heading))
heading = (heading + 3) % 360
fix = {"t": int(time.time()), "la": round(lat, 5), "lo": round(lon, 5),
"s": 42, "h": int(heading), "q": 3, "n": 9}
client.publish(f"{BASE}/position", json.dumps(fix), qos=0)
time.sleep(5)
except KeyboardInterrupt:
client.publish(f"{BASE}/status", "offline", qos=1, retain=True).wait_for_publish()
client.loop_stop()
client.disconnect()
Notieren Sie sich das ausgegebene Präfix; Sie brauchen es für die Karte. Der öffentliche MQTT-Broker erlaubt 10 Nachrichten pro Sekunde und Client sowie 8 Topic-Ebenen, das vollständige Topic connectmq/demo/xxxxxx/fleet/north/vehicle/v0142/position (8 Ebenen) passt also gerade noch. Kürzen Sie die Hierarchie, wenn Sie weitere Ebenen hinzufügen. Denken Sie daran, dass der öffentliche MQTT-Broker für alle lesbar und nur für Tests und Prototypen gedacht ist: Verwenden Sie nur simulierte Koordinaten.
Eine Live-Flottenkarte aus Abonnements
Die Karte ist einfach ein MQTT-Client im Browser, der Positionen abonniert und Marker verschiebt. Diese Seite nutzt MQTT.js über Secure WebSocket und Leaflet.
// map.js - Leaflet und MQTT.js in die Seite laden und ein Container-Element mit der id "map" anlegen
const PREFIX = "connectmq/demo/REPLACE_WITH_PREFIX";
const map = L.map("map").setView([41.01, 28.97], 13);
L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
attribution: "OpenStreetMap contributors",
}).addTo(map);
const markers = new Map();
const client = mqtt.connect("wss://broker.connectmq.com:8084/mqtt", {
clientId: "fleet-map-" + Math.random().toString(16).slice(2, 10),
protocolVersion: 5,
});
client.on("connect", () => {
client.subscribe(`${PREFIX}/fleet/+/vehicle/+/position`);
client.subscribe(`${PREFIX}/fleet/+/vehicle/+/status`);
});
client.on("message", (topic, payload) => {
const parts = topic.split("/");
const vehicleId = parts[parts.length - 2];
const kind = parts[parts.length - 1];
if (kind === "position") {
const fix = JSON.parse(payload.toString());
let m = markers.get(vehicleId);
if (!m) {
m = L.marker([fix.la, fix.lo]).addTo(map).bindTooltip(vehicleId);
markers.set(vehicleId, m);
}
m.setLatLng([fix.la, fix.lo]);
m.setTooltipContent(`${vehicleId} - ${fix.s} km/h`);
} else if (kind === "status") {
const m = markers.get(vehicleId);
if (m) m.setOpacity(payload.toString() === "online" ? 1 : 0.4);
}
});
Authentifizieren Sie die Karte im Produktivbetrieb mit Zugangsdaten, die Ihr Backend ausstellt, statt ein gemeinsames Passwort in die Seite einzubetten, und erwägen Sie bei vielen gleichzeitig geöffneten Kartenansichten ein kleines Backend, das einmal abonniert und die Positionen an die Browser verteilt.
Historie speichern und Geofencing
ConnectMQ ist der Broker, keine Datenbank, daher laufen Historie und Geofencing als Ihre eigenen MQTT-Clients. Ein Python- oder Node.js-Service, der fleet/+/vehicle/+/position abonniert, kann in PostgreSQL mit PostGIS oder eine Zeitreihendatenbank schreiben, und ein zweiter Service kann jeden Fix gegen Geofence-Polygone prüfen und auf das Event-Topic publizieren. Ab Serverless (Paid) lassen Shared Subscriptions wie $share/geo/fleet/+/vehicle/+/position mehrere Instanzen die Last aufteilen. Die Schwellenwert-Alarmregeln im Portal sind zudem praktisch für einfache Prüfungen, etwa wenn die Batteriespannung in den Diagnosedaten unter einen Grenzwert fällt.
Empfangslücken auf dem Gerät abfangen
Tracker verbringen echte Zeit ohne Empfang, die Firmware braucht also einen Plan dafür. Halten Sie einen Ringpuffer der letzten Fixes im Flash oder RAM vor, dimensioniert für die längste erwartete Lücke, zum Beispiel eine Stunde Fixes mit reduzierter Rate. Kehrt die Verbindung zurück, publizieren Sie zuerst die aktuelle Position, damit die Live-Karte sofort aktualisiert wird, und leeren Sie dann den Puffer in Bündeln mit ihren ursprünglichen Zeitstempeln. Konsumenten, die Historie schreiben, sollten per Upsert nach Fahrzeug und Zeitstempel arbeiten, damit ein unter QoS 1 doppelt zugestelltes Bündel keine doppelten Punkte erzeugt.
Checkliste für den Produktivbetrieb
- Eindeutige Zugangsdaten und Client ID pro Tracker, TLS auf 8883.
- Gerätezeitstempel bei jedem Fix; nach Ausfällen bündeln.
- Adaptives Meldeintervall.
- Retained Status mit Last Will.
- Persistente Sessions und Message Expiry für Befehle.
- Keine personenbezogenen Daten in Topic-Namen.
- Reconnect-Backoff mit Jitter für die gesamte Flotte.
Die ESP32-Seite eines Trackers behandelt ESP32-GPS-Tracker mit MQTT, und MQTT für mobile Apps beschreibt Apps für Fahrer oder Disponenten. Zu den Hosting-Optionen lesen Sie den Leitfaden zum Cloud-MQTT-Broker.
Orten Sie Ihre Flotte mit ConnectMQ
Erstellen Sie unter app.connectmq.com ohne Kreditkarte einen kostenlosen Serverless-Broker, richten Sie Ihre Tracker oder den Simulator per TLS darauf und sehen Sie zu, wie Positionen im Live-Topic-Explorer eintreffen. ConnectMQ ist ein verwalteter MQTT-Broker, mit den Plänen Serverless (Paid) und Dedicated, wenn Ihre Flotte wächst.
Häufig gestellte Fragen
Wie oft sollte ein Fahrzeug-Tracker seine Position über MQTT publizieren?
Alle 5 bis 15 Sekunden während der Fahrt und alle paar Minuten im Stand sind ein üblicher Ausgangspunkt. Passen Sie das an Ihren Anwendungsfall und Ihren Datentarif an.
Welche QoS sollte ich für GPS-Positionen verwenden?
QoS 0 reicht für häufige Positionen oft aus, weil der nächste Fix den letzten ersetzt. Verwenden Sie QoS 1 für Ereignisse und Befehle, die ankommen müssen.
Woran erkenne ich, dass ein Fahrzeug offline geht?
Registrieren Sie einen retained Last Will offline auf dem Status-Topic des Fahrzeugs und publizieren Sie nach dem Verbinden ein retained online.
Kann ConnectMQ den Standortverlauf speichern?
Nein, der Broker leitet Nachrichten weiter. Betreiben Sie einen eigenen Subscriber-Service, der Positionen in eine Datenbank wie PostgreSQL mit PostGIS schreibt.



