AS608 光學指紋模組教學 :從採集到辨識完整實作

介紹如何使用 AS608 光學指紋模組搭配 Arduino,實作從採集到辨識的完整指紋門禁系統,涵蓋矩陣鍵盤輸入 ID、LCD 顯示結果、蜂鳴器音效回饋,附完整程式碼與接線說明。

AS608 光學指紋模組教學 :從採集到辨識完整實作

你有沒有想過,要怎麼幫自己的 Arduino 專題加上指紋辨識功能?今天傑森要帶大家實作一個完整的指紋辨識系統,從「採集指紋」到「驗證身份」全部搞定,還會接上 LCD 顯示畫面和矩陣鍵盤,做出一個相當實用的門禁雛形。


你會學到什麼

  • AS608 光學指紋模組的接線方式
  • 用鍵盤輸入 ID,採集並儲存指紋
  • 即時比對指紋,顯示通過 / 拒絕訊息
  • 搭配 LCD I2C 顯示器和蜂鳴器音效

材料清單

材料 數量
Arduino UNO(或相容板) 1
AS608 光學指紋模組 1
I2C LCD 16x2(PCF8574 介面) 1
4x3 矩陣鍵盤 1
蜂鳴器(主動或被動皆可) 1
麵包板 + 杜邦線 若干
完整套件購買連結:傑森創工指紋辨識套件
Arduino指紋辨識系統套件,全套DIY零件,含程式及線路圖 AS608模組
本套件包含完成專題的所有材料,包含Arduino Uno和AS608指紋辨識模組。 主要針對採集指紋進行記錄,並進行指紋驗証。 範例程式也分成兩支,方便大家學習研究。 建議有一定Arduino基礎者使用 套件內容: AS608指紋辨識模組,Arduino Uno開發板,1602 LCD顯示器,壓克力平台,170孔麵包板,4x3薄膜鍵盤,無源蜂鳴器,足量杜邦線

函式庫安裝

本教學需要安裝三個函式庫,請在 Arduino IDE 的「程式庫管理員」搜尋並安裝:

  1. Adafruit Fingerprint Sensor Library
  2. LiquidCrystal_PCF8574
  3. Adafruit Keypad

接線方式

AS608 指紋模組

AS608 模組使用 UART 序列通訊。這裡我們用 SoftwareSerial,把 PIN 2 / PIN 3 模擬成 RX / TX。

AS608 腳位 Arduino 腳位
VCC 3.3V 或 5V(依模組規格)
GND GND
TX PIN 2(Arduino RX)
RX PIN 3(Arduino TX)
注意: AS608 的 TX 接到 Arduino 的 RX(PIN 2),RX 接到 Arduino 的 TX(PIN 3),別接反了。

I2C LCD

LCD 使用 I2C 介面,只需要 4 條線:

LCD 腳位 Arduino 腳位
VCC 5V
GND GND
SDA A4
SCL A5
LCD 的 I2C 位址通常是 0x270x3F,可以用 I2C Scanner 草稿碼確認。程式裡預設是 0x3F,如果 LCD 沒有顯示,請改成 0x27 再試。

矩陣鍵盤(採集程式使用)

鍵盤腳位 Arduino 腳位
R1 PIN 11
R2 PIN 10
R3 PIN 9
R4 PIN 8
C1 PIN 7
C2 PIN 6
C3 PIN 5

蜂鳴器

蜂鳴器腳位 Arduino 腳位
正極 PIN 4
負極 GND

範例一:採集指紋(enroll_test)

這支程式的功能是:透過矩陣鍵盤輸入 ID 編號(1~127),然後掃描手指兩次,把指紋特徵值儲存到 AS608 模組的內建快閃記憶體。

完整程式碼

#include <Adafruit_Fingerprint.h>
#include <LiquidCrystal_PCF8574.h>
#include "Adafruit_Keypad.h"

LiquidCrystal_PCF8574 lcd(0x3F); // 設定LCD的I2C位址

// 用 SoftwareSerial 設定 RX、TX
// PIN2 是 RX,接到 AS608 的 TX
// PIN3 是 TX,接到 AS608 的 RX
SoftwareSerial mySerial(2, 3);

Adafruit_Fingerprint finger = Adafruit_Fingerprint(&mySerial);

