Upload files to "/"
This commit is contained in:
parent
4eabfa8402
commit
2ec3454eee
|
@ -0,0 +1,289 @@
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include <HTTPClient.h>
|
||||||
|
#include <FirebaseESP32.h>
|
||||||
|
#include <WiFiManager.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#define API_KEY "AIzaSyDCtvZMg-Ma0BCjG3eEpEEgs19EJVBTx90"
|
||||||
|
#define DATABASE_URL "https://floodwatch-cabc5-default-rtdb.asia-southeast1.firebasedatabase.app/"
|
||||||
|
|
||||||
|
String namaSungai = "sungai_bedadung";
|
||||||
|
|
||||||
|
FirebaseData fbdo;
|
||||||
|
FirebaseAuth auth;
|
||||||
|
FirebaseConfig config;
|
||||||
|
|
||||||
|
const char* backendHistoriURL = "https://services-flood.vercel.app/api/histori";
|
||||||
|
const char* backendNotifikasiURL = "https://services-flood.vercel.app/api/notifikasi";
|
||||||
|
|
||||||
|
#define LORA_TX 17
|
||||||
|
#define LORA_RX 16
|
||||||
|
#define M0 25
|
||||||
|
#define M1 26
|
||||||
|
|
||||||
|
#define LED_PIN 15
|
||||||
|
#define BUTTON_PIN 4
|
||||||
|
|
||||||
|
unsigned long lastDataTime = 0;
|
||||||
|
unsigned long lastBackendTime = 0;
|
||||||
|
unsigned long lastThresholdCheck = 0;
|
||||||
|
unsigned long lastDaruratCheck = 0;
|
||||||
|
|
||||||
|
const unsigned long dataInterval = 5000;
|
||||||
|
const unsigned long backendInterval = 60000;
|
||||||
|
const unsigned long thresholdInterval = 10000;
|
||||||
|
const unsigned long daruratInterval = 5000;
|
||||||
|
|
||||||
|
float lastKetinggian = 0;
|
||||||
|
float lastKetinggianDarurat = 0;
|
||||||
|
float nilaiAmbangBatas = 150.0;
|
||||||
|
float tinggiSensorDariDasar = 200.0;
|
||||||
|
|
||||||
|
const float ambangKenaikanDarurat = 30.0; // Lonjakan ketinggian cepat dalam 5 detik
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
|
||||||
|
pinMode(M0, OUTPUT);
|
||||||
|
pinMode(M1, OUTPUT);
|
||||||
|
digitalWrite(M0, LOW);
|
||||||
|
digitalWrite(M1, LOW);
|
||||||
|
Serial2.begin(9600, SERIAL_8N1, LORA_RX, LORA_TX);
|
||||||
|
delay(500);
|
||||||
|
|
||||||
|
pinMode(LED_PIN, OUTPUT);
|
||||||
|
pinMode(BUTTON_PIN, INPUT_PULLUP);
|
||||||
|
digitalWrite(LED_PIN, LOW);
|
||||||
|
|
||||||
|
if (digitalRead(BUTTON_PIN) == LOW) {
|
||||||
|
WiFiManager wm;
|
||||||
|
wm.resetSettings();
|
||||||
|
delay(1000);
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
WiFi.mode(WIFI_STA);
|
||||||
|
WiFiManager wm;
|
||||||
|
wm.setTimeout(180);
|
||||||
|
if (!wm.autoConnect("FloodWatch-Receiver")) {
|
||||||
|
Serial.println("Failed to connect and hit timeout");
|
||||||
|
delay(3000);
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("Connected to WiFi: " + WiFi.SSID());
|
||||||
|
digitalWrite(LED_PIN, HIGH);
|
||||||
|
|
||||||
|
configTime(0, 0, "pool.ntp.org", "time.nist.gov");
|
||||||
|
|
||||||
|
config.api_key = API_KEY;
|
||||||
|
config.database_url = DATABASE_URL;
|
||||||
|
auth.user.email = "sungaibedadung@gmail.com";
|
||||||
|
auth.user.password = "123456";
|
||||||
|
Firebase.begin(&config, &auth);
|
||||||
|
Firebase.reconnectWiFi(true);
|
||||||
|
|
||||||
|
Serial.println("📡 Receiver LoRa Flood Detection Ready");
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
unsigned long currentMillis = millis();
|
||||||
|
|
||||||
|
if (Serial2.available()) {
|
||||||
|
String incoming = Serial2.readStringUntil('\n');
|
||||||
|
incoming.trim();
|
||||||
|
|
||||||
|
Serial.print("📩 Pesan masuk: ");
|
||||||
|
Serial.println(incoming);
|
||||||
|
|
||||||
|
if (incoming.startsWith("DATA:")) {
|
||||||
|
processSensorData(incoming);
|
||||||
|
lastDataTime = currentMillis;
|
||||||
|
} else if (incoming.startsWith("ACK:")) {
|
||||||
|
Serial.println("✅ Sender menerima update kalibrasi");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentMillis - lastThresholdCheck >= thresholdInterval) {
|
||||||
|
lastThresholdCheck = currentMillis;
|
||||||
|
checkFirebaseThreshold();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentMillis - lastBackendTime >= backendInterval) {
|
||||||
|
lastBackendTime = currentMillis;
|
||||||
|
sendToBackend(lastKetinggian);
|
||||||
|
}
|
||||||
|
|
||||||
|
checkKenaikanDarurat(); // 🔥 Tambahan untuk deteksi mendadak
|
||||||
|
updateLEDStatus();
|
||||||
|
checkResetButton();
|
||||||
|
}
|
||||||
|
|
||||||
|
void processSensorData(String data) {
|
||||||
|
int kStart = data.indexOf("KETINGGIAN=");
|
||||||
|
int hStart = data.indexOf("HUJAN=");
|
||||||
|
int endData = data.indexOf(';', hStart);
|
||||||
|
|
||||||
|
if (kStart != -1 && hStart != -1 && endData != -1) {
|
||||||
|
float ketinggian = data.substring(kStart + 11, hStart - 1).toFloat();
|
||||||
|
int hujan = data.substring(hStart + 6, endData).toInt();
|
||||||
|
|
||||||
|
lastKetinggian = ketinggian;
|
||||||
|
|
||||||
|
Serial.println("📊 Data Sensor:");
|
||||||
|
Serial.println(" Ketinggian: " + String(ketinggian) + " cm");
|
||||||
|
Serial.println(" Hujan: " + String(hujan) + "%");
|
||||||
|
|
||||||
|
updateFirebaseData(ketinggian, hujan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkFirebaseThreshold() {
|
||||||
|
String pathTinggi = "/" + namaSungai + "/kalibrasi/tinggiSensor";
|
||||||
|
String pathAmbang = "/" + namaSungai + "/threshold/nilai";
|
||||||
|
bool needUpdate = false;
|
||||||
|
|
||||||
|
if (Firebase.getFloat(fbdo, pathTinggi) && fbdo.httpCode() == 200) {
|
||||||
|
float newTinggi = fbdo.floatData();
|
||||||
|
if (newTinggi != tinggiSensorDariDasar) {
|
||||||
|
tinggiSensorDariDasar = newTinggi;
|
||||||
|
Serial.println("🔄 Tinggi sensor diperbarui: " + String(newTinggi));
|
||||||
|
needUpdate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Firebase.getFloat(fbdo, pathAmbang) && fbdo.httpCode() == 200) {
|
||||||
|
float newAmbang = fbdo.floatData();
|
||||||
|
if (newAmbang != nilaiAmbangBatas) {
|
||||||
|
nilaiAmbangBatas = newAmbang;
|
||||||
|
Serial.println("🔄 Ambang batas diperbarui: " + String(newAmbang));
|
||||||
|
needUpdate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needUpdate) {
|
||||||
|
String cmd = "SETTING:TINGGI=" + String(tinggiSensorDariDasar, 1) +
|
||||||
|
",BATAS=" + String(nilaiAmbangBatas, 1) + ";";
|
||||||
|
Serial2.println(cmd);
|
||||||
|
Serial.println("📤 Mengirim kalibrasi: " + cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateFirebaseData(float ketinggian, int hujan) {
|
||||||
|
String path = "/" + namaSungai;
|
||||||
|
|
||||||
|
Firebase.setFloat(fbdo, path + "/ketinggian", ketinggian);
|
||||||
|
Firebase.setInt(fbdo, path + "/hujan", hujan);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendToBackend(float ketinggian) {
|
||||||
|
if (WiFi.status() != WL_CONNECTED) {
|
||||||
|
Serial.println("⚠ WiFi tidak terhubung");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String level = "AMAN";
|
||||||
|
if (ketinggian >= nilaiAmbangBatas) {
|
||||||
|
level = "BAHAYA";
|
||||||
|
} else if (ketinggian >= nilaiAmbangBatas * 0.7) {
|
||||||
|
level = "WASPADA";
|
||||||
|
}
|
||||||
|
|
||||||
|
time_t now;
|
||||||
|
time(&now);
|
||||||
|
|
||||||
|
HTTPClient http;
|
||||||
|
http.begin(backendHistoriURL);
|
||||||
|
http.addHeader("Content-Type", "application/json");
|
||||||
|
|
||||||
|
String jsonHistori = "{\"sungai\":\"" + namaSungai +
|
||||||
|
"\",\"ketinggian\":" + String(ketinggian, 1) +
|
||||||
|
",\"timestamp\":" + String(now) +
|
||||||
|
",\"level\":\"" + level + "\"}";
|
||||||
|
|
||||||
|
int httpCode = http.POST(jsonHistori);
|
||||||
|
Serial.printf(httpCode > 0 ? "✅ Histori terkirim (HTTP %d)\n" : "❌ Gagal kirim histori (HTTP %d)\n", httpCode);
|
||||||
|
http.end();
|
||||||
|
|
||||||
|
if (level == "WASPADA" || level == "BAHAYA") {
|
||||||
|
http.begin(backendNotifikasiURL);
|
||||||
|
http.addHeader("Content-Type", "application/json");
|
||||||
|
|
||||||
|
String jsonNotif = "{\"sungai\":\"" + namaSungai +
|
||||||
|
"\",\"ketinggian\":" + String(ketinggian, 1) +
|
||||||
|
",\"timestamp\":" + String(now) +
|
||||||
|
",\"level\":\"" + level + "\"}";
|
||||||
|
|
||||||
|
int notifCode = http.POST(jsonNotif);
|
||||||
|
Serial.printf(notifCode > 0 ? "📣 Notifikasi terkirim (HTTP %d)\n" : "⚠ Gagal kirim notifikasi (HTTP %d)\n", notifCode);
|
||||||
|
http.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🔥 Deteksi Kenaikan Cepat Air
|
||||||
|
void checkKenaikanDarurat() {
|
||||||
|
unsigned long now = millis();
|
||||||
|
if (now - lastDaruratCheck >= daruratInterval) {
|
||||||
|
float delta = lastKetinggian - lastKetinggianDarurat;
|
||||||
|
|
||||||
|
if (delta >= ambangKenaikanDarurat && lastKetinggian >= nilaiAmbangBatas * 0.7) {
|
||||||
|
Serial.println("🚨 Deteksi banjir mendadak! Naik cepat.");
|
||||||
|
kirimNotifikasiDarurat();
|
||||||
|
}
|
||||||
|
|
||||||
|
lastKetinggianDarurat = lastKetinggian;
|
||||||
|
lastDaruratCheck = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🔔 Kirim Notifikasi Darurat Langsung
|
||||||
|
void kirimNotifikasiDarurat() {
|
||||||
|
if (WiFi.status() != WL_CONNECTED) return;
|
||||||
|
|
||||||
|
time_t now;
|
||||||
|
time(&now);
|
||||||
|
|
||||||
|
HTTPClient http;
|
||||||
|
http.begin(backendNotifikasiURL);
|
||||||
|
http.addHeader("Content-Type", "application/json");
|
||||||
|
|
||||||
|
String jsonNotif = "{\"sungai\":\"" + namaSungai +
|
||||||
|
"\",\"ketinggian\":" + String(lastKetinggian, 1) +
|
||||||
|
",\"timestamp\":" + String(now) +
|
||||||
|
",\"level\":\"DARURAT\"}";
|
||||||
|
|
||||||
|
int code = http.POST(jsonNotif);
|
||||||
|
Serial.printf(code > 0 ? "🚨 Notif DARURAT dikirim (HTTP %d)\n" : "❌ Gagal kirim notif darurat (HTTP %d)\n", code);
|
||||||
|
http.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateLEDStatus() {
|
||||||
|
static unsigned long lastBlink = 0;
|
||||||
|
static bool ledState = false;
|
||||||
|
|
||||||
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
|
digitalWrite(LED_PIN, HIGH);
|
||||||
|
} else {
|
||||||
|
unsigned long now = millis();
|
||||||
|
if (now - lastBlink >= 500) {
|
||||||
|
lastBlink = now;
|
||||||
|
ledState = !ledState;
|
||||||
|
digitalWrite(LED_PIN, ledState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkResetButton() {
|
||||||
|
static unsigned long lastPress = 0;
|
||||||
|
|
||||||
|
if (digitalRead(BUTTON_PIN) == LOW && millis() - lastPress > 200) {
|
||||||
|
lastPress = millis();
|
||||||
|
Serial.println("🔄 Tombol reset WiFi ditekan");
|
||||||
|
digitalWrite(LED_PIN, LOW);
|
||||||
|
|
||||||
|
WiFiManager wm;
|
||||||
|
wm.resetSettings();
|
||||||
|
delay(1000);
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,160 @@
|
||||||
|
#include <HardwareSerial.h>
|
||||||
|
|
||||||
|
// -------------------- Pin Konfigurasi --------------------
|
||||||
|
#define LORA_TX 17
|
||||||
|
#define LORA_RX 16
|
||||||
|
#define M0 25
|
||||||
|
#define M1 26
|
||||||
|
#define TRIG_PIN 5
|
||||||
|
#define ECHO_PIN 18
|
||||||
|
#define RAIN_SENSOR_PIN 34
|
||||||
|
#define BUZZER_PIN 27
|
||||||
|
|
||||||
|
// -------------------- Variabel Sensor --------------------
|
||||||
|
float ketinggianAir;
|
||||||
|
int rainPercent;
|
||||||
|
|
||||||
|
// -------------------- Kalibrasi --------------------
|
||||||
|
float tinggiSensorDariDasar = 200.0;
|
||||||
|
float batasAmanKetinggian = 150.0;
|
||||||
|
|
||||||
|
// -------------------- Timing & Filter --------------------
|
||||||
|
unsigned long lastSendTime = 0;
|
||||||
|
const unsigned long sendInterval = 2000;
|
||||||
|
|
||||||
|
#define FILTER_SAMPLES 5
|
||||||
|
float distanceSamples[FILTER_SAMPLES];
|
||||||
|
int currentSample = 0;
|
||||||
|
|
||||||
|
// -------------------- Buzzer --------------------
|
||||||
|
unsigned long lastBuzzerTime = 0;
|
||||||
|
const unsigned long buzzerInterval = 200;
|
||||||
|
bool buzzerState = false;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
|
||||||
|
pinMode(M0, OUTPUT);
|
||||||
|
pinMode(M1, OUTPUT);
|
||||||
|
digitalWrite(M0, LOW);
|
||||||
|
digitalWrite(M1, LOW);
|
||||||
|
Serial2.begin(9600, SERIAL_8N1, LORA_RX, LORA_TX);
|
||||||
|
delay(500);
|
||||||
|
|
||||||
|
Serial.println("🚀 Sender LoRa Flood Detection Ready");
|
||||||
|
|
||||||
|
pinMode(TRIG_PIN, OUTPUT);
|
||||||
|
pinMode(ECHO_PIN, INPUT);
|
||||||
|
pinMode(RAIN_SENSOR_PIN, INPUT);
|
||||||
|
pinMode(BUZZER_PIN, OUTPUT);
|
||||||
|
digitalWrite(BUZZER_PIN, LOW);
|
||||||
|
|
||||||
|
for (int i = 0; i < FILTER_SAMPLES; i++) {
|
||||||
|
distanceSamples[i] = tinggiSensorDariDasar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
unsigned long currentMillis = millis();
|
||||||
|
|
||||||
|
processIncomingMessages();
|
||||||
|
|
||||||
|
float distance = readUltrasonic();
|
||||||
|
ketinggianAir = tinggiSensorDariDasar - distance;
|
||||||
|
ketinggianAir = constrain(ketinggianAir, 0, tinggiSensorDariDasar);
|
||||||
|
|
||||||
|
rainPercent = readRainSensor();,
|
||||||
|
|
||||||
|
controlBuzzer(currentMillis);
|
||||||
|
|
||||||
|
if (currentMillis - lastSendTime >= sendInterval) {
|
||||||
|
lastSendTime = currentMillis;
|
||||||
|
sendSensorData();
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(50);
|
||||||
|
}
|
||||||
|
|
||||||
|
void processIncomingMessages() {
|
||||||
|
if (Serial2.available()) {
|
||||||
|
String incoming = Serial2.readStringUntil('\n');
|
||||||
|
incoming.trim();
|
||||||
|
|
||||||
|
Serial.print("📩 Pesan masuk: ");
|
||||||
|
Serial.println(incoming);
|
||||||
|
|
||||||
|
if (incoming.startsWith("SETTING:")) {
|
||||||
|
int tStart = incoming.indexOf("TINGGI=");
|
||||||
|
int bStart = incoming.indexOf("BATAS=");
|
||||||
|
int endCmd = incoming.indexOf(';');
|
||||||
|
|
||||||
|
if (tStart != -1 && bStart != -1 && endCmd != -1) {
|
||||||
|
float newTinggi = incoming.substring(tStart + 7, bStart - 1).toFloat();
|
||||||
|
float newBatas = incoming.substring(bStart + 6, endCmd).toFloat();
|
||||||
|
|
||||||
|
if (newTinggi > 0 && newBatas > 0) {
|
||||||
|
tinggiSensorDariDasar = newTinggi;
|
||||||
|
batasAmanKetinggian = newBatas;
|
||||||
|
|
||||||
|
Serial.println("🔧 Kalibrasi diperbarui:");
|
||||||
|
Serial.println(" Tinggi Sensor: " + String(tinggiSensorDariDasar) + " cm");
|
||||||
|
Serial.println(" Batas Aman: " + String(batasAmanKetinggian) + " cm");
|
||||||
|
|
||||||
|
Serial2.println("ACK:SETTING_OK;");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float readUltrasonic() {
|
||||||
|
digitalWrite(TRIG_PIN, LOW);
|
||||||
|
delayMicroseconds(2);
|
||||||
|
digitalWrite(TRIG_PIN, HIGH);
|
||||||
|
delayMicroseconds(10);
|
||||||
|
digitalWrite(TRIG_PIN, LOW);
|
||||||
|
|
||||||
|
long duration = pulseIn(ECHO_PIN, HIGH, 30000);
|
||||||
|
|
||||||
|
if (duration == 0) {
|
||||||
|
Serial.println("⚠ Error: Sensor ultrasonik tidak merespon");
|
||||||
|
return tinggiSensorDariDasar;
|
||||||
|
}
|
||||||
|
|
||||||
|
float distance = duration * 0.034 / 2;
|
||||||
|
distanceSamples[currentSample] = distance;
|
||||||
|
currentSample = (currentSample + 1) % FILTER_SAMPLES;
|
||||||
|
|
||||||
|
float avgDistance = 0;
|
||||||
|
for (int i = 0; i < FILTER_SAMPLES; i++) {
|
||||||
|
avgDistance += distanceSamples[i];
|
||||||
|
}
|
||||||
|
return avgDistance / FILTER_SAMPLES;
|
||||||
|
}
|
||||||
|
|
||||||
|
int readRainSensor() {
|
||||||
|
int rawValue = analogRead(RAIN_SENSOR_PIN);
|
||||||
|
int percent = map(rawValue, 4095, 0, 0, 100);
|
||||||
|
return constrain(percent, 0, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
void controlBuzzer(unsigned long currentTime) {
|
||||||
|
if (ketinggianAir >= batasAmanKetinggian) {
|
||||||
|
if (currentTime - lastBuzzerTime >= buzzerInterval) {
|
||||||
|
lastBuzzerTime = currentTime;
|
||||||
|
buzzerState = !buzzerState;
|
||||||
|
digitalWrite(BUZZER_PIN, buzzerState ? HIGH : LOW);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
digitalWrite(BUZZER_PIN, LOW);
|
||||||
|
buzzerState = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendSensorData() {
|
||||||
|
String dataStr = "DATA:KETINGGIAN=" + String(ketinggianAir, 1) +
|
||||||
|
",HUJAN=" + String(rainPercent) + ";";
|
||||||
|
|
||||||
|
Serial2.println(dataStr);
|
||||||
|
Serial.println("📤 Mengirim data: " + dataStr);
|
||||||
|
}
|
Loading…
Reference in New Issue