#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <WiFi.h>
#include <WebServer.h>
#include <Preferences.h>
#include <HTTPClient.h>
#include <ESP32Ping.h> // Library untuk fungsi ping
// --- 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);
// --- 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 notificationTargetIP = "192.168.1.2"; // IP tujuan untuk notifikasi HTTP GET
// Array untuk 5 IP target yang akan di-ping
IPAddress targetIPs[5];
// Status ping untuk setiap IP
bool pingStatus[5] = {false, false, false, false, false};
// Pesan notifikasi default (dapat diubah melalui web UI)
String msgTarget1Down = "Target IP 1 Down";
String msgTarget2Down = "Target IP 2 Down";
String msgTarget3Down = "Target IP 3 Down";
String msgTarget4Down = "Target IP 4 Down";
String msgTarget5Down = "Target IP 5 Down";
// Flag untuk mencegah pengiriman notifikasi berulang untuk status down
bool target1DownSent = false;
bool target2DownSent = false;
bool target3DownSent = false;
bool target4DownSent = false;
bool target5DownSent = false;
// Variabel untuk interval loop non-blocking
unsigned long previousMillis = 0;
const long pingInterval = 10000; // Cek ping setiap 10 detik
// --- Fungsi URL Encoding ---
// 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 Pengiriman Notifikasi ---
// Mengirim notifikasi HTTP GET ke IP target dengan pesan tertentu
void sendNotification(String message) {
if (WiFi.status() == WL_CONNECTED) {
String encodedMessage = urlEncode(message);
String url = "http://" + notificationTargetIP + "/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>ESP32 Ping Monitor & Notifikasi</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: #007bff; 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: #0056b3; }"
".section { margin-top: 30px; border-top: 1px solid #eee; padding-top: 20px; }"
".status-box { background-color: #e6f7ff; border: 1px solid #91d5ff; padding: 10px; border-radius: 5px; margin-bottom: 20px; }"
".status-box p { margin: 5px 0; }"
".ping-status ul { list-style: none; padding: 0; }"
".ping-status li { background-color: #f8f9fa; border: 1px solid #e9ecef; padding: 8px; margin-bottom: 5px; border-radius: 4px; }"
".ping-status .up { color: #28a745; font-weight: bold; }"
".ping-status .down { color: #dc3545; font-weight: bold; }"
".ping-status .na { color: gray; font-weight: bold; }"
"</style></head><body>";
html += "<div class='container'>";
html += "<h1>ESP32 Ping Monitor & Notifikasi</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-ESP32-PING</p>"; // SSID AP baru
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 += "<p><strong>IP Penerima Notifikasi:</strong> " + notificationTargetIP + "</p>";
html += "</div>";
// Tampilkan Status Ping Saat Ini
html += "<div class='section ping-status'>";
html += "<h2>Status Ping Terakhir:</h2><ul>";
IPAddress unsetIP; // Defaultnya 0.0.0.0
for (int i = 0; i < 5; i++) {
html += "<li><strong>Target " + String(i + 1) + " (" + targetIPs[i].toString() + "):</strong> ";
if (targetIPs[i] == unsetIP) {
html += "<span class='na'>N/A (Tidak Dikonfigurasi)</span></li>";
} else {
html += "<span class='" + String(pingStatus[i] ? "up" : "down") + "'>" + String(pingStatus[i] ? "UP" : "DOWN") + "</span></li>";
}
}
html += "</ul></div>";
html += "<form action='/save' method='post'>";
html += "<div class='section'><h2>Pengaturan WiFi</h2>";
html += "<label for='ssid'>SSID WiFi:</label><input type='text' name='ssid' value='" + ssid + "'><br>";
html += "<label for='password'>Password WiFi:</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 Monitoring & Notifikasi</h2>";
html += "<label for='port'>Port Web Server:</label><input type='number' name='port' value='" + String(serverPort) + "'><br>";
html += "<label for='notification_target_ip'>IP Penerima Notifikasi (misal: 192.168.1.100):</label><input type='text' name='notification_target_ip' value='" + notificationTargetIP + "'><br>";
html += "<h3>Alamat IP Target Ping (hingga 5):</h3>";
for (int i = 0; i < 5; i++) {
html += "<label for='target_ip_" + String(i) + "'>Target IP " + String(i + 1) + ":</label><input type='text' name='target_ip_" + String(i) + "' value='" + targetIPs[i].toString() + "'><br>";
}
html += "</div>";
html += "<div class='section'><h2>Pesan Notifikasi Kustom (IP Down)</h2>";
html += "<label for='msg_target1'>Pesan Target IP 1 Down:</label><input type='text' name='msg_target1' value='" + msgTarget1Down + "'><br>";
html += "<label for='msg_target2'>Pesan Target IP 2 Down:</label><input type='text' name='msg_target2' value='" + msgTarget2Down + "'><br>";
html += "<label for='msg_target3'>Pesan Target IP 3 Down:</label><input type='text' name='msg_target3' value='" + msgTarget3Down + "'><br>";
html += "<label for='msg_target4'>Pesan Target IP 4 Down:</label><input type='text' name='msg_target4' value='" + msgTarget4Down + "'><br>";
html += "<label for='msg_target5'>Pesan Target IP 5 Down:</label><input type='text' name='msg_target5' value='" + msgTarget5Down + "'><br>";
html += "</div>";
html += "<input type='submit' value='Simpan Pengaturan & Restart'></form>";
html += "</div></body></html>";
server.send(200, "text/html", html);
}
// Menyimpan konfigurasi dari form
void handleSave() {
// Simpan pengaturan WiFi
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();
notificationTargetIP = server.arg("notification_target_ip");
// Simpan IP Target Ping
for (int i = 0; i < 5; i++) {
String argName = "target_ip_" + String(i);
if (server.hasArg(argName)) {
targetIPs[i].fromString(server.arg(argName));
}
}
// Simpan pesan notifikasi kustom
msgTarget1Down = server.arg("msg_target1");
msgTarget2Down = server.arg("msg_target2");
msgTarget3Down = server.arg("msg_target3");
msgTarget4Down = server.arg("msg_target4");
msgTarget5Down = server.arg("msg_target5");
preferences.begin("ping_monitor", 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("notifTargetIP", notificationTargetIP);
for (int i = 0; i < 5; i++) {
String key = "targetIP" + String(i);
preferences.putString(key.c_str(), targetIPs[i].toString());
}
preferences.putString("msgT1", msgTarget1Down);
preferences.putString("msgT2", msgTarget2Down);
preferences.putString("msgT3", msgTarget3Down);
preferences.putString("msgT4", msgTarget4Down);
preferences.putString("msgT5", msgTarget5Down);
preferences.end(); // Tutup NVS
server.send(200, "text/html", "<html><body><h1>Pengaturan Tersimpan! 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("ping_monitor", 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);
notificationTargetIP = preferences.getString("notifTargetIP", "192.168.1.2"); // Default IP penerima
// Muat IP Target Ping
IPAddress unsetIP; // 0.0.0.0
for (int i = 0; i < 5; i++) {
String key = "targetIP" + String(i);
String ipString = preferences.getString(key.c_str(), "");
if (!targetIPs[i].fromString(ipString)) {
targetIPs[i] = unsetIP; // Jika gagal parsing atau kosong, set ke 0.0.0.0
}
}
// Muat pesan notifikasi kustom
msgTarget1Down = preferences.getString("msgT1", "Target IP 1 Down");
msgTarget2Down = preferences.getString("msgT2", "Target IP 2 Down");
msgTarget3Down = preferences.getString("msgT3", "Target IP 3 Down");
msgTarget4Down = preferences.getString("msgT4", "Target IP 4 Down");
msgTarget5Down = preferences.getString("msgT5", "Target IP 5 Down");
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 (20 * 500ms)
delay(500);
Serial.print(".");
if (attempts == 10) { // Coba ulang WiFi.begin jika terlalu lama
Serial.println("\nMencoba koneksi ulang WiFi...");
WiFi.disconnect();
WiFi.reconnect();
}
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-ESP32-PING", "rejeki88"); // SSID & Pass untuk AP
Serial.print("AP Mode Aktif. IP: ");
Serial.println(WiFi.softAPIP());
Serial.print("AP Password: ");
Serial.println("rejeki88");
}
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
// Tidak ada pin ADC yang digunakan di sini, jadi tidak perlu analogReadResolution
// 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("ESP32 Ping Monitor");
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();
// Melakukan ping setiap pingInterval (non-blocking)
if (currentMillis - previousMillis >= pingInterval) {
previousMillis = currentMillis; // Simpan waktu terakhir
Serial.println("Melakukan Ping ke Target IP...");
IPAddress unsetIP; // 0.0.0.0
for (int i = 0; i < 5; i++) {
if (targetIPs[i] != unsetIP) { // Hanya ping IP yang valid (bukan 0.0.0.0)
pingStatus[i] = Ping.ping(targetIPs[i]);
Serial.printf("Ping ke %s: %s\n", targetIPs[i].toString().c_str(), pingStatus[i] ? "UP" : "DOWN");
// Kirim notifikasi jika terhubung ke WiFi dan IP down
if (WiFi.status() == WL_CONNECTED) {
if (!pingStatus[i]) { // Jika IP DOWN
// Periksa flag notifikasi untuk IP ini
bool *sentFlag = nullptr;
String *msg = nullptr;
switch (i) {
case 0: sentFlag = &target1DownSent; msg = &msgTarget1Down; break;
case 1: sentFlag = &target2DownSent; msg = &msgTarget2Down; break;
case 2: sentFlag = &target3DownSent; msg = &msgTarget3Down; break;
case 3: sentFlag = &target4DownSent; msg = &msgTarget4Down; break;
case 4: sentFlag = &target5DownSent; msg = &msgTarget5Down; break;
}
if (sentFlag && msg && !(*sentFlag)) {
sendNotification(*msg);
*sentFlag = true;
}
} else { // Jika IP UP kembali
// Reset flag notifikasi untuk IP ini
bool *sentFlag = nullptr;
switch (i) {
case 0: sentFlag = &target1DownSent; break;
case 1: sentFlag = &target2DownSent; break;
case 2: sentFlag = &target3DownSent; break;
case 3: sentFlag = &target4DownSent; break;
case 4: sentFlag = &target5DownSent; break;
}
if (sentFlag) {
*sentFlag = false; // IP kembali UP, reset flag agar bisa notifikasi lagi jika down
}
}
}
} else {
pingStatus[i] = true; // Anggap "UP" jika IP tidak dikonfigurasi, agar tidak ada notifikasi palsu
Serial.printf("Target IP %d: N/A (Tidak Dikonfigurasi)\n", i+1);
}
}
updateDisplay(); // Perbarui tampilan OLED setelah ping
}
server.handleClient(); // Tangani permintaan web server (harus selalu ada di loop)
}
// --- Fungsi OLED Display ---
void updateDisplay() {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
// Tampilkan status WiFi di baris paling atas
display.print("WiFi: ");
if (WiFi.status() == WL_CONNECTED) {
display.print("Connected");
} else {
display.print("AP Mode");
}
display.println(); // Pindah ke baris berikutnya
// Tampilkan IP Address saat ini
display.print("IP: ");
if (WiFi.status() == WL_CONNECTED) {
display.println(WiFi.localIP().toString());
} else {
display.println(WiFi.softAPIP().toString());
}
display.println("--- Ping Status ---");
IPAddress unsetIP; // 0.0.0.0
for (int i = 0; i < 5; i++) {
display.print("T"); display.print(i + 1); display.print(": ");
if (targetIPs[i] == unsetIP) { // Jika IP tidak dikonfigurasi
display.println("N/A");
} else {
display.setTextColor(pingStatus[i] ? SSD1306_WHITE : SSD1306_INVERSE); // Warna terbalik jika DOWN
display.println(pingStatus[i] ? "UP" : "DOWN");
display.setTextColor(SSD1306_WHITE); // Kembalikan warna normal
}
}
display.display();
}
No comments:
Post a Comment