// 採集成功的旋律
int melody[] = { 262, 233, 415 };
int noteDurations[] = { 8, 4, 2 };

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

Adafruit_Keypad customKeypad = Adafruit_Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);

uint8_t id = 0;
int digits = 0;
int inputOK = 0;
char enterNumber[3];

void setup() {
  Serial.begin(9600);
  delay(100);
  Serial.println("Fingerprint sensor enrollment");

  finger.begin(57600); // AS608 的 Baud Rate

  if (finger.verifyPassword()) {
    Serial.println("Found fingerprint sensor!");
  } else {
    Serial.println("Did not find fingerprint sensor :(");
    while (1) { delay(1); }
  }

  lcd.begin(16, 2);
  lcd.setBacklight(255);
  lcd.clear();
  customKeypad.begin();
}

void loop() {
  lcd.setCursor(0, 0);
  lcd.print("Enter ID Number:");
  lcd.setCursor(digits, 1);
  lcd.cursor();
  lcd.blink();

  customKeypad.tick();

  while (customKeypad.available()) {
    keypadEvent e = customKeypad.read();
    if (e.bit.EVENT == KEY_JUST_RELEASED) {

      if ((char)e.bit.KEY == '#') {
        // # = 確認輸入
        digits = 3;
        if (id > 0 && id < 128) {
          inputOK = 1;
          Serial.println(id);
        } else {
          // ID 超出範圍,顯示錯誤
          lcd.clear();
          lcd.setCursor(0, 0);
          lcd.print("Error Number!");
          lcd.setCursor(0, 1);
          lcd.print("Try Again!");
          delay(2000);
          lcd.clear();
          lcd.setCursor(0, 1);
          lcd.cursor();
          lcd.blink();
          memset(enterNumber, 0, sizeof(enterNumber));
          id = 0;
          digits = 0;
        }

      } else if ((char)e.bit.KEY == '*') {
        // * = 清除,重新輸入
        lcd.clear();
        lcd.setCursor(0, 1);
        lcd.cursor();
        lcd.blink();
        memset(enterNumber, 0, sizeof(enterNumber));
        id = 0;
        digits = 0;

      } else {
        // 數字鍵,最多輸入 3 位
        if (digits < 3) {
          lcd.setCursor(digits, 1);
          lcd.print((char)e.bit.KEY);
          enterNumber[digits] = (char)e.bit.KEY;
          id = atoi(enterNumber);
          digits++;
        }
      }
    }
  }

  delay(10);

  if (inputOK == 1) {
    inputOK = 0;
    lcd.setCursor(0, 0);
    lcd.print("Place You Finger");
    lcd.noCursor();
    lcd.noBlink();
    while (!getFingerprintEnroll());
  }
}

uint8_t getFingerprintEnroll() {
  int p = -1;
  Serial.print("Waiting for valid finger to enroll as #");
  Serial.println(id);

  // 第一次掃描
  while (p != FINGERPRINT_OK) {
    p = finger.getImage();
    switch (p) {
      case FINGERPRINT_OK:        Serial.println("Image taken"); break;
      case FINGERPRINT_NOFINGER:  Serial.println("."); break;
      default: break;
    }
  }

  p = finger.image2Tz(1); // 轉換成特徵值,存到緩衝區 1
  if (p != FINGERPRINT_OK) return p;

  // 提示移開手指
  Serial.println("Remove finger");
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Remove finger");
  delay(2000);

  p = 0;
  while (p != FINGERPRINT_NOFINGER) { p = finger.getImage(); }

  // 第二次掃描(確認比對)
  p = -1;
  Serial.println("Place same finger again");
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Place the same");
  lcd.setCursor(0, 1);
  lcd.print("finger again");

  while (p != FINGERPRINT_OK) {
    p = finger.getImage();
  }

  p = finger.image2Tz(2); // 轉換成特徵值,存到緩衝區 2
  if (p != FINGERPRINT_OK) return p;

  // 比對兩次特徵值,建立模型
  p = finger.createModel();
  if (p == FINGERPRINT_ENROLLMISMATCH) {
    Serial.println("Fingerprints did not match");
    return p;
  } else if (p != FINGERPRINT_OK) return p;

  // 儲存到指定 ID 的位置
  p = finger.storeModel(id);
  if (p == FINGERPRINT_OK) {
    Serial.println("Stored!");
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Finger Stored!");
    digits = 0;
    id = 0;
    playMusic();
    delay(3000);
  }
  return p;
}

