ESP32 Kesmeler(Interrupts)
Bu yazımızda, bir PIR hareket sensörü kullanarak ESP32 Kesmeler(Interrupts)’in nasıl kullanılacağını anlatıyoruz. Hareket algılandığında (bir kesinti tetiklendiğinde), ESP32 bir zamanlayıcı başlatır ve önceden tanımlanmış bir süre boyunca bir LED’i açar. Zamanlayıcı geri sayımı bitirdiğinde, LED otomatik olarak kapanır.
Bu örnekle iki önemli kavramı da keşfedeceğiz: kesintiler ve zamanlayıcılar.
ESP32’yi Arduino IDE kullanarak programlayacağız, bu yüzden devam etmeden önce ESP32 kütüphanesinin kurulu olduğundan emin olun:
Arduino IDE’ye ESP32 Desteği Kurmak (Windows, Linux, MacOS)
Gerekli Malzemeler
- ESP32 Geliştirme Kartı(DOIT DEVKIT V1)
- PIR Hareket Sensörü
- 5mm LED
- 330Ω direnç
- Bağlantı kabloları
- Breadboard
Kesmeler(Interrupts) Nedir?
Bir PIR hareket sensörü ile bir olayı tetiklemek için kesmeler kullanırsınız. Kesmeler, mikrodenetleyici programlarında işlerin otomatik olarak gerçekleşmesi için yararlıdır ve zamanlama sorunlarının çözülmesine yardımcı olabilir.
Kesmelerle, bir pinin mevcut değerini sürekli olarak kontrol etmeniz gerekmez. Kesmelerle, bir değişiklik algılandığında bir olay tetiklenir (bir işlev çağrılır).
Arduino IDE’de bir kesme ayarlamak için, argüman olarak kabul eden attachInterrupt() işlevini kullanırız: GPIO pini, yürütülecek işlevin adı ve mod:
attachInterrupt(digitalPinToInterrupt(GPIO), işlev, mod);
GPIO Kesmesi
İlk argüman GPIO numarasıdır. Normalde, gerçek GPIO’yu bir kesme pini olarak ayarlamak için digitalPinToInterrupt(GPIO) kullanmanız gerekir. Örneğin, bir kesme olarak GPIO 27’yi kullanmak istiyorsak, şunu kullanırız:
digitalPinToInterrupt(27)
Bir ESP32 kartı ile, aşağıdaki şekilde kırmızı bir dikdörtgenle vurgulanan tüm pinler, kesme pinleri olarak yapılandırılabilir. Bu örnekte, PIR Hareket sensörüne bağlı bir kesme olarak GPIO 27’yi kullanacağız.
Tetiklenecek İşlev
attachInterrupt() işlevinin ikinci argümanı, kesme her tetiklendiğinde çağrılacak işlevin adıdır.
Mod
Üçüncü argüman moddur. 5 farklı mod vardır:
- LOW: Pin DÜŞÜK olduğunda kesmeyi tetikler.
- HIGH: Pin YÜKSEK olduğunda kesmeyi tetikler.
- CHANGE: Pin değeri değiştiğinde kesmeyi tetikler. Örneğin YÜKSEK’ten DÜŞÜK’e veya DÜŞÜK’ten YÜKSEK’e;
- FALLING: Pin YÜKSEK’ten DÜŞÜK’e gittiğinde tetikler.
- RISING: Pin DÜŞÜK’ten YÜKSEK’e geçtiğinde tetikler.
Bu örnek için YÜKSELİŞ modu kullanılacaktır, çünkü PIR hareket sensörü hareket algıladığında bağlı olduğu GPIO DÜŞÜK’ten YÜKSEK’e geçer.
Zamanlayıcıların Tanımı
Bu örnekte ayrıca zamanlayıcıları da tanıtacağız. Hareket algılandıktan sonra LED’in önceden belirlenmiş bir süre boyunca açık kalmasını istiyoruz. Kodunuzu bloke eden ve belirli bir saniye boyunca başka bir şey yapmanıza izin vermeyen bir delay() işlevi kullanmak yerine bir zamanlayıcı kullanmalıyız.
delay() İşlevi
Yaygın olarak kullanıldığı için delay() işlevine aşina olmalısınız. Bu işlevin kullanımı oldukça basittir. Argüman olarak tek bir int sayısını kabul eder. Bu sayı, programın bir sonraki kod satırına geçene kadar beklemesi gereken süreyi milisaniye cinsinden temsil eder.
delay(beklenecek milisaniye)
delay(1000) yaptığınızda programınız o satırda 1 saniye durur.
delay() bir engelleme işlevidir. Engelleme işlevleri, bir programın söz konusu görev tamamlanana kadar başka bir şey yapmasını engeller. Aynı anda birden fazla görevin gerçekleşmesi gerekiyorsa, delay()‘i kullanamazsınız.
Çoğu proje için gecikmeleri kullanmaktan kaçınmalı ve bunun yerine zamanlayıcıları kullanmalısınız.
millis() İşlevi
millis() adlı bir işlevi kullanarak, programın ilk başlamasından bu yana geçen milisaniye sayısını döndürebilirsiniz.
millis()
Bu işlev neden yararlıdır? Çünkü biraz matematik kullanarak, kodunuzu engellemeden ne kadar zaman geçtiğini kolayca doğrulayabilirsiniz.
milis() ile LED Yakmak
Aşağıdaki kod parçası, yanıp sönen bir LED projesi oluşturmak için millis() işlevini nasıl kullanabileceğinizi gösterir. 1000 milisaniye boyunca bir LED’i açar ve ardından kapatır.
const int ledPin = 26;
int ledState = LOW;
unsigned long previousMillis = 0;
const long interval = 1000;
void setup() {
pinMode(ledPin, OUTPUT);
}
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
if (ledState == LOW) {
ledState = HIGH;
} else {
ledState = LOW;
}
digitalWrite(ledPin, ledState);
}
}
Bir delay() işlevi olmadan çalışan bu LED yakmak örneğine daha yakından bakalım (bunun yerine millis() işlevini kullanır).
Temel olarak, bu kod önceki kaydedilen zamanı (previousMillis) şimdiki zamandan (currentMillis) çıkarır. Kalan aralıktan daha büyükse (bu durumda 1000 milisaniye), program öncekiMillis değişkenini geçerli zamana günceller ve LED’i açar veya kapatır.
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
Bu kod engelleyici olmadığından, ilk if ifadesinin dışında bulunan herhangi bir kod normal şekilde çalışmalıdır.
Artık loop() işlevinize başka görevler ekleyebileceğinizi ve kodunuzun her saniye LED’i yanıp sönmeye devam edeceğini anlayabilmelisiniz.
Bu kodu ESP32’nize yükleyebilir ve test etmek için aşağıdaki şematik diyagramı bir araya getirebilir ve nasıl çalıştığını görmek için milisaniye sayısını değiştirebilirsiniz.
Not: ESP32’nize kod yüklerken herhangi bir sorun yaşadıysanız, ESP32 Hataları ve Çözümleri sayfamıza bakın.
ESP32 PIR Hareket Sensörü Kullanımı
Kesmeler ve zamanlayıcılar fikirlerini anladıktan sonra, projeye devam edelim.
Devre Şeması
Yapacağımız devrenin montajı kolay, dirençli bir LED kullanacağız. LED, GPIO 26’ya bağlı. 3.3V’da çalışan PIR Hareket Sensörünü kullanacağız. GPIO 27’ye bağlanacaktır. Bir sonraki şematik diyagramı takip etmeniz yeterlidir.
Önemli: Bu projede kullanılan PIR Hareket Sensörü 3.3V ile çalışmaktadır. Ancak, HC-SR501 gibi başka bir PIR hareket sensörü kullanıyorsanız, 5V’da çalışır. 3.3V’da çalışacak şekilde değiştirebilir veya Vin pinini kullanarak basitçe çalıştırabilirsiniz.
Arduino Kodu
Devreyi şematik diyagramda gösterildiği gibi bağladıktan sonra, verilen kodu Arduino IDE’nize kopyalayın.
Kodu olduğu gibi yükleyebilir veya hareket algıladıktan sonra LED’in yanacağı saniyeyi değiştirebilirsiniz. Basitçe timeSeconds değişkenini istediğiniz saniye sayısıyla değiştirin
#define timeSeconds 10
const int led = 26;
const int motionSensor = 27;
unsigned long now = millis();
unsigned long lastTrigger = 0;
boolean startTimer = false;
void IRAM_ATTR detectsMovement() {
Serial.println("HAREKET TESPIT EDILDI!!!");
digitalWrite(led, HIGH);
startTimer = true;
lastTrigger = millis();
}
void setup() {
Serial.begin(115200);
pinMode(motionSensor, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(motionSensor), detectsMovement, RISING);
pinMode(led, OUTPUT);
digitalWrite(led, LOW);
}
void loop() {
now = millis();
if(startTimer && (now - lastTrigger > (timeSeconds*1000))) {
Serial.println("Hareket durdu...");
digitalWrite(led, LOW);
startTimer = false;
}
}
Şimdi koda bir göz atalım. Led ve motionSensor değişkenlerine iki GPIO pini atayarak başlayın.
const int led = 26;
const int motionSensor = 27;
Ardından, hareket algılandıktan sonra LED’i kapatmak için bir zamanlayıcı ayarlamanıza izin verecek değişkenler oluşturun.
long now = millis();
long lastTrigger = 0;
boolean startTimer = false;
now değişkeni geçerli süreyi tutar. lastTrigger değişkeni, PIR sensörünün hareketi algıladığı zamanı tutar. startTimer, hareket algılandığında zamanlayıcıyı başlatan bir boole değişkenidir.
setup()
setup() içinde, Seri bağlantı noktasını 115200 baud hızında başlatarak başlayın.
Serial.begin(115200);
PIR Hareket sensörünü INPUT PULLUP olarak ayarlayın.
pinMode(motionSensor, INPUT_PULLUP);
PIR sensör pinini bir kesme olarak ayarlamak için, daha önce açıklandığı gibi attachInterrupt() işlevini kullanın.
attachInterrupt(digitalPinToInterrupt(motionSensor), detectsMovement, RISING);
Hareketi algılayacak pin GPIO 27’dir ve RISING modundayken detectsMovement() fonksiyonunu çağırır.
loop()
loop() işlevi sürekli olarak tekrar tekrar çalışıyor. Her döngüde, now değişkenini geçerli zamanla günceller.
now = millis();
loop() içinde başka hiçbir şey yapılmaz.
Ancak, hareket algılandığında, setup() üzerinde daha önce bir kesme ayarladığımız için detectsMovement() işlevi çağrılır.
detectsMovement() işlevi, Seri Monitörde bir mesaj yazdırır, LED’i açar, startTimer boolean değişkenini true olarak ayarlar ve lastTrigger değişkenini geçerli zamanla günceller.
void IRAM_ATTR detectsMovement() {
Serial.println("HAREKET TESPIT EDILDI!!!");
digitalWrite(led, HIGH);
startTimer = true;
lastTrigger = millis();
}
Not: IRAM_ATTR, kesme kodunu RAM’de çalıştırmak için kullanılır, aksi takdirde kod flash’ta saklanır ve daha yavaştır.
Bu adımdan sonra kod, loop()’a geri döner.
Bu sefer startTimer değişkeni doğrudur. Yani saniye cinsinden tanımlanan süre geçtiğinde (hareket algılandığından beri), aşağıdaki if ifadesi doğru olacaktır.
if(startTimer && (now - lastTrigger > (timeSeconds*1000))) {
Serial.println("Hareket durdu...");
digitalWrite(led, LOW);
startTimer = false;
}
“Hareket durdu…” mesajı Seri Monitörde yazdırılır, LED kapanır ve startTimer değişkeni false olarak ayarlanır.
Özetlemek gerekirse, mevcut GPIO değerini sürekli okumaya gerek kalmadan GPIO durumundaki bir değişikliği algılamak için kesmeler kullanılır. Kesintilerle, bir değişiklik algılandığında bir işlev tetiklenir. Ayrıca, kodunuzu bloke etmek zorunda kalmadan önceden tanımlanmış sayıda saniyenin geçip geçmediğini kontrol etmenizi sağlayan basit bir zamanlayıcıyı nasıl ayarlayacağınızı da gördünüz.
Yorum yapma özelliği, forum tarafından gelen istek sebebiyle kapatılmıştır. Lütfen tartışmalar ve sorularınız için topluluk forumumuza katılın.