Search This Blog

Thursday, 26 June 2025

ESP32 TERIMA NOTIF SETING 10 NOTIF SETING IP STATIK CLEN PLUS PORT SETING + SIM800L+TOMBOL TES CALL AJA

 #include <WiFi.h>

#include <WebServer.h>

#include <Preferences.h>


// Untuk Hardware Serial, gunakan Serial1 atau Serial2 pada ESP32.

// Serial1: RX (GPIO9), TX (GPIO10)

// Serial2: RX (GPIO16), TX (GPIO17)

// Pastikan pin ini tidak digunakan untuk hal lain.

HardwareSerial sim800l(2); // Menggunakan Hardware Serial '2' (Serial2)


// --- Pengaturan Dasar ---

#define MAX_CODES 10 // Jumlah maksimum kode notifikasi yang dapat disimpan


// Variabel Global

Preferences preferences; // Objek untuk menyimpan dan memuat pengaturan dari NVS (Non-Volatile Storage)

WebServer server;      // Inisialisasi WebServer tanpa port spesifik di sini

int web_port = 80;     // Port default server web


// Pengaturan Klien WiFi (Default)

String client_ssid = "Dara@home";

String client_pass = "rejeki88";


// Variabel global baru untuk konfigurasi IP statis

String client_ip = "";      // Alamat IP statis

String client_gateway = ""; // Gateway statis

String client_subnet = "";  // Subnet Mask statis

String client_dns1 = "";    // Opsional: Server DNS Primer

String client_dns2 = "";    // Opsional: Server DNS Sekunder


// Pengaturan Access Point (Fallback) - Digunakan jika koneksi WiFi klien gagal

const char* ap_ssid = "PUTU@TECKNO-SIM800L";

const char* ap_pass = "rejeki88";


// Array untuk menyimpan kode notifikasi yang diizinkan

String allowedCodes[MAX_CODES];


// Variabel global baru untuk nomor telepon penerima

String targetPhoneNumber1 = "";

String targetPhoneNumber2 = "";

String targetPhoneNumber3 = "";


// Variabel untuk menyimpan status saat ini

String lastNotification = "Belum ada";     // Menampilkan notifikasi terakhir yang diterima

String ipAddress = "N/A";                 // Menampilkan alamat IP ESP32 saat ini

String testActionStatus = "";             // Variabel untuk menyimpan status aksi tes (SMS/Panggilan)


// Static variable untuk melacak nomor telepon yang akan dipanggil berikutnya dari notifikasi

static int nextCallIndex = 0; 


// --- PENGATURAN UNTUK SIM800L ---

// PIN untuk Hardware Serial 2 (Serial2): RX2 (GPIO16), TX2 (GPIO17)

// Pastikan Anda menghubungkan SIM800L TX ke RX2 (GPIO16) dan SIM800L RX ke TX2 (GPIO17)

// Anda dapat mengubah pin ini jika perlu saat inisialisasi HardwareSerial,

// tetapi GPIO16 dan GPIO17 adalah pin default untuk Serial2.


/**

 * @brief Mengirim SMS ke semua nomor telepon yang dikonfigurasi.

 *

 * @param message Pesan SMS yang akan dikirim.

 */

void sendSMS(String message) {

  Serial.println("Mengirim SMS: " + message);

  // Array nomor telepon target untuk iterasi yang lebih mudah

  String phoneNumbers[] = {targetPhoneNumber1, targetPhoneNumber2, targetPhoneNumber3};

  bool smsSent = false; // Flag untuk melacak apakah setidaknya satu SMS berhasil dikirim


  for (int i = 0; i < 3; i++) {

    if (phoneNumbers[i] != "") { // Hanya kirim jika nomor tidak kosong

      Serial.println("Mengirim ke: " + phoneNumbers[i]);

      sim800l.println("AT+CMGF=1"); // Atur mode teks

      delay(1000);

      sim800l.println("AT+CMGS=\"" + phoneNumbers[i] + "\"");

      delay(1000);

      sim800l.print(message); // Isi pesan

      delay(100);

      sim800l.write(26); // Karakter CTRL+Z (ASCII 26) untuk mengirim SMS

      delay(5000); // Beri waktu modul untuk mengirim SMS


      String simResponse = "";

      // Baca respons dari modul SIM800L

      while (sim800l.available()) {

        simResponse += (char)sim800l.read();

      }

      Serial.print("Respons SIM800L: " + simResponse);


      if (simResponse.indexOf("OK") != -1) {

        Serial.println("\nSMS terkirim ke " + phoneNumbers[i]);

        smsSent = true;

      } else {

        Serial.println("\nGagal mengirim SMS ke " + phoneNumbers[i]);

      }

    }

  }

  // Perbarui status aksi tes berdasarkan keberhasilan pengiriman SMS

  if (smsSent) {

    testActionStatus = "SMS tes berhasil dikirim!";

  } else {

    testActionStatus = "SMS tes gagal dikirim atau tidak ada nomor terdaftar.";

  }

}


