ESP32 ve MySQL ile Uzaktan Cihaz Kontrolü: Güvenli ve Anlık Bir IoT Çözümü

ESP32 ve MySQL ile Uzaktan Cihaz Kontrolü: Güvenli ve Anlık Bir IoT Çözümü

Giriş

Bu proje, ESP32 mikrodenetleyiciyi bir MySQL veritabanı ve PHP destekli bir web sunucusuyla birleştirerek, herhangi bir cihazın durumunu her yerden ve her cihazdan kontrol etmenizi sağlayan yenilikçi bir IoT çözümüdür. Sistem, güvenli HTTPS iletişimiyle anlık veri senkronizasyonu sunar, böylece fiziksel cihazınızın durumunu dünyanın neresinde olursanız olun, akıllı telefonunuz, tabletiniz veya bilgisayarınız üzerinden kolayca yönetebilirsiniz. Bu sayede, uzaktan erişim ve otomasyon imkanlarını parmaklarınızın ucuna getirir.

Nesnelerin İnterneti (IoT) çağında, fiziksel dünyayı dijital komutlarla yönetebilmek, otomasyon ve uzaktan kontrol açısından büyük bir potansiyel sunmaktadır. Bu makalede, güçlü ve çok yönlü bir mikrodenetleyici olan ESP32‘yi kullanarak, internet üzerinden bir fiziksel cihazı (örneğin bir ışık) nasıl uzaktan kontrol edebileceğinizi adım adım açıklayacağız. Projemiz, basit bir web arayüzü ile cihazın durumunu yönetmemizi sağlarken, aynı zamanda web sunucuları, veritabanı iletişimi ve güvenli bağlantı (HTTPS) gibi önemli IoT bileşenlerini de bir araya getiriyor.

Projenin Amacı ve Temel İşleyişi

Bu projenin temel amacı, dünyanın herhangi bir yerinden bir web sitesi aracılığıyla bir cihazın durumunu değiştirebilmek ve anlık olarak güncel durumunu görebilmektir. Sistemin ana bileşenleri ve işleyişi şu şekilde özetlenebilir:

  1. Web Arayüzü (HTML/CSS/JavaScript): Kullanıcının cihazı açıp kapatması ve durumunu görmesi için basit, kullanıcı dostu bir web sayfası sunar.
  2. Web Sunucusu (PHP): Web arayüzünden gelen komutları alır ve veritabanıyla etkileşime girer. Aynı zamanda, ESP32’nin cihazın güncel durumunu sorgulamasına olanak tanır.
  3. MySQL Veritabanı: Kontrol edilen cihazın o anki durumunu (açık/kapalı gibi bir dijital değer olarak) güvenli bir şekilde saklayan merkezi depolama birimidir.
  4. ESP32 Mikrodenetleyici: Fiziksel cihazı (örneğin bir LED’i) doğrudan kontrol eden ve veritabanındaki güncel durumu düzenli aralıklarla sorgulayan akıllı cihazdır.

İşleyiş adımları şu şekildedir: Kullanıcı web arayüzündeki kontrol butonlarına tıklar. Bu eylem, JavaScript aracılığıyla web sunucusundaki PHP betiklerine güvenli (HTTPS) bir istek olarak iletilir. PHP betikleri, veritabanındaki cihaz durumunu günceller. Aynı zamanda, ESP32 mikrodenetleyici de düzenli (saniyeler içinde) aralıklarla bu veritabanını HTTPS üzerinden sorgular, cihazın güncel durumunu öğrenir ve fiziksel çıktısını (örneğin LED’i açarak veya kapatarak) buna göre ayarlar.

Gerekli Bileşenler

