7 / 8 tamamlandı
görev 7 · ~40 dk

Sistemin hali

if yığınından çık — durumları adlandır

hedef

Karmaşayı if ekleyerek değil, durumları adlandırarak çözmek. Kursun son ve en değerli görevi.

Önce şunu bil: bir noktadan sonra if yetmiyor

Bir kapı kilidi yazdığını düşün. Başta basit:

kod · .ino
if (kartOkundu) { kilidiAc(); }

Sonra istekler geliyor:

Her birini bir if olarak eklersen kod kısa sürede okunmaz hale gelir. Daha kötüsü: birbiriyle çelişen if'ler yazarsın. Sistem aynı anda hem alarmda hem kilidi açık olur.

Bu bir dikkat sorunu değil, yöntem sorunu.

Çözüm: sistemin her an tek bir hali olsun

Durum Ne demek
BEKLIYOR Evdesin, kapı kapalı, alarm kurulu değil
ACIK Kilit açıldı, girip çıkabilirsin
KURULU Dışarıdasın, alarm devrede
ALARM Bir şey oldu, siren çalıyor

Buna durum makinesi deniyor. Kural tek: sistem aynı anda iki durumda olamaz, ve bir durumdan diğerine geçiş açıkça yazılır.

Çamaşır makinesinden asansöre, ATM'den oyun karakterine kadar her yerde bu var.

enum — sınırlı seçenek listesi

kod · .ino
enum Durum { BEKLIYOR, ACIK, KURULU, ALARM };
Durum durum = BEKLIYOR;

enum, "bu değişken sadece şu dört değerden birini alabilir" demenin yolu. int kullansaydın 7 ya da −3 yazabilirdin ve derleyici sesini çıkarmazdı. enum ile yanlış bir değer yazarsan hemen uyarır.

switch — duruma göre dallanmak

kod · .ino
// PARCA - sadece kalibi gosteriyor, tam kod asagidaki blokta
switch (durum) {
  case BEKLIYOR:
    // burada ne yapiliyor
    break;

  case ACIK:
    break;
}

break yazmayı unutursan bir sonraki case'e de düşer ve iki durumun kodu birden çalışır. Sinsi bir hatadır.

Geçiş tablosu — koddan önce bunu yaz

Durum Olay Yeni durum
BEKLIYOR 1 kart okundu ACIK
ACIK 2 süre doldu BEKLIYOR
ACIK 1 kart tekrar okundu KURULU
KURULU 3 kapı açıldı ALARM
KURULU 1 kart okundu ACIK
ALARM 1 kart okundu ACIK

Bu tablo sistemin tamamı. Kod bunun tercümesinden ibaret — ve tabloyu yazmadan koda başlarsan kaybolursun.

Kod

Olayları Serial'den göndereceksin: 1 kart, 2 süre doldu, 3 kapı açıldı.

kod · .ino
enum Durum { BEKLIYOR, ACIK, KURULU, ALARM };
Durum durum = BEKLIYOR;

void durumDegistir(Durum yeni) {
  durum = yeni;
  Serial.print(">>> Yeni durum: ");
  switch (yeni) {
    case BEKLIYOR: Serial.println("BEKLIYOR"); break;
    case ACIK:     Serial.println("ACIK");     break;
    case KURULU:   Serial.println("KURULU - alarm devrede"); break;
    case ALARM:    Serial.println("!! ALARM !!"); break;
  }
}

void setup() {
  Serial.begin(9600);
  Serial.println("1 = kart okundu, 2 = sure doldu, 3 = kapi acildi");
  durumDegistir(BEKLIYOR);
}

void loop() {
  if (Serial.available() > 0) {
    int olay = Serial.parseInt();
    while (Serial.available()) Serial.read();

    switch (durum) {

      case BEKLIYOR:
        if (olay == 1) durumDegistir(ACIK);
        break;

      case ACIK:
        if (olay == 2) durumDegistir(BEKLIYOR);
        if (olay == 1) durumDegistir(KURULU);
        break;

      case KURULU:
        // ŞİMDİ SEN YAZ:
        // olay 3 ise ALARM'a gec.
        // olay 1 ise ACIK'a gec.
        break;

      case ALARM:
        if (olay == 1) durumDegistir(ACIK);
        break;
    }
  }
}

Şimdi sen yaz

🔍 İpucu 1

Yukarıdaki case'lere bak, aynı kalıbı kullan.

Geçiş tablosunda KURULU satırları iki tane: biri olay 3 için, biri olay 1 için.

✅ Cevap
kod · .ino
case KURULU:
  if (olay == 3) durumDegistir(ALARM);
  if (olay == 1) durumDegistir(ACIK);
  break;

Şuna dikkat et: aynı olay, duruma göre farklı anlama geliyor.

Olay 1 (kart okundu) BEKLIYOR'dayken "beni içeri al" demek. ACIK'tayken "alarmı kur ve çık" demek. ALARM'dayken "sustur" demek. Tek bir fiziksel eylem, üç ayrı anlam.

Asansör butonu gibi: kattayken çağırır, içerideyken gönderir. Aynı düğme, farklı bağlam.

if yığınıyla bunu yazmayı dene — kodun neden karışacağını hemen görürsün.