/**

 * @brief Melakukan panggilan ke nomor telepon yang dikonfigurasi.

 *

 * @param phoneNumber Nomor telepon tujuan panggilan.

 */

void makeCall(String phoneNumber) {

  if (phoneNumber == "") {

    Serial.println("Tidak ada nomor telepon untuk melakukan panggilan tes.");

    testActionStatus = "Gagal: Tidak ada nomor telepon tes terdaftar.";

    return;

  }


  Serial.println("Melakukan panggilan ke: " + phoneNumber);

  sim800l.println("ATD" + phoneNumber + ";"); // Perintah AT untuk melakukan panggilan

  delay(10000); // Beri waktu modul untuk memanggil (sekitar 10 detik)


  String simResponse = "";

  // Baca respons dari modul SIM800L

  while (sim800l.available()) {

    simResponse += (char)sim800l.read();

  }

  Serial.print("Respons SIM800L: " + simResponse);


  if (simResponse.indexOf("OK") != -1) {

    Serial.println("\nPanggilan tes berhasil dimulai ke " + phoneNumber);

    testActionStatus = "Panggilan tes berhasil dimulai ke " + phoneNumber + ".";

  } else {

    Serial.println("\nGagal memulai panggilan tes ke " + phoneNumber);

    testActionStatus = "Panggilan tes gagal dimulai ke " + phoneNumber + ".";

  }

  // Anda mungkin ingin menambahkan perintah ATH untuk mengakhiri panggilan setelah beberapa waktu

  delay(15000); // Biarkan panggilan berdering sebentar

  sim800l.println("ATH"); // Akhiri panggilan

  delay(1000);

}



// --- Fungsi Web Server ---


/**

 * @brief Menangani permintaan ke jalur root ("/").

 * Membangun dan mengirim halaman HTML untuk konfigurasi perangkat.

 */