Bu kapsamlı projeyi hayata geçirmek için aşağıdaki donanım ve yazılım bileşenlerine ihtiyacımız olacak:

  • Donanım:
    • ESP32 Geliştirme Kartı
    • Kontrol etmek istediğiniz bir fiziksel çıkış (örn: bir LED, bir röle veya küçük bir motor)
    • Gerekliyse uygun dirençler veya diğer elektronik bileşenler
    • Bağlantı kabloları (Jumper kablolar)
  • Yazılım:
    • Arduino IDE (ESP32 kodunu yazmak ve yüklemek için)
    • PHP ve MySQL destekli bir web hosting hesabı (örneğin www.nurullahozkan.net gibi kendi alan adınızda)
    • phpMyAdmin (MySQL veritabanınızı yönetmek için bir araç)
    • Modern bir web tarayıcısı (Microsoft Edge, Google Chrome, Mozilla Firefox vb.)

Adım 1: Proje Dosyalarının Hazırlığı

Projemizin temelini oluşturan yazılım dosyalarımızı yerel bilgisayarımızda düzenli bir şekilde tutuyoruz. Bu dosyalar, sistemin farklı katmanlarını temsil eder ve birbiriyle uyum içinde çalışır.

Görselde de gördüğünüz gibi, ArduinoIDE.ino ESP32 kartımıza yükleyeceğimiz ana program kodunu içerirken, esp32.html kullanıcı arayüzümüzü, verigonder.php ve verioku.php ise web sunucumuzda çalışacak veritabanı etkileşim betiklerimizi temsil eder.

Adım 2: MySQL Veritabanı Yapılandırması

Cihazımızın durumunu kalıcı olarak saklayacak bir veritabanına ihtiyacımız var. Bunun için PHPMyAdmin arayüzünü kullanarak MySQL veritabanımızı yapılandırıyoruz.

İlk olarak, projemiz için özel bir veritabanı oluşturduk (nurullah3_1). Bu veritabanı içinde ise cihazımızın durumunu tutacak DENEME adında bir tablo ve bu tabloda da led_durum adında bir sütun tanımladık. Bu sütun, cihazın açık (1) veya kapalı (0) durumunu sayısal olarak saklayacak.

Bu görselde, led_durum sütununun mevcut değerini görüyoruz; bu, veritabanının doğru şekilde çalıştığını ve cihazımızın durum bilgisini başarıyla tuttuğunu gösterir.

Adım 3: PHP Sunucu Betiklerinin Yüklenmesi

Web arayüzümüz ile veritabanımız arasındaki köprüyü kurmak için iki temel PHP betiği kullanıyoruz: verioku.php ve verigonder.php. Bu betikler, web sunucumuzda (örneğin www.nurullahozkan.net adresinizde) çalışır ve güvenli HTTPS bağlantıları üzerinden veri alışverişini yönetir.

  • verioku.php: ESP32 veya web arayüzü tarafından cihazın anlık durumunu öğrenmek için kullanılır. Veritabanındaki led_durum değerini okur ve 0 veya 1 olarak yanıt verir.
  • verigonder.php: Web arayüzünden gelen komutları (cihazı açma veya kapatma isteği) alır ve bu komut doğrultusunda veritabanındaki led_durum değerini günceller.
<?php
// Veritabanı Bağlantı Bilgileri
$servername = "localhost"; // Genellikle localhost, hosting sağlayıcınızın bilgisine göre değişebilir
$username = "veritababi_kullaniciadi";
$password = "veritabaniparolasi:"; // VERİTABANI ŞİFRENİZ BURADA
$dbname = "veritabani";

// Veritabanı bağlantısı oluşturma
$conn = new mysqli($servername, $username, $password, $dbname);

// Bağlantıyı kontrol etme
if ($conn->connect_error) {
    die("Veritabanı bağlantı hatası: " . $conn->connect_error);
}

