Uygulamalı Eğitimler

Raspberry Pi'de MQTT: 15 Dakikada Sensörden Broker'a

Raspberry Pi'deki BME280 veya DHT22 ölçümlerini Python ve paho-mqtt 2.x ile bir MQTT broker'a gönderin, ardından betiği systemd servisi olarak çalıştırın.

Güncellendi · 8 dk okuma

Raspberry Pi'de MQTT: 15 Dakikada Sensörden Broker'a

Raspberry Pi'deki sensör ölçümlerini MQTT'ye taşımak için üç parça gerekir: bir sensör kütüphanesi, paho-mqtt istemcisi ve betiği ayakta tutan bir servis yöneticisi. Bu eğitimde Pi'ye bir BME280 (veya DHT22) bağlıyor, sıcaklık, nem ve basıncı 10 saniyede bir JSON olarak publish ediyor ve publisher'ı kendi kendine yeniden bağlanan, açılışta başlayan bir systemd servisi olarak çalıştırıyoruz.

Aşağıdaki her şey Raspberry Pi OS Bookworm (veya daha yenisi), Python 3.11+, paho-mqtt 2.x ve Adafruit'in CircuitPython sensör sürücüleri için yazıldı. Pi Zero 2 W, Pi 3, Pi 4 ve Pi 5'te çalışır.

Gerekenler

ParçaNotlar
Ağa bağlı Raspberry PiWi-Fi veya Ethernet, SSH açık
BME280 modülü (I2C)Sıcaklık, nem, basınç. Adres 0x76 veya 0x77
ya da DHT22 / AM2302Tek GPIO pininde sıcaklık ve nem
4 jumper kablo3,3 V, GND, SDA, SCL (BME280)
Bir MQTT brokerErişilebilen herhangi bir broker; örnekler broker.connectmq.com kullanır

BME280'i 3V3 (pin 1), GND (pin 6), SDA (pin 3, GPIO2) ve SCL (pin 5, GPIO3) pinlerine bağlayın. DHT22 ise 3V3, GND ve GPIO4 (pin 7) gibi bir veri pinine bağlanır; modülünüzde yoksa 10 kΩ'luk bir pull-up direnci ekleyin.

Pi'de broker çalıştırmanız gerekmez. Yine de orada bir tane istiyorsanız Raspberry Pi'de broker çalıştırma yazısına bakın; bu makale yalnızca istemci tarafını ele alıyor.

1. I2C'yi açın ve sensörü kontrol edin

I2C'yi açın ve sensörü bulun
sudo raspi-config nonint do_i2c 0
sudo apt update && sudo apt install -y i2c-tools python3-venv
i2cdetect -y 1

i2cdetect bir tablo yazdırır. Orada 76 veya 77 görmelisiniz; bu BME280'in adresidir. Tablo boşsa bir kablolama sorunu vardır (SDA ile SCL'nin yer değiştirmesi klasik hatadır) ya da I2C henüz etkin değildir; gerekirse raspi-config sonrası bir kez yeniden başlatın.

2. Sanal ortam oluşturun

Bookworm, sistem genelinde pip install komutunu error: externally-managed-environment ile reddeder. Bunun yerine bir proje klasöründe venv kullanın:

Proje kurulumu
mkdir -p ~/sensors && cd ~/sensors
python3 -m venv .venv
.venv/bin/pip install paho-mqtt adafruit-circuitpython-bme280
# DHT22 için:
# .venv/bin/pip install paho-mqtt adafruit-circuitpython-dht

Adafruit sürücüleri, Linux'ta board ve busio modüllerini sağlayan uyumluluk katmanı Blinka'yı da kurar.

MQTT koduna geçmeden önce hızlı bir kontrol:

Sensörü bir kez okuyun
.venv/bin/python -c "import board; from adafruit_bme280 import basic as b; s=b.Adafruit_BME280_I2C(board.I2C(), address=0x76); print(s.temperature, s.relative_humidity, s.pressure)"

ValueError: No I2C device at address: 0x76 alırsanız address=0x77 deneyin.

3. Publisher betiği

Betik, Pi düştüğünde subscriber'ların bunu öğrenmesi için bir Last Will ile bağlanır, retained bir online durumu publish eder ve ardından 10 saniyede bir JSON ölçüm gönderir. Kimlik bilgileri ortam değişkenlerinden okunur; böylece aynı dosya hem masanızda hem de üretimde çalışır.

publish.py
import json, os, socket, time
import board
import paho.mqtt.client as mqtt
from adafruit_bme280 import basic as adafruit_bme280

HOST = os.getenv("MQTT_HOST", "broker.connectmq.com")
PORT = int(os.getenv("MQTT_PORT", "8883"))
DEVICE = socket.gethostname()
BASE = f"testmqtt/pi/{DEVICE}"
INTERVAL = 10

sensor = adafruit_bme280.Adafruit_BME280_I2C(board.I2C(), address=0x76)

def on_connect(client, userdata, flags, reason_code, properties):
    if reason_code.is_failure:
        print(f"bağlantı başarısız: {reason_code}", flush=True)
        return
    print("bağlandı", flush=True)
    client.publish(f"{BASE}/status", "online", qos=1, retain=True)

def on_disconnect(client, userdata, flags, reason_code, properties):
    print(f"bağlantı koptu: {reason_code}", flush=True)

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=f"pi-{DEVICE}")
client.username_pw_set(os.getenv("MQTT_USER"), os.getenv("MQTT_PASS"))
if PORT == 8883:
    client.tls_set()                      # sistemin CA deposu
client.will_set(f"{BASE}/status", "offline", qos=1, retain=True)
client.reconnect_delay_set(min_delay=1, max_delay=60)
client.on_connect = on_connect
client.on_disconnect = on_disconnect

client.connect_async(HOST, PORT, keepalive=60)
client.loop_start()                       # ağ thread'i, bağlanana kadar dener

while True:
    reading = {
        "temperature": round(sensor.temperature, 2),
        "humidity": round(sensor.relative_humidity, 1),
        "pressure": round(sensor.pressure, 1),
        "ts": int(time.time()),
    }
    info = client.publish(f"{BASE}/bme280", json.dumps(reading), qos=1)
    print(f"publish mid={info.mid} rc={info.rc} {reading}", flush=True)
    time.sleep(INTERVAL)

Açıklamaya değer birkaç tercih:

  • connect_async() + loop_start(): açılışta ağ henüz hazır olmayabilir. connect() hemen hata fırlatırdı; connect_async() ise paho'nun arka plan thread'inin denemeye devam etmesini sağlar ve aynı thread sonraki her kopmadan sonra reconnect_delay_set() ile belirlenen backoff ile yeniden bağlanır.
  • Ölçümler için QoS 1: kısa bir kesinti verileri sessizce kaybettirmesin diye. paho, bağlantı yokken QoS 1 mesajlarını kuyruğa alır ve bağlantı geri gelince gönderir. QoS 0 daha hafif olurdu ama kesinti sırasında gönderilen her şeyi düşürür.
  • Retained durum + Last Will: sonradan abone olan herkes hemen online ya da offline görür. Bu kalıp MQTT Last Will yazısında anlatılıyor.
  • Hostname'den türetilen client ID: her Pi için kalıcı ve benzersizdir. Aynı ID'yi paylaşan iki cihaz birbirini broker'dan döngü halinde atar.

Bunun yerine DHT22 kullanmak

DHT22 protokolü Linux altında zamanlamaya duyarlıdır, bu yüzden okumalar düzenli olarak RuntimeError ile başarısız olur (örneğin "Checksum did not validate" veya "A full buffer was not returned"). Bu normaldir; örneği atlayın ve tekrar deneyin:

dht22 okuması (sensör kısmının yerine)
import adafruit_dht
dht = adafruit_dht.DHT22(board.D4)

def read_dht():
    for _ in range(5):
        try:
            return {"temperature": dht.temperature, "humidity": dht.humidity}
        except RuntimeError:
            time.sleep(2)              # DHT22 okumalar arasında ~2 sn ister
    return None                        # bu turda hiçbir şey publish edilmez

Ana döngüde read_dht() çağırın ve yalnızca bir değer döndüğünde publish edin.

4. Elle test edin

Kimlik bilgilerini yalnızca kullanıcınızın okuyabileceği bir dosyaya koyun ve betiği ön planda çalıştırın:

~/sensors/mqtt.env
MQTT_HOST=broker.connectmq.com
MQTT_PORT=8883
MQTT_USER=username
MQTT_PASS=password
Bir kez çalıştırın
chmod 600 ~/sensors/mqtt.env
set -a; . ~/sensors/mqtt.env; set +a
.venv/bin/python publish.py

Önce bağlandı, ardından her 10 saniyede bir publish mid=... satırı görmelisiniz. Şimdi başka bir makineden izleyin:

Dizüstü bilgisayarınızdan abone olun
mosquitto_sub -h broker.connectmq.com -p 8883 --capath /etc/ssl/certs \
  -u "$MQTT_USER" -P "$MQTT_PASS" -t 'testmqtt/pi/#' -v