void handleRoot() {

  // Bangun halaman HTML untuk konfigurasi

  String html = "<!DOCTYPE html><html><head><meta charset='UTF-8' name='viewport' content='width=device-width, initial-scale=1.0'>"

                "<title>ESP32 Receiver Config</title>"

                "<style>"

                "body{font-family:Arial,sans-serif;background:#f2f2f2;margin:0;padding:15px;}"

                ".container{max-width:800px;margin:auto;background:white;padding:20px;border-radius:10px;box-shadow:0 2px 10px rgba(0,0,0,0.1);}"

                "h1,h2{color:#333;}"

                "form label{display:block;margin-top:10px;font-weight:bold;}"

                "form input[type='text'],form input[type='password']{width:calc(100% - 20px);padding:8px;margin-top:5px;border:1px solid #ccc;border-radius:5px;}"

                "form input[type='submit'], .test-button{background-color:#007bff;color:white;border:none;padding:12px 20px;border-radius:5px;cursor:pointer;font-size:16px;margin-top:20px;display:inline-block;margin-right:10px;}"

                ".test-button.call{background-color:#28a745;}"

                ".test-button.sms{background-color:#ffc107; color:black;}"

                ".status{background:#e9f5ff;border-left:5px solid #007bff;padding:10px;margin-top:20px;}"

                ".grid{display:grid;grid-template-columns:repeat(auto-fit, minmax(250px, 1fr));grid-gap:10px;}"

                ".message {background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; padding: 10px; border-radius: 5px; margin-bottom: 15px;}"

                ".error-message {background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; padding: 10px; border-radius: 5px; margin-bottom: 15px;}"

                "</style></head><body>"

                "<div class='container'>"

                "<h1>Konfigurasi Penerima Notifikasi</h1>";


  // Tampilkan pesan status aksi tes (SMS/Panggilan) jika ada

  if (testActionStatus != "") {

    if (testActionStatus.startsWith("Gagal")) {

      html += "<div class='error-message'>" + testActionStatus + "</div>";

    } else {

      html += "<div class='message'>" + testActionStatus + "</div>";

    }

    testActionStatus = ""; // Hapus status setelah ditampilkan untuk mencegah tampilan berulang

  }


  html += "<div class='status'>"

            "<h2>Status Saat Ini</h2>"

            "<strong>Status WiFi:</strong> " + (WiFi.status() == WL_CONNECTED ? "Terhubung ke " + client_ssid : "Mode Access Point Aktif") + "<br>"

            "<strong>Alamat IP:</strong> " + ipAddress + "<br>"

            "<strong>Port Web:</strong> " + String(web_port) + "<br>" // Tampilkan port web saat ini

            "<strong>Notifikasi Terakhir:</strong> " + lastNotification +

            "</div>"

            "<form action='/save' method='post'>"

            "<h2>Pengaturan WiFi Client</h2>"

            "<label for='ssid'>Nama WiFi (SSID):</label>"

            "<input type='text' name='ssid' value='" + client_ssid + "'>"

            "<label for='password'>Password WiFi:</label>"

            "<input type='password' name='password' value='" + client_pass + "'>"


            "<h2>Pengaturan Port Web</h2>" // Bagian baru untuk port web

            "<label for='port'>Port Web (misalnya 80, 8080):</label>"

            "<input type='text' name='port' value='" + String(web_port) + "'>"


            "<h2>Pengaturan IP Statis (Kosongkan untuk DHCP)</h2>" // Bagian untuk IP statis

            "<label for='ip'>Alamat IP:</label>"

            "<input type='text' name='ip' value='" + client_ip + "'>"

            "<label for='gateway'>Gateway:</label>"

            "<input type='text' name='gateway' value='" + client_gateway + "'>"

            "<label for='subnet'>Subnet Mask:</label>"

            "<input type='text' name='subnet' value='" + client_subnet + "'>"

            "<label for='dns1'>DNS Primer (Opsional):</label>"

            "<input type='text' name='dns1' value='" + client_dns1 + "'>"

            "<label for='dns2'>DNS Sekunder (Opsional):</label>"

            "<input type='text' name='dns2' value='" + client_dns2 + "'>"


            "<h2>Nomor Telepon Penerima Notifikasi</h2>" // Bagian untuk nomor telepon

            "<label for='phone1'>Nomor HP 1:</label>"

            "<input type='text' name='phone1' value='" + targetPhoneNumber1 + "'>"

            "<label for='phone2'>Nomor HP 2:</label>"

            "<input type='text' name='phone2' value='" + targetPhoneNumber2 + "'>"

            "<label for='phone3'>Nomor HP 3:</label>"

            "<input type='text' name='phone3' value='" + targetPhoneNumber3 + "'>"

            "<h2>Kode Notifikasi yang Diizinkan (10 Kode)</h2>"

            "<div class='grid'>";


  // Tambahkan bidang input untuk setiap kode yang diizinkan

  for (int i = 0; i < MAX_CODES; i++) {

    html += "<div><label for='code" + String(i) + "'>Kode #" + String(i + 1) + "</label>"

            "<input type='text' name='code" + String(i) + "' value='" + allowedCodes[i] + "'></div>";

  }


  html += "</div>"

            "<input type='submit' value='Simpan Pengaturan & Restart'>"

            "</form>"

            "<h2>Tes SIM800L</h2>"

            "<a href='/testCall' class='test-button call'>Tes Panggilan ke HP1</a>"

            "<a href='/testSms' class='test-button sms'>Tes SMS ke HP1</a>"

            "</div></body></html>";


  // Kirim respons HTML ke klien

  server.send(200, "text/html", html);

}


/**

 * @brief Menangani permintaan untuk menyimpan konfigurasi yang dikirim melalui formulir POST.

 * Menyimpan pengaturan ke NVS dan me-restart ESP32.

 */

