#include <WiFi.h>
#include <WiFiClientSecure.h> // Diperlukan untuk komunikasi HTTPS (Telegram)
#include <UniversalTelegramBot.h> // Library untuk Telegram
#include <DHT.h> // Library untuk DHT
#include <LiquidCrystal_I2C.h> // Library untuk LCD I2C
// --- Konfigurasi Wi-Fi ---
const char* ssid = "Wokwi-GUEST"; // Ganti dengan SSID WiFi Anda
const char* password = ""; // Ganti dengan Password WiFi Anda
// --- Konfigurasi Telegram Bot ---
#define BOT_TOKEN "7566279125:AAEGB9SgbTrPfwWKqv4GIymIz25Vq_Dzqq4" // Ganti dengan Token Bot Anda dari BotFather
#define CHAT_ID "1932670738" // Ganti dengan Chat ID Telegram Anda
WiFiClientSecure client;
UniversalTelegramBot bot(BOT_TOKEN, client);
// --- Pin untuk Sensor ---
const int pirPin = 13; // GPIO untuk Sensor PIR HC-SR501 OUT
const int trigPin = 14; // GPIO untuk Sensor Ultrasonik HC-SR04 Trig
const int echoPin = 27; // GPIO untuk Sensor Ultrasonik HC-SR04 Echo
const int dhtPin = 32; // GPIO untuk Sensor DHT22 Data
#define DHTTYPE DHT22 // Tipe sensor DHT (bisa juga DHT11)
// --- Pin untuk Komponen Output ---
const int ledPin = 2; // GPIO untuk LED (Anoda via Resistor)
const int buzzerPin = 4; // GPIO untuk Buzzer Aktif (+)
const int relayPin = 16; // GPIO untuk Modul Relay IN
// --- Objek Sensor dan LCD ---
DHT dht(dhtPin, DHTTYPE);
// Ganti 0x27 jika alamat I2C LCD Anda berbeda (misal 0x3F)
LiquidCrystal_I2C lcd(0x27, 16, 2); // Alamat I2C dan ukuran LCD (16 kolom, 2 baris)
// --- Variabel untuk menyimpan nilai sensor ---
long duration; // Untuk ultrasonik: waktu pantulan suara
int distanceCm; // Untuk ultrasonik: jarak dalam cm
float temperature; // Untuk DHT: suhu dalam Celsius
float humidity; // Untuk DHT: kelembaban dalam persen
// --- Variabel Waktu untuk Debounce/Delay Notifikasi ---
unsigned long previousPirMillis = 0;
const long pirDelay = 5000; // Delay 5 detik setelah deteksi PIR sebelum bisa mendeteksi lagi
// Variabel waktu terpisah untuk notifikasi jarak
unsigned long lastDistanceTelegramSendTime = 0;
const long distanceTelegramSendInterval = 5000; // Interval minimal 5 detik antar notifikasi jarak
// Variabel waktu terpisah untuk notifikasi suhu
unsigned long lastTemperatureTelegramSendTime = 0;
// >>> NILAI INI DIUBAH UNTUK PENGUJIAN CEPAT <<<
const long temperatureTelegramSendInterval = 5000; // Contoh: Interval minimal 5 detik (5000 ms) antar notifikasi suhu
// >>> JANGAN LUPA KEMBALIKAN KE 60000 MS UNTUK PENGGUNAAN NORMAL <<<
// --- Fungsi untuk menginisialisasi WiFi ---
void setupWiFi() {
Serial.print("Menghubungkan ke WiFi...");
lcd.setCursor(0,0);
lcd.print("Connecting WiFi...");
WiFi.begin(ssid, password);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) { // Coba 20 kali
delay(500);
Serial.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nTerhubung ke WiFi!");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
lcd.clear();
lcd.setCursor(0,0);
lcd.print("WiFi Connected!");
lcd.setCursor(0,1);
lcd.print(WiFi.localIP());
} else {
Serial.println("\nGagal terhubung ke WiFi.");
lcd.clear();
lcd.setCursor(0,0);
lcd.print("WiFi Failed!");
lcd.setCursor(0,1);
lcd.print("Check credentials");
// Anda mungkin ingin menambahkan logika untuk mencoba kembali atau mode offline
}
delay(2000);
} // End of setupWiFi()
// --- Fungsi Setup (Dijalankan sekali saat ESP32 pertama kali menyala/reset) ---
void setup() {
Serial.begin(115200); // Inisialisasi komunikasi serial
// Inisialisasi Pin Input/Output
pinMode(pirPin, INPUT);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
pinMode(ledPin, OUTPUT);
pinMode(buzzerPin, OUTPUT); // Pin buzzer tetap sebagai OUTPUT
pinMode(relayPin, OUTPUT);
// Pastikan output dalam keadaan mati saat startup
digitalWrite(ledPin, LOW);
digitalWrite(relayPin, LOW); // Relay nonaktif saat startup
noTone(buzzerPin); // Pastikan buzzer mati saat startup
// Inisialisasi LCD
lcd.init(); // Inisialisasi modul LCD
lcd.backlight(); // Nyalakan backlight LCD
lcd.clear(); // Bersihkan tampilan LCD
lcd.print("Sistem Memulai...");
delay(1000);
// Inisialisasi DHT Sensor
dht.begin();
// Koneksi Wi-Fi
setupWiFi();
// Konfigurasi waktu untuk HTTPS (Penting untuk Telegram API)
// Gunakan server NTP untuk mendapatkan waktu yang akurat
configTime(0, 0, "pool.ntp.org");
client.setCACert(TELEGRAM_CERTIFICATE_ROOT); // Sertifikat root untuk koneksi aman ke Telegram
// >>> TAMBAHAN UNTUK PENGUJIAN KONEKSI TELEGRAM SAAT STARTUP <<<
if (WiFi.status() == WL_CONNECTED) {
Serial.println("Mencoba mengirim pesan uji ke Telegram saat startup...");
if (bot.sendMessage(CHAT_ID, "Bot berhasil terhubung dan mengirim pesan uji saat startup!", "")) {
Serial.println("Pesan uji Telegram berhasil dikirim.");
} else {
Serial.println("Gagal mengirim pesan uji Telegram. Periksa token, chat ID, dan koneksi internet.");
}
} else {
Serial.println("Tidak terhubung ke WiFi, tidak dapat mengirim pesan uji Telegram.");
}
// >>> AKHIR TAMBAHAN PENGUJIAN <<<
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Sistem Aktif!");
delay(1500);
lcd.clear();
} // End of setup()
// --- Fungsi Loop (Dijalankan berulang kali terus menerus) ---
void loop() {
// --- 1. Pembacaan Sensor PIR (Gerakan) ---
int pirState = digitalRead(pirPin);
if (pirState == HIGH && (millis() - previousPirMillis > pirDelay)) {
// Gerakan terdeteksi DAN delay notifikasi sudah berlalu
digitalWrite(ledPin, HIGH); // Nyalakan LED
// Menggunakan tone() untuk buzzer pasif
tone(buzzerPin, 1000); // Nyalakan Buzzer dengan frekuensi 1000 Hz
delay(500); // Buzzer berbunyi selama 500 milidetik (0.5 detik)
noTone(buzzerPin); // Matikan Buzzer
Serial.println("Gerakan Terdeteksi!");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("GERAKAN DETECTED!");
// Kirim notifikasi Telegram (jika Wi-Fi terhubung)
if (WiFi.status() == WL_CONNECTED) {
// Baris ini yang diperbaiki: memastikan string ditutup dengan benar
Serial.println("Kondisi PIR terpenuhi, mencoba mengirim notifikasi gerakan..."); // Debug print
if (bot.sendMessage(CHAT_ID, "⚠️ PERHATIAN: Gerakan terdeteksi di ruangan!", "")) {
Serial.println("Notifikasi gerakan berhasil dikirim.");
} else {
Serial.println("Gagal mengirim notifikasi gerakan.");
}
}
previousPirMillis = millis(); // Update waktu deteksi terakhir
} else if (pirState == LOW) {
digitalWrite(ledPin, LOW); // Matikan LED jika tidak ada gerakan
noTone(buzzerPin); // Pastikan buzzer mati jika tidak ada gerakan
}
// --- 2. Pembacaan Sensor Ultrasonik (Jarak) ---
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
duration = pulseIn(echoPin, HIGH); // Mengukur durasi pulsa Echo
distanceCm = duration * 0.034 / 2; // Menghitung jarak (kecepatan suara: 0.034 cm/µs)
Serial.print("Jarak: ");
Serial.print(distanceCm);
Serial.println(" cm");
// Tampilkan di LCD (ganti baris setelah PIR/DHT)
lcd.setCursor(0, 1);
if (distanceCm == 0 || distanceCm > 400) { // Jika pembacaan tidak valid atau terlalu jauh
lcd.print("Jarak: --- cm");
} else {
lcd.print("Jarak: ");
lcd.print(distanceCm);
lcd.print(" cm ");
}
// Menggunakan variabel waktu terpisah untuk notifikasi jarak
if (distanceCm > 0 && distanceCm < 50 && (millis() - lastDistanceTelegramSendTime > distanceTelegramSendInterval)) {
// Jika objek dalam jarak 50 cm DAN interval notifikasi sudah berlalu
Serial.println("Objek dekat terdeteksi!");
if (WiFi.status() == WL_CONNECTED) {
Serial.println("Kondisi Jarak terpenuhi, mencoba mengirim notifikasi jarak..."); // Debug print
if (bot.sendMessage(CHAT_ID, "🔍 Objek terdeteksi dekat! Jarak: " + String(distanceCm) + " cm", "")) {
Serial.println("Notifikasi jarak berhasil dikirim.");
} else {
Serial.println("Gagal mengirim notifikasi jarak.");
}
lastDistanceTelegramSendTime = millis(); // Update waktu kirim notifikasi jarak
}
}
delay(500); // Jeda singkat untuk ultrasonik
// --- 3. Pembacaan Sensor DHT (Suhu & Kelembaban) ---
temperature = dht.readTemperature();
humidity = dht.readHumidity();
if (isnan(temperature) || isnan(humidity)) { // Cek apakah pembacaan valid
Serial.println("Gagal membaca dari sensor DHT!");
lcd.setCursor(0, 0); // Tampilkan di baris atas LCD
lcd.print("DHT Gagal Baca!");
} else {
Serial.print("Suhu: ");
Serial.print(temperature);
Serial.print(" C, Kelembaban: ");
Serial.print(humidity);
Serial.println(" %");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Suhu: ");
lcd.print(temperature);
lcd.print(" C");
lcd.setCursor(0, 1);
lcd.print("Kelembaban: ");
lcd.print(humidity);
lcd.print(" %");
// Kontrol Relay berdasarkan Suhu
if (temperature > 30.0) { // Contoh ambang batas suhu 30 derajat Celsius
digitalWrite(relayPin, HIGH); // Aktifkan Relay (sesuaikan jika relay Anda aktif LOW)
Serial.println("Relay ON (Suhu Tinggi)");
// Menggunakan variabel waktu terpisah untuk notifikasi suhu
if (WiFi.status() == WL_CONNECTED && (millis() - lastTemperatureTelegramSendTime > temperatureTelegramSendInterval)) {
Serial.println("Kondisi Suhu terpenuhi, mencoba mengirim notifikasi suhu..."); // Debug print
if (bot.sendMessage(CHAT_ID, "🔥 Suhu tinggi terdeteksi: " + String(temperature) + " C. Kipas/Perangkat dihidupkan.", "")) {
Serial.println("Notifikasi suhu berhasil dikirim.");
} else {
Serial.println("Gagal mengirim notifikasi suhu.");
}
lastTemperatureTelegramSendTime = millis(); // Update waktu kirim notifikasi suhu
} else {
Serial.print("Kondisi Suhu terpenuhi, tapi notifikasi belum bisa dikirim. Waktu berlalu: "); // Debug print
Serial.print(millis() - lastTemperatureTelegramSendTime);
Serial.print(" ms. Interval: ");
Serial.print(temperatureTelegramSendInterval);
Serial.println(" ms.");
}
} else {
digitalWrite(relayPin, LOW); // Nonaktifkan Relay
Serial.println("Relay OFF (Suhu Normal)");
// Anda bisa mengirim notifikasi jika suhu turun kembali, tapi akan terlalu sering
}
}
// --- Opsional: Penanganan Pesan Masuk Telegram (untuk perintah) ---
// int numNewMessages = bot.getUpdates(bot.lastUpdate.update_id + 1);
// while (numNewMessages) {
// handleNewMessages(numNewMessages);
// numNewMessages = bot.getUpdates(bot.lastUpdate.update_id + 1);
// }
delay(2000); // Jeda sebelum mengulang loop untuk menghindari pembacaan terlalu cepat
lcd.clear(); // Bersihkan LCD untuk tampilan berikutnya dari loop (opsional, tergantung preferensi tampilan)
} // End of loop()
/*
// --- Fungsi contoh untuk menangani pesan masuk Telegram (jika ingin mengirim perintah ke ESP32) ---
// Uncomment bagian ini jika Anda ingin menambahkan fungsionalitas pengiriman perintah
void handleNewMessages(int numNewMessages) {
Serial.println("Pesan baru masuk Telegram.");
for (int i=0; i<numNewMessages; i++) {
String chat_id = String(bot.messages[i].chat_id);
String text = bot.messages[i].text;
Serial.println("Pesan dari " + chat_id + ": " + text);
if (text == "/status") {
String message = "Sistem Status:\n";
message += "Suhu: " + String(temperature) + " C\n";
message += "Kelembaban: " + String(humidity) + " %\n";
message += "Jarak: " + String(distanceCm) + " cm\n";
message += "Gerakan: " + (digitalRead(pirPin) == HIGH ? "Terdeteksi" : "Aman");
bot.sendMessage(chat_id, message, "");
} else if (text == "/relay_on") {
digitalWrite(relayPin, HIGH);
bot.sendMessage(chat_id, "Relay dihidupkan.", "");
} else if (text == "/relay_off") {
digitalWrite(relayPin, LOW);
bot.sendMessage(chat_id, "Relay dimatikan.", "");
} else if (text == "/start") {
String welcome = "Selamat datang di Bot Pemantauan Ruangan!\n";
welcome += "Gunakan perintah berikut:\n";
welcome += "/status - Cek status sensor\n";
welcome += "/relay_on - Hidupkan relay\n";
welcome += "/relay_off - Matikan relay";
bot.sendMessage(chat_id, welcome, "");
} else {
bot.sendMessage(chat_id, "Perintah tidak dikenal.", "");
}
}
} // End of handleNewMessages()
*/