// POST ile gelen led_durum değerini al
if (isset($_POST['led_durum'])) {
    $led_durum = intval($_POST['led_durum']); // Sayısal değere dönüştür

    // Tabloda sadece tek bir satır olduğu varsayıldığı için UPDATE komutunu kullanırız.

    // Önce bir satır olup olmadığını kontrol edelim
    $check_sql = "SELECT COUNT(*) AS count FROM DENEME";
    $result = $conn->query($check_sql);
    $row = $result->fetch_assoc();
    $row_count = $row['count'];

    if ($row_count == 0) {
        // Tabloda hiç satır yoksa, INSERT yap
        $sql = "INSERT INTO DENEME (led_durum) VALUES ($led_durum)";
    } else {
        // Tabloda satır varsa, UPDATE yap
        $sql = "UPDATE DENEME SET led_durum = $led_durum";
    }

    if ($conn->query($sql) === TRUE) {
        echo "Veritabanı güncellendi: " . $led_durum;
    } else {
        echo "Hata: " . $sql . "<br>" . $conn->error;
    }
} else {
    echo "led_durum değeri POST ile gönderilmedi.";
}

$conn->close();
?>
<?php
// Veritabanı Bağlantı Bilgileri
$servername = "localhost"; // Genellikle localhost, hosting sağlayıcınızın bilgisine göre değişebilir
$username = "veritababi_kullaniciadi";
$password = "veritabaniparolasi:"; // VERİTABANI ŞİFRENİZ BURADA
$dbname = "veritabani";

// Veritabanı bağlantısı oluşturma
$conn = new mysqli($servername, $username, $password, $dbname);

// Bağlantıyı kontrol etme
if ($conn->connect_error) {
    // Bağlantı hatasında 0 gönder ve betiği sonlandır
    echo "0";
    die("Veritabanı bağlantı hatası: " . $conn->connect_error);
}

// led_durum değerini sorgulama (tabloda sadece tek bir satır olması beklenir)
$sql = "SELECT led_durum FROM DENEME LIMIT 1";
$result = $conn->query($sql);

if ($result && $result->num_rows > 0) {
    // Veri bulundu
    $row = $result->fetch_assoc();
    echo $row["led_durum"]; // led_durum değerini yazdır
} else {
    // Veri bulunamazsa veya sorgu başarısız olursa varsayılan olarak kapalı (0) gönder
    echo "0";
    // Tabloda hiç satır yoksa, başlangıç değeri eklemek iyi bir fikir olabilir
    // Bu kısım, ilk kurulumda tablo boşsa otomatik olarak bir varsayılan değer ekler.
    $insert_sql = "INSERT INTO DENEME (led_durum) VALUES (0)";
    if ($conn->query($insert_sql) === TRUE) {
        // Serial.println("Tabloya varsayilan deger eklendi."); // Bu, PHP çıktısına yansıyabilir, dikkatli olun
    } else {
        // Serial.println("Varsayilan deger eklenirken hata: " . $conn->error);
    }
}

$conn->close();
?>

Bu PHP dosyalarını, web sitenizin ana dizinine veya web sunucunuzda erişilebilir bir alt dizine yüklemelisiniz. Özellikle bu dosyaların veritabanınıza doğru kullanıcı adı ve şifre ile bağlanabildiğinden emin olunması kritik öneme sahiptir.

