diff --git a/arduino_fixed_code.ino b/arduino_fixed_code.ino deleted file mode 100644 index e69de29..0000000 diff --git a/arduino_fixed_code/arduino_fixed_code.ino b/arduino_fixed_code/arduino_fixed_code.ino deleted file mode 100644 index 540cebb..0000000 --- a/arduino_fixed_code/arduino_fixed_code.ino +++ /dev/null @@ -1,375 +0,0 @@ -/** - * ============================================ - * Smart Rack Security System - ESP32 - * PIR + Reed Switch + SW420 + LoRa + Laravel API - * ============================================ - */ - -#include -#include -#include -#include -#include -#include - -// ============================================ -// WIFI -// ============================================ -const char* ssid = "gege"; -const char* password = "biasaaja"; - -// GANTI DENGAN IP LARAVEL KAMU -const char* API_BASE = "https://keamanan-rak-barang-production.up.railway.app/api"; - -// ============================================ -// DEVICE -// ============================================ -const int DEVICE_ID = 1; -const String NODE_ID = "LORA_001"; - -// ============================================ -// PIN SENSOR -// ============================================ -#define PIR_PIN 33 -#define REED_PIN 32 -#define SW420_PIN 34 -#define BUZZER 25 -#define LED_MERAH 13 -#define LED_HIJAU 4 - -// ============================================ -// PIN LORA SX1278 RA-02 -// ============================================ -#define LORA_SS 27 -#define LORA_RST 14 -#define LORA_DIO0 26 - -// ============================================ -// VARIABEL -// ============================================ -int pirState; -int reedState; -int vibrationState; -int lastVibration = LOW; -bool motionActive = false; -bool doorOpen = false; -unsigned long motionStartTime = 0; -unsigned long doorOpenTime = 0; -unsigned long lastHeartbeat = 0; -const unsigned long HEARTBEAT_INTERVAL = 300000; - -// ============================================ -// LED -// ============================================ -void setLED(bool bahaya) { - digitalWrite(LED_MERAH, bahaya ? HIGH : LOW); - digitalWrite(LED_HIJAU, bahaya ? LOW : HIGH); -} - -// ============================================ -// BUZZER -// ============================================ -void buzzerPIR() { - digitalWrite(BUZZER, HIGH); - delay(300); - digitalWrite(BUZZER, LOW); -} - -void buzzerREED() { - for (int i = 0; i < 2; i++) { - digitalWrite(BUZZER, HIGH); - delay(100); - digitalWrite(BUZZER, LOW); - delay(100); - } -} - -void buzzerGETAR() { - for (int i = 0; i < 3; i++) { - digitalWrite(BUZZER, HIGH); - delay(80); - digitalWrite(BUZZER, LOW); - delay(80); - } -} - -void buzzerStartup() { - for (int i = 0; i < 2; i++) { - digitalWrite(BUZZER, HIGH); - delay(200); - digitalWrite(BUZZER, LOW); - delay(100); - } -} - -// ============================================ -// WIFI -// ============================================ -void setupWifi() { - Serial.println("Menghubungkan WiFi: " + String(ssid)); - WiFi.mode(WIFI_STA); - WiFi.begin(ssid, password); - - int attempt = 0; - while (WiFi.status() != WL_CONNECTED && attempt < 30) { - delay(500); - Serial.print("."); - attempt++; - } - - if (WiFi.status() == WL_CONNECTED) { - Serial.println(""); - Serial.println("WiFi Connected!"); - Serial.println("IP: " + WiFi.localIP().toString()); - } else { - Serial.println(""); - Serial.println("WiFi gagal terhubung. Cek SSID/Password."); - Serial.println("Pastikan hotspot 2.4GHz dan sudah aktif."); - } -} - -void checkWifi() { - if (WiFi.status() != WL_CONNECTED) { - Serial.println("WiFi putus, reconnecting..."); - WiFi.disconnect(); - delay(1000); - WiFi.begin(ssid, password); - - int attempt = 0; - while (WiFi.status() != WL_CONNECTED && attempt < 20) { - delay(500); - Serial.print("."); - attempt++; - } - - if (WiFi.status() == WL_CONNECTED) { - Serial.println("\nWiFi terhubung kembali! IP: " + WiFi.localIP().toString()); - } else { - Serial.println("\nGagal reconnect. Tunggu 10 detik..."); - delay(10000); // tunggu lebih lama sebelum coba lagi - } - } -} - -// ============================================ -// HTTP POST -// ============================================ -bool httpPost(String endpoint, String jsonBody) { - if (WiFi.status() != WL_CONNECTED) { - Serial.println("WiFi disconnected, skip kirim data"); - return false; - } - - HTTPClient http; - String url = String(API_BASE) + endpoint; - - http.begin(url); - http.addHeader("Content-Type", "application/json"); - http.addHeader("Accept", "application/json"); - http.setTimeout(8000); - - int httpCode = http.POST(jsonBody); - - if (httpCode > 0) { - String response = http.getString(); - Serial.println("HTTP " + String(httpCode) + " → " + endpoint); - if (httpCode == 201 || httpCode == 200) { - Serial.println("✓ Data tersimpan"); - http.end(); - return true; - } else { - Serial.println("✗ Response: " + response.substring(0, 150)); - } - } else { - Serial.println("✗ HTTP Error: " + String(httpCode)); - Serial.println(" URL: " + url); - Serial.println(" Cek: server jalan? IP benar? Firewall?"); - } - - http.end(); - return false; -} - -// ============================================ -// KIRIM PIR -// ============================================ -void kirimDataPIR(bool detected, int intensity, int duration, String zone) { - StaticJsonDocument<256> doc; - doc["device_id"] = DEVICE_ID; - doc["motion_detected"] = detected; - doc["motion_intensity"] = intensity; - doc["duration_seconds"] = duration; - doc["detection_zone"] = zone; - - String body; - serializeJson(doc, body); - Serial.println("Kirim PIR → intensity=" + String(intensity) + " duration=" + String(duration) + "s"); - httpPost("/pir/data", body); -} - -// ============================================ -// KIRIM GETARAN -// ============================================ -void kirimDataGetaran(float x, float y, float z) { - StaticJsonDocument<256> doc; - doc["device_id"] = DEVICE_ID; - doc["x_axis"] = x; - doc["y_axis"] = y; - doc["z_axis"] = z; - doc["threshold"] = 2.0; - - String body; - serializeJson(doc, body); - float mag = sqrt(x*x + y*y + z*z); - Serial.println("Kirim Getaran → magnitude=" + String(mag, 2)); - httpPost("/vibration/data", body); -} - -// ============================================ -// KIRIM REED SWITCH -// ============================================ -void kirimDataReedSwitch(bool opened, int duration) { - StaticJsonDocument<256> doc; - doc["device_id"] = DEVICE_ID; - doc["door_opened"] = opened; - doc["duration_seconds"] = duration; - doc["access_method"] = "manual"; - doc["door_location"] = "rack"; - doc["is_forced_entry"] = false; - - String body; - serializeJson(doc, body); - Serial.println("Kirim Reed Switch → opened=" + String(opened) + " duration=" + String(duration) + "s"); - httpPost("/door-access/data", body); -} - -// ============================================ -// HEARTBEAT -// ============================================ -void kirimHeartbeat() { - String payload = "HEARTBEAT|" + NODE_ID; - LoRa.beginPacket(); - LoRa.print(payload); - LoRa.endPacket(); - Serial.println("Heartbeat terkirim. Uptime: " + String(millis()/1000) + "s"); -} - -// ============================================ -// SETUP -// ============================================ -void setup() { - Serial.begin(115200); - delay(1000); - - // SENSOR - pinMode(PIR_PIN, INPUT); - pinMode(REED_PIN, INPUT_PULLUP); // PULLUP untuk reed switch - pinMode(SW420_PIN, INPUT); - - // OUTPUT - pinMode(BUZZER, OUTPUT); - pinMode(LED_MERAH, OUTPUT); - pinMode(LED_HIJAU, OUTPUT); - - digitalWrite(BUZZER, LOW); - setLED(false); // LED hijau menyala default - - Serial.println("================================="); - Serial.println(" SMART RACK SECURITY SYSTEM"); - Serial.println("================================="); - - // WIFI - setupWifi(); - - // LORA - LoRa.setPins(LORA_SS, LORA_RST, LORA_DIO0); - if (!LoRa.begin(433E6)) { - Serial.println("LoRa gagal! Lanjut tanpa LoRa."); - } else { - LoRa.setTxPower(17); - Serial.println("LoRa OK"); - } - - buzzerStartup(); - Serial.println("System Ready!"); - Serial.println("API: " + String(API_BASE)); - Serial.println("Device ID: " + String(DEVICE_ID)); -} - -// ============================================ -// LOOP -// ============================================ -void loop() { - checkWifi(); - - pirState = digitalRead(PIR_PIN); - reedState = digitalRead(REED_PIN); // LOW = tertutup, HIGH = terbuka - vibrationState = digitalRead(SW420_PIN); - - // ========================================= - // PIR - Deteksi Gerakan - // ========================================= - if (pirState == HIGH && !motionActive) { - motionActive = true; - motionStartTime = millis(); - Serial.println(">>> PIR: Gerakan terdeteksi!"); - buzzerPIR(); - setLED(true); - } - else if (pirState == LOW && motionActive) { - motionActive = false; - int duration = (millis() - motionStartTime) / 1000; - int intensity = random(60, 90); - kirimDataPIR(true, intensity, duration, "center"); - Serial.println(">>> PIR: Gerakan selesai, durasi=" + String(duration) + "s"); - } - - // ========================================= - // REED SWITCH - Status Rak - // ========================================= - if (reedState == HIGH && !doorOpen) { - doorOpen = true; - doorOpenTime = millis(); - Serial.println(">>> Reed Switch: Rak terbuka!"); - buzzerREED(); - setLED(true); - } - else if (reedState == LOW && doorOpen) { - doorOpen = false; - int duration = (millis() - doorOpenTime) / 1000; - kirimDataReedSwitch(true, duration); - Serial.println(">>> Reed Switch: Rak tertutup, durasi=" + String(duration) + "s"); - } - - // ========================================= - // SW-420 - Deteksi Getaran - // ========================================= - if (vibrationState == HIGH && lastVibration == LOW) { - Serial.println(">>> SW-420: Getaran terdeteksi!"); - float x = random(-300, 300) / 100.0; - float y = random(-300, 300) / 100.0; - float z = random(80, 120) / 100.0; - buzzerGETAR(); - setLED(true); - kirimDataGetaran(x, y, z); - } - - // ========================================= - // Kembali normal jika tidak ada bahaya - // ========================================= - if (!motionActive && !doorOpen && vibrationState == LOW) { - setLED(false); - } - - lastVibration = vibrationState; - - // ========================================= - // HEARTBEAT setiap 5 menit - // ========================================= - if (millis() - lastHeartbeat >= HEARTBEAT_INTERVAL) { - kirimHeartbeat(); - lastHeartbeat = millis(); - } - - delay(300); -} diff --git a/arduino_fixed_code/esp32_gateway_receiver.ino b/arduino_fixed_code/esp32_gateway_receiver.ino new file mode 100644 index 0000000..b8aae20 --- /dev/null +++ b/arduino_fixed_code/esp32_gateway_receiver.ino @@ -0,0 +1,343 @@ +/** + * ============================================ + * Smart Rack Security System + * ESP32 GATEWAY (RECEIVER) — Di luar rak + * + * Fungsi : Terima data LoRa dari Node, forward ke Railway API + * Komunikasi : LoRa SX1278 (RX) + WiFi (HTTPS ke Railway) + * TIDAK ada sensor di sini + * ============================================ + */ + +#include +#include +#include +#include +#include +#include + +// ============================================ +// KONFIGURASI — SESUAIKAN +// ============================================ +const char* WIFI_SSID = "gege"; +const char* WIFI_PASSWORD = "biasaaja"; +const char* API_BASE = "https://keamanan-rak-barang-production.up.railway.app/api"; +const int DEVICE_ID = 1; // ID device di database Railway + +// ============================================ +// PIN LORA SX1278 RA-02 +// ============================================ +#define LORA_SS 27 +#define LORA_RST 14 +#define LORA_DIO0 26 +#define LORA_FREQ 433E6 // Harus sama dengan Node Sender! + +// ============================================ +// PIN LED STATUS GATEWAY (opsional) +// ============================================ +#define LED_WIFI 2 // LED bawaan ESP32 — nyala = WiFi OK +#define LED_LORA 4 // Kedip = terima paket LoRa + +// ============================================ +// STATISTIK +// ============================================ +unsigned long totalReceived = 0; +unsigned long totalSent = 0; +unsigned long totalFailed = 0; +unsigned long lastStatusPrint = 0; + +// ============================================ +// SETUP WIFI +// ============================================ +void setupWifi() { + Serial.println("[WiFi] Menghubungkan ke: " + String(WIFI_SSID)); + WiFi.mode(WIFI_STA); + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + + int attempt = 0; + while (WiFi.status() != WL_CONNECTED && attempt < 40) { + delay(500); + Serial.print("."); + attempt++; + } + + if (WiFi.status() == WL_CONNECTED) { + Serial.println("\n[WiFi] ✓ Terhubung! IP: " + WiFi.localIP().toString()); + digitalWrite(LED_WIFI, HIGH); + } else { + Serial.println("\n[WiFi] ✗ Gagal. Cek SSID/Password atau pastikan 2.4GHz."); + digitalWrite(LED_WIFI, LOW); + } +} + +void checkWifi() { + if (WiFi.status() != WL_CONNECTED) { + Serial.println("[WiFi] Putus, reconnecting..."); + digitalWrite(LED_WIFI, LOW); + WiFi.disconnect(); + delay(1000); + WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + + int attempt = 0; + while (WiFi.status() != WL_CONNECTED && attempt < 20) { + delay(500); + Serial.print("."); + attempt++; + } + + if (WiFi.status() == WL_CONNECTED) { + Serial.println("\n[WiFi] ✓ Terhubung kembali!"); + digitalWrite(LED_WIFI, HIGH); + } else { + Serial.println("\n[WiFi] ✗ Gagal reconnect."); + } + } +} + +// ============================================ +// HTTP POST KE RAILWAY API +// ============================================ +bool httpPost(String endpoint, String jsonBody) { + if (WiFi.status() != WL_CONNECTED) { + Serial.println("[HTTP] Skip — WiFi tidak terhubung"); + return false; + } + + WiFiClientSecure client; + client.setInsecure(); // Skip SSL verify — cukup untuk Railway + + HTTPClient http; + String url = String(API_BASE) + endpoint; + + http.begin(client, url); + http.addHeader("Content-Type", "application/json"); + http.addHeader("Accept", "application/json"); + http.setTimeout(10000); + + int httpCode = http.POST(jsonBody); + + if (httpCode > 0) { + if (httpCode == 200 || httpCode == 201) { + Serial.println("[HTTP] ✓ " + endpoint + " → " + String(httpCode)); + http.end(); + totalSent++; + return true; + } else { + String resp = http.getString(); + Serial.println("[HTTP] ✗ " + endpoint + " → " + String(httpCode)); + Serial.println(" " + resp.substring(0, 120)); + } + } else { + Serial.println("[HTTP] ✗ Error: " + String(httpCode) + " — " + endpoint); + } + + http.end(); + totalFailed++; + return false; +} + +// ============================================ +// PROSES PAKET PIR +// ============================================ +void prosesPIR(String nodeId, String jsonStr) { + StaticJsonDocument<256> src; + if (deserializeJson(src, jsonStr) != DeserializationError::Ok) { + Serial.println("[PIR] JSON parse error"); + return; + } + + StaticJsonDocument<256> doc; + doc["device_id"] = DEVICE_ID; + doc["motion_detected"] = src["motion_detected"] | false; + doc["motion_intensity"] = src["motion_intensity"] | 50; + doc["duration_seconds"] = src["duration_seconds"] | 0; + doc["detection_zone"] = src["detection_zone"] | "center"; + + String body; + serializeJson(doc, body); + Serial.println("[PIR] Node=" + nodeId + " → forward ke API"); + httpPost("/pir/data", body); +} + +// ============================================ +// PROSES PAKET REED SWITCH +// ============================================ +void prosesREED(String nodeId, String jsonStr) { + StaticJsonDocument<256> src; + if (deserializeJson(src, jsonStr) != DeserializationError::Ok) { + Serial.println("[REED] JSON parse error"); + return; + } + + StaticJsonDocument<256> doc; + doc["device_id"] = DEVICE_ID; + doc["door_opened"] = src["door_opened"] | false; + doc["duration_seconds"] = src["duration_seconds"] | 0; + doc["access_method"] = src["access_method"] | "manual"; + doc["door_location"] = src["door_location"] | "rack"; + doc["is_forced_entry"] = src["is_forced_entry"] | false; + + String body; + serializeJson(doc, body); + Serial.println("[REED] Node=" + nodeId + " → forward ke API"); + httpPost("/door-access/data", body); +} + +// ============================================ +// PROSES PAKET GETARAN SW420 +// ============================================ +void prosesVIBRATION(String nodeId, String jsonStr) { + StaticJsonDocument<256> src; + if (deserializeJson(src, jsonStr) != DeserializationError::Ok) { + Serial.println("[VIBRATION] JSON parse error"); + return; + } + + StaticJsonDocument<256> doc; + doc["device_id"] = DEVICE_ID; + doc["x_axis"] = src["x_axis"] | 0.0; + doc["y_axis"] = src["y_axis"] | 0.0; + doc["z_axis"] = src["z_axis"] | 0.0; + doc["threshold"] = src["threshold"] | 2.0; + + String body; + serializeJson(doc, body); + Serial.println("[VIBRATION] Node=" + nodeId + " → forward ke API"); + httpPost("/vibration/data", body); +} + +// ============================================ +// PROSES PAKET HEARTBEAT +// ============================================ +void prosesHEARTBEAT(String nodeId, String jsonStr) { + StaticJsonDocument<128> src; + deserializeJson(src, jsonStr); + long uptime = src["uptime_s"] | 0; + Serial.println("[HEARTBEAT] Node=" + nodeId + " masih hidup, uptime=" + String(uptime) + "s"); + + // Kirim heartbeat ke API agar device tetap Online di dashboard + StaticJsonDocument<128> doc; + doc["node_id"] = nodeId; + doc["gateway_id"] = "GATEWAY_001"; + doc["payload"] = "HEARTBEAT|" + nodeId; + + String body; + serializeJson(doc, body); + httpPost("/lora/receive", body); +} + +// ============================================ +// PARSE & ROUTE PAKET LORA +// Format paket: TYPE|NODE_ID|{json} +// ============================================ +void prosesPacket(String packet, int rssi, float snr) { + totalReceived++; + + // Kedip LED tanda terima paket + digitalWrite(LED_LORA, HIGH); delay(50); digitalWrite(LED_LORA, LOW); + + Serial.println("─────────────────────────────────"); + Serial.println("[LoRa RX] RSSI=" + String(rssi) + " SNR=" + String(snr, 1)); + Serial.println("[LoRa RX] Raw: " + packet.substring(0, 100)); + + // Parse format: TYPE|NODE_ID|{json} + int sep1 = packet.indexOf('|'); + if (sep1 < 0) { + Serial.println("[LoRa RX] Format tidak dikenal, skip."); + return; + } + + int sep2 = packet.indexOf('|', sep1 + 1); + if (sep2 < 0) { + Serial.println("[LoRa RX] Format tidak lengkap, skip."); + return; + } + + String type = packet.substring(0, sep1); + String nodeId = packet.substring(sep1 + 1, sep2); + String payload = packet.substring(sep2 + 1); + + Serial.println("[LoRa RX] Type=" + type + " Node=" + nodeId); + + checkWifi(); // Pastikan WiFi masih konek sebelum forward + + if (type == "PIR") prosesPIR(nodeId, payload); + else if (type == "REED") prosesREED(nodeId, payload); + else if (type == "VIBRATION") prosesVIBRATION(nodeId, payload); + else if (type == "HEARTBEAT") prosesHEARTBEAT(nodeId, payload); + else { + Serial.println("[LoRa RX] Type tidak dikenal: " + type); + } +} + +// ============================================ +// SETUP +// ============================================ +void setup() { + Serial.begin(115200); + delay(1000); + + pinMode(LED_WIFI, OUTPUT); + pinMode(LED_LORA, OUTPUT); + digitalWrite(LED_WIFI, LOW); + digitalWrite(LED_LORA, LOW); + + Serial.println("================================="); + Serial.println(" SMART RACK — GATEWAY RECEIVER"); + Serial.println(" API: " + String(API_BASE)); + Serial.println(" Device ID: " + String(DEVICE_ID)); + Serial.println("================================="); + + // WiFi + setupWifi(); + + // LoRa — HARUS setting sama persis dengan Node Sender + LoRa.setPins(LORA_SS, LORA_RST, LORA_DIO0); + if (!LoRa.begin(LORA_FREQ)) { + Serial.println("[LoRa] GAGAL INIT! Cek wiring."); + while (true) { + digitalWrite(LED_LORA, HIGH); delay(200); + digitalWrite(LED_LORA, LOW); delay(200); + } + } + + LoRa.setSpreadingFactor(9); // Harus sama dengan Node! + LoRa.setSignalBandwidth(125E3); + LoRa.setCodingRate4(5); + + Serial.println("[LoRa] ✓ OK — " + String(LORA_FREQ / 1E6) + " MHz, menunggu paket..."); + Serial.println("Gateway siap!"); +} + +// ============================================ +// LOOP +// ============================================ +void loop() { + // Cek apakah ada paket LoRa masuk + int packetSize = LoRa.parsePacket(); + + if (packetSize > 0) { + String packet = ""; + while (LoRa.available()) { + packet += (char)LoRa.read(); + } + int rssi = LoRa.packetRssi(); + float snr = LoRa.packetSnr(); + + prosesPacket(packet, rssi, snr); + } + + // Print statistik setiap 60 detik + if (millis() - lastStatusPrint >= 60000) { + Serial.println("─────────────────────────────────"); + Serial.println("[STATUS] Uptime: " + String(millis()/1000) + "s"); + Serial.println("[STATUS] LoRa diterima : " + String(totalReceived)); + Serial.println("[STATUS] API berhasil : " + String(totalSent)); + Serial.println("[STATUS] API gagal : " + String(totalFailed)); + Serial.println("[STATUS] WiFi: " + String(WiFi.status() == WL_CONNECTED ? "OK" : "PUTUS")); + Serial.println("─────────────────────────────────"); + lastStatusPrint = millis(); + } + + delay(10); // Kecil saja agar LoRa tidak miss paket +} diff --git a/arduino_fixed_code/esp32_node_sender.ino b/arduino_fixed_code/esp32_node_sender.ino new file mode 100644 index 0000000..83ff713 --- /dev/null +++ b/arduino_fixed_code/esp32_node_sender.ino @@ -0,0 +1,304 @@ +/** + * ============================================ + * Smart Rack Security System + * ESP32 NODE (SENDER) — Di dalam / dekat rak + * + * Sensor : PIR + Reed Switch Magnetik + SW420 + * Output : Buzzer + LED Merah + LED Hijau + * Komunikasi : LoRa SX1278 (TX only) + * TIDAK butuh WiFi + * ============================================ + */ + +#include +#include +#include +#include + +// ============================================ +// IDENTITAS NODE +// ============================================ +#define NODE_ID "NODE_001" // ID unik node ini + +// ============================================ +// PIN SENSOR +// ============================================ +#define PIR_PIN 33 +#define REED_PIN 32 // INPUT_PULLUP — LOW = tertutup, HIGH = terbuka +#define SW420_PIN 34 + +// ============================================ +// PIN OUTPUT +// ============================================ +#define BUZZER 25 +#define LED_MERAH 13 +#define LED_HIJAU 4 + +// ============================================ +// PIN LORA SX1278 RA-02 +// ============================================ +#define LORA_SS 27 +#define LORA_RST 14 +#define LORA_DIO0 26 +#define LORA_FREQ 433E6 // 433 MHz — sesuaikan modul kamu + +// ============================================ +// VARIABEL STATE SENSOR +// ============================================ +int pirState = LOW; +int reedState = LOW; +int vibrationState = LOW; +int lastVibration = LOW; + +bool motionActive = false; +bool doorOpen = false; + +unsigned long motionStartTime = 0; +unsigned long doorOpenTime = 0; +unsigned long lastHeartbeat = 0; + +// Heartbeat ke gateway setiap 2 menit +const unsigned long HEARTBEAT_INTERVAL = 120000; + +// ============================================ +// LED +// ============================================ +void setLED(bool bahaya) { + digitalWrite(LED_MERAH, bahaya ? HIGH : LOW); + digitalWrite(LED_HIJAU, bahaya ? LOW : HIGH); +} + +// ============================================ +// BUZZER +// ============================================ +void buzzerPIR() { + digitalWrite(BUZZER, HIGH); delay(300); + digitalWrite(BUZZER, LOW); +} + +void buzzerREED() { + for (int i = 0; i < 2; i++) { + digitalWrite(BUZZER, HIGH); delay(100); + digitalWrite(BUZZER, LOW); delay(100); + } +} + +void buzzerGETAR() { + for (int i = 0; i < 3; i++) { + digitalWrite(BUZZER, HIGH); delay(80); + digitalWrite(BUZZER, LOW); delay(80); + } +} + +void buzzerStartup() { + for (int i = 0; i < 2; i++) { + digitalWrite(BUZZER, HIGH); delay(200); + digitalWrite(BUZZER, LOW); delay(100); + } +} + +// ============================================ +// KIRIM PAKET LORA +// Format: TYPE|NODE_ID|json_payload +// ============================================ +bool loRaSend(String type, String jsonPayload) { + String packet = type + "|" + NODE_ID + "|" + jsonPayload; + + LoRa.beginPacket(); + LoRa.print(packet); + int result = LoRa.endPacket(); + + if (result) { + Serial.println("[LoRa TX] ✓ " + packet.substring(0, 80)); + } else { + Serial.println("[LoRa TX] ✗ Gagal kirim paket"); + } + return result; +} + +// ============================================ +// KIRIM DATA PIR +// ============================================ +void kirimPIR(bool detected, int intensity, int duration) { + StaticJsonDocument<200> doc; + doc["motion_detected"] = detected; + doc["motion_intensity"] = intensity; + doc["duration_seconds"] = duration; + doc["detection_zone"] = "center"; + + String body; + serializeJson(doc, body); + Serial.println("[PIR] detected=" + String(detected) + " intensity=" + String(intensity) + " dur=" + String(duration) + "s"); + loRaSend("PIR", body); +} + +// ============================================ +// KIRIM DATA REED SWITCH +// ============================================ +void kirimReed(bool opened, int duration) { + StaticJsonDocument<200> doc; + doc["door_opened"] = opened; + doc["duration_seconds"] = duration; + doc["access_method"] = "manual"; + doc["door_location"] = "rack"; + doc["is_forced_entry"] = false; + + String body; + serializeJson(doc, body); + Serial.println("[REED] opened=" + String(opened) + " dur=" + String(duration) + "s"); + loRaSend("REED", body); +} + +// ============================================ +// KIRIM DATA GETARAN SW420 +// ============================================ +void kirimGetaran(float x, float y, float z) { + StaticJsonDocument<200> doc; + doc["x_axis"] = x; + doc["y_axis"] = y; + doc["z_axis"] = z; + doc["threshold"] = 2.0; + + String body; + serializeJson(doc, body); + float mag = sqrt(x*x + y*y + z*z); + Serial.println("[SW420] magnitude=" + String(mag, 2)); + loRaSend("VIBRATION", body); +} + +// ============================================ +// HEARTBEAT — beri tahu gateway node masih hidup +// ============================================ +void kirimHeartbeat() { + StaticJsonDocument<64> doc; + doc["uptime_s"] = millis() / 1000; + + String body; + serializeJson(doc, body); + Serial.println("[HEARTBEAT] uptime=" + String(millis()/1000) + "s"); + loRaSend("HEARTBEAT", body); +} + +// ============================================ +// SETUP +// ============================================ +void setup() { + Serial.begin(115200); + delay(1000); + + // Pin sensor + pinMode(PIR_PIN, INPUT); + pinMode(REED_PIN, INPUT_PULLUP); + pinMode(SW420_PIN, INPUT); + + // Pin output + pinMode(BUZZER, OUTPUT); + pinMode(LED_MERAH, OUTPUT); + pinMode(LED_HIJAU, OUTPUT); + + digitalWrite(BUZZER, LOW); + setLED(false); // LED hijau nyala default + + Serial.println("================================="); + Serial.println(" SMART RACK — NODE SENDER"); + Serial.println(" Node ID : " NODE_ID); + Serial.println("================================="); + + // Init LoRa + LoRa.setPins(LORA_SS, LORA_RST, LORA_DIO0); + if (!LoRa.begin(LORA_FREQ)) { + Serial.println("[LoRa] GAGAL INIT! Cek wiring."); + // Blink LED merah terus sebagai tanda error + while (true) { + digitalWrite(LED_MERAH, HIGH); delay(200); + digitalWrite(LED_MERAH, LOW); delay(200); + } + } + + LoRa.setTxPower(17); + LoRa.setSpreadingFactor(9); // SF9 — balance range vs speed + LoRa.setSignalBandwidth(125E3); + LoRa.setCodingRate4(5); + + Serial.println("[LoRa] OK — " + String(LORA_FREQ / 1E6) + " MHz"); + + buzzerStartup(); + Serial.println("Node siap!"); +} + +// ============================================ +// LOOP +// ============================================ +void loop() { + pirState = digitalRead(PIR_PIN); + reedState = digitalRead(REED_PIN); + vibrationState = digitalRead(SW420_PIN); + + // ========================================= + // PIR — Deteksi Gerakan + // ========================================= + if (pirState == HIGH && !motionActive) { + motionActive = true; + motionStartTime = millis(); + Serial.println(">>> PIR: Gerakan terdeteksi!"); + buzzerPIR(); + setLED(true); + } + else if (pirState == LOW && motionActive) { + motionActive = false; + int duration = (millis() - motionStartTime) / 1000; + int intensity = random(60, 90); // estimasi intensitas + kirimPIR(true, intensity, duration); + Serial.println(">>> PIR: Selesai, durasi=" + String(duration) + "s"); + } + + // ========================================= + // REED SWITCH — Status Rak + // ========================================= + if (reedState == HIGH && !doorOpen) { + doorOpen = true; + doorOpenTime = millis(); + Serial.println(">>> REED: Rak terbuka!"); + buzzerREED(); + setLED(true); + } + else if (reedState == LOW && doorOpen) { + doorOpen = false; + int duration = (millis() - doorOpenTime) / 1000; + kirimReed(true, duration); + Serial.println(">>> REED: Rak tertutup, durasi=" + String(duration) + "s"); + } + + // ========================================= + // SW-420 — Deteksi Getaran + // ========================================= + if (vibrationState == HIGH && lastVibration == LOW) { + Serial.println(">>> SW420: Getaran terdeteksi!"); + // Simulasi nilai axis — ganti dengan sensor akselerometer nyata jika ada + float x = random(-300, 300) / 100.0; + float y = random(-300, 300) / 100.0; + float z = random(80, 120) / 100.0; + buzzerGETAR(); + setLED(true); + kirimGetaran(x, y, z); + } + + // ========================================= + // Reset LED jika semua aman + // ========================================= + if (!motionActive && !doorOpen && vibrationState == LOW) { + setLED(false); + } + + lastVibration = vibrationState; + + // ========================================= + // HEARTBEAT setiap 2 menit + // ========================================= + if (millis() - lastHeartbeat >= HEARTBEAT_INTERVAL) { + kirimHeartbeat(); + lastHeartbeat = millis(); + } + + delay(300); +} diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php index e8af643..10a691f 100644 --- a/resources/views/home.blade.php +++ b/resources/views/home.blade.php @@ -22,25 +22,44 @@ @@ -331,5 +350,36 @@ feather.replace() + + \ No newline at end of file