DIY ELECTRONICSEN

← Tüm projeler

Şifreli Kapı Kilidi

Orta50 dk

4x4 tuş takımından girilen şifreyle açılan bir kapı kilidi: LCD ekran ne yapılacağını söyler ve girilen haneleri * olarak gösterir, şifre doğruysa servo motor mandalı çevirip kapıyı 5 saniyeliğine açar ve ekranda geri sayım yapar. Üst üste 3 yanlış şifrede tuş takımı 30 saniye kilitlenir. Kapı açıkken A tuşuyla şifre değiştirilir; yeni şifre iki kez girilir ve Arduino'nun EEPROM belleğine kaydedildiği için elektrik gidip gelse de unutulmaz. Tuş takımından matris olarak okumayı, bir programı durumlara (kilitli, açık, bekleme, şifre değiştirme) bölmeyi, millis() ile beklerken tuşları dinlemeyi ve kalıcı bellek kullanmayı öğretir. Kitaplık Yöneticisi'nden "Keypad" ve "LiquidCrystal I2C" kurulur; Servo ve EEPROM Arduino ile gelir. Bu bir öğrenme projesidir; gerçek bir kapının tek kilidi olarak kullanma.

Uygulamada adım adım yap

Gerekli malzemeler

Bağlantı adımları

  1. 1

    Güç sıralarını bağla (5V ve GND)

    Güç sıralarını bağla (5V ve GND)

    Kırmızı erkek-erkek jumper kabloyu Arduino'nun güç başlığındaki 5V pinine tak, diğer ucunu breadboard'un üstteki kırmızı çizgili (+) güç sırasına bağla. Siyah kabloyu aynı başlıktaki GND pinlerinden birine tak, diğer ucunu üstteki mavi çizgili (−) güç sırasına bağla. Uno'da tek 5V pini var; bu iki sıra 5V ve GND'yi hem LCD'ye hem servoya dağıtacak. Breadboard'u yalnızca bunun için kullanıyoruz.

    • Arduino 5V → üstteki kırmızı çizgili (+) sıra (kırmızı kablo)
    • Arduino GND → üstteki mavi çizgili (−) sıra (siyah kablo)
  2. 2

    Tuş takımını bağla

    Tuş takımını bağla

    Tuş takımının kablosunun ucundaki 8 delikli sokete 8 erkek-erkek kablo tak. Tuşlar sana bakarken soketin soldan ilk 4 pini satırlardır, son 4 pini sütunlardır. 1. pini Arduino'nun 9, 2. pini 8, 3. pini 7, 4. pini 6 numaralı pinine bağla (rowPins); 5. pini 5, 6. pini 4, 7. pini 3, 8. pini 2 numaralı pinine bağla (colPins). Tuş takımına ayrıca besleme gerekmez: Keypad kütüphanesi satırları sırayla tarayıp hangi tuşun basıldığını bulur. 0 ve 1 numaralı pinler boş kalır, çünkü onları USB üzerinden Seri Monitör kullanır.

    • Arduino pin 9 → tuş takımı soketinin 1. pini (yeşil kablo)
    • Arduino pin 8 → tuş takımı soketinin 2. pini (yeşil kablo)
    • Arduino pin 7 → tuş takımı soketinin 3. pini (yeşil kablo)
    • Arduino pin 6 → tuş takımı soketinin 4. pini (yeşil kablo)
    • Arduino pin 5 → tuş takımı soketinin 5. pini (mavi kablo)
    • Arduino pin 4 → tuş takımı soketinin 6. pini (mavi kablo)
    • Arduino pin 3 → tuş takımı soketinin 7. pini (mavi kablo)
    • Arduino pin 2 → tuş takımı soketinin 8. pini (mavi kablo)
  3. 3

    LCD ekranı bağla

    LCD ekranı bağla

    Ekranı breadboard'a takma; arkasındaki I2C modülünün dört pinine erkek-dişi jumper kabloların dişi uçlarını tak. GND'yi üstteki (−) sıraya, VCC'yi üstteki (+) sıraya bağla. SDA'yı sarı kabloyla Arduino'nun A4 pinine, SCL'yi yeşil kabloyla A5 pinine bağla. Ekran Türkçe harfleri gösteremediği için kodda "Sifre" gibi ş'siz yazılar kullanılıyor.

    • LCD GND → üstteki mavi çizgili (−) sıra (siyah kablo)
    • LCD VCC → üstteki kırmızı çizgili (+) sıra (kırmızı kablo)
    • Arduino A4 → LCD SDA (sarı kablo)
    • Arduino A5 → LCD SCL (yeşil kablo)
  4. 4

    Servoyu bağla ve mandal yap

    Servoyu bağla ve mandal yap

    SG90'ın kablosunun ucundaki üç delikli fişe üç erkek-erkek kablo tak. Kahverengi (GND) kablonun karşısındakini üstteki (−) sıraya, kırmızı (5V) kablonun karşısındakini üstteki (+) sıraya, turuncu (sinyal) kablonun karşısındakini doğrudan Arduino'nun 10 numaralı pinine bağla (SERVO_PIN 10). Servonun tek kollu başlığını mile, kod açılışta servoyu 0°'ye getirdikten sonra tak: kol 0°'de mandalı kapatsın, 90°'de açsın. Denemek için servoyu bir karton kutunun kapağının iç yüzüne bantla; kol kutunun kenarına takılınca kapak açılmaz.

    • servo kahverengi kablo (GND) → üstteki mavi çizgili (−) sıra (siyah kablo)
    • servo kırmızı kablo (5V) → üstteki kırmızı çizgili (+) sıra (kırmızı kablo)
    • Arduino pin 10 → servo turuncu kablo (sinyal) (turuncu kablo)

Arduino kodu

// Kitaplık Yöneticisi'nden kurulacaklar:
// "Keypad" ve "LiquidCrystal I2C"
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
#include <Servo.h>
#include <EEPROM.h>

// Tuş takımı: 4 satır, 4 sütun
const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};
// Soketin 8 pini soldan sağa: önce 4
// satır, sonra 4 sütun
byte rowPins[ROWS] = {9, 8, 7, 6};
byte colPins[COLS] = {5, 4, 3, 2};

const int SERVO_PIN = 10;
// Servonun kilitli ve açık açısı
const int LOCKED_ANGLE = 0;
const int OPEN_ANGLE = 90;

// Kapının açık kalma süresi (ms)
const unsigned long OPEN_TIME = 5000;
// Üst üste bu kadar yanlışta beklet
const int MAX_TRIES = 3;
const unsigned long BLOCK_TIME = 30000;
// Şifre değiştirirken bu kadar tuşa
// basılmazsa vazgeçilir (ms)
const unsigned long IDLE_TIME = 20000;

// Şifre 4-8 hane; ilk açılışta 1234
const byte MIN_LEN = 4;
const byte MAX_LEN = 8;
// Şifreyi unutursan bu sayıyı değiştir
// ve yükle: şifre 1234'e döner
const byte EEPROM_MARK = 42;

// I2C adresi çoğunda 0x27, bazısında 0x3F
LiquidCrystal_I2C lcd(0x27, 16, 2);
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
Servo lock;

enum State { LOCKED, OPEN, BLOCKED, NEW_CODE, CONFIRM_CODE };
State state = LOCKED;

char code[MAX_LEN + 1] = "1234";
char entry[MAX_LEN + 1] = "";
char newCode[MAX_LEN + 1] = "";
byte entryLen = 0;
int wrongTries = 0;
unsigned long stateStart = 0;
int shownSeconds = -1;

// EEPROM: 0. bayt işaret, 1. uzunluk,
// sonrası haneler
void loadCode() {
  if (EEPROM.read(0) != EEPROM_MARK) return;
  byte len = EEPROM.read(1);
  if (len < MIN_LEN || len > MAX_LEN) return;
  for (byte i = 0; i < len; i++) {
    code[i] = EEPROM.read(2 + i);
  }
  code[len] = '\0';
}

