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ça | Notlar |
|---|---|
| Ağa bağlı Raspberry Pi | Wi-Fi veya Ethernet, SSH açık |
| BME280 modülü (I2C) | Sıcaklık, nem, basınç. Adres 0x76 veya 0x77 |
| ya da DHT22 / AM2302 | Tek GPIO pininde sıcaklık ve nem |
| 4 jumper kablo | 3,3 V, GND, SDA, SCL (BME280) |
| Bir MQTT broker | Eriş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
sudo raspi-config nonint do_i2c 0
sudo apt update && sudo apt install -y i2c-tools python3-venv
i2cdetect -y 1i2cdetect 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:
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-dhtAdafruit 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:
.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.
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 sonrareconnect_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
onlineya daofflinegö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:
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 edilmezAna 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:
MQTT_HOST=broker.connectmq.com
MQTT_PORT=8883
MQTT_USER=username
MQTT_PASS=passwordchmod 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:
mosquitto_sub -h broker.connectmq.com -p 8883 --capath /etc/ssl/certs \
-u "$MQTT_USER" -P "$MQTT_PASS" -t 'testmqtt/pi/#' -vYa 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.
[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.targetpi 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.
sudo systemctl daemon-reload
sudo systemctl enable --now bme280-mqtt
journalctl -u bme280-mqtt -fPython 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
sudo reboot: ölçümler, açık bir SSH oturumu olmadan bir dakika içinde geri gelmelidir.- Ağ bağlantısını bir dakikalığına kesin. Broker eksik keep-alive'ı fark ettiğinde (en fazla 1,5 × 60 saniye) subscriber'lar
offlinegörür, yeniden bağlanınca tekraronlineve ardından kesinti sırasında kuyruğa alınan ölçümler gelir. 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.