void handleSave() {

  // Mulai preferensi dalam mode baca-tulis

  preferences.begin("config", false);


  // Simpan kredensial WiFi

  client_ssid = server.arg("ssid");

  client_pass = server.arg("password");

  preferences.putString("ssid", client_ssid);

  preferences.putString("password", client_pass);


  // Simpan pengaturan port web

  web_port = server.arg("port").toInt();

  if (web_port == 0) web_port = 80; // Pastikan port valid, default ke 80 jika 0 atau tidak valid

  preferences.putInt("web_port", web_port);


  // Simpan pengaturan IP statis

  client_ip = server.arg("ip");

  client_gateway = server.arg("gateway");

  client_subnet = server.arg("subnet");

  client_dns1 = server.arg("dns1");

  client_dns2 = server.arg("dns2");

  preferences.putString("ip", client_ip);

  preferences.putString("gateway", client_gateway);

  preferences.putString("subnet", client_subnet);

  preferences.putString("dns1", client_dns1);

  preferences.putString("dns2", client_dns2);


  // Simpan nomor telepon penerima

  targetPhoneNumber1 = server.arg("phone1");

  targetPhoneNumber2 = server.arg("phone2");

  targetPhoneNumber3 = server.arg("phone3");

  preferences.putString("phone1", targetPhoneNumber1);

  preferences.putString("phone2", targetPhoneNumber2);

  preferences.putString("phone3", targetPhoneNumber3);


  // Simpan 10 kode notifikasi

  for (int i = 0; i < MAX_CODES; i++) {

    allowedCodes[i] = server.arg("code" + String(i));

    // Gunakan .c_str() untuk mengonversi String ke const char* untuk preference.putString

    preferences.putString(("code" + String(i)).c_str(), allowedCodes[i]);

  }


  // Akhiri preferensi, melakukan perubahan ke NVS

  preferences.end();


  // Respons HTML setelah menyimpan

  String html = "<!DOCTYPE html><html><head><title>Saved</title><style>"

                "body{font-family:Arial,sans-serif;text-align:center;padding-top:50px;}"

                "h1{color:#4CAF50;}"

                "</style></head><body>"

                "<h1>Pengaturan Disimpan!</h1>"

                "<p>Perangkat akan restart dalam 2 detik...</p>"

                "</body></html>";

  server.send(200, "text/html", html);

  delay(2000); // Tunggu 2 detik sebelum memulai ulang

  ESP.restart(); // Mulai ulang ESP32 untuk menerapkan pengaturan baru

}


/**

 * @brief Menangani permintaan notifikasi yang masuk ke jalur "/notif".

 * Memeriksa kode yang diterima dan memicu aksi (panggilan bergantian) jika valid.

 */

void handleNotif() {

  // Periksa apakah argumen 'src' ada dalam permintaan GET

  if (server.hasArg("src")) {

    String receivedCode = server.arg("src"); // Dapatkan kode dari parameter 'src'

    Serial.println("Notifikasi diterima: " + receivedCode);

    lastNotification = receivedCode; // Perbarui status notifikasi terakhir di UI


    bool isValid = false;

    // Periksa apakah kode yang diterima ada dalam daftar yang diizinkan

    for (int i = 0; i < MAX_CODES; i++) {

      // Pastikan kode yang diizinkan tidak kosong sebelum membandingkan

      if (allowedCodes[i] != "" && receivedCode == allowedCodes[i]) {

        isValid = true;

        break; // Kode ditemukan, tidak perlu memeriksa lebih lanjut

      }

    }


    if (isValid) {

      Serial.println("Kode valid. Memicu aksi panggilan bergantian...");

      server.send(200, "text/plain", "OK. Notifikasi diterima.");

        

      // --- TEMPATKAN AKSI ANDA DI SINI ---

      // Gantikan sendSMS dengan makeCall ke semua nomor bergantian

      String phoneNumbers[3] = {targetPhoneNumber1, targetPhoneNumber2, targetPhoneNumber3};

      

      // Cari nomor telepon yang valid untuk dipanggil berikutnya secara bergantian

      bool numberFound = false;

      int initialIndex = nextCallIndex; // Simpan index awal untuk deteksi loop


      // Loop untuk menemukan nomor valid berikutnya, mengabaikan yang kosong

      for (int i = 0; i < 3; ++i) { // Coba maksimal 3 kali untuk memastikan kita melewati semua slot

          if (phoneNumbers[nextCallIndex] != "") {

              makeCall(phoneNumbers[nextCallIndex]);

              numberFound = true;

              nextCallIndex = (nextCallIndex + 1) % 3; // Maju ke nomor berikutnya

              break; // Nomor ditemukan dan dipanggil, keluar dari loop

          }

          nextCallIndex = (nextCallIndex + 1) % 3; // Jika nomor saat ini kosong, maju

          if (nextCallIndex == initialIndex) { // Jika sudah kembali ke awal dan belum menemukan nomor

              break; // Mencegah loop tak terbatas jika semua nomor kosong

          }

      }


      if (!numberFound) {

          testActionStatus = "Panggilan notifikasi gagal: Tidak ada nomor telepon terdaftar yang valid.";

          Serial.println("Panggilan notifikasi gagal: Tidak ada nomor telepon terdaftar yang valid.");

      } else {

          // makeCall sudah memperbarui testActionStatus, jadi tidak perlu di sini

          Serial.println("Panggilan notifikasi berhasil dipicu.");

      }

      // ------------------------------------


    } else {

      Serial.println("Kode tidak valid.");

      server.send(401, "text/plain", "Tidak Sah. Kode tidak ada dalam daftar yang diizinkan.");

    }

  } else {

    // Jika parameter 'src' hilang

    server.send(400, "text/plain", "Permintaan Buruk. Parameter 'src' hilang.");

  }

}


/**

 * @brief Menangani permintaan tes panggilan ke nomor HP pertama.

 * Memicu fungsi makeCall dan mengarahkan kembali ke halaman utama.

 */

void handleTestCall() {

  Serial.println("Permintaan tes panggilan diterima.");

  makeCall(targetPhoneNumber1); // Lakukan panggilan ke nomor HP pertama

  // Setelah aksi, arahkan kembali ke halaman root untuk memperbarui status

  server.sendHeader("Location", "/");

  server.send(302, "text/plain", "Redirecting to home...");

}


/**

 * @brief Menangani permintaan tes SMS.

 * Memicu fungsi sendSMS dengan pesan tes dan mengarahkan kembali ke halaman utama.

 */

void handleTestSms() {

  Serial.println("Permintaan tes SMS diterima.");

  sendSMS("Tes SMS dari ESP32!"); // Kirim SMS tes

  // Setelah aksi, arahkan kembali ke halaman root untuk memperbarui status

  server.sendHeader("Location", "/");

  server.send(302, "text/plain", "Redirecting to home...");

}



// --- Fungsi Setup dan Loop ---


/**

 * @brief Memuat pengaturan yang disimpan dari NVS (Non-Volatile Storage).

 */

void loadSettings() {

  preferences.begin("config", true); // Buka NVS dalam mode hanya-baca


  client_ssid = preferences.getString("ssid", "Dara@home"); // Muat SSID, dengan nilai default

  client_pass = preferences.getString("password", "rejeki88"); // Muat password, dengan nilai default


  web_port = preferences.getInt("web_port", 80); // Muat port web, default ke 80


  // Muat pengaturan IP statis

  client_ip = preferences.getString("ip", "");

  client_gateway = preferences.getString("gateway", "");

  client_subnet = preferences.getString("subnet", "");

  client_dns1 = preferences.getString("dns1", "");

  client_dns2 = preferences.getString("dns2", "");


  // Muat nomor telepon penerima

  targetPhoneNumber1 = preferences.getString("phone1", "");

  targetPhoneNumber2 = preferences.getString("phone2", "");

  targetPhoneNumber3 = preferences.getString("phone3", "");


  // Muat 10 kode notifikasi, dengan pesan default untuk beberapa yang pertama

  for (int i = 0; i < MAX_CODES; i++) {

    String default_msg = "";

    if (i == 0) default_msg = "UPS1 MATI";

    if (i == 1) default_msg = "UPS2 MATI";

    if (i == 2) default_msg = "PLN MATI";

    if (i == 3) default_msg = "GENSET MATI";


    allowedCodes[i] = preferences.getString(("code" + String(i)).c_str(), default_msg);

  }

  preferences.end(); // Tutup preferensi

}


/**

 * @brief Mengatur koneksi WiFi, mencoba mode klien terlebih dahulu,

 * kemudian beralih ke Access Point jika koneksi klien gagal.

 */

void setupWiFi() {

  WiFi.mode(WIFI_STA); // Atur WiFi ke mode Station (klien)


  // Periksa apakah pengaturan IP statis disediakan

  if (client_ip != "" && client_gateway != "" && client_subnet != "") {

    IPAddress ip, gateway, subnet, dns1, dns2;

    // Coba konversi string IP ke objek IPAddress

    if (ip.fromString(client_ip) && gateway.fromString(client_gateway) && subnet.fromString(client_subnet)) {

      if (client_dns1 != "" && dns1.fromString(client_dns1)) {

        if (client_dns2 != "" && dns2.fromString(client_dns2)) {

          WiFi.config(ip, gateway, subnet, dns1, dns2); // Gunakan IP statis dengan DNS primer dan sekunder

        } else {

          WiFi.config(ip, gateway, subnet, dns1); // Gunakan IP statis dengan hanya DNS primer

        }

      } else {

        WiFi.config(ip, gateway, subnet); // Gunakan hanya IP, Gateway, Subnet

      }

      Serial.println("Menggunakan IP Statis.");

    } else {

      Serial.println("IP Statis tidak valid, menggunakan DHCP.");

      // Hapus pengaturan IP statis yang tidak valid untuk kembali ke DHCP

      client_ip = "";

      client_gateway = "";

      client_subnet = "";

      client_dns1 = "";

      client_dns2 = "";

    }

  } else {

    Serial.println("IP Statis tidak dikonfigurasi, menggunakan DHCP.");

  }


  WiFi.begin(client_ssid.c_str(), client_pass.c_str()); // Coba sambungkan ke WiFi yang disimpan


  Serial.print("Menyambungkan ke " + client_ssid);

  int attempts = 0;

  // Tunggu koneksi atau batas waktu (10 detik = 20 * 500ms)

  while (WiFi.status() != WL_CONNECTED && attempts < 20) {

    delay(500); // Tunggu 0.5 detik

    Serial.print(".");

    attempts++;

  }


  if (WiFi.status() == WL_CONNECTED) {

    Serial.println("\nTersambung!");

    ipAddress = WiFi.localIP().toString(); // Dapatkan alamat IP yang ditetapkan

    Serial.println("Alamat IP: " + ipAddress);

  } else {

    Serial.println("\nKoneksi Gagal. Mengaktifkan Mode Access Point.");

    WiFi.mode(WIFI_AP); // Beralih ke mode Access Point

    WiFi.softAP(ap_ssid, ap_pass); // Mulai Access Point

    ipAddress = WiFi.softAPIP().toString(); // Dapatkan alamat IP AP

    Serial.println("SSID AP: " + String(ap_ssid));

    Serial.println("Alamat IP AP: " + ipAddress);

  }

}


/**

 * @brief Fungsi setup Arduino (berjalan sekali saat power-on atau reset).

 * Menginisialisasi serial, SIM800L, memuat pengaturan, mengatur WiFi,

 * dan memulai server web.

 */

void setup() {

  Serial.begin(115200); // Inisialisasi komunikasi serial untuk debugging

  Serial.println("\nMemulai ESP32 Receiver...");


  // Inisialisasi SIM800L dengan Hardware Serial 2 (RX2=GPIO16, TX2=GPIO17)

  // Baud rate standar untuk SIM800L adalah 9600.

  sim800l.begin(9600, SERIAL_8N1, 16, 17);

  Serial.println("SIM800L (Hardware Serial) diinisialisasi.");

  

  loadSettings(); // Muat pengaturan yang disimpan dari NVS

  setupWiFi();    // Pengaturan koneksi WiFi (klien atau AP)


  // Daftarkan handler server web untuk URL yang berbeda

  server.on("/", HTTP_GET, handleRoot);    // Jalur root untuk halaman konfigurasi

  server.on("/save", HTTP_POST, handleSave); // Simpan konfigurasi dari formulir

  server.on("/notif", HTTP_GET, handleNotif); // Endpoint notifikasi untuk menerima kode

  server.on("/testCall", HTTP_GET, handleTestCall); // Handler untuk tes panggilan SIM800L

  server.on("/testSms", HTTP_GET, handleTestSms);    // Handler untuk tes SMS SIM800L


  server.begin(web_port); // Mulai server web pada port yang dikonfigurasi

  Serial.println("Server Web dimulai pada port: " + String(web_port));

}


/**

 * @brief Fungsi loop Arduino (berjalan berulang kali).

 * Secara terus-menerus menangani permintaan web yang masuk.

 */

void loop() {

  server.handleClient(); // Harus dipanggil secara terus-menerus untuk menangani permintaan web yang masuk

}


No comments:

Post a Comment

Tandon Air Otomatis (ESP32 Master & Display) + Kode Lengkap

  Panduan Lengkap Tandon Air Otomatis (ESP32 Master & Display) + Kode Lengkap Diperbarui: 09 August 2025 Artikel ini memandu Anda memban...