2026 – 2027 Eğitim Dönemi erken kayıt dönemi başladı. Birebir eğitim programlarımız 21 Eylül 2026 tarihinde başlıyor.
Berk Akademi
Ana Sayfa

Arduino’da delay() Yerine millis() Kullanımı: Çoklu Görev Mantığı

arduino-delay-yerine-millis-kullanimi
Bu yazıda neler var?
  1. millis() ile delay() arasındaki temel fark nedir?
  2. Neden millis() kullanırken unsigned long tercih edilir?
  3. İki bağımsız görevi planlama ve hatayı teşhis etme adımları
  4. Çalışan millis() örneği: LED ve sayaç aynı loop() içinde
  5. delay() ne zaman kullanılabilir, ne zaman millis() seçilmelidir?
  6. Sık Sorulan Sorular

Arduino’da delay() yerine millis() kullanmak, programın belirli bir süre boyunca tamamen beklemesi yerine geçen zamanı kontrol ederek başka görevleri de aynı loop() içinde sürdürebilmesini sağlar. delay() çağrısı akışı duraklatır; millis() ise programı durdurmadan zaman bilgisini okur.

Bu nedenle tek bir kısa bekleme yeterliyse delay() pratik olabilir. LED, buton, sensör ve Serial Monitor gibi birden fazla iş aynı anda yürütülecekse millis() daha esnek bir zamanlama yaklaşımı sunar. Bu rehberde kavramı, eğitmenimiz Berk Keskin’in benimsediği “koddan önce düşünme” yaklaşımıyla görevleri parçalara ayırarak ele alacağız.

millis() ile delay() arasındaki temel fark nedir?

Önce iki görevi ve zaman aralıklarını bir zaman çizelgesine yerleştirelim:

Zaman Gerçekleşen olay
0 ms Program başlar, görevlerin başlangıç zamanları kaydedilir.
500 ms LED durumu değiştirilir.
1000 ms LED yeniden değiştirilir ve sayaç Serial Monitor’a yazdırılır.
1500 ms LED tekrar değiştirilir.

Aynı planı görev tablosu şeklinde de düşünebiliriz:

Görev Çalışma aralığı Yapılan işlem
LED görevi Her 500 ms LED açma-kapama durumunu değiştirir.
Sayaç görevi Her 1000 ms Geçen süreyi veya sayaç değerini Serial Monitor’a gönderir.

delay(500) yazıldığında Arduino, bu çağrıdan sonra yaklaşık 500 milisaniye boyunca bekler. Bu sırada loop() içindeki sonraki kodlara geçilmez. Örneğin aynı akışta bir buton kontrolü varsa, butonun okunması da bu bekleme süresince gecikebilir.

millis() ise programın başlangıcından beri geçen süreyi okumaya yarar. Program beklemek yerine zamanı kontrol eder ve görev için belirlediğimiz aralık dolduğunda ilgili işlemi çalıştırır. Temel karar çerçevesi şöyledir:

  1. Yapılacak görevleri listele.
  2. Her görevin hangi aralıkla çalışacağını belirle.
  3. Her görev için ayrı bir önceki zaman değişkeni oluştur.
  4. Her görevi kendi zaman koşuluyla kontrol et.

Böylece LED görevi 500 milisaniyelik ritmini korurken sayaç görevi 1000 milisaniyelik ritminde çalışabilir. Görevlerin birbirinin zamanını ezmemesi için her biri ayrı zaman değişkeni kullanmalıdır.

Neden millis() kullanırken unsigned long tercih edilir?

Neden millis() kullanırken unsigned long tercih edilir?

millis() bir zaman sayacı gibi düşünülebilir. Bu sayaçtan okunan değeri saklamak için genellikle unsigned long kullanılır. Bu veri türü, negatif değerlere ihtiyaç duyulmayan zaman ölçümlerinde geniş bir pozitif değer aralığı sağlar.

Zaman kontrolünün başlangıç seviyesinde anlaşılması kolay ve güvenilir biçimi şudur:

unsigned long now = millis();

if (now - previousTime >= interval) {
  previousTime = now;
  // Görev burada çalışır
}

