#include #include #include #include #include #include #include #include // ========== HARDWARE CONFIGURATION ========== // #define SS_PIN 5 // RFID SDA pin #define RST_PIN 27 // RFID Reset pin #define RELAY_PIN 26 // Relay #define LED_ACCESS 15 // Green LED #define LED_DENIED 12 // Red LED #define BUTTON_PIN 14 // Manual button // ========== LCD CONFIGURATION ========== // #define LCD_ADDRESS 0x27 // I2C address (usually 0x27 or 0x3F) #define LCD_COLS 16 // 16x2 LCD #define LCD_ROWS 2 LiquidCrystal_I2C lcd(LCD_ADDRESS, LCD_COLS, LCD_ROWS); // ========== NETWORK CONFIGURATION ========== // const char* ssid = "POCO X7 Pro"; const char* password = "12345678"; const char* mqtt_server = "192.168.214.186"; const int mqtt_port = 1883; // ========== EEPROM CONFIGURATION ========== // #define EEPROM_SIZE 512 #define MAX_CARDS 20 // Maximum stored cards #define UID_LENGTH 20 // RFID UID length // ========== MQTT TOPICS ========== // const char* mqtt_topic_status = "doorlock/status"; const char* mqtt_topic_control = "doorlock/control"; const char* mqtt_topic_access = "doorlock/access"; const char* mqtt_topic_card_manage = "doorlock/card/manage"; const char* mqtt_topic_card_response = "doorlock/card/response"; // ========== GLOBAL VARIABLES ========== // WiFiClient espClient; PubSubClient client(espClient); MFRC522 mfrc522(SS_PIN, RST_PIN); String allowedUIDs[MAX_CARDS]; int allowedUIDsCount = 0; bool doorUnlocked = false; unsigned long unlockStartTime = 0; const unsigned long unlockDuration = 5000; // Door unlock duration (5 sec) unsigned long lastRFIDRead = 0; const unsigned long rfidDebounce = 1000; // RFID debounce time (1 sec) void setup() { Serial.begin(115200); // Initialize EEPROM EEPROM.begin(EEPROM_SIZE); loadRegisteredCards(); // Initialize LCD (simplified) Wire.begin(); lcd.init(); lcd.backlight(); lcd.clear(); lcd.print("Initializing..."); // Initialize RFID SPI.begin(); mfrc522.PCD_Init(); delay(100); // RFID stabilization // Initialize GPIO pinMode(RELAY_PIN, OUTPUT); pinMode(LED_ACCESS, OUTPUT); pinMode(LED_DENIED, OUTPUT); pinMode(BUTTON_PIN, INPUT_PULLUP); digitalWrite(RELAY_PIN, HIGH); // Ensure door is locked // Connect to WiFi & MQTT connectWiFi(); client.setServer(mqtt_server, mqtt_port); client.setCallback(mqttCallback); lcd.clear(); lcd.print("System Ready"); Serial.println("[SYSTEM] System ready"); } void loop() { if (!client.connected()) { reconnectMQTT(); } client.loop(); handleButton(); handleRFID(); handleUnlockTimer(); } // ========== LCD FUNCTIONS ========== // void updateLCD(String line1, String line2) { lcd.clear(); lcd.print(line1.substring(0, LCD_COLS)); lcd.setCursor(0, 1); lcd.print(line2.substring(0, LCD_COLS)); } void resetDisplay() { lcd.clear(); lcd.print("Smart Door Lock"); lcd.setCursor(0, 1); lcd.print("Tap RFID Card"); } // ========== RFID FUNCTIONS ========== // void handleRFID() { if (millis() - lastRFIDRead < rfidDebounce) return; if (mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) { String uid = getRFIDUID(); lastRFIDRead = millis(); Serial.println("[RFID] Card detected: " + uid); updateLCD("UID Detected:", uid.substring(0, 16)); delay(1000); if (checkAccess(uid)) { unlockDoor(); publishAccess("rfid", "granted", uid); } else { showAccessDenied(); publishAccess("rfid", "denied", uid); } mfrc522.PICC_HaltA(); mfrc522.PCD_StopCrypto1(); } } String getRFIDUID() { String content; for (byte i = 0; i < mfrc522.uid.size; i++) { content.concat(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " "); content.concat(String(mfrc522.uid.uidByte[i], HEX)); } content.toUpperCase(); content.trim(); return content; } // ========== ACCESS MANAGEMENT ========== // bool checkAccess(String uid) { String normalizedUID = uid; normalizedUID.replace(" ", ""); for (int i = 0; i < allowedUIDsCount; i++) { String allowedUID = allowedUIDs[i]; allowedUID.replace(" ", ""); if (normalizedUID.equals(allowedUID)) { return true; } } return false; } void unlockDoor() { digitalWrite(RELAY_PIN, LOW); digitalWrite(LED_ACCESS, HIGH); updateLCD("Access Granted", "Door Unlocked"); doorUnlocked = true; unlockStartTime = millis(); client.publish(mqtt_topic_status, "unlocked"); Serial.println("[DOOR] Door unlocked"); } void lockDoor() { digitalWrite(RELAY_PIN, HIGH); digitalWrite(LED_ACCESS, LOW); resetDisplay(); doorUnlocked = false; client.publish(mqtt_topic_status, "locked"); Serial.println("[DOOR] Door locked"); } void handleUnlockTimer() { if (doorUnlocked && millis() - unlockStartTime > unlockDuration) { lockDoor(); } } void showAccessDenied() { updateLCD("Access Denied", "Invalid Card"); digitalWrite(LED_DENIED, HIGH); delay(1500); digitalWrite(LED_DENIED, LOW); resetDisplay(); } // ========== EEPROM FUNCTIONS ========== // void saveRegisteredCards() { EEPROM.write(0, allowedUIDsCount); for(int i = 0; i < allowedUIDsCount; i++) { int addr = 1 + (i * UID_LENGTH); String uid = allowedUIDs[i]; for(int j = 0; j < uid.length(); j++) { EEPROM.write(addr + j, uid[j]); } EEPROM.write(addr + uid.length(), '\0'); } EEPROM.commit(); Serial.println("[EEPROM] Card data saved"); } void loadRegisteredCards() { allowedUIDsCount = EEPROM.read(0); if(allowedUIDsCount > MAX_CARDS) allowedUIDsCount = 0; for(int i = 0; i < allowedUIDsCount; i++) { int addr = 1 + (i * UID_LENGTH); String uid; char c; while((c = EEPROM.read(addr++)) != '\0' && uid.length() < UID_LENGTH) { uid += c; } allowedUIDs[i] = uid; Serial.println("[EEPROM] Loaded card: " + uid); } } // ========== NETWORK FUNCTIONS ========== // void connectWiFi() { Serial.println("[WiFi] Connecting..."); WiFi.begin(ssid, password); updateLCD("Connecting WiFi", ""); int attempts = 0; while (WiFi.status() != WL_CONNECTED && attempts < 20) { delay(500); Serial.print("."); attempts++; } if (WiFi.status() == WL_CONNECTED) { Serial.println("\n[WiFi] Connected"); Serial.println("IP Address: " + WiFi.localIP().toString()); updateLCD("WiFi Connected", WiFi.localIP().toString()); delay(1000); } else { Serial.println("\n[WiFi] Connection failed"); updateLCD("WiFi Failed", "Check Connection"); delay(2000); } } void reconnectMQTT() { while (!client.connected()) { Serial.println("[MQTT] Connecting..."); updateLCD("Connecting MQTT", ""); if (client.connect("ESP32DoorLock")) { Serial.println("[MQTT] Connected"); client.subscribe(mqtt_topic_control); client.subscribe(mqtt_topic_card_manage); client.publish(mqtt_topic_status, "System Ready"); updateLCD("MQTT Connected", "System Ready"); delay(1000); } else { Serial.print("[MQTT] Failed, rc="); Serial.print(client.state()); updateLCD("MQTT Failed", "Retrying..."); delay(5000); } } resetDisplay(); } // ========== MQTT CALLBACK ========== // void mqttCallback(char* topic, byte* payload, unsigned int length) { String message; for (int i = 0; i < length; i++) { message += (char)payload[i]; } Serial.print("[MQTT] Message received: ["); Serial.print(topic); Serial.print("] "); Serial.println(message); if (String(topic) == mqtt_topic_control) { if (message.equalsIgnoreCase("OPEN")) { unlockDoor(); publishAccess("remote", "granted", ""); } else if (message.equalsIgnoreCase("LOCK")) { lockDoor(); } } else if (String(topic) == mqtt_topic_card_manage) { handleCardManagement(message); } } void handleCardManagement(String message) { DynamicJsonDocument doc(256); DeserializationError error = deserializeJson(doc, message); if (error) { Serial.print("[MQTT] JSON parse error: "); Serial.println(error.c_str()); return; } String action = doc["action"]; String uid = doc["uid"]; String normalizedUID = uid; normalizedUID.replace(" ", ""); if (action == "add") { if (allowedUIDsCount >= MAX_CARDS) { String response = "{\"status\":\"error\",\"message\":\"Card capacity full\"}"; client.publish(mqtt_topic_card_response, response.c_str()); updateLCD("Add Failed", "Capacity Full"); delay(2000); resetDisplay(); return; } for (int i = 0; i < allowedUIDsCount; i++) { String existingUID = allowedUIDs[i]; existingUID.replace(" ", ""); if (normalizedUID.equals(existingUID)) { String response = "{\"status\":\"error\",\"message\":\"Card already registered\"}"; client.publish(mqtt_topic_card_response, response.c_str()); updateLCD("Card Already", "Registered"); delay(2000); resetDisplay(); return; } } allowedUIDs[allowedUIDsCount] = uid; allowedUIDsCount++; saveRegisteredCards(); String response = "{\"status\":\"success\",\"message\":\"Card added successfully\"}"; client.publish(mqtt_topic_card_response, response.c_str()); updateLCD("Card Added", uid.substring(0, 16)); delay(2000); resetDisplay(); } else if (action == "remove") { bool removed = false; for (int i = 0; i < allowedUIDsCount; i++) { String existingUID = allowedUIDs[i]; existingUID.replace(" ", ""); if (normalizedUID.equals(existingUID)) { for (int j = i; j < allowedUIDsCount - 1; j++) { allowedUIDs[j] = allowedUIDs[j+1]; } allowedUIDsCount--; removed = true; saveRegisteredCards(); break; } } if (removed) { String response = "{\"status\":\"success\",\"message\":\"Card removed successfully\"}"; client.publish(mqtt_topic_card_response, response.c_str()); updateLCD("Card Removed", uid.substring(0, 16)); } else { String response = "{\"status\":\"error\",\"message\":\"Card not found\"}"; client.publish(mqtt_topic_card_response, response.c_str()); updateLCD("Card Not", "Found"); } delay(2000); resetDisplay(); } } void publishAccess(String method, String status, String uid) { String payload = "{\"method\":\"" + method + "\",\"status\":\"" + status + "\",\"uid\":\"" + uid + "\",\"time\":" + millis() + "}"; client.publish(mqtt_topic_access, payload.c_str()); Serial.println("[ACCESS] " + payload); } // ========== MANUAL BUTTON ========== // void handleButton() { static unsigned long lastButtonPress = 0; static bool lastButtonState = HIGH; // Menyimpan state sebelumnya static bool buttonPressed = false; // Flag untuk menandai tombol sudah diproses const unsigned long debounceTime = 500; // Diperpanjang menjadi 500ms bool currentButtonState = digitalRead(BUTTON_PIN); // Deteksi perubahan state (edge detection) if (currentButtonState != lastButtonState) { lastButtonPress = millis(); lastButtonState = currentButtonState; buttonPressed = false; } // Jika tombol ditekan (LOW) dan belum diproses if (currentButtonState == LOW && !buttonPressed && millis() - lastButtonPress > debounceTime) { buttonPressed = true; // Set flag bahwa tombol sudah diproses if (!doorUnlocked) { Serial.println("[BUTTON] Door unlocked manually"); unlockDoor(); publishAccess("button", "granted", ""); updateLCD("Manual Access", "Door Unlocked"); } } }