Extracting and restoring fingerprint templates using GT521F52 via direct serial commands for SQL storage and cross-device use library support needed

I am currently working on your GT521F52 Optical Fingerprint sensor,
I have reached out to the forum regarding the coding part, my task is to extract the saved fingerprint data in the form of a template and store it in the sql database and again set the extracted template in a different GT521F52 optical fingerprint sensor .
I am not able to find a suitable library for my above tasks.
Currently I am directly using the commands in the code.
code:
#include <AltSoftSerial.h>
#include <SoftwareSerial.h>
#include <SPI.h>

// Fingerprint Sensor on AltSoftSerial
AltSoftSerial mySerial; // RX = 8, TX = 9

// ESP on SoftwareSerial
SoftwareSerial espSerial(10, 11); // RX = 10, TX = 11

// GT-521F52 Command Constants
const byte CMD_HEADER[2] = {0x55, 0xAA};
const byte CMD_DEVICE_ID[2] = {0x01, 0x00};
const uint8_t CMD_OPEN = 0x01;
const uint8_t CMD_LED_ON = 0x12;
const uint8_t CMD_ENROLL_START = 0x22;
const uint8_t CMD_ENROLL_1 = 0x23;
const uint8_t CMD_ENROLL_2 = 0x24;
const uint8_t CMD_ENROLL_3 = 0x25;
const uint8_t CMD_CAPTURE = 0x60;
const uint8_t CMD_IDENTIFY = 0x51;
const uint8_t CMD_DELETE_ID = 0x40;
const uint8_t CMD_GET_TEMPLATE = 0x70;

void sendCommand(uint8_t cmd, uint16_t pL = 0, uint16_t pH = 0) {
byte pkt[12];
pkt[0] = CMD_HEADER[0]; pkt[1] = CMD_HEADER[1];
pkt[2] = CMD_DEVICE_ID[0]; pkt[3] = CMD_DEVICE_ID[1];
pkt[4] = lowByte(pL); pkt[5] = highByte(pL);
pkt[6] = lowByte(pH); pkt[7] = highByte(pH);
pkt[8] = cmd; pkt[9] = 0x00;
uint16_t csum = 0; for (int i = 0; i < 10; i++) csum += pkt[i];
pkt[10] = lowByte(csum); pkt[11] = highByte(csum);
mySerial.write(pkt, 12);
}

bool readAck(byte *r) {
unsigned long t = millis();
while (mySerial.available() < 12) {
if (millis() - t > 3000) return false;
}
mySerial.readBytes(r, 12);
return (r[8] == 0x30);
}

String getInput(String prompt, unsigned long to = 20000) {
Serial.println(prompt);
unsigned long start = millis();
while (millis() - start < to) {
if (Serial.available()) {
String s = Serial.readStringUntil(‘\n’);
s.trim();
while (Serial.available()) Serial.read(); // flush extra chars
Serial.print(":inbox_tray: You entered: "); Serial.println(s);
delay(100); // give time before next serial use
return s;
}
}
Serial.println(“:alarm_clock: Input timeout.”);
return “”;
}

