IoT Eğitimleri

Saf Python ile Streamlit MQTT Dashboard: 100 Satırda Canlı Sensör Grafikleri

broker.connectmq.com'a bağlı, arka planda çalışan bir paho-mqtt abonesiyle beslenen canlı bir Streamlit panosu oluşturun; iş parçacığı güvenli bir tampon, otomatik yenilenen grafikler ve onu besleyecek bir simülatörle birlikte.

IoT sensörlerinden canlı sıcaklık ve nem çizgi grafiklerini gösteren streamlit mqtt dashboard
Bunu kendi MQTT broker’ınızda çalıştırınFree planla özel, yönetilen bir MQTT broker alın ve hazır kod örnekleriyle başlayın.
Ücretsiz deneyin Kod örnekleri Kart gerekmez

Bir Streamlit MQTT dashboard, tek satır HTML veya JavaScript yazmadan canlı sensör mesajlarını grafiklere ve metriklere dönüştürmenizi sağlar. Bu eğitimde saf Python ile bir tane oluşturacaksınız: broker.connectmq.com adresine bağlı arka planda çalışan bir paho-mqtt abonesi, iş parçacığı açısından güvenli bir tampon ve kendini her iki saniyede bir yenileyen bir sayfa. Panonun ilk dakikadan itibaren verisi olması için küçük bir simülatör de geliştireceksiniz.

Streamlit ve MQTT Neden Biraz Yapıştırıcıya İhtiyaç Duyar?

Streamlit'in alışılmadık bir çalışma modeli vardır. Kullanıcı sayfayla her etkileşime girdiğinde veya bir yenileme tetiklendiğinde Streamlit tüm betiğinizi baştan sona yeniden çalıştırır. Bu basit veri uygulamaları için harikadır, ancak MQTT client'ları uzun ömürlüdür: bir TCP bağlantısı tutar, bir ağ döngüsü çalıştırır ve her mesaj geldiğinde callback'leri tetiklerler.

Betiğin en üstünde safça bir MQTT client'ı oluşturursanız her yeniden çalıştırma başka bir bağlantı oluşturur. Bir dakika içinde düzinelerce client'ınız, yinelenen mesajlarınız ve public broker'da client ID'lerin yeniden kullanılması nedeniyle düşürülen bağlantılarınız olur. Çözüm, client'ı sunucu süreci başına tam olarak bir kez oluşturmak ve Streamlit'in her yeniden çalıştırmada paylaşılan bir tampondan okumasını sağlamaktır. Streamlit bunun için doğru aracı sunar: st.cache_resource.

Mimari şöyle görünür:

Bileşen Nerede çalışır Sorumluluk
paho-mqtt client Arka plandaki ağ iş parçacığı (loop_start) Bağlantıyı sürdürür, mesajları alır
deque tampon + kilit Paylaşılan bellek, bir kez oluşturulur En son N ölçümü tutar
Streamlit betiği Her yenilemede yeniden çalışır Tamponu kopyalar, bir DataFrame oluşturur, grafikleri çizer
st.fragment(run_every=...) Streamlit zamanlayıcısı Birkaç saniyede bir yalnızca grafik bölümünü yeniden çalıştırır

Ön Koşullar

Python 3.9 veya üzeri ve üç pakete ihtiyacınız var:

python -m venv .venv
source .venv/bin/activate        # Windows: .venv\Scripts\activate
pip install "streamlit>=1.37" "paho-mqtt>=2.0" pandas

Streamlit 1.37, bu eğitimde otomatik yenileme için kullanılan run_every parametreli st.fragment özelliğini getirdi. paho-mqtt 2.x, aşağıda gösterilen açık callback API sürümünü gerektirir; eski koddan geliyorsanız önce paho-mqtt 2.0 callback API rehberini okuyun.

Verilerinizin public MQTT test broker üzerinde başkalarınınkiyle karışmaması için rastgele bir topic öneki seçin. Bu eğitim connectmq/demo/k7f3q2 kullanır; her iki dosyada da k7f3q2 kısmını kendi rastgele dizenizle değiştirin.

Adım 1: Bir Sensör Simülatörü

Panoyu oluşturmadan önce veri yayınlayan bir şey hazırlayın. Bunu simulator.py olarak kaydedin:

import json
import math
import random
import time
import uuid

import paho.mqtt.client as mqtt

