(Özel Proje) Ağ Durum Denetleyicisi: ESP32 ve ENC28J60 ile Kesintisiz Bağlantı Takibi

(Özel Proje) Ağ Durum Denetleyicisi: ESP32 ve ENC28J60 ile Kesintisiz Bağlantı Takibi

Giriş

Günümüzde internet bağlantısının sürekliliği, yalnızca konfor değil, çoğu sistem için bir zorunluluktur. Akıllı ev uygulamalarından güvenlik kameralarına, endüstriyel otomasyonlardan uzaktan erişimli sistemlere kadar pek çok yapı, kararlı bir internet bağlantısına bağımlıdır. Ancak modem veya yönlendirici donmaları, yazılım hataları ya da uzun süreli çalışma gibi nedenlerle ağ bağlantısının kesilmesi sık karşılaşılan bir sorundur.

Bu projede, ESP32 mikrodenetleyici ve ENC28J60 Ethernet modülü kullanılarak geliştirilen Ağ Durum Denetleyicisi sistemiyle, ağ kesintilerine karşı akıllı bir çözüm sunulmaktadır. Sistem, hem ağdaki hedef IP adresini düzenli aralıklarla kontrol eder hem de kendi Ethernet bağlantısının kararlılığını izleyerek gerektiğinde müdahalede bulunur.


Projenin Amacı ve Temel İşleyişi

Bu projenin amacı, ağ kesintilerini tespit ederek otomatik şekilde müdahale edebilen düşük maliyetli ve güvenilir bir sistem geliştirmektir. Temel prensip, önceden belirlenen bir hedef IP adresine (örneğin modem veya router) belli aralıklarla ping göndermek ve yanıt alınamazsa bir röleyi tetikleyerek cihazın elektrik bağlantısını kısa süreliğine kesmektir.

Bu işlem sayesinde modem veya yönlendirici gibi cihazlar donduğu zaman, sistem bunları otomatik olarak yeniden başlatır.

Aynı zamanda ESP32 cihazının kendi IP adresini kaybetmesi veya fiziksel bağlantısının kopması gibi durumlar da algılanır ve ENC28J60 Ethernet modülü yeniden başlatılarak sistemin kendi bağlantısı da onarılır.

ESP32 ile ENC28J60 Ethernet Modülü Bağlantısı

Bu projede, Ethernet bağlantısı için ENC28J60 modülü kullanılmaktadır. ENC28J60, SPI haberleşme protokolüyle çalışan bir Ethernet denetleyicisidir ve ESP32 ile kolayca haberleşebilir.

Gerekli Bağlantılar (SPI Temelli):

ENC28J60 PinESP32 Pin (Örnek)Açıklama
VCC3.3VGüç girişi (3.3V)
GNDGNDToprak hattı
SCKGPIO18SPI Saat sinyali
MISOGPIO19SPI Veri çıkışı (ESP okur)
MOSIGPIO23SPI Veri girişi (ESP yazar)
CS (Chip Select)GPIO5SPI cihaz seçimi
INT (opsiyonel)KullanılmayabilirKesme pini, kullanılmasa da olur

🛠 Not: ENC28J60 modülü genellikle 3.3V ile çalışır. 5V uygulamaktan kaçının, aksi takdirde zarar görebilir.

Seri Port Başlatma Ekranı

Sistem ilk başlatıldığında EEPROM’dan kayıtlı ağ ayarları okunur ve seri monitörde aşağıdaki gibi bir ekran görüntülenir:


👉 AYARLAR EEPROM'DAN YUKLENDI. mesajı ile başlar ve mevcut IP, gateway, subnet ve hedef IP bilgileri listelenir.


Ping Denetimi

Sistem çalışmaya başladığında belirlenen hedef IP adresine periyodik olarak ping gönderilir. Her başarılı ping, seri monitörde aşağıdaki gibi görünür:


👉 PING DENENIYOR: 192.168.1.2... BASARILI PING. satırları görünür.


Ping Başarısızlığı ve Röle Müdahalesi

Arka arkaya 5 ping denemesi başarısız olursa, sistem röleyi tetikler. Bu, bağlı olan cihazın (genellikle modem) yeniden başlatılması anlamına gelir:

👉 BASARISIZ DENEME: 5 ve ROLE ETKIN: 1 DAKIKA BOYUNCA ACILDI. gibi mesajlar gösterilir.

Bu müdahale sonrası sistem 1 dakika bekler ve ardından röleyi kapatarak bağlantının tekrar sağlanmasını bekler.


Kullanıcı Dostu Ayarlar için Seri Komut Sistemi