void saveCode() {
  byte len = strlen(code);
  EEPROM.update(0, EEPROM_MARK);
  EEPROM.update(1, len);
  for (byte i = 0; i < len; i++) {
    EEPROM.update(2 + i, code[i]);
  }
}

// LCD Türkçe harf bilmez: ş yerine s
void show(const char *top, const char *bottom) {
  lcd.clear();
  lcd.print(top);
  lcd.setCursor(0, 1);
  lcd.print(bottom);
}

// Kısa bir uyarı gösterip bekle
void message(const char *text) {
  show(text, "");
  delay(1500);
}

// Yeni duruma geç, girişi sıfırla
void prompt(State next) {
  state = next;
  entryLen = 0;
  entry[0] = '\0';
  if (next == NEW_CODE) {
    show("Yeni sifre:", "");
  } else if (next == CONFIRM_CODE) {
    show("Tekrar girin:", "");
  } else {
    show("Sifre girin:", "");
  }
}

// Geri sayımı alt satırın sonuna yaz
void showRemaining(unsigned long total) {
  int left = (total - (millis() - stateStart) + 999) / 1000;
  if (left == shownSeconds) return;
  shownSeconds = left;
  lcd.setCursor(12, 1);
  if (left < 10) lcd.print(' ');
  lcd.print(left);
  lcd.print("sn");
}

void startTimer(State next) {
  state = next;
  stateStart = millis();
  shownSeconds = -1;
}

void openDoor() {
  wrongTries = 0;
  lock.write(OPEN_ANGLE);
  startTimer(OPEN);
  show("Kapi acik", "Kapanma:");
  Serial.println("Doğru şifre: kapı açıldı.");
}

void lockDoor() {
  lock.write(LOCKED_ANGLE);
  prompt(LOCKED);
  Serial.println("Kapı kilitlendi.");
}

// Şifre değiştirmekten vazgeç
void cancelChange() {
  prompt(LOCKED);
  Serial.println("Şifre değiştirme iptal.");
}

void wrongCode() {
  wrongTries++;
  Serial.print("Yanlış şifre, deneme ");
  Serial.println(wrongTries);
  if (wrongTries >= MAX_TRIES) {
    startTimer(BLOCKED);
    show("Cok fazla deneme", "Bekleyin:");
    Serial.println("Çok fazla deneme: tuşlar kilitlendi.");
    return;
  }
  show("Yanlis sifre!", "Kalan hak:");
  lcd.setCursor(11, 1);
  lcd.print(MAX_TRIES - wrongTries);
  delay(1500);
  prompt(LOCKED);
}

// # basılınca girileni duruma göre işle
void submit() {
  if (state == LOCKED) {
    if (strcmp(entry, code) == 0) {
      openDoor();
    } else {
      wrongCode();
    }
  } else if (entryLen < MIN_LEN) {
    message("En az 4 hane!");
    prompt(state);
  } else if (state == NEW_CODE) {
    strcpy(newCode, entry);
    prompt(CONFIRM_CODE);
  } else if (strcmp(entry, newCode) == 0) {
    strcpy(code, newCode);
    saveCode();
    Serial.println("Şifre değişti ve kaydedildi.");
    message("Sifre degisti");
    prompt(LOCKED);
  } else {
    Serial.println("Şifreler eşleşmedi.");
    message("Eslesmedi!");
    prompt(NEW_CODE);
  }
}

void setup() {
  Serial.begin(9600);
  lcd.init();
  lcd.backlight();
  lock.attach(SERVO_PIN);
  lock.write(LOCKED_ANGLE);
  loadCode();
  prompt(LOCKED);
  Serial.println("Kilit hazır.");
}

