Dasar · 9 mnt baca

Arduino millis() vs delay(): Mengatur Waktu Tanpa Memblokir Kodemu

Pelajari millis() di Arduino: kenapa delay() membekukan sketch-mu, blink tanpa delay, beberapa tugas sekaligus, timing 49 hari yang aman dari overflow, micros(), ESP32, dan Pico.

Fungsi millis() di Arduino mengembalikan waktu sejak board mulai berjalan, dalam milidetik, dan merupakan cara standar untuk mengatur waktu tanpa delay(). Ketika kamu menulis delay(1000), Arduino tidak mengerjakan apa pun selama satu detik penuh: tekanan tombol tidak terdeteksi, sensor tidak dibaca, dan layar tidak diperbarui. Dengan millis(), kamu justru bertanya “sudah berapa lama sejak tugas terakhir?”; kamu menjalankan tugas yang sudah waktunya, melewati yang belum, dan loop() tidak pernah berhenti. Dengan begitu satu Arduino bisa mengedipkan LED, membaca tombol, mengukur sensor, dan mengirim laporan ke port serial sekaligus.

Di panduan ini kamu akan mempelajari kenapa delay() mengunci programmu, contoh terkenal blink tanpa delay, cara menjalankan beberapa tugas bersamaan, cara aman melewati overflow yang terjadi setelah 49,7 hari, fungsi micros() yang menghitung mikrodetik, dan pola timer yang bisa dipakai ulang. Di bagian terakhir kita lihat bagaimana logika yang sama ditulis di ESP32 dan di Raspberry Pi Pico (MicroPython).

Board Arduino Uno dan LED L bawaannya
Arduino Uno: berkat LED “L” bawaan di pin 13, kamu tidak butuh komponen tambahan untuk mencoba millis().

Apa itu delay() dan kenapa programmu berhenti?

delay(ms) membuat prosesor berputar di dalam loop sampai waktu yang kamu berikan habis. Selama waktu itu, baris berikutnya di loop() tidak dijangkau; inilah yang disebut penantian yang memblokir (blocking). Untuk mengedipkan satu LED itu tidak masalah, tetapi masalah dimulai begitu kamu menambahkan tombol ke proyek. Kode di bawah mengedipkan LED sekali per detik dan juga memeriksa apakah tombol ditekan:

C++ (Arduino IDE)
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}

Di kode ini, tombol hanya terdeteksi sekali setiap dua detik, dan itu pun hanya kalau kebetulan sedang ditahan tepat pada momen itu; tekanan singkat hilang di antara dua panggilan delay(). Interupsi tetap berjalan selama delay(): millis() terus menghitung dan byte yang masuk ke port serial disimpan di buffer 64 byte. Tetapi kodemu menunggu. Ketika kamu menambahkan layar, sensor, dan buzzer, setiap delay() menahan semua pekerjaan lain dan perangkat terasa lamban. Solusinya: berhenti menunggu dan mulai mengukur waktu.

Apa itu millis() dan bagaimana cara kerjanya?

millis() mengembalikan jumlah milidetik sejak board dinyalakan atau di-reset, sebagai unsigned long (32-bit, tanpa tanda). Di Arduino Uno, penghitung ini diperbarui di latar belakang oleh interupsi overflow dari timer hardware Timer0. Di 16 MHz, Timer0 overflow setiap 1,024 ms; kode core mengumpulkan kelebihan 0,024 ms itu dan, kira-kira setiap 43 ms, menaikkan penghitung sebesar 2, bukan 1. Jadi millis() melompati beberapa nilai sama sekali, karena itu pengecekan kesamaan seperti millis() == 5000 tidak bisa diandalkan; selalu pakai >=. Perhatikan juga bahwa millis() tidak bertambah selama interupsi dinonaktifkan (misalnya di dalam rutin interupsi).