Burada now mevcut zamanı, previousTime görevin son çalıştığı zamanı, interval ise beklenmesi gereken süreyi temsil eder. Çıkarma işlemi, “son çalışmadan bu yana ne kadar zaman geçti?” sorusunu cevaplar.

özellik delay() millis() çoklu görev etkisi
Çalışma biçimi Akışı bekletir. Zamanı okur. millis() birden fazla göreve daha uygundur.
Kullanım kolaylığı Başlangıçta daha basittir. Değişken ve koşul planı ister. Görevleri ayrı takip etmeyi sağlar.

Yeni başlayanların sık yaptığı üç hata vardır:

  • Yanlış veri türü: Zaman değerini çok küçük bir türde saklamak, değer büyüdükçe beklenmeyen sonuçlara yol açabilir.
  • Eşitlik kontrolü: if (now == previousTime + interval) koşulu risklidir. loop() tam o milisaniyede çalışmazsa koşul kaçırılır. Bu yüzden >= kullanılır.
  • Hatalı güncelleme: previousTime = millis(); satırını koşulun dışında ve her döngüde çalıştırmak, geçen süreyi sürekli sıfırlar. Zaman değişkeni yalnızca görev gerçekten çalıştığında güncellenmelidir.

Teşhis için şu sırayı izleyin:

  1. Her görevin ayrı bir previousTime değişkeni var mı?
  2. Karşılaştırmada >= kullanılıyor mu?
  3. previousTime yalnızca koşul sağlandığında güncelleniyor mu?
  4. Serial Monitor çıktısı beklenen aralıkta mı?

İki bağımsız görevi planlama ve hatayı teşhis etme adımları

İki bağımsız görevi planlama ve hatayı teşhis etme adımları

Arduino’da iki görevi aynı loop() içinde çalıştırmanın en güvenli yolu, her görevi kendi zaman aralığı ve kendi “son çalışma zamanı” değişkeniyle takip etmektir. Örneğin LED için 500 milisaniyelik, Serial Monitor sayacı için 1000 milisaniyelik ayrı bir plan oluşturulur. Eğitmenimiz Berk Keskin’in koddan önce düşünmeyi merkeze alan yaklaşımında olduğu gibi burada da önce görevleri ve zaman çizelgesini netleştirmek, ardından kod yazmak hata ayıklamayı kolaylaştırır.

Görev Aralık Zaman değişkeni Koddaki kontrol
LED durumunu değiştirme 500 ms previousLed now - previousLed >= ledInterval
Sayaç yazdırma 1000 ms previousSerial now - previousSerial >= serialInterval

Bu tablo doğrudan kod yapısına çevrilir: LED aralığı → previousLed, sayaç aralığı → previousSerial. Döngünün başında bir kez unsigned long now = millis(); alınır. Ardından her görev, birbirinden bağımsız if bloklarında kontrol edilir. Bir görev çalıştığında yalnızca o görevin zaman değişkeni güncellenmelidir.

  1. Görevleri listeleyin: LED değiştirme, sensör okuma veya seri çıktı verme gibi işleri birbirinden ayırın.
  2. Her görev için interval belirleyin: LED için 500, sayaç için 1000 gibi milisaniye cinsinden sabitler tanımlayın.
  3. Ayrı zaman değişkenleri kullanın: İki görevi tek bir previous değişkeniyle takip etmeyin.
  4. Tek bir “şimdi” değeri alın: millis() değerini döngünün başında okuyun.
  5. Çıkarma yöntemiyle karşılaştırın: now - previousLed >= ledInterval biçimi, zaman sayacı taşma yaptığında daha güvenli bir yaklaşımdır.
  6. Doğru değişkeni güncelleyin: LED çalıştıysa previousLed, sayaç çalıştıysa previousSerial güncellenmelidir.

