Arduino millis() ve delay(): Kodu Durdurmadan Zamanlama
Arduino millis() nedir, delay() neden programı durdurur? Blink without delay, aynı anda birden fazla iş, 49 günlük taşma, micros(), ESP32 ve Pico karşılıkları.
Arduino millis() fonksiyonu, kart çalışmaya başladığından beri geçen süreyi milisaniye cinsinden verir ve delay() kullanmadan zamanlama yapmanın temel yoludur. delay(1000) yazdığında Arduino bir saniye boyunca başka hiçbir şey yapmaz: butona basılsa fark etmez, sensör okunmaz, ekran güncellenmez. millis() ile ise "son işten bu yana ne kadar zaman geçti?" diye sorarsın; vakti gelen işi yaparsın, gelmeyeni atlarsın ve loop() hiç durmadan döner. Böylece tek bir Arduino aynı anda LED yakıp söndürebilir, buton okuyabilir, sensör ölçebilir ve seri porta rapor gönderebilir.
Bu rehberde delay() fonksiyonunun neden programı kilitlediğini, ünlü blink without delay (delay olmadan yanıp sönme) örneğini, birden fazla işi aynı anda yürütmeyi, 49,7 gün sonra yaşanan taşmayı (overflow) güvenle atlatmayı, mikrosaniye ölçen micros() fonksiyonunu ve tekrar kullanılabilir zamanlayıcı desenlerini öğreneceksin. Son bölümlerde aynı mantığın ESP32'de ve Raspberry Pi Pico'da (MicroPython) nasıl yazıldığını da göreceğiz.