Cihaz seri port üzerinden kullanıcıyla etkileşim kurabilir. Kurulum moduna geçmek için "setup" komutunu yazmak yeterlidir. Ardından aşağıdaki komutlar kullanılabilir:

  • IP X.X.X.X → IP adresini ayarlar
  • GATEWAY X.X.X.X → Ağ geçidini ayarlar
  • SUBNET X.X.X.X → Alt ağ maskesini ayarlar
  • TARGET X.X.X.X → Ping atılacak hedef IP adresini ayarlar
  • SAVE → Ayarları EEPROM’a kaydeder
  • SHOW → Mevcut ayarları gösterir
  • EXIT → Kurulum modundan çıkar
  • RESET EEPROM → Tüm EEPROM verisini sıfırlar

Tüm bu komutlar ile sistemin esnek şekilde konfigürasyonu sağlanır.


Kod Yapısı ve Teknik Özellikler

Not : Röle için kullanılacak pini kendi projenize göre ayarlayın örnek olarak GPIO7 verilmiştir.

#include <UIPEthernet.h>
#include <EEPROM.h>

#define RELAY_PIN 7 
// EEPROM boyutu: NetworkConfig yapisi ve magic number icin yer
#define EEPROM_SIZE (sizeof(NetworkConfig) + sizeof(uint32_t)) 

// EEPROM verisinin gecerliligini kontrol etmek icin sihirli bir sayi
const uint32_t EEPROM_MAGIC = 0xABCD1234; 

struct NetworkConfig {
  byte ip[4];
  byte gateway[4];
  byte subnet[4];
  byte targetIP[4];
};

NetworkConfig config;
const byte mac[6] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

String inputBuffer = "";
bool inSetupMode = false;

// Role kontrolu icin non-blocking degiskenler
unsigned long relayActiveStartTime = 0;
bool relayIsActive = false;

// Ping kontrolu icin degiskenler
unsigned long lastPingTime = 0;
int failCount = 0; 
const unsigned long PING_INTERVAL_MS = 5000; // Her 5 saniyede bir ping

// Ethernet baglanti durumunu kontrol etmek icin degiskenler
unsigned long lastEthernetCheckTime = 0;
const unsigned long ETHERNET_CHECK_INTERVAL = 10000; // Her 10 saniyede bir kontrol

void setup() {
  Serial.begin(115200);
  pinMode(RELAY_PIN, OUTPUT);
  digitalWrite(RELAY_PIN, LOW); // Baslangicta roleyi kapali tut
  delay(1000); // Seri portun baslamasi icin kisa bir gecikme

  // ESP32 icin EEPROM'u baslatma, boyutu belirtmek önemli
  EEPROM.begin(EEPROM_SIZE); 
  
  loadConfig(); // Yapilandirmayi EEPROM'dan yukle

  IPAddress ip(config.ip[0], config.ip[1], config.ip[2], config.ip[3]);
  IPAddress gateway(config.gateway[0], config.gateway[1], config.gateway[2], config.gateway[3]);
  IPAddress subnet(config.subnet[0], config.subnet[1], config.subnet[2], config.subnet[3]);

  // Ethernet baslangici
  Ethernet.begin(mac, ip, gateway, subnet);
  Serial.print("SISTEM BASLATILDI. IP: ");
  Serial.println(Ethernet.localIP().toString());
  printConfig();
  Serial.println("Seri Porttan 'setup' yazarak kuruluma girebilirsiniz.");
}

