Add SISTEM KEAMANAN PINTU RUMAH DENGAN RFID (RADIO FREQUENCY INDENTIFICATION) BERBASIS INTERNET OF THINGS
This commit is contained in:
commit
25055c5df9
|
@ -0,0 +1,417 @@
|
||||||
|
#include <SPI.h>
|
||||||
|
#include <MFRC522.h>
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include <PubSubClient.h>
|
||||||
|
#include <Wire.h>
|
||||||
|
#include <LiquidCrystal_I2C.h>
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
#include <EEPROM.h>
|
||||||
|
|
||||||
|
// ========== 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue