Bu kapsamlı rehberimizde, Arduino IDE kullanarak ESP8266 kesmeler (interrupts) ve zamanlayıcıların (timers) asenkron olarak nasıl kullanılacağını öğreniyoruz. Kesmeler, mikrodenetleyicinin genel amaçlı giriş/çıkış (GPIO) pinlerindeki lojik seviye değişikliklerini, yazılımsal döngülerle pini sürekli taramaya (polling) gerek kalmadan anında algılamamızı sağlar. Bu sayede pinde bir durum değişimi oluştuğu anda donanım seviyesinde bir olay tetiklenerek hedef fonksiyon otomatik olarak çağrılır.

Örnek uygulama senaryomuzda, bir PIR hareket sensörü kullanarak ortamdaki anlık hareketleri algılayacağız: Hareket tespit edildiği anda ESP8266 donanımsal bir kesme tetikleyecek, arka planda milisaniye tabanlı bir zamanlayıcı başlatacak ve tanımlanan süre boyunca çıkışa bağlı LED’i aktif hale getirecektir. Tanımlanan süre dolduğunda ise LED, ana program akışını bloke etmeden otomatik olarak sönecektir.
Arduino ortamında donanımsal bir kesme tanımlamak için attachInterrupt() fonksiyonu çağrılır. Bu fonksiyona parametre olarak kesme atanacak GPIO pini, kesme tetiklendiğinde çağrılacak olan ISR (Interrupt Service Routine) fonksiyonu ve tetikleme modu (CHANGE, RISING veya FALLING) iletilir. ESP8266 mimarisinde kesme fonksiyonlarının RAM üzerinden hızlıca yürütülebilmesi için ISR fonksiyon tanımının önüne mutlaka ICACHE_RAM_ATTR niteliği (attribute) eklenmelidir.
attachInterrupt(digitalPinToInterrupt(GPIO), ISR, mode);Bu rehbere başlamadan önce, bilgisayarınızdaki Arduino IDE üzerinde ESP8266 kart desteğinin eksiksiz olarak kurulu olduğundan emin olmalısınız. Kurulum adımları için hazırladığımız ESP8266 Arduino IDE Kart Desteği Kurulumu içeriğimize göz atabilirsiniz.
ESP8266 Kesmeler Interrupt ve Timer Kullanımı: Kesme (Interrupt) Mimarisi
Kesmeler, gömülü sistem programlamada olay güdümlü (event-driven) kod yapılarının oluşturulması ve kritik zamanlama hassasiyeti bulunan problemlerin çözülmesi için biçilmiş kaftandır.
Kesmeler sayesinde lojik pin durumunun taranması için işlemci çekirdeği gereksiz yere meşgul edilmez. Pin üzerinde tanımlanan lojik seviye değişimi (örneğin buton yükselen veya düşen kenar sinyalleri) oluştuğunda donanım seviyesinde bir olay tetiklenerek doğrudan Kesme Servis Rutini (Interrupt Service Routine – ISR) çağrılır.
Bir kesme sinyali alındığında, işlemci o anda yürüttüğü ana kod dizilimini geçici olarak askıya alır, kesmeye özel atanan ISR fonksiyonunu yürütür ve bu işlem bittikten sonra aşağıdaki çalışma şemasında gösterildiği gibi ana program akışına kaldığı yerden devam eder:

Bu yaklaşım; özellikle sensörlerden gelen anlık veri değişimlerinin kaçırılmaması, buton tetiklemeleri ve enerji tasarrufu gerektiren uyku modlarından (Deep Sleep) uyanma senaryoları için kritik öneme sahiptir.
Kesmelerin elektriksel ve donanımsal yürütme sürecini matematiksel olarak modelleyebiliriz. Bir donanımsal kesme tetiklendiğinde, işlemcinin bu sinyali algılayıp ISR fonksiyonunun ilk satırını çalıştırmasına kadar geçen süreye kesme gecikmesi (interrupt latency) denir. Bu gecikme süresi (
) şu şekilde formüle edilir:
![]()
Burada;
sinyalin CPU saat çevrimleriyle senkronizasyon süresini,
CPU yazmaçlarının (registers) yığına (stack) yedeklenme süresini,
kesme vektör tablosundan ilgili ISR adresinin çekilme süresini ve
ise eğer fonksiyon flash bellekteyse oluşabilecek önbellek ıskalama süresini temsil eder.
ESP8266 üzerinde yer alan Tensilica L106 32-bit RISC işlemci çekirdeği
varsayılan saat frekansında çalışırken, her bir saat çevriminin periyodu (
):
![]()
olarak hesaplanır. Eğer ISR fonksiyonu IRAM yerine yavaş olan Flash hafızada tutulursa, önbellek ıskalamaları (
) nedeniyle gecikme süresi mikro saniyeler seviyesine fırlayabilir. Ancak fonksiyon tanımının önüne ICACHE_RAM_ATTR makrosu eklendiğinde, ilgili kod blokları doğrudan statik RAM (IRAM) üzerine kopyalanır ve sıfır bekleme çevrimiyle (zero wait states) çalıştırılır. Bu sayede donanım seviyesindeki kesme gecikmesi minimum seviyeye indirgenir:
![]()
attachInterrupt() Fonksiyonunun Yapısı
Arduino IDE üzerinde donanımsal bir kesmeyi aktifleştirmek için attachInterrupt() fonksiyonu şu parametre düzeniyle kullanılır:
attachInterrupt(digitalPinToInterrupt(GPIO), ISR, mode);1. Parametre: GPIO Kesme Pini
İlk parametre, donanımsal kesmenin atanacağı fiziksel pini tanımlar. İlgili pin numarasını donanım seviyesinde kesmeye uyarlayabilmek için mutlaka digitalPinToInterrupt(GPIO) makrosu kullanılmalıdır. Örneğin, GPIO 14 (D5) pinini kesme pini olarak yapılandırmak istiyorsak şu ifadeyi ekleriz:
digitalPinToInterrupt(14)ESP8266 mikrodenetleyicisi donanımsal yapısı gereği GPIO 16 hariç tüm genel amaçlı giriş/çıkış (GPIO) pinleri üzerinde donanımsal kesme mimarisini tam olarak destekler.
2. Parametre: Kesme Servis Rutini (ISR)
İkinci parametre, kesme anında donanımın otomatik olarak çağıracağı fonksiyonun adını (ISR) işaret eder.
ISR fonksiyonları olabildiğince minimalist, hızlı ve hafif yapıda kodlanmalıdır. Bu fonksiyon içerisinde kesinlikle delay() veya Serial.print() gibi yavaş çalışan bloklayıcı fonksiyonlar kullanılmamalıdır. En iyi tasarım deseni; ISR içerisinde sadece global bir durum değişkenini (bayrak/flag) güncelleyip çıkmak ve asıl ağır iş yüklerini loop() fonksiyonu içerisinde bu bayrağı kontrol ederek yürütmektir.
ESP8266 mimarisinde, kesme fonksiyon kodlarının önbellek (Flash) yerine doğrudan hızlı statik RAM üzerinde tutulması ve anında yürütülebilmesi için fonksiyon tanımından önce ICACHE_RAM_ATTR ön eki eklenmelidir.
3. Parametre: Kesme Tetikleme Modları
Üçüncü parametre, kesmenin hangi elektriksel sinyal geçişinde tetikleneceğini belirler. Arduino ortamında 3 farklı kesme modu desteklenir:
- CHANGE: Pindeki lojik seviyenin her türlü değişiminde (Lojik 0’dan 1’e veya Lojik 1’den 0’a geçişlerde) kesmeyi tetikler.
- FALLING: Pin lojik YÜKSEK (3.3V) seviyesinden DÜŞÜK (GND) seviyesine geçtiğinde kesmeyi tetikler (Düşen Kenar).
- RISING: Pin lojik DÜŞÜK seviyesinden YÜKSEK seviyesine geçtiğinde kesmeyi tetikler (Yükselen Kenar).
PIR hareket sensörleri hareket algıladığı anda çıkış sinyalini Lojik DÜŞÜK seviyesinden YÜKSEK (3.3V) seviyesine çektiği için bu projemizde tetikleme modu olarak RISING kullanacağız.
Zamanlayıcılar (Timers) ve Bloklamasız Kodlama Girişi
Sensörden hareket sinyali alındıktan sonra LED’i belirli bir süre boyunca açık tutmak istiyoruz. Ancak bu bekleme süresini oluştururken işlemciyi kilitleyen ve arka plandaki diğer görevlerin yürütülmesini tamamen engelleyen klasik delay() fonksiyonu yerine, bloklamasız (non-blocking) zamanlama kütüphanelerini ve sayaç mimarisini kullanacağız.
delay() ve millis() Karşılaştırması
Klasik delay() fonksiyonu parametre olarak milisaniye cinsinden tam sayı bir değer kabul eder. İşlemci bu süre boyunca tamamen duraklar ve bir sonraki satıra geçmez:
delay(bekleme_suresi_ms);Örneğin delay(1000) komutu çalıştığında işlemci 1 saniye boyunca adeta kilitlenir. Bu durum “bloklayıcı” (blocking) yapı olarak adlandırılır. Eğer projenizde aynı anda hem Wi-Fi üzerinden sunucuyla haberleşmeniz, hem sensörleri okumanız hem de zamanlama yapmanız gerekiyorsa delay() kullanmak kararsızlıklara ve bağlantı kopmalarına neden olur. Modern projelerde bu fonksiyondan olabildiğince kaçınılmalıdır.
Bloklamasız zamanlama için millis() fonksiyonundan yararlanırız. Bu fonksiyon, mikrodenetleyici çalışmaya başladığı andan itibaren geçen süreyi milisaniye cinsinden unsigned long veri tipinde döndürür:
millis();Bu değer üzerinden kuracağımız matematiksel algoritmalar sayesinde, işlemciyi hiçbir şekilde bloke etmeden arka planda süre takibini mükemmel bir şekilde gerçekleştirebiliriz.
millis() Kullanarak Bloklamasız LED Kontrolü (Gecikmesiz Sinyal)
Zamanlayıcı mantığına yabancıysanız aşağıdaki basit yapıyı incelemenizi öneririz. Bu kod, delay() kullanmadan 1000 milisaniye aralıklarla kart üzerindeki LED’in durumunu tersleyerek yanıp sönmesini (blink) sağlar:
const int ledPini = 2; // Yerleşik LED (GPIO2)
int ledDurumu = LOW;
unsigned long oncekiMillis = 0; // Son sinyal değişim zamanı
const long sure = 1000; // Periyot süresi (ms)
void setup() {
pinMode(ledPini, OUTPUT);
}
void loop() {
unsigned long simdikiMillis = millis();
// Belirlenen periyot süresi aşıldı mı?
if (simdikiMillis - oncekiMillis >= sure) {
oncekiMillis = simdikiMillis; // Zaman damgasını güncelle
// LED durumunu tersle
if (ledDurumu == LOW) {
ledDurumu = HIGH;
} else {
ledDurumu = LOW;
}
digitalWrite(ledPini, ledDurumu);
}
}Bu kod yapısı, her döngüde güncel zaman damgası olan simdikiMillis ile hafızada tutulan son işlem zamanı oncekiMillis arasındaki farkı kontrol eder. Fark belirlenen hedef süreyi aştığı anda zaman damgası güncellenir ve işlem gerçekleştirilir. En büyük avantajı ise bu kontrolün dışında kalan loop içerisindeki diğer tüm kod blokları mikrodenetleyici bloke edilmeden mikro saniyeler içerisinde akmaya devam eder.