void playMusic() {
  for (int thisNote = 0; thisNote < 3; thisNote++) {
    int noteDuration = 1000 / noteDurations[thisNote];
    tone(4, melody[thisNote], noteDuration);
    delay(noteDuration * 1.30);
    noTone(4);
  }
}

操作流程說明 (很重要!)

  1. 接電後,LCD 顯示「Enter ID Number:」
  2. 用鍵盤輸入 1~127 之間的 ID,按 # 確認(* 可清除重輸)
  3. LCD 顯示「Place You Finger」,把手指放上去
  4. LCD 顯示「Remove finger」,移開手指
  5. LCD 顯示「Place the same finger again」,再放一次同一根手指
  6. 兩次比對成功後,LCD 顯示「Finger Stored!」,蜂鳴器響三聲
ID 的有效範圍是 1~127,AS608 模組最多可以存 127 組指紋。

範例二:辨識指紋(fingerprint_test)

這支程式用來驗證手指是否已在資料庫中,並根據比對結果顯示對應人名(ACCESS GRANTED)或拒絕(ACCESS DENIED)。

完整程式碼

#include <Adafruit_Fingerprint.h>
#include <LiquidCrystal_PCF8574.h>

LiquidCrystal_PCF8574 lcd(0x3F);

SoftwareSerial mySerial(2, 3);

Adafruit_Fingerprint finger = Adafruit_Fingerprint(&mySerial);

// 驗證通過的旋律
int melody[] = { 262, 233, 415 };
int noteDurations[] = { 8, 4, 2 };

// 驗證失敗的旋律
int melodyFail[] = { 110, 55 };
int noteDurationsFail[] = { 12, 2 };

void setup() {
  Serial.begin(9600);
  delay(100);
  Serial.println("Fingerprint detect test");

  finger.begin(57600);
  delay(5);

  if (finger.verifyPassword()) {
    Serial.println("Found fingerprint sensor!");
  } else {
    Serial.println("Did not find fingerprint sensor :(");
    while (1) { delay(1); }
  }

  // 顯示目前模組內已有幾組指紋
  finger.getTemplateCount();
  Serial.print("Sensor contains ");
  Serial.print(finger.templateCount);
  Serial.println(" templates");

  Serial.println("Waiting for valid finger...");

  lcd.begin(16, 2);
  lcd.setBacklight(255);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Put your finger");
  lcd.setCursor(0, 1);
  lcd.print("for indentify..");
  lcd.setCursor(15, 1);
  lcd.blink(); // 右下角游標閃動,表示等待中
}

void loop() {
  getFingerprintIDez();
  delay(50);
}

int getFingerprintIDez() {
  uint8_t p = finger.getImage();
  if (p != FINGERPRINT_OK) return -1;

  p = finger.image2Tz();
  if (p != FINGERPRINT_OK) return -1;

  p = finger.fingerFastSearch(); // 快速比對資料庫所有指紋
  if (p != FINGERPRINT_OK) {
    // 比對失敗
    lcd.noBlink();
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("ACCESS DENIED!");
    playMusicFail();
    delay(3000);
    // 回到等待畫面
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Put your finger");
    lcd.setCursor(0, 1);
    lcd.print("for indentify..");
    lcd.setCursor(15, 1);
    lcd.blink();
    return -1;
  }

  // 比對成功,取得 ID 和信心分數
  Serial.print("Found ID #");
  Serial.print(finger.fingerID);
  Serial.print(" with confidence of ");
  Serial.println(finger.confidence);

  // 信心分數 > 100 才算真正通過
  if (finger.confidence > 100) {
    // 設定每個 ID 對應的人名(請依實際情況修改)
    // 注意:ID 沒有 0,所以 index 0 隨意填
    char idname[][16] = {"Nobody", "Peter Chen", "Amy Wang", "Kelly Lee", "Jessica Wu"};

    lcd.noBlink();
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(idname[finger.fingerID]); // 顯示對應人名
    lcd.setCursor(0, 1);
    lcd.print("ACCESS GRANTED!");
    playMusic();
    delay(3000);

    // 回到等待畫面
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Put your finger");
    lcd.setCursor(0, 1);
    lcd.print("for indentify..");
    lcd.setCursor(15, 1);
    lcd.blink();
  }

  return finger.fingerID;
}

void playMusic() {
  for (int thisNote = 0; thisNote < 3; thisNote++) {
    int noteDuration = 1000 / noteDurations[thisNote];
    tone(4, melody[thisNote], noteDuration);
    delay(noteDuration * 1.30);
    noTone(4);
  }
}

void playMusicFail() {
  for (int thisNote = 0; thisNote < 2; thisNote++) {
    int noteDuration = 1000 / noteDurationsFail[thisNote];
    tone(4, melodyFail[thisNote], noteDuration);
    delay(noteDuration * 1.30);
    noTone(4);
  }
}

操作流程說明

  1. 接LCD 顯示「Put your finger for indentify..」,右下角游標閃動
  2. 把手指放到模組上,程式自動開始比對
  3. 比對成功:LCD 第一行顯示對應人名,第二行顯示「ACCESS GRANTED!」,蜂鳴器播放三聲上揚旋律
  4. 比對失敗:LCD 顯示「ACCESS DENIED!」,蜂鳴器播放兩聲下降旋律
  5. 3 秒後自動回到等待畫面

關鍵程式解析

為什麼採集要掃兩次?

p = finger.image2Tz(1); // 第一次,存到緩衝區 1
// ... 等使用者移開手指、再放上去 ...
p = finger.image2Tz(2); // 第二次,存到緩衝區 2
p = finger.createModel(); // 比對兩個緩衝區,建立最終模型

AS608 採集指紋的流程需要掃描兩次,目的是確保特徵值夠穩定。兩次掃描結果會分別存進內部緩衝區 1 和緩衝區 2,再由 createModel() 合併建立最終的指紋模型,最後 storeModel(id) 才把模型存到指定 ID 的位置。

信心分數是什麼?

if (finger.confidence > 100) { ... }

finger.confidence 是比對的相似度分數,範圍 0~255,分數越高表示越吻合。程式設定大於 100 才算通過,這個門檻值可以依需求調整:

  • 分數越高(例如 > 150),安全性更高,但比較容易被誤判為失敗
  • 分數越低(例如 > 50),通過率高,但也比較容易被相似指紋騙過

人名對照表的設定

char idname[][16] = {"Nobody", "Peter Chen", "Amy Wang", "Kelly Lee", "Jessica Wu"};
lcd.print(idname[finger.fingerID]);

這個陣列的 index 對應的就是採集時設定的 ID。因為 AS608 的 ID 從 1 開始,所以 idname[0] 填 "Nobody" 佔位,idname[1] 對應 ID 1 的人,以此類推。


常見問題

Q:LCD 沒有顯示任何文字? A:先確認 I2C 位址。把 LCD 的位址從 0x3F 改成 0x27 試試,或是跑 I2C Scanner 確認實際位址。

Q:序列監控顯示「Did not find fingerprint sensor」? A:檢查 TX / RX 接線有沒有接反,以及 AS608 的電源是否正常(部分模組需要 3.3V,不可接 5V)。

Q:採集時顯示「Fingerprints did not match」? A:兩次掃描放的手指角度不同,導致特徵值差異太大。建議每次都盡量用相同的方式、相同的力道放上去。

Q:辨識時信心分數不到 100? A:先試著重新採集同一根手指,放手指的角度盡量保持一致,採集品質會影響後續辨識的分數。


如果覺得這篇教學有幫助,歡迎分享給同樣在玩 Arduino 的朋友!

傑森創工 Facebook 粉絲專頁

套件購買:傑森創工 — 指紋辨識套件

Arduino指紋辨識系統套件,全套DIY零件,含程式及線路圖 AS608模組
本套件包含完成專題的所有材料,包含Arduino Uno和AS608指紋辨識模組。 主要針對採集指紋進行記錄,並進行指紋驗証。 範例程式也分成兩支,方便大家學習研究。 建議有一定Arduino基礎者使用 套件內容: AS608指紋辨識模組,Arduino Uno開發板,1602 LCD顯示器,壓克力平台,170孔麵包板,4x3薄膜鍵盤,無源蜂鳴器,足量杜邦線