Fingerprint-Based Biometric Authentication System with Keypad Backup & LCD Display using Arduino

Biometric Access System with Dual Authentication | Arduino + R307 + Keypad | MakeMindz
🔐 Part of the Arduino Expert Projects series — View all on MakeMindz →
Expert Project
🔐 Expert Project · Dual Authentication

Biometric Access System with Dual Authentication using Arduino

🔴 Expert Level 📅 February 2026 ⏱ 20 min read 🛠 Arduino · R307 · ILI9341 · Keypad

Build a powerful dual-authentication security system using Arduino Uno, R307 Fingerprint Sensor, 4×4 keypad, I2C LCD, and ILI9341 TFT display. Combines biometric fingerprint verification with PIN entry backup — ideal for door locks, banking kiosks, locker systems, and secure access control. Full diagram.json, complete Arduino C++ code, and live Wokwi simulation included.

Launch Live Simulation on Wokwi

What This Project Builds

This system simulates a mini banking authentication terminal using Arduino. Unlike simple password locks, it features a graphical TFT fingerprint scan animation, transaction validation logic, account scrolling menu, failure mode simulation, real-time LED indicators, and a touchscreen PIN pattern entry — all in one project.

Two authentication methods work in tandem, increasing security, reliability, and usability:

🟢 Method 1: Fingerprint

  • Place finger on R307 sensor
  • Compare against stored templates
  • Match → Green LED + access granted
  • No match → Red LED + access denied
  • LCD displays result message

🔵 Method 2: PIN Entry

  • Enter 4-digit PIN via 4×4 keypad
  • System validates entered PIN
  • Correct PIN → access granted
  • Wrong PIN → access denied
  • Backup if fingerprint unavailable

Key Features

✔ Dual Authentication ✔ TFT Fingerprint Animation ✔ Multi-User Support ✔ Banking-style Interface ✔ SOS Alert Option ✔ Sales Tracking ✔ Touch Debouncing ✔ Limit Checking (Max ₹2000) ✔ Green/Red LED Feedback ✔ Account Scrolling Menu

Components Required

All components are simulated in Wokwi — no physical hardware needed to try this project.

🤖
Arduino Uno
Main controller
👆
R307 Fingerprint Sensor
Biometric authentication
⌨️
4×4 Membrane Keypad
PIN entry input
🖥
16×2 I2C LCD
User interface messages
📱
ILI9341 TFT Display
Fingerprint animation
👌
FT6206 Touch Controller
Capacitive touch input
🟢
Green LED
Access granted indicator
🔴
Red LED
Access denied indicator
Resistors (220Ω)
LED current limiting

Libraries Required

LibraryPurposeInstall via
Wire.hI2C communicationBuilt-in Arduino
LiquidCrystal_I2C.hI2C LCD controlLibrary Manager
Keypad.hMatrix keypad inputLibrary Manager
Adafruit_GFX.hGraphics primitivesLibrary Manager
Adafruit_ILI9341.hTFT display driverLibrary Manager
Adafruit_FT6206.hCapacitive touchLibrary Manager

Wiring Guide

Fingerprint Sensor (R307) → Arduino

R307 PinArduino PinWire Color
VCC5VRed
GNDGNDBlack
TXRX (via SoftwareSerial)Yellow
RXTX (via SoftwareSerial)Green

I2C LCD (16×2) → Arduino

LCD PinArduino PinWire Color
VCC5V (via j5 junction)Red
GNDGND (via j3 junction)Black
SDAA4 (via j6 junction)Green
SCLA5 (via j4 junction)Yellow

ILI9341 TFT Display → Arduino

TFT PinArduino PinWire ColorProtocol
SCK (SCLK)13BlueSPI
MISO12BlueSPI
MOSI11BlueSPI
CS10BlueChip Select
D/CA2BlueData/Command
RSTA3BlueReset
SDA (Touch)A4 (shared I2C)GreenI2C
SCL (Touch)A5 (shared I2C)YellowI2C
VCC5VRedPower
GNDGNDBlackGround

4×4 Keypad → Arduino

Keypad PinArduino PinWire Color
R1 (Row 1)9Green
R2 (Row 2)8Green
R3 (Row 3)7Green
R4 (Row 4)6Green
C1 (Col 1)5Green
C2 (Col 2)4Green
C3 (Col 3)3Green
C4 (Col 4)2Green

LED Indicators → Arduino

LEDAnode (+)Cathode (−)
🟢 Green LEDA0 (via 220Ω resistor)GND
🔴 Red LEDA1 (via 220Ω resistor)GND
⚠️

Shared I2C bus: The I2C LCD and FT6206 touch controller share SDA (A4) and SCL (A5). Ensure both have different I2C addresses. The LCD uses 0x27 and the FT6206 uses its own default address.


How It Works — State Machine

The system uses state machine programming for clean transitions between phases. There are four distinct states, each handling specific user interactions:

1️⃣
Merchant Entry
Amount input via keypad
2️⃣
Fingerprint Phase
Touch TFT to scan
3️⃣
Account Selection
Scroll & choose bank
4️⃣
PIN Tap Phase
4-tap pattern on TFT

Step-by-Step System Flow

  1. 1

    System Startup — Amount Entry

    LCD displays "Enter Amount: Rs.". The user types the transaction amount using the keypad (digits 0–9). Press 'A' to clear, '#' to confirm. If amount exceeds ₹2000, the system rejects it with a limit exceeded message and Red LED.

  2. 2

    Fingerprint Verification Phase

    LCD shows "Place Finger on Scanner..." and the TFT displays a cyan ellipse outline. The user touches the TFT screen to simulate placing a finger. The system runs a concentric oval scan animation.

    If failMode = 2 (hold 'A' key), it returns red animation + "Unknown Finger" + Red LED. Otherwise, green animation + "WELCOME: [Username]" + transitions to account selection.

  3. 3

    Account Selection Menu

    The LCD shows the user's linked bank accounts (e.g., "SBI 4894", "PNB 7930"). Press 'D' to scroll down, 'C' to scroll up through 5 accounts. Press a number key (1–5) to select an account. Two users with different bank sets are supported.

  4. 4

    PIN Tap Pattern Entry

    LCD shows "Tap Pattern Now". User taps the TFT screen 4 times — each tap shows a '*' on the LCD. After 4 taps, the system validates. If failMode = 1 (hold 'B' key), it fails. Otherwise it shows "PAYMENT SUCCESS" + Green LED + reference number.

    SOS Mode: Press '*' anytime to activate SOS. On successful payment, the LCD shows "SOS ALERT SENT!" instead of the reference number.


diagram.json — Complete Circuit

Paste this into the diagram.json tab in your Wokwi project to instantly load the full circuit — Arduino Uno, TFT display, I2C LCD, 4×4 keypad, two LEDs, and all junctions pre-wired.

JSON — diagram.json
{
  "version": 1,
  "author": "Tapish Chahera",
  "editor": "wokwi",
  "parts": [
    {
      "type": "wokwi-arduino-uno", "id": "uno",
      "top": 152.6, "left": -33.4, "rotate": 180, "attrs": {}
    },
    { "type": "wokwi-membrane-keypad", "id": "keypad",
      "top": 55.6, "left": 245.6, "attrs": {} },
    {
      "type": "wokwi-led", "id": "led-green",
      "top": 44.4, "left": 195.8,
      "attrs": { "color": "green", "flip": "" }
    },
    {
      "type": "wokwi-led", "id": "led-red",
      "top": 44.4, "left": 138.2,
      "attrs": { "color": "red", "flip": "" }
    },
    {
      "type": "wokwi-lcd1602", "id": "lcd1",
      "top": -89.6, "left": -23.2,
      "attrs": { "pins": "i2c" }
    },
    {
      "type": "board-ili9341-cap-touch", "id": "lcd2",
      "top": -47.24, "left": -288.38, "attrs": {}
    },
    { "type": "wokwi-junction", "id": "j1", "top": 91.2,  "left": 62.4,   "attrs": {} },
    { "type": "wokwi-junction", "id": "j2", "top": 91.2,  "left": 148.8,  "attrs": {} },
    { "type": "wokwi-junction", "id": "j3", "top": 91.2,  "left": -52.8,  "attrs": {} },
    { "type": "wokwi-junction", "id": "j4", "top": 158.4, "left": -100.8, "attrs": {} },
    { "type": "wokwi-junction", "id": "j5", "top": 72,    "left": -43.2,  "attrs": {} },
    { "type": "wokwi-junction", "id": "j6", "top": 139.2, "left": -110.4, "attrs": {} }
  ],
  "connections": [
    // ── LED ground connections ──────────────────
    [ "led-green:C", "uno:GND",   "black", [ "v0" ] ],
    [ "led-red:C",   "uno:GND",   "black", [ "v0" ] ],
    // ── Keypad → Arduino digital pins ───────────
    [ "uno:9", "keypad:R1", "green", [ "v57.6",  "h325.6"  ] ],
    [ "uno:8", "keypad:R2", "green", [ "v67.2",  "h345.6"  ] ],
    [ "uno:7", "keypad:R3", "green", [ "v76.8",  "h371.1"  ] ],
    [ "uno:6", "keypad:R4", "green", [ "v86.4",  "h390.1"  ] ],
    [ "uno:5", "keypad:C1", "green", [ "v96",    "h409.1"  ] ],
    [ "uno:4", "keypad:C2", "green", [ "v105.6", "h428.1"  ] ],
    [ "uno:3", "keypad:C3", "green", [ "v115.2", "h447.35" ] ],
    [ "uno:2", "keypad:C4", "green", [ "v124.8", "h466.6"  ] ],
    // ── TFT SPI connections ─────────────────────
    [ "lcd2:SCK",  "uno:13", "blue", [ "v134.4", "h307.6" ] ],
    [ "lcd2:MISO", "uno:12", "blue", [ "v124.8", "h278.9" ] ],
    [ "lcd2:MOSI", "uno:11", "blue", [ "v144",   "h298.2" ] ],
    [ "lcd2:CS",   "uno:10", "blue", [ "v153.6", "h317.5" ] ],
    // ── GND junction network ────────────────────
    [ "uno:GND.2", "j1:J",      "black", [ "h-3.9",   "v-38.3"   ] ],
    [ "j1:J",      "j2:J",      "black", [ "v0" ] ],
    [ "j2:J",      "led-green:C", "black", [ "v0", "h67.2" ] ],
    [ "j2:J",      "led-red:C",   "black", [ "v0" ] ],
    [ "j1:J",      "j3:J",      "black", [ "v0" ] ],
    [ "j3:J",      "lcd1:GND", "black", [ "v0" ] ],
    [ "j3:J",      "lcd2:GND", "black", [ "v230.4", "h-192" ] ],
    // ── I2C SCL (A5) ────────────────────────────
    [ "uno:A5",    "j4:J",      "yellow", [ "v0" ] ],
    [ "j4:J",      "lcd1:SCL", "yellow", [ "v0" ] ],
    [ "j4:J",      "lcd2:SCL", "yellow", [ "v105.6", "h-67.2" ] ],
    // ── 5V power junction ───────────────────────
    [ "uno:5V",    "j5:J",      "red", [ "v-86.3", "h-119" ] ],
    [ "j5:J",      "lcd1:VCC", "red", [ "v0" ] ],
    [ "j5:J",      "lcd2:VCC", "red", [ "v259.2", "h-211.2" ] ],
    // ── I2C SDA (A4) ────────────────────────────
    [ "uno:A4",    "j6:J",      "green", [ "v0" ] ],
    [ "j6:J",      "lcd1:SDA", "green", [ "v0" ] ],
    [ "j6:J",      "lcd2:SDA", "green", [ "v115.2", "h-48" ] ],
    // ── LED anode → Arduino analog pins ─────────
    [ "uno:A0",    "led-green:A", "green", [ "v-28.7", "h188.2" ] ],
    [ "uno:A1",    "led-red:A",   "green", [ "v-38.3", "h140.1" ] ],
    // ── TFT control pins ────────────────────────
    [ "uno:A3",    "lcd2:RST",    "blue",
        [ "v-28.7", "h-71.3", "v163.2", "h-153.6" ] ],
    [ "uno:A2",    "lcd2:D/C",    "blue",
        [ "v-38.3", "h-90.4", "v163.2", "h-134.4" ] ]
  ],
  "dependencies": {}
}

How to use: In Wokwi, click the diagram.json tab, select all existing text, and paste. The full wired circuit loads instantly including all junction nodes.


Arduino C++ — Complete Program

Full state-machine implementation with fingerprint simulation via touch, PIN tap pattern, multi-user bank data, sales tracking, and SOS mode.

Arduino C++ — sketch.ino
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <Adafruit_FT6206.h>

#define TFT_CS  10
#define TFT_DC  A2
#define TFT_RST A3
#define LED_GRN A0
#define LED_RED A1

const byte ROWS = 4, COLS = 4;
char keys[ROWS][COLS] = {
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'}
};
byte rowPins[ROWS] = {9, 8, 7, 6};
byte colPins[COLS] = {5, 4, 3, 2};

Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
LiquidCrystal_I2C lcd(0x27, 16, 2);
Adafruit_ILI9341 tft(TFT_CS, TFT_DC, TFT_RST);
Adafruit_FT6206 ctp = Adafruit_FT6206();

enum State { MERCHANT_ENTRY, ID_PHASE, ACCOUNT_SEL, PIN_PHASE };
State currentState = MERCHANT_ENTRY;

int currentUser = 0;
String userNames[] = {"Tapish Chahera", "Simran Chawla"};
String userBanks[2][5] = {
  {"SBI 4894", "PNB 7930", "HDFC 4689", "ICICI 1122", "AXIS 9901"},
  {"KOTAK 2210", "INDUS 5543", "YES 9090", "BOB 3321", "UCO 1199"}
};
const int totalBanks = 5;
int scrollIndex = 0;
String amount = "";
String selectedBank = "";
int pinTaps = 0;
int failMode = 0;
float totalSales = 0;
bool sosActive = false;
unsigned long lastTouch = 0;

void scanFingerEffect(uint16_t color) {
  for (int r = 0; r <= 50; r += 3) {
    int ry = (r * 8) / 5;
    tft.drawEllipse(120, 160, r, ry, color);
    tft.drawEllipse(120, 160, r+1, ry+1, color);
  }
}

void updateBankList() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(String(scrollIndex + 1) + "." + userBanks[currentUser][scrollIndex]);
  if (scrollIndex + 1 < totalBanks) {
    lcd.setCursor(0, 1);
    lcd.print(String(scrollIndex + 2) + "." + userBanks[currentUser][scrollIndex + 1]);
  }
}

void resetSystem() {
  currentState = MERCHANT_ENTRY;
  amount = ""; pinTaps = 0; failMode = 0;
  selectedBank = ""; scrollIndex = 0; sosActive = false;
  digitalWrite(LED_GRN, LOW); digitalWrite(LED_RED, LOW);
  lcd.clear(); lcd.print("Enter Amount:");
  lcd.setCursor(0, 1); lcd.print("Rs. ");
  tft.fillScreen(ILI9341_BLACK);
  tft.drawEllipse(120, 160, 50, 80, 0x5AEB);
  tft.drawEllipse(120, 160, 49, 79, 0x5AEB);
}

void setup() {
  pinMode(LED_GRN, OUTPUT); pinMode(LED_RED, OUTPUT);
  lcd.init(); lcd.backlight();
  tft.begin(); tft.setRotation(0);
  ctp.begin(10); // Low threshold for Wokwi touch sensitivity
  resetSystem();
}

void loop() {
  KeyState kstate = keypad.getState();
  char key = keypad.getKey();

  if (kstate == HOLD) {
    char lastKey = keypad.key[0].kchar;
    if (lastKey == 'A') failMode = 2;
    if (lastKey == 'B') failMode = 1;
    if (lastKey == 'D' && currentState == MERCHANT_ENTRY) {
      lcd.clear(); lcd.print("TOTAL SALES:");
      lcd.setCursor(0,1); lcd.print("Rs. " + String(totalSales, 2));
      delay(3000); resetSystem();
    }
  }

  if (key == '*') sosActive = true;
  if (key == 'B' && currentState == MERCHANT_ENTRY) currentUser = !currentUser;

  // ── State: MERCHANT_ENTRY ──────────────────
  if (currentState == MERCHANT_ENTRY) {
    if (key >= '0' && key <= '9') {
      amount += key;
      lcd.setCursor(4, 1); lcd.print(amount);
    } else if (key == 'A') {
      amount = "";
      lcd.setCursor(4, 1); lcd.print("            ");
    } else if (key == '#') {
      if (amount.toFloat() > 2000) {
        lcd.clear(); lcd.print("LIMIT EXCEEDED!");
        lcd.setCursor(0,1); lcd.print("Max Rs. 2000");
        digitalWrite(LED_RED, HIGH);
        delay(3000); resetSystem();
      } else if (amount != "") {
        currentState = ID_PHASE;
        lcd.clear(); lcd.print("Place Finger on");
        lcd.setCursor(0, 1); lcd.print("Scanner...");
        tft.fillScreen(ILI9341_BLACK);
        tft.drawEllipse(120, 160, 50, 80, ILI9341_CYAN);
        tft.drawEllipse(120, 160, 49, 79, ILI9341_CYAN);
      }
    }
  }

  // ── State: ACCOUNT_SEL ────────────────────
  else if (currentState == ACCOUNT_SEL) {
    if (key == 'D' && scrollIndex < totalBanks - 2) { scrollIndex++; updateBankList(); }
    if (key == 'C' && scrollIndex > 0)              { scrollIndex--; updateBankList(); }
    if (key >= '1' && key <= '9') {
      int choice = (key - '0') - 1;
      if (choice < totalBanks) {
        selectedBank = userBanks[currentUser][choice];
        currentState = PIN_PHASE;
        lcd.clear(); lcd.print("Selected: " + selectedBank.substring(0,6));
        lcd.setCursor(0, 1); lcd.print("Tap Pattern Now");
        tft.fillScreen(ILI9341_BLACK);
        tft.drawEllipse(120, 160, 50, 80, ILI9341_YELLOW);
        tft.drawEllipse(120, 160, 49, 79, ILI9341_YELLOW);
      }
    }
  }

  // ── Touch input with debouncing ───────────
  if (ctp.touched()) {
    TS_Point p = ctp.getPoint();
    if (millis() - lastTouch > 250) {
      lastTouch = millis();

      if (currentState == ID_PHASE) {
        scanFingerEffect(ILI9341_CYAN);
        if (failMode == 2) {
          lcd.clear(); lcd.print("ACCESS DENIED");
          lcd.setCursor(0,1); lcd.print("Unknown Finger");
          digitalWrite(LED_RED, HIGH);
          scanFingerEffect(ILI9341_RED);
          delay(3000); resetSystem();
        } else {
          lcd.clear(); lcd.print("WELCOME:");
          lcd.setCursor(0, 1); lcd.print(userNames[currentUser]);
          scanFingerEffect(ILI9341_GREEN);
          delay(1500);
          currentState = ACCOUNT_SEL;
          updateBankList();
          tft.fillScreen(ILI9341_BLACK);
          tft.drawEllipse(120, 160, 50, 80, ILI9341_WHITE);
        }
      } else if (currentState == PIN_PHASE) {
        pinTaps++;
        lcd.setCursor(0, 1); lcd.print("PIN: ");
        for(int i=0; i<pinTaps; i++) lcd.print("*");
        scanFingerEffect(ILI9341_WHITE);
        tft.fillScreen(ILI9341_BLACK);
        tft.drawEllipse(120, 160, 50, 80, ILI9341_YELLOW);
        if (pinTaps == 4) {
          lcd.clear();
          if (failMode == 1) {
            lcd.print("TXN FAILED");
            lcd.setCursor(0,1); lcd.print("Wrong Pattern");
            digitalWrite(LED_RED, HIGH);
            scanFingerEffect(ILI9341_RED);
          } else {
            lcd.print("PAYMENT SUCCESS");
            totalSales += amount.toFloat();
            digitalWrite(LED_GRN, HIGH);
            scanFingerEffect(ILI9341_GREEN);
            lcd.setCursor(0,1);
            if (sosActive) lcd.print("SOS ALERT SENT!");
            else lcd.print("Ref: " + String(random(1000,9999)));
          }
          delay(4000); resetSystem();
        }
      }
    }
  }
}
📌

Fail mode testing: Hold 'A' on the keypad to simulate fingerprint rejection, or hold 'B' to simulate wrong PIN pattern. These keys must be held during the respective authentication phase.

🚨

SOS Mode: Press the '*' key at any point during the transaction. On success, the LCD will display "SOS ALERT SENT!" instead of the reference number — simulating a distress signal feature.


Applications

🚪Smart Door Lock
🏢Office Access Control
🏧ATM Banking Kiosk
🔒Secure Locker System
📋Attendance Monitoring
🗄️Cabinet Security
💳POS Authentication
💰Biometric Payment
💻Workstation Login

Learning Outcomes

Biometric module interfacing
I2C communication protocol
SPI TFT display handling
State machine programming
Touch input debouncing
Multi-user data handling
Embedded UI design
Secure authentication logic
🎓

Perfect for STEM projects, final-year engineering demos, robotics exhibitions, and IoT security prototypes. This project demonstrates real-world authentication architecture on a microcontroller.


All Arduino Projects on MakeMindz

Comments

try for free