NodeMCU&ESPressif

ESP8266 Kesmeler Interrupt ve Timer Kullanımı

Bu içeriğimizde, Arduino IDE kullanarak ESP8266 kesmeleri ve zamanlayıcıları nasıl kullanacağınızı öğreneceksiniz. Kesmeler, mevcut değerini sürekli olarak kontrol etmenize gerek kalmadan GPIO durumundaki değişiklikleri algılamanıza olanak tanır. Kesmeler, bir değişiklik algılandığında bir olay tetiklenir (bir işlev çağrılır).

Örnek olarak, bir PIR hareket sensörü kullanarak hareketi algılayacağız: hareket algılandığında, ESP8266 bir zamanlayıcı başlatır ve önceden tanımlanmış sayıda saniye boyunca bir LED’i açar. Zamanlayıcı geri sayımı bitirdiğinde LED otomatik olarak kapanır.

Bir kesme oluşturmak için, AttachInterrupt()‘u çağırırız ve argüman olarak GPIO kesme pinini, ISR’yi (çağrılacak fonksiyon) ve modu iletiriz. ISR işlevi, ICACHE_RAM_ATTR özniteliğini bildirmiş olmalıdır. Mod, DEĞİŞİM(CHANGE), YÜKSELEN(RISING) veya DÜŞEN(FALLING) olabilir.

attachInterrupt(digitalPinToInterrupt(GPIO), ISR, mod);

Bu eğiticiye devam etmeden önce, Arduino IDE’nizde ESP8266 eklentisinin kurulu olması gerekir. Henüz yapmadıysanız, bu içeriğe göz atın: ESP8266’yı Arduino IDE’ye Kurmak

ESP8266 Kesmelere Giriş

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.

Kesintilerle mevcut pin değerini sürekli kontrol etmeniz gerekmez. Bir değişiklik algılandığında, bir olay tetiklenir – bir işlev çağrılır. Bu işleve kesme servis rutini (ISR) denir.

Bir kesinti olduğunda, işlemci bir görevi yürütmek için ana programın yürütülmesini durdurur ve ardından aşağıdaki şekilde gösterildiği gibi ana programa geri döner.

Bu, özellikle, hareket algılandığında veya pin durumunu sürekli kontrol etmeye gerek kalmadan bir düğmeye basıldığında bir eylemi tetiklemek için kullanışlıdır.

attachInterrupt() Fonksiyonu

Arduino IDE’de bir kesme ayarlamak için, argüman olarak kabul eden attachInterrupt() işlevini kullanırsınız: GPIO kesme pini, yürütülecek işlevin adı ve mod:

attachInterrupt(digitalPinToInterrupt(GPIO), ISR, mode);

GPIO Kesme Pini

İlk argüman bir GPIO kesmesidir. GPIO’yu bir kesme pini olarak ayarlamak için digitalPinToInterrupt(GPIO) kullanmalısınız. Örneğin, GPIO 14’ü kesme olarak kullanmak istiyorsanız, şunu kullanın:

digitalPinToInterrupt(14)

ESP8266, GPIO16 dışında herhangi bir GPIO’daki kesmeleri destekler.

ISR

attachInterrupt() işlevinin ikinci argümanı, kesme her tetiklendiğinde çağrılacak işlevin adıdır – kesme hizmeti rutini (interrupt service routine – ISR).

ISR işlevi mümkün olduğunca basit olmalıdır, böylece işlemci ana programın yürütülmesine hızlı bir şekilde geri döner.

En iyi yaklaşım, genel bir değişken kullanarak ve loop() içinde kesmenin gerçekleştiğini ana koda bildirmek ve bu bayrağı kontrol edip temizlemek ve kodu yürütmektir.

ISR’lerin RAM’de kesme kodunu çalıştırabilmesi için işlev tanımından önce ICACHE_RAM_ATTR‘ye sahip olması gerekir.

Kesme Modları

Üçüncü argüman moddur ve 3 farklı mod vardır:

  • CHANGE: pin değeri değiştiğinde kesmeyi tetiklemek için kullanılır – ö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 tetiklenir;
  • RISING: pin DÜŞÜK’ten YÜKSEK’e gittiğinde tetiklenir.

Örneğimiz için RISING 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.

ESP8266 Zamanlayıcılara/Timers Giriş

