#include <HardwareSerial.h>
#include <Wire.h> // Untuk komunikasi I2C OLED
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <WiFi.h> // Library untuk konektivitas WiFi
#include <WiFiClient.h> // Untuk melakukan koneksi TCP sebagai "ping"
// Definisi pin RX dan TX untuk komunikasi serial antara ESP32 dan SIM800L
#define SIM800L_RX_PIN 16 // Pin RX ESP32 terhubung ke TX SIM800L
#define SIM800L_TX_PIN 17 // Pin TX ESP32 terhubung ke RX SIM800L
// Definisi pin untuk LED indikator
#define LED_PIN 2 // Contoh pin GPIO2 untuk LED
// Definisi pin untuk tombol fisik
#define BUTTON_PIN 4 // Contoh pin GPIO4 untuk tombol
// Hubungkan satu sisi tombol ke pin ini, dan sisi lain ke GND.
// Resistor pull-up internal akan diaktifkan.
// Dimensi layar OLED (128x64)
#define SCREEN_WIDTH 128 // Lebar piksel OLED
#define SCREEN_HEIGHT 64 // Tinggi piksel OLED
// Kredensial WiFi Anda
const char* WIFI_SSID = "Dara@home"; // Nama WiFi
const char* WIFI_PASSWORD = "rejeki88"; // Kata sandi WiFi
// Deklarasi objek SSD1306 (alamat I2C OLED 0x3C atau 0x3D)
// Ubah 0x3C jika OLED Anda menggunakan alamat 0x3D
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// Buat objek HardwareSerial untuk SIM800L
HardwareSerial sim800lSerial(1); // Gunakan UART1 pada ESP32 (UART0 digunakan untuk debugging Serial)
// Variabel global untuk target ping (sekarang hostname)
String pingTargetHostname = "google.com"; // Target ping ke google.com
// Variabel global untuk menyimpan kekuatan sinyal GSM
int gsmSignalRSSI = -1; // -1 menunjukkan tidak diketahui atau tidak ada sinyal
// --- Deklarasi Prototype Fungsi ---
String sendATCommand(String command);
String sendATCommand(String command, unsigned long timeoutMs); // Overloaded untuk timeout kustom
void getAndDisplaySignalStrength();
void sendSMS(String phoneNumber, String message);
void setLedStatus(bool pingSuccess);
void makeCall(String phoneNumber, int callDurationSeconds);
void connectWiFi(); // Fungsi untuk terhubung ke WiFi
bool testPingTarget(); // Fungsi untuk menguji ping ke target dinamis (hostname)
void updateOLEDMainStatus(); // Fungsi untuk memperbarui tampilan status utama
// Variabel untuk debouncing tombol
long lastButtonPressTime = 0; // Waktu terakhir tombol ditekan
const long debounceDelay = 50; // Waktu debounce dalam milidetik
// Variabel global untuk status ping dan berkedip
String pingStatusMessage = "Menunggu..."; // Pesan status ping untuk ditampilkan di OLED
bool showPingStatusBlink = true; // Flag untuk mengontrol kedipan global
unsigned long lastPingStatusBlinkToggle = 0; // Waktu terakhir status ping di-toggle
const unsigned long pingStatusBlinkInterval = 500; // Interval kedipan 500ms
void setup() {
// Inisialisasi komunikasi serial untuk debugging (serial monitor)
Serial.begin(115200);
Serial.println("Memulai pengujian koneksi ESP32 dengan SIM800L, OLED, LED, Tombol, dan WiFi...");
// Inisialisasi pin LED sebagai output
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW); // Pastikan LED mati saat inisialisasi
// Inisialisasi pin tombol sebagai input dengan resistor pull-up internal
// Tombol harus dihubungkan untuk menghubungkan pin ini ke GND saat ditekan.
pinMode(BUTTON_PIN, INPUT_PULLUP);
Serial.println("Pin tombol diinisialisasi dengan INPUT_PULLUP.");
// Inisialisasi OLED
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Alamat I2C umum untuk OLED
Serial.println(F("Alokasi SSD1306 gagal"));
for (;;) ; // Jangan lanjutkan jika gagal, berhenti di sini
}
display.display(); // Tampilkan logo boot-up Adafruit
delay(2000);
display.clearDisplay(); // Hapus tampilan
display.setTextSize(1); // Ukuran teks 1
display.setTextColor(SSD1306_WHITE); // Warna teks putih
// Tampilkan pesan inisialisasi di OLED
display.setCursor(0, 0);
display.println("ESP32 + SIM800L");
display.println("Inisialisasi...");
display.display();
delay(2000);
// Inisialisasi komunikasi serial untuk SIM800L
// Baud rate 9600, 8 bit data, tanpa paritas, 1 bit stop
sim800lSerial.begin(9600, SERIAL_8N1, SIM800L_RX_PIN, SIM800L_TX_PIN);
delay(1000); // Beri waktu modul untuk stabil
// --- Pengujian Modul SIM800L dan Indikator LED ---
Serial.println("Mengirim AT command untuk memeriksa koneksi SIM800L...");
display.clearDisplay();
display.setCursor(0, 0);
display.println("Cek Koneksi GSM...");
display.display();
String atResponse = sendATCommand("AT");
setLedStatus(atResponse.indexOf("OK") != -1);
delay(1000);
Serial.println("Mengirim AT+CSQ untuk memeriksa kualitas sinyal SIM800L...");
display.clearDisplay();
display.setCursor(0, 0);
display.println("Mengecek Sinyal GSM...");
display.display();
getAndDisplaySignalStrength(); // Perbarui kekuatan sinyal sekarang
delay(2000);
Serial.println("Mengirim AT+CREG? untuk memeriksa pendaftaran jaringan SIM800L...");
display.clearDisplay();
display.setCursor(0, 0);
display.println("Mendaftar Jaringan GSM...");
display.display();
String cregResponse = sendATCommand("AT+CREG?");
setLedStatus(cregResponse.indexOf("OK") != -1);
delay(2000);
// --- Koneksi WiFi Awal ---
connectWiFi(); // Lakukan koneksi WiFi saat startup
// Pesan pengujian akhir di OLED
updateOLEDMainStatus(); // Perbarui tampilan status utama setelah inisialisasi
}
void loop() {
// Logika ping SIM800L dan kekuatan sinyal
static unsigned long lastSim800lPingTime = 0;
static unsigned long sim800lPingInterval = 10000; // Ping SIM800L setiap 10 detik
if (millis() - lastSim800lPingTime >= sim800lPingInterval) {
lastSim800lPingTime = millis();
Serial.println("Melakukan ping SIM800L (AT command) untuk indikator LED...");
String atResponseLoop = sendATCommand("AT");
setLedStatus(atResponseLoop.indexOf("OK") != -1);
getAndDisplaySignalStrength(); // Perbarui kekuatan sinyal di OLED dan LED
}
// --- Logika Koneksi WiFi dan Ping Target ---
static unsigned long lastTargetPingTime = 0;
static unsigned long targetPingInterval = 30000; // Periksa target setiap 30 detik
static bool emergencyActionInitiated = false;
static unsigned long lastEmergencyActionTime = 0;
const unsigned long emergencyActionCooldown = 5 * 60 * 1000; // Cooldown 5 menit untuk tindakan darurat (SMS & Panggilan)
if (WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi tidak terhubung. Mencoba menyambung kembali...");
connectWiFi(); // Coba sambungkan kembali jika tidak terhubung
}
if (WiFi.status() == WL_CONNECTED) {
// Periksa Ping Target
if (millis() - lastTargetPingTime >= targetPingInterval) {
lastTargetPingTime = millis();
Serial.print("Melakukan Uji Ping ");
Serial.print(pingTargetHostname);
Serial.println(" (Port 80)..."); // Port 80 untuk pengujian konektivitas umum
if (testPingTarget()) { // Jika ping berhasil
Serial.print("Ping ");
Serial.print(pingTargetHostname);
Serial.println(" (Port 80) Berhasil (koneksi WiFi baik).");
pingStatusMessage = "Ping " + pingTargetHostname + ": OK"; // Update status text
emergencyActionInitiated = false; // Setel ulang flag tindakan darurat jika target dapat dijangkau
} else { // Jika ping timeout
Serial.print("Ping ");
Serial.print(pingTargetHostname);
Serial.println(" (Port 80) Timeout! Memulai aksi darurat...");
pingStatusMessage = "Ping " + pingTargetHostname + ": PUTUS"; // Perbarui teks status
// Picu tindakan darurat (SMS & Panggilan) jika ping gagal
if (!emergencyActionInitiated || (millis() - lastEmergencyActionTime >= emergencyActionCooldown)) {
Serial.println("Mengirim SMS: KONEKSI INTERNET PUTUS (" + pingTargetHostname + " GAGAL)");
sendSMS("+628112508805", "KONEKSI INTERNET PUTUS (" + pingTargetHostname + " GAGAL)"); // Kirim SMS
delay(2000); // Penundaan singkat sebelum panggilan
makeCall("+628112508805", 15); // Lakukan panggilan 15 detik
emergencyActionInitiated = true;
lastEmergencyActionTime = millis();
} else {
Serial.println("Aksi darurat (SMS/Panggilan) telah dipicu baru-baru ini karena kegagalan ping. Menunggu cooldown...");
}
}
updateOLEDMainStatus(); // Perbarui tampilan setelah ping target
}
}
// --- Logika Pengecekan Tombol ---
int buttonState = digitalRead(BUTTON_PIN);
if (buttonState == LOW && (millis() - lastButtonPressTime) > debounceDelay) {
if (lastButtonPressTime == 0) { // Hanya picu jika ini adalah penekanan baru
Serial.println("Tombol Ditekan! Memulai panggilan...");
display.clearDisplay();
display.setCursor(0,0);
display.println("Tombol Ditekan!");
display.println("Memanggil...");
display.display();
delay(500); // Penundaan singkat untuk pembaruan tampilan
makeCall("+628112508805", 15); // Durasi panggilan 15 detik saat tombol ditekan
updateOLEDMainStatus(); // Perbarui tampilan setelah panggilan selesai
}
lastButtonPressTime = millis(); // Catat waktu penekanan ini
} else if (buttonState == HIGH) {
lastButtonPressTime = 0; // Setel ulang timer debounce jika tombol dilepaskan
}
// --- Tampilkan Status Ping di Baris Bawah OLED (Berkedip) ---
if (millis() - lastPingStatusBlinkToggle >= pingStatusBlinkInterval) {
lastPingStatusBlinkToggle = millis();
showPingStatusBlink = !showPingStatusBlink; // Toggle visibilitas
// Panggil fungsi untuk memperbarui seluruh tampilan status utama, termasuk baris berkedip
updateOLEDMainStatus();
}
}
// Fungsi untuk mengirim perintah AT ke SIM800L dan membaca responsnya dengan timeout default
String sendATCommand(String command) {
return sendATCommand(command, 2000); // Timeout default 2 detik
}
// Fungsi untuk mengirim perintah AT ke SIM800L dan membaca responsnya dengan timeout kustom
String sendATCommand(String command, unsigned long timeoutMs) {
Serial.print("Mengirim: ");
Serial.println(command);
sim800lSerial.println(command);
String response = "";
unsigned long timeout = millis();
while (millis() - timeout < timeoutMs) {
if (sim800lSerial.available()) {
char c = sim800lSerial.read();
Serial.write(c);
response += c;
}
}
Serial.println();
return response;
}
// Fungsi untuk mengontrol status LED berdasarkan hasil "ping"
// Jika pingSuccess TRUE (modul merespons "OK"), LED tetap menyala.
// Jika pingSuccess FALSE (modul tidak merespons "OK" atau timeout), LED berkedip cepat.
void setLedStatus(bool pingSuccess) {
if (pingSuccess) {
digitalWrite(LED_PIN, HIGH); // LED ON (stabil menyala)
} else {
for (int i = 0; i < 2; i++) { // Berkedip cepat 2 kali untuk menunjukkan masalah
digitalWrite(LED_PIN, HIGH); // LED menyala
delay(50); // Penundaan singkat
digitalWrite(LED_PIN, LOW); // LED mati
delay(50); // Penundaan singkat
}
digitalWrite(LED_PIN, LOW); // Pastikan LED mati setelah berkedip
}
}
// Fungsi untuk mendapatkan dan menampilkan kekuatan sinyal (RSSI)
void getAndDisplaySignalStrength() {
String response = sendATCommand("AT+CSQ");
setLedStatus(response.indexOf("OK") != -1); // Perbarui status LED berdasarkan respons AT+CSQ
int tempRssi = -1;
int commaIndex = response.indexOf(',');
if (response.indexOf("+CSQ:") != -1 && commaIndex != -1) {
String rssiStr = response.substring(response.indexOf(":") + 1, commaIndex);
tempRssi = rssiStr.toInt();
}
gsmSignalRSSI = tempRssi; // Simpan dalam variabel global
Serial.print("Sinyal GSM RSSI: ");
Serial.println(gsmSignalRSSI);
}
// Fungsi untuk mengirim SMS ke nomor telepon tertentu dengan pesan tertentu
void sendSMS(String phoneNumber, String message) {
Serial.println("Mengatur mode SMS ke Text Mode...");
String modeResponse = sendATCommand("AT+CMGF=1");
setLedStatus(modeResponse.indexOf("OK") != -1);
delay(100); // Beri modul sedikit waktu
Serial.print("Mengirim SMS ke: ");
Serial.println(phoneNumber);
Serial.print("Pesan: ");
Serial.println(message);
sim800lSerial.print("AT+CMGS=\"");
sim800lSerial.print(phoneNumber);
sim800lSerial.println("\"");
delay(100); // Penundaan kecil sebelum mengirim konten pesan
sim800lSerial.print(message);
sim800lSerial.write(0x1A); // Ctrl+Z untuk mengirim SMS
Serial.println("Pesan dikirim. Menunggu konfirmasi...");
String smsConfirmation = sendATCommand("", 10000); // Tunggu hingga 10 detik untuk konfirmasi
setLedStatus(smsConfirmation.indexOf("OK") != -1 || smsConfirmation.indexOf("+CMGS:") != -1);
if (smsConfirmation.indexOf("OK") != -1 || smsConfirmation.indexOf("+CMGS:") != -1) {
Serial.println("SMS berhasil dikirim.");
} else {
Serial.println("Gagal mengirim SMS atau tidak ada konfirmasi.");
}
}
// Fungsi untuk melakukan panggilan telepon ke nomor yang ditentukan
// Panggilan akan otomatis terputus setelah callDurationSeconds
void makeCall(String phoneNumber, int callDurationSeconds) {
Serial.print("Melakukan panggilan ke: ");
Serial.println(phoneNumber);
display.clearDisplay(); // Hapus seluruh tampilan selama panggilan
display.setCursor(0,0);
display.println("Memanggil:");
display.println(phoneNumber);
display.display();
String callResponse = sendATCommand("ATD" + phoneNumber + ";");
setLedStatus(callResponse.indexOf("OK") != -1);
if (callResponse.indexOf("OK") != -1) {
Serial.println("Panggilan dimulai. Menunggu koneksi atau timeout...");
display.clearDisplay(); // Hapus lagi untuk status
display.setCursor(0,0);
display.println("Panggilan Berlangsung...");
display.println(phoneNumber);
display.display();
delay(callDurationSeconds * 1000);
Serial.println("Mengakhiri panggilan...");
display.clearDisplay(); // Hapus lagi
display.setCursor(0,0);
display.println("Mengakhiri Panggilan...");
display.display();
String hangupResponse = sendATCommand("ATH");
setLedStatus(hangupResponse.indexOf("OK") != -1);
delay(1000);
Serial.println("Panggilan diakhiri.");
} else {
Serial.println("Gagal memulai panggilan atau nomor tidak valid.");
display.clearDisplay(); // Hapus lagi
display.setCursor(0,0);
display.println("Panggilan Gagal!");
display.println("Periksa Sinyal/No.");
display.display();
}
// Setelah panggilan, panggil updateOLEDMainStatus untuk memperbarui tampilan utama
updateOLEDMainStatus();
}
// Fungsi untuk menghubungkan ESP32 ke WiFi
void connectWiFi() {
Serial.print("Menghubungkan ke WiFi SSID: ");
Serial.println(WIFI_SSID);
display.clearDisplay();
display.setCursor(0, 0);
display.println("Menghubungkan WiFi:");
display.println(WIFI_SSID);
display.display();
// Menggunakan DHCP secara default dengan WiFi.begin()
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) { // Coba 20 kali (sekitar 10 detik)
delay(500);
Serial.print(".");
display.print(".");
display.display();
attempts++;
}
display.clearDisplay();
display.setCursor(0, 0);
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nWiFi Terhubung!");
Serial.print("Alamat IP: ");
Serial.println(WiFi.localIP());
display.println("WiFi Terhubung!");
display.print("IP: ");
display.println(WiFi.localIP()); // Tampilkan Alamat IP yang diperoleh
display.print("GW: ");
display.println(WiFi.gatewayIP()); // Tampilkan IP Gateway
pingStatusMessage = "Menunggu..."; // Reset status ping
} else {
Serial.println("\nGagal Terhubung ke WiFi!");
display.println("Gagal Terhubung!");
display.println("Cek SSID/Sandi.");
pingStatusMessage = "WiFi PUTUS"; // Atur status ping jika WiFi gagal
}
display.display();
delay(2000);
}
// Fungsi untuk menguji koneksi ke target ping (hostname) pada port 80
bool testPingTarget() {
IPAddress resolvedIP;
// Menggunakan WiFi.hostByName untuk menyelesaikan hostname ke alamat IP
int n = WiFi.hostByName(pingTargetHostname.c_str(), resolvedIP);
if (n == 0) { // hostByName mengembalikan 0 jika gagal
Serial.print("ERROR: Gagal menyelesaikan hostname ");
Serial.print(pingTargetHostname);
Serial.println("!");
return false;
}
Serial.print("Mencoba koneksi ke ");
Serial.print(pingTargetHostname);
Serial.print(" (");
Serial.print(resolvedIP);
Serial.println(") (Port 80)..."); // Port 80 untuk pengujian konektivitas umum
WiFiClient client;
client.setTimeout(3000); // Atur timeout koneksi ke 3 detik
bool connected = client.connect(resolvedIP, 80); // Coba terhubung ke port 80
if (connected) {
Serial.print("Koneksi ");
Serial.print(pingTargetHostname);
Serial.print(" (");
Serial.print(resolvedIP);
Serial.println(") (Port 80) Berhasil.");
client.stop();
return true;
} else {
Serial.print("Koneksi ");
Serial.print(pingTargetHostname);
Serial.print(" (");
Serial.print(resolvedIP);
Serial.println(") (Port 80) Gagal (Timeout).");
client.stop();
return false;
}
}
// Fungsi untuk memperbarui tampilan status utama di OLED
// Ini akan dipanggil secara berkala dan setelah peristiwa penting
void updateOLEDMainStatus() {
display.clearDisplay(); // Hapus seluruh tampilan
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
// Tampilkan IP lokal dan status WiFi
display.setCursor(0, 0);
if (WiFi.status() == WL_CONNECTED) {
display.println("WiFi Connected!");
display.print("IP: ");
display.println(WiFi.localIP());
display.print("GW: "); // Tampilkan IP Gateway di baris terpisah
display.println(WiFi.gatewayIP());
} else {
display.println("WiFi Disconnected!");
display.println("Coba Koneksi Lagi...");
}
// Tampilkan status sinyal GSM
display.setCursor(0, 4 * 8); // Mulai setelah 4 baris teks awal (sekitar y=32)
if (gsmSignalRSSI != -1) {
display.print("GSM Signal: ");
display.print(gsmSignalRSSI);
display.println(" dBm"); // Tampilkan nilai RSSI
} else {
display.println("GSM Signal: N/A");
}
// Tampilkan status ping target di baris paling bawah
display.setCursor(0, SCREEN_HEIGHT - 8); // Baris paling bawah (8 piksel dari bawah)
if (showPingStatusBlink) {
display.println(pingStatusMessage);
} else {
display.println(""); // Biarkan kosong untuk efek kedip
}
display.display(); // Tampilkan semua perubahan
}
No comments:
Post a Comment