Search This Blog

Saturday, 28 June 2025

FINAL ESP PENGIRIM NOTIF BISA RUBAH PESAN NOTIF YANG AKAN DISAMPAIKAN KE SMS

 #include <Wire.h>

#include <Adafruit_GFX.h>

#include <Adafruit_SSD1306.h>

#include <WiFi.h>

#include <WebServer.h>

#include <Preferences.h>

#include <HTTPClient.h>


// --- Konfigurasi OLED ---

#define SCREEN_WIDTH 128    // Lebar layar OLED

#define SCREEN_HEIGHT 64    // Tinggi layar OLED

// Inisialisasi objek display OLED

// Alamat I2C default untuk SSD1306 adalah 0x3C

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);


// --- Konfigurasi Pembacaan Tegangan ---

#define NUM_SAMPLES 500         // Jumlah sampel untuk rata-rata dan RMS

#define VREF 3.3                // Tegangan referensi ADC ESP32

#define ADC_RES 4095.0          // Resolusi ADC (12-bit)

#define NOISE_THRESHOLD_VOLTAGE 50.0 // Ambang batas tegangan (misal, di bawah 50V dianggap mati)

#define VOLTAGE_CALIBRATION 312.0 // Faktor kalibrasi untuk mengubah nilai ADC menjadi tegangan AC

                                   // Sesuaikan ini dengan modul sensor tegangan AC Anda


// Pin GPIO untuk sensor tegangan

const int pinUPS1 = 32;

const int pinUPS2 = 33;

const int pinPLN = 34;

const int pinGENSET = 35;


// Variabel untuk menyimpan nilai tegangan yang dibaca

float vUPS1 = 0, vUPS2 = 0, vPLN = 0, vGENSET = 0;


// --- Objek & Variabel Global ---

Preferences preferences; // Untuk menyimpan konfigurasi di NVS (Non-Volatile Storage)

WebServer server;        // Objek Web Server

HTTPClient http;         // Objek HTTPClient, diinisialisasi sekali


// --- Variabel Konfigurasi (akan dimuat dari NVS) ---

String ssid = "";           // SSID WiFi

String password = "";       // Password WiFi

String ipStr = "";          // IP statis (opsional)

String gwStr = "";          // Gateway statis (opsional)

String subnetStr = "";      // Subnet statis (opsional)

int serverPort = 80;        // Port web server

String targetIP = "192.168.1.2"; // IP tujuan untuk notifikasi HTTP GET


// Pesan notifikasi default (dapat diubah melalui web UI)

String msgUPS1 = "UPS1 MATI";

String msgUPS2 = "UPS2 MATI";

String msgPLN = "PLN MATI";

String msgGENSET = "GENSET MATI";


// Flag untuk mencegah pengiriman notifikasi berulang

bool ups1Sent = false;

bool ups2Sent = false;

bool plnSent = false;

bool gensetSent = false;


// Variabel untuk interval loop non-blocking

unsigned long previousMillis = 0;

const long interval = 1000; // Cek setiap 1 detik


// --- Fungsi URL Encoding (Ditambahkan Baru) ---

// Melakukan URL encoding pada string, terutama mengganti spasi dengan %20

String urlEncode(const String& str) {

  String encodedString = "";

  char c;

  char code0;

  char code1;

  for (int i = 0; i < str.length(); i++) {

    c = str.charAt(i);

    if (c == ' ') { // Ganti spasi dengan %20

      encodedString += "%20";

    } else if (isalnum(c)) { // Karakter alfanumerik tidak perlu di-encode

      encodedString += c;

    } else { // Encode karakter lainnya (minimalis untuk kasus ini)

      code0 = (c & 0xf) + '0';

      if ((c & 0xf) > 9) {

        code0 = (c & 0xf) - 10 + 'A';

      }

      c = (c >> 4) & 0xf;

      code1 = c + '0';

      if (c > 9) {

        code1 = c - 10 + 'A';

      }

      encodedString += '%';

      encodedString += code1;

      encodedString += code0;

    }

  }

  return encodedString;

}



// --- Fungsi Pembacaan Tegangan AC ---

// Fungsi ini mengukur tegangan AC RMS dari pin ADC

float readVoltageAC(int pin) {

  float sumOfSquares = 0;

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

    int sensorValue = analogRead(pin);

    sumOfSquares += pow(sensorValue, 2);

    delayMicroseconds(100);

  }

  float avgSquare = sumOfSquares / NUM_SAMPLES;

  float rmsADC = sqrt(avgSquare);


  float acVoltage = rmsADC * (VREF / ADC_RES) * VOLTAGE_CALIBRATION;

  return acVoltage < NOISE_THRESHOLD_VOLTAGE ? 0.0 : acVoltage;

}



// --- Fungsi Pengiriman Notifikasi ---

// Mengirim notifikasi HTTP GET ke IP target dengan pesan tertentu

void sendNotification(String message) {

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

    // --- PERUBAHAN DI SINI: Panggil fungsi urlEncode yang baru kita buat ---

    String encodedMessage = urlEncode(message);

    String url = "http://" + targetIP + "/notif?src=" + encodedMessage;


    Serial.print("Mengirim notifikasi: ");

    Serial.println(url);


    http.begin(url);

    int httpCode = http.GET();


    if (httpCode > 0) {

      Serial.printf("[HTTP] GET... code: %d\n", httpCode);

      if (httpCode == HTTP_CODE_OK) {

        String payload = http.getString();

        Serial.println(payload);

      }

    } else {

      Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());

    }

    http.end();

  } else {

    Serial.println("Tidak terhubung ke WiFi, notifikasi tidak terkirim.");

  }

}


// --- Web Server Handlers ---

// Menampilkan halaman web konfigurasi

void handleRoot() {

  String html = "<!DOCTYPE html><html><head><meta charset='UTF-8'>"

                "<meta name='viewport' content='width=device-width, initial-scale=1.0'>"

                "<title>PUTU-TECKNO Config</title>"

                "<style>"

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

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

                "h1, h2 { font-size: 22px; color: #333; }"

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

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

                "form input[type='submit'] { background-color: #4CAF50; color: white; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer; font-size: 16px; margin-top: 20px; }"

                "form input[type='submit']:hover { background-color: #45a049; }"

                ".section { margin-top: 30px; border-top: 1px solid #eee; padding-top: 20px; }"

                ".nilai { font-size: 24px; font-weight: bold; color: #222; margin: 10px 0; }"

                ".off { color: red; font-weight: bold; }"

                ".status-box { background-color: #e6f7ff; border: 1px solid #91d5ff; padding: 10px; border-radius: 5px; margin-bottom: 20px; }"

                ".status-box p { margin: 5px 0; }"

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


  html += "<div class='container'>";

  html += "<h1>PUTU-TECKNO - WiFi & Notifikasi Settings</h1>";


  // Tampilkan Status Koneksi WiFi

  html += "<div class='status-box'>";

  html += "<h2>Status Koneksi</h2>";

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

    html += "<p><strong>Status:</strong> Terhubung ke WiFi</p>";

    html += "<p><strong>SSID:</strong> " + WiFi.SSID() + "</p>";

    html += "<p><strong>IP Lokal:</strong> " + WiFi.localIP().toString() + "</p>";

  } else {

    html += "<p><strong>Status:</strong> Mode Access Point Aktif</p>";

    html += "<p><strong>SSID AP:</strong> PUTU@TECKNO-4AC</p>";

    html += "<p><strong>IP AP:</strong> " + WiFi.softAPIP().toString() + "</p>";

    html += "<p><strong>Password AP:</strong> rejeki88</p>";

  }

  html += "<p><strong>Port Web:</strong> " + String(serverPort) + "</p>";

  html += "</div>";



  html += "<form action='/save' method='post'>";

  html += "<div class='section'><h2>Pengaturan WiFi</h2>";

  html += "<label for='ssid'>SSID:</label><input type='text' name='ssid' value='" + ssid + "'><br>";

  html += "<label for='password'>Password:</label><input type='password' name='password' value='" + password + "'><br>";

  html += "</div>";


  html += "<div class='section'><h2>Pengaturan Jaringan Statis (Opsional)</h2>";

  html += "<label for='ip'>IP Address (kosongkan untuk DHCP):</label><input type='text' name='ip' value='" + ipStr + "'><br>";

  html += "<label for='gw'>Gateway:</label><input type='text' name='gw' value='" + gwStr + "'><br>";

  html += "<label for='subnet'>Subnet Mask:</label><input type='text' name='subnet' value='" + subnetStr + "'><br>";

  html += "</div>";


  html += "<div class='section'><h2>Pengaturan Server</h2>";

  html += "<label for='port'>Port Web Server:</label><input type='number' name='port' value='" + String(serverPort) + "'><br>";

  html += "<label for='target_ip'>Target IP Notifikasi (misal: 192.168.1.100):</label><input type='text' name='target_ip' value='" + targetIP + "'><br>";

  html += "</div>";


  html += "<div class='section'><h2>Pesan Notifikasi Kustom</h2>";

  html += "<label for='msg_ups1'>Pesan UPS1 Mati:</label><input type='text' name='msg_ups1' value='" + msgUPS1 + "'><br>";

  html += "<label for='msg_ups2'>Pesan UPS2 Mati:</label><input type='text' name='msg_ups2' value='" + msgUPS2 + "'><br>";

  html += "<label for='msg_pln'>Pesan PLN Mati:</label><input type='text' name='msg_pln' value='" + msgPLN + "'><br>";

  html += "<label for='msg_genset'>Pesan GENSET Mati:</label><input type='text' name='msg_genset' value='" + msgGENSET + "'><br>";

  html += "</div>";


  html += "<input type='submit' value='Simpan Pengaturan & Restart'></form>";


  html += "<div class='section'><h1>Monitoring Tegangan Server</h1>";

  // Perbaikan tampilan nilai tegangan

  html += "<div class='nilai'>UPS1 : " + String(vUPS1 < NOISE_THRESHOLD_VOLTAGE ? "<span class='off'>OFF</span>" : (String(vUPS1, 1) + "V").c_str()) + "</div>";

  html += "<div class='nilai'>UPS2 : " + String(vUPS2 < NOISE_THRESHOLD_VOLTAGE ? "<span class='off'>OFF</span>" : (String(vUPS2, 1) + "V").c_str()) + "</div>";

  html += "<div class='nilai'>PLN  : " + String(vPLN < NOISE_THRESHOLD_VOLTAGE ? "<span class='off'>OFF</span>" : (String(vPLN, 1) + "V").c_str()) + "</div>";

  html += "<div class='nilai'>GENSET: " + String(vGENSET < NOISE_THRESHOLD_VOLTAGE ? "<span class='off'>OFF</span>" : (String(vGENSET, 1) + "V").c_str()) + "</div>";

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


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

}


// Menyimpan konfigurasi dari form

void handleSave() {

  ssid = server.arg("ssid");

  password = server.arg("password");

  ipStr = server.arg("ip");

  gwStr = server.arg("gw");

  subnetStr = server.arg("subnet");

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

  targetIP = server.arg("target_ip");


  msgUPS1 = server.arg("msg_ups1");

  msgUPS2 = server.arg("msg_ups2");

  msgPLN = server.arg("msg_pln");

  msgGENSET = server.arg("msg_genset");


  preferences.begin("wifi", false); // Buka NVS dalam mode baca/tulis

  preferences.putString("ssid", ssid);

  preferences.putString("password", password);

  preferences.putString("ip", ipStr);

  preferences.putString("gw", gwStr);

  preferences.putString("subnet", subnetStr);

  preferences.putInt("port", serverPort);

  preferences.putString("targetIP", targetIP);

  preferences.putString("msgUPS1", msgUPS1);

  preferences.putString("msgUPS2", msgUPS2);

  preferences.putString("msgPLN", msgPLN);

  preferences.putString("msgGENSET", msgGENSET);

  preferences.end(); // Tutup NVS


  server.send(200, "text/html", "<html><body><h1>Settings Saved! Restarting...</h1><p>Mohon tunggu beberapa saat.</p></body></html>");

  delay(2000);

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

}


// Memuat pengaturan dari NVS dan menginisialisasi WiFi

void setupWiFi() {

  preferences.begin("wifi", true); // Buka NVS dalam mode baca saja

  ssid = preferences.getString("ssid", "");

  password = preferences.getString("password", "");

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

  gwStr = preferences.getString("gw", "");

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

  serverPort = preferences.getInt("port", 80);

  targetIP = preferences.getString("targetIP", "192.168.1.2"); // Default IP receiver notifikasi

  msgUPS1 = preferences.getString("msgUPS1", "UPS1 MATI");

  msgUPS2 = preferences.getString("msgUPS2", "UPS2 MATI");

  msgPLN = preferences.getString("msgPLN", "PLN MATI");

  msgGENSET = preferences.getString("msgGENSET", "GENSET MATI");

  preferences.end(); // Tutup NVS


  if (ssid != "") {

    Serial.print("Mencoba menyambungkan ke WiFi: ");

    Serial.println(ssid);

    if (ipStr != "" && gwStr != "" && subnetStr != "") {

      IPAddress ip, gw, sn;

      if (ip.fromString(ipStr) && gw.fromString(gwStr) && sn.fromString(subnetStr)) {

        WiFi.config(ip, gw, sn);

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

      } else {

        Serial.println("Konfigurasi IP statis tidak valid. Menggunakan DHCP.");

        // Bersihkan string IP statis agar kembali ke DHCP

        ipStr = "";

        gwStr = "";

        subnetStr = "";

      }

    } else {

      Serial.println("Tidak ada konfigurasi IP statis. Menggunakan DHCP.");

    }


    WiFi.begin(ssid.c_str(), password.c_str());

    int attempts = 0;

    while (WiFi.status() != WL_CONNECTED && attempts < 20) { // Coba 10 detik

      delay(500);

      Serial.print(".");

      attempts++;

    }

    Serial.println();


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

      Serial.println("WiFi Tersambung!");

      Serial.print("IP Address: ");

      Serial.println(WiFi.localIP());

    } else {

      Serial.println("Gagal terhubung ke WiFi yang dikonfigurasi.");

    }

  } else {

    Serial.println("Tidak ada SSID tersimpan. Memulai mode Access Point.");

  }


  // Jika tidak terhubung ke WiFi atau tidak ada SSID tersimpan, mulai AP

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

    WiFi.mode(WIFI_AP);

    WiFi.softAP("PUTU@TECKNO-4AC", "rejeki88"); // SSID & Pass untuk AP

    Serial.print("AP Mode Aktif. IP: ");

    Serial.println(WiFi.softAPIP());

  }


  server.on("/", HTTP_GET, handleRoot);

  server.on("/save", HTTP_POST, handleSave);

  server.begin(serverPort);

  Serial.print("Web server dimulai pada port: ");

  Serial.println(serverPort);

}


// --- Setup Awal ---

void setup() {

  Serial.begin(115200); // Inisialisasi Serial Monitor

  analogReadResolution(12); // Set resolusi ADC ke 12-bit


  // Inisialisasi OLED

  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // 0x3C adalah alamat I2C umum

    Serial.println(F("OLED tidak ditemukan atau gagal inisialisasi!"));

    while (true); // Berhenti jika OLED tidak terdeteksi

  }

  display.clearDisplay();

  display.setTextColor(WHITE);

  display.setTextSize(1);

  display.setCursor(10, 10);

  display.println("Monitoring 4 Tegangan");

  display.println("PUTU-TECKNO");

  display.display();

  delay(2000); // Tampilkan pesan startup sebentar


  setupWiFi(); // Konfigurasi WiFi dan Web Server

}


// --- Loop Utama ---

void loop() {

  unsigned long currentMillis = millis();


  // Membaca tegangan setiap interval (non-blocking)

  if (currentMillis - previousMillis >= interval) {

    previousMillis = currentMillis; // Simpan waktu terakhir


    vUPS1 = readVoltageAC(pinUPS1);

    vUPS2 = readVoltageAC(pinUPS2);

    vPLN = readVoltageAC(pinPLN);

    vGENSET = readVoltageAC(pinGENSET);


    // Update tampilan OLED

    display.clearDisplay();

    display.setTextSize(1);

    display.setCursor(0, 0);

    display.printf("UPS1    : %s\n", vUPS1 < NOISE_THRESHOLD_VOLTAGE ? "OFF" : (String(vUPS1, 1) + "V").c_str());

    display.setCursor(0, 16);

    display.printf("UPS2    : %s\n", vUPS2 < NOISE_THRESHOLD_VOLTAGE ? "OFF" : (String(vUPS2, 1) + "V").c_str());

    display.setCursor(0, 32);

    display.printf("PLN     : %s\n", vPLN < NOISE_THRESHOLD_VOLTAGE ? "OFF" : (String(vPLN, 1) + "V").c_str());

    display.setCursor(0, 48);

    display.printf("GENSET  : %s\n", vGENSET < NOISE_THRESHOLD_VOLTAGE ? "OFF" : (String(vGENSET, 1) + "V").c_str());

    display.display();


    // Log ke Serial Monitor

    Serial.printf("UPS1: %.1fV | UPS2: %.1fV | PLN: %.1fV | GENSET: %.1fV\n", vUPS1, vUPS2, vPLN, vGENSET);


    // Kirim notifikasi jika terhubung ke WiFi

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

      if (vUPS1 < NOISE_THRESHOLD_VOLTAGE && !ups1Sent) {

        sendNotification(msgUPS1);

        ups1Sent = true;

      } else if (vUPS1 >= NOISE_THRESHOLD_VOLTAGE) {

        ups1Sent = false;

      }


      if (vUPS2 < NOISE_THRESHOLD_VOLTAGE && !ups2Sent) {

        sendNotification(msgUPS2);

        ups2Sent = true;

      } else if (vUPS2 >= NOISE_THRESHOLD_VOLTAGE) {

        ups2Sent = false;

      }


      if (vPLN < NOISE_THRESHOLD_VOLTAGE && !plnSent) {

        sendNotification(msgPLN);

        plnSent = true;

      } else if (vPLN >= NOISE_THRESHOLD_VOLTAGE) {

        plnSent = false;

      }


      if (vGENSET < NOISE_THRESHOLD_VOLTAGE && !gensetSent) {

        sendNotification(msgGENSET);

        gensetSent = true;

      } else if (vGENSET >= NOISE_THRESHOLD_VOLTAGE) {

        gensetSent = false;

      }

    }

  }


  server.handleClient(); // Tangani permintaan web server (harus selalu ada di loop)

}

No comments:

Post a Comment

CARGER AKI CONTROLER INA226-LIBARY WE ROTARI SW+OLED 0.96 SUKSES TAMPILAN LEBIH BAIK

 #include <Wire.h>  #include <Adafruit_GFX.h>    // Library grafis Adafruit #include <Adafruit_SSD1306.h> // Library untuk...