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 Pin | ESP32 Pin (Örnek) | Açıklama |
---|---|---|
VCC | 3.3V | Güç girişi (3.3V) |
GND | GND | Toprak hattı |
SCK | GPIO18 | SPI Saat sinyali |
MISO | GPIO19 | SPI Veri çıkışı (ESP okur) |
MOSI | GPIO23 | SPI Veri girişi (ESP yazar) |
CS (Chip Select) | GPIO5 | SPI cihaz seçimi |
INT (opsiyonel) | Kullanılmayabilir | Kesme 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 ayarlarGATEWAY X.X.X.X
→ Ağ geçidini ayarlarSUBNET X.X.X.X
→ Alt ağ maskesini ayarlarTARGET X.X.X.X
→ Ping atılacak hedef IP adresini ayarlarSAVE
→ Ayarları EEPROM’a kaydederSHOW
→ Mevcut ayarları gösterirEXIT
→ Kurulum modundan çıkarRESET 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.