BROKER = "broker.connectmq.com"
PREFIX = "connectmq/demo/k7f3q2"
SENSORS = ["line1", "line2", "line3"]

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=f"sim-{uuid.uuid4().hex[:8]}")
client.connect(BROKER, 1883, keepalive=60)
client.loop_start()

t = 0
try:
    while True:
        for sensor in SENSORS:
            reading = {
                "sensor": sensor,
                "temperature": round(22 + 3 * math.sin(t / 20) + random.uniform(-0.5, 0.5), 2),
                "humidity": round(45 + 5 * math.cos(t / 30) + random.uniform(-1, 1), 2),
                "ts": time.time(),
            }
            client.publish(f"{PREFIX}/sensors/{sensor}", json.dumps(reading), qos=0)
        t += 1
        time.sleep(1)
except KeyboardInterrupt:
    pass
finally:
    client.loop_stop()
    client.disconnect()

Saniyede üç mesaj yayınlar; bu, public broker'ın client başına saniyede 10 mesaj sınırının oldukça altındadır. Kendi terminalinde python simulator.py ile çalıştırın ve açık bırakın.

Adım 2: Streamlit MQTT Dashboard

Bunu dashboard.py olarak kaydedin:

import json
import threading
import time
import uuid
from collections import deque

import pandas as pd
import paho.mqtt.client as mqtt
import streamlit as st

BROKER = "broker.connectmq.com"
PORT = 1883
PREFIX = "connectmq/demo/k7f3q2"
TOPIC = f"{PREFIX}/sensors/+"
MAX_POINTS = 600


@st.cache_resource
def start_mqtt():
    """Create one MQTT client per Streamlit server process."""
    state = {
        "buffer": deque(maxlen=MAX_POINTS),
        "lock": threading.Lock(),
        "connected": False,
        "received": 0,
    }

    def on_connect(client, userdata, flags, reason_code, properties):
        if reason_code.is_failure:
            return
        state["connected"] = True
        client.subscribe(TOPIC, qos=0)

    def on_disconnect(client, userdata, disconnect_flags, reason_code, properties):
        state["connected"] = False

    def on_message(client, userdata, msg):
        try:
            data = json.loads(msg.payload)
            row = {
                "time": pd.to_datetime(data.get("ts", time.time()), unit="s"),
                "sensor": data.get("sensor", msg.topic.rsplit("/", 1)[-1]),
                "temperature": float(data["temperature"]),
                "humidity": float(data["humidity"]),
            }
        except (ValueError, KeyError, TypeError):
            return  # bozuk payload'ları yok say
        with state["lock"]:
            state["buffer"].append(row)
            state["received"] += 1

    client = mqtt.Client(
        mqtt.CallbackAPIVersion.VERSION2,
        client_id=f"streamlit-{uuid.uuid4().hex[:8]}",
    )
    # Private bir ConnectMQ broker için:
    # client.username_pw_set("YOUR_USERNAME", "YOUR_PASSWORD")
    # client.tls_set()  ve 8883 portunu kullanın
    client.on_connect = on_connect
    client.on_disconnect = on_disconnect
    client.on_message = on_message
    client.reconnect_delay_set(min_delay=1, max_delay=30)
    client.connect_async(BROKER, PORT, keepalive=60)
    client.loop_start()
    return state


st.set_page_config(page_title="MQTT Sensor Dashboard", layout="wide")
st.title("Live MQTT Sensor Dashboard")
st.caption(f"Subscribed to `{TOPIC}` on {BROKER}")

state = start_mqtt()


@st.fragment(run_every=2)
def live_view():
    with state["lock"]:
        rows = list(state["buffer"])
        received = state["received"]

    status = "Connected" if state["connected"] else "Connecting..."
    c1, c2, c3 = st.columns(3)
    c1.metric("Broker status", status)
    c2.metric("Messages received", received)
    c3.metric("Points in buffer", len(rows))

    if not rows:
        st.info("Waiting for data. Is simulator.py running with the same prefix?")
        return

    df = pd.DataFrame(rows)
    latest = df.groupby("sensor").last()

    cols = st.columns(len(latest))
    for col, (sensor, r) in zip(cols, latest.iterrows()):
        col.metric(f"{sensor} temperature", f"{r['temperature']:.1f} °C")

    temp = df.pivot_table(index="time", columns="sensor", values="temperature")
    hum = df.pivot_table(index="time", columns="sensor", values="humidity")

    left, right = st.columns(2)
    left.subheader("Temperature")
    left.line_chart(temp)
    right.subheader("Humidity")
    right.line_chart(hum)


live_view()

Şununla başlatın:

streamlit run dashboard.py

Streamlit http://localhost:8501 adresini açar. Birkaç saniye içinde durum Connected olarak görünür, sayaçlar artmaya başlar ve her grafikte üç çizgi belirir.

Pano Nasıl Çalışır?

Tek client, çok sayıda yeniden çalıştırma

st.cache_resource, start_mqtt() fonksiyonunu ilk seferde çalıştırır ve sonraki her çağrıda, yeniden çalıştırmalar ve aynı sunucudaki tarayıcı oturumları boyunca aynı nesneyi döndürür. Bu, bir MQTT bağlantısının ihtiyaç duyduğu ömrün ta kendisidir. Önbelleğe alınan nesne tüm izleyiciler tarafından paylaşıldığından pano, kaç kişi bakarsa baksın broker'a tek bir bağlantı açar; bu da bağlantı sınırı olan planlarda önemlidir.

İş parçacığı güvenliği

paho'nun loop_start() metodu ağ döngüsünü arka plandaki bir iş parçacığında çalıştırır; bu yüzden on_message, Streamlit betiğinizden farklı bir iş parçacığında çalışır. CPython'da bir deque yapısına eleman eklemek atomiktir, ancak başka bir iş parçacığı eleman eklerken onu kopyalamak "deque mutated during iteration" hatasına yol açabilir. Kilit, kopyalama ile eklemeyi karşılıklı olarak dışlar ve yalnızca mikrosaniyeler boyunca tutulur; böylece ağ iş parçacığını asla yavaşlatmaz.

Deque üzerindeki maxlen bellek koruyucunuzdur. 600 noktayla pano üç sensörün yaklaşık son 200 saniyesini gösterir ve ne kadar uzun çalışırsa çalışsın bellek sabit kalır.

Fragment'larla kısmi yeniden çalıştırma

@st.fragment(run_every=2), Streamlit'e tüm betik yerine yalnızca bu fonksiyonu her iki saniyede bir yeniden çalıştırmasını söyler. Başlık ve açıklama yerinde kalır, grafikler güncellenir ve sayfa titremez. Eski Streamlit sürümlerinde üçüncü taraf bir otomatik yenileme bileşenine veya bir time.sleep() ile st.rerun() döngüsüne ihtiyacınız olurdu; ikisi de daha hantaldır.

Yeniden bağlanma

connect_async() ile loop_start() birlikte, broker kısa süreliğine erişilemez olsa bile panonun başlaması anlamına gelir; paho, reconnect_delay_set ile belirlenen gecikmelerle arka planda yeniden dener. on_connect içinde abone olmak önemlidir: clean session ile yeniden bağlanıldığında abonelikler kaybolur ve geri yüklendikleri yer on_connect olur. paho-mqtt yeniden bağlanma rehberi bu modeli daha ayrıntılı ele alır.

Yaygın Sorunlar ve Çözümleri

  1. Yinelenen veya iki kat gelen mesajlar. Client'ı st.cache_resource dışında oluşturuyorsunuz ya da start_mqtt fonksiyonunu düzenlediniz ve eski iş parçacığı çalışmaya devam ederken Streamlit yeni bir önbelleğe alınmış client oluşturdu. MQTT kurulumunu değiştirdikten sonra streamlit run sürecini yeniden başlatın.
  2. Pano sonsuza dek "Connecting..." diyor. Ağınızda 1883 portu engellenmiş olabilir. client.tls_set() ile 8883'te TLS'e geçin veya mosquitto_sub -h broker.connectmq.com -p 1883 -i test-k7f3q2 -t 'connectmq/demo/k7f3q2/#' -v ile test edin.
  3. Bağlı ama veri yok. dashboard.py ve simulator.py içindeki önek, büyük/küçük harf dahil tam olarak eşleşmelidir.
  4. Grafikler zıplıyor. Zaman damgaları publisher'dan gelir. Cihazların saatleri senkronize değilse bunun yerine alım zamanını kullanın (on_message içinde time.time()).
  5. Çok sensörle sayfa yavaşlıyor. Her iki saniyede binlerce satırı pivot etmek birikir. MAX_POINTS değerini düşürün, run_every değerini artırın veya grafiğe dökmeden önce df.resample() ile örnek sayısını azaltın.