void loop() {
  // Seri porttan komut okuma
  while (Serial.available()) { 
    char c = Serial.read();
    inputBuffer += c;
    if (c == '\n' || c == '\r') {
      inputBuffer.trim(); 
      if (inputBuffer.length() > 0) { 
        processCommand(inputBuffer);
      }
      inputBuffer = ""; 
    }
  }

  // Kurulum modunda degilsek ag islemlerini yap
  if (!inSetupMode) {
    // Sadece röle aktif değilken Ethernet bağlantısını kontrol et ve yeniden başlat
    if (!relayIsActive) { // <-- Yeni kontrol eklendi
        if (millis() - lastEthernetCheckTime >= ETHERNET_CHECK_INTERVAL) {
            lastEthernetCheckTime = millis();

            // Eğer Ethernet bağlantısı fiziksel olarak kesikse VEYA IP adresi yoksa yeniden başlatmayı dene
            if (Ethernet.linkStatus() == LinkOFF || Ethernet.localIP()[0] == 0) {
                Serial.println("ETHERNET BAGLANTISI KESIK VEYA IP ADRESI YOK. YENIDEN BAGLANMAYA CALISILIYOR...");
                
                delay(100); 

                IPAddress ip(config.ip[0], config.ip[1], config.ip[2], config.ip[3]);
                IPAddress gateway(config.gateway[0], config.gateway[1], config.gateway[2], config.gateway[3]);
                IPAddress subnet(config.subnet[0], config.subnet[1], config.subnet[2], config.subnet[3]);
                Ethernet.begin(mac, ip, gateway, subnet);
                
                Serial.print("ETHERNET YENIDEN BASLATILDI.");
                Serial.println();
            }
        }
    }


    // Non-blocking ping logic
    // Sadece röle aktif değilken ping denemesi yap
    if (millis() - lastPingTime >= PING_INTERVAL_MS && !relayIsActive) { // <-- Mevcut kontrol korunuyor
      lastPingTime = millis(); 
      IPAddress target(config.targetIP[0], config.targetIP[1], config.targetIP[2], config.targetIP[3]);
      
      Serial.print("PING DENENIYOR: ");
      Serial.print(target);
      Serial.print("...");
        
      bool success = simulatePing(target); 
      if (!success) {
        failCount++; 
        Serial.print(" BASARISIZ DENEME: ");
        Serial.println(failCount);
        if (failCount >= 5) {
          Serial.println("ROLE ETKIN: 1 DAKIKA BOYUNCA ACILDI.");
          digitalWrite(RELAY_PIN, HIGH);
          relayActiveStartTime = millis();
          relayIsActive = true;
          failCount = 0; 
        }
      } else {
        Serial.println(" BASARILI PING.");
        failCount = 0; 
      }
    }

    // Non-blocking role kapatma logic'i
    if (relayIsActive && millis() - relayActiveStartTime >= 60000) { 
      digitalWrite(RELAY_PIN, LOW);
      Serial.println("ROLE KAPATILDI.");
      relayIsActive = false;
    }
  }
}

void processCommand(String cmd) {
  cmd.trim();
  cmd.toLowerCase(); 

  if (cmd == "setup") {
    inSetupMode = true;
    Serial.println("KURULUM MODUNA GECILDI.");
    Serial.println("KOMUTLAR:");
    Serial.println("  IP X.X.X.X");
    Serial.println("  GATEWAY X.X.X.X");
    Serial.println("  SUBNET X.X.X.X");
    Serial.println("  TARGET X.X.X.X");
    Serial.println("  SAVE");
    Serial.println("  SHOW");
    Serial.println("  EXIT");
    Serial.println("  RESET EEPROM"); 
  } else if (inSetupMode && cmd.startsWith("ip ")) { 
    parseIP(cmd.substring(3), config.ip);
    Serial.print("IP AYARLANDI: ");
    printIP(config.ip);
  } else if (inSetupMode && cmd.startsWith("gateway ")) { 
    parseIP(cmd.substring(8), config.gateway);
    Serial.print("GATEWAY AYARLANDI: ");
    printIP(config.gateway);
  } else if (inSetupMode && cmd.startsWith("subnet ")) { 
    parseIP(cmd.substring(7), config.subnet);
    Serial.print("SUBNET AYARLANDI: ");
    printIP(config.subnet);
  } else if (inSetupMode && cmd.startsWith("target ")) { 
    parseIP(cmd.substring(7), config.targetIP);
    Serial.print("HEDEF IP AYARLANDI: ");
    printIP(config.targetIP);
  } else if (inSetupMode && cmd == "save") { 
    saveConfig();
    Serial.println("AYARLAR KAYDEDILDI.");
  } else if (inSetupMode && cmd == "show") { 
    printConfig();
  } else if (inSetupMode && cmd == "exit") { 
    inSetupMode = false;
    Serial.println("KURULUM MODUNDAN CIKILDI. YENI AYARLARIN UYGULANMASI ICIN CIHAZI YENIDEN BASLATMANIZ GEREKEBILIR.");
  } else if (inSetupMode && cmd == "reset eeprom") { 
    Serial.println("EEPROM SIFIRLANIYOR...");
    for (int i = 0; i < EEPROM_SIZE; ++i) {
      EEPROM.write(i, 0xFF); 
    }
    EEPROM.commit();
    Serial.println("EEPROM SIFIRLANDI. CIHAZI YENIDEN BASLATINIZ.");
  }
  else {
    if (!inSetupMode) { 
       Serial.println("Bilinen bir komut degil veya kurulum modunda degilsiniz. Kurulum modu icin 'setup' yazin.");
    } else { 
      Serial.println("Kurulum modunda bilinmeyen komut.");
    }
  }
}

void saveConfig() {
  EEPROM.put(0, EEPROM_MAGIC); 
  EEPROM.put(sizeof(uint32_t), config); 
  EEPROM.commit(); 
}
  
