Bir ESP32 PIR hareket sensörü MQTT düğümü, ucuz bir kızılötesi dedektörü herhangi bir dashboard'un veya otomasyonun tepki verebileceği bir olay kaynağına dönüştürür. Bu eğitimde bir HC-SR501 veya AM312 bağlayacak, hareketi bir kesmeyle (interrupt) yakalayacak, gürültülü tetiklemeleri debounce edecek, olayları broker.connectmq.com'a yayınlayacak ve yeni subscriber'lara bir şeyin en son tam olarak ne zaman hareket ettiğini söyleyen retained bir son hareket zaman damgası tutacaksınız.
PIR Sensörleri Nasıl Davranır
Pasif kızılötesi (PIR) bir sensör, görüş alanı boyunca kızılötesi radyasyondaki değişiklikleri, tipik olarak hareket eden sıcak bir bedeni algılar. Modül bunu basit bir dijital çıkışa dönüştürür: hareket algılandığı sürece yüksek, aksi halde düşük. Ayrıntılar MQTT için önemlidir, çünkü çıkış olay başına temiz tek bir darbe veren bir sinyal değildir.
| Modül | Besleme | Çıkış | Ayarlanabilir | Notlar |
|---|---|---|---|---|
| HC-SR501 | 5 V (VIN pini) | 3,3 V yüksek | Hassasiyet, tutma süresi, tetikleme modu jumper'ı | Büyük lens, birkaç metre menzil, ısınma süresi gerekir |
| AM312 | 2,7 ila 12 V | 3,3 V yüksek | Yok | Minik, birkaç saniyelik sabit tutma süresi, 3,3 V tasarımlar için iyi |
| HC-SR505 | 4,5 ila 20 V | 3,3 V yüksek | Yok | Kompakt, birkaç saniyelik sabit tutma süresi |
Kod yazmadan önce bilmeniz gerekenler:
- Isınma. Güç verildikten sonra PIR modülleri, sensör dengelenirken bir dakikaya kadar rastgele tetiklemeler üretir. Bu süre içindeki hareketi yok sayın.
- Tutma süresi. Çıkış, son algılanan hareketten sonra yapılandırılmış bir süre boyunca yüksek kalır. HC-SR501'de bir potansiyometre bunu birkaç saniyeden birkaç dakikaya kadar ayarlar.
- Yeniden tetikleme modu. HC-SR501 jumper'ı "H" (tekrar) konumundayken devam eden hareket çıkışı yüksek tutar. "L" modunda hareket devam etse bile çıkış tutma süresinden sonra düşer, ardından yeniden tetiklenir. Genellikle istediğiniz "H" modudur.
- Yanlış tetiklemeler. Güneş ışığı, ısıtıcılar, havalandırma menfezleri ve sensörün hemen yanına yerleştirilmiş Wi-Fi antenleri sahte darbelere neden olabilir.
Bağlantı
HC-SR501 için: VCC'yi ESP32'nin 5V (VIN) pinine, GND'yi GND'ye, OUT'u GPIO 27'ye bağlayın. Çıkış 3,3 V'tur; bu yüzden doğrudan ESP32 girişine bağlanır. AM312 için: VCC'yi 3V3'e, GND'yi GND'ye, OUT'u GPIO 27'ye bağlayın. Sensörü ESP32 anteninden biraz uzakta tutun; yakın yerleşim, hayalet tetiklemelerin bilinen bir kaynağıdır.
Olay Tasarımı: Debounce, Bekleme Süresi ve Topic'ler
PIR çıkışının her kenarını yayınlamak subscriber'ları mesaj yağmuruna tutar ve herkese açık broker'daki istemci başına hız kotasını boşa harcar. Sketch ham sinyali iki temiz duruma dönüştürür:
- Çıkış yükseldiğinde ve önceki olay bir bekleme (cooldown) süresinden daha eskiyse hareket algılandı.
- Çıkış sessiz bir süre boyunca düşük kaldığında hareket bitti.
| Topic | Retained | Payload |
|---|---|---|
connectmq/demo/pir9d4k/motion |
Evet | {"state":"detected","ts":"2026-10-16T18:03:11Z","count":14} |
connectmq/demo/pir9d4k/motion/last |
Evet | 2026-10-16T18:03:11Z |
connectmq/demo/pir9d4k/status |
Evet | online / offline |
Temel fikir motion/last topic'idir: saatler sonra bile subscribe olan her istemci son algılamanın zamanını anında alır. Zaman damgaları NTP'den gelir; ESP32 NTP MQTT zaman damgası rehberi burada kullanılan zaman eşitleme ayrıntılarını açıklar.
ESP32 PIR Hareket Sensörü MQTT Sketch'i
#include <WiFi.h>
#include <PubSubClient.h>
#include <time.h>
const char* WIFI_SSID = "YOUR_WIFI_SSID";
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
const char* MQTT_HOST = "broker.connectmq.com";
const uint16_t MQTT_PORT = 1883;
const char* PREFIX = "connectmq/demo/pir9d4k"; // kendi rastgele kimliğinizi kullanın
const uint8_t PIR_PIN = 27;
const uint32_t WARMUP_MS = 60000; // güç verildikten sonraki tetiklemeleri yok say
const uint32_t COOLDOWN_MS = 10000; // "detected" olayları arasındaki minimum süre
const uint32_t CLEAR_MS = 30000; // çıkış bu kadar süre düşükse -> "clear"
WiFiClient net;
PubSubClient mqtt(net);
String clientId, tMotion, tLast, tStatus;
volatile bool edgeSeen = false;
volatile uint32_t lastEdgeMs = 0;
bool motionActive = false;
uint32_t lastDetectedMs = 0;
uint32_t lastHighMs = 0;
uint32_t motionCount = 0;
void IRAM_ATTR onPirRise() {
edgeSeen = true;
lastEdgeMs = millis();
}
bool isoNow(char* buf, size_t len) {
time_t now = time(nullptr);
if (now < 1700000000) return false; // henüz eşitlenmedi
struct tm utc;
gmtime_r(&now, &utc);
strftime(buf, len, "%Y-%m-%dT%H:%M:%SZ", &utc);
return true;
}
void publishState(const char* state) {
char ts[24] = "unknown";
bool synced = isoNow(ts, sizeof(ts));
char payload[128];
snprintf(payload, sizeof(payload), "{\"state\":\"%s\",\"ts\":\"%s\",\"count\":%lu}",
state, ts, (unsigned long)motionCount);
mqtt.publish(tMotion.c_str(), payload, true);
if (strcmp(state, "detected") == 0 && synced) {
mqtt.publish(tLast.c_str(), ts, true);
}
Serial.println(payload);
}
void connectWifi() {
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED) delay(300);
Serial.printf("Wi-Fi OK, IP %s\n", WiFi.localIP().toString().c_str());
}
void connectMqtt() {
while (!mqtt.connected()) {
if (mqtt.connect(clientId.c_str(), tStatus.c_str(), 1, true, "offline")) {
mqtt.publish(tStatus.c_str(), "online", true);
Serial.println("MQTT connected");
} else {
Serial.printf("MQTT failed, state %d\n", mqtt.state());
delay(5000);
}
}
}
void setup() {
Serial.begin(115200);
pinMode(PIR_PIN, INPUT_PULLDOWN);
attachInterrupt(digitalPinToInterrupt(PIR_PIN), onPirRise, RISING);
clientId = "esp32-pir-" + String((uint32_t)ESP.getEfuseMac(), HEX);
tMotion = String(PREFIX) + "/motion";
tLast = String(PREFIX) + "/motion/last";
tStatus = String(PREFIX) + "/status";
connectWifi();
configTime(0, 0, "pool.ntp.org", "time.cloudflare.com");
mqtt.setServer(MQTT_HOST, MQTT_PORT);
connectMqtt();
Serial.println("Warming up PIR sensor...");
}
void loop() {
if (WiFi.status() != WL_CONNECTED) connectWifi();
if (!mqtt.connected()) connectMqtt();
mqtt.loop();
uint32_t now = millis();
bool pinHigh = digitalRead(PIR_PIN) == HIGH;
if (pinHigh) lastHighMs = now;
if (now < WARMUP_MS) { // ısınma gürültüsünü at
edgeSeen = false;
return;
}
// Yükselen kenar (kesmeyle yakalanan) veya çıkış hâlâ yüksek -> hareket
if (edgeSeen || pinHigh) {
edgeSeen = false;
if (!motionActive && now - lastDetectedMs >= COOLDOWN_MS) {
motionActive = true;
lastDetectedMs = now;
motionCount++;
publishState("detected");
}
}
// Çıkış yeterince uzun süre düşük -> clear
if (motionActive && !pinHigh && now - lastHighMs >= CLEAR_MS) {
motionActive = false;
publishState("clear");
}
}
Kesme servis rutini yalnızca bir bayrak ve bir zaman ayarlar. Asla bir kesmenin içinden publish yapmayın: ağ kodu kesme açısından güvenli değildir ve ESP32 çöker. Bayrak, mqtt.loop() meşgulken bile bir AM312'den gelen çok kısa bir darbenin kaçırılmamasını garanti eder.
ESP32 PIR Hareket Sensörü MQTT Olaylarını Test Etmek
Önekinize subscribe olun ve bir dakikalık ısınmadan sonra sensörün önünde elinizi sallayın:
mosquitto_sub -h broker.connectmq.com -p 1883 -i pir-view-9d4k -t "connectmq/demo/pir9d4k/#" -v
Bir detected durumu, ardından oda sessizleştikten yaklaşık 30 saniye sonra clear görürsünüz. Subscriber'ı durdurun, bekleyin ve yeniden başlatın: retained motion/last değeri hemen gelir. Herkese açık alan paylaşımlıdır ve herkes tarafından okunabilir; bu yüzden onu gerçek bir ev veya ofis için kullanmayın. Hareket verileri bir yerin ne zaman boş olduğunu açığa çıkarır. Amaçlanan kullanımı için herkese açık MQTT test broker'ı sayfasına bakın.
Zamanlamayı Ayarlamak
Davranışı üç sabit kontrol eder:
- COOLDOWN_MS,
detectedolayının ne sıklıkla tetiklenebileceğini sınırlar. On saniye bir koridor lambası için uygundur; bir güvenlik kaydı için kısa bir aradan sonraki her yeni varlığı isteyebilirsiniz. - CLEAR_MS, odanın ne zaman boş sayılacağına karar verir. Buna modülün kendi tutma süresini ekleyin: 5 saniyelik HC-SR501 tutma süresi ve 30 saniyelik CLEAR_MS ile
clear, son hareketten yaklaşık 35 saniye sonra gelir. - WARMUP_MS, modülün oturma süresini kapsamalıdır. HC-SR501 için altmış saniye güvenlidir.
HC-SR501 tutma süresi potansiyometresini minimuma çevirin ve zamanlamayı firmware'e bırakın. Yazılımsal zamanlama uzaktan daha kolay değiştirilir ve modüller arasında tutarlıdır.
Hareket Olaylarına Tepki Vermek
Broker olayları iletir; onlarla ne yapılacağına başka bir şey karar verir. Tipik tüketiciler; MQTT binary sensor ile Home Assistant, bir lambayı yakıp bildirim gönderen bir Node-RED akışı veya hareket algılandığında bir kare yakalayan bir kameradır; ESP32-CAM MQTT projesi bu sensörle doğal olarak eşleşir. Her tüketici motion topic'ine sıradan bir MQTT istemcisi olarak subscribe olur.
Retained offline will mesajı güvenlik kullanımları için de aynı derecede önemlidir. Sessizce ölen bir hareket sensörü, sessiz bir odayla tıpatıp aynı görünür. ESP32 MQTT Last Will eğitimi, broker'ın bunu sizin adınıza nasıl yayınladığını açıklar.
Sorun Giderme
- Sürekli tetikleme. Sensör ESP32 antenine çok yakın veya bir ısı kaynağına bakıyor. Yerini değiştirin, hassasiyeti düşürün ve ısınma mantığını kontrol edin.
- Hiç tetikleme yok. HC-SR501'in 3,3 V'tan değil 5 V'tan beslendiğini ve OUT'un yapılandırılmış pine gittiğini kontrol edin.
- Algılanıyor ama hiç bitmiyor. Sürekli hareketle yeniden tetikleme modu veya beklenenden uzun bir tutma süresi. Tutma potansiyometresini azaltın.
- ESP32 harekette sıfırlanıyor. Kesme işleyicisinin içinde iş yapıyorsunuz. Onu bayrak ayarlamakla sınırlı tutun.
- Zaman damgası unknown gösteriyor. NTP henüz eşitlenmedi; dışa giden UDP 123'e izin verin.
Production Kurulumu
Herhangi bir gerçek mekan için kimlik doğrulamalı ve şifreli bir broker kullanın. app.connectmq.com adresinde bir tane oluşturun, ISRG Root X1 sertifikasıyla 8883 portunda WiFiClientSecure kullanmaya geçin ve YOUR_CLIENT_ID, YOUR_USERNAME ve YOUR_PASSWORD ile bağlanın. ConnectMQ portalında sensörü ayarlarken motion topic'ini canlı topic gezgininde izleyebilirsiniz.
Birden fazla sensör çalıştırıyorsanız, tek bir düz topic yerine her odaya site/floor1/hallway/motion gibi kendi önek seviyesini verin. Böylece tüketiciler tek bir odaya subscribe olabilir veya tüm kat için wildcard kullanabilir; hassas bir odanın hareket sayısı üzerine kurulan bir eşik uyarı kuralı da ek backend kodu olmadan size basit bir mesai dışı uyarısı sağlar.
Hareket Uyarılarınızı ConnectMQ Üzerinde Çalıştırın
Hareket uyarıları ancak sensörden telefona giden yol güvenilir ve gizli olduğunda yararlıdır. ConnectMQ; TLS, retained mesajlar, last will desteği ve kredi kartı gerektirmeyen ücretsiz bir Serverless plan sunan bir bulut MQTT broker hizmetidir. Bu düğümü bağlayın, tercih ettiğiniz otomasyon aracını kurun ve ESP32 IoT eğitimi koleksiyonunda daha fazla cihaz projesi bulun.
Sık Sorulan Sorular
PIR sensörüm neden güç verildikten hemen sonra tetikleniyor?
PIR modüllerinin dengelenmesi bir dakikaya kadar sürer ve bu süre boyunca rastgele çıkış üretirler. Sketch'te olduğu gibi bir ısınma süresi içindeki tetiklemeleri yok sayın.
Hareket olayları retained mesaj olmalı mı?
Yeni subscriber'ların bağlamı hemen alması için mevcut durumu ve son hareket zaman damgasını retained yapın. Yalnızca bir kez tetiklenmesi gereken tek seferlik bildirimleri retained yapmayın.
PIR sensörüyle deep sleep kullanabilir miyim?
Evet. PIR çıkışını bir ext0 uyandırma kaynağı olarak yapılandırın, uyandıktan sonra Wi-Fi'a bağlanın, olayı yayınlayın ve tekrar uykuya geçin. Mesaj ulaşmadan önce birkaç saniyelik bir gecikme bekleyin.
Bir kesme gerçekten gerekli mi?
Uzun tutma süreli bir HC-SR501 için yoklama (polling) yeterlidir. AM312 gibi kısa darbeli modüllerin darbeleri ağ kodu çalışırken kaçırılabilir; bu yüzden kesme bayrağı algılamayı güvenilir kılar.