void loop() {
  char key = keypad.getKey();

  if (state == OPEN) {
    // Süre dolunca ya da # ile kapanır
    if (key == '#' || millis() - stateStart >= OPEN_TIME) {
      lockDoor();
    } else if (key == 'A') {
      // Önce kilitle, yeni şifreyi sor
      lock.write(LOCKED_ANGLE);
      startTimer(NEW_CODE);
      prompt(NEW_CODE);
    } else {
      showRemaining(OPEN_TIME);
    }
    return;
  }

  if (state == BLOCKED) {
    // Beklerken tuşlar yok sayılır
    if (millis() - stateStart >= BLOCK_TIME) {
      wrongTries = 0;
      prompt(LOCKED);
      Serial.println("Bekleme bitti.");
    } else {
      showRemaining(BLOCK_TIME);
    }
    return;
  }

  // Şifre değiştirirken her tuş süreyi
  // baştan başlatır
  if (state != LOCKED) {
    if (key) {
      stateStart = millis();
    } else if (millis() - stateStart >= IDLE_TIME) {
      cancelChange();
      return;
    }
  }

  if (!key) return;
  if (key >= '0' && key <= '9') {
    // Haneler ekranda * olarak görünür
    if (entryLen < MAX_LEN) {
      entry[entryLen++] = key;
      entry[entryLen] = '\0';
      lcd.setCursor(entryLen - 1, 1);
      lcd.print('*');
    }
  } else if (key == '*') {
    // Son haneyi sil
    if (entryLen > 0) {
      entryLen--;
      entry[entryLen] = '\0';
      lcd.setCursor(entryLen, 1);
      lcd.print(' ');
    }
  } else if (key == '#') {
    submit();
  } else if (key == 'D' && state != LOCKED) {
    cancelChange();
  }
}

Seri Monitörde göreceğin

SERIAL MONITOR · 9600 BAUD
Kilit hazır.
Yanlış şifre, deneme 1
Doğru şifre: kapı açıldı.
Şifre değişti ve kaydedildi.
Yanlış şifre, deneme 1
Yanlış şifre, deneme 2
Yanlış şifre, deneme 3
Çok fazla deneme: tuşlar kilitlendi.
Bekleme bitti.
Doğru şifre: kapı açıldı.
Kapı kilitlendi.

Sonuç

Açılışta servo 0°'ye döner ve ekranda "Sifre girin:" yazar; ilk şifre 1234. Rakamlara bastıkça alt satırda * belirir; * tuşu son haneyi siler, # tuşu onaylar. Şifre doğruysa servo 90°'ye döner, ekranda "Kapi acik" ve 5'ten geriye sayım görünür; süre bitince ya da # tuşuna basınca kapı yeniden kilitlenir. Yanlış şifrede kalan hak sayısı gösterilir; üst üste 3 yanlışta tuş takımı 30 saniye kilitlenir ve ekranda geri sayım görünür. Şifreyi değiştirmek için kapı açıkken A'ya bas (kapı hemen kilitlenir), yeni şifreyi (4-8 hane) yaz, #'e bas, aynısını bir kez daha yaz ve #'e bas; D tuşu vazgeçirir, 20 saniye tuşa basılmazsa da vazgeçilir. Yeni şifre EEPROM'a kaydedilir: Arduino'yu çıkarıp taksan da geçerlidir. Şifreyi unutursan koddaki EEPROM_MARK sayısını değiştirip yeniden yükle; şifre 1234'e döner. Ekran yanıyor ama yazı yoksa I2C modülündeki küçük trimpotu çevir; hâlâ yoksa koddaki 0x27'yi 0x3F yap. Bastığın tuşla ekrana gelen tuş farklıysa tuş takımının kablolarının sırasına bak; bazı tuş takımlarında soket ters sıradadır, o zaman rowPins ve colPins dizilerindeki sayıları yer değiştir. Servo dönerken ekran titriyor ya da Arduino yeniden başlıyorsa servo USB'den yeterli akım alamıyordur: kolun bir yere sıkışmadığından emin ol ya da servoyu ayrı bir 5V kaynaktan besleyip GND'leri birleştir.

Uygulamada her bağlantıyı tek tek işaretle, envanterindeki parçalarla eşleştir ve kaldığın yerden devam et.

Uygulamada adım adım yap