Skip to content

library expansion and compatibility with other ZhianTec fp sensors #11

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 23 commits into from
Feb 5, 2020
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
b7639f9
hack for other compatible fp sensors
stitchesnburns Feb 12, 2019
358c49a
added reset library functionality
stitchesnburns Feb 12, 2019
9e3722e
Merge pull request #2 from stitchesnburns/stitchesnburns-patch-2
stitchesnburns Feb 12, 2019
3ef2398
fixed travis build failure
stitchesnburns Feb 12, 2019
c874c42
created an empty_lilbrary() function
stitchesnburns Feb 12, 2019
c17ed41
created an empty_library() function
stitchesnburns Feb 12, 2019
98cd938
Update adafruit_fingerprint.py
stitchesnburns Feb 12, 2019
a04f55f
Update fingerprint_simpletest.py
stitchesnburns Feb 12, 2019
2a94c93
Update fingerprint_simpletest.py
stitchesnburns Feb 12, 2019
667b492
implemented additional functionalities
stitchesnburns Feb 22, 2019
4971ed2
some fixes
stitchesnburns Feb 22, 2019
9c4034d
some more fixes
stitchesnburns Feb 22, 2019
1e343c3
finger_fast_search() based on module's capacity
stitchesnburns Feb 26, 2019
ef52314
fixed read_templates() rounding up
stitchesnburns Mar 4, 2019
425f4be
Merge pull request #3 from stitchesnburns/stitchesnburns-patch-1-1
stitchesnburns Mar 4, 2019
9513b83
changed 'buffer' variable to 'sensorbuffer'
stitchesnburns Mar 5, 2019
d117944
fixed bug when sending last packet of data to fp sensor
stitchesnburns Mar 15, 2019
8291727
code clean up
stitchesnburns Mar 20, 2019
fd74ea7
Merge branch 'master' into stitchesnburns-patch-1
stitchesnburns Jan 9, 2020
263b435
Reverted default example
stitchesnburns Jan 9, 2020
a03e236
Added another example based on the default one
stitchesnburns Jan 9, 2020
0e3ac3c
updated to comply with pylint
stitchesnburns Jan 9, 2020
ff628d1
expanded the example to include saving fingerprint image
stitchesnburns Jan 14, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
238 changes: 226 additions & 12 deletions adafruit_fingerprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,17 @@
_STORE = const(0x06)
_LOAD = const(0x07)
_UPLOAD = const(0x08)
_DOWNLOAD = const(0x09)
_UPLOADIMAGE = const(0x0A)
_DOWNLOADIMAGE = const(0x0B)
_DELETE = const(0x0C)
_EMPTY = const(0x0D)
_READSYSPARA = const(0x0F)
_HISPEEDSEARCH = const(0x1B)
_VERIFYPASSWORD = const(0x13)
_TEMPLATECOUNT = const(0x1D)
_TEMPLATEREAD = const(0x1F)
_GETECHO = const(0x53)

# Packet error code
OK = const(0x0)
Expand All @@ -92,6 +97,7 @@
INVALIDREG = const(0x1A)
ADDRCODE = const(0x20)
PASSVERIFY = const(0x21)
MODULEOK = const(0x55)

class Adafruit_Fingerprint:
"""UART based fingerprint sensor."""
Expand All @@ -103,6 +109,11 @@ class Adafruit_Fingerprint:
confidence = None
templates = []
template_count = None
library_size = None
security_level = None
device_address = None
data_packet_size = None
baudrate = None

def __init__(self, uart, passwd=(0, 0, 0, 0)):
# Create object with UART for interface, and default 32-bit password
Expand All @@ -111,6 +122,14 @@ def __init__(self, uart, passwd=(0, 0, 0, 0)):
if self.verify_password() != OK:
raise RuntimeError('Failed to find sensor, check wiring!')

def check_module(self):
"""Checks the state of the fingerprint scanner module.
Returns OK or error."""
self._send_packet([_GETECHO])
if self._get_packet(12)[0] != MODULEOK:
raise RuntimeError('Something is wrong with the sensor.')
return True

