Raspberry Pi Systemwerte per MQTT zu veröffentlichen ist der einfachste Weg, auf einen Blick zu erkennen, ob jeder Ihrer Pis gesund ist. In diesem Tutorial schreiben Sie einen Python-Dienst, der in festem Intervall CPU-Temperatur, Last, Arbeitsspeicher, Festplattenbelegung, Betriebszeit und Drosselungs-Flags der Stromversorgung meldet, sich mit einem retained Status online meldet und den Broker über einen Last Will als offline markieren lässt. Dasselbe Skript läuft unverändert auf einem Pi oder auf fünfzig.
Warum Raspberry Pis über MQTT überwachen
Pis im Feld fallen auf vorhersehbare Weise aus: SD-Karten laufen mit Logs voll, Gehäuse stauen Wärme und die CPU drosselt, billige Netzteile verursachen Unterspannung, und WLAN-Abbrüche machen ein Gerät still und leise unerreichbar. Klassische Monitoring-Stacks fragen jede Maschine ab, was offene Ports oder einen Agenten mit eingehendem Zugriff erfordert. MQTT dreht das um. Jeder Pi baut eine ausgehende Verbindung zu einem Broker auf und sendet seine eigenen Werte, das funktioniert also hinter NAT, an Mobilfunkroutern und in Netzen, die Sie nicht kontrollieren.
Der Broker liefert die Anwesenheitserkennung gleich mit. Mit einem retained Status online und einem Last Will offline sieht jedes abonnierende Dashboard sofort den aktuellen Zustand jedes Pi, und der Broker aktualisiert ihn, wenn ein Gerät verschwindet. Wenn Ihnen die Mechanik neu ist, lesen Sie zuerst, was ein MQTT-Broker macht.
Die Metriken und ihre Quellen
| Metrik | Quelle | Warum sie wichtig ist |
|---|---|---|
cpu_temp |
/sys/class/thermal/thermal_zone0/temp |
Mit steigender Temperatur beginnt die Drosselung |
load1, load5 |
os.getloadavg() |
Dauerhafte Last über der Kernzahl bedeutet einen Rückstau |
cpu_pct |
psutil.cpu_percent() |
Kurzfristige CPU-Auslastung |
mem_pct |
psutil.virtual_memory() |
Speicherlecks in lang laufenden Diensten |
disk_pct |
psutil.disk_usage("/") |
Ein volles Root-Dateisystem legt alles lahm |
uptime_s |
psutil.boot_time() |
Unerwartete Neustarts zeigen sich als Rücksprung |
throttled |
vcgencmd get_throttled |
Flags für Unterspannung und thermische Drosselung |
wifi_rssi |
/proc/net/wireless |
Schwaches Signal erklärt Verbindungsabbrüche |
Der Wert von get_throttled ist ein Bitfeld. Bit 0 bedeutet aktuelle Unterspannung, Bit 2 aktuelle Drosselung, und die Bits 16 und 18 bedeuten, dass diese Zustände seit dem Booten aufgetreten sind. Ein Wert ungleich null verdient für sich allein einen Alarm, denn er weist fast immer auf ein Netzteilproblem hin, das früher oder später die SD-Karte beschädigt. Die Raspberry-Pi-Dokumentation listet jedes Bit auf.
Topic-Design für eine Flotte
Verwenden Sie einen Teilbaum pro Gerät, mit dem Hostnamen als Geräte-ID. Fügen Sie zum Testen auf dem kostenlosen öffentlichen MQTT-Broker ein zufälliges Segment hinzu, damit sich keine fremden Daten mit Ihren überschneiden:
connectmq/demo/a9e217/fleet/<hostname>/stats JSON, every 30 s, not retained
connectmq/demo/a9e217/fleet/<hostname>/status "online" / "offline", retained, last will
Ein Monitoring-Client abonniert dann connectmq/demo/a9e217/fleet/+/status und connectmq/demo/a9e217/fleet/+/stats und entdeckt neue Pis automatisch, sobald sie zum ersten Mal veröffentlichen. In einer Fabrik bietet es sich an, das Präfix nach der ISA-95-Hierarchie aus Standort, Bereich und Linie aufzubauen, damit die Edge-Geräte ihren Platz im Unified Namespace (UNS) neben den Maschinendaten finden.
Abhängigkeiten installieren
Legen Sie auf jedem Pi eine virtuelle Umgebung an, damit der Dienst nicht von Python-Systempaketen abhängt:
sudo apt update && sudo apt install -y python3-venv
python3 -m venv ~/pistats-env
~/pistats-env/bin/pip install "paho-mqtt>=2.0" psutil
Der Publisher für Raspberry Pi Systemwerte per MQTT
Speichern Sie dies als ~/pistats.py:
#!/usr/bin/env python3
"""Publish Raspberry Pi health metrics over MQTT."""
import json
import os
import socket
import ssl
import subprocess
import time
import paho.mqtt.client as mqtt
import psutil
HOST = os.getenv("MQTT_HOST", "broker.connectmq.com")
PORT = int(os.getenv("MQTT_PORT", "1883"))
USERNAME = os.getenv("MQTT_USERNAME") # YOUR_USERNAME in der Produktion
PASSWORD = os.getenv("MQTT_PASSWORD") # YOUR_PASSWORD in der Produktion
HOSTNAME = socket.gethostname()
CLIENT_ID = os.getenv("MQTT_CLIENT_ID", f"pistats-{HOSTNAME}-a9e217")
PREFIX = os.getenv("MQTT_PREFIX", "connectmq/demo/a9e217/fleet")
INTERVAL = int(os.getenv("INTERVAL", "30"))
STATS_TOPIC = f"{PREFIX}/{HOSTNAME}/stats"
STATUS_TOPIC = f"{PREFIX}/{HOSTNAME}/status"
def cpu_temp():
try:
with open("/sys/class/thermal/thermal_zone0/temp") as f:
return round(int(f.read().strip()) / 1000, 1)
except (OSError, ValueError):
return None
def throttled():
try:
out = subprocess.run(["vcgencmd", "get_throttled"],
capture_output=True, text=True, timeout=2).stdout
return int(out.strip().split("=")[1], 16)
except (OSError, IndexError, ValueError, subprocess.TimeoutExpired):
return None
def wifi_rssi():
try:
with open("/proc/net/wireless") as f:
lines = f.readlines()[2:]
for line in lines:
parts = line.split()
if parts and parts[0].startswith("wlan"):
return int(float(parts[3]))
except (OSError, IndexError, ValueError):
pass
return None
def collect():
load1, load5, _ = os.getloadavg()
t = throttled()
return {
"ts": int(time.time()),
"cpu_temp": cpu_temp(),
"cpu_pct": psutil.cpu_percent(interval=None),
"load1": round(load1, 2),
"load5": round(load5, 2),
"mem_pct": psutil.virtual_memory().percent,
"disk_pct": psutil.disk_usage("/").percent,
"uptime_s": int(time.time() - psutil.boot_time()),
"throttled": t,
"undervoltage_now": bool(t & 0x1) if t is not None else None,
"undervoltage_seen": bool(t & 0x10000) if t is not None else None,
"wifi_rssi": wifi_rssi(),
}
def on_connect(client, userdata, flags, reason_code, properties):
if reason_code.is_failure:
print(f"Connect failed: {reason_code}")
return
print(f"Connected to {HOST}:{PORT} as {CLIENT_ID}")
client.publish(STATUS_TOPIC, "online", qos=1, retain=True)
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, protocol=mqtt.MQTTv5)
if USERNAME:
client.username_pw_set(USERNAME, PASSWORD)
if PORT == 8883:
client.tls_set(cert_reqs=ssl.CERT_REQUIRED)
client.will_set(STATUS_TOPIC, "offline", qos=1, retain=True)
client.on_connect = on_connect
client.on_disconnect = on_disconnect
client.reconnect_delay_set(min_delay=2, max_delay=60)
client.connect_async(HOST, PORT, keepalive=60)
client.loop_start()
psutil.cpu_percent(interval=None) # Zähler initialisieren
try:
while True:
time.sleep(INTERVAL)
if client.is_connected():
client.publish(STATS_TOPIC, json.dumps(collect()), qos=0)
finally:
client.publish(STATUS_TOPIC, "offline", qos=1, retain=True).wait_for_publish(3)
client.disconnect()
client.loop_stop()
Einige bewusste Entscheidungen: Die Werte nutzen QoS 0, weil ein gelegentlich fehlender Messwert keine Rolle spielt und der nächste kurz darauf folgt. Der Status nutzt QoS 1 und Retain, weil die Anwesenheit stimmen muss. connect_async mit loop_start bedeutet, dass das Skript auch dann startet, wenn das Netzwerk beim Booten noch nicht bereit ist; paho versucht es im Hintergrund weiter. Bei einem sauberen Herunterfahren veröffentlicht der finally-Block offline explizit, denn der Last Will greift nur bei einem unerwarteten Verbindungsabbruch.
Führen Sie es einmal von Hand aus und beobachten Sie die Ausgabe von einem anderen Rechner:
~/pistats-env/bin/python ~/pistats.py
mosquitto_sub -h broker.connectmq.com -p 1883 -i watcher-a9e217 \
-t 'connectmq/demo/a9e217/fleet/#' -v
Ziehen Sie das Netzwerkkabel des Pi, und nach etwa dem Anderthalbfachen des Keepalive-Intervalls veröffentlicht der Broker in seinem Namen offline. Das ist der MQTT Last Will bei der Arbeit.
Als systemd-Dienst ausführen
Legen Sie eine Umgebungsdatei an, damit die Zugangsdaten nicht im Skript stehen:
sudo tee /etc/pistats.env >/dev/null <<'EOF'
MQTT_HOST=broker.connectmq.com
MQTT_PORT=1883
INTERVAL=30
EOF
sudo chmod 600 /etc/pistats.env
sudo tee /etc/systemd/system/pistats.service >/dev/null <<'EOF'
[Unit]
Description=Publish Raspberry Pi stats over MQTT
After=network-online.target
Wants=network-online.target
[Service]
User=pi
EnvironmentFile=/etc/pistats.env
ExecStart=/home/pi/pistats-env/bin/python /home/pi/pistats.py
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now pistats
journalctl -u pistats -f
Passen Sie Benutzer und Pfade an Ihr Konto an. Weitere Muster für Skripte beim Systemstart finden Sie unter Python-MQTT-Skript beim Booten starten.
Die Flotte auf einen privaten Broker umziehen
Systemwerte verraten Hostnamen, Betriebszeit und Netzwerkdetails, deshalb sollte eine echte Flotte nicht an einen öffentlichen MQTT-Broker melden. Mit ConnectMQ, einem Cloud-MQTT-Broker, legen Sie im Portal einen Broker an und erhalten Zugangsdaten. Ändern Sie nur die Umgebungsdatei:
MQTT_HOST=broker.connectmq.com
MQTT_PORT=8883
MQTT_USERNAME=YOUR_USERNAME
MQTT_PASSWORD=YOUR_PASSWORD
MQTT_CLIENT_ID=YOUR_CLIENT_ID
MQTT_PREFIX=site1/fleet
ConnectMQ bindet Client IDs an die ausgegebenen Zugangsdaten, daher braucht jeder Pi einen eigenen Satz aus dem Portal. Port 8883 nutzt TLS mit einem Let's-Encrypt-Zertifikat, dem der Pi bereits vertraut. Der kostenlose Serverless-Plan erlaubt bis zu 25 Verbindungen, was für eine ansehnliche Flotte zu Hause oder im Labor reicht; der kostenpflichtige Serverless-Plan geht deutlich weiter.
Das ConnectMQ-Portal hilft auch auf der Monitoring-Seite, ohne zusätzliche Software. Der Live-Topic-Explorer zeigt die neuesten Werte jedes Pi, und Schwellwert-Alarmregeln melden etwa, wenn cpu_temp einen Grenzwert überschreitet, mit einem Alarmprotokoll zur späteren Durchsicht.
Alarmschwellen wählen, die etwas bedeuten
Werte zu sammeln ist einfach; zu entscheiden, welche eine Benachrichtigung verdienen, ist der eigentliche Nutzen der Flottenüberwachung. Ständig auslösende Alarme werden ignoriert, beginnen Sie also mit einer kurzen Liste von Bedingungen, die fast immer auf ein echtes Problem hindeuten:
- Unterspannung seit dem Booten. Jedes
undervoltage_seenungleich null weist auf ein schwaches Netzteil oder ein dünnes USB-Kabel hin. Beheben Sie es, bevor es die SD-Karte beschädigt. - Dauerhaft hohe CPU-Temperatur. Kurze Spitzen während Updates sind normal. Eine Temperatur, die viele Minuten nahe der Drosselungsgrenze bleibt, bedeutet eine verdeckte Lüftung, einen fehlenden Kühlkörper oder ein Gehäuse in direkter Sonne.
- Festplattenbelegung über etwa 90 Prozent. Logs und Container-Images wachsen unbemerkt. Ein volles Root-Dateisystem bricht Paketupdates, Datenbanken und manchmal sogar den Bootvorgang.
- Unerwartet zurückgesetzte Betriebszeit. Fällt
uptime_sohne geplanten Neustart, ist der Pi abgestürzt oder hat Strom verloren; die Ursache ist meist Stromversorgung oder Wärme. - Länger als einige Intervalle offline. Kombinieren Sie den retained Status mit dem Alter der letzten Werte-Nachricht, damit ein kurzer WLAN-Aussetzer niemanden alarmiert.
Load Average und Arbeitsspeicher beobachten Sie besser als Trends statt als Alarme. Ein langsamer Anstieg über Tage verrät meist einen Dienst mit Speicherleck, lange bevor er Probleme verursacht.
Die Daten nutzen
Sobald die Werte fließen, entscheiden Sie, wie viel Werkzeug Sie möchten:
- Schneller Blick: der Topic-Explorer von ConnectMQ oder ein beliebiger MQTT-Desktop-Client.
- Wandanzeige: Das Raspberry Pi Kiosk-Dashboard kann eine Kachel pro Pi anzeigen.
- Verlauf und Diagramme: Telegraf in InfluxDB mit Grafana obendrauf, wie im Tutorial Raspberry Pi mit Grafana und InfluxDB.
Fehlerbehebung
| Symptom | Ursache | Lösung |
|---|---|---|
throttled ist null |
vcgencmd fehlt oder keine Berechtigung |
libraspberrypi-bin installieren, Benutzer zur Gruppe video hinzufügen |
cpu_pct beim ersten Messwert immer 0.0 |
Zähler nicht initialisiert | Das Skript initialisiert ihn vor der Schleife |
| Status wechselt ständig zwischen online und offline | Zwei Pis mit derselben Client ID | Jedem Pi eine eindeutige Client ID geben |
offline erscheint nach dem Abziehen nie |
Keepalive-Timeout läuft noch | Etwa das 1,5-Fache des Keepalive abwarten |
wifi_rssi ist bei Ethernet null |
Keine WLAN-Schnittstelle | Erwartet; ignorieren oder das Feld entfernen |
Starten Sie mit ConnectMQ
Bringen Sie den Zustand aller Pis an einen Ort: Registrieren Sie sich unter app.connectmq.com, legen Sie ohne Kreditkarte einen kostenlosen Serverless-Broker an und tragen Sie die Zugangsdaten in /etc/pistats.env ein. Für die gesamte Reihe der Pi-Projekte beginnen Sie mit dem Raspberry Pi MQTT Tutorial.
Häufig gestellte Fragen
Wie oft sollte ein Raspberry Pi Systemwerte veröffentlichen?
Alle 30 bis 60 Sekunden reicht für die Zustandsüberwachung völlig aus. Kürzere Intervalle erzeugen mehr Verkehr, ohne viel mehr zu verraten, da sich Temperatur und Festplattenbelegung langsam ändern.
Bremst das Veröffentlichen der Werte den Raspberry Pi aus?
Nicht nennenswert. Ein paar Dateien zu lesen und alle 30 Sekunden eine kleine JSON-Nachricht zu senden, ist selbst auf einem Pi Zero 2 W eine vernachlässigbare Last.
Woran erkenne ich, dass ein Pi offline geht?
Abonnieren Sie das retained Status-Topic. Das Skript setzt einen Last Will mit offline, den der Broker automatisch veröffentlicht, wenn der Pi die Verbindung unerwartet verliert.
Kann ich die GPU-Temperatur oder eigene Metriken ergänzen?
Ja, ergänzen Sie Felder im Dictionary von collect(). Bei aktuellen Pi-Modellen teilen sich CPU und GPU einen Chip, der Wert der Thermal Zone spiegelt also bereits beide wider.



