Skip to content

Commit e68d121

Browse files
authored
Merge pull request #11 from stitchesnburns/stitchesnburns-patch-1
library expansion and compatibility with other ZhianTec fp sensors
2 parents f60c5fa + ff628d1 commit e68d121

File tree

2 files changed

+479
-12
lines changed

2 files changed

+479
-12
lines changed

adafruit_fingerprint.py

Lines changed: 226 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,17 @@
6262
_STORE = const(0x06)
6363
_LOAD = const(0x07)
6464
_UPLOAD = const(0x08)
65+
_DOWNLOAD = const(0x09)
66+
_UPLOADIMAGE = const(0x0A)
67+
_DOWNLOADIMAGE = const(0x0B)
6568
_DELETE = const(0x0C)
6669
_EMPTY = const(0x0D)
70+
_READSYSPARA = const(0x0F)
6771
_HISPEEDSEARCH = const(0x1B)
6872
_VERIFYPASSWORD = const(0x13)
6973
_TEMPLATECOUNT = const(0x1D)
7074
_TEMPLATEREAD = const(0x1F)
75+
_GETECHO = const(0x53)
7176

7277
# Packet error code
7378
OK = const(0x0)
@@ -92,6 +97,7 @@
9297
INVALIDREG = const(0x1A)
9398
ADDRCODE = const(0x20)
9499
PASSVERIFY = const(0x21)
100+
MODULEOK = const(0x55)
95101

96102
class Adafruit_Fingerprint:
97103
"""UART based fingerprint sensor."""
@@ -103,6 +109,11 @@ class Adafruit_Fingerprint:
103109
confidence = None
104110
templates = []
105111
template_count = None
112+
library_size = None
113+
security_level = None
114+
device_address = None
115+
data_packet_size = None
116+
baudrate = None
106117

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

125+
def check_module(self):
126+
"""Checks the state of the fingerprint scanner module.
127+
Returns OK or error."""
128+
self._send_packet([_GETECHO])
129+
if self._get_packet(12)[0] != MODULEOK:
130+
raise RuntimeError('Something is wrong with the sensor.')
131+
return True
132+
114133
def verify_password(self):
115134
"""Checks if the password/connection is correct, returns True/False"""
116135
self._send_packet([_VERIFYPASSWORD] + list(self.password))
@@ -124,13 +143,26 @@ def count_templates(self):
124143
self.template_count = struct.unpack('>H', bytes(r[1:3]))[0]
125144
return r[0]
126145

146+
def read_sysparam(self):
147+
"""Returns the system parameters on success via attributes."""
148+
self._send_packet([_READSYSPARA])
149+
r = self._get_packet(28)
150+
if r[0] != OK:
151+
raise RuntimeError('Command failed.')
152+
self.library_size = struct.unpack('>H', bytes(r[5:7]))[0]
153+
self.security_level = struct.unpack('>H', bytes(r[7:9]))[0]
154+
self.device_address = bytes(r[9:13])
155+
self.data_packet_size = struct.unpack('>H', bytes(r[13:15]))[0]
156+
self.baudrate = struct.unpack('>H', bytes(r[15:17]))[0]
157+
return r[0]
158+
127159
def get_image(self):
128160
"""Requests the sensor to take an image and store it memory, returns
129161
the packet error code or OK success"""
130162
self._send_packet([_GETIMAGE])
131163
return self._get_packet(12)[0]
132164

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

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

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

