#include <Adafruit_Fingerprint.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Servo.h>
// LCD setup
LiquidCrystal_I2C lcd(0x27, 20, 3);
// Use SoftwareSerial for boards like ATmega328 (e.g., Arduino Uno)
#if (defined(__AVR__) || defined(ESP8266)) && !defined(__AVR_ATmega2560__)
#include <SoftwareSerial.h>
SoftwareSerial mySerial(2, 3);
#else
#define mySerial Serial1
#endif
Adafruit_Fingerprint finger = Adafruit_Fingerprint(&mySerial);
// Pin definitions
const int redledPin = 4;
const int ledPin = 5;
const int buzzerPin = 6;
const int irSensorPin = 7;
const int servoPin = 9; // Servo motor pin
// Servo setup
Servo servoMotor;
int currentServoPos = 0;
int targetServoPos = 0;
bool isDoorMoving = false;
unsigned long lastServoMoveTime = 0;
const unsigned long servoMoveInterval = 20;
// Fingerprint variables
uint8_t totalMatches = 0;
uint8_t totalFailures = 0;
bool isMatching = false;
bool doorOpen = false;
unsigned long matchStartTime = 0;
const unsigned long matchTimeout = 30000;
unsigned long doorOpenTime = 0;
const unsigned long autoCloseDelay = 30000; //30 second
// IR sensor debounce
int irSensorState = HIGH;
unsigned long lastIrReadTime = 0;
const unsigned long irDebounceDelay = 50;
void setup() {
Serial.begin(9600);
while (!Serial);
pinMode(ledPin, OUTPUT);
pinMode(buzzerPin, OUTPUT);
pinMode(irSensorPin, INPUT);
pinMode(redledPin, OUTPUT);
//lcd.begin();
lcd.init();
lcd.backlight();
lcdshow("Fingerprint Ready");
servoMotor.attach(servoPin);
servoMotor.write(0); // Door starts closed
finger.begin(57600);
if (finger.verifyPassword()) {
lcdshow("Fingerprint sensor found.");
} else {
lcdshow("Fingerprint sensor error!");
// while (1); // Halt if sensor not found
}
displayMenu();
lcdshow("Welcome Face&Finger Based Door Sys.");
buzzer();
}
void displayMenu() {
Serial.println("\n=== Fingerprint Menu ===");
Serial.println("127. Empty Database");
Serial.println("2. Enroll Fingerprint");
Serial.println("3. Start Matching");
Serial.println("Enter choice:");
}
void loop() {
if (Serial.available()) {
int choice = Serial.parseInt(); // Read user input
switch (choice) {
case 127:
emptyDatabase();
buzzer();
break;
case 2:
buzzer();
enrollFingerprint();
break;
case 3:
lcdshow("Fingerprint matching mode activated.");
buzzer();
isMatching = true;
matchStartTime = millis();
break;
default:
Serial.println("Invalid choice.");
break;
}
displayMenu(); // Display menu after each valid choice
}
if (isMatching) handleFingerprintMatching();
// Close door if it has been open too long or IR sensor detects inactivity
if (doorOpen && (millis() - doorOpenTime >= autoCloseDelay || readIRSensor())) {
closeDoor();
}
// Handle non-blocking servo movement
if (isDoorMoving && (millis() - lastServoMoveTime >= servoMoveInterval)) {
moveServo();
lastServoMoveTime = millis();
}
}
void handleFingerprintMatching() {
static unsigned long lastCheck = 0;
unsigned long currentMillis = millis();
// Check for timeout
if (isMatching && (currentMillis - matchStartTime >= matchTimeout)) {
lcdshow("Matching timed out.");
isMatching = false; // Reset matching state
totalFailures++; // Increment failure count
buzzer(); // Alert the user
return; // Exit the function
}
// Check for a valid fingerprint every 500 ms
if (currentMillis - lastCheck >= 500) {
lastCheck = currentMillis;
if (getFingerprintID() != FINGERPRINT_OK) {
lcdshow("Match! Opening door.");
openDoor();
totalMatches++;
updateLCD();
isMatching = false; // Reset matching state
} else {
lcdshow("Awaiting fingerprint... " );
buzzer();
}
}
}
uint8_t getFingerprintID() {
uint8_t p = finger.getImage();
if (p != FINGERPRINT_OK) return p;
p = finger.image2Tz();
if (p != FINGERPRINT_OK) return p;
p = finger.fingerSearch();
if (p == FINGERPRINT_OK) {
digitalWrite(ledPin, HIGH);
digitalWrite(buzzerPin, LOW);
} else {
digitalWrite(ledPin, LOW);
digitalWrite(buzzerPin, HIGH);
delay(500);
digitalWrite(buzzerPin, LOW);
}
return p;
}
void openDoor() {
targetServoPos = 80;
isDoorMoving = true;
doorOpen = true;
doorOpenTime = millis();
}
void buzzer()
{
digitalWrite(buzzerPin, HIGH);
delay(50);
digitalWrite(buzzerPin, LOW);
}
void closeDoor() {
targetServoPos = 0;
isDoorMoving = true;
doorOpen = false;
lcdshow("Door Closed");
}
void moveServo() {
if (currentServoPos < targetServoPos) {
currentServoPos++;
} else if (currentServoPos > targetServoPos) {
currentServoPos--;
}
servoMotor.write(currentServoPos);
if (currentServoPos == targetServoPos) isDoorMoving = false;
}
bool readIRSensor() {
if (millis() - lastIrReadTime > irDebounceDelay) {
lastIrReadTime = millis();
int currentState = digitalRead(irSensorPin);
if (currentState != irSensorState) {
irSensorState = currentState;
return currentState == LOW;
}
}
return false;
}
void emptyDatabase() {
lcdshow("Emptying database...");
finger.emptyDatabase();
lcdshow("Database cleared.");
}
void enrollFingerprint() {
uint8_t id;
lcdshow("Enter ID (1-127)");
id = readNumber();
if (id == 0) {
lcdshow("Invalid ID");
return;
}
while (getFingerprintEnroll(id) != FINGERPRINT_OK) {
lcdshow("Enroll failed, retrying...");
}
lcdshow("Enroll success");
}
uint8_t getFingerprintEnroll(uint8_t id) {
int p = finger.getImage();
if (p != FINGERPRINT_OK) return p;
p = finger.image2Tz(1);
if (p != FINGERPRINT_OK) return p;
lcdshow("Remove finger...");
delay(2000);
while (finger.getImage() != FINGERPRINT_NOFINGER);
lcdshow("Place finger again...");
p = finger.getImage();
if (p != FINGERPRINT_OK) return p;
p = finger.image2Tz(2);
if (p != FINGERPRINT_OK) return p;
p = finger.createModel();
if (p != FINGERPRINT_OK) return p;
p = finger.storeModel(id);
return (p == FINGERPRINT_OK) ? FINGERPRINT_OK : p;
}
void lcdshow(const char* message) {
lcd.clear();
updateLCD();
lcd.setCursor(0, 1);
lcd.print(message);
}
void updateLCD() {
lcd.setCursor(0, 0);
lcd.print("Matches:");
lcd.print(totalMatches);
lcd.setCursor(11, 0);
lcd.print("Faild:");
lcd.print(totalFailures);
}
int readNumber() {
while (Serial.available() == 0); // Wait for input
return Serial.parseInt(); // Read the number once available
}