#include #include #include #include #include #include #include #define DEBUG_MODE true const char* ssid = "KOPI"; const char* password = "digoreng123"; const String API_BASE_URL = "https://smartlocker.punyapadias.my.id/api/"; #define SS_PIN 21 #define RST_PIN 5 MFRC522 mfrc522(SS_PIN, RST_PIN); LiquidCrystal_I2C lcd(0x27, 16, 2); const int trigPins[3] = {17, 16, 33}; const int echoPins[3] = {35, 34, 32}; #define RELAY1_PIN 14 #define RELAY2_PIN 27 #define RELAY3_PIN 26 #define BUZZER_PIN 13 enum SystemState { STATE_IDLE, STATE_RFID_DETECTED, STATE_OPENING_LOCKER, STATE_REGISTERING_RFID, STATE_ERROR }; SystemState currentState = STATE_IDLE; unsigned long lastApiCallTime = 0; const long apiCallInterval = 5000; unsigned long rfidActionStartTime = 0; const long rfidActionDuration = 10000; bool lcdIdleShown = false; struct RFIDMapping { String uid; int lokerId; int relayPin; }; RFIDMapping rfidMappings[] = { {"36CB3503", 1, RELAY1_PIN}, {"5C312903", 2, RELAY2_PIN}, {"611B2803", 3, RELAY3_PIN}, }; const int jumlahRFID = sizeof(rfidMappings) / sizeof(rfidMappings[0]); void setRFIDtoLoker(int lokerId, String rfid); void updateStatusLoker(int lokerId, String status); int getFirstEmptyRFIDLokerId(); String getStatusLoker(int lokerId); void clearRFID(); void playBuzzer(int count, int durationMs, int pauseMs); void displayLcdMessage(String line1, String line2, int duration = 0); void handleRFIDAction(String uidStr, int mappedLokerId, int mappedRelayPin); void handleNewRFIDRegistration(String uidStr); void displayDefaultScreen() { lcd.clear(); lcd.setCursor(0, 0); lcd.print("Loker Pintar"); lcd.setCursor(0, 1); lcd.print("Scan Kartu"); } void setup() { Serial.begin(115200); Wire.begin(4, 22); lcd.init(); lcd.backlight(); lcd.setCursor(0, 0); lcd.print("Inisialisasi..."); // Menyambung ke WiFi WiFi.begin(ssid, password); lcd.setCursor(0, 1); lcd.print("WiFi connecting..."); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } lcd.setCursor(0, 1); lcd.print("WiFi connected "); Serial.println("\nWiFi Connected. IP: " + WiFi.localIP().toString()); // Inisialisasi RFID SPI.begin(); mfrc522.PCD_Init(); // Inisialisasi sensor ultrasonik for (int i = 0; i < 3; i++) { pinMode(trigPins[i], OUTPUT); pinMode(echoPins[i], INPUT); } // Inisialisasi relay dan buzzer pinMode(RELAY1_PIN, OUTPUT); digitalWrite(RELAY1_PIN, HIGH); pinMode(RELAY2_PIN, OUTPUT); digitalWrite(RELAY2_PIN, HIGH); pinMode(RELAY3_PIN, OUTPUT); digitalWrite(RELAY3_PIN, HIGH); pinMode(BUZZER_PIN, OUTPUT); digitalWrite(BUZZER_PIN, LOW); // Tampilkan layar utama lcd.clear(); displayDefaultScreen(); lastApiCallTime = millis(); } void loop() { // Debug: tampilkan state saat ini setiap 1 detik static unsigned long lastDebugTime = 0; if (millis() - lastDebugTime > 1000) { Serial.println("[DEBUG] Current state: " + String(currentState)); lastDebugTime = millis(); } // Recovery: jika RFID tidak terbaca dalam waktu lama, inisialisasi ulang static unsigned long lastRFIDScanTime = 0; if (millis() - lastRFIDScanTime > 10000) { Serial.println("[RFID] Timeout, re-initialize RFID."); mfrc522.PCD_Init(); lastRFIDScanTime = millis(); } switch (currentState) { case STATE_IDLE: // Tampilkan LCD hanya sekali saat idle if (!lcdIdleShown) { displayDefaultScreen(); lcdIdleShown = true; } // Cek kartu RFID if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) { lcdIdleShown = false; // agar layar bisa refresh nanti playBuzzer(1, 50, 0); lastRFIDScanTime = millis(); // reset timeout counter // Ambil UID String uidStr = ""; for (byte i = 0; i < mfrc522.uid.size; i++) { if (mfrc522.uid.uidByte[i] < 0x10) uidStr += "0"; uidStr += String(mfrc522.uid.uidByte[i], HEX); } uidStr.toUpperCase(); Serial.println("[RFID] UID: " + uidStr); displayLcdMessage("RFID Terbaca:", uidStr); // Cek UID pada daftar bool found = false; int lokerId = -1; int relayPin = -1; for (int i = 0; i < jumlahRFID; i++) { if (uidStr == rfidMappings[i].uid) { found = true; lokerId = rfidMappings[i].lokerId; relayPin = rfidMappings[i].relayPin; break; } } if (found) { currentState = STATE_OPENING_LOCKER; handleRFIDAction(uidStr, lokerId, relayPin); } else { // Jika tidak ditemukan, aktifkan buzzer "tit-tit-tit" dan tampilkan pesan playBuzzer(3, 100, 100); // Bunyi 3 kali dengan interval 100ms displayLcdMessage("Kartu Tidak", "Terdaftar!", 2000); currentState = STATE_REGISTERING_RFID; handleNewRFIDRegistration(uidStr); } clearRFID(); delay(200); // beri waktu recovery RFID } break; case STATE_REGISTERING_RFID: case STATE_OPENING_LOCKER: lcdIdleShown = false; currentState = STATE_IDLE; break; case STATE_ERROR: if (WiFi.status() != WL_CONNECTED) { displayLcdMessage("WiFi Disconnect!", "Reconnecting..."); WiFi.reconnect(); delay(1000); } else { lcdIdleShown = false; currentState = STATE_IDLE; } break; } } void handleRFIDAction(String uidStr, int mappedLokerId, int mappedRelayPin) { if (DEBUG_MODE) Serial.println("[RFID Action] Membuka Loker ID: " + String(mappedLokerId) + " dengan UID: " + uidStr); // Aktifkan relay untuk membuka loker digitalWrite(mappedRelayPin, LOW); playBuzzer(2, 70, 50); if (DEBUG_MODE) Serial.println("[RFID Action] Relay " + String(mappedRelayPin) + " AKTIF (Membuka)."); // Menunggu beberapa detik sambil menampilkan jarak & status loker unsigned long showStart = millis(); float jarak = 0.0; String status = ""; // Baca sensor ultrasonik hanya sekali int trigPin = trigPins[mappedLokerId - 1]; int echoPin = echoPins[mappedLokerId - 1]; // Tunggu 10 detik dan update status loker while (millis() - showStart < 10000) { // Menghitung jarak dengan sensor ultrasonik digitalWrite(trigPin, LOW); delayMicroseconds(2); digitalWrite(trigPin, HIGH); delayMicroseconds(10); digitalWrite(trigPin, LOW); long durasi = pulseIn(echoPin, HIGH); jarak = durasi * 0.034 / 2; // Menghitung jarak dalam cm status = (jarak < 10) ? "digunakan" : "kosong"; // Status jika jarak < 10 cm dianggap 'digunakan' // Tampilkan informasi di LCD displayLcdMessage("Loker " + String(mappedLokerId), status + " | " + String(jarak, 0) + "cm"); delay(500); // Mengurangi delay untuk responsifitas yang lebih cepat } // Matikan relay setelah 10 detik digitalWrite(mappedRelayPin, HIGH); if (DEBUG_MODE) Serial.println("[RFID Action] Relay " + String(mappedRelayPin) + " NONAKTIF (Terkunci)."); // Perbarui status loker ke API updateStatusLoker(mappedLokerId, status); // Kembali ke state idle setelah selesai currentState = STATE_IDLE; } void handleNewRFIDRegistration(String uidStr) { displayLcdMessage("Mencari Loker", "Kosong..."); int kosongLokerIdAPI = getFirstEmptyRFIDLokerId(); if (kosongLokerIdAPI != -1) { setRFIDtoLoker(kosongLokerIdAPI, uidStr); displayLcdMessage("RFID Diset", "Loker ID: " + String(kosongLokerIdAPI), 2000); playBuzzer(1, 150, 0); } else { displayLcdMessage("Gagal Set RFID", "Loker Penuh", 2000); playBuzzer(3, 100, 50); } } void updateStatusLoker(int lokerId, String status) { if (WiFi.status() == WL_CONNECTED) { HTTPClient http; String url = API_BASE_URL + "lokers/" + String(lokerId) + "/status"; http.begin(url); http.setTimeout(3000); http.addHeader("Content-Type", "application/json"); String jsonPayload = "{\"status\": \"" + status + "\"}"; int httpResponseCode = http.POST(jsonPayload); if (httpResponseCode > 0) { String response = http.getString(); if (DEBUG_MODE) Serial.println("[API] Status update response: " + response); } else { currentState = STATE_ERROR; displayLcdMessage("API Gagal", "Update Status", 2000); playBuzzer(2, 100, 50); } http.end(); } else { if (DEBUG_MODE) Serial.println("[API] WiFi belum terhubung, tidak bisa update status."); currentState = STATE_ERROR; displayLcdMessage("WiFi Disconnect!", "API Update Gagal", 2000); playBuzzer(2, 100, 50); } } int getFirstEmptyRFIDLokerId() { if (WiFi.status() == WL_CONNECTED) { HTTPClient http; String url = API_BASE_URL + "lokers/pertama-rfid-null"; http.begin(url); http.setTimeout(3000); int httpCode = http.GET(); if (httpCode == 200) { String response = http.getString(); StaticJsonDocument<256> doc; DeserializationError error = deserializeJson(doc, response); if (error) return -1; if (doc.containsKey("data") && doc["data"].containsKey("id")) { int parsedId = doc["data"]["id"].as(); if (DEBUG_MODE) Serial.println("[API] ID loker kosong ditemukan: " + String(parsedId)); http.end(); return parsedId; } else { if (DEBUG_MODE) Serial.println("[API] Respons JSON tidak mengandung 'data.id'."); http.end(); return -1; } } else if (httpCode == 404) { http.end(); return -1; } else { currentState = STATE_ERROR; http.end(); } } else { currentState = STATE_ERROR; displayLcdMessage("WiFi Disconnect!", "API Get Gagal", 2000); playBuzzer(2, 100, 50); } return -1; } void setRFIDtoLoker(int lokerId, String rfid) { if (WiFi.status() == WL_CONNECTED) { HTTPClient http; String url = API_BASE_URL + "lokers/" + String(lokerId) + "/setrfid"; http.begin(url); http.setTimeout(3000); http.addHeader("Content-Type", "application/json"); String jsonPayload = "{\"rfid\": \"" + rfid + "\"}"; int httpResponseCode = http.POST(jsonPayload); if (httpResponseCode > 0) { String response = http.getString(); if (DEBUG_MODE) Serial.println("[API] SET RFID Response: " + response); StaticJsonDocument<256> doc; DeserializationError error = deserializeJson(doc, response); if (!error && doc.containsKey("message")) { String message = doc["message"].as(); if (message.indexOf("RFID sudah digunakan") != -1) { if (DEBUG_MODE) Serial.println("[API] Server Report: RFID sudah digunakan."); displayLcdMessage("RFID Sudah", "Terdaftar", 2000); playBuzzer(2, 100, 50); } } } else { if (DEBUG_MODE) Serial.println("[API] Gagal kirim set RFID: " + http.errorToString(httpResponseCode)); currentState = STATE_ERROR; displayLcdMessage("API Gagal", "Set RFID", 2000); playBuzzer(2, 100, 50); } http.end(); } else { if (DEBUG_MODE) Serial.println("[API] WiFi belum terhubung, tidak bisa set RFID."); currentState = STATE_ERROR; displayLcdMessage("WiFi Disconnect!", "API Set RFID Gagal", 2000); playBuzzer(2, 100, 50); } } String getStatusLoker(int lokerId) { if (DEBUG_MODE) Serial.println("[API] getStatusLoker dipanggil untuk Loker " + String(lokerId)); if (WiFi.status() == WL_CONNECTED) { HTTPClient http; String url = API_BASE_URL + "single-loker/" + String(lokerId); http.begin(url); http.setTimeout(3000); int httpCode = http.GET(); if (DEBUG_MODE) Serial.println("[API] HTTP Code (getStatusLoker): " + String(httpCode)); if (httpCode == 200) { String payload = http.getString(); if (DEBUG_MODE) Serial.println("[API] GET LOKER Status Response: " + payload); StaticJsonDocument<256> doc; DeserializationError error = deserializeJson(doc, payload); if (!error && doc.containsKey("data") && doc["data"].containsKey("status")) { String status = doc["data"]["status"].as(); if (DEBUG_MODE) Serial.println("[API] Status dari API: " + status); http.end(); return status; } else { if (DEBUG_MODE) Serial.println("[API] Parsing JSON error atau format tidak sesuai."); if (DEBUG_MODE) Serial.println(error.c_str()); http.end(); } } else if (httpCode == 404) { if (DEBUG_MODE) Serial.println("[API] Loker " + String(lokerId) + " tidak ditemukan."); http.end(); return "NotFound"; } else { if (DEBUG_MODE) Serial.println("[API] Gagal ambil status loker: " + http.errorToString(httpCode)); currentState = STATE_ERROR; http.end(); } } else { if (DEBUG_MODE) Serial.println("[API] WiFi belum terhubung, tidak bisa get status loker."); currentState = STATE_ERROR; } return "Unknown"; } void clearRFID() { mfrc522.PICC_HaltA(); mfrc522.PCD_StopCrypto1(); if (DEBUG_MODE) Serial.println("[RFID] RFID Reader Direset."); } void playBuzzer(int count, int durationMs, int pauseMs) { for (int i = 0; i < count; i++) { digitalWrite(BUZZER_PIN, HIGH); delay(durationMs); digitalWrite(BUZZER_PIN, LOW); if (i < count - 1) delay(pauseMs); } } void displayLcdMessage(String line1, String line2, int duration) { lcd.clear(); lcd.setCursor(0, 0); lcd.print(line1); lcd.setCursor(0, 1); lcd.print(line2); if (duration > 0) delay(duration); }