189+
def load_model(self, location, slot=1):
190+
"""Requests the sensor to load a model from the given memory location
191+
to the given slot. Returns the packet error code or success"""
192+
self._send_packet([_LOAD, slot, location >> 8, location & 0xFF])
193+
return self._get_packet(12)[0]
194+
195+
def get_fpdata(self, sensorbuffer='char', slot=1):
196+
"""Requests the sensor to transfer the fingerprint image or
197+
template. Returns the data payload only."""
198+
if slot != 1 or slot != 2:
199+
# raise error or use default value?
200+
slot = 2
201+
if sensorbuffer == 'image':
202+
self._send_packet([_UPLOADIMAGE])
203+
elif sensorbuffer == 'char':
204+
self._send_packet([_UPLOAD, slot])
205+
else:
206+
raise RuntimeError('Uknown sensor buffer type')
207+
if self._get_packet(12)[0] == 0:
208+
res = self._get_data(9)
209+
# print('datasize: ' + str(len(res)))
210+
# print(res)
211+
return res
212+
213+
def send_fpdata(self, data, sensorbuffer='char', slot=1):
214+
"""Requests the sensor to receive data, either a fingerprint image or
215+
a character/template data. Data is the payload only."""
216+
if slot != 1 or slot != 2:
217+
# raise error or use default value?
218+
slot = 2
219+
if sensorbuffer == 'image':
220+
self._send_packet([_DOWNLOADIMAGE])
221+
elif sensorbuffer == 'char':
222+
self._send_packet([_DOWNLOAD, slot])
223+
else:
224+
raise RuntimeError('Uknown sensor buffer type')
225+
if self._get_packet(12)[0] == 0:
226+
self._send_data(data)
227+
# print('datasize: ' + str(len(res)))
228+
# print(res)
229+
return True
230+
231+
def empty_library(self):
232+
"""Requests the sensor to delete all models from flash memory.
233+
Returns the packet error code or OK success"""
234+
self._send_packet([_EMPTY])
235+
return self._get_packet(12)[0]
236+
157237
def read_templates(self):
158238
"""Requests the sensor to list of all template locations in use and
159-
stores them in self.templates. Returns the packet error code or OK success"""
160-
self._send_packet([_TEMPLATEREAD, 0x00])
161-
r = self._get_packet(44)
239+
stores them in self.templates. Returns the packet error code or
240+
OK success"""
241+
import math
162242
self.templates = []
163-
for i in range(32):
164-
byte = r[i+1]
165-
for bit in range(8):
166-
if byte & (1 << bit):
167-
self.templates.append(i * 8 + bit)
243+
self.read_sysparam()
244+
temp_r = [0x0c, ]
245+
for j in range(math.ceil(self.library_size/256)):
246+
self._send_packet([_TEMPLATEREAD, j])
247+
r = self._get_packet(44)
248+
if r[0] == OK:
249+
for i in range(32):
250+
byte = r[i+1]
251+
for bit in range(8):
252+
if byte & (1 << bit):
253+
self.templates.append((i * 8) + bit + (j * 256))
254+
temp_r = r
255+
else:
256+
r = temp_r
168257
return r[0]
169258

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

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

301+
# we should check the checksum
302+
# but i don't know how
303+
# not yet anyway
304+
#packet_sum = struct.unpack('>H', res[9+(length-2):9+length])[0]
305+
#print(packet_sum)
306+
#print(packet_type + length + struct.unpack('>HHHH', res[9:9+(length-2)]))
307+
204308
reply = [i for i in res[9:9+(length-2)]]
205309
#print(reply)
206310
return reply
207311

312+
def _get_data(self, expected):
313+
""" Gets packet from serial and checks structure for _DATAPACKET
314+
and _ENDDATAPACKET. Alternate method for getting data such
315+
as fingerprint image, etc. Returns the data payload."""
316+
res = self._uart.read(expected)
317+
if (not res) or (len(res) != expected):
318+
raise RuntimeError('Failed to read data from sensor')
319+
320+
# first two bytes are start code
321+
start = struct.unpack('>H', res[0:2])[0]
322+
# print(start)
323+
if start != _STARTCODE:
324+
raise RuntimeError('Incorrect packet data')
325+
# next 4 bytes are address
326+
addr = [i for i in res[2:6]]
327+
# print(addr)
328+
if addr != self.address:
329+
raise RuntimeError('Incorrect address')
330+
331+
packet_type, length = struct.unpack('>BH', res[6:9])
332+
#print(str(packet_type) + ' ' + str(length))
333+
334+
# todo: check checksum
335+
336+
if packet_type != _DATAPACKET:
337+
if packet_type != _ENDDATAPACKET:
338+
raise RuntimeError('Incorrect packet data')
339+
340+
if packet_type == _DATAPACKET:
341+
res = self._uart.read(length-2)
342+
# todo: we should really inspect the headers and checksum
343+
reply = [i for i in res[0:length]]
344+
self._uart.read(2) # disregard checksum but we really shouldn't
345+
reply += self._get_data(9)
346+
elif packet_type == _ENDDATAPACKET:
347+
res = self._uart.read(length-2)
348+
# todo: we should really inspect the headers and checksum
349+
reply = [i for i in res[0:length]]
350+
self._uart.read(2) # disregard checksum but we really shouldn't
351+
# print(len(reply))
352+
# print(reply)
353+
return reply
354+
208355
def _send_packet(self, data):
209356
packet = [_STARTCODE >> 8, _STARTCODE & 0xFF]
210357
packet = packet + self.address
@@ -222,3 +369,70 @@ def _send_packet(self, data):
222369

223370
#print("Sending: ", [hex(i) for i in packet])
224371
self._uart.write(bytearray(packet))
372+
373+
def _send_data(self, data):
374+
print(len(data))
375+
self.read_sysparam()
376+
if self.data_packet_size == 0:
377+
data_length = 32
378+
elif self.data_packet_size == 1:
379+
data_length = 64
380+
elif self.data_packet_size == 2:
381+
data_length = 128
382+
elif self.data_packet_size == 3:
383+
data_length = 256
384+
385+
i = 0
386+
for i in range(int(len(data) / (data_length - 2))):
387+
start = i * (data_length - 2)
388+
end = (i + 1) * (data_length - 2)
389+
# print(start)
390+
# print(end)
391+
# print(i)
392+
393+
packet = [_STARTCODE >> 8, _STARTCODE & 0xFF]
394+
packet = packet + self.address
395+
packet.append(_DATAPACKET)
396+
length = len(data[start:end]) + 2
397+
# print(length)
398+
packet.append(length >> 8)
399+
packet.append(length & 0xFF)
400+
checksum = _DATAPACKET + (length >> 8) + (length & 0xFF)
401+
402+
for j in range(len(data[start:end])):
403+
packet.append(data[j])
404+
checksum += data[j]
405+
406+
packet.append(checksum >> 8)
407+
packet.append(checksum & 0xFF)
408+
409+
# print("Sending: ", [hex(i) for i in packet])
410+
self._uart.write(packet)
411+
# print(i)
412+
413+
i += 1
414+
start = i * (data_length - 2)
415+
end = (i + 1) * (data_length - 2)
416+
# print(start)
417+
# print(end)
418+
# print(i)
419+
420+
packet = [_STARTCODE >> 8, _STARTCODE & 0xFF]
421+
packet = packet + self.address
422+
packet.append(_ENDDATAPACKET)
423+
length = len(data[start:end]) + 2
424+
# print(length)
425+
packet.append(length >> 8)
426+
packet.append(length & 0xFF)
427+
checksum = _ENDDATAPACKET + (length >> 8) + (length & 0xFF)
428+
429+
for j in range(len(data[start:end])):
430+
packet.append(data[j])
431+
checksum += data[j]
432+
433+
packet.append(checksum >> 8)
434+
packet.append(checksum & 0xFF)
435+
436+
# print("Sending: ", [hex(i) for i in packet])
437+
self._uart.write(packet)
438+
# print(i)

0 commit comments

Comments
 (0)