def verify_password(self):
"""Checks if the password/connection is correct, returns True/False"""
self._send_packet([_VERIFYPASSWORD] + list(self.password))
Expand All @@ -124,13 +143,26 @@ def count_templates(self):
self.template_count = struct.unpack('>H', bytes(r[1:3]))[0]
return r[0]

def read_sysparam(self):
"""Returns the system parameters on success via attributes."""
self._send_packet([_READSYSPARA])
r = self._get_packet(28)
if r[0] != OK:
raise RuntimeError('Command failed.')
self.library_size = struct.unpack('>H', bytes(r[5:7]))[0]
self.security_level = struct.unpack('>H', bytes(r[7:9]))[0]
self.device_address = bytes(r[9:13])
self.data_packet_size = struct.unpack('>H', bytes(r[13:15]))[0]
self.baudrate = struct.unpack('>H', bytes(r[15:17]))[0]
return r[0]

def get_image(self):
"""Requests the sensor to take an image and store it memory, returns
the packet error code or OK success"""
self._send_packet([_GETIMAGE])
return self._get_packet(12)[0]

def image_2_tz(self, slot):
def image_2_tz(self, slot=1):
"""Requests the sensor convert the image to a template, returns
the packet error code or OK success"""
self._send_packet([_IMAGE2TZ, slot])
Expand All @@ -142,10 +174,10 @@ def create_model(self):
self._send_packet([_REGMODEL])
return self._get_packet(12)[0]

def store_model(self, location):
def store_model(self, location, slot=1):
"""Requests the sensor store the model into flash memory and assign
a location. Returns the packet error code or OK success"""
self._send_packet([_STORE, 1, location >> 8, location & 0xFF])
self._send_packet([_STORE, slot, location >> 8, location & 0xFF])
return self._get_packet(12)[0]

def delete_model(self, location):
Expand All @@ -154,27 +186,92 @@ def delete_model(self, location):
self._send_packet([_DELETE, location >> 8, location & 0xFF, 0x00, 0x01])
return self._get_packet(12)[0]

def load_model(self, location, slot=1):
"""Requests the sensor to load a model from the given memory location
to the given slot. Returns the packet error code or success"""
self._send_packet([_LOAD, slot, location >> 8, location & 0xFF])
return self._get_packet(12)[0]

def get_fpdata(self, sensorbuffer='char', slot=1):
"""Requests the sensor to transfer the fingerprint image or
template. Returns the data payload only."""
if slot != 1 or slot != 2:
# raise error or use default value?
slot = 2
if sensorbuffer == 'image':
self._send_packet([_UPLOADIMAGE])
elif sensorbuffer == 'char':
self._send_packet([_UPLOAD, slot])
else:
raise RuntimeError('Uknown sensor buffer type')
if self._get_packet(12)[0] == 0:
res = self._get_data(9)
# print('datasize: ' + str(len(res)))
# print(res)
return res

def send_fpdata(self, data, sensorbuffer='char', slot=1):
"""Requests the sensor to receive data, either a fingerprint image or
a character/template data. Data is the payload only."""
if slot != 1 or slot != 2:
# raise error or use default value?
slot = 2
if sensorbuffer == 'image':
self._send_packet([_DOWNLOADIMAGE])
elif sensorbuffer == 'char':
self._send_packet([_DOWNLOAD, slot])
else:
raise RuntimeError('Uknown sensor buffer type')
if self._get_packet(12)[0] == 0:
self._send_data(data)
# print('datasize: ' + str(len(res)))
# print(res)
return True

def empty_library(self):
"""Requests the sensor to delete all models from flash memory.
Returns the packet error code or OK success"""
self._send_packet([_EMPTY])
return self._get_packet(12)[0]