FungsiKegunaannyaResolusi (Uno, 16 MHz)Overflow setelahMemblokir program?
delay(ms)Menunggu selama milidetik yang diberikan1 ms-Ya
delayMicroseconds(us)Penantian singkat (akurat dari 3 sampai 16383 µs)Sekitar 1 µs-Ya
millis()Milidetik sejak mulai1 ms (sesekali melompat 2)Sekitar 49,7 hariTidak
micros()Mikrodetik sejak mulai4 µsSekitar 71,6 menitTidak
  1. Buat variabel unsigned long untuk mengingat kapan tugas terakhir kali dikerjakan (oncekiZaman).
  2. Di setiap putaran loop(), baca waktu sekarang dengan millis().
  3. Periksa apakah waktunya sudah cukup dengan kondisi simdi - oncekiZaman >= ARALIK.
  4. Kalau waktunya sudah tiba, kerjakan tugasnya (balik status LED) dan perbarui oncekiZaman.
  5. Kalau belum, jangan lakukan apa-apa dan biarkan loop() berlanjut; justru pada momen inilah tugas-tugasmu yang lain berjalan.
C++ (Arduino IDE)
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}

Di kode ini kita menulis oncekiZaman += ARALIK, bukan oncekiZaman = simdi. Bedanya kecil tetapi penting: kalau loop() kadang terlambat beberapa milidetik, cara pertama menambahkan keterlambatan itu setiap kali dan LED bergeser seiring berjalannya jam, sedangkan cara kedua menggeser waktu target dengan langkah tetap sehingga periode rata-ratanya tetap tepat 500 ms. Pakai += untuk jam, metronom, dan pengukuran rutin. Kalau programmu bisa terblokir lama (misalnya ketika menunggu Wi-Fi), += akan mencoba mengejar langkah-langkah yang terlewat satu demi satu; dalam kasus itu = simdi lebih aman. Kalau kamu juga ingin mengubah kecerahan LED tanpa menghentikan kode, lihat artikel PWM dan analogWrite.

Mengerjakan beberapa hal sekaligus: multitasking dengan millis()

Arduino Uno punya prosesor satu inti dan sebenarnya tidak bisa mengerjakan dua hal pada waktu yang sama. Tetapi karena loop() bisa berjalan puluhan ribu kali per detik, memberi setiap tugas beberapa mikrodetik ketika gilirannya tiba terlihat “bersamaan” dari luar. Ini disebut cooperative multitasking. Aturannya sederhana: setiap tugas punya variabel waktu dan intervalnya sendiri, dan tidak ada tugas yang menunggu lama. Di contoh di bawah, empat tugas berjalan berdampingan:

C++ (Arduino IDE)
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}
TugasPinIntervalKegunaannya
LED merah8300 msBerkedip cepat
LED hijau91000 msBerkedip pelan
Tombol + LED kuning2 dan 10Debounce 50 msMembalik LED kuning di setiap tekanan
Laporan serial-1000 msMencetak lama berjalan dalam detik

Logika tombolnya juga berbasis millis(): setiap kali bacaan mentah berubah, timer dimulai ulang, dan kalau bacaan tetap stabil selama 50 ms, tekanan itu diterima sebagai tekanan sungguhan. Dengan begitu pantulan mekanis tombol tidak mengubah satu tekanan menjadi puluhan, dan kodenya tidak pernah menunggu. Untuk detail pengkabelan tombol dan resistor pull-up internal, baca Cara Menghubungkan Push Button ke Arduino. Resistor 220 Ω sudah cukup untuk LED; untuk warna dan arus lain, pakai kalkulator resistor LED.

Overflow 49,7 hari dan perbandingan yang aman dari overflow

Sebuah unsigned long bisa menampung paling banyak 4.294.967.295. Ketika millis() mencapai angka itu, setelah sekitar 49 hari, 17 jam, dan 2 menit, ia mulai menghitung lagi dari 0. Kalau board-mu menyala berminggu-minggu (kontrol rumah kaca, akuarium, sistem alarm), momen itu pasti datang. Kabar baiknya: kode yang ditulis dengan pengurangan, dalam bentuk simdi - onceki >= aralik, tidak terpengaruh overflow. Pengurangan tanpa tanda dihitung modulo 2 pangkat 32. Misalnya, kalau onceki = 4.294.967.000 dan setelah overflow simdi = 200, maka simdi - onceki tidak menghasilkan angka negatif; hasilnya benar, yaitu 496. Jadi selama interval yang kamu tunggu lebih pendek dari 49,7 hari, perbandingannya selalu bekerja.

KodeHasilKenapa?
if (millis() - onceki >= aralik) (bentuk pengurangan)BenarPengurangan tanpa tanda memberi selisih yang benar bahkan melewati overflow
if (millis() >= onceki + aralik) (bentuk penjumlahan)SalahKetika onceki + aralik overflow, hasilnya menjadi angka kecil dan kondisinya terpicu terlalu cepat
if (millis() == hedef)Salahmillis() kadang melompat 2, jadi nilai persisnya mungkin tidak pernah muncul
int onceki = millis(); (int 16-bit)SalahDi Uno, int berukuran 16-bit dan menyimpan nilai yang salah setelah 32,7 detik
long onceki = millis(); (long bertanda)BerisikoTipe bertanda menjadi negatif setelah 24,8 hari; pakai unsigned long agar tidak membingungkan
Jangan pernah mendeklarasikan variabel waktu sebagai int. Di Arduino Uno, int berukuran 16-bit dan menampung paling banyak 32.767, jadi programmu mulai berperilaku aneh sejak detik ke-33. Setiap variabel yang bekerja dengan millis() atau micros() sebaiknya bertipe unsigned long (atau uint32_t). Ketika menulis interval yang besar, pakai juga bentuk 60000UL: di Uno, hasil kali seperti 60 * 1000 melewati batas int dan berubah menjadi angka negatif.

Mengukur mikrodetik dengan micros()

micros() bekerja dengan cara yang sama, tetapi mengembalikan mikrodetik sejak mulai. Di board 16 MHz seperti Arduino Uno dan Nano, resolusinya 4 µs (8 µs di board 8 MHz), dan overflow sekitar setiap 71,6 menit; aturan pengurangan juga berlaku di sini. Fungsi ini dipakai untuk pekerjaan presisi seperti mengukur berapa lama sepotong kode berjalan, mengukur pulsa pendek, atau menghasilkan suara. Contoh di bawah mengukur berapa lama satu panggilan analogRead() berlangsung, dua kali per detik; di Uno kamu akan melihat sekitar 110–120 µs:

C++ (Arduino IDE)
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}

Untuk interval yang lebih panjang, pilih millis() daripada micros(); periode overflow 71 menit terlalu pendek untuk penantian yang lama. delayMicroseconds() hanya untuk penantian singkat beberapa mikrodetik yang tidak bisa dihindari, seperti mengirim pulsa trigger 10 µs ke pin TRIG sensor jarak HC-SR04. Penantian sesingkat itu tidak terasa menunda tugas-tugasmu yang lain.

Pola timer: periodik, sekali jalan, dan state machine

  • Tugas periodik: pekerjaan yang berulang pada interval tertentu, seperti LED berkedip, membaca sensor setiap 2 detik, atau menyegarkan layar setiap detik. Polanya sudah kamu lihat di atas.
  • Timer sekali jalan (timeout): berjalan sekali, beberapa waktu setelah sebuah peristiwa. Lampu tangga adalah contoh yang bagus: nyalakan lampu ketika ada gerakan, simpan waktunya, dan matikan kalau tidak ada gerakan baru selama 30 detik. Memperbarui waktu ketika ada gerakan baru memperpanjang periodenya.
  • Menunggu dengan batas waktu: ketika menunggu balasan atau sinyal, kamu memberi batas atas agar tidak pernah terjebak selamanya; misalnya, tampilkan error kalau tidak ada data yang masuk dalam 2 detik.
  • State machine: kamu menyimpan langkah perangkat yang sedang berjalan di sebuah variabel dan pindah ke langkah berikutnya ketika waktu setiap langkah habis. Lampu lalu lintas, program mesin cuci, dan level game ditulis dengan cara ini.
  • Deteksi tekan lama: dengan mengukur berapa lama tombol ditahan memakai millis(), kamu bisa memberi tekanan singkat dan tekanan lama tugas yang berbeda.
C++ (Arduino IDE)
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}

Di kode ini, struktur kecil (struct) bernama Zamanlayici menyederhanakan memulai periode dan menanyakan apakah periodenya sudah habis menjadi satu baris. Lampu lalu lintasnya adalah state machine dengan empat langkah: di setiap langkah lampu-lampunya diatur dan waktu transisi berikutnya dimulai. Karena loop() tidak pernah menunggu, kamu bisa dengan mudah menambahkan tombol penyeberang, sensor cahaya LDR untuk mode malam, atau layar yang menampilkan sisa waktu ke board yang sama. Kalau butuh banyak timer, library timer siap pakai di Library Manager bekerja dengan prinsip yang sama.

