#include <Arduino.
h> // Include Arduino core functions
HardwareSerial fingerSerial(2); // Use UART2 of ESP32 for fingerprint
sensor
// ---------------- Pin / Baud Config ----------------
#define FP_BAUD 57600 // Baud rate for fingerprint sensor
#define RX_PIN 16 // ESP32 pin connected to sensor TX
#define TX_PIN 17 // ESP32 pin connected to sensor RX
#define EXT_LED_PIN 4 // External LED pin on ESP32
int nextUserID = 1; // Start user IDs from 1 (mapped to module slots
0..199)
//PACKET HANDLING FUNCTIONS
// Send a packet to the fingerprint sensor
void sendPacket(uint8_t *content, uint16_t length) {
uint8_t packet[512]; uint16_t sum=0;
// Header: Start code = 0xEF01
packet[0]=0xEF; packet[1]=0x01;
// Module address = default 0xFFFFFFFF
packet[2]=0xFF; packet[3]=0xFF; packet[4]=0xFF; packet[5]=0xFF;
// Packet identifier = 0x01 (command packet)
packet[6]=0x01;
// Packet length = command length + 2 bytes (checksum)
uint16_t lenField=length+2;
packet[7]=lenField>>8; packet[8]=lenField&0xFF;
// Copy the actual command payload
memcpy(&packet[9],content,length);
// Calculate checksum (sum from packet[6] to end of content)
for(int i=6;i<9+length;i++) sum+=packet[i];
// Append checksum high and low bytes
packet[9+length]=sum>>8; packet[10+length]=sum&0xFF;
// Send packet to fingerprint sensor
[Link](packet,11+length);
}
// Read a packet from the fingerprint sensor with timeout
int readPacket(uint8_t*buf,int maxlen,unsigned long timeout=1000){
unsigned long t0=millis();
while(millis()-t0<timeout){
if(![Link]()){ delay(1); continue; }
// Check for header 0xEF01
if([Link]()!=0xEF) continue;
while(![Link]());
if([Link]()!=0x01) continue;
buf[0]=0xEF; buf[1]=0x01;
int idx=2;
// Read module address (4 bytes)
for(int i=0;i<4;i++){ while(![Link]());
buf[idx++]=[Link]();}
// Packet identifier
while(![Link]()); buf[idx++]=[Link]();
// Length high + low
while(![Link]()); buf[idx++]=[Link]();
while(![Link]()); buf[idx++]=[Link]();
// Read remaining payload bytes
uint16_t lenVal=((uint16_t)buf[7]<<8)|buf[8];
if(idx+lenVal>maxlen) return 0;
for(uint16_t k=0;k<lenVal;k++){ while(![Link]());
buf[idx++]=[Link]();}
return idx; // Total length of received packet
}
return 0; // Timeout
}
// Send a command and wait for ACK response
int sendCmdAndGetAck(uint8_t*cmd,uint16_t cmdLen,uint8_t*ack,int
ackMax,unsigned long timeout=1000){
while([Link]()) [Link](); // Clear buffer
sendPacket(cmd,cmdLen); // Send command
packet
return readPacket(ack,ackMax,timeout); // Wait for ACK
packet
}
// COMMAND FUNCTIONS
// Generate image from finger
void cmd_GenImg(uint8_t*out,int*len){uint8_t
c[1]={0x01};*len=sendCmdAndGetAck(c,1,out,200,800);}
// Convert image to template buffer (1 or 2)
void cmd_Img2Tz(uint8_t b,uint8_t*out,int*len){uint8_t
c[2]={0x02,b};*len=sendCmdAndGetAck(c,2,out,200,800);}
// Create model by combining buffer1 & buffer2
void cmd_RegModel(uint8_t*out,int*len){uint8_t
c[1]={0x05};*len=sendCmdAndGetAck(c,1,out,200,800);}
// Store model into library (moduleID = slot number)
void cmd_Store(uint16_t moduleID,uint8_t*out,int*len){uint8_t
c[4]={0x06,0x01,(uint8_t)(moduleID>>8),(uint8_t)moduleID};*len=sendCmdA
ndGetAck(c,4,out,200,1000);}
// Search library for a matching fingerprint
void cmd_SearchAll(uint8_t*out,int*len){uint8_t
c[6]={0x04,0x01,0,0,0,0xC8};*len=sendCmdAndGetAck(c,6,out,200,1000);}
// Delete template
void cmd_Delete(uint16_t moduleID,uint16_t
num,uint8_t*out,int*len){uint8_t
c[5]={0x0C,(uint8_t)(moduleID>>8),(uint8_t)moduleID,(uint8_t)(num>>8),(
uint8_t)num};*len=sendCmdAndGetAck(c,5,out,200,1200);}
// Get number of enrolled fingerprints
void cmd_GetTemplateNum(uint8_t*out,int*len){uint8_t
c[1]={0x1D};*len=sendCmdAndGetAck(c,1,out,200,800);}
// Read index table (to know which IDs are used)
void cmd_ReadIndexTable(uint8_t page,uint8_t*out,int*len){uint8_t
c[2]={0x1F,page};*len=sendCmdAndGetAck(c,2,out,300,1200);}
// Control Aura LED (mode, speed, color, times)
void cmd_AuraLed(uint8_t ctrl,uint8_t spd,uint8_t color,uint8_t times){
uint8_t c[5]={0x35,ctrl,spd,color,times};uint8_t ack[32];int len;
sendCmdAndGetAck(c,5,ack,sizeof(ack),500);
}
//LED CONTROL MODES
void ledBlueScan(){cmd_AuraLed(0x01,0x80,0x02,0);} // breathing blue
void ledGreen(){cmd_AuraLed(0x03,0,0x04,0);} // solid green
void ledRed(){cmd_AuraLed(0x02,0x80,0x01,1);} // red flash
// ENROLLMENT FUNCTION
void enrollAtUserID(int userID){
int moduleID = userID - 1; // Convert userID (1-based) to module
slot (0-based)
uint8_t ack[128]; int ackLen;
[Link]("Enrolling ID %d\n",userID);
// Step 1: Capture first image
[Link]("Step 1: place finger");
while(1){
cmd_GenImg(ack,&ackLen);
if(ackLen>9&&ack[9]==0x00){ // success
cmd_Img2Tz(1,ack,&ackLen);
if(ackLen>9&&ack[9]==0x00) break; // stored in buffer 1
}
delay(200);
}
// Ask to remove finger
[Link]("Remove finger...");
delay(1000);
// Step 2: Capture again
[Link]("Step 2: place same finger again");
while(1){
cmd_GenImg(ack,&ackLen);
if(ackLen>9&&ack[9]==0x00){
cmd_Img2Tz(2,ack,&ackLen);
if(ackLen>9&&ack[9]==0x00) break; // stored in buffer 2
}
delay(200);
}
// Combine both images → model
cmd_RegModel(ack,&ackLen);
if(!(ackLen>9&&ack[9]==0x00)){[Link](" ❌ RegModel failed");
return;}
// Store model in flash library
cmd_Store(moduleID,ack,&ackLen);
if(ackLen>9&&ack[9]==0x00){
[Link](" ✅ Stored fingerprint ID %d\n",userID);
ledGreen(); delay(600); ledBlueScan();
}
else {
[Link](" ❌ Store failed");
ledRed(); delay(600); ledBlueScan();
}
}
// IDENTIFICATION FUNCTION
void identifyOnce(){
uint8_t ack[128]; int ackLen;
// Capture finger image
cmd_GenImg(ack,&ackLen);
if(!(ackLen>9) || ack[9]!=0x00) return; // no finger present
// Convert to template buffer
cmd_Img2Tz(1,ack,&ackLen);
if(!(ackLen>9&&ack[9]==0x00)) return;
// Search library for match
cmd_SearchAll(ack,&ackLen);
if(ackLen>13&&ack[9]==0x00){
// Extract matched ID and score
uint16_t moduleID=((uint16_t)ack[10]<<8)|ack[11];
uint16_t score=((uint16_t)ack[12]<<8)|ack[13];
int userID = moduleID + 1;
[Link](" ✅ Match: user ID=%d, score=%d\n",userID,score);
// Blink external LED 3 times
for(int i=0;i<3;i++){
digitalWrite(EXT_LED_PIN,HIGH);
delay(300);
digitalWrite(EXT_LED_PIN,LOW);
delay(300);
}
// Module LED: green success
ledGreen(); delay(400); ledBlueScan();
} else {
[Link](" ❌ No match");
ledRed(); delay(400); ledBlueScan();
}
}
// LIST + DELETE FUNCTIONS
void listStored(){
uint8_t ack[200]; int len;
// Get number of enrolled fingerprints
cmd_GetTemplateNum(ack,&len);
if(len>11&&ack[9]==0x00){
int count=((int)ack[10]<<8)|ack[11];
[Link]("Total enrolled = %d\n",count);
}
// Read index table (page 0 = IDs 0..255)
cmd_ReadIndexTable(0,ack,&len);
[Link]("Enrolled IDs: ");
if(len>41&&ack[9]==0x00){
for(int i=0;i<32;i++){
uint8_t b=ack[10+i];
for(int j=0;j<8;j++){
int moduleID=i*8+j;
if(moduleID<200 && (b&(1<<j))){
[Link]("%d ", moduleID+1); // Convert back to userID
}
}
}
}
[Link]();
}
void deleteWithTimeout(){
[Link]("Enter ID to delete (1..200) within 5s:");
while([Link]()) [Link](); // Clear input buffer
unsigned long t0=millis();
String s="";
// Wait for user input
while(millis()-t0<5000){
if([Link]()){
char c=[Link]();
if(isDigit(c)) s+=c;
else if(c=='\n'||c=='\r') break;
}
}
if([Link]()==0){[Link]("Timeout"); return;}
int userID=[Link]();
if(userID<1||userID>200){[Link]("Invalid ID"); return;}
int moduleID=userID-1;
uint8_t ack[128]; int len;
// Delete given ID
cmd_Delete(moduleID,1,ack,&len);
if(len>9&&ack[9]==0x00) [Link]("Deleted ID %d\n",userID);
else [Link]("Delete failed");
}
//ARDUINO SETUP + LOOP
void setup(){
pinMode(EXT_LED_PIN,OUTPUT);
digitalWrite(EXT_LED_PIN,LOW); // External LED OFF at start
[Link](115200); // Debug over USB
[Link](FP_BAUD,SERIAL_8N1,RX_PIN,TX_PIN); // UART2 for
sensor
delay(200);
[Link]("=== R503-M22 Direct Match Mode ===");
ledBlueScan(); // Module LED: scanning mode (blue breathing)
}
void loop(){
// Handle serial commands
if([Link]()){
char c=[Link]();
if(c=='E'){ // Enroll new finger
enrollAtUserID(nextUserID);
nextUserID++;
if(nextUserID>200) nextUserID=1;
}
else if(c=='S'){ listStored(); } // Show stored fingerprints
else if(c=='D'){ deleteWithTimeout(); } // Delete fingerprint
}
// Always try to identify a finger if placed
identifyOnce();
delay(150);
}