Sistemin hali
if yığınından çık — durumları adlandır
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:
if (kartOkundu) { kilidiAc(); }
Sonra istekler geliyor:
- Kilit beş saniye sonra kapansın
- Evden çıkarken alarm kurulabilsin
- Alarm kuruluyken kapı açılırsa siren çalsın
- Siren çalarken kart okutulunca sussun
- Alarm kuruluyken kart okutulursa içeri alsın
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
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
// 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ı.
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
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
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:
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.