millis(), delay(), dan Ticker di ESP32

Di core Arduino untuk ESP32, millis() dan micros() punya nama yang sama dan tipe unsigned long 32-bit yang sama; nilainya dihitung dari penghitung 64-bit esp_timer_get_time(). Artinya periode overflow 49,7 hari dan 71,6 menit juga berlaku di ESP32, dan kamu memakai aturan pengurangan yang sama. Perbedaan pentingnya ada di delay(): di ESP32, delay() memanggil vTaskDelay() dari FreeRTOS. Prosesornya tidak berputar sia-sia; tugas lain seperti Wi-Fi dan Bluetooth berjalan selama kamu menunggu. Namun loop()-mu tetap menunggu, jadi pekerjaan seperti web server, MQTT, atau membaca tombol tetap tertunda. Untuk pekerjaan periodik, library Ticker yang ikut dengan core ESP32 juga praktis:

C++ (Arduino IDE)
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}

Fungsi yang kamu berikan ke Ticker dipanggil oleh tugas timer di latar belakang; jangan lakukan apa pun yang lambat di dalamnya (permintaan HTTP, output serial yang panjang, delay), cukup set flag atau ubah pin. esp_timer_get_time() yang 64-bit tidak akan overflow selama ratusan ribu tahun, jadi bisa kamu pakai untuk pengukuran yang butuh presisi mikrodetik di perangkat yang menyala berbulan-bulan. Pin LED bawaan bisa berbeda antar-board; peta pin board-mu bisa kamu cek di alat Pinout. Kalau kamu baru mengenal ESP32, artikel Apa Itu ESP32? adalah awal yang bagus untuk pengaturan dan pemilihan pin.

Raspberry Pi Pico (MicroPython): ticks_ms() dan ticks_diff()

Di MicroPython kamu memakai time.sleep(), time.sleep_ms(), dan time.sleep_us() sebagai ganti delay(), dan time.ticks_ms() sebagai ganti millis(). Tetapi hati-hati: nilai tick tidak berputar kembali di 2 pangkat 32 seperti di Arduino, melainkan di batas yang lebih kecil yang tergantung port-nya. Di Pico batasnya 2 pangkat 30 ms, sekitar 12,4 hari (kamu bisa mengeceknya di board-mu dengan time.ticks_add(0, -1)). Jadi jangan pernah mengurangkan atau membandingkan dua nilai tick secara langsung; ambil selisihnya dengan time.ticks_diff(new, old) dan pakai time.ticks_add() untuk menghitung waktu di masa depan. Kode di bawah mengedipkan LED bawaan Pico tanpa memblokir, sementara tombol di GP14 membalik LED di GP15:

MicroPython
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()
27

Untuk pekerjaan periodik, MicroPython juga punya machine.Timer: baris Timer(period=500, mode=Timer.PERIODIC, callback=function) memanggil fungsimu setiap 500 ms. Buat callback ini tetap singkat, sama seperti dengan Ticker. Kalau kamu memprogram Pico dari Arduino IDE (dengan core arduino-pico), millis() dan micros() bekerja persis seperti di Arduino. Untuk menginstal MicroPython di Pico dan mengenal pin-pinnya, baca artikel Apa Itu Raspberry Pi Pico?.

Ide proyek yang memakai millis()

  • Tes reaksi: nyalakan LED pada momen acak, ukur waktu sampai tombol ditekan dengan millis(), dan tampilkan hasilnya dalam milidetik.
  • Lampu tangga: nyalakan lampu ketika sensor PIR mendeteksi gerakan dan matikan 30 detik setelah gerakan terakhir, sementara kodenya tetap mengerjakan hal lain.
  • Sensor parkir yang makin cepat: perpendek jeda bunyi buzzer ketika jarak yang diukur HC-SR04 makin dekat.
  • Melodi tanpa blocking: urutkan nada di buzzer dengan millis(), bukan delay(), agar tombol tetap berfungsi selama lagu dimainkan.
  • Sensor dan layar bersamaan: baca sensor DHT11 setiap 2 detik, tetapi segarkan layar setiap 250 ms.
  • Stopwatch: buat stopwatch dengan tombol mulai, berhenti, dan reset yang menampilkan waktu di display 7-segmen.

Kesalahan umum dan cara mengatasinya

KesalahanGejalaSolusi
Variabel waktu dideklarasikan sebagai intProgram jadi kacau setelah sekitar 33 detikJadikan setiap variabel waktu unsigned long
Menulis millis() >= onceki + aralikTiming rusak setelah 49,7 hariGanti ke bentuk millis() - onceki >= aralik
Memeriksa millis() == hedefPeristiwanya kadang tidak pernah terpicuPakai >=, bukan kesamaan
Lupa memperbarui oncekiBegitu waktunya tiba, tugasnya berulang di setiap putaranTulis onceki = simdi atau onceki += aralik di dalam kondisi
delay() yang tertinggal di dalam kode timerTugas lain tetap tertundaPecah penantian panjang menjadi state machine
Menunggu dengan millis() di dalam interupsiBoard membekuJangan pernah menunggu di dalam ISR; cukup set flag
Hasil kali int seperti 60 * 1000Intervalnya jadi negatif atau terlalu pendekTulis 60000UL atau 60UL * 1000
Cara mudah untuk memastikan kodemu benar-benar berjalan tanpa memblokir adalah menghitung berapa kali loop() berjalan per detik: naikkan penghitung di setiap putaran, cetak ke port serial sekali per detik, lalu reset. Kalau angkanya tiba-tiba turun dari ribuan ke satuan, ada sesuatu di suatu tempat yang menunggu (delay, pulseIn, atau loop while yang panjang).

Begitu kamu menguasai millis(), proyek-proyekmu berubah total: LED, tombol, sensor, dan layar bekerja bersama tanpa saling menunggu. Untuk langkah berikutnya, kamu bisa memilih proyek yang butuh pengaturan waktu dari daftar ide proyek Arduino. Di aplikasi BlueGrays kamu bisa menjelajahi proyek seperti Lampu Lalu Lintas, Lampu Strobo Polisi, Lampu Tangga dan Koridor Pintar, serta Tes Reaksi dengan Peringatan Suara langkah demi langkah, dengan skema rangkaian dan kode siap pakai, lalu mencobanya di board-mu sendiri.

Pertanyaan umum

Apa itu millis() di Arduino?

millis() adalah fungsi yang mengembalikan waktu sejak Arduino dinyalakan atau di-reset, dalam milidetik. Nilainya bertipe unsigned long dan mulai lagi dari nol setelah sekitar 49,7 hari. Fungsi ini dipakai untuk mengatur waktu tanpa menghentikan program.

Apa perbedaan millis() dan delay()?

delay() menghentikan program selama waktu yang diberikan, jadi tidak ada hal lain, seperti membaca tombol atau memperbarui layar, yang bisa terjadi sementara itu. millis() hanya memberi tahu berapa lama waktu sudah berlalu dan tidak pernah menunggu. Dengan memeriksa waktu yang berlalu dan menjalankan tugas yang sudah waktunya, kamu bisa menjalankan beberapa tugas sekaligus.

Apakah programku rusak ketika millis() overflow setelah 49 hari?

Tidak, kalau kamu memeriksa waktu dengan pengurangan dalam bentuk millis() - onceki >= aralik; pengurangan tanpa tanda memberi selisih yang benar bahkan melewati overflow. Masalah hanya muncul pada perbandingan yang ditulis dengan penjumlahan, seperti millis() >= onceki + aralik, dan pada variabel int.

Bisakah millis() di-reset di Arduino?

Tidak ada fungsi resmi untuk me-reset millis(), dan mengubah variabel internal core tidak disarankan. Kamu juga tidak perlu: simpan momen yang ingin dijadikan titik awal di sebuah variabel dan hitung waktu yang berlalu dari situ. Me-reset board dengan sendirinya memulai ulang millis() dari 0.

Bisakah Arduino mengerjakan dua hal sekaligus?

Karena Uno hanya punya satu inti, secara fisik ia tidak bisa menjalankan dua instruksi pada waktu yang sama, tetapi kalau setiap pekerjaan kamu pecah menjadi langkah-langkah pendek dengan millis(), loop() berjalan ribuan kali per detik dan memberi giliran ke setiap pekerjaan. Dari luar, LED, tombol, sensor, dan layar tampak bekerja bersamaan. ESP32 juga punya dua inti dan tugas FreeRTOS.