Üretime Taşımak

Public broker deneyler için idealdir, ancak public alanındaki her şey herkes tarafından okunabilir. Gerçek tesis veya bina verilerini gösteren bir pano için:

  • ConnectMQ üzerinde private bir broker oluşturun ve üretilen kimlik bilgilerini 8883 portunda TLS ile kullanın. Python MQTT TLS rehberi tam tls_set() yapılandırmasını gösterir.
  • Gizli bilgileri koda gömmek yerine .streamlit/secrets.toml içinde tutun ve st.secrets["mqtt"]["username"] ile okuyun.
  • Bellek içi tamponun ötesinde geçmişe ihtiyacınız varsa bir veritabanına yazan ayrı bir abone çalıştırın ve panonun onu sorgulamasını sağlayın. Python ile MQTT'den SQLite'a eğitimi hafif bir başlangıç noktasıdır, MQTT ve pandas rehberi ise analizde yardımcı olur.

Streamlit bir Python aracıdır, ancak tarayıcı panolarıyla aynı ailede yer alır. Saf bir frontend yaklaşımını tercih ediyorsanız tarayıcıda JavaScript ile MQTT client'ı doğrudan sayfadan WebSocket üzerinden bağlanır; MQTT client kütüphanelerine genel bakış ise her dildeki başlıca seçenekleri karşılaştırır. Streamlit dokümantasyonu fragment'ları, önbelleğe almayı ve dağıtımı ayrıntılı olarak ele alır.

Streamlit Panolarınızı ConnectMQ Üzerinde Çalıştırın

ConnectMQ tamamen yönetilen bir MQTT broker'dır: 8883'te TLS, üretilmiş kimlik bilgileri ve sensörlerinizin ne yayınladığını grafiğe dökmeden önce kontrol etmeniz için canlı topic gezgini ile tarayıcı içi client sunan bir portal elde edersiniz. broker.connectmq.com ile başlayın, ardından kendi bulut MQTT broker'ınıza geçin. Kredi kartı gerekmeden ücretsiz Serverless planına kaydolun.

Sık Sorulan Sorular

Streamlit doğrudan MQTT'ye abone olabilir mi?

Streamlit'in yerleşik MQTT desteği yoktur, ancak herhangi bir Python MQTT client'ı çalışır. Güvenilir model, st.cache_resource ile bir kez oluşturulan ve ağ döngüsünü arka plandaki bir iş parçacığında çalıştıran bir paho-mqtt client'ıdır.

Pano ne sıklıkla yenilenmeli?

Bir ila beş saniye çoğu sensör panosu için uygundur. Daha hızlı yenilemeler, yavaş değişen değerleri daha kullanışlı hale getirmeden Streamlit sunucusunda CPU tüketir.

Neden her mesajı birkaç kez alıyorum?

Birden fazla MQTT client'ı çalışıyor; genellikle client st.cache_resource dışında oluşturulduğu için veya eski bir önbelleğe alınmış client bir kod değişikliğinden sonra hayatta kaldığı için. Yalnızca bir client olması için Streamlit sürecini yeniden başlatın.

Birçok kullanıcı aynı Streamlit MQTT panosunu görüntüleyebilir mi?

Evet. Önbelleğe alınmış client sunucudaki tüm oturumlar tarafından paylaşılır; bu yüzden on izleyici bile tek bir broker bağlantısı kullanır ve aynı tampondan okur.

İlgili yazılar

Test, Betik ve Hata Ayıklama için En İyi MQTT CLI Araçları

mosquitto_pub/sub, MQTTX CLI, HiveMQ MQTT CLI, MQTT.js CLI, curl ve mqttui'nin uygulamalı karşılaştırması; betik yazma ve hata ayıklama için doğru aracı seçebilmeniz için aynı test broker.connectmq.com üzerinde çalıştırıldı.

7 dk okuma

Tüm yazılar →

Özel MQTT broker’ınız birkaç tık uzağınızda

Hesabınızı oluşturun, broker kimlik bilgilerinizi alın ve ilk cihazınızı bağlayın — kurulacak, yamalanacak ya da ölçeklendirilecek sunucu yok.