Hata teşhisinde şu belirti → muhtemel hata → kontrol sırasını kullanabilirsiniz:

  • LED yanıp sönmüyorpreviousLed yanlış güncelleniyor veya LED pini doğru yapılandırılmamış olabilir → pinMode, digitalWrite ve LED görevindeki zaman değişkenini kontrol edin.
  • Serial Monitor çıktısı duruyorSerial.begin() eksik olabilir veya sayaç bloğu hiç çalışmıyordur → seri başlatmayı ve now - previousSerial karşılaştırmasını inceleyin.
  • Görevler aynı anda çalışıyor → Aralıkların ortak bir zamana denk gelmesi mümkündür; bu tek başına hata değildir → Her görevin gerçekten ayrı değişkenle takip edildiğini kontrol edin.
  • Görev yalnızca bir kez çalışıyor → Zaman değişkeni yanlış yerde güncelleniyor veya eşitlik kontrolü kullanılıyordur → == yerine >= kullanın ve güncellemeyi ilgili if bloğunun içine taşıyın.
  • Zaman hesabı beklenmedik davranıyorint gibi küçük veya işaretsiz olmayan bir veri türü seçilmiş olabilir → Zaman değişkenlerinde unsigned long kullanın.

Çalışan millis() örneği: LED ve sayaç aynı loop() içinde

Aşağıdaki örnekte LED yaklaşık her 500 milisaniyede bir durum değiştirir; sayaç ise yaklaşık her 1000 milisaniyede bir Serial Monitor’a yazdırılır. Kodda delay() bulunmadığı için iki görev aynı döngü içinde birbirini bekletmez.

unsigned long previousLed = 0;
unsigned long previousSerial = 0;
const unsigned long ledInterval = 500;
const unsigned long serialInterval = 1000;
bool ledState = false;
int counter = 0;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(9600);
}

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

  if (now - previousLed >= ledInterval) {
    previousLed = now;
    ledState = !ledState;
    digitalWrite(LED_BUILTIN, ledState);
  }

  if (now - previousSerial >= serialInterval) {
    previousSerial = now;
    Serial.print("Sayaç: ");
    Serial.println(counter++);
  }
}
  • ledInterval ve previousLed, LED görevinin zaman çizelgesini temsil eder.
  • serialInterval ve previousSerial, sayaç görevinin bağımsız zaman takibidir.
  • now yalnızca bir kez alınır; iki if bloğu aynı zaman değerini kullanır.
  • now - previous yaklaşımı ve >= kontrolü, döngünün tam milisaniyede çalışmaması durumunda görevin kaçırılmasını önlemeye yardımcı olur.

Serial Monitor’da başlangıç anına bağlı olarak Sayaç: 0, Sayaç: 1, Sayaç: 2 şeklinde ilerleyen çıktılar görülür. LED’in durumu yaklaşık yarım saniyede bir değişir. LED_BUILTIN ifadesi seçilen kartın yerleşik LED tanımını kullanır; kartınızda bu tanım veya LED’in aktif çalışma mantığı farklıysa kartın pin dokümantasyonunu kontrol ederek uygun pini ve lojik seviyeyi seçin.

delay() ne zaman kullanılabilir, ne zaman millis() seçilmelidir?

delay() tamamen kaçınılması gereken bir komut değildir. Programın yalnızca tek bir işi yaptığı, bekleme süresinin kısa olduğu ve bu sırada başka bir girişin kontrol edilmesine ihtiyaç duyulmadığı basit uygulamalarda okunabilir bir seçenek olabilir.

Örneğin başlangıç seviyesindeki bir LED yakıp söndürme denemesinde, LED’in durumunu değiştirdikten sonra kısa süre beklemek amacıyla delay() kullanılabilir. Ancak aynı loop() içinde LED’i yönetmek, butonu okumak, sensörü kontrol etmek ve Serial Monitor’e bilgi göndermek istiyorsanız durum değişir. delay() çalışırken program bu görevlerin hiçbirine geçemez.

Senaryo Uygun yaklaşım Neden?
Tek LED’in basit biçimde yanıp sönmesi delay() düşünülebilir Bekleme sırasında kontrol edilecek başka görev yoktur.
LED çalışırken butonun da okunması millis() Butonun bekleme boyunca kontrol edilmeye devam etmesi gerekir.
Sensör ölçümü ve Serial Monitor çıktısı millis() Her görev için ayrı zaman aralığı tanımlanabilir.

Somut bir senaryo düşünelim: LED’i bir saniye aralıklarla değiştiren kodda delay(1000) kullanılıyorsa program, bir saniye boyunca butonu okuyamaz. Kullanıcı bu aralıkta butona kısa süre basıp bırakırsa giriş gözden kaçabilir. millis() yaklaşımında ise LED’in zamanı takip edilirken loop() dönmeye devam eder ve buton sürekli kontrol edilebilir.

Karar verirken şu adımları izleyebilirsiniz:

  1. Bekleme sırasında başka bir görevin çalışması gerekip gerekmediğini belirleyin.
  2. Buton gibi kısa süreli değişimlerin kaçırılıp kaçırılmayacağını düşünün.
  3. Birden fazla görevin farklı aralıklarla çalışıp çalışmayacağını kontrol edin.
  4. Başka işler devam edecekse millis(), beklemek sorun oluşturmuyorsa ve süre kısaysa delay() seçeneğini değerlendirin.

Kısacası temel karar sorusu şudur: “Beklerken başka bir işi kontrol etmem gerekiyor mu?” Cevap evetse millis() daha uygun bir zamanlama yaklaşımıdır. Benzer programlama ve algoritma konularını pekiştirmek için yazılım ve algoritma rehberleri arşivini inceleyebilirsiniz.

Sık Sorulan Sorular

millis() kullanırken neden delay() yazmamalıyım?

millis() kullanmak, kodun hiçbir yerinde delay() bulunamayacağı anlamına gelmez. Fakat uzun bir delay(), loop() akışını durdurduğu için millis tabanlı görevler de bu süre boyunca kontrol edilemez. Çoklu görev mantığını korumak istiyorsanız bloklayan beklemelerden kaçınmalısınız.

Arduino’da millis() için neden unsigned long kullanılır?

millis() tarafından döndürülen zaman değeriyle uyumlu olması ve geçen süre hesabının taşma durumunda da çıkarma yöntemiyle çalışabilmesi için zaman değişkenleri unsigned long olarak tanımlanır.

millis() ile iki farklı görevin zaman aralığı nasıl ayrı tutulur?

Her görev için ayrı bir önceki çalışma zamanı ve ayrı bir aralık değişkeni tutulur. Örneğin LED için oncekiLedZamani, Serial Monitor çıktısı için oncekiYazdirmaZamani kullanılabilir. Böylece görevler birbirini bekletmeden farklı aralıklarla ilerler.

millis() kodunda LED çalışıyor ama Serial Monitor neden güncellenmiyor?

Serial görevinin önceki zaman değişkeni yanlış güncellenmiş, zaman karşılaştırmasında == kullanılmış veya ilgili if bloğu LED koşulunun içine yerleştirilmiş olabilir. Her görevin zaman değişkenini ayırın, simdi - oncekiZaman >= aralik karşılaştırmasını kullanın ve Serial Monitor hızının Serial.begin() değeriyle eşleştiğini kontrol edin.

Özetle, basit beklemelerde delay(); programın aynı anda birden fazla işi takip etmesi gereken durumlarda ise millis() daha doğru seçimdir. Bu rehber eğitmenimiz Berk Keskin tarafından hazırlanmıştır.

Bu içerik aradığın cevabı verdi mi?
Yanıtın, hangi yazıları geliştirmemiz gerektiğini anlamamıza yardımcı olur.
Bu içeriğin üretilmesinde yapay zeka araçlarından destek alınmıştır.

Bu konudan sonra ne okuyabilirsin?

Tüm yazılar

İlgili Eğitimler

Berk Keskin — Yazılım Geliştirici ve Eğitmen
Yazar

Berk Keskin Kimdir?

Yazılıma 12 yaşında başladı; bugün öğrencinin seviyesine ve hedefine göre şekillenen sürdürülebilir öğrenme sistemleri tasarlıyor. 500'den fazla kişiye ezber değil, düşünerek kod yazmayı öğretti — Berk Akademi'de izlemeye değil üretmeye dayalı öğrenme kültürünü o kuruyor.

WhatsApp Hemen Ara