void loadConfig() {
  uint32_t magic;
  EEPROM.get(0, magic); 

  if (magic == EEPROM_MAGIC) {
    EEPROM.get(sizeof(uint32_t), config); 
    Serial.println("AYARLAR EEPROM'DAN YUKLENDI.");
  } else {
    Serial.println("GECERSIZ EEPROM VERISI VEYA ILK CALISTIRMA. VARSAYILAN AYARLAR YUKLENIYOR VE KAYDEDILIYOR.");
    config.ip[0] = 192; config.ip[1] = 168; config.ip[2] = 1; config.ip[3] = 177;
    config.gateway[0] = 192; config.gateway[1] = 168; config.gateway[2] = 1; config.gateway[3] = 1;
    config.subnet[0] = 255; config.subnet[1] = 255; config.subnet[2] = 255; config.subnet[3] = 0;
    config.targetIP[0] = 192; config.targetIP[1] = 168; config.targetIP[2] = 1; config.targetIP[3] = 1;
    saveConfig(); 
  }
}

void printConfig() {
  Serial.print("IP: ");
  printIP(config.ip);
  Serial.print("GATEWAY: ");
  printIP(config.gateway);
  Serial.print("SUBNET: ");
  printIP(config.subnet);
  Serial.print("HEDEF IP: ");
  printIP(config.targetIP);
}

void printIP(byte ip[4]) {
  for (int i = 0; i < 4; i++) {
    Serial.print(ip[i]);
    if (i < 3) Serial.print(".");
  }
  Serial.println();
}

void parseIP(String ipStr, byte ip[4]) {
  int currentPart = 0;
  int dotIndex = -1;
  int prevDotIndex = -1;

  for (int i = 0; i < 4; ++i) {
    dotIndex = ipStr.indexOf('.', prevDotIndex + 1);
    String part;
    if (dotIndex == -1) { 
      part = ipStr.substring(prevDotIndex + 1);
    } else {
      part = ipStr.substring(prevDotIndex + 1, dotIndex);
    }
    
    int val = part.toInt();
    if (val >= 0 && val <= 255) {
      ip[currentPart++] = (byte)val;
    } else {
      Serial.print("GECERSIZ IP BOLUMU ALGILANDI: '");
      Serial.print(part);
      Serial.println("'. VARSAYILAN OLARAK 0 ATANACAKTIR.");
      ip[currentPart++] = 0; 
    }
    
    prevDotIndex = dotIndex;
    if (dotIndex == -1) break; 
  }
  while (currentPart < 4) {
    ip[currentPart++] = 0;
  }
}

bool simulatePing(IPAddress target) {
  // Ethernet'in link durumu OFF ise ve IP almadıysa, hızlıca başarısız dön.
  // Bu, Ethernet çipini gereksiz yere TCP bağlantısı kurmaya zorlamaz ve resetlenmeyi engeller.
  if (Ethernet.linkStatus() == LinkOFF || Ethernet.localIP()[0] == 0) {
    return false; 
  }

  EthernetClient client;
  unsigned long connectAttemptTime = millis();
  const unsigned long CONNECT_TIMEOUT_MS = 3000; 

  while (!client.connect(target, 80) && (millis() - connectAttemptTime < CONNECT_TIMEOUT_MS)) {
    yield(); 
  }

  if (client.connected()) {
    client.stop(); 
    return true; 
  }
  return false; 
}

Projede kullanılan kod, aşağıdaki teknik başlıkları içerir:

  • EEPROM üzerinden yapılandırma kaydı
  • ENC28J60 ile statik IP üzerinden Ethernet bağlantısı
  • TCP bağlantısı ile ping simülasyonu
  • Röle kontrolü (non-blocking şekilde)
  • Seri porttan komut işleme
  • Kendi Ethernet bağlantısını otomatik onarma

Uygulama Alanları

Bu proje birçok alanda kullanılabilir:

  • Akıllı Evler: Bağlantı kopmalarını önleyerek otomasyonun güvenliğini artırır.
  • Uzaktan Kamera Sistemleri: Gözetim ve güvenlik sistemlerinde ağ kopmalarını otomatik olarak düzeltir.
  • Endüstriyel Otomasyon: Üretim hattı gibi kritik sistemlerde bağlantı devamlılığını sağlar.
  • Kırsal Alanlar ve Erişilmesi Zor Noktalar: Fiziksel müdahale gerektirmeden ağ cihazlarını yeniden başlatır.

Sonuç

ESP32 ve ENC28J60 kullanılarak geliştirilen bu Ağ Durum Denetleyicisi, hem kendi ağ kararlılığını kontrol eder hem de hedef cihazların bağlantı sürekliliğini sağlar. Bu sistem sayesinde pahalı ve karmaşık çözümlere gerek kalmadan, uygun maliyetle akıllı bir ağ izleme ve müdahale altyapısı oluşturulabilir.

Yorumlar kapalı.

nurullahozkan@outlook.com.tr