Bu içerik için zamanlayıcıları kullanacağız. Hareket algılandıktan sonra LED’in önceden belirlenmiş bir süre boyunca açık kalmasını istiyoruz. Kodunuzu engelleyen ve belirli bir saniye boyunca başka bir şey yapmanıza izin vermeyen bir delay() işlevi kullanmak yerine bir zamanlayıcı kullanacağız.

delay() vs millis()

delay() işlevi, bağımsız değişken 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(milisaniye cinsinden beklenecek süre);

delay(1000)’i kullandığınız zaman programınız 1 saniyeliğine durur. delay() aslında 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() 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.

millis() ile LED Yakma(delay() olmadan)

millis() işlevine aşina değilseniz, bu bölümü okumanızı öneririz. Zamanlayıcılara zaten aşina iseniz, PIR hareket sensörü projesine geçebilirsiniz.

Aşağıdaki kod parçası, led yakmak 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 ledPini =  26;   
int ledDurumu = LOW;             // ledDurumu ledin yanıp yanmayacağına karar verir
unsigned long oncekiMillis = 0;        // LED'in en son güncellendiği zamanı saklar
const long sure = 1000;           // yanıp sönme süresi (milisaniye)

void setup() {

  pinMode(ledPini, OUTPUT);
}

void loop() {
  
  unsigned long simdikiMillis = millis();

  if (simdikiMillis - oncekiMillis >= sure) {
    // LED'i en son ne zaman yaktığınızı kaydeder
    oncekiMillis = simdikiMillis;

    // LED kapalıysa aç ve açıksa kapa:
    if (ledDurumu == LOW) {
      ledDurumu = HIGH;
    } else {
      ledDurumu = LOW;
    }

    // LED'i değişkenin ledDurumu ile ayarlama:
    digitalWrite(ledPini, ledDurumu);
  }
}

Delay() işlevi olmadan çalışan led yakıp söndürme koduna daha yakından bakalım :