Adım 4: Web Arayüzü (esp32.html) Oluşturma ve Yükleme

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>ESP32 LED Kontrol</title>
    <script type="text/javascript" src="https://me.kis.v2.scr.kaspersky-labs.com/FD126C42-EBFA-4E12-B309-BB3FDD723AC1/main.js?attr=lz6CnEWQZcqrrPm8ENmN1lDSausuKYnE21vRP2AHTAgk_5ZpYcaRIxGOVHhaCO30y0pH1OD8n6frGjRngEDZqQWGKn1PN-7Hclp8sSAEXiBttzi2-fCV9B_zklYZ_isNmMEzzjNU7dJyrV8aN4NMaNi20Z1HNvqt79NnDX4iZpGnNdBVdSP8uWxmKuaQzcG6" charset="UTF-8"></script><link rel="stylesheet" crossorigin="anonymous" href="https://me.kis.v2.scr.kaspersky-labs.com/E3E8934C-235A-4B0E-825A-35A08381A191/abn/main.css?attr=aHR0cHM6Ly9jcDYwLnNlcnZlcm5hbWUuY286MjA4My9jcHNlc3M3OTk1MTU4ODA0L2Rvd25sb2FkP3NraXBlbmNvZGU9MSZmaWxlPSUyZmhvbWUlMmZudXJ1bGxhaDMlMmZwdWJsaWNfaHRtbCUyZmVzcDMyLmh0bWw"/><style>
        body {
            font-family: Arial, sans-serif;
            background-color: #007bff; /* Mavi arka plan */
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            margin: 0;
        }
        .container {
            background-color: #ffffff; /* Beyaz kutu */
            padding: 30px;
            border-radius: 10px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
            text-align: center;
            width: 300px;
        }
        h1 {
            color: #333;
            margin-bottom: 20px;
        }
        .led-status {
            font-size: 3em;
            font-weight: bold;
            margin-bottom: 30px;
            color: #007bff;
        }
        .button-container button {
            background-color: #28a745; /* Yeşil AÇ butonu */
            color: white;
            padding: 15px 30px;
            border: none;
            border-radius: 5px;
            font-size: 1.2em;
            cursor: pointer;
            margin: 10px;
            transition: background-color 0.3s ease;
        }
        .button-container button.off {
            background-color: #dc3545; /* Kırmızı KAPAT butonu */
        }
        .button-container button:hover {
            opacity: 0.9;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>LED Durumu</h1>
        <p class="led-status" id="ledStatus">Durum Yükleniyor...</p>
        <div class="button-container">
            <button onclick="sendLedCommand(1)">AÇ</button>
            <button class="off" onclick="sendLedCommand(0)">KAPAT</button>
        </div>
    </div>

    <script>
        const SERVER_URL = "https://www.nurullahozkan.net";

        function updateStatus() {
            var xhr = new XMLHttpRequest();
            xhr.onreadystatechange = function() {
                if (this.readyState == 4 && this.status == 200) {
                    var status = this.responseText.trim();
                    var ledStatusElement = document.getElementById("ledStatus");
                    if (status === "1") {
                        ledStatusElement.innerHTML = "AÇIK";
                        ledStatusElement.style.color = "#28a745"; // Yeşil
                    } else {
                        ledStatusElement.innerHTML = "KAPALI";
                        ledStatusElement.style.color = "#dc3545"; // Kırmızı
                    }
                } else if (this.readyState == 4) {
                    var ledStatusElement = document.getElementById("ledStatus");
                    ledStatusElement.innerHTML = "Durum Okunamadı";
                    ledStatusElement.style.color = "orange";
                    console.error("Durum okuma hatası: " + this.status);
                }
            };
            xhr.open("GET", SERVER_URL + "/verioku.php", true);
            xhr.send();
        }

        function sendLedCommand(status) {
            var xhr = new XMLHttpRequest();
            xhr.open("POST", SERVER_URL + "/verigonder.php", true);
            xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
            xhr.onreadystatechange = function() {
                if (this.readyState == 4 && this.status == 200) {
                    console.log("Komut gönderildi, yanıt: " + this.responseText);
                    updateStatus(); // Komut sonrası durumu güncelle
                } else if (this.readyState == 4) {
                    console.error("Komut gönderme hatası: " + this.status);
                    alert("LED kontrol hatası oluştu!");
                }
            };
            xhr.send("led_durum=" + status);
        }

        window.onload = updateStatus;
        setInterval(updateStatus, 2000);
    </script>
</body>
</html>

Kullanıcıların cihazı kontrol edebileceği arayüzümüz esp32.html adını taşıyan basit bir web sayfasıdır. Bu sayfa, HTML yapısını, görsel tasarımı için CSS’i ve sunucu ile etkileşim kurmak için JavaScript’i bir araya getirir.

Önemli Not: esp32.html dosyasını, PHP dosyalarınızla aynı web sunucusuna yüklemeniz gerekmektedir (örneğin https://www.nurullahozkan.net/esp32.html). Bu, tarayıcıların güvenlik politikaları (CORS) nedeniyle HTTP/HTTPS isteklerini engellemesini önlemek için zorunludur. Web sayfası içindeki JavaScript kodu, SERVER_URL sabitini güncelleyerek PHP dosyalarına HTTPS üzerinden doğru adresten ulaşmasını sağlar.

Yukarıdaki görsel, kullanıcı arayüzümüzün ne kadar basit ve işlevsel olduğunu gösteriyor. Bu arayüz sayesinde, cihazın güncel durumu (örneğin “AÇIK”) net bir şekilde görüntülenir ve kullanıcılar tek bir tıklamayla cihazı yönetebilir.

Adım 5: ESP32 Arduino Kodunun Yüklenmesi

#include <WiFi.h>
#include <HTTPClient.h>
#include <WiFiClientSecure.h>

// --- WiFi Bağlantı Bilgileri ---
const char* ssid = "WIFIADINIZ";
const char* password = "WIFISIFRE";

// --- LED Pini Tanımı ---
const int ledPin = 2;

// --- Veritabanı (PHP Üzerinden) Bilgileri ---
const char* dbHostName = "www.nurullahozkan.net";
const char* veriOkuPath = "/verioku.php";

// --- Zamanlayıcı Ayarı ---
unsigned long previousMillis = 0;
const long interval = 1000; // 1 saniyede bir veritabanını kontrol et

// --- Fonksiyon Tanımlaması ---
void updateLedStatusFromDB();
void reconnectWiFi(); // Yeni: WiFi yeniden bağlanma fonksiyonu

void setup() {
  Serial.begin(115200);
  pinMode(ledPin, OUTPUT);

  // --- WiFi Bağlantısı (Dinamik IP) ---
  Serial.print("WiFi'ye baglaniliyor: ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  // setup'ta bağlantı kurulana kadar bekle
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nWiFi baglantisi basarili!");
  Serial.print("ESP32 IP Adresi: ");
  Serial.println(WiFi.localIP());

  // Başlangıçta LED durumunu bir kere çek ve ayarla
  updateLedStatusFromDB();
}

void loop() {
  // WiFi bağlantısını sürekli kontrol et
  if (WiFi.status() != WL_CONNECTED) {
    reconnectWiFi(); // Bağlantı yoksa yeniden bağlanmaya çalış
  }

  unsigned long currentMillis = millis();

  // Belirli aralıklarla veritabanını kontrol et
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    // Sadece WiFi bağlıysa veritabanını sorgula
    if (WiFi.status() == WL_CONNECTED) {
      updateLedStatusFromDB();
    } else {
      Serial.println("WiFi bagli degil, veritabani sorgusu atlandi.");
    }
  }
}

/**
 * WiFi bağlantısı kesildiğinde yeniden bağlanmaya çalışır.
 */
void reconnectWiFi() {
  Serial.println("WiFi baglantisi kesildi, yeniden baglaniliyor...");
  digitalWrite(ledPin, LOW); // İsteğe bağlı: Bağlantı kesildiğinde LED'i kapat
  
  WiFi.begin(ssid, password);
  unsigned long startAttemptTime = millis();
  // Bağlantı kurulana kadar veya belirli bir süre geçene kadar bekle
  while (WiFi.status() != WL_CONNECTED && millis() - startAttemptTime < 30000) { // 30 saniye boyunca dene
    delay(500);
    Serial.print(".");
  }

  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("\nWiFi yeniden baglandi!");
    Serial.print("Yeni IP Adresi: ");
    Serial.println(WiFi.localIP());
    // Bağlantı kurulunca LED durumunu hemen güncelle
    updateLedStatusFromDB();
  } else {
    Serial.println("\nWiFi yeniden baglanamadi. Tekrar denenecek.");
    // Başarısız olursa belirli bir süre bekleyip tekrar loop'a dönecek
  }
}

/**
 * Veritabanından LED durumunu çeker ve LED'i buna göre ayarlar.
 */
void updateLedStatusFromDB() {
  WiFiClientSecure client;
  client.setInsecure(); 

  HTTPClient http;
  
  String url = String("https://") + dbHostName + veriOkuPath;

  Serial.print("Veritabani kontrolu icin URL: ");
  Serial.println(url);

  http.begin(client, url);

  int httpCode = http.GET();
  if (httpCode > 0) {
    if (httpCode == HTTP_CODE_OK) {
      String payload = http.getString();
      Serial.print("Veritabanindan gelen (Ham): ");
      Serial.println(payload);

      payload.trim(); 
      
      Serial.print("Veritabanindan gelen (Temizlenmis): ");
      Serial.println(payload);

      if (payload == "1") {
        digitalWrite(ledPin, HIGH);
        Serial.println("LED ACIK olarak ayarlandi.");
      } else if (payload == "0") {
        digitalWrite(ledPin, LOW);
        Serial.println("LED KAPALI olarak ayarlandi.");
      } else {
        Serial.println("Gecersiz veri alindi. LED durumunda degisiklik yapilmadi.");
      }
    }
  } else {
    Serial.printf("[HTTP] GET hatasi: %s\n", http.errorToString(httpCode).c_str());
    // Hata durumunda LED'in durumunu değiştirmemeyi tercih ediyoruz.
    // Ancak isterseniz belirli bir duruma ayarlayabilirsiniz (örn: digitalWrite(ledPin, LOW);)
  }
  http.end();
}

Projemizin beyni olan ESP32, veritabanındaki cihaz durumunu düzenli olarak kontrol ederek fiziksel çıkışı günceller. Arduino IDE’yi kullanarak bu kodu ESP32 kartımıza yüklüyoruz.

ESP32 kodu şu temel işlevleri yerine getirir:

  • Wi-Fi ağınıza dinamik IP adresiyle bağlanır.
  • Her 1 saniyede bir (ayarlanabilir aralık), HTTPS üzerinden verioku.php betiğine bir istek gönderir.
  • verioku.php‘den gelen yanıtı (0 veya 1) okur.
  • Yanıt 1 ise cihaza bağlı çıkışı açar (örn: LED’i yakar); 0 ise kapatır (örn: LED’i söndürür).
  • İnternet bağlantısı kesildiğinde veya sunucuya ulaşılamadığında, ESP32 otomatik olarak Wi-Fi ağına yeniden bağlanmaya çalışır ve bağlantı kurulduğunda veritabanı sorgulamasına devam eder.

Kodu ESP32’ye yükledikten sonra, Arduino IDE’nin Seri Port Ekranı’nı açarak ESP32’nizin tüm bu süreçleri nasıl yönettiğini gözlemleyebiliriz.

Seri Port ekranı, ESP32’nin ağa başarıyla bağlandığını, veritabanına ulaştığını ve cihazın durumunu (1 olarak alınmış) başarıyla güncellediğini açıkça göstermektedir. Bu anlık geri bildirim, sistemin sorunsuz çalıştığını doğrular.

Sonuç

Bu projeyle, ESP32’nin gücünü, MySQL veritabanının esnekliğini ve PHP’nin sunucu tarafı yeteneklerini bir araya getirerek uzaktan cihaz kontrolü için basit ama oldukça etkili bir IoT çözümü geliştirdik. HTTPS üzerinden güvenli iletişim, Wi-Fi kesintilerine karşı otomatik yeniden bağlanma ve neredeyse anlık durum güncellemeleri sayesinde, kurduğumuz bu sistem, gelecekteki ev otomasyonu veya diğer IoT projeleri için sağlam bir temel sunmaktadır. Bu proje, sadece bir cihazı uzaktan kontrol etmekle kalmayıp, IoT ekosisteminin temel çalışma prensiplerini de anlamamıza olanak tanımıştır.

Yorumlar kapalı.

nurullahozkan@outlook.com.tr