Ya da kurulumla uğraşmadan tarayıcınızda online MQTT istemcisini açın, aynı broker'a bağlanın ve testmqtt/pi/# topic'ine abone olun. Önce retained status mesajını, sonra her aralıkta yeni bir bme280 JSON payload'ını göreceksiniz.

Pi'nizin ölçümlerini tarayıcıda izleyin

Hiçbir şey gelmiyorsa yazdırılan reason code'a bakın. Bad user name or password ve Not authorized broker tarafındaki retlerdir; bunları MQTT reason code aracında arayın. Zaman aşımı genellikle portun engellendiği anlamına gelir; bağlantı hataları rehberi her durumu tek tek ele alıyor.

5. systemd servisi olarak çalıştırın

SSH oturumunda başlatılan bir betik, oturumu kapattığınızda ölür. systemd onu açılışta başlatır, çökerse yeniden başlatır ve çıktısını journal'da toplar.

/etc/systemd/system/bme280-mqtt.service
[Unit]
Description=BME280 to MQTT publisher
Wants=network-online.target
After=network-online.target

[Service]
User=pi
WorkingDirectory=/home/pi/sensors
EnvironmentFile=/home/pi/sensors/mqtt.env
ExecStart=/home/pi/sensors/.venv/bin/python -u /home/pi/sensors/publish.py
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

pi yerine gerçek kullanıcı adınızı yazın; güncel Raspberry Pi OS imajları kurulum sırasında bir kullanıcı adı seçtirir. Kullanıcının i2c grubunda (DHT22 için gpio) olması gerekir; varsayılan kullanıcı zaten bu gruplardadır.

Etkinleştirin ve logları izleyin
sudo systemctl daemon-reload
sudo systemctl enable --now bme280-mqtt
journalctl -u bme280-mqtt -f

Python komut satırındaki -u, çıktı tamponlamayı kapatır ve log satırlarının journal'da hemen görünmesini sağlar (betikteki flush=True da aynı işi yapar).

Arızalara dayandığını kanıtlayın

  1. sudo reboot: ölçümler, açık bir SSH oturumu olmadan bir dakika içinde geri gelmelidir.
  2. Ağ bağlantısını bir dakikalığına kesin. Broker eksik keep-alive'ı fark ettiğinde (en fazla 1,5 × 60 saniye) subscriber'lar offline görür, yeniden bağlanınca tekrar online ve ardından kesinti sırasında kuyruğa alınan ölçümler gelir.
  3. sudo systemctl kill bme280-mqtt: systemd betiği 5 saniye sonra yeniden başlatır.

Sonraki adımlar

  • Payload'ı dashboard'lar ve veritabanları için biçimlendirin: Python'da JSON payload'lar.
  • Daha fazla sensör ve bir ekran ekleyin: MQTT hava istasyonu bu betiğin üzerine kurulur.
  • Discovery mesajları publish ederek Home Assistant'ı besleyin; ayrıntılar Home Assistant MQTT yazısında.

Sıkıcı kısımlar halledildiği sürece bir Pi, bir sensör ve kırk satır Python güvenilir telemetri için yeterlidir: bağımlılıklar için bir venv, kalıcı bir client ID, varlık bilgisi için bir Last Will ve yeniden başlatmalar için systemd. Her arızayı masanızda bir kez test edin; bahçedeki Pi de aynı şekilde davranacaktır.

Sıkça sorulan sorular

Raspberry Pi'nin üzerinde bir MQTT broker çalıştırmam gerekir mi?

Hayır. Pi'nin yalnızca paho-mqtt gibi bir istemci kütüphanesine ihtiyacı vardır. Erişebildiği herhangi bir broker'a publish edebilir: aynı Pi'ye kurulu bir broker, yerel ağınızdaki başka bir makine ya da bulutta yönetilen bir broker.

Raspberry Pi OS'ta pip neden "externally-managed-environment" hatası veriyor?

Raspberry Pi OS Bookworm ve sonrası sistem Python'unu korur (PEP 668). python3 -m venv ile bir sanal ortam oluşturun, paho-mqtt ve sensör kütüphanelerini oraya kurun ve systemd servisinizi venv içindeki python'a yönlendirin.

MQTT için BME280 mi, DHT22 mi kullanmalıyım?

BME280 I2C üzerinden konuşur, her seferinde güvenilir okuma yapar ve hava basıncını da ölçer. DHT22 ucuzdur ama zamanlamaya duyarlı tek telli bir protokol kullanır; bu yüzden okumalar düzenli olarak başarısız olur ve kodunuzun tekrar denemesi gerekir. Uzun süreli telemetri için BME280 daha zahmetsizdir.