void verifyFingerprint() {
byte resp[12];

Serial.println(“:backhand_index_pointing_right: Place finger to verify using sensor…”);

// Step 1: Capture fingerprint
while (true) {
// Flush old data to avoid stale bytes
while (mySerial.available()) mySerial.read();

sendCommand(CMD_CAPTURE, 1);  // high quality
if (readAck(resp)) break;
delay(300);  // Give time for user to place finger

}

// Step 2: Identify fingerprint
sendCommand(CMD_IDENTIFY);
if (!readAck(resp)) {
Serial.println(“:cross_mark: IDENTIFY failed.”);
return;
}

// Extract matched ID
uint16_t matchedID = resp[5] << 8 | resp[4];
Serial.print(":white_check_mark: Matched ID: ");
Serial.println(matchedID);

// Step 3: Get template of matched ID
const int SZ = 498;
const int CH = 64;
uint8_t tmpl[SZ];

sendCommand(CMD_GET_TEMPLATE, matchedID);
if (!readAck(resp)) {
Serial.println(“:cross_mark: CMD_GET_TEMPLATE failed”);
return;
}

// Read template data
int received = 0;
unsigned long t0 = millis();
while (received < SZ && millis() - t0 < 5000) {
if (mySerial.available()) {
tmpl[received++] = mySerial.read();
}
}

if (received != SZ) {
Serial.print(“:hourglass_not_done: Only received “);
Serial.print(received);
Serial.println(” bytes before timeout.”);
return;
}

// Step 4: Send to ESP to request employee info
espSerial.print(“TEMPLATE:”);
espSerial.print(matchedID);
espSerial.println(“,FetchInfo,START”);
delay(100);

// Send in chunks
int totalChunks = (SZ + CH - 1) / CH;
char hexBuf[CH * 2 + 2];

for (int i = 0; i < totalChunks; i++) {
int offset = i * CH;
int len = min(CH, SZ - offset);

for (int j = 0; j < len; j++) {
  sprintf(&hexBuf[j * 2], "%02X", tmpl[offset + j]);
}

hexBuf[len * 2] = '\0';
espSerial.print("CHUNK:");
espSerial.println(hexBuf);
espSerial.flush();
delay(50);

}

// Signal end of transfer
espSerial.println(“FETCHINFO:END”);

// Step 5: Wait for employee info
unsigned long start = millis();
while (millis() - start < 5000) {
if (espSerial.available()) {
String result = espSerial.readStringUntil(‘\n’);
result.trim();

  if (result.startsWith("EMPINFO:")) {
    result.remove(0, 8);  // Remove "EMPINFO:"

    int p1 = result.indexOf(',');
    int p2 = result.indexOf(',', p1 + 1);
    int p3 = result.indexOf(',', p2 + 1);
    int p4 = result.indexOf(',', p3 + 1);

    String id         = result.substring(0, p1);
    String name       = result.substring(p1 + 1, p2);
    String team       = result.substring(p2 + 1, p3);
    String desig      = result.substring(p3 + 1, p4);
    String fingerName = result.substring(p4 + 1);

    Serial.println("🔍 Employee Match Found:");
    Serial.print("Emp_ID: ");       Serial.println(id);
    Serial.print("Emp_Name: ");     Serial.println(name);
    Serial.print("Team: ");         Serial.println(team);
    Serial.print("Designation: ");  Serial.println(desig);
    Serial.print("Finger Name: ");  Serial.println(fingerName);
  } else {
    Serial.print("⚠️ Unexpected ESP response: ");
    Serial.println(result);
  }
  return;
}

}

Serial.println(“:cross_mark: No response from ESP.”);
}

void enrollFingerprint() {
String nm, tm, ds, fg;
uint16_t enrollID;
byte resp[12];

// Ask for ID
Serial.println(“Enter ID (0–199):”);
while (Serial.available() == 0);
enrollID = Serial.readStringUntil(‘\n’).toInt();
Serial.print(":inbox_tray: You entered ID: ");
Serial.println(enrollID);

// Employee metadata
Serial.println(“Enter Name:”);
while (nm.length() == 0) nm = Serial.readStringUntil(‘\n’);
nm.trim();

Serial.println(“Enter Team:”);
while (tm.length() == 0) tm = Serial.readStringUntil(‘\n’);
tm.trim();

Serial.println(“Enter Designation:”);
while (ds.length() == 0) ds = Serial.readStringUntil(‘\n’);
ds.trim();

Serial.println(“Enter Finger Name:”);
while (fg.length() == 0) fg = Serial.readStringUntil(‘\n’);
fg.trim();

// Begin enrollment
sendCommand(CMD_ENROLL_START, enrollID);
if (!readAck(resp)) {
Serial.println(“:cross_mark: ENROLL_START failed”);
return;
}

for (int i = 1; i <= 3; i++) {
Serial.print(":backhand_index_pointing_right: Place finger for scan ");
Serial.println(i);

while (true) {
  sendCommand(CMD_CAPTURE, 1);  // best image
  if (readAck(resp)) break;
  delay(500);
}

sendCommand(CMD_ENROLL_1 + (i - 1));
if (!readAck(resp)) {
  Serial.print("❌ ENROLL_"); Serial.print(i); Serial.println(" failed");
  return;
}

Serial.println("✋ Remove finger");
delay(2000);

}

Serial.println(“:white_check_mark: Enroll success.”);

// ================= Template Transfer ==================
espSerial.print(“TEMPLATE:”);
espSerial.print(enrollID);
espSerial.print(“,”);
espSerial.print(fg);
espSerial.println(“,START”);
Serial.println(“:outbox_tray: TEMPLATE:START sent”);

// Request template
const int SZ = 498;
const int CH = 64;
uint8_t tmpl[SZ];

sendCommand(CMD_GET_TEMPLATE, enrollID);
if (!readAck(resp)) {
Serial.println(“:cross_mark: CMD_GET_TEMPLATE failed”);
return;
}

// Read template chunk-by-chunk (64 bytes)
int received = 0;
unsigned long t0 = millis();

while (received < SZ && millis() - t0 < 5000) {
if (mySerial.available()) {
tmpl[received++] = mySerial.read();
}
}

if (received != SZ) {
Serial.print(“:hourglass_not_done: Only received “);
Serial.print(received);
Serial.println(” bytes before timeout.”);
return;
}

// Send 64-byte template in 8 chunks (64 x 7 + 50 bytes last)
int totalChunks = (SZ + CH - 1) / CH;
char hexBuf[CH * 2 + 2]; // +2 to safely hold optional padding + null

for (int i = 0; i < totalChunks; i++) {
int offset = i * CH;
int len = min(CH, SZ - offset);

for (int j = 0; j < len; j++) {
  sprintf(&hexBuf[j * 2], "%02X", tmpl[offset + j]);  // always 2-digit hex
}

int hexLen = len * 2;
hexBuf[hexLen] = '\0';

// ✅ Ensure even-length hex string (safety for unhexlify)
if (hexLen % 2 != 0) {
  hexBuf[hexLen] = '0';
  hexBuf[hexLen + 1] = '\0';
  hexLen += 1;
}

espSerial.print("SENDTEMPLATE:");
espSerial.print(i + 1);
espSerial.print("/");
espSerial.print(totalChunks);
espSerial.print(":");
espSerial.println(hexBuf);

Serial.print("✅ Hex length: ");
Serial.println(strlen(hexBuf));
Serial.print("Hex Char: ");
Serial.println(hexBuf);

espSerial.flush();      // Wait until all data is sent
delay(100);             // Give ESP time to process & forward

Serial.print("📤 Chunk ");
Serial.print(i + 1);
Serial.print("/");
Serial.println(totalChunks);

delay(50);  // allow server to process

}

// Send EMPINFO
espSerial.print(“EMPINFO:”);
espSerial.print(enrollID); espSerial.print(“,”);
espSerial.print(nm); espSerial.print(“,”);
espSerial.print™; espSerial.print(“,”);
espSerial.print(ds); espSerial.print(“,”);
espSerial.println(fg);

// Confirm on Serial
Serial.println(“====== Enrolled Fingerprint ======”);
Serial.print("Emp_ID: "); Serial.println(enrollID);
Serial.print("Emp_Name: "); Serial.println(nm);
Serial.print("Team: "); Serial.println™;
Serial.print("Designation: "); Serial.println(ds);
Serial.print("Finger Name: "); Serial.println(fg);

delay(200);
// End marker
espSerial.println(“TEMPLATE:END”);
Serial.println(“:outbox_tray: TEMPLATE:END sent”);
espSerial.flush();
delay(100);
}

void deleteFingerprint() {
byte response[12];

Serial.println();
Serial.print("Enter ID to delete: ");
while (!Serial.available()) { /* wait */ }

String s = Serial.readStringUntil(‘\n’);
s.trim();
uint16_t delID = s.toInt();

Serial.print(":inbox_tray: Deleting ID: ");
Serial.println(delID);

sendCommand(CMD_DELETE_ID, delID);

if (readAck(response)) {
Serial.print(":white_check_mark: Successfully deleted ID ");
Serial.println(delID);
} else {
Serial.print(“:cross_mark: Delete failed. ACK code: 0x”);
Serial.println(response[8], HEX);
}
}

void setup() {
Serial.begin(9600);
mySerial.begin(9600);
espSerial.begin(9600);
delay(500);
byte r[12];
sendCommand(CMD_OPEN);
if (!readAck(r)) { Serial.println(“:cross_mark: Sensor init failed”); while (1); }
Serial.println(“:white_check_mark: Sensor ready”);
sendCommand(CMD_LED_ON, 1);
readAck(r);
}

void loop() {
Serial.println(“\n📋 MENU:\n1. Verify Fingerprint\n2. Enroll Fingerprint\n3. Delete Fingerprint”);
String opt = getInput(“Enter option (1/2/3):”);

opt.trim(); // Ensure it’s clean

Serial.print(“:backhand_index_pointing_right: Option received: [”);
Serial.print(opt);
Serial.println(“]”);

if (opt == “1”) verifyFingerprint();
else if (opt == “2”) enrollFingerprint();
else if (opt == “3”) deleteFingerprint();
else Serial.println(“:cross_mark: Option not relevant”);

delay(1000);
}

This is way out “in the weeds”, as we don’t provide custom code help…but I did kick this to 3 AI models and while they disagree about some fundamental stuff (like whether the GT521F52 can load templates or not…it can definitely save them) that I imagine you should be able to glean a working situation from going through all 3

ChatGPT
Claude
Perplexity

I’d start with the ChatGPT one, as it seems to disagree with the other 2 the most