目录
一、前言
最近在做一个物联网的项目,主体构思是根据AS608模块来采集指纹并生成对应的指纹库,然后通过一个主控芯片ESP32S3来链接,再加上一个4G模块用来上传数据到云端平台,这样就可以实现线上平台统一管理,多台指纹识别设备互相协同构成系统。
二、AS608指纹模块简介
AS608 指纹识别模块主要是指采用了杭州晟元芯片技术有限公司(Synochip)的 AS608 指纹识别芯片而做成的指纹模块,模块厂商只是基于该芯片设计外围电路,集成一个可供2次开发的指纹模块;所以只要是基于AS608芯片的指纹模块,其控制电路及控制协议几乎是一样的,只是厂家和性能不同而已。
资料链接:(包含上位机、模块使用说明、Arduino测试程序等本文涉及的相关资料)
通过网盘分享的文件:CSDN-AS608.rar
链接: https://pan.baidu.com/s/1SzOkoVvrtSNGuyuNM1S56Q?pwd=1234
提取码: 1234
三、初步测试AS608模块
(一)硬件信息(详细信息请查看模块使用手册)
1、特性参数
2、接口说明
(二)硬件连接
- 电源线,连接ESP32S3上的3.3V和GND就行了,注意别接反了,听说容易烧坏。
- 通信线,需要根据自己设定的串口进行连接。例如:我设定的ESP32S3串口引脚是 RX_PIN = 16,TX_PIN = 17,那么它们分别就跟AS608上的TX和RX相连即可。
#define ESP_RX 16 //Connect with TXpin of AS608
#define ESP_TX 17 //Connect with RXpin of AS608
(三)驱动程序
1、下载相关库文件及示例
可以直接访问以下链接:
也可以在Aduino IDE里面直接搜索下载:
2、根据需求修改代码
代码中串口设定需要我们去进行调整。如下图,修改前:
修改后:
再阐述一遍修改原因:由于不同型号的主控芯片对应的可用于UART串口通信的引脚也不同,所以需要根据自己所使用的串口引脚。
修改了串口之后就可以运行示例进行一些简单的尝试:
当然这些示例都是一些非常简单的功能,和我们想设计的系统复杂度差别较大。这里其实也是我写篇文章的原因:网上搜到的关于AS608的文章都是讲的比较简单(可以直接搜索其他文章进行操作,如有需要也可留言我进行编写发布),但没涉及到怎么去提取存储在模块内的指纹库内的数据(真正用来校验指纹的数据)。
四、获取AS608的指纹特征库数据
(一)理论依据
指令格式(重点):
为了获取指纹特征库的数据,我们需要重点关注数据包和结束包,并且在串口通信过程中,获取数据之后把包头包尾等部分去除(详见后文)。
为了获取指纹特征数据库,可以先了解一下AS608的应答过程:
(二)录入指纹获取相应的指纹特征数据库
这一过程是必须的,也是为了后续验证我们通过代码所截取到的数据是否准确。我们使用上位机来完成这一步(上文的资料里面有)
使用一个USB转UART的小模块,然后把AS608链接上电脑(注意调整模块的电压为3.3V),之后打开上位机(具体步骤参照资料里面的软件使用说明)
选择正确的串口号(如图所示为COM7):
成功连接之后应该会如下显示:
之后再按照界面的提示,或者软件使用说明,可以先尝试一下模块的具体功能,录入几个指纹数据。
然后再获取指纹库:
会生成一个.mb后缀的数据文件。这种后缀的文件可以用EmEditor 中的十六进制试图二进制打开方式查看:(这个打开方式我找了好久,啊啊啊……)
就可以看到如下内容: 暂时先不去管里面的数据怎么划分。
OK,到这步你已经拥有了一个存储了指纹数据的AS608模块,接下来就是尝试通过ESP32S3链接AS608模块来获取指纹特征库(这也是真正与系统交互的关键)。
(三)获取指纹特征数据
首先我们通过上位机看一下我们的数据包大小是多少:
注意看其实此时的大小指的是我们数据包中数据那一部分有多大,其实还有数据前(包头2 bytes+地址 4 bytes+标识 1 byte+长度 2 bytes)总共9 bytes,结尾的校验码2 bytes。
所以总共一个数据包的实际长度是:128+9+2 = 139 bytes。
根据前面提到的模块应答流程可以知道,一个数据包后面会跟一个结束包,所以我们至少一口气读两个包也就是:139*2=278 bytes。
如果再去除掉包头包尾那些没有用的数据,就可以得到我们想要的指纹特征库了。
OK,理论分析完毕,开始实践。
最终实验代码如下:(对于fingerID = 0 的指纹特征数据,连续读取了三个包,总共是417 bytes,未去除掉包头包尾)
#include <Adafruit_Fingerprint.h>
#define ESP_RX 16 //Connect with TXpin of AS608
#define ESP_TX 17 //Connect with RXpin of AS608
HardwareSerial mySerial(2);
Adafruit_Fingerprint finger = Adafruit_Fingerprint(&mySerial);
int getFingerprintIDez();
void setup()
{
Serial.begin(9600);
while (!Serial);
delay(100);
Serial.println("TEST begin!");
mySerial.begin(57600, SERIAL_8N1, ESP_RX, ESP_TX);
// set the data rate for the sensor serial port
finger.begin(57600);
delay(5);
if (finger.verifyPassword()) {
Serial.println("Found fingerprint sensor!");
Serial.println(F("Reading sensor parameters"));
finger.getParameters();
Serial.print(F("Status: 0x")); Serial.println(finger.status_reg, HEX);
Serial.print(F("Sys ID: 0x")); Serial.println(finger.system_id, HEX);
Serial.print(F("Capacity: ")); Serial.println(finger.capacity);
Serial.print(F("Security level: ")); Serial.println(finger.security_level);
Serial.print(F("Device address: ")); Serial.println(finger.device_addr, HEX);
Serial.print(F("Packet len: ")); Serial.println(finger.packet_len);
Serial.print(F("Baud rate: ")); Serial.println(finger.baud_rate);
} else {
Serial.println("Did not find fingerprint sensor :(");
while (1);
}
/*
// Try to get the templates for fingers 1 through 10
for (int finger = 0; finger < 10; finger++) {
downloadFingerprintTemplate(finger);
}
*/
int finger = 0 ;
downloadFingerprintTemplate(finger);
}
uint8_t downloadFingerprintTemplate(uint16_t id)
{
Serial.println("------------------------------------");
Serial.print("Attempting to load #"); Serial.println(id);
uint8_t p = finger.loadModel(id);
switch (p)
{
case FINGERPRINT_OK:
Serial.print("Template "); Serial.print(id); Serial.println(" loaded");
break;
case FINGERPRINT_PACKETRECIEVEERR:
Serial.println("Communication error");
return p;
default:
Serial.print("Unknown error "); Serial.println(p);
return p;
}
// OK success!
Serial.print("Attempting to get #"); Serial.println(id);
p = finger.getModel();
switch (p) {
case FINGERPRINT_OK:
Serial.print("Template "); Serial.print(id); Serial.println(" transferring:");
break;
default:
Serial.print("Unknown error "); Serial.println(p);
return p;
}
///*
int Length = 417;
uint8_t templateBuffer[Length];
memset(templateBuffer, 0xff, Length); //zero out template buffer
int index=0;
uint32_t starttime = millis();
while ((index < Length) && ((millis() - starttime) < 1000))
{
if (mySerial.available())
{
templateBuffer[index] = mySerial.read();
index++;
}
}
Serial.print(index); Serial.println(" bytes read");
// // filtering only the data packets
// int uindx = 9, index = 0;
// memcpy(fingerTemplate + index, bytesReceived + uindx, 256); // first 256 bytes
// uindx += 256; // skip data
// uindx += 2; // skip checksum
// uindx += 9; // skip next header
// index += 256; // advance pointer
// memcpy(fingerTemplate + index, bytesReceived + uindx, 256); // second 256 bytes
for(int i = 0 ; i < Length ; i ++){
if(i % 29 == 0 && i != 0){
Serial.print("0x");
printHex(templateBuffer[i], 2);
Serial.println(", ");
}
else{
Serial.print("0x");
printHex(templateBuffer[i], 2);
Serial.print(", ");
}
}
Serial.println("done.");
//*/
/*
// one data packet is 267 bytes. in one data packet, 11 bytes are 'usesless' :D
uint8_t bytesReceived[534]; // 2 data packets
memset(bytesReceived, 0xff, 534);
uint32_t starttime = millis();
int i = 0;
while (i < 534 && (millis() - starttime) < 20000) {
if (mySerial.available()) {
bytesReceived[i++] = mySerial.read();
}
}
for (int count= 0; count < 34; count++)
{
for (int i = 0; i < 16; i++)
{
Serial.print("0x");
Serial.print(bytesReceived[count*16+i], HEX);
Serial.print(", ");
}
Serial.println();
}
Serial.print(i); Serial.println(" bytes read.");
Serial.println("Decoding packet...");
uint8_t fingerTemplate[512]; // the real template
memset(fingerTemplate, 0xff, 512);
// filtering only the data packets
int uindx = 9, index = 0;
memcpy(fingerTemplate + index, bytesReceived + uindx, 256); // first 256 bytes
uindx += 256; // skip data
uindx += 2; // skip checksum
uindx += 9; // skip next header
index += 256; // advance pointer
memcpy(fingerTemplate + index, bytesReceived + uindx, 256); // second 256 bytes
for (int i = 0; i < 512; ++i)
{
Serial.print("0x");
printHex(fingerTemplate[i], 2);
if(i%20 == 0){
Serial.println();
}
else
Serial.print("\t");
}
*/
return p;
}
void printHex(int num, int precision) {
char tmp[16];
char format[128];
sprintf(format, "%%.%dX", precision);
sprintf(tmp, format, num);
Serial.print(tmp);
}
void loop()
{}
运行结果如下:
值得一提的是,包长:0x82 = 130(Dec),和我们用上位机读到的是一致的。在图中我把数据包前的包头和末尾的包尾都做了一定的标注。
手动去除掉包头包尾部分与之前获得的标准指纹特征库进行对比,手动去除包头包尾之后:
可以和前文的EmEditor展示出来的指纹特征数据进行对比,也可以说是完全一致的。
为了保证普适性,我还验证了fingerID = 1 时,数据也是一致的。如果获取其他未使用的ID,那么程序就会报错,显示无法获取指纹特征库。
那么接下来就只需要通过程序去让他自动去除包头包尾。
五、最终获取指纹特征库
代码如下:(获取三个数据包的内的数据)
#include <Adafruit_Fingerprint.h>
#define ESP_RX 16 //Connect with TXpin of AS608
#define ESP_TX 17 //Connect with RXpin of AS608
HardwareSerial mySerial(2);
Adafruit_Fingerprint finger = Adafruit_Fingerprint(&mySerial);
int getFingerprintIDez();
void setup()
{
Serial.begin(9600);
while (!Serial);
delay(100);
Serial.println("TEST begin!");
mySerial.begin(57600, SERIAL_8N1, ESP_RX, ESP_TX);
// set the data rate for the sensor serial port
finger.begin(57600);
delay(5);
if (finger.verifyPassword()) {
Serial.println("Found fingerprint sensor!");
Serial.println(F("Reading sensor parameters"));
finger.getParameters();
Serial.print(F("Status: 0x")); Serial.println(finger.status_reg, HEX);
Serial.print(F("Sys ID: 0x")); Serial.println(finger.system_id, HEX);
Serial.print(F("Capacity: ")); Serial.println(finger.capacity);
Serial.print(F("Security level: ")); Serial.println(finger.security_level);
Serial.print(F("Device address: ")); Serial.println(finger.device_addr, HEX);
Serial.print(F("Packet len: ")); Serial.println(finger.packet_len);
Serial.print(F("Baud rate: ")); Serial.println(finger.baud_rate);
} else {
Serial.println("Did not find fingerprint sensor :(");
while (1);
}
/*
// Try to get the templates for fingers 1 through 10
for (int finger = 0; finger < 10; finger++) {
downloadFingerprintTemplate(finger);
}
*/
int finger = 0 ;
downloadFingerprintTemplate(finger);
}
uint8_t downloadFingerprintTemplate(uint16_t id)
{
Serial.println("------------------------------------");
Serial.print("Attempting to load #"); Serial.println(id);
uint8_t p = finger.loadModel(id);
switch (p)
{
case FINGERPRINT_OK:
Serial.print("Template "); Serial.print(id); Serial.println(" loaded");
break;
case FINGERPRINT_PACKETRECIEVEERR:
Serial.println("Communication error");
return p;
default:
Serial.print("Unknown error "); Serial.println(p);
return p;
}
// OK success!
Serial.print("Attempting to get #"); Serial.println(id);
p = finger.getModel();
switch (p) {
case FINGERPRINT_OK:
Serial.print("Template "); Serial.print(id); Serial.println(" transferring:");
break;
default:
Serial.print("Unknown error "); Serial.println(p);
return p;
}
///*
int Length = 417;
uint8_t templateBuffer[Length];
memset(templateBuffer, 0xff, Length);
int index=0;
uint32_t starttime = millis();
while ((index < Length) && ((millis() - starttime) < 1000))
{
if (mySerial.available())
{
templateBuffer[index] = mySerial.read();
index++;
}
}
Serial.print(index); Serial.println(" bytes read");
Length = Length - 33;
uint8_t fingerTemplate[Length]; // the real template
memset(fingerTemplate, 0xff, Length);
// filtering only the data packets
int uindx = 9;
index = 0;
memcpy(fingerTemplate + index, templateBuffer + uindx, 128); // first 128 bytes
uindx += 128; // skip data
uindx += 2; // skip checksum
uindx += 9; // skip next header
index += 128; // advance pointer
memcpy(fingerTemplate + index, templateBuffer + uindx, 128); // second 128 bytes
uindx += 128; // skip data
uindx += 2; // skip checksum
uindx += 9; // skip next header
index += 128; // advance pointer
memcpy(fingerTemplate + index, templateBuffer + uindx, 128); // third 128 bytes
for(int i = 0 ; i < Length ; i ++){
if(i % 29 == 0 && i != 0){
Serial.print("0x");
printHex(fingerTemplate[i], 2);
Serial.println(", ");
}
else{
Serial.print("0x");
printHex(fingerTemplate[i], 2);
Serial.print(", ");
}
}
Serial.println("done.");
//*/
return p;
}
void printHex(int num, int precision) {
char tmp[16];
char format[128];
sprintf(format, "%%.%dX", precision);
sprintf(tmp, format, num);
Serial.print(tmp);
}
void loop()
{}
运行结果如下:(成功去除了包头包尾部分)
六、总结
在本文介绍了如何调试AS608模块,并通过它获取指纹特征库数据;基于此结果,有 个方向:通过再给ESP32S3搭载外设(SIM7600X模块)使硬件部分具备联网的功能,实现对系统的实时管理,再配合搭建一个云平台就可以搭建起整个框架,适合与其他管理系统进行协同;或者搭载一个储存模块,平时把指纹数据库拷贝下来,周期性的把数据进行上传同步,单纯用于门禁指纹锁之类的还是很便利的。
挖坑:接下来将对数据库的建立,PC端程序的编写进行记录分享,如果感兴趣的话欢迎持续关注。
如果对本文的内容有任何改进的建议也欢迎评论\私信告诉我!如果本文对你有帮助的话,不妨点个赞。欢迎留言讨论问题,一起讨论问题、解决问题。