delay() nedir, neden programı durdurur?
delay(ms), verdiğin süre dolana kadar işlemciyi bir döngüde bekletir. Bu süre boyunca loop() içindeki bir sonraki satıra geçilmez; buna bloklayan (engelleyen) bekleme denir. Tek bir LED'i yakıp söndürmek için sorun yoktur, ama projeye bir buton eklediğin anda sorun başlar. Aşağıdaki kodda LED saniyede bir yanıp sönerken butona basılıp basılmadığına da bakılıyor:
1const int LED_PIN = 13; // dahili LED / built-in LED
2const int BUTON_PIN = 2; // buton, diğer ucu GND / button, other leg to GND
3
4void setup() {
5 pinMode(LED_PIN, OUTPUT);
6 pinMode(BUTON_PIN, INPUT_PULLUP);
7 Serial.begin(9600);
8}
9
10void loop() {
11 digitalWrite(LED_PIN, HIGH);
12 delay(1000); // 1 sn bekle, buton okunmaz / wait 1 s, button is not read
13 digitalWrite(LED_PIN, LOW);
14 delay(1000);
15 if (digitalRead(BUTON_PIN) == LOW) { // 2 saniyede bir bakılır / checked once every 2 s
16 Serial.println(F("Butona basildi / Button pressed"));
17 }
18}Bu kodda buton yalnızca iki saniyede bir ve ancak tam o anda basılı tutuluyorsa algılanır; kısa bir basış iki delay() arasında kaybolur. Kesmeler (interrupt) delay() sırasında da çalışmaya devam eder: millis() saymayı sürdürür, seri porttan gelen baytlar 64 baytlık tampona yazılır. Ama senin kodun bekler. Projeye ekran, sensör ve buzzer ekledikçe her delay() diğer bütün işleri geciktirir ve cihaz hantal hissettirir. Çözüm, beklemeyi bırakıp zamanı ölçmektir.
millis() nedir, nasıl çalışır?
millis(), kart açıldığından ya da resetlendiğinden beri geçen milisaniyeleri unsigned long (32 bit, işaretsiz) türünde döndürür. Arduino Uno'da bu sayaç arka planda Timer0 donanım zamanlayıcısının taşma kesmesiyle güncellenir. 16 MHz'te Timer0 her 1,024 ms'de bir taşar; çekirdek kod aradaki 0,024 ms'lik farkı biriktirir ve yaklaşık her 43 ms'de bir sayacı 1 yerine 2 artırır. Yani millis() bazı değerleri hiç göstermez; bu yüzden millis() == 5000 gibi eşitlik kontrolleri güvenilmezdir, her zaman >= kullanılır. Ayrıca millis(), kesmeler kapalıyken (örneğin bir kesme fonksiyonunun içinde) ilerlemez.
| Fonksiyon | Ne yapar? | Çözünürlük (Uno, 16 MHz) | Taşma süresi | Programı durdurur mu? |
|---|---|---|---|---|
| delay(ms) | Verilen milisaniye kadar bekler | 1 ms | - | Evet |
| delayMicroseconds(us) | Kısa süre bekler (3–16383 µs arasında doğru) | Yaklaşık 1 µs | - | Evet |
| millis() | Açılıştan beri geçen milisaniye | 1 ms (ara ara 2 artar) | Yaklaşık 49,7 gün | Hayır |
| micros() | Açılıştan beri geçen mikrosaniye | 4 µs | Yaklaşık 71,6 dakika | Hayır |
Blink without delay: millis() ile LED yakıp söndürmek
- Son işin yapıldığı anı tutmak için unsigned long türünde bir değişken oluştur (oncekiZaman).
- Her loop() turunda millis() ile şu anki zamanı al.
- simdi - oncekiZaman >= ARALIK koşuluyla yeterli süre geçip geçmediğine bak.
- Süre dolduysa işi yap (LED'i ters çevir) ve oncekiZaman değişkenini güncelle.
- Süre dolmadıysa hiçbir şey yapma, loop() devam etsin; diğer işler tam bu sırada çalışır.
1const int LED_PIN = LED_BUILTIN; // Uno'da 13 / pin 13 on the Uno
2const unsigned long ARALIK = 500; // yanıp sönme aralığı (ms) / blink interval (ms)
3
4unsigned long oncekiZaman = 0; // son değişim anı / time of the last toggle
5bool ledDurumu = false;
6
7void setup() {
8 pinMode(LED_PIN, OUTPUT);
9}
10
11void loop() {
12 unsigned long simdi = millis(); // şu anki zaman / current time
13 if (simdi - oncekiZaman >= ARALIK) { // taşmaya karşı güvenli / overflow-safe
14 oncekiZaman += ARALIK; // kaymayı önler / prevents drift
15 ledDurumu = !ledDurumu;
16 digitalWrite(LED_PIN, ledDurumu);
17 }
18 // burada başka işler beklemeden çalışır / other work runs here without waiting
19}Kodda oncekiZaman = simdi yerine oncekiZaman += ARALIK yazdık. Fark küçük ama önemli: loop() bazen birkaç milisaniye geç kalırsa ilk yöntem bu gecikmeyi her seferinde biriktirir ve LED saatler içinde kayar; ikinci yöntem ise hedef zamanı sabit adımlarla ilerletir, ortalama periyot tam 500 ms kalır. Saat, metronom ya da düzenli ölçüm gibi işlerde += kullan. Program uzun süre bloklanabiliyorsa (örneğin Wi-Fi bağlantısını beklerken) += kaçırılan adımları arka arkaya yakalamaya çalışır; böyle durumlarda = simdi daha güvenlidir. LED'in parlaklığını da kodu durdurmadan değiştirmek istersen PWM ve analogWrite yazısına göz at.
Aynı anda birden fazla iş: millis() ile çoklu görev
Arduino Uno tek çekirdekli bir işlemcidir ve gerçek anlamda iki işi aynı anda yapamaz. Ama loop() saniyede on binlerce kez dönebildiği için, her işe sırası geldiğinde birkaç mikrosaniye ayırmak dışarıdan bakınca "aynı anda" görünür. Buna işbirlikçi çoklu görev (cooperative multitasking) denir. Kural basit: her görevin kendi zaman değişkeni ve kendi aralığı olur, hiçbir görev uzun süre beklemez. Aşağıdaki örnekte dört görev bir arada çalışıyor:
1const int KIRMIZI_LED = 8; // 300 ms'de bir yanıp söner / blinks every 300 ms
2const int YESIL_LED = 9; // 1000 ms'de bir yanıp söner / blinks every 1000 ms
3const int SARI_LED = 10; // butonla açılıp kapanır / toggled by the button
4const int BUTON = 2; // diğer ucu GND / other leg to GND
5
6unsigned long kirmiziZaman = 0, yesilZaman = 0, butonZaman = 0, seriZaman = 0;
7bool kirmizi = false, yesil = false, sari = false;
8int sonOkuma = HIGH; // son ham okuma / last raw reading
9int kararliDurum = HIGH; // titreşimsiz durum / debounced state
10
11void setup() {
12 pinMode(KIRMIZI_LED, OUTPUT);
13 pinMode(YESIL_LED, OUTPUT);
14 pinMode(SARI_LED, OUTPUT);
15 pinMode(BUTON, INPUT_PULLUP);
16 Serial.begin(9600);
17}
18
19void loop() {
20 unsigned long simdi = millis();
21
22 // Görev 1: kırmızı LED / Task 1: red LED
23 if (simdi - kirmiziZaman >= 300) {
24 kirmiziZaman = simdi;
25 kirmizi = !kirmizi;
26 digitalWrite(KIRMIZI_LED, kirmizi);
27 }
28
29 // Görev 2: yeşil LED / Task 2: green LED
30 if (simdi - yesilZaman >= 1000) {
31 yesilZaman = simdi;
32 yesil = !yesil;
33 digitalWrite(YESIL_LED, yesil);
34 }
35
36 // Görev 3: 50 ms debounce ile buton / Task 3: button with 50 ms debounce
37 int okuma = digitalRead(BUTON);
38 if (okuma != sonOkuma) { // değişti, sayacı yeniden başlat / changed, restart the timer
39 butonZaman = simdi;
40 sonOkuma = okuma;
41 }
42 if (simdi - butonZaman >= 50 && okuma != kararliDurum) {
43 kararliDurum = okuma;
44 if (kararliDurum == LOW) { // basıldı / pressed
45 sari = !sari;
46 digitalWrite(SARI_LED, sari);
47 }
48 }
49
50 // Görev 4: saniyede bir seri rapor / Task 4: serial report once per second
51 if (simdi - seriZaman >= 1000) {
52 seriZaman = simdi;
53 Serial.print(F("Calisma suresi (s) / Uptime (s): "));
54 Serial.println(simdi / 1000);
55 }
56}| Görev | Pin | Aralık | Ne yapar? |
|---|---|---|---|
| Kırmızı LED | 8 | 300 ms | Hızlı yanıp söner |
| Yeşil LED | 9 | 1000 ms | Yavaş yanıp söner |
| Buton + sarı LED | 2 ve 10 | 50 ms debounce | Her basışta sarı LED'i açar ya da kapatır |
| Seri rapor | - | 1000 ms | Çalışma süresini saniye olarak yazar |
Butonda kullanılan yöntem de millis() tabanlıdır: ham okuma her değiştiğinde sayaç yeniden başlar ve okuma 50 ms boyunca sabit kalırsa basış gerçek kabul edilir. Böylece butonun mekanik titreşimi (bouncing) tek basışı onlarca basış gibi göstermez ve kod hiç beklemez. Buton bağlantısı ve dahili pull-up direnci hakkında ayrıntı için Arduino'ya Buton Nasıl Bağlanır? yazısına bakabilirsin. LED'ler için 220 Ω direnç yeterlidir; farklı renk ve akımlar için LED direnci hesaplayıcı aracını kullan.
49,7 gün taşma (overflow) sorunu ve güvenli karşılaştırma
unsigned long en fazla 4.294.967.295 değerini tutabilir. millis() bu sayıya ulaşınca, yani yaklaşık 49 gün 17 saat 2 dakika sonra, yeniden 0'dan saymaya başlar. Kart haftalarca açık kalacaksa (sera kontrolü, akvaryum, alarm sistemi) bu an mutlaka gelir. İyi haber şu: simdi - onceki >= aralik biçiminde, çıkarma ile yazılmış kod taşmadan etkilenmez. İşaretsiz sayılarda çıkarma 2 üzeri 32'ye göre (modüler) yapılır. Örneğin onceki = 4.294.967.000 iken taşmadan sonra simdi = 200 olursa simdi - onceki işlemi eksi bir sayı vermez, doğru sonuç olan 496'yı verir. Bu nedenle beklediğin aralık 49,7 günden kısa olduğu sürece karşılaştırma her zaman doğru çalışır.
| Yazım | Sonuç | Neden? |
|---|---|---|
| if (millis() - onceki >= aralik) | Doğru | İşaretsiz çıkarma, taşma anında da doğru farkı verir |
| if (millis() >= onceki + aralik) | Hatalı | onceki + aralik taşınca küçük bir sayıya döner, koşul erken doğru olur |
| if (millis() == hedef) | Hatalı | millis() bazen 2 artar, tam değer hiç görülmeyebilir |
| int onceki = millis(); | Hatalı | Uno'da int 16 bittir, 32,7 saniyeden sonra yanlış değer tutar |
| long onceki = millis(); | Riskli | İşaretli tür 24,8 gün sonra eksiye döner; karışıklığı önlemek için unsigned long kullan |
micros() ile mikrosaniye ölçmek
micros(), aynı mantıkla açılıştan beri geçen mikrosaniyeleri verir. Arduino Uno ve Nano gibi 16 MHz kartlarda çözünürlüğü 4 µs, 8 MHz kartlarda 8 µs'dir ve yaklaşık 71,6 dakikada bir taşar; çıkarma kuralı burada da geçerlidir. Bir kod parçasının ne kadar sürdüğünü ölçmek, kısa darbeleri zamanlamak ya da ses üretmek gibi hassas işlerde kullanılır. Aşağıdaki örnek, yarım saniyede bir analogRead() çağrısının süresini ölçer; Uno'da yaklaşık 110–120 µs görürsün:
1unsigned long raporZaman = 0;
2
3void setup() {
4 Serial.begin(9600);
5}
6
7void loop() {
8 if (millis() - raporZaman >= 500) { // yarım saniyede bir ölç / measure every half second
9 raporZaman = millis();
10 unsigned long basla = micros(); // başlangıç (µs) / start (µs)
11 int deger = analogRead(A0);
12 unsigned long sure = micros() - basla; // geçen süre (µs) / elapsed time (µs)
13 Serial.print(F("A0 = "));
14 Serial.print(deger);
15 Serial.print(F(" sure / time: "));
16 Serial.print(sure);
17 Serial.println(F(" us"));
18 }
19}Daha uzun aralıklarda micros() yerine millis() tercih et; 71 dakikalık taşma süresi uzun beklemeler için kısa kalır. delayMicroseconds() ise yalnızca birkaç mikrosaniyelik, kaçınılmaz beklemeler içindir: örneğin HC-SR04 mesafe sensörünün TRIG pinine 10 µs'lik tetik darbesi göndermek gibi. Böyle kısa beklemeler diğer görevleri fark edilir biçimde geciktirmez.
Zamanlayıcı desenleri: periyodik, tek seferlik ve durum makinesi
- Periyodik görev: Belirli aralıklarla tekrar eden iş; yanıp sönen LED, 2 saniyede bir sensör okuma, saniyede bir ekran yenileme. Kalıbını yukarıda gördün.
- Tek seferlik zamanlayıcı (timeout): Bir olaydan belirli süre sonra bir kez çalışır. Merdiven ışığı iyi bir örnektir: hareket olunca ışığı yak, zamanı kaydet, 30 saniye boyunca yeni hareket gelmezse söndür. Yeni hareket geldiğinde zamanı yenilemek süreyi uzatır.
- Zaman aşımı korumalı bekleme: Bir cevabı ya da sinyali beklerken sonsuza kadar takılmamak için bir üst sınır koyarsın; örneğin 2 saniye içinde veri gelmezse hata mesajı gösterirsin.
- Durum makinesi (state machine): Cihazın bulunduğu adımı bir değişkende tutarsın, her adımın süresi dolunca bir sonrakine geçersin. Trafik lambası, çamaşır makinesi programı ya da bir oyunun seviyeleri böyle yazılır.
- Uzun basış algılama: Butonun ne kadar süre basılı tutulduğunu millis() ile ölçerek kısa ve uzun basışa farklı görevler verebilirsin.
1// Yeniden kullanılabilir zamanlayıcı / Reusable timer
2struct Zamanlayici {
3 unsigned long baslangic = 0;
4 unsigned long sure = 0;
5
6 void kur(unsigned long ms) { // süreyi şimdiden başlat / start the period from now
7 baslangic = millis();
8 sure = ms;
9 }
10 bool doldu() const { // süre doldu mu? / has it expired?
11 return millis() - baslangic >= sure;
12 }
13};
14
15const int KIRMIZI = 11, SARI = 12, YESIL = 13;
16enum Durum { DUR, HAZIRLAN, GEC, YAVASLA }; // trafik lambası adımları / traffic light states
17Durum durum = DUR;
18Zamanlayici isik;
19
20void lambalar(bool k, bool s, bool y) {
21 digitalWrite(KIRMIZI, k);
22 digitalWrite(SARI, s);
23 digitalWrite(YESIL, y);
24}
25
26void setup() {
27 pinMode(KIRMIZI, OUTPUT);
28 pinMode(SARI, OUTPUT);
29 pinMode(YESIL, OUTPUT);
30 lambalar(true, false, false);
31 isik.kur(5000); // kırmızı 5 sn / red for 5 s
32}
33
34void loop() {
35 if (isik.doldu()) {
36 switch (durum) {
37 case DUR: durum = HAZIRLAN; lambalar(true, true, false); isik.kur(1500); break;
38 case HAZIRLAN: durum = GEC; lambalar(false, false, true); isik.kur(5000); break;
39 case GEC: durum = YAVASLA; lambalar(false, true, false); isik.kur(2000); break;
40 case YAVASLA: durum = DUR; lambalar(true, false, false); isik.kur(5000); break;
41 }
42 }
43 // loop serbest: buton, sensör, ekran... / loop stays free: buttons, sensors, display...
44}Bu kodda Zamanlayici adlı küçük bir yapı (struct), süreyi başlatmayı ve dolup dolmadığını sormayı tek satıra indiriyor. Trafik lambası dört adımdan oluşan bir durum makinesi: her adımda lambalar ayarlanıyor ve bir sonraki geçişin süresi kuruluyor. loop() hiç beklemediği için aynı karta bir yaya butonu, gece modu için bir LDR ışık sensörü ya da kalan süreyi gösteren bir ekran rahatça eklenebilir. Çok sayıda zamanlayıcı kullanacaksan Kütüphane Yöneticisi'ndeki hazır zamanlayıcı kütüphaneleri de aynı ilkeyle çalışır.
ESP32'de millis(), delay() ve Ticker
ESP32'nin Arduino çekirdeğinde millis() ve micros() aynı adla ve aynı 32 bitlik unsigned long türüyle çalışır; değerler 64 bitlik esp_timer_get_time() sayacından hesaplanır. Bu yüzden 49,7 gün ve 71,6 dakikalık taşma süreleri ESP32'de de geçerlidir ve aynı çıkarma kuralını kullanırsın. Önemli fark delay() tarafındadır: ESP32'de delay(), FreeRTOS'un vTaskDelay() fonksiyonunu çağırır. İşlemci boşa dönmez, bekleme süresince Wi-Fi ve Bluetooth gibi diğer görevler çalışır. Yine de senin loop() fonksiyonun bekler; web sunucusu, MQTT ya da buton okuma gibi işler gecikir. Periyodik işler için ESP32 çekirdeğiyle gelen Ticker kütüphanesi de kullanışlıdır:
1#include <Ticker.h>
2
3const int LED1 = 2; // çoğu DevKit'te dahili LED / built-in LED on most DevKits
4const int LED2 = 4;
5
6Ticker ticker;
7bool led1Durum = false;
8bool led2Durum = false;
9unsigned long oncekiZaman = 0;
10
11void led1Degistir() { // kısa tut, uzun iş yapma / keep it short, no long work
12 led1Durum = !led1Durum;
13 digitalWrite(LED1, led1Durum);
14}
15
16void setup() {
17 Serial.begin(115200);
18 pinMode(LED1, OUTPUT);
19 pinMode(LED2, OUTPUT);
20 ticker.attach_ms(250, led1Degistir); // her 250 ms'de çağır / call every 250 ms
21}
22
23void loop() {
24 unsigned long simdi = millis();
25 if (simdi - oncekiZaman >= 1000) { // Arduino ile aynı desen / same pattern as on Arduino
26 oncekiZaman = simdi;
27 led2Durum = !led2Durum;
28 digitalWrite(LED2, led2Durum);
29 int64_t us = esp_timer_get_time(); // 64 bit, pratikte taşmaz / 64-bit, practically never overflows
30 Serial.printf("millis=%lu esp_timer=%lld us\n", simdi, us);
31 }
32}Ticker'a verilen fonksiyon arka plandaki bir zamanlayıcı görevi tarafından çağrılır; içinde uzun süren işler (HTTP isteği, uzun seri çıktı, delay) yapma, yalnızca bir bayrak kaldır ya da pin değiştir. 64 bitlik esp_timer_get_time() yüz binlerce yıl taşmaz; aylarca açık kalacak ve mikrosaniye hassasiyeti gereken ölçümlerde onu kullanabilirsin. Dahili LED'in pini karta göre değişebilir; kendi kartının pin haritasını Pinout aracında görebilirsin. ESP32'ye yeni başlıyorsan ESP32 Nedir? yazısı kurulum ve pin seçimi için iyi bir başlangıç.
Raspberry Pi Pico (MicroPython): ticks_ms() ve ticks_diff()
MicroPython'da delay() yerine time.sleep(), time.sleep_ms() ve time.sleep_us(); millis() yerine time.ticks_ms() kullanılır. Ama dikkat: ticks değerleri Arduino'daki gibi 2 üzeri 32'de değil, porta göre değişen daha küçük bir sınırda başa döner. Pico'da bu sınır 2 üzeri 30 ms, yani yaklaşık 12,4 gündür (kendi kartında time.ticks_add(0, -1) ile görebilirsin). Bu yüzden iki ticks değerini asla doğrudan çıkarma ya da karşılaştırma; farkı time.ticks_diff(yeni, eski) ile al, ileri bir zaman hesaplamak için time.ticks_add() kullan. Aşağıdaki kod Pico'nun dahili LED'ini kodu durdurmadan yakıp söndürürken GP14'teki butonla GP15'teki LED'i kontrol eder:
1from machine import Pin
2import time
3
4led = Pin("LED", Pin.OUT) # dahili LED (Pico ve Pico W) / onboard LED (Pico and Pico W)
5dis_led = Pin(15, Pin.OUT) # GP15'e harici LED / external LED on GP15
6buton = Pin(14, Pin.IN, Pin.PULL_UP) # GP14 buton, diğer ucu GND / GP14 button, other leg to GND
7
8ARALIK = 500 # ms
9onceki = time.ticks_ms()
10son_okuma = 1 # son ham okuma / last raw reading
11kararli = 1 # titreşimsiz durum / debounced state
12degisim = time.ticks_ms() # son değişim anı / time of the last change
13
14while True:
15 simdi = time.ticks_ms()
16 if time.ticks_diff(simdi, onceki) >= ARALIK: # taşmaya karşı güvenli / wrap-safe
17 onceki = time.ticks_add(onceki, ARALIK) # kaymasız / drift-free
18 led.toggle()
19 okuma = buton.value()
20 if okuma != son_okuma: # değişti, sayacı yeniden başlat / changed, restart the timer
21 degisim = simdi
22 son_okuma = okuma
23 if time.ticks_diff(simdi, degisim) >= 50 and okuma != kararli: # 50 ms debounce
24 kararli = okuma
25 if kararli == 0: # basıldı / pressed
26 dis_led.toggle()
27Periyodik işler için MicroPython'da machine.Timer da vardır: Timer(period=500, mode=Timer.PERIODIC, callback=fonksiyon) satırı verdiğin fonksiyonu her 500 ms'de bir çağırır. Bu geri çağırma fonksiyonlarını da Ticker'daki gibi kısa tut. Pico'yu Arduino IDE ile (arduino-pico çekirdeği) programlıyorsan millis() ve micros() Arduino'daki gibi çalışır. Pico'ya MicroPython kurmak ve pinleri tanımak için Raspberry Pi Pico Nedir? yazısına bakabilirsin.
millis() ile proje fikirleri
- Reaksiyon testi: LED'i rastgele bir anda yak, butona basılana kadar geçen süreyi millis() ile ölç ve sonucu milisaniye olarak göster.
- Merdiven ışığı: PIR sensörü hareket algılayınca ışığı yak, son hareketten 30 saniye sonra söndür; bu süre boyunca kod diğer işlere devam etsin.
- Hızlanan park sensörü: HC-SR04 ile ölçülen mesafe kısaldıkça buzzer'ın bip aralığını kısalt.
- Bloklamayan melodi: Buzzer ile notaları delay() yerine millis() ile sırala; melodi çalarken butonlar da çalışsın.
- Sensör ve ekran birlikte: DHT11 sensörünü 2 saniyede bir oku, ekranı ise her 250 ms'de bir yenile.
- Kronometre: Başlat, durdur ve sıfırla butonlarıyla çalışan, süreyi bir 7 segment display üzerinde gösteren bir kronometre yap.
Sık yapılan hatalar ve çözümleri
| Hata | Belirti | Çözüm |
|---|---|---|
| Zaman değişkeni int türünde | Program yaklaşık 33 saniye sonra saçmalıyor | Tüm zaman değişkenlerini unsigned long yap |
| millis() >= onceki + aralik yazmak | 49,7 gün sonra zamanlama bozuluyor | millis() - onceki >= aralik biçimine geç |
| millis() == hedef kontrolü | Olay bazen hiç tetiklenmiyor | Eşitlik yerine >= kullan |
| onceki değişkenini güncellemeyi unutmak | Süre bir kez dolunca iş her turda tekrar ediyor | Koşulun içinde onceki = simdi ya da onceki += aralik yaz |
| Zamanlayıcı kodunun içinde delay() kalması | Diğer görevler yine gecikiyor | Uzun beklemeleri durum makinesine böl |
| Kesme fonksiyonunda millis() ile beklemek | Kart kilitleniyor | ISR içinde bekleme yapma, yalnızca bayrak kaldır |
| 60 * 1000 gibi int çarpımı | Aralık eksi ya da çok kısa çıkıyor | 60000UL ya da 60UL * 1000 yaz |
millis() mantığını bir kez kavradığında projelerin tamamen değişir: LED'ler, butonlar, sensörler ve ekranlar birbirini beklemeden birlikte çalışır. Bir sonraki adım için Arduino proje fikirleri listesinden zamanlama gerektiren bir proje seçebilirsin. BlueGrays uygulamasında Trafik Lambası, Polis Çakar, Akıllı Merdiven ve Koridor Aydınlatması ve Ses Uyarılı Reaksiyon Testi gibi projeleri adım adım devre şeması ve hazır koduyla inceleyip kendi kartında deneyebilirsin.
Sıkça sorulan sorular
Arduino millis() nedir?
millis(), Arduino'nun açıldığı ya da resetlendiği andan beri geçen süreyi milisaniye cinsinden veren fonksiyondur. Değeri unsigned long türündedir ve yaklaşık 49,7 gün sonra sıfırdan yeniden başlar. Programı durdurmadan zamanlama yapmak için kullanılır.
millis() ile delay() arasındaki fark nedir?
delay() verilen süre boyunca programı bekletir; bu sırada buton okuma ya da ekran güncelleme gibi işler yapılamaz. millis() ise yalnızca geçen süreyi söyler, beklemez. Geçen süreyi kontrol edip vakti gelen işi yaparak birden fazla işi aynı anda yürütebilirsin.
millis() 49 gün sonra sıfırlanınca program bozulur mu?
Zamanı millis() - onceki >= aralik biçiminde çıkarma ile kontrol ediyorsan bozulmaz; işaretsiz çıkarma taşma anında da doğru farkı verir. Sorun, millis() >= onceki + aralik gibi toplama ile yazılmış karşılaştırmalarda ve int türündeki değişkenlerde ortaya çıkar.
Arduino'da millis() sıfırlanabilir mi?
Resmî bir millis() sıfırlama fonksiyonu yoktur ve çekirdeğin iç değişkenini değiştirmek önerilmez. Buna gerek de yoktur: sıfırlamak istediğin anı bir değişkene kaydeder, geçen süreyi o andan itibaren hesaplarsın. Kartı resetlemek ise millis() değerini doğal olarak 0'dan başlatır.
Arduino aynı anda iki iş yapabilir mi?
Uno tek çekirdekli olduğu için iki komutu fiziksel olarak aynı anda çalıştıramaz, ama millis() ile her işi kısa adımlara bölersen loop() saniyede binlerce kez dönerek hepsine sıra verir. Dışarıdan bakınca LED, buton, sensör ve ekran aynı anda çalışıyormuş gibi görünür. ESP32'de ise iki çekirdek ve FreeRTOS görevleri de vardır.
millis() saat yapmak için yeterince doğru mu?
millis() kartın osilatörüne bağlıdır; Arduino Uno'daki seramik rezonatör yüzünden günde birkaç dakikaya varan sapmalar görülebilir. Kısa aralıkları ölçmek için yeterlidir, ama günlerce doğru kalması gereken bir saat için DS1307 ya da DS3231 gibi bir RTC modülü veya ESP32'de internetten NTP ile saat almak daha doğrudur.
Bu yazıdan sonra yapabileceğin projeler
Tüm projeleri gör
Led Yakma
Arduino ile tek bir LED'i belirli aralıklarla yakıp söndüren temel giriş projesi. digitalWrite ve delay fonksiyonlarının kullanımını öğretir.

Trafik Lambası
Kırmızı, sarı ve yeşil LED'lerle gerçek bir trafik lambası simülasyonu yapan proje. Zamanlama ve sıralı kontrol mantığını öğretir.

Polis Çakar
İki LED'i hızlı aralıklarla yanıp söndürerek polis çakar lambası efekti oluşturan proje. for döngüsü ve zamanlama kontrolünü öğretir.

Hız Ayarlı Yürüyen Led
Potansiyometre ile LED yürüme hızını gerçek zamanlı olarak ayarlayabilen proje. analogRead ile potansiyometre değerini okuyarak delay süresini dinamik kontrol etmeyi öğretir.

Buton İle Led Yakma (On Off)
Her buton basışında LED'in durumunu değiştiren (aç/kapa) toggle projesi. Durum değişkeni ve debounce mantığını öğretir.

Akıllı Merdiven ve Koridor Aydınlatması
Çift yönlü HC-SR04 mesafe sensörü ile hareket yönüne doğru sırayla yanan ve sönen şık bir otomasyon projesi.

Arduino Ses Uyarılı Reaksiyon Testi
Arduino Reaksiyon Test İstasyonu Bu proje, bir kullanıcının görsel ve işitsel uyaranlara karşı refleks hızını milisaniye (ms) hassasiyetinde ölçmek için geliştirilmiş dijital bir test düzeneğidir. Sistem; bir Arduino kontrolcü, üç aşamalı LED bildirim grubu, sesli uyarı için bir buzzer ve sonuçları anlık gösteren I2C LCD ekrandan oluşur. Çalışma Prensibi: Test başladığında sistem sırasıyla kırmızı ve sarı LED'leri yakarak kullanıcıyı hazırlar. Rastgele belirlenen bir süre sonunda yeşil LED'in y

4'lü 7 Segment Display İle Dijital Saat
DS1302 RTC modülü ve 4'lü 7 segment display ile saat ve dakikayı gösteren dijital saat projesi. Gerçek zamanlı saat verisi ile segment gösterimini birleştirir.
Diğer yazılar
delay() kullanınca butonun neden geç tepki verdiğini, millis() ile LED, buton ve sensörü aynı anda yönetmeyi, 49,7 günlük taşmaya karşı güvenli kod yazmayı ve ESP32 ile Pico'daki karşılıkları öğren.

