1) #include 2) #include 3) #include 4) #include 5) #include 6) #include 7) #include 8) 9) #define DEBUG_MODE true 10) 11) const char* ssid = "KOPI"; 12) const char* password = "digoreng123"; 13) const String API_BASE_URL = "https://smartlocker.punyapadias.my.id/api/"; 14) 15) #define SS_PIN 21 16) #define RST_PIN 5 17) MFRC522 mfrc522(SS_PIN, RST_PIN); 18) 19) LiquidCrystal_I2C lcd(0x27, 16, 2); 20) 21) const int trigPins[3] = {17, 16, 33}; 22) const int echoPins[3] = {35, 34, 32}; 23) 24) #define RELAY1_PIN 14 25) #define RELAY2_PIN 27 26) #define RELAY3_PIN 26 27) 28) #define BUZZER_PIN 13 29) 30) enum SystemState { 31) STATE_IDLE, 32) STATE_RFID_DETECTED, 33) STATE_OPENING_LOCKER, 34) STATE_REGISTERING_RFID, 35) STATE_ERROR 36) }; 37) SystemState currentState = STATE_IDLE; 38) 39) unsigned long lastApiCallTime = 0; 40) const long apiCallInterval = 5000; 41) unsigned long rfidActionStartTime = 0; 42) const long rfidActionDuration = 10000; 43) bool lcdIdleShown = false; 44) 45) 46) struct RFIDMapping { 47) String uid; 48) int lokerId; 49) int relayPin; 50) }; 51) 52) RFIDMapping rfidMappings[] = { 53) {"36CB3503", 1, RELAY1_PIN}, 54) {"5C312903", 2, RELAY2_PIN}, 55) {"611B2803", 3, RELAY3_PIN}, 56) }; 57) const int jumlahRFID = sizeof(rfidMappings) / sizeof(rfidMappings[0]); 58) 59) void setRFIDtoLoker(int lokerId, String rfid); 60) void updateStatusLoker(int lokerId, String status); 61) int getFirstEmptyRFIDLokerId(); 62) String getStatusLoker(int lokerId); 63) void clearRFID(); 64) void playBuzzer(int count, int durationMs, int pauseMs); 65) void displayLcdMessage(String line1, String line2, int duration = 0); 66) void handleRFIDAction(String uidStr, int mappedLokerId, int mappedRelayPin); 67) void handleNewRFIDRegistration(String uidStr); 68) 69) void displayDefaultScreen() { 70) lcd.clear(); 71) lcd.setCursor(0, 0); 72) lcd.print("Loker Pintar"); 73) lcd.setCursor(0, 1); 74) lcd.print("Scan Kartu"); 75) } 76) 77) void setup() { 78) Serial.begin(115200); 79) Wire.begin(4, 22); 80) lcd.init(); 81) lcd.backlight(); 82) lcd.setCursor(0, 0); 83) lcd.print("Inisialisasi..."); 84) 85) // Menyambung ke WiFi 86) WiFi.begin(ssid, password); 87) lcd.setCursor(0, 1); 88) lcd.print("WiFi connecting..."); 89) while (WiFi.status() != WL_CONNECTED) { 90) delay(500); 91) Serial.print("."); 92) } 93) 94) lcd.setCursor(0, 1); 95) lcd.print("WiFi connected "); 96) Serial.println("\nWiFi Connected. IP: " + WiFi.localIP().toString()); 97) 98) // Inisialisasi RFID 99) SPI.begin(); 100) mfrc522.PCD_Init(); 101) 102) // Inisialisasi sensor ultrasonik 103) for (int i = 0; i < 3; i++) { 104) pinMode(trigPins[i], OUTPUT); 105) pinMode(echoPins[i], INPUT); 106) } 107) 108) // Inisialisasi relay dan buzzer 109) pinMode(RELAY1_PIN, OUTPUT); digitalWrite(RELAY1_PIN, HIGH); 110) pinMode(RELAY2_PIN, OUTPUT); digitalWrite(RELAY2_PIN, HIGH); 111) pinMode(RELAY3_PIN, OUTPUT); digitalWrite(RELAY3_PIN, HIGH); 112) 113) pinMode(BUZZER_PIN, OUTPUT); 114) digitalWrite(BUZZER_PIN, LOW); 115) 116) // Tampilkan layar utama 117) lcd.clear(); 118) displayDefaultScreen(); 119) 120) lastApiCallTime = millis(); 121) } 122) 123) void loop() { 124) // Debug: tampilkan state saat ini setiap 1 detik 125) static unsigned long lastDebugTime = 0; 126) if (millis() - lastDebugTime > 1000) { 127) Serial.println("[DEBUG] Current state: " + String(currentState)); 128) lastDebugTime = millis(); 129) } 130) 131) // Recovery: jika RFID tidak terbaca dalam waktu lama, inisialisasi ulang 132) static unsigned long lastRFIDScanTime = 0; 133) if (millis() - lastRFIDScanTime > 10000) { 134) Serial.println("[RFID] Timeout, re-initialize RFID."); 135) mfrc522.PCD_Init(); 136) lastRFIDScanTime = millis(); 137) } 138) 139) switch (currentState) { 140) case STATE_IDLE: 141) // Tampilkan LCD hanya sekali saat idle 142) if (!lcdIdleShown) { 143) displayDefaultScreen(); 144) lcdIdleShown = true; 145) } 146) 147) // Cek kartu RFID 148) if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) { 149) lcdIdleShown = false; // agar layar bisa refresh nanti 150) playBuzzer(1, 50, 0); 151) lastRFIDScanTime = millis(); // reset timeout counter 152) 153) // Ambil UID 154) String uidStr = ""; 155) for (byte i = 0; i < mfrc522.uid.size; i++) { 156) if (mfrc522.uid.uidByte[i] < 0x10) uidStr += "0"; 157) uidStr += String(mfrc522.uid.uidByte[i], HEX); 158) } 159) uidStr.toUpperCase(); 160) 161) Serial.println("[RFID] UID: " + uidStr); 162) displayLcdMessage("RFID Terbaca:", uidStr); 163) 164) // Cek UID pada daftar 165) bool found = false; 166) int lokerId = -1; 167) int relayPin = -1; 168) for (int i = 0; i < jumlahRFID; i++) { 169) if (uidStr == rfidMappings[i].uid) { 170) found = true; 171) lokerId = rfidMappings[i].lokerId; 172) relayPin = rfidMappings[i].relayPin; 173) break; 174) } 175) } 176) 177) if (found) { 178) currentState = STATE_OPENING_LOCKER; 179) handleRFIDAction(uidStr, lokerId, relayPin); 180) } else { 181) // Jika tidak ditemukan, aktifkan buzzer "tit-tit-tit" dan tampilkan pesan 182) playBuzzer(3, 100, 100); // Bunyi 3 kali dengan interval 100ms 183) displayLcdMessage("Kartu Tidak", "Terdaftar!", 2000); 184) currentState = STATE_REGISTERING_RFID; 185) handleNewRFIDRegistration(uidStr); 186) } 187) 188) clearRFID(); 189) delay(200); // beri waktu recovery RFID 190) } 191) break; 192) 193) case STATE_REGISTERING_RFID: 194) case STATE_OPENING_LOCKER: 195) lcdIdleShown = false; 196) currentState = STATE_IDLE; 197) break; 198) 199) case STATE_ERROR: 200) if (WiFi.status() != WL_CONNECTED) { 201) displayLcdMessage("WiFi Disconnect!", "Reconnecting..."); 202) WiFi.reconnect(); 203) delay(1000); 204) } else { 205) lcdIdleShown = false; 206) currentState = STATE_IDLE; 207) } 208) break; 209) } 210) } 211) 212) 213) 214) 215) 216) void handleRFIDAction(String uidStr, int mappedLokerId, int mappedRelayPin) { 217) if (DEBUG_MODE) Serial.println("[RFID Action] Membuka Loker ID: " + String(mappedLokerId) + " dengan UID: " + uidStr); 218) 219) // Aktifkan relay untuk membuka loker 220) digitalWrite(mappedRelayPin, LOW); 221) playBuzzer(2, 70, 50); 222) if (DEBUG_MODE) Serial.println("[RFID Action] Relay " + String(mappedRelayPin) + " AKTIF (Membuka)."); 223) 224) // Menunggu beberapa detik sambil menampilkan jarak & status loker 225) unsigned long showStart = millis(); 226) float jarak = 0.0; 227) String status = ""; 228) 229) // Baca sensor ultrasonik hanya sekali 230) int trigPin = trigPins[mappedLokerId - 1]; 231) int echoPin = echoPins[mappedLokerId - 1]; 232) 233) // Tunggu 10 detik dan update status loker 234) while (millis() - showStart < 10000) { 235) // Menghitung jarak dengan sensor ultrasonik 236) digitalWrite(trigPin, LOW); 237) delayMicroseconds(2); 238) digitalWrite(trigPin, HIGH); 239) delayMicroseconds(10); 240) digitalWrite(trigPin, LOW); 241) 242) long durasi = pulseIn(echoPin, HIGH); 243) jarak = durasi * 0.034 / 2; // Menghitung jarak dalam cm 244) status = (jarak < 10) ? "digunakan" : "kosong"; // Status jika jarak < 10 cm dianggap 'digunakan' 245) 246) // Tampilkan informasi di LCD 247) displayLcdMessage("Loker " + String(mappedLokerId), 248) status + " | " + String(jarak, 0) + "cm"); 249) delay(500); // Mengurangi delay untuk responsifitas yang lebih cepat 250) } 251) 252) // Matikan relay setelah 10 detik 253) digitalWrite(mappedRelayPin, HIGH); 254) if (DEBUG_MODE) Serial.println("[RFID Action] Relay " + String(mappedRelayPin) + " NONAKTIF (Terkunci)."); 255) 256) // Perbarui status loker ke API 257) updateStatusLoker(mappedLokerId, status); 258) 259) // Kembali ke state idle setelah selesai 260) currentState = STATE_IDLE; 261) } 262) 263) 264) 265) void handleNewRFIDRegistration(String uidStr) { 266) displayLcdMessage("Mencari Loker", "Kosong..."); 267) int kosongLokerIdAPI = getFirstEmptyRFIDLokerId(); 268) 269) if (kosongLokerIdAPI != -1) { 270) setRFIDtoLoker(kosongLokerIdAPI, uidStr); 271) displayLcdMessage("RFID Diset", "Loker ID: " + String(kosongLokerIdAPI), 2000); 272) playBuzzer(1, 150, 0); 273) } else { 274) displayLcdMessage("Gagal Set RFID", "Loker Penuh", 2000); 275) playBuzzer(3, 100, 50); 276) } 277) } 278) 279) void updateStatusLoker(int lokerId, String status) { 280) if (WiFi.status() == WL_CONNECTED) { 281) HTTPClient http; 282) String url = API_BASE_URL + "lokers/" + String(lokerId) + "/status"; 283) 284) http.begin(url); 285) http.setTimeout(3000); 286) http.addHeader("Content-Type", "application/json"); 287) 288) String jsonPayload = "{\"status\": \"" + status + "\"}"; 289) int httpResponseCode = http.POST(jsonPayload); 290) 291) if (httpResponseCode > 0) { 292) String response = http.getString(); 293) if (DEBUG_MODE) Serial.println("[API] Status update response: " + response); 294) } else { 295) currentState = STATE_ERROR; 296) displayLcdMessage("API Gagal", "Update Status", 2000); 297) playBuzzer(2, 100, 50); 298) } 299) http.end(); 300) } else { 301) if (DEBUG_MODE) Serial.println("[API] WiFi belum terhubung, tidak bisa update status."); 302) currentState = STATE_ERROR; 303) displayLcdMessage("WiFi Disconnect!", "API Update Gagal", 2000); 304) playBuzzer(2, 100, 50); 305) } 306) } 307) 308) int getFirstEmptyRFIDLokerId() { 309) if (WiFi.status() == WL_CONNECTED) { 310) HTTPClient http; 311) String url = API_BASE_URL + "lokers/pertama-rfid-null"; 312) 313) http.begin(url); 314) http.setTimeout(3000); 315) int httpCode = http.GET(); 316) 317) if (httpCode == 200) { 318) String response = http.getString(); 319) StaticJsonDocument<256> doc; 320) DeserializationError error = deserializeJson(doc, response); 321) 322) if (error) return -1; 323) 324) if (doc.containsKey("data") && doc["data"].containsKey("id")) { 325) int parsedId = doc["data"]["id"].as(); 326) if (DEBUG_MODE) Serial.println("[API] ID loker kosong ditemukan: " + String(parsedId)); 327) http.end(); 328) return parsedId; 329) } else { 330) if (DEBUG_MODE) Serial.println("[API] Respons JSON tidak mengandung 'data.id'."); 331) http.end(); 332) return -1; 333) } 334) } else if (httpCode == 404) { 335) http.end(); 336) return -1; 337) } else { 338) currentState = STATE_ERROR; 339) http.end(); 340) } 341) } else { 342) currentState = STATE_ERROR; 343) displayLcdMessage("WiFi Disconnect!", "API Get Gagal", 2000); 344) playBuzzer(2, 100, 50); 345) } 346) return -1; 347) } 348) 349) void setRFIDtoLoker(int lokerId, String rfid) { 350) if (WiFi.status() == WL_CONNECTED) { 351) HTTPClient http; 352) String url = API_BASE_URL + "lokers/" + String(lokerId) + "/setrfid"; 353) 354) http.begin(url); 355) http.setTimeout(3000); 356) http.addHeader("Content-Type", "application/json"); 357) 358) String jsonPayload = "{\"rfid\": \"" + rfid + "\"}"; 359) int httpResponseCode = http.POST(jsonPayload); 360) 361) if (httpResponseCode > 0) { 362) String response = http.getString(); 363) if (DEBUG_MODE) Serial.println("[API] SET RFID Response: " + response); 364) StaticJsonDocument<256> doc; 365) DeserializationError error = deserializeJson(doc, response); 366) 367) if (!error && doc.containsKey("message")) { 368) String message = doc["message"].as(); 369) if (message.indexOf("RFID sudah digunakan") != -1) { 370) if (DEBUG_MODE) Serial.println("[API] Server Report: RFID sudah digunakan."); 371) displayLcdMessage("RFID Sudah", "Terdaftar", 2000); 372) playBuzzer(2, 100, 50); 373) } 374) } 375) } else { 376) if (DEBUG_MODE) Serial.println("[API] Gagal kirim set RFID: " + http.errorToString(httpResponseCode)); 377) currentState = STATE_ERROR; 378) displayLcdMessage("API Gagal", "Set RFID", 2000); 379) playBuzzer(2, 100, 50); 380) } 381) http.end(); 382) } else { 383) if (DEBUG_MODE) Serial.println("[API] WiFi belum terhubung, tidak bisa set RFID."); 384) currentState = STATE_ERROR; 385) displayLcdMessage("WiFi Disconnect!", "API Set RFID Gagal", 2000); 386) playBuzzer(2, 100, 50); 387) } 388) } 389) 390) String getStatusLoker(int lokerId) { 391) if (DEBUG_MODE) Serial.println("[API] getStatusLoker dipanggil untuk Loker " + String(lokerId)); 392) if (WiFi.status() == WL_CONNECTED) { 393) HTTPClient http; 394) String url = API_BASE_URL + "single-loker/" + String(lokerId); 395) 396) http.begin(url); 397) http.setTimeout(3000); 398) int httpCode = http.GET(); 399) if (DEBUG_MODE) Serial.println("[API] HTTP Code (getStatusLoker): " + String(httpCode)); 400) 401) if (httpCode == 200) { 402) String payload = http.getString(); 403) if (DEBUG_MODE) Serial.println("[API] GET LOKER Status Response: " + payload); 404) 405) StaticJsonDocument<256> doc; 406) DeserializationError error = deserializeJson(doc, payload); 407) 408) if (!error && doc.containsKey("data") && doc["data"].containsKey("status")) { 409) String status = doc["data"]["status"].as(); 410) if (DEBUG_MODE) Serial.println("[API] Status dari API: " + status); 411) http.end(); 412) return status; 413) } else { 414) if (DEBUG_MODE) Serial.println("[API] Parsing JSON error atau format tidak sesuai."); 415) if (DEBUG_MODE) Serial.println(error.c_str()); 416) http.end(); 417) } 418) } else if (httpCode == 404) { 419) if (DEBUG_MODE) Serial.println("[API] Loker " + String(lokerId) + " tidak ditemukan."); 420) http.end(); 421) return "NotFound"; 422) } else { 423) if (DEBUG_MODE) Serial.println("[API] Gagal ambil status loker: " + http.errorToString(httpCode)); 424) currentState = STATE_ERROR; 425) http.end(); 426) } 427) } else { 428) if (DEBUG_MODE) Serial.println("[API] WiFi belum terhubung, tidak bisa get status loker."); 429) currentState = STATE_ERROR; 430) } 431) return "Unknown"; 432) } 433) 434) void clearRFID() { 435) mfrc522.PICC_HaltA(); 436) mfrc522.PCD_StopCrypto1(); 437) if (DEBUG_MODE) Serial.println("[RFID] RFID Reader Direset."); 438) } 439) 440) void playBuzzer(int count, int durationMs, int pauseMs) { 441) for (int i = 0; i < count; i++) { 442) digitalWrite(BUZZER_PIN, HIGH); 443) delay(durationMs); 444) digitalWrite(BUZZER_PIN, LOW); 445) if (i < count - 1) delay(pauseMs); 446) } 447) } 448) 449) void displayLcdMessage(String line1, String line2, int duration) { 450) lcd.clear(); 451) lcd.setCursor(0, 0); 452) lcd.print(line1); 453) lcd.setCursor(0, 1); 454) lcd.print(line2); 455) if (duration > 0) delay(duration); 456) }