Temel olarak, bu kod önceki kaydedilen zamanı (oncekiMillis) şimdiki zamandan (simdikiMillis) çı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 (simdikiMillis - oncekiMillis >= sure) {
    // LED'i en son ne zaman yaktığınızı kaydeder
    oncekiMillis = simdikiMillis;

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 kodunuz her saniye LED’i yanıp sönmeye devam edeceğini anlayabilmelisiniz.

Test etmek için bu kodu ESP8266’nıza yükleyebilirsiniz. Yerleşik LED her saniye yanıp sönmelidir.

Ufak bir seri iletişim ile her 1000 milisaniyede hazırladığımız sayaçların durumunuda kontrol edebilirsiniz.

ESP8266 Kesmeler ile PIR Haraket Sensörü Kullanımı

Bu bölümde, kodunuzdaki kesmeler, ve zamanlayıcıları kullanarak bir PIR hareket sensörüyle hareketi nasıl algılayacağınızı öğreneceksiniz.

Gerekli Malzemeler

Bu öğreticiyi tamamlamak için gereken parçaların listesi:

  • ESP8266 geliştirme kartı
  • PIR Hareket Sensörü
  • 3mm LEDya da 5mm LED
  • 330Ω direnç
  • Devre kartı
  • Bağlantı kabloları

Devre Şeması

PIR hareket sensörünü ve bir LED’i ESP8266’nıza bağlayın. LED’i GPIO 12’ye (D6) ve PIR hareket sensörü veri pinini GPIO 14’e (D5) bağlayın.

Önemli: Bu projede kullanılan Mini AM312 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.

Aşağıdaki şekil AM312 PIR hareket sensörü pin çıkışını göstermektedir.

Program 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ğı saniye sayısını değiştirebilirsiniz. Basitçe sureSaniye değişkenini istediğiniz saniye sayısıyla değiştirin.

#define sureSaniye 10

const int led = 12;
const int hareketSensoru = 14;

//timer kismi
unsigned long simdi = millis();
unsigned long sonTetiklenme = 0;
boolean sayaciBaslat = false;

// Hareketin algılanıp algılanmadığını kontrol eder, LED'i YÜKSEK olarak ayarlar ve bir zamanlayıcı başlatır
ICACHE_RAM_ATTR void hareketTespitEt() {
  Serial.println("Hareket Tespit Edildi!!!");
  digitalWrite(led, HIGH);
  sayaciBaslat = true;
  sonTetiklenme = millis();
}

void setup() {
  Serial.begin(115200);

  pinMode(hareketSensoru, INPUT_PULLUP);
  // hareketSensoru pinini interrupt olarak ayarlar, interrupt fonksiyonunu atayıp RISING modunu ayarlar
  attachInterrupt(digitalPinToInterrupt(hareketSensoru), hareketTespitEt, RISING);

  pinMode(led, OUTPUT);
  digitalWrite(led, LOW);
}

void loop() {
  // simdiki zaman
  simdi = millis();
  // sureSaniye değişkeninde tanımlanan saniye sayısından sonra LED'i kapatır
  if (sayaciBaslat && (simdi - sonTetiklenme > (sureSaniye * 1000))) {
    Serial.println("Hareket durur...");
    digitalWrite(led, LOW);
    sayaciBaslat = false;
  }
}

Şimdi koda bir göz atalım:

led ve hareketSensoru değişkenlerine iki GPIO pini atayarak başlıyoruz:

const int led = 12;
const int hareketSensoru = 14;

Ardından, hareket algılandıktan sonra LED’i kapatmak için bir zamanlayıcı ayarlamanıza izin verecek değişkenler oluşturun.

unsigned long simdi = millis();
unsigned long sonTetiklenme = 0;
boolean sayaciBaslat = false;

simdi değişkeni geçerli süreyi tutar. sonTetikleme değişkeni, PIR sensörünün hareketi algıladığı zamanı tutar. sayaciBaslat, hareket algılandığında zamanlayıcıyı başlatan bir boole değişkenidir.

setup() Kısmı

setup() kısmında, seri bağlantı noktasını 115200 baud hızında başlatıyoruz.

Serial.begin(115200);

PIR Hareket sensörünü INPUT_PULLUP olarak ayarlıyoruz:

  pinMode(hareketSensoru, INPUT_PULLUP);

PIR sensör pinini bir kesme olarak ayarlamak için, daha önce açıklandığı gibi attachInterrupt() işlevini kullanıyoruz.

attachInterrupt(digitalPinToInterrupt(hareketSensoru), hareketTespitEt, RISING);

Hareketi algılayacak olan pin GPIO 14’tür ve RISING modundayken hareketTespitEt() fonksiyonunu çağırır.

LED, durumu DÜŞÜK durumda başlayan bir ÇIKIŞ olarak ayarlarız:

pinMode(led, OUTPUT);
digitalWrite(led, LOW);

loop() Kısmı

loop() işlevi sürekli olarak tekrar tekrar çalışır. Her döngüde, simdi değişkeni geçerli zamanla güncellenir.

 simdi = millis();

loop() içinde başka hiçbir şey yapılmaz. Ancak, hareket algılandığında, setup() içinde daha önce bir kesme ayarladığımız için hareketTespitEt() işlevi çağrılır.

hareketTespitEt() işlevi, Seri Monitörde bir mesaj yazdırır, LED’i açar, sayaciBaslat boolean değişkenini true olarak ayarlar ve sonTetiklenme değişkenini geçerli zamanla günceller.

ICACHE_RAM_ATTR void hareketTespitEt() {
  Serial.println("Hareket Tespit Edildi!!!");
  digitalWrite(led, HIGH);
  sayaciBaslat = true;
  sonTetiklenme = millis();
}

Bu adımdan sonra kod, loop()‘a geri döner. Bu sefer sayaciBaslat değişkeni doğru olur. 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 (sayaciBaslat && (simdi - sonTetiklenme > (sureSaniye * 1000))) {
    Serial.println("Hareket durdu...");
    digitalWrite(led, LOW);
    sayaciBaslat = false;
  }

“Hareket durdu…” mesajı Seri Monitörde yazdırılır, LED kapanır ve sayaciBaslat değişkeni false olarak ayarlanır.

Sonuç

Kodu ESP8266’nıza yükleyin. Doğru kartın ve COM bağlantı noktasının seçili olduğundan emin olun.

115200 baud hızında Seri Monitörü açın.

Elinizi PIR sensörünün önüne getirin. LED yanmalıdır ve Seri Monitörde “Hareket Tespit Edildi!!!” diyen bir mesaj yazdırılır. 10 saniye sonra LED sönmelidir.

Özetlemek gerekirse, kesintiler bir GPIO durumundaki bir değişikliği tespit etmek ve anında bir işlevi tetiklemek için kullanışlıdır.