def read_templates(self):
"""Requests the sensor to list of all template locations in use and
stores them in self.templates. Returns the packet error code or OK success"""
self._send_packet([_TEMPLATEREAD, 0x00])
r = self._get_packet(44)
stores them in self.templates. Returns the packet error code or
OK success"""
import math
self.templates = []
for i in range(32):
byte = r[i+1]
for bit in range(8):
if byte & (1 << bit):
self.templates.append(i * 8 + bit)
self.read_sysparam()
temp_r = [0x0c, ]
for j in range(math.ceil(self.library_size/256)):
self._send_packet([_TEMPLATEREAD, j])
r = self._get_packet(44)
if r[0] == OK:
for i in range(32):
byte = r[i+1]
for bit in range(8):
if byte & (1 << bit):
self.templates.append((i * 8) + bit + (j * 256))
temp_r = r
else:
r = temp_r
return r[0]

def finger_fast_search(self):
"""Asks the sensor to search for a matching fingerprint template to the
last model generated. Stores the location and confidence in self.finger_id
and self.confidence. Returns the packet error code or OK success"""
# high speed search of slot #1 starting at page 0x0000 and page #0x00A3
self._send_packet([_HISPEEDSEARCH, 0x01, 0x00, 0x00, 0x00, 0xA3])
#self._send_packet([_HISPEEDSEARCH, 0x01, 0x00, 0x00, 0x00, 0xA3])
# or page #0x03E9 to accommodate modules with up to 1000 capacity
#self._send_packet([_HISPEEDSEARCH, 0x01, 0x00, 0x00, 0x03, 0xE9])
# or base the page on module's capacity
self.read_sysparam()
capacity = self.library_size
self._send_packet([_HISPEEDSEARCH, 0x01, 0x00, 0x00, capacity >> 8,
capacity & 0xFF])
r = self._get_packet(16)
self.finger_id, self.confidence = struct.unpack('>HH', bytes(r[1:5]))
# print(r)
return r[0]

##################################################
Expand All @@ -201,10 +298,60 @@ def _get_packet(self, expected):
if packet_type != _ACKPACKET:
raise RuntimeError('Incorrect packet data')

# we should check the checksum
# but i don't know how
# not yet anyway
#packet_sum = struct.unpack('>H', res[9+(length-2):9+length])[0]
#print(packet_sum)
#print(packet_type + length + struct.unpack('>HHHH', res[9:9+(length-2)]))

reply = [i for i in res[9:9+(length-2)]]
#print(reply)
return reply

def _get_data(self, expected):
""" Gets packet from serial and checks structure for _DATAPACKET
and _ENDDATAPACKET. Alternate method for getting data such
as fingerprint image, etc. Returns the data payload."""
res = self._uart.read(expected)
if (not res) or (len(res) != expected):
raise RuntimeError('Failed to read data from sensor')

# first two bytes are start code
start = struct.unpack('>H', res[0:2])[0]
# print(start)
if start != _STARTCODE:
raise RuntimeError('Incorrect packet data')
# next 4 bytes are address
addr = [i for i in res[2:6]]
# print(addr)
if addr != self.address:
raise RuntimeError('Incorrect address')

packet_type, length = struct.unpack('>BH', res[6:9])
#print(str(packet_type) + ' ' + str(length))

# todo: check checksum

if packet_type != _DATAPACKET:
if packet_type != _ENDDATAPACKET:
raise RuntimeError('Incorrect packet data')

if packet_type == _DATAPACKET:
res = self._uart.read(length-2)
# todo: we should really inspect the headers and checksum
reply = [i for i in res[0:length]]
self._uart.read(2) # disregard checksum but we really shouldn't
reply += self._get_data(9)
elif packet_type == _ENDDATAPACKET:
res = self._uart.read(length-2)
# todo: we should really inspect the headers and checksum
reply = [i for i in res[0:length]]
self._uart.read(2) # disregard checksum but we really shouldn't
# print(len(reply))
# print(reply)
return reply

def _send_packet(self, data):
packet = [_STARTCODE >> 8, _STARTCODE & 0xFF]
packet = packet + self.address
Expand All @@ -222,3 +369,70 @@ def _send_packet(self, data):

#print("Sending: ", [hex(i) for i in packet])
self._uart.write(bytearray(packet))

def _send_data(self, data):
print(len(data))
self.read_sysparam()
if self.data_packet_size == 0:
data_length = 32
elif self.data_packet_size == 1:
data_length = 64
elif self.data_packet_size == 2:
data_length = 128
elif self.data_packet_size == 3:
data_length = 256

i = 0
for i in range(int(len(data) / (data_length - 2))):
start = i * (data_length - 2)
end = (i + 1) * (data_length - 2)
# print(start)
# print(end)
# print(i)

packet = [_STARTCODE >> 8, _STARTCODE & 0xFF]
packet = packet + self.address
packet.append(_DATAPACKET)
length = len(data[start:end]) + 2
# print(length)
packet.append(length >> 8)
packet.append(length & 0xFF)
checksum = _DATAPACKET + (length >> 8) + (length & 0xFF)

for j in range(len(data[start:end])):
packet.append(data[j])
checksum += data[j]

packet.append(checksum >> 8)
packet.append(checksum & 0xFF)

# print("Sending: ", [hex(i) for i in packet])
self._uart.write(packet)
# print(i)

i += 1
start = i * (data_length - 2)
end = (i + 1) * (data_length - 2)
# print(start)
# print(end)
# print(i)

packet = [_STARTCODE >> 8, _STARTCODE & 0xFF]
packet = packet + self.address
packet.append(_ENDDATAPACKET)
length = len(data[start:end]) + 2
# print(length)
packet.append(length >> 8)
packet.append(length & 0xFF)
checksum = _ENDDATAPACKET + (length >> 8) + (length & 0xFF)

for j in range(len(data[start:end])):
packet.append(data[j])
checksum += data[j]

packet.append(checksum >> 8)
packet.append(checksum & 0xFF)

# print("Sending: ", [hex(i) for i in packet])
self._uart.write(packet)
# print(i)
25 changes: 20 additions & 5 deletions examples/fingerprint_simpletest.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import time
import board
import busio
#import busio
from digitalio import DigitalInOut, Direction
import adafruit_fingerprint
import serial

led = DigitalInOut(board.D13)
led.direction = Direction.OUTPUT

uart = busio.UART(board.TX, board.RX, baudrate=57600)
#uart = busio.UART(board.TX, board.RX, baudrate=57600)

# If using with a computer such as Linux/RaspberryPi, Mac, Windows...
#import serial
#uart = serial.Serial("/dev/ttyUSB0", baudrate=57600, timeout=1)
uart = serial.Serial("/dev/ttyAMA0", baudrate=57600, timeout=1)

finger = adafruit_fingerprint.Adafruit_Fingerprint(uart)

Expand Down Expand Up @@ -152,10 +154,13 @@ def enroll_finger(location):

def get_num():
"""Use input() to get a valid number from 1 to 127. Retry till success!"""
i = 0
while (i > 127) or (i < 1):
#i = 0
i = -1
#while (i > 127) or (i < 1):
while (i > 999) or (i < 0):
try:
i = int(input("Enter ID # from 1-127: "))
#i = int(input("Enter ID # from 1-127: "))
i = int(input("Enter ID # from 0-999: "))
except ValueError:
pass
return i
Expand All @@ -166,9 +171,14 @@ def get_num():
if finger.read_templates() != adafruit_fingerprint.OK:
raise RuntimeError('Failed to read templates')
print("Fingerprint templates:", finger.templates)
if finger.count_templates() != adafruit_fingerprint.OK:
raise RuntimeError('Failed to read templates')
print("Number of templates: ", finger.template_count)
print("e) enroll print")
print("e) enroll print")
print("f) find print")
print("d) delete print")
print("r) reset library")
print("----------------")
c = input("> ")

Expand All @@ -184,3 +194,8 @@ def get_num():
print("Deleted!")
else:
print("Failed to delete")
if c == 'r':
if finger.empty_library() == adafruit_fingerprint.OK:
print("Library empty!")
else:
print("Failed to empty library")