Seri terminal üzerinden de zamanlayıcı sayaç durumlarını anlık olarak izleyebilir ve doğruluğunu test edebilirsiniz:

PIR Sensörü ve Donanımsal Kesme Uygulaması
Bu bölümde, öğrendiğimiz kesme ve bloklamasız zamanlama (millis) tekniklerini birleştirerek kararlı bir hareket algılama projesi hazırlıyoruz.
Fiziksel butonlar veya mekanik anahtarlar kapatıldığında, metal kontakların birbirine teması esnasında mikro saniyeler mertebesinde elektriksel arklar (çınlamalar) meydana gelir. Bu duruma kontak sıçraması (bouncing) adı verilir. Donanımsal kesmeler son derece hassas çalıştığı için, tek bir buton basımını onlarca farklı yükselen/düşen kenar kesmesi gibi algılayarak ISR fonksiyonunu mükerrer defalar tetikleyebilir.
Bu gürültüyü donanımsal olarak engellemek amacıyla pürüzsüzleştirici bir RC alçak geçiren filtre (RC Low-Pass Filter) kullanılabilir. RC filtresinin zaman sabiti (
):
![]()
şeklinde hesaplanır. Kondansatör üzerindeki voltajın (
), besleme gerilimi olan
seviyesine göre şarj olma denklemi:
![]()
formülüyle ifade edilir. ESP8266 GPIO giriş pini için lojik-1 eşik voltaj değeri (
) kabul edilirse, sinyalin bu eşik değere ulaşarak kararlı hale gelme süresi (
):
![]()
Örnek olarak
ve
seçildiğinde filtre karakteristikleri şu şekilde elde edilir:
![]()
Yazılımsal olarak ise debouncing işlemi, ISR fonksiyonu içerisinde iki kesme tetiklemesi arasında geçen sürenin matematiksel olarak sorgulanmasıyla (örneğin
kontrolüyle) kolaylıkla çözülebilir.
Gerekli Donanım Bileşenleri
- ESP8266 Geliştirme Kartı (NodeMCU)
- PIR Hareket Sensörü (Örn: AM312 veya HC-SR501)
- 5mm veya 3mm LED
- 330Ω Direnç (Akım sınırlayıcı)
- Breadboard
- Bağlantı Kabloları (Jumper)
Uygulama Bağlantı Şeması
LED anot bacağını 330 Ohm direnç üzerinden GPIO 12 (D6) pinine bağlayın. PIR sensörünün veri (OUT) bacağını ise doğrudan GPIO 14 (D5) pinine entegre edin:

Önemli Donanım Notu: Kullandığımız kompakt Mini AM312 PIR sensörü lojik olarak doğrudan 3.3V ile çalışır ve ESP8266 GPIO pinleriyle kusursuz uyumludur. Ancak projenizde yaygın bulunan HC-SR501 model sensör kullanacaksanız, bu sensör 5V beslemeye ihtiyaç duyar. Sensörün besleme bacağını kart üzerindeki VIN (5V) pinine bağlamaya özen gösterin.
AM312 sensör bacak bağlantı düzenini aşağıdaki şemadan kontrol edebilirsiniz:

Arduino Proje Kaynak Kodu
Bağlantıları tamamladıktan sonra aşağıdaki kararlı kaynak kodu kopyalayarak Arduino IDE editörüne yapıştırın. LED’in hareket algılandıktan sonra ne kadar süre açık kalacağını belirlemek için en üstteki sureSaniye tanımlamasını değiştirebilirsiniz:
#define sureSaniye 10
const int ledPin = 12; // D6
const int hareketSensorPin = 14; // D5
// Asenkron Zamanlayıcı Değişkenleri
unsigned long simdikiZaman = millis();
unsigned long sonTetiklenmeZamani = 0;
volatile bool sayacAktif = false;
// Kesme Servis Rutini (ISR) - Doğrudan IRAM üzerinde yürütülür
ICACHE_RAM_ATTR void hareketISR() {
// ISR içerisinde hızlı ve bloklamasız işlemler
digitalWrite(ledPin, HIGH);
sayacAktif = true;
sonTetiklenmeZamani = millis();
}
void setup() {
Serial.begin(115200);
// PIR sensörü için dahili pull-up direnci aktif edilir
pinMode(hareketSensorPin, INPUT_PULLUP);
// Donanımsal kesme yükselen kenar tetiklemeli olarak kurulur
attachInterrupt(digitalPinToInterrupt(hareketSensorPin), hareketISR, RISING);
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);
Serial.println("Sistem Hazır. Hareket bekleniyor...");
}
void loop() {
simdikiZaman = millis();
// Belirlenen süre dolduğunda LED'i ana akışı bloke etmeden kapatır
if (sayacAktif && (simdikiZaman - sonTetiklenmeZamani > (sureSaniye * 1000))) {
Serial.println("Hareket sonlandı. LED kapatılıyor.");
digitalWrite(ledPin, LOW);
sayacAktif = false;
}
}Kodun Mimari ve Satır Analizi
Projede kullanılacak pin numaralarını tanımlayarak başlıyoruz:
const int ledPin = 12;
const int hareketSensorPin = 14;Zamanlayıcı kontrolünü gerçekleştirmek üzere değişkenleri tanımlıyoruz. Kesme fonksiyonu (ISR) ile ana döngü (loop) arasında ortak kullanılan ve anlık güncellenmesi gereken sayacAktif değişkeninin derleyici tarafından optimize edilip atlanmasını önlemek için volatile belirteci ile tanımlanması kritik bir programlama kuralıdır:
unsigned long simdikiZaman = millis();
unsigned long sonTetiklenmeZamani = 0;
volatile bool sayacAktif = false;setup() fonksiyonunda seri haberleşmeyi başlatıp, giriş pinimizi donanımsal gürültüleri engellemek adına INPUT_PULLUP olarak yapılandırıyoruz. Ardından kesme atamasını gerçekleştiriyoruz:
attachInterrupt(digitalPinToInterrupt(hareketSensorPin), hareketISR, RISING);PIR sensörü hareket algıladığı anda (RISING yani 3.3V yükselişi) donanım seviyesinde hareketISR() fonksiyonu anında tetiklenir. Fonksiyon içerisinde çıkıştaki LED aktif edilir, sayaç durum bayrağı true yapılır ve son tetiklenme zamanı millis() ile güncellenir:
ICACHE_RAM_ATTR void hareketISR() {
digitalWrite(ledPin, HIGH);
sayacAktif = true;
sonTetiklenmeZamani = millis();
}Ana program akışında (loop) işlemci hiçbir şekilde bekletilmez. Sadece sayacAktif bayrağının aktifliği ve geçen süre kontrol edilir. Hedeflenen süre dolduğu anda LED kapatılarak bayrak sıfırlanır:
if (sayacAktif && (simdikiZaman - sonTetiklenmeZamani > (sureSaniye * 1000))) {
Serial.println("Hareket sonlandı. LED kapatılıyor.");
digitalWrite(ledPin, LOW);
sayacAktif = false;
}Uygulama Testi ve Sonuçlar
Hazırladığınız kodu Arduino IDE aracılığıyla kartınıza yükleyin. Seri Monitör ekranını 115200 baud hızında açarak sistem durumunu izleyin:

Sensörün önünde elinizi gezdirdiğiniz anda LED anında aktif olacak ve ekrana “Hareket Tespit Edildi!!!” yazısı düşecektir. Belirlenen 10 saniyelik süre tamamlandığında ise LED sönerek sistem yeni hareketler algılamak üzere beklemeye başlayacaktır:

Genel Değerlendirme
Bu rehberimizde, Arduino IDE ve ESP8266 mikrodenetleyici donanımı kullanarak kesmelerin ve bloklamasız zamanlayıcıların (millis) asenkron olarak nasıl yapılandırılacağını kararlı bir sensör uygulamasıyla birlikte inceledik. Bu olay güdümlü yazılım mimarisi, gömülü sistemlerinizde donanım kaynaklarının son derece kararlı ve verimli kullanılmasını sağlayacaktır.
Resmi esp8266 veri sayfasına buradan ulaşabilirsiniz.
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.