📄 Görevin tam kodu
kod · .ino
enum Durum { BEKLIYOR, ACIK, KURULU, ALARM };
Durum durum = BEKLIYOR;

void durumDegistir(Durum yeni) {
  durum = yeni;
  Serial.print(">>> Yeni durum: ");
  switch (yeni) {
    case BEKLIYOR: Serial.println("BEKLIYOR"); break;
    case ACIK:     Serial.println("ACIK");     break;
    case KURULU:   Serial.println("KURULU - alarm devrede"); break;
    case ALARM:    Serial.println("!! ALARM !!"); break;
  }
}

void setup() {
  Serial.begin(9600);
  Serial.println("1 = kart okundu, 2 = sure doldu, 3 = kapi acildi");
  durumDegistir(BEKLIYOR);
}

void loop() {
  if (Serial.available() > 0) {
    int olay = Serial.parseInt();
    while (Serial.available()) Serial.read();

    switch (durum) {

      case BEKLIYOR:
        if (olay == 1) durumDegistir(ACIK);
        break;

      case ACIK:
        if (olay == 2) durumDegistir(BEKLIYOR);
        if (olay == 1) durumDegistir(KURULU);
        break;

      case KURULU:
        if (olay == 3) durumDegistir(ALARM);
        if (olay == 1) durumDegistir(ACIK);
        break;

      case ALARM:
        if (olay == 1) durumDegistir(ACIK);
        break;
    }
  }
}

Bütün geçişler durumDegistir içinden geçiyor. Gerçek sistemde kilidi kapatan, buzzer'ı susturan, ekranı yazan tek yer orası olacak. Böyle yazmazsan iki ay sonra "bu alarm neden susmuyor" diye kodun altı ayrı yerine bakarsın.

Çalıştı mı?

Sırayla gir ve tabloyla karşılaştır:

kod
1  ->  ACIK
1  ->  KURULU
3  ->  !! ALARM !!
1  ->  ACIK
2  ->  BEKLIYOR
3  ->  hicbir sey olmuyor

Son satır önemli: BEKLIYOR durumundayken kapı açılması alarm çalmıyor. Çünkü evdesin, kapıyı sen açtın. Aynı olay, farklı durumda farklı anlam.

Olmadıysa

Ne görüyorsun Ne yap
Her olayda birden fazla durum yazılıyor break; yazmayı unutmuşsun
KURULU'dayken 3 girince bir şey olmuyor case KURULU içine kodu yazdın mı?
Derleme hatası: ACIK was not declared enum satırını setup'ın üstüne, en dışa yazdın mı?
Her şey hep BEKLIYOR kalıyor durum = yeni; satırı durumDegistir içinde duruyor mu?

Kendini test et

Soru: Sisteme "çıkış gecikmesi" eklemek istiyorsun — alarm kurulduktan sonra 30 saniye boyunca hareket sayılmasın, evden çıkabilesin. Kodun neresini değiştirirsin?

Cevabı gör

Yeni bir durum eklersin: CIKIS_BEKLIYOR.

Ve geçiş tablosuna iki satır:

Durum Olay Yeni durum
ACIK kart okundu CIKIS_BEKLIYOR
CIKIS_BEKLIYOR 30 saniye doldu KURULU

enum'a bir kelime, switch'e bir case, tabloya iki satır. Var olan hiçbir şeye dokunmadın.

Durum makinesinin asıl değeri burada. Yeni bir davranış eklemek, eskisini bozma riski taşımıyor. if yığınında aynı şeyi yapsaydın her yeni kural, önceki kuralların hepsiyle çakışma ihtimali taşırdı.

Süreyi ölçmek için Görev 6'daki millis() kalıbını kullanırsın. Gördün mü — parçalar birleşmeye başladı.


Bitirdin

Artık kitlerdeki kodu okuyabiliyorsun. Gördüğün her şeyi bugün kendin yazdın:

Kitte göreceğin Burada öğrendiğin
void setup() / void loop() Görev 0
analogRead() bir sayı veriyor Görev 1 — sayıyı sen yazmıştın
SUSUZ_ESIGI ve eşik karşılaştırması Görev 2
bool susuzMu ve iki eşik Görev 3
void sula(), && ile birleşen şartlar Görev 4
byte YETKILI[2][4] ve for döngüsü Görev 5
millis() - sonSulama >= ARALIK Görev 6
enum Durum ve switch (durum) Görev 7

Bundan sonrası ezber değil. Kitin kodunu okuduğunda ne yaptığını anlayacak, değiştirmek istediğinde nereye dokunacağını bileceksin.

Bir farkla: oradaki sayılar senin klavyenden değil, gerçek bir sensörden gelecek. Ve karar verdiğinde gerçekten bir şey hareket edecek.

Takıldın, hata tablosu da yetmedi mi?

WhatsApp'tan sor — hangi görevdesin, ekranda ne yazıyor, devrenin fotoğrafı.

Şimdi gerçek bir sistem kur

Buraya kadar yazdığın kod ekranda kaldı. Kitlerde aynı kod bir sensöre, bir motora, bir kilide bağlanıyor — çalıştığını gözünle görüyorsun.

Kitlere bak →