今天我就分享一波自己的成果。
心心念念的想做一个物联网的东西出来,终于在上个月搞定了,虽说肯定是有些漏洞(目前我是没有找出来的),但是效果看起来还不错。学了这么久的Python、Qt、liunx命令总算派上用场了。
材料:
树莓派一台
指纹模块(AS608光学指纹识别模块)
OLED液晶屏
usb转TTL
杜邦线
先说明一下
如果想要附件可以点击下面 链接 去下载:
树莓派上的源码下载https://download.csdn.net/download/happy_grass/11253753
电脑上的上位机源码下载https://download.csdn.net/download/happy_grass/11253807
oled上的字体https://download.csdn.net/download/happy_grass/11253815
现在我来说是我的想法和思路
刚开始看到隔壁实验室的指纹打卡机时好像是那种用usb读取数据的(好像是节约成本吧),我就想能这也太不智能了吧,就想着能不能让它智能些,比如通过电脑看到它的日志啊,或者用手机看到它的日志啊什么的(后来我才知道淘宝上TM已经出现这种完善的成品指纹考勤机了)。光想没用啊,还是要靠实践来检验真理。于是我就想到了手上的树莓派,想想就有些兴奋,可以向网上那些大佬一样DIY一个东西出来了,由于我的知识有限,无法自己搞个指纹打卡模块,就到网上买了一个,先开始买的正点原子的指纹模块,原子哥不愧是我原子哥,和他讨论问题时真是尽心尽力。文档很好,不过他那个和模块通信的程序不是很全,就几个,可以能是直接储存在模块里的缘故,可是我不想储存在模块里啊,我学了Python加上Mysql,我想直接存在数据库里,这样更加安全可靠。于是我就开始解决通信问题,将他发回来的数据解码,捣鼓了2天才完成,有点心酸。
先把配置文件分享了,下面代码会用到
[root]
pwd = 123
[db]
db_port = 3306
db_user = root
db_host = 127.0.0.1
db_pwd = 123456
db_database = finger
db_table = test1
[email]
smtpServer = smtp.qq.com
popServer = pop.qq.com
smtpSrcAddr = 发送请求的你的邮箱
smtpSrcAddrPwd = 允许的密码
smtpDstAddr = 目的邮箱
smtpPort = 465
[TCP]
ip = 192.168.137.249
port = 9090
[serial]
port = /dev/ttyAMA0 (先不用这个串口,这个是我配置过的,你可以先用usb转ttl连接树莓派的usb口和指纹模块)
baudrate = 57600
timeout = 10
[lewei]
ip = tcp.lewei50.com
port = 9960
userKey = 你自己的userKey
gatewayNode = 02
分享一波代码
要用电脑和指纹模块通信首先要装
python要serial模块,在cmd命令行中用命令 pip install serial 就可以下载serial 了
‘’’
import serial
import serial.tools.list_ports
import configparser
import os, sys
from core import dealFile
PATH=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(PATH)
SureCore = {
0x00: True, # 表示指令执行完毕或OK;
0x01: 101, # 表示数据包接收错误;
0x02: 102, # 表示传感器上没有手指;
0x03: 103, # 表示录入指纹图像失败;
0x06: 202, # 表示指纹图像太乱而生不成特征;
0x07: 203, # 表示指纹图像正常,但特征点太少而生不成特征;
0x15: 204, # 表示图像缓冲区内没有有效原始图而生不成图像;
0x08: 302, # 表示指纹不匹配;
0x09: 402, # 表示没搜索到;此时页码与得分为 0
0x0a: 502, # 表示合并失败(两枚指纹不属于同一手指);
0x0d: 802, # 表示指令执行失败;
0x0f: 1002, # 表示不能发送后续数据包;
0x1e: 1602, # 表示注册失败。
0x10: 1202, # 表示删除模板失败;
0x11: 1302, # 删除 flash 数据库中所有指纹模板
}
'''返回= 999 校验和错误'''
isOccupy = 0 # 是否被占用
"""串口配置"""
cf = configparser.ConfigParser()
cf = dealFile.readConf(cf)
serialPort = cf.get("serial", "port")
serialBaudrate = cf.getint("serial", "baudrate")
serialTimeout = cf.getint("serial", "timeout")
class DealBuff:
"""切割收到的数据"""
HEAD = [0xEF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF]
def __init__(self, buff):
self.buff = buff
"""处理读出的指纹数据用到"""
self.tag = list() # 包标识
self.data = list()
self.check = list()
pass
def read(self):
buff = [x for x in bytes(self.buff)]
check = self.slit(buff)
return self.tag, self.data, check
def listCut(self, buff, num):
"""切割数组"""
rec = list()
for i in range(num):
bytes_buf = (buff.pop(0))
# rec.append(buff.pop(0))
rec.append(bytes_buf)
return rec, buff
pass
def slit(self, buff):
""""选择数据"""
# 初始化中间变量
popList = list()
check_Num = 0
check = list()
head = list()
if len(buff) < 6: # 判断是否有效数据
return True
head, buff = self.listCut(buff, 6) # 选择出头及判断
for i in range(6):
if head[i] != self.HEAD[i]:
return False
popList, buff = self.listCut(buff, 1) # 取出包标识
self.tag.append(popList)
check_Num += popList[0] # 计算校验和
popList, buff = self.listCut(buff, 2) # 取出包长度
check_Num += popList[0] + popList[1] # 计算校验和
popList, buff = self.listCut(buff, popList[0] * 16 + popList[1]) # 取出包数据
check.append(popList.pop()) # 取出校验数据
check.append(popList.pop())
for i in popList: # 计算校验和
check_Num += i
self.data.extend(popList) # 导入有用数据
if check_Num % 65535 != check[0] + check[1]*256: # 检验校验和
return False
rec = self.slit(buff) # 得到是否正确分析完数据
return rec
pass
def write(self):
"""要写的数据打包"""
pack = self.dataSeparate(self.buff, 128) # 将数据分成每组128个元素
return pack
pass
def dataSeparate(self, buff, numPart):
"""把数据分组打包"""
num = int(len(buff) / numPart)
newData = list()
for i in range(num):
newData.append(buff[i * numPart:(i+1) * numPart])
packData = list()
for i in range(num-1):
data = self.packData(newData[i], 0x02) # 数据包没结束
packData.extend(data)
packData.extend(self.packData(newData[num-1], 0x08)) # 数据包结束
return packData
pass
def packData(self, buff, flage):
num = len(buff) + 2
senddata = [flage, int(num / 256), int(num % 256)] + buff
sum = 0
for i in senddata:
sum += i
senddata = self.HEAD + senddata
senddata.append(int(sum / 256))
senddata.append(int(sum % 256))
return senddata
pass
pass
class DealFinger:
"""和指纹模块交互"""
HEAD = [0xEF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF]
tag = list() # 包标识
cmd = list()
data = list()
ser = serial.Serial()
# ser.port = '/dev/ttyAMA0'
# ser.baudrate = 57600
# ser.timeout = 10
def __init__(self, cmd=None, data=None, server=None):
"""初始化函数"""
self.cmd = cmd
self.data = data
self.server = server
pass
def run(self, cmd=None):
"""启动命令"""
if hasattr(self, self.cmd):
self.link()
func = getattr(self, self.cmd)
return func(self.data)
pass
def link(self):
"""连接串口"""
self.ser.port = serialPort
self.ser.baudrate = serialBaudrate
self.ser.timeout = serialTimeout
# self.ser.bytesize = 8
# self.ser.parity = 'N'
# self.ser.xonxoff = 0
# self.ser.rtscts = 0:q
self.ser.close()
self.ser.open()
pass
def finger(self, data):
self.link()
check = self.getImage() # 检测获取图像
if check is not True:
return check, []
self.link()
check = self.genChar(data) # 检测生成特征值
if check is not True:
return check, []
# ================= 生成特征值时图像会清除,所以要重新采集 ========================== #
self.link()
check = self.getImage() # 检测获取图像
if check is not True:
return check, []
if self.server is not None:
self.server.send("True".encode("utf8")) # 发送数据说明准备好了
self.link()
check = self.upImage() # 上传图像
if check is not True: # 校验和错误
return check, []
self.tag, self.data = self.getUpImage()
if len(self.tag) is 0 and len(self.data) is 0: # 得到数据错误
return False, []
return True, []
def save(self, data=None):
self.link()
check = self.regModel()
if check is not True: # 校验和错误
return check, []
self.link()
check = self.upChar(data) # 上传特征值
if check is not True: # 校验和错误
return check, []
self.tag, self.data = self.getUpChar()
if len(self.tag) is 0 and len(self.data) is 0: # 得到数据错误
return False, []
return True, []
pass
def check(self, data=None):
"""检验指纹, 生成特征值"""
self.link()
check = self.match() # 比较指纹特征
if check is not True:
return check, []
score = self.data[1]*255 + self.data[2] # 返回的分数值
self.link()
check = self.regModel() # 合成指纹特征值
if check is not True:
return check, []
return True, score
pass
def isFinger(self, data=None):
"""判断现在的指纹和下载的指纹是否相同"""
self.link()
check = self.downCharCheck()
if check is not True: # 判断是否可以发送数据
return check, []
"""下载指纹"""
self.link()
self.downCharData(data)
"""检验指纹"""
self.link()
check = self.match()
if check is not True:
return check, []
score = self.data[1] * 255 + self.data[2] # 返回的分数值
return True, score
pass
def saveToHard(self, data=None):
"""储存指纹到硬件"""
check = self.psEnroll()
if check is not True:
return check, []
return True, self.data
pass
def checkPSIdentify(self,data=None):
"""自动检测指纹"""
check = self.psIdentify()
if check is not True:
return check, []
return True, self.data
pass
def creatFinger(self, data=None):
"""创建指纹库"""
num = len(data)
for i in range(num):
fingerData = eval(data[i][4])
fingerData = DealBuff(fingerData)
fingerData = fingerData.write()
self.link()
check = self.downCharCheck()
if check is not True: # 判断是否可以发送数据
return check
"""下载指纹"""
self.link()
self.downCharData(fingerData)
"""储存指纹"""
self.link()
check = self.psStoreChar(eval(data[i][0]))
if check is not True:
print(check)
return check
sys.stdout.write("\r"+"#"*(i+1) + str((i+1) * 100 / num))
sys.stdout.flush()
print("\r")
return True
pass
def psEnroll(self, data=None):
"""一次性储存 返回页码"""
cmd = self.HEAD + [0x01, 0x00, 0x03, 0x10, 0x00, 0x14]
return self.isOk(cmd, 14)
pass
def psEmpty(self, data=None):
""" 清空指纹库 """
cmd = self.HEAD + [0x01, 0x00, 0x03, 0x0D, 0x00, 0x11]
return self.isOk(cmd, 12)
pass
def psDeletChar(self, data=None):
"""删除是定指纹"""
num1 = int(data / 256)
num2 = int(data % 256)
cmd = [0x01, 0x00, 0x07, 0x0C, num1, num2, 0x00, 0x01]
check = 0
for i in cmd: # 得到校验和
check = check + i
cmd.append(int(check / 256))
cmd.append(int(check % 256))
cmd = self.HEAD + cmd
return self.isOk(cmd, 12)
pass
def psIdentify(self, data=None):
"""自动验证指纹 """
cmd = self.HEAD + [0x01, 0x00, 0x03, 0x11, 0x00, 0x15]
return self.isOk(cmd, 16)
pass
def psStoreChar(self, data=None):
""" 储存模板 到指定位置"""
num1 = int(data / 256)
num2 = int(data % 256)
cmd = [0x01, 0x00, 0x06, 0x6, 0x02, num1, num2]
check = 0
for i in cmd: # 得到校验和
check = check + i
cmd.append(int(check / 256))
cmd.append(int(check % 256))
cmd = self.HEAD + cmd
return self.isOk(cmd, 12)
pass
def getImage(self, data=None):
"""获取图像"""
cmd = self.HEAD + [0x01, 0x00, 0x03, 0x01, 0x00, 0x05] # 发送命令获取内容
return self.isOk(cmd, 12)
pass
def genChar(self, data=None):
"""生成特征文件"""
if data == "1":
cmd = self.HEAD + [0x01, 0x00, 0x04, 0x02, 0x01, 0x00, 0x08] # 发送命令
return self.isOk(cmd, 12) # 图像接收数据 12 大小
elif data == "2":
cmd = self.HEAD + [0x01, 0x00, 0x04, 0x02, 0x02, 0x00, 0x09] # 发送命令
return self.isOk(cmd, 12) # 图像接收数据 12 大小
pass
def match(self, data=None):
"""比较指纹特征"""
cmd = self.HEAD + [0x01, 0x00, 0x03, 0x03, 0x00, 0x07] # 发送命令获取内容
return self.isOk(cmd, 14)
pass
def regModel(self, data=None):
"""合成指纹特征值"""
cmd = self.HEAD + [0x01, 0x00, 0x03, 0x05, 0x00, 0x09] # 发送命令获取内容
return self.isOk(cmd, 12)
pass
def upChar(self, data=None):
"""上传特征模块检测"""
buff = bytes()
if data == "1":
cmd = self.HEAD + [0x01, 0x00, 0x04, 0x08, 0x01, 0x00, 0x0e] # 发送命令
return self.isOk(cmd, 12, False)
elif data == "2":
cmd = self.HEAD + [0x01, 0x00, 0x04, 0x08, 0x02, 0x00, 0x0F] # 发送命令
return self.isOk(cmd, 12, False)
pass
def getUpChar(self, data=None):
"""上传特征模块数据"""
buff = self.ser.read(834)
self.ser.close()
subpackage = DealBuff(buff) # 分割内容
self.tag, self.data, check = subpackage.read()
if check is not True: # 校验和错误
return [], []
return self.tag, self.data
def downCharCheck(self, data=None):
"""
下载特征值检测
先要初始化,发送获取图像命令
"""
self.getImage()
self.link()
cmd = self.HEAD + [0x01, 0x00, 0x04, 0x09, 0x02, 0x00, 0x10] # 发送命令 下载的数据放在buff2中
return self.isOk(cmd, 12)
pass
def downCharData(self, data):
"""下载特征值的数据"""
self.writeRead(data, 0) # 发送数据 接收为0
"""如果不重新断开然后连接会出现卡死:发送的数据丢失"""
self.link()
self.ser.close()
self.link()
self.ser.close()
pass
def upImage(self, data=None):
"""上传图像检测"""
cmd = self.HEAD + [0x01, 0x00, 0x03, 0x0a, 0x00, 0x0e] # 发送命令
return self.isOk(cmd, 12, False)
pass
def getUpImage(self, data=None):
"""获取后续的图像数据"""
buff = self.ser.read(40032)
self.ser.close()
subpackage = DealBuff(buff) # 分割内容
self.tag, self.data, check = subpackage.read()
if check is not True: # 校验和错误
return [], []
return self.tag, self.data
def writeRead(self, cmd, length, close=True):
"""发送命令读取原始字节"""
cmd = bytes(cmd)
self.ser.write(cmd)
buff = self.ser.read(length) # 接收数据
if close:
self.ser.close() # 接受完数据断开com
return buff
pass
def isOk(self, cmd, length, close=True):
"""判断数据是否合格"""
buff = self.writeRead(cmd, length, close) # 图像接收数据 12 大小
subpackage = DealBuff(buff) # 分割内容
self.tag, self.data, check = subpackage.read()
# 检验数据是否可靠
if check is not True: # 校验和错误
return 999
return self.check_code(self.data)
pass
def check_code(self, data):
"""检验指令码"""
return SureCore[data[0]]
pass
pass
if __name__ == "__main__":
# HEAD = [0xEF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF]
# data = [0xEF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x03, 0x0A, 0x00, 0x0E]
# data = HEAD + [0x01, 0x00, 0x03, 0x01, 0x00, 0x05]
# data = HEAD + [0x01, 0x00, 0x03, 0x05, 0x00, 0x09]
# data = HEAD + [0x01, 0x00, 0x04, 0x08, 0x02, 0x00, 0x0f]
‘’’
注意:
我是用Qt写的客户端,用python写的服务器,模拟两个电脑通信(虽然只要一个自己的电脑)
先来一波服务器的代码
里面用到了mySql, 用来存储指纹数据
同时为了上传图像到客户端还要下载numpy, PIL 模块,其中python3中没有PIL 要下pillow
用到的cmd命令 pip install numpy, pip install pillow
Tcpserver.py
下面是服务器可能用到的
dealFile.py
服务器完成后就可以写客户端端了。(其实是两个同时进行然后相互补充)
下面是Qt代码
主要是通讯,其他的界面其实都还好,就按照自己的想法
Qt客户端 .cpp文件
客户端头文件 .h
3.然后就是数据库储存了
就是一些简单的mysql语句
dealSql.py
4.可以用Pycharm远程调试树莓派啦
网上有很多远程调试的例子,如果还是不会可以留言
首先用编写打卡的程序,用到树莓派GPIO口
clockIn.py
当能够正常打卡后就需要在液晶屏上显示了
oledshow.py
5.怎么实现物联呢
这就要用到公网平台了,应为我们都是“私网”,
刚开始想用yeelink的,但是发现好像没人维护用不了了,我就发现了“乐为物联”这个平台,操作简单
你首先得在上面注册一个用户,他上面有api的介绍,很容易看懂
再分享一波
我是想在微信上发送命令
weChat.py
如果你不想在注册或者嫌麻烦也可以通过邮箱来发送命令
邮箱的代码在下面
emailSendReceive.py
6.最后看看主程序把
分别写两个脚本运行就好了
fingerStart.py
weChatClient.py
逻辑结构图如下