Apakah millis() cukup akurat untuk membuat jam?

millis() bergantung pada osilator board; karena resonator keramik di Arduino Uno, millis() bisa bergeser sampai beberapa menit per hari. Untuk mengukur interval pendek tidak masalah, tetapi untuk jam yang harus tetap tepat selama berhari-hari, modul jam (RTC) seperti DS1307 atau DS3231, atau mengambil waktu lewat NTP di ESP32, lebih akurat.

Lihat semua proyek
LED Berkedip
#1Pemula

LED Berkedip

"Halo" pertama di dunia elektronika: menyalakan dan mematikan satu LED. Arduino menyalakan dan mematikan LED dengan jeda singkat, sehingga cahayanya berkedip cepat. Kamu melihat dengan cara paling sed

4.6(188)
1127
Lampu Lalu Lintas
#3Pemula

Lampu Lalu Lintas

Tiruan mini lampu lalu lintas yang kamu lihat di jalan setiap hari. Merah menyala dua detik, kuning menyala sebentar, hijau menyala dua detik, lalu kembali ke merah lewat kuning lagi, terus berulang.

4.8(131)
397
Lampu Polisi LED
#4Pemula

Lampu Polisi LED

Persis seperti lampu di mobil polisi: LED merah berkedip tiga kali dengan cepat, lalu LED biru berkedip tiga kali, dan polanya terus berulang tanpa henti. Yang kamu butuhkan hanya dua LED dan dua resi

4.7(103)
258
LED Berjalan dengan Kecepatan yang Bisa Diatur
#7Menengah

LED Berjalan dengan Kecepatan yang Bisa Diatur

Enam LED menyala satu demi satu, sehingga cahayanya seolah berlari dari satu ujung ke ujung lainnya. Putar kenop potensiometer dan kecepatan larinya langsung berubah: ke satu arah cahayanya melesat, k

4.6(72)
146
Nyala/Mati LED dengan Tombol (On/Off)
#10Pemula

Nyala/Mati LED dengan Tombol (On/Off)

Tekan tombol sekali dan lampu menyala, tekan lagi dan lampu mati, persis seperti sakelar lampu di kamarmu. Arduino menyadari setiap tekanan dan membalik LED ke keadaan sebaliknya, sambil menunggu sebe

4.8(100)
167
Lampu Tangga dan Koridor Pintar
#82Menengah
Premium

Lampu Tangga dan Koridor Pintar

Injak tangga atau masuk ke lorong, dan lampu menyala satu per satu ke arah kamu berjalan. Ada sensor jarak di setiap ujung; sensor mana pun yang kamu lewati, delapan LED menyala satu per satu dari uju

5.0(35)
51
Tes Waktu Reaksi Arduino dengan Isyarat Suara: Ukur Refleksmu dalam Milidetik (LCD + Buzzer + 3 LED)
#97Menengah
Premium

Tes Waktu Reaksi Arduino dengan Isyarat Suara: Ukur Refleksmu dalam Milidetik (LCD + Buzzer + 3 LED)

Seberapa cepat refleksmu? Tekan tombol: LED merah berkata bersiap, LED kuning berkata siap, dan jeda acak 1,5 sampai 3,5 detik dimulai. Begitu LED hijau menyala dan buzzer berbunyi bip, tekan lagi; waktu reaksimu muncul di LCD I2C dalam milidetik, dengan rekor terbaik dan rata-rata sesimu di bawahnya. Tekan sebelum hijau dan layar menampilkan TOO EARLY! dengan suara kalah. Tahan tombol untuk pemain baru, agar semua orang beradu dengan urutan jeda yang sama.

4.6(33)
50
Jam Digital dengan Layar 7 Segmen 4 Digit
#48Menengah
Premium

Jam Digital dengan Layar 7 Segmen 4 Digit

Jam digital empat digit yang menyala, mirip jam di oven dapur. Modul jam DS1302 menjaga waktu, dan setiap detik Arduino membaca jam dan menit lalu menampilkannya di layar empat digit, dengan titik kec

4.8(56)
73