HC-SR04 ile bir Raspberry Pi mesafe sensörü MQTT projesi, tek bir ucuz parçayla iki yaygın sorunu çözer: bir tank ne kadar dolu ve orada bir şey (ya da biri) var mı? Bu eğitimde ultrasonik sensörü güvenli şekilde bağlayacak, gpiozero ile Python'da kararlı ölçümler alacak ve bir panonun veya otomasyonun bunlara göre hareket edebilmesi için hem doluluk yüzdesini hem de varlık olaylarını MQTT üzerinden yayınlayacaksınız.
HC-SR04 Mesafeyi Nasıl Ölçer?
HC-SR04, TRIG pini 10 mikrosaniyelik bir darbe aldığında kısa bir 40 kHz ultrason patlaması gönderir, ardından yansıma geri dönene kadar ECHO pinini yüksekte tutar. Bu yankı darbesinin süresi ses hızıyla çarpılıp ikiye bölündüğünde mesafeyi verir. Sensör birkaç santimetreden birkaç metreye kadar çalışır; en iyi sonuçları bir su yüzeyi veya duvar gibi dik açıdaki düz, sert yüzeylere karşı verir.
Gerçek projeler için iki sonuç önemlidir. Birincisi, Pi yankı darbesini yazılımla zamanlamak zorundadır; bu yüzden Linux zamanlaması bir miktar titreşim (jitter) ekler, bunu filtreleyeceksiniz. İkincisi, ses hızı hava sıcaklığıyla değişir; bu nedenle soğuk bir sabah ile sıcak bir öğleden sonra arasında küçük bir kayma bekleyin. Bir tank seviyesi veya varlık dedektörü için her iki etki de yönetilebilir.
Gerilim Bölücü ile Kablolama
HC-SR04 5 V ile çalışır ve ECHO çıkışı da 5 V'tur; bu da bir Raspberry Pi GPIO pinine zarar verebilir. ECHO üzerinde her zaman bir gerilim bölücü kullanın:
| HC-SR04 pini | Bağlantı |
|---|---|
| VCC | Pi 5 V (fiziksel pin 2) |
| GND | Pi GND (fiziksel pin 6) |
| TRIG | GPIO23 (fiziksel pin 16), doğrudan |
| ECHO | GPIO24'e (fiziksel pin 18) 1 k direnç; GPIO24'ten GND'ye 2 k direnç |
1 k ve 2 k çifti 5 V'u yaklaşık 3,3 V'a böler. 1 k ile 1,8 k veya 2,2 k kombinasyonu da çalışır. TRIG doğrudan bağlanabilir, çünkü Pi'nin 3,3 V çıkışı sensörü tetiklemek için yeterlidir. Modülün 3,3 V uyumlu sürümünü satın alırsanız dokümantasyonunu kontrol edin; bölücüye ihtiyacınız olmayabilir.
Yazılımın Kurulumu
sudo apt update
sudo apt install -y python3-venv python3-lgpio
mkdir -p ~/distance-mqtt && cd ~/distance-mqtt
python3 -m venv --system-site-packages .venv
source .venv/bin/activate
pip install "paho-mqtt>=2.0" gpiozero
MQTT olmadan hızlı test:
python -c "from gpiozero import DistanceSensor; from time import sleep; s = DistanceSensor(echo=24, trigger=23, max_distance=4); sleep(1); print(round(s.distance * 100, 1), 'cm')"
Sensörü bir duvara doğrultun ve sonucu bir şerit metreyle karşılaştırın. Her zaman tam olarak max_distance değerini veren bir ölçüm, yankı algılanmadığı anlamına gelir; bu genellikle ECHO üzerindeki bir kablolama sorunudur.
İki Mod: Seviye ve Varlık
Aynı ölçüm iki şekilde yorumlanabilir ve betik ikisini de yayınlar:
| Mod | Hesaplama | Topic | Yayın zamanı | Retained |
|---|---|---|---|---|
| Tank seviyesi | (empty_distance - distance) / (empty_distance - full_distance) |
.../level |
Her 30 saniyede | Evet |
| Varlık | Histerezisli bir eşiğin altındaki mesafe | .../presence |
Yalnızca değişiklikte | Evet |
| Ham | cm cinsinden medyan mesafe | her iki payload'ın içinde |
Her iki topic'i retained tutmak, herhangi bir zamanda açılan bir panonun güncel seviyeyi ve birinin orada olup olmadığını hemen göstermesi anlamına gelir. Varlığı yalnızca değişiklikte yayınlamak trafiği çok küçük tutar ve otomasyonları basitleştirir: abone olun ve her mesaja tepki verin.
Tankı Kalibre Etmek
Sensörü tankın tepesine, dimdik aşağı bakacak şekilde, giriş borusundan ve duvarlardan uzak monte edin. Ardından iki mesafe ölçün:
EMPTY_CM: sensörden tabana (veya kullanılabilir en düşük seviyeye) olan mesafe.FULL_CM: sensörden dolu işaretindeki suya olan mesafe. Sensör yüzeyi ile en yüksek su seviyesi arasında en az sensörün birkaç santimetrelik minimum menzili kadar boşluk bırakın.
Her iki değeri de ortam değişkenlerinde ayarlayın. Betik, sıçramaların veya boş bir tankın imkansız değerler üretmemesi için sonucu yüzde 0 ile 100 arasında sınırlar.
Raspberry Pi Mesafe Sensörü MQTT Betiği
distance_mqtt.py olarak kaydedin:
#!/usr/bin/env python3
"""HC-SR04 tank level and presence detection over MQTT."""
import json
import os
import statistics
import time
import uuid
import paho.mqtt.client as mqtt
from gpiozero import DistanceSensor
BROKER = os.getenv("MQTT_HOST", "broker.connectmq.com")
PORT = int(os.getenv("MQTT_PORT", "1883"))
USERNAME = os.getenv("MQTT_USERNAME")
PASSWORD = os.getenv("MQTT_PASSWORD")
CLIENT_ID = os.getenv("MQTT_CLIENT_ID", f"distance-{uuid.uuid4().hex[:8]}")
PREFIX = os.getenv("MQTT_PREFIX", "connectmq/demo/9c3d55/tank1")
EMPTY_CM = float(os.getenv("EMPTY_CM", "120"))
FULL_CM = float(os.getenv("FULL_CM", "20"))
PRESENCE_ON_CM = float(os.getenv("PRESENCE_ON_CM", "60"))
PRESENCE_OFF_CM = float(os.getenv("PRESENCE_OFF_CM", "80"))
LEVEL_INTERVAL = 30
SAMPLES = 7
sensor = DistanceSensor(echo=24, trigger=23, max_distance=4, queue_len=5)
def measure_cm():
"""Median of several readings, ignoring out-of-range values."""
values = []
for _ in range(SAMPLES):
d = sensor.distance * 100
if 2 < d < 399: # 400 cm means no echo within max_distance
values.append(d)
time.sleep(0.06) # let echoes die down between pings
return statistics.median(values) if values else None
def on_connect(client, userdata, flags, reason_code, properties):
if reason_code.is_failure:
print(f"Connection refused: {reason_code}")
return
print(f"Connected as {CLIENT_ID}")
client.publish(f"{PREFIX}/status", "online", qos=1, retain=True)
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=CLIENT_ID)
client.on_connect = on_connect
client.will_set(f"{PREFIX}/status", "offline", qos=1, retain=True)
client.reconnect_delay_set(min_delay=1, max_delay=60)
if USERNAME:
client.username_pw_set(USERNAME, PASSWORD)
if PORT == 8883:
client.tls_set()
client.connect(BROKER, PORT, keepalive=30)
client.loop_start()
present = None
last_level = 0.0
try:
while True:
distance = measure_cm()
now = time.time()
if distance is None:
print("No valid echo")
time.sleep(1)
continue
# Presence with hysteresis: switch on below ON, off above OFF.
new_present = present
if distance < PRESENCE_ON_CM:
new_present = True
elif distance > PRESENCE_OFF_CM:
new_present = False
if new_present is not None and new_present != present:
present = new_present
payload = {"present": present, "distance_cm": round(distance, 1), "ts": int(now)}
client.publish(f"{PREFIX}/presence", json.dumps(payload), qos=1, retain=True)
print("presence", payload)
if now - last_level >= LEVEL_INTERVAL:
span = EMPTY_CM - FULL_CM
percent = max(0.0, min(100.0, (EMPTY_CM - distance) / span * 100))
payload = {"level_pct": round(percent, 1), "distance_cm": round(distance, 1), "ts": int(now)}
client.publish(f"{PREFIX}/level", json.dumps(payload), qos=1, retain=True)
print("level", payload)
last_level = now
time.sleep(0.5)
except KeyboardInterrupt:
pass
finally:
client.publish(f"{PREFIX}/status", "offline", qos=1, retain=True).wait_for_publish(2)
client.loop_stop()
client.disconnect()
sensor.close()
Gerçek bir kurulumda genellikle sensör başına tek bir mod kullanırsınız: bir tank sensörünün varlık olaylarına, bir kapı girişi sensörünün de doluluk seviyesine ihtiyacı yoktur. İkisini tek bir betikte tutmak deneme yapmayı kolaylaştırır; ihtiyacınız olmayan bloğu silebilirsiniz.
Medyan Filtreleme ve Histerezis Neden Önemli?
Ultrasonik ölçümler zaman zaman sıçrar: tank duvarından gelen başıboş bir yankı, sudaki bir dalgalanma veya Linux'taki bir zamanlama aksaklığı. Ortalama almak tek bir kötü değerin sonucu çekmesine izin verir; yedi ölçümün medyanı ise onu basitçe yok sayar. Histerezis farklı bir sorunu çözer. Histerezis olmadan tam eşikte duran bir kişi varlık durumunu saniyede birkaç kez açıp kapatır ve aboneleri olaylara boğar. Ayrı açma ve kapama mesafeleriyle durum bir kez değişir ve öyle kalır.
Çıktıyı Test Etmek
mosquitto_sub -h broker.connectmq.com -p 1883 -i watch-9c3d55 \
-t 'connectmq/demo/9c3d55/tank1/#' -v
Elinizi sensöre doğru hareket ettirin: 60 cm'yi geçtiğinizde "present": true içeren bir presence mesajı, 80 cm'nin ötesine geçtiğinizde de false mesajı görünür. Her 30 saniyede bir level mesajı kalibrasyonunuza göre doluluk yüzdesini raporlar. Genel MQTT test broker'ının herkes tarafından okunabildiğini unutmayın; rastgele bir önek kullanın ve gerçek saha adlarını test topic'lerinden uzak tutun.
Verilerle Otomasyon
Seviye ve varlık bilgisi MQTT'ye ulaştığında otomasyon bir sensör sorunu olmaktan çıkıp bir abone sorununa dönüşür:
- Pompa kontrolü: ikinci bir betik veya Node-RED akışı
leveltopic'ine abone olur ve seviye yüzde 20'nin altına düştüğünde Raspberry Pi röle MQTT denetleyicisi üzerinden bir pompayı çalıştırır, yüzde 90'ın üzerinde ise kapatır. - Alarmlar: özel bir ConnectMQ broker'ında
level_pctüzerindeki bir eşik alarm kuralı, MQTT eşik alarmları yazısında anlatıldığı gibi neredeyse boş bir tank konusunda sizi uyarır. - Ev otomasyonu: Home Assistant retained
presencetopic'ini bir doluluk sensörü olarak okuyup ışıkları açabilir.
Şebeke elektriği olmayan uzak bir tankta pille çalışan bir seviye sensörüne ihtiyacınız varsa ESP32 su tankı seviye sensörü projesi derin uyku ile aynı payload'ı kullanır; böylece ikisi de aynı panoyu besleyebilir.
Sorun Giderme
| Sorun | Olası neden ve çözüm |
|---|---|
| Her zaman 400 cm okuyor | Yankı algılanmıyor; bölücüyü ve ECHO kablolamasını kontrol edin |
| Ölçümler aşırı sıçrıyor | Sensör tank duvarını görüyor; merkeze ve kenarlardan uzağa monte edin |
| Ölçümler gün içinde kayıyor | Ses hızı sıcaklıkla değişir; yeniden kalibre edin veya sıcaklık kompanzasyonu ekleyin |
| Tankta yoğuşma hataları | Dönüştürücüler üzerinde nem; su geçirmez bir ultrasonik modül kullanın |
GPIO busy |
GPIO23 veya GPIO24'ü başka bir betik kullanıyor |
Üretim Kurulumu
Bir çiftlikteki, atölyedeki veya binadaki bir tank için test broker'ından özel bir broker'a geçin. ConnectMQ, 8883 portunda TLS ve otomatik oluşturulan kimlik bilgileri sunan yönetilen bir MQTT broker hizmetidir. Ortam değişkenlerinde MQTT_PORT=8883, MQTT_USERNAME=YOUR_USERNAME, MQTT_PASSWORD=YOUR_PASSWORD, MQTT_CLIENT_ID=YOUR_CLIENT_ID ve farm1/water/tank1 gibi anlamlı bir önek ayarlayın, ardından betiği bir systemd servisi olarak çalıştırın. Raspberry Pi MQTT eğitimi servis dosyası şablonunu içerir ve burada kullanılan yeniden bağlanma davranışını açıklar.
ConnectMQ ile Başlayın
Seviye ve varlık verilerinizi her panonun ve otomasyonun erişebileceği bir bulut MQTT broker üzerine taşıyın. Kredi kartı gerekmeden ücretsiz Serverless planla ConnectMQ'ya kaydolun, bir broker oluşturun ve Pi'nizi dakikalar içinde TLS üzerinden bağlayın.
Sık Sorulan Sorular
HC-SR04 için gerçekten bir gerilim bölücüye ihtiyacım var mı?
Standart 5 V modül için evet. ECHO pini 5 V çıkış verir ve Raspberry Pi GPIO pinleri yalnızca 3,3 V için tasarlanmıştır.
HC-SR04, Raspberry Pi üzerinde ne kadar doğrudur?
Ölçümler medyan filtrelemeden geçirildiğinde iyi koşullarda yaklaşık bir santimetre doğruluk sağlar. Linux'taki yazılım zamanlaması ve sıcaklık değişiklikleri bir miktar sapma ekler.
HC-SR04 kapalı bir tankta su seviyesini ölçebilir mi?
Evet, yüzeye dimdik baktığı ve kuru kaldığı sürece. Nemli tanklarda su geçirmez bir ultrasonik modül daha uzun ömürlüdür.
Varlık bilgisi neden yalnızca değiştiğinde yayınlanıyor?
Bu, trafiği minimumda tutar ve otomasyonları basitleştirir. Mesaj retained olduğu için yeni aboneler yine de güncel durumu hemen alır.



