采用高性能 Cortex 内核的主控,集成了高安全性商用指纹算法,支持指纹录入、图像采集、特征值提取和指纹比对等功能。无需了解复杂指纹识别算法,只需通过串口发送指令即进行二次开发,可快速集成到各种对体积和精度要求较高的指纹识别应用中。

用户拿到模块后,可先使用测试软件或测试程序对模块进行测试。在对模块有了一定的了解后,可使用指令表进行二次开发。
模块可以通过 UART 串口控制或者通过USB接口控制。
如果接到开发板上,可以使用UART串口连接。
| 指纹模块 | 目标主板 | 备注 |
|---|---|---|
| GND | GND | 电源地 |
| RX | TX | UART数据传输引脚,需要交叉接线 |
| TX | RX | UART数据传输引脚,需要交叉接线 |
| VIN | 3.3V | 3.3V供电输入 |
| IRQ | GPIO | 指纹触摸检测引脚, 有手指触摸输出高电平,配合VT引脚使用, 可接入普通GPIO引脚,设置为输入状态使用 |
| VT | GPIO | 触摸供电引脚,和IRQ引脚配合使用,可接入普通GPIO |
可以接入配送的USB引脚使用。 如果是自己另外设计线材或者定制线材,注意处理电平转换。 模块只能支持3.3V电平
本模块采用高精密元件,在采集指纹时:
手指轻轻地触碰到采集窗口就能识别,不需要用力按压指纹采集窗。


1、使用 FT232 USB转UART串口模块进行测试(如用户使用其它 USB 转串口模块,操作也类似,模块需要另外购买),安装 FT232 驱动
2、参考引脚定义#UART串口连接连接模块(注意 RXD TXD 是交叉相连的):
3、将 FT232 连接到 PC 机的 USB 接口,打开设备管理器, 查看相应的 COM 口:
4、下载测试软件:Capacitive-Fingerprint-Reader-(B)-Demo:
打开软件:
选择对应的通信方式:
Serial : 串口通信
Usb : USB通信
选择串口通信时,注意选择对应的波特率和串口号
注意:提供的上位机软件只能支持Windows PC
串口与USB的操作方式一致,这里使用USB进行演示
点击初始化传感器,如果连接成功则会接收到如图所示结果:
参数类型:
Device ID :表示本模块设备编号,可设置范围: 1-255
Security Level :表示安全等级,可设置值:1-5,数值越大:误识率(FAR)越低,但拒真率(FRR)越高
Duplication Check :指纹重复检查状态开/关(1/0)。
Baudrate :波特率的索引。
Auto Learn :表示指纹模板自学习状态开/关(1/0)。
选择对应的参数,输入好对应的参数值,点击设置参数即可改变模块的参数;点击获取参数即可获取模块的当前参数。
首先先将ID(指纹存储的编号)设置好,在选择图像数量(需要录入几次手指),然后点击录入指纹,根据提示一步一步的操作,最后成功结果如下图。
如果你不知道ID应该设置成多少,可以使用“可注册编号”、“获取注册状态”、“获取已注册用户列表”来寻找可注册的ID
选择要比对的模板指纹ID,点击 比对(1:1) 如果你开启了自学习功能,比对时间会略大于0.5S,并保存手指上没有保存的那一部分指纹。
点击 比对(1:N) 会自动比对1~3000(设置的指纹容量为多少就会比对到哪)的编号中所有已经注册的指纹,它是连续比对的,即比对成功后停止0.5s左右自动进行下一次比对。
1、点击用户总数,会查询该模块在1~1000(设置的指纹容量为多少就会查询到哪)的编号中注册了多少指纹。
2、点击获取已注册用户列表,会查询该模块在1~3000(设置的指纹容量为多少就会查询到哪)中注册了多少指纹,滑动结果处的指纹,可以看到已经注册的指纹编号。
点击“可注册编号”,会从编号1开始查询第一个没有被注册的编号。
获取当前ID编号的注册状态
点击 获取注册状态 会显示当前ID编号的存储空间是否是空的,下图便是该ID编号的存储空间不是空的(即ID已被注册)。
点击“单个删除”,删除当前ID编号的指纹。
点击全部删除,删除1~3000(设置的指纹容量为多少就会到哪)的编号中所有以注册的指纹。
点击上传指纹图像,然后在传感器上按下自己的手指。
也可以打开预览图像,这样在每次需要输入指纹时,都会显示指纹图像(注:串口模式下不推荐使用,传输图像数据需要6秒左右)
可以选择打开拉伸显示,满图像的显示指纹
选择已经保存过指纹的ID编号
点击上传特征值
点击“批量上传特征值”,选择一个文件夹用来保存特征值文件。会上传1~3000(指纹容量的选择数量)编号的指纹数据。以FPT的格式存放在你选择的文件夹下面。

配置好ID编号和图像数量,具体参考 “用户录入”
点击“从图像注册”,选择指纹模图像。图像大小为242*266

1、单个下载
配置好ID编号。
点击下图所示的打开文件,选择一个FPT格式的文件,点击“下载特征值”。
2、批量下载
点击“批量下载特征值”选择对应的文件夹,文件夹中所有的FPT格式文件按照排序方式依次下载到模块当中,他们的ID号是依次增加的。
点击下图所示的打开文件,选择指纹图像,点击“下载图像识别”。
配置好ID编号和图像数量,具体参考 “用户录入”
点击下图所示的打开文件,选择一个FPT格式的文件,点击“下载特征值识别”,将会和你选择的指纹ID编号内的指纹模板进行对比;
点击“下载特征值比对”,将会和你按下的手指进行比对。
点击“坏损特征值编号”,接收到的结果回将显示被损坏的特征值的总数和第一个被损坏的特征值的编号。
点击“进入休眠”
进入休眠状态后,无论进行什么操作都没有反应,只有重新上电后才有反应
树莓派、Arduino、STM32的函数内容可能会有部分不同,但是操作方式与功能是一样的。
| 模块 | STM32 | 功能 |
| VIN | 3.3V | 电源输入 |
| GND | GND | 电源地 |
| TXD | PA10 | 模块串口发送端 |
| RXD | PA9 | 模块串口接收端 |
| 模块 | Arduino | 功能 |
| VIN | 3.3V | 电源输入 |
| GND | GND | 电源地 |
| TXD | 10 | 模块串口发送端 |
| RXD | 11 | 模块串口接收端 |
| 模块 | Raspberry Pi | 功能 |
| VIN | 3.3V | 电源输入 |
| GND | GND | 电源地 |
| TXD | RXD | 模块串口发送端 |
| RXD | TXD | 模块串口接收端 |
按连接方式连接好对应的STM32/Arduino与指纹模块
将Open103Z/Arduino UNO的USB与电脑连接 选择对应的串口:
CMD0 :检查连接是否成功
CMD1 :检查是否有手指按下
CMD2 :注册指纹
CMD3 :删除指纹
CMD4 :比对一个指纹
CMD5 :比对范围内的指纹
CMD6 :查询第一个未注册指纹的编号
CMD7 :查询指纹注册总数
CMD8 :上传指纹图像到主机
CMD9 :下载指纹图像到模块
注:Arduino因为内存的原因,没有CMD8、CMD9两条指令,使用115200波特率时会有丢包现象的发生推荐使用57600的波特率
修改方法:
1、使用软件进行修改
2、使用下面的指令进行修改
设置成115200波特率:
55 AA 00 00 02 00 05 00 03 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0e 01
设置成57600波特率:
55 AA 00 00 02 00 05 00 03 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0d 01
设置成功后的返回值为:
AA 55 01 00 02 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 01
输入命令
sudo raspi-config





重启
sudo reboot
sudo apt install git-core sudo git clone https://github.com/WiringPi/WiringPi cd WiringPi/ sudo ./build #查看wiringpi版本,一般在2.70以上 gpio -v
#python3的串口库 sudo apt-get install python3-serial #python2的串口库 sudo apt-get install python-serial
wget https://www.waveshare.net/w/upload/1/1d/Capacitive-Fingerprint-Reader%28B%29-Code.zip unzip unzip Capacitive-Fingerprint-Reader\(B\)-Code cd cd Capacitive-Fingerprint-Reader\(B\)-Code/RaspberryPi/
c
cd c make sudo ./test
python
cd python sudo python main.py
python3
cd python3 sudo python3 main.py
fingerprint.h 用来保存程序运行的主体函数。
cmd.h 用来保存你的指纹图像数据。
首先处理好握手信号的数据 然后等待模块初始化完成,最后发送握手信号,握手成功则进行下面的操作,不成功等待1秒后再次进行握手,共执行3次。
void CMD_Init(void)
{
uint8_t i = 0;
Cmd_Packet_Init();
Handshake_Signal();
while(1)
{
Tx_cmd();
if( Rx_cmd(1) )
{
printf("Connection closed by server\n\r");
printf("Try to reconnect\n\r");
if(i++ > 3)
{
printf("Power on the device again");
while(1);
}
}
else
break;
HAL_Delay(1000);
}
}
取消第四行的注释等待方式为:等待0.5秒 不取消第四行的注释等待方式为:等待模块发送初始化完成标志(该方法必须将RST引脚连接好)
void Handshake_Signal(void)
{
// Select a power-on waiting mode
//#define CMD_DELAY
#ifdef CMD_DELAY
HAL_Delay(500);
#else
Handshake_flag = 1;
HAL_GPIO_WritePin(Finger_RST_GPIO_Port,Finger_RST_Pin,GPIO_PIN_RESET);
HAL_Delay(250);
HAL_GPIO_WritePin(Finger_RST_GPIO_Port,Finger_RST_Pin,GPIO_PIN_SET);
while(1)
{
if(flag == 1)
{
if(Handshake_data == 0x55)
{
Handshake_flag = 0;
flag = 0;
break;
}
else
{
printf("The communication fails. Power on the device again");
while(1);
}
}
HAL_Delay(1);
}
#endif
#undef CMD_DELAY
}
void Cmd_Packet_Init(void)
{
CMD.PREFIX = Command;
CMD.SID = Command_SID;
CMD.DID = Command_DID;
CMD.CMD = CMD_TEST_CONNECTION;
CMD.LEN = DATA_0;
for(int i = 0 ; i <CMD_Len ; i++)
CMD.DATA[i] = 0x00;
}
uint8_t Tx_Data_Process(void)
{
while(1)
{
if( Usart2_ReceiveStruct.RX_flag == 1 )
{
Usart2_ReceiveStruct.RX_Size=0;
Usart2_ReceiveStruct.RX_flag=0;
flag = 0;
if((Usart2_ReceiveStruct.RX_pData[0] == 'C') && (Usart2_ReceiveStruct.RX_pData[1] == 'M') && (Usart2_ReceiveStruct.RX_pData[2] == 'D'))
{
switch(Usart2_ReceiveStruct.RX_pData[3])
{
case '0': CmdTestConnection( 0 ) ; break;
case '1': CmdFingerDetect( 0 ) ; break;
case '2': AddUser( ) ; break;
case '3': ClearUser( 0 ) ; break;
case '4': VerifyUser( ) ; break;
case '5': ScopeVerifyUser( ) ; break;
case '6': CmdGetEmptyID( 0 ) ; break;
case '7': GetUserCount( 1 ) ; break;
case '8': CmdUpImageCode( 1 ) ; break;
case '9': CmdDownImage( ) ; break;
}
break;
}
}
HAL_Delay(1);
}
return 0;
}
void Tx_cmd(void)
{
uint16_t CKS = 0 ;
if(mode == 0 || mode == 2)
{
cmd[0] = CMD.PREFIX & 0xff;
cmd[1] = (CMD.PREFIX & 0xff00) >> 8;
cmd[2] = CMD.SID;
cmd[3] = CMD.DID;
cmd[4] = CMD.CMD ;
cmd[5] = 0x00 ;
cmd[6] = CMD.LEN & 0xff;
cmd[7] = (CMD.LEN & 0xff00) >> 8;
for(int i = 0 ; i < CMD.LEN ; i++)
cmd[8+i] = CMD.DATA[i];
for(int i = 0 ; i < 24 ; i++)
CKS = CKS + cmd[i];
cmd[24] = CKS & 0xff;
cmd[25] = (CKS & 0xff00) >> 8;
HAL_UART_Transmit(&huart1,cmd, 26,2);
}
else
{
cmd_data[0] = CMD_DATA.PREFIX & 0xff ;
cmd_data[1] = (CMD_DATA.PREFIX & 0xff00) >> 8 ;
cmd_data[2] = CMD_DATA.SID ;
cmd_data[3] = CMD_DATA.DID ;
cmd_data[4] = CMD_DATA.CMD ;
cmd_data[5] = 0x00 ;
cmd_data[6] = CMD_DATA.LEN & 0xff;
cmd_data[7] = (CMD_DATA.LEN & 0xff00) >> 8;
if(SN <129 )
{
for(int i = 0 ; i < CMD_DATA.LEN ; i++)
cmd_data[8+i] = CMD_DATA.DATA[i];
for(int i = 0 ; i < 506 ; i++)
CKS = CKS + cmd_data[i];
cmd_data[506] = CKS & 0xff;
cmd_data[507] = (CKS & 0xff00) >> 8;
HAL_UART_Transmit(&huart1,cmd_data, 508 , 44);
}
else
{
for(int i = 0 ; i < CMD_DATA.LEN ; i++)
cmd_data[8+i] = CMD_DATA.DATA[i];
for(int i = 0 ; i < 398 ; i++)
CKS = CKS + cmd_data[i];
cmd_data[398] = CKS & 0xff;
cmd_data[399] = (CKS & 0xff00) >> 8;
HAL_UART_Transmit(&huart1,cmd_data, 400,38);
}
}
}
uint8_t Rx_cmd( uint8_t back )
{
uint8_t a=1;
uint16_t CKS = 0;
while(a)
{
if( flag == 1 )
{
a = 0;
flag = 0;
if(rps[4] == 0xff)
return 1;
Rx_CMD_Process();
if(mode == 0)
{
for(int i=0 ; i<24 ; i++)
CKS = CKS + rps[i];
if(CKS == RPS.CKS)
return Rx_Data_Process(back);
}
else
{
for(int i=0 ; i<10 ; i++)
CKS = CKS + rps[i];
if(CKS == RPS.CKS)
return 0;
else
return RPS.CMD;
}
}
HAL_Delay(1);
}
return 1;
}
void Rx_CMD_Process(void)
{
RPS.PREFIX = rps[0] + rps[1] * 0x100;
RPS.SID = rps[2];
RPS.DID = rps[3];
RPS.CMD = rps[4] + rps[5] * 0x100;
RPS.LEN = rps[6] + rps[7] * 0x100;
RPS.RET = rps[8] + rps[9] * 0x100;
if(mode == 0)
{
for(int i=0 ; i<RPS_Len ; i++)
RPS.DATA[i] = rps[10 +i];
RPS.CKS = rps[24] + rps[25] * 0x100;
}
else
RPS.CKS = rps[10] + rps[11] * 0x100;
}
uint8_t Rx_Data_Process( uint8_t back )
{
uint8_t a = 0;
switch(RPS.CMD)
{
case CMD_TEST_CONNECTION: a = RpsTestConnection(back); break;
case CMD_FINGER_DETECT: a = RpsFingerDetect(back) ; break;
case CMD_GET_IMAGE: a = RpsGetImage(back); break;
case CMD_GENERATE: a = RpsGenerate(back); break;
case CMD_MERGE: a = RpsMerge(back); break;
case CMD_DEL_CHAR : a = RpsDelChar(back); break;
case CMD_STORE_CHAR: a =RpsStoreCher(back) ; break;
case CMD_SEARCH: a = RpsSearch(back) ; break;
case CMD_VERIFY: a= RpsVerify(back) ; break;
case CMD_GET_EMPTY_ID : a = RpsGetEmptyID(back); break;
case CMD_GET_ENROLL_COUNT : a = RpsGetEnrollCount(back); break;
case CMD_DOWN_IMAGE : a = RpsDownImage(back); break;
}
return a;
}
uint8_t RPS_RET(void)
{
switch(RPS.RET)
{
case ERR_SUCCESS: printf("Instruction processing succeeded\r\n"); break;
case ERR_FAIL: printf("Instruction processing failure\r\n"); break;
case ERR_TIME_OUT: printf("No prints were entered within the time limit\r\n"); break;
case ERR_FP_NOT_DETECTED: printf("There is no fingerprint input on the collector\r\n"); break;
case ERR_FP_CANCEL: printf("Instruction cancelled\r\n"); break;
case ERR_INVALID_BUFFER_ID: printf("The Ram Buffer number is invalid\r\n"); break;
case ERR_BAD_QUALITY: printf("Poor fingerprint image quality\r\n"); break;
case ERR_GEN_COUNT: printf("Invalid number of combinations\r\n"); break;
case ERR_INVALID_TMPL_NO: printf("The specified Template number is invalid\r\n"); break;
case ERR_DUPLICATION_ID: printf("The fingerprint has been registered, and the id is : %d\r\n",RPS.DATA[0]+RPS.DATA[1]*0x100 ); break;
case ERR_INVALID_PARAM: printf("Specified range invalid\r\n"); break;
case ERR_TMPL_EMPTY: printf("Template is not registered in the specified range\r\n"); break;
case ERR_VERIFY: printf("Description Failed to specify fingerprint comparison\r\n"); break;
case ERR_IDENTIFY: printf("Fingerprint comparison failed for the specified range\r\n"); break;
}
return RPS.RET;
}
周一-周五(9:30-6:30)周六(9:30-5:30)
手机:13434470212
邮箱:services04@spotpear.cn
QQ:202004841
