树莓派:基于物联网的指纹打卡器

今天我就分享一波自己的成果。

心心念念的想做一个物联网的东西出来,终于在上个月搞定了,虽说肯定是有些漏洞(目前我是没有找出来的),但是效果看起来还不错。学了这么久的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

先上个最终成果的图

树莓派:基于物联网的指纹打卡器_第1张图片
树莓派:基于物联网的指纹打卡器_第2张图片
树莓派:基于物联网的指纹打卡器_第3张图片树莓派:基于物联网的指纹打卡器_第4张图片

树莓派:基于物联网的指纹打卡器_第5张图片
看起来感觉还可以,基本完成了电脑和手机都可以获得日志的效果

大家最期待的过程了

现在我来说是我的想法和思路

刚开始看到隔壁实验室的指纹打卡机时好像是那种用usb读取数据的(好像是节约成本吧),我就想能这也太不智能了吧,就想着能不能让它智能些,比如通过电脑看到它的日志啊,或者用手机看到它的日志啊什么的(后来我才知道淘宝上TM已经出现这种完善的成品指纹考勤机了)。光想没用啊,还是要靠实践来检验真理。于是我就想到了手上的树莓派,想想就有些兴奋,可以向网上那些大佬一样DIY一个东西出来了,由于我的知识有限,无法自己搞个指纹打卡模块,就到网上买了一个,先开始买的正点原子的指纹模块,原子哥不愧是我原子哥,和他讨论问题时真是尽心尽力。文档很好,不过他那个和模块通信的程序不是很全,就几个,可以能是直接储存在模块里的缘故,可是我不想储存在模块里啊,我学了Python加上Mysql,我想直接存在数据库里,这样更加安全可靠。于是我就开始解决通信问题,将他发回来的数据解码,捣鼓了2天才完成,有点心酸。

先把配置文件分享了,下面代码会用到

用户root 给电脑连接的

[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

smtpServer 发送请求服务

popServer 接收请求服务

smtpSrcAddr 服务端的地址

smtpSrcAddrPwd 服务端的密码

smtpDstAddr 发送的目的邮箱

smtpPort 端口号 qq端口号为 465

[email]
smtpServer = smtp.qq.com
popServer = pop.qq.com
smtpSrcAddr = 发送请求的你的邮箱
smtpSrcAddrPwd = 允许的密码
smtpDstAddr = 目的邮箱
smtpPort = 465

TCP服务

[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

1.首先要在电脑上调通指纹发送数据的所有命令

分享一波代码

要用电脑和指纹模块通信首先要装

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]

‘’’
注意:

1.可以先在串口调试助手上输入命令,看看数据是怎么样子的然后在分析数据,最后写程序处理数据

2.按照说明书提示最好不要改 模块的地址和密码,不然可能会成为砖头

2.非常重要!!!本人是吃了亏的,真是日了狗, 指纹模块中有个设置波特率的,说明书上是说波特率可以设置为9600倍数,其实并不是这样的,我先开始觉得速度太慢了就设置了先开始是12倍,觉得可以用还是太慢,就设置了24倍,还是可以用,但是还是慢,我一冲动就设置成了96倍,我的个乖乖,然后就是不管怎么唤它都没反应,为此我还和原子哥讨论了很久,结果就是这东西成板砖了。 然后不得已就又买了个便宜的(原子哥虽然服务好,技术支持好,就是东西太贵了,弄懂原理了就不用再买贵的了)。

下面的代码我就不贴出来了太多了还无法折叠可以去下载源程序

2.就是写一个服务器和客户端啦

我是用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
逻辑结构图如下

树莓派:基于物联网的指纹打卡器_第6张图片

你可能感兴趣的:(树莓派:基于物联网的指纹打卡器)