846 lines
16 KiB
C++
846 lines
16 KiB
C++
// ======================================================
|
|
// LIBRARY
|
|
// ======================================================
|
|
#include <WiFi.h>
|
|
#include <Firebase_ESP_Client.h>
|
|
#include <ESP32Servo.h>
|
|
#include "time.h"
|
|
|
|
// ======================================================
|
|
// WIFI
|
|
// ======================================================
|
|
#define WIFI_SSID "siip"
|
|
#define WIFI_PASSWORD "mbayardulu"
|
|
|
|
// ======================================================
|
|
// FIREBASE
|
|
// ======================================================
|
|
#define API_KEY "AIzaSyDwXyctX6eQSoUNBsMbD2CzbeVekdOuBdI"
|
|
|
|
#define DATABASE_URL "https://tugas-akhir-9947b-default-rtdb.asia-southeast1.firebasedatabase.app/"
|
|
|
|
#define USER_EMAIL "chika@gmail.com"
|
|
#define USER_PASSWORD "chika123"
|
|
|
|
FirebaseData fbdo;
|
|
FirebaseAuth auth;
|
|
FirebaseConfig config;
|
|
|
|
// ======================================================
|
|
// PIN
|
|
// ======================================================
|
|
const int pirPin1 = 32;
|
|
const int pirPin2 = 21;
|
|
|
|
const int trigPin = 4;
|
|
const int echoPin = 18;
|
|
|
|
const int buzzerPin = 14;
|
|
|
|
const int servoPin1 = 25;
|
|
const int servoPin2 = 26;
|
|
const int servoPin3 = 27;
|
|
|
|
const int soundPin = 33;
|
|
|
|
// ======================================================
|
|
// SERVO
|
|
// ======================================================
|
|
Servo servo1;
|
|
Servo servo2;
|
|
Servo servo3;
|
|
|
|
// ======================================================
|
|
// VARIABLE
|
|
// ======================================================
|
|
unsigned long lastTrigger = 0;
|
|
unsigned long lastFirebase = 0;
|
|
unsigned long lastMotionTime = 0;
|
|
unsigned long lastAIRead = 0;
|
|
unsigned long lastHistoryTime = 0;
|
|
|
|
const unsigned long cooldown = 8000;
|
|
const unsigned long pirHoldTime = 700;
|
|
const unsigned long historyCooldown = 12000;
|
|
|
|
String hasilAI = "tidak dikenal";
|
|
String lastStatus = "Aman";
|
|
|
|
int validCounter = 0;
|
|
|
|
long lastDistance = 0;
|
|
float lastSoundDB = 35;
|
|
|
|
String lastDeteksi = "tidak dikenal";
|
|
long savedDistance = 0;
|
|
float savedSoundDB = 0;
|
|
String savedDeteksi = "tidak dikenal";
|
|
|
|
// ======================================================
|
|
// UPDATE STATUS
|
|
// ======================================================
|
|
void updateStatus(String statusBaru) {
|
|
|
|
if (!Firebase.ready()) return;
|
|
|
|
if (statusBaru != lastStatus) {
|
|
|
|
Firebase.RTDB.setString(
|
|
&fbdo,
|
|
"/Kebun/Status",
|
|
statusBaru
|
|
);
|
|
|
|
lastStatus = statusBaru;
|
|
}
|
|
}
|
|
|
|
// ======================================================
|
|
// FORMAT WAKTU
|
|
// ======================================================
|
|
String getFormattedTime() {
|
|
|
|
struct tm timeinfo;
|
|
|
|
if (!getLocalTime(&timeinfo)) {
|
|
return "Waktu Error";
|
|
}
|
|
|
|
char buffer[30];
|
|
|
|
strftime(
|
|
buffer,
|
|
sizeof(buffer),
|
|
"%d-%m-%Y %H:%M:%S",
|
|
&timeinfo
|
|
);
|
|
|
|
return String(buffer);
|
|
}
|
|
|
|
// ======================================================
|
|
// SIMPAN HISTORY
|
|
// ======================================================
|
|
void simpanRiwayatDeteksi() {
|
|
|
|
if (
|
|
lastSoundDB <= 0 ||
|
|
lastDistance <= 2 ||
|
|
lastDistance > 60
|
|
) {
|
|
return;
|
|
}
|
|
|
|
FirebaseJson json;
|
|
|
|
// ==========================
|
|
// SENSOR PEMICU
|
|
// ==========================
|
|
String sensorPemicu = "-";
|
|
|
|
if (digitalRead(pirPin1) == HIGH &&
|
|
digitalRead(pirPin2) == HIGH) {
|
|
|
|
sensorPemicu = "PIR 1 & PIR 2";
|
|
}
|
|
else if (digitalRead(pirPin1) == HIGH) {
|
|
|
|
sensorPemicu = "PIR 1";
|
|
}
|
|
else if (digitalRead(pirPin2) == HIGH) {
|
|
|
|
sensorPemicu = "PIR 2";
|
|
}
|
|
|
|
json.set("sensor_pemicu", sensorPemicu);
|
|
|
|
// ==========================
|
|
// DATA RIWAYAT
|
|
// ==========================
|
|
json.set("jarak", lastDistance);
|
|
|
|
json.set("db", lastSoundDB);
|
|
|
|
json.set("deteksi", "burung");
|
|
|
|
json.set("pir1", digitalRead(pirPin1));
|
|
|
|
json.set("pir2", digitalRead(pirPin2));
|
|
|
|
json.set("waktu", getFormattedTime());
|
|
|
|
Firebase.RTDB.pushJSON(
|
|
&fbdo,
|
|
"/Kebun/Riwayat",
|
|
&json
|
|
);
|
|
}
|
|
|
|
// ======================================================
|
|
// SETUP
|
|
// ======================================================
|
|
void setup() {
|
|
|
|
Serial.begin(115200);
|
|
|
|
delay(2000);
|
|
|
|
// WARMUP PIR
|
|
delay(5000);
|
|
|
|
analogReadResolution(12);
|
|
|
|
analogSetPinAttenuation(
|
|
soundPin,
|
|
ADC_11db
|
|
);
|
|
|
|
// ====================================================
|
|
// PINMODE
|
|
// ====================================================
|
|
pinMode(pirPin1, INPUT_PULLDOWN); // Mengaktifkan pull-down internal
|
|
pinMode(pirPin2, INPUT_PULLDOWN); // Mengaktifkan pull-down internal
|
|
|
|
pinMode(soundPin, INPUT);
|
|
|
|
pinMode(buzzerPin, OUTPUT);
|
|
|
|
pinMode(trigPin, OUTPUT);
|
|
pinMode(echoPin, INPUT);
|
|
|
|
digitalWrite(buzzerPin, LOW);
|
|
|
|
// ====================================================
|
|
// SERVO DETACH
|
|
// ====================================================
|
|
servo1.detach();
|
|
servo2.detach();
|
|
servo3.detach();
|
|
|
|
// ====================================================
|
|
// WIFI
|
|
// ====================================================
|
|
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
|
|
|
|
WiFi.setSleep(false);
|
|
|
|
Serial.print("Menghubungkan WiFi");
|
|
|
|
while (WiFi.status() != WL_CONNECTED) {
|
|
|
|
Serial.print(".");
|
|
delay(300);
|
|
}
|
|
|
|
Serial.println();
|
|
Serial.println("WiFi Connected");
|
|
|
|
// ====================================================
|
|
// INTERNET TIME
|
|
// ====================================================
|
|
configTime(
|
|
7 * 3600,
|
|
0,
|
|
"pool.ntp.org"
|
|
);
|
|
|
|
// ====================================================
|
|
// FIREBASE
|
|
// ====================================================
|
|
config.api_key = API_KEY;
|
|
|
|
config.database_url = DATABASE_URL;
|
|
|
|
auth.user.email = USER_EMAIL;
|
|
auth.user.password = USER_PASSWORD;
|
|
|
|
Firebase.begin(&config, &auth);
|
|
|
|
Firebase.reconnectWiFi(true);
|
|
|
|
Serial.println("Firebase Connected");
|
|
|
|
updateStatus("Aman");
|
|
}
|
|
|
|
// ======================================================
|
|
// LOOP
|
|
// ======================================================
|
|
void loop() {
|
|
|
|
// ====================================================
|
|
// WIFI CHECK
|
|
// ====================================================
|
|
if (WiFi.status() != WL_CONNECTED) {
|
|
|
|
WiFi.disconnect();
|
|
WiFi.reconnect();
|
|
|
|
delay(1000);
|
|
|
|
return;
|
|
}
|
|
|
|
// ====================================================
|
|
// FIREBASE READY
|
|
// ====================================================
|
|
if (!Firebase.ready()) {
|
|
return;
|
|
}
|
|
|
|
// ====================================================
|
|
// PIR
|
|
// ====================================================
|
|
int pir1 = digitalRead(pirPin1);
|
|
int pir2 = digitalRead(pirPin2);
|
|
|
|
bool gerakan = false;
|
|
|
|
if (
|
|
pir1 == HIGH ||
|
|
pir2 == HIGH
|
|
) {
|
|
|
|
delay(80);
|
|
|
|
if (
|
|
digitalRead(pirPin1) == HIGH ||
|
|
digitalRead(pirPin2) == HIGH
|
|
) {
|
|
|
|
gerakan = true;
|
|
|
|
lastMotionTime = millis();
|
|
}
|
|
}
|
|
|
|
// ====================================================
|
|
// HOLD PIR
|
|
// ====================================================
|
|
if (
|
|
millis() - lastMotionTime
|
|
< pirHoldTime
|
|
) {
|
|
|
|
gerakan = true;
|
|
}
|
|
|
|
// ====================================================
|
|
// SENSOR
|
|
// ====================================================
|
|
long distance = getDistanceAverage();
|
|
|
|
float soundDB = getSoundDB();
|
|
|
|
lastDistance = distance;
|
|
lastSoundDB = soundDB;
|
|
|
|
// ====================================================
|
|
// VALIDASI
|
|
// ====================================================
|
|
bool jarakValid = false;
|
|
bool aiValid = false;
|
|
|
|
if (
|
|
distance > 2 &&
|
|
distance <= 60 // Batas maksimal sekarang 60 cm
|
|
) {
|
|
|
|
jarakValid = true;
|
|
}
|
|
|
|
// ====================================================
|
|
// AI
|
|
// ====================================================
|
|
if (millis() - lastAIRead > 1000) {
|
|
|
|
lastAIRead = millis();
|
|
|
|
if (
|
|
Firebase.RTDB.getString(
|
|
&fbdo,
|
|
"/Kebun/AI/Klasifikasi"
|
|
)
|
|
) {
|
|
|
|
hasilAI = fbdo.stringData();
|
|
|
|
hasilAI.trim();
|
|
|
|
hasilAI.toLowerCase();
|
|
|
|
if (gerakan) {
|
|
lastDeteksi = hasilAI;
|
|
} else {
|
|
lastDeteksi = "tidak dikenal";
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hasilAI == "burung")
|
|
{
|
|
aiValid = true;
|
|
}
|
|
|
|
if (hasilAI == "burung" ||
|
|
hasilAI == "manusia")
|
|
{
|
|
savedDeteksi = hasilAI;
|
|
savedDistance = distance;
|
|
savedSoundDB = soundDB;
|
|
}
|
|
|
|
// ====================================================
|
|
// VALID COUNTER
|
|
// ====================================================
|
|
if (
|
|
gerakan &&
|
|
jarakValid &&
|
|
aiValid
|
|
) {
|
|
|
|
validCounter++;
|
|
|
|
} else {
|
|
|
|
validCounter = 0;
|
|
}
|
|
|
|
// ====================================================
|
|
// FIREBASE
|
|
// ====================================================
|
|
if (millis() - lastFirebase > 3000) {
|
|
|
|
lastFirebase = millis();
|
|
|
|
Firebase.RTDB.setInt(
|
|
&fbdo,
|
|
"/Kebun/Sensor/Jarak",
|
|
lastDistance
|
|
);
|
|
|
|
Firebase.RTDB.setFloat(
|
|
&fbdo,
|
|
"/Kebun/Sensor/Suara_dB",
|
|
lastSoundDB
|
|
);
|
|
|
|
// SIMPAN DETEKSI TERAKHIR YANG VALID
|
|
if (gerakan && hasilAI == "burung") {
|
|
savedDeteksi = "burung";
|
|
}
|
|
else if (gerakan && hasilAI == "manusia") {
|
|
savedDeteksi = "manusia";
|
|
}
|
|
|
|
String deteksiFirebase = savedDeteksi;
|
|
|
|
Serial.println("===== DEBUG FIREBASE =====");
|
|
|
|
Serial.print("Gerakan = ");
|
|
Serial.println(gerakan);
|
|
|
|
Serial.print("AI = ");
|
|
Serial.println(hasilAI);
|
|
|
|
Serial.print("deteksiFirebase = ");
|
|
Serial.println(deteksiFirebase);
|
|
|
|
Serial.println("==========================");
|
|
|
|
Firebase.RTDB.setString(
|
|
&fbdo,
|
|
"/Kebun/Sensor/Deteksi",
|
|
deteksiFirebase
|
|
);
|
|
|
|
// ==========================
|
|
// PIR 1
|
|
// ==========================
|
|
Firebase.RTDB.setInt(
|
|
&fbdo,
|
|
"/Kebun/Sensor/PIR1",
|
|
pir1
|
|
);
|
|
|
|
// ==========================
|
|
// PIR 2
|
|
// ==========================
|
|
Firebase.RTDB.setInt(
|
|
&fbdo,
|
|
"/Kebun/Sensor/PIR2",
|
|
pir2
|
|
);
|
|
|
|
Firebase.RTDB.setString(
|
|
&fbdo,
|
|
"/Kebun/Sensor/UpdateTerakhir",
|
|
getFormattedTime()
|
|
);
|
|
}
|
|
|
|
// ====================================================
|
|
// MODE
|
|
// ====================================================
|
|
int mode = 0;
|
|
|
|
if (
|
|
Firebase.RTDB.getInt(
|
|
&fbdo,
|
|
"/kontrol/mode"
|
|
)
|
|
) {
|
|
|
|
mode = fbdo.intData();
|
|
}
|
|
|
|
// ====================================================
|
|
// MODE OTOMATIS
|
|
// ====================================================
|
|
if (mode == 0) {
|
|
|
|
updateStatus("Otomatis");
|
|
|
|
if (
|
|
validCounter >= 1 &&
|
|
millis() - lastTrigger > cooldown &&
|
|
millis() - lastHistoryTime > historyCooldown
|
|
) {
|
|
|
|
lastTrigger = millis();
|
|
|
|
lastHistoryTime = millis();
|
|
|
|
updateStatus("Bahaya");
|
|
|
|
simpanRiwayatDeteksi();
|
|
|
|
aksiPengusiran();
|
|
|
|
validCounter = 0;
|
|
}
|
|
}
|
|
|
|
// ====================================================
|
|
// MODE MANUAL
|
|
// ====================================================
|
|
else {
|
|
|
|
updateStatus("Manual");
|
|
|
|
int buzzerManual = 0;
|
|
int servoManual = 0;
|
|
|
|
Firebase.RTDB.getInt(
|
|
&fbdo,
|
|
"/kontrol/buzzer"
|
|
);
|
|
|
|
buzzerManual = fbdo.intData();
|
|
|
|
Firebase.RTDB.getInt(
|
|
&fbdo,
|
|
"/kontrol/servo"
|
|
);
|
|
|
|
servoManual = fbdo.intData();
|
|
|
|
// ==================================================
|
|
// BUZZER MANUAL
|
|
// ==================================================
|
|
if (buzzerManual == 1) {
|
|
|
|
digitalWrite(buzzerPin, HIGH);
|
|
|
|
delay(5000);
|
|
|
|
digitalWrite(buzzerPin, LOW);
|
|
|
|
Firebase.RTDB.setInt(
|
|
&fbdo,
|
|
"/kontrol/buzzer",
|
|
0
|
|
);
|
|
}
|
|
|
|
// ==================================================
|
|
// SERVO MANUAL
|
|
// ==================================================
|
|
if (servoManual == 1) {
|
|
|
|
aksiServoOnly();
|
|
|
|
Firebase.RTDB.setInt(
|
|
&fbdo,
|
|
"/kontrol/servo",
|
|
0
|
|
);
|
|
}
|
|
}
|
|
|
|
// ====================================================
|
|
// SERIAL MONITOR
|
|
// ====================================================
|
|
Serial.println("========================");
|
|
|
|
Serial.print("PIR1: ");
|
|
Serial.println(pir1);
|
|
|
|
Serial.print("PIR2: ");
|
|
Serial.println(pir2);
|
|
|
|
Serial.print("Gerakan: ");
|
|
Serial.println(gerakan);
|
|
|
|
Serial.print("Jarak: ");
|
|
Serial.print(distance);
|
|
Serial.println(" cm");
|
|
|
|
Serial.print("dB: ");
|
|
Serial.println(soundDB);
|
|
|
|
Serial.print("AI: ");
|
|
Serial.println(hasilAI);
|
|
|
|
Serial.println("========================");
|
|
|
|
delay(100);
|
|
}
|
|
|
|
// ======================================================
|
|
// ULTRASONIK
|
|
// ======================================================
|
|
long getDistanceRaw() {
|
|
|
|
digitalWrite(trigPin, LOW);
|
|
delayMicroseconds(5);
|
|
|
|
digitalWrite(trigPin, HIGH);
|
|
delayMicroseconds(10);
|
|
|
|
digitalWrite(trigPin, LOW);
|
|
|
|
long duration =
|
|
pulseIn(echoPin, HIGH, 20000);
|
|
|
|
if (duration == 0) {
|
|
|
|
return -1;
|
|
}
|
|
|
|
long distance =
|
|
duration * 0.0343 / 2;
|
|
|
|
if (
|
|
distance < 2 ||
|
|
distance > 400
|
|
) {
|
|
|
|
return -1;
|
|
}
|
|
|
|
return distance;
|
|
}
|
|
|
|
// ======================================================
|
|
// ULTRASONIK FILTER
|
|
// ======================================================
|
|
long getDistanceAverage() {
|
|
|
|
long d1 = getDistanceRaw();
|
|
|
|
if (
|
|
d1 == -1 &&
|
|
lastDistance > 0
|
|
) {
|
|
|
|
d1 = lastDistance;
|
|
}
|
|
|
|
delay(3);
|
|
|
|
long d2 = getDistanceRaw();
|
|
|
|
if (
|
|
d2 == -1 &&
|
|
lastDistance > 0
|
|
) {
|
|
|
|
d2 = lastDistance;
|
|
}
|
|
|
|
delay(3);
|
|
|
|
long d3 = getDistanceRaw();
|
|
|
|
if (
|
|
d3 == -1 &&
|
|
lastDistance > 0
|
|
) {
|
|
|
|
d3 = lastDistance;
|
|
}
|
|
|
|
long values[3] = {d1, d2, d3};
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
|
|
for (int j = i + 1; j < 3; j++) {
|
|
|
|
if (values[j] < values[i]) {
|
|
|
|
long temp = values[i];
|
|
values[i] = values[j];
|
|
values[j] = temp;
|
|
}
|
|
}
|
|
}
|
|
|
|
return values[1];
|
|
}
|
|
|
|
// ======================================================
|
|
// SOUND SENSOR
|
|
// ======================================================
|
|
float getSoundDB() {
|
|
|
|
long total = 0;
|
|
|
|
const int samples = 50;
|
|
|
|
for (int i = 0; i < samples; i++) {
|
|
|
|
total += analogRead(soundPin);
|
|
|
|
delayMicroseconds(200);
|
|
}
|
|
|
|
float average = total / samples;
|
|
|
|
float noise = abs(average - 2048);
|
|
|
|
if (noise < 15) {
|
|
|
|
noise = 0;
|
|
}
|
|
|
|
float db =
|
|
25 + (noise / 18.0);
|
|
|
|
if (db > 75)
|
|
db = 75;
|
|
|
|
static float smoothDb = 35;
|
|
|
|
smoothDb =
|
|
(smoothDb * 0.75) +
|
|
(db * 0.25);
|
|
|
|
return smoothDb;
|
|
}
|
|
|
|
// ======================================================
|
|
// SERVO ONLY MANUAL
|
|
// ======================================================
|
|
void aksiServoOnly() {
|
|
|
|
servo1.attach(servoPin1);
|
|
servo2.attach(servoPin2);
|
|
servo3.attach(servoPin3);
|
|
|
|
unsigned long startTime = millis();
|
|
|
|
while (millis() - startTime < 30000) {
|
|
|
|
// ======================
|
|
// SERVO 1
|
|
// ======================
|
|
servo1.write(90);
|
|
delay(700);
|
|
|
|
servo1.write(0);
|
|
delay(300);
|
|
|
|
// ======================
|
|
// SERVO 2
|
|
// ======================
|
|
servo2.write(90);
|
|
delay(700);
|
|
|
|
servo2.write(0);
|
|
delay(300);
|
|
|
|
// ======================
|
|
// SERVO 3
|
|
// ======================
|
|
servo3.write(90);
|
|
delay(700);
|
|
|
|
servo3.write(0);
|
|
delay(300);
|
|
}
|
|
|
|
servo1.detach();
|
|
servo2.detach();
|
|
servo3.detach();
|
|
}
|
|
|
|
// ======================================================
|
|
// AKSI PENGUSIRAN OTOMATIS
|
|
// ======================================================
|
|
void aksiPengusiran() {
|
|
|
|
// =========================
|
|
// BUZZER DULU
|
|
// =========================
|
|
digitalWrite(buzzerPin, HIGH);
|
|
|
|
delay(3000);
|
|
|
|
digitalWrite(buzzerPin, LOW);
|
|
|
|
delay(300);
|
|
|
|
// =========================
|
|
// SERVO
|
|
// =========================
|
|
servo1.attach(servoPin1);
|
|
servo2.attach(servoPin2);
|
|
servo3.attach(servoPin3);
|
|
|
|
unsigned long startTime = millis();
|
|
|
|
while (millis() - startTime < 30000) {
|
|
|
|
// ======================
|
|
// SERVO 1
|
|
// ======================
|
|
servo1.write(90);
|
|
delay(700);
|
|
|
|
servo1.write(0);
|
|
delay(300);
|
|
|
|
// ======================
|
|
// SERVO 2
|
|
// ======================
|
|
servo2.write(90);
|
|
delay(700);
|
|
|
|
servo2.write(0);
|
|
delay(300);
|
|
|
|
// ======================
|
|
// SERVO 3
|
|
// ======================
|
|
servo3.write(90);
|
|
delay(700);
|
|
|
|
servo3.write(0);
|
|
delay(300);
|
|
}
|
|
|
|
servo1.detach();
|
|
servo2.detach();
|
|
servo3.detach();
|
|
} |