├── .gitattributes ├── .github ├── PULL_REQUEST_TEMPLATE │ └── adafruit_circuitpython_pr.md └── workflows │ ├── build.yml │ ├── failure-help-text.yml │ ├── release_gh.yml │ └── release_pypi.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .readthedocs.yaml ├── CODE_OF_CONDUCT.md ├── LICENSE ├── LICENSES ├── CC-BY-4.0.txt ├── MIT.txt └── Unlicense.txt ├── README.rst ├── README.rst.license ├── adafruit_fingerprint.py ├── docs ├── _static │ ├── favicon.ico │ └── favicon.ico.license ├── api.rst ├── api.rst.license ├── conf.py ├── examples.rst ├── examples.rst.license ├── index.rst ├── index.rst.license └── requirements.txt ├── examples ├── fingerprint_picturetest.py ├── fingerprint_r503.py ├── fingerprint_r503_rpi.py ├── fingerprint_simpletest.py ├── fingerprint_simpletest_rpi.py ├── fingerprint_template_file_compare.py └── fingerprint_template_folder_compare_with_file_rpi.py ├── optional_requirements.txt ├── pyproject.toml ├── requirements.txt └── ruff.toml /.gitattributes: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2024 Justin Myers for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: Unlicense 4 | 5 | .py text eol=lf 6 | .rst text eol=lf 7 | .txt text eol=lf 8 | .yaml text eol=lf 9 | .toml text eol=lf 10 | .license text eol=lf 11 | .md text eol=lf 12 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | Thank you for contributing! Before you submit a pull request, please read the following. 6 | 7 | Make sure any changes you're submitting are in line with the CircuitPython Design Guide, available here: https://docs.circuitpython.org/en/latest/docs/design_guide.html 8 | 9 | If your changes are to documentation, please verify that the documentation builds locally by following the steps found here: https://adafru.it/build-docs 10 | 11 | Before submitting the pull request, make sure you've run Pylint and Black locally on your code. You can do this manually or using pre-commit. Instructions are available here: https://adafru.it/check-your-code 12 | 13 | Please remove all of this text before submitting. Include an explanation or list of changes included in your PR, as well as, if applicable, a link to any related issues. 14 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | name: Build CI 6 | 7 | on: [pull_request, push] 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Run Build CI workflow 14 | uses: adafruit/workflows-circuitpython-libs/build@main 15 | -------------------------------------------------------------------------------- /.github/workflows/failure-help-text.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Scott Shawcroft for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | name: Failure help text 6 | 7 | on: 8 | workflow_run: 9 | workflows: ["Build CI"] 10 | types: 11 | - completed 12 | 13 | jobs: 14 | post-help: 15 | runs-on: ubuntu-latest 16 | if: ${{ github.event.workflow_run.conclusion == 'failure' && github.event.workflow_run.event == 'pull_request' }} 17 | steps: 18 | - name: Post comment to help 19 | uses: adafruit/circuitpython-action-library-ci-failed@v1 20 | -------------------------------------------------------------------------------- /.github/workflows/release_gh.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | name: GitHub Release Actions 6 | 7 | on: 8 | release: 9 | types: [published] 10 | 11 | jobs: 12 | upload-release-assets: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Run GitHub Release CI workflow 16 | uses: adafruit/workflows-circuitpython-libs/release-gh@main 17 | with: 18 | github-token: ${{ secrets.GITHUB_TOKEN }} 19 | upload-url: ${{ github.event.release.upload_url }} 20 | -------------------------------------------------------------------------------- /.github/workflows/release_pypi.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | name: PyPI Release Actions 6 | 7 | on: 8 | release: 9 | types: [published] 10 | 11 | jobs: 12 | upload-release-assets: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Run PyPI Release CI workflow 16 | uses: adafruit/workflows-circuitpython-libs/release-pypi@main 17 | with: 18 | pypi-username: ${{ secrets.pypi_username }} 19 | pypi-password: ${{ secrets.pypi_password }} 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2022 Kattni Rembor, written for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | # Do not include files and directories created by your personal work environment, such as the IDE 6 | # you use, except for those already listed here. Pull requests including changes to this file will 7 | # not be accepted. 8 | 9 | # This .gitignore file contains rules for files generated by working with CircuitPython libraries, 10 | # including building Sphinx, testing with pip, and creating a virual environment, as well as the 11 | # MacOS and IDE-specific files generated by using MacOS in general, or the PyCharm or VSCode IDEs. 12 | 13 | # If you find that there are files being generated on your machine that should not be included in 14 | # your git commit, you should create a .gitignore_global file on your computer to include the 15 | # files created by your personal setup. To do so, follow the two steps below. 16 | 17 | # First, create a file called .gitignore_global somewhere convenient for you, and add rules for 18 | # the files you want to exclude from git commits. 19 | 20 | # Second, configure Git to use the exclude file for all Git repositories by running the 21 | # following via commandline, replacing "path/to/your/" with the actual path to your newly created 22 | # .gitignore_global file: 23 | # git config --global core.excludesfile path/to/your/.gitignore_global 24 | 25 | # CircuitPython-specific files 26 | *.mpy 27 | 28 | # Python-specific files 29 | __pycache__ 30 | *.pyc 31 | 32 | # Sphinx build-specific files 33 | _build 34 | 35 | # This file results from running `pip -e install .` in a local repository 36 | *.egg-info 37 | 38 | # Virtual environment-specific files 39 | .env 40 | .venv 41 | 42 | # MacOS-specific files 43 | *.DS_Store 44 | 45 | # IDE-specific files 46 | .idea 47 | .vscode 48 | *~ 49 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2024 Justin Myers for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: Unlicense 4 | 5 | repos: 6 | - repo: https://github.com/pre-commit/pre-commit-hooks 7 | rev: v4.5.0 8 | hooks: 9 | - id: check-yaml 10 | - id: end-of-file-fixer 11 | - id: trailing-whitespace 12 | - repo: https://github.com/astral-sh/ruff-pre-commit 13 | rev: v0.3.4 14 | hooks: 15 | - id: ruff-format 16 | - id: ruff 17 | args: ["--fix"] 18 | - repo: https://github.com/fsfe/reuse-tool 19 | rev: v3.0.1 20 | hooks: 21 | - id: reuse 22 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: Unlicense 4 | 5 | # Read the Docs configuration file 6 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 7 | 8 | # Required 9 | version: 2 10 | 11 | sphinx: 12 | configuration: docs/conf.py 13 | 14 | build: 15 | os: ubuntu-20.04 16 | tools: 17 | python: "3" 18 | 19 | python: 20 | install: 21 | - requirements: docs/requirements.txt 22 | - requirements: requirements.txt 23 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | # Adafruit Community Code of Conduct 8 | 9 | ## Our Pledge 10 | 11 | In the interest of fostering an open and welcoming environment, we as 12 | contributors and leaders pledge to making participation in our project and 13 | our community a harassment-free experience for everyone, regardless of age, body 14 | size, disability, ethnicity, gender identity and expression, level or type of 15 | experience, education, socio-economic status, nationality, personal appearance, 16 | race, religion, or sexual identity and orientation. 17 | 18 | ## Our Standards 19 | 20 | We are committed to providing a friendly, safe and welcoming environment for 21 | all. 22 | 23 | Examples of behavior that contributes to creating a positive environment 24 | include: 25 | 26 | * Be kind and courteous to others 27 | * Using welcoming and inclusive language 28 | * Being respectful of differing viewpoints and experiences 29 | * Collaborating with other community members 30 | * Gracefully accepting constructive criticism 31 | * Focusing on what is best for the community 32 | * Showing empathy towards other community members 33 | 34 | Examples of unacceptable behavior by participants include: 35 | 36 | * The use of sexualized language or imagery and sexual attention or advances 37 | * The use of inappropriate images, including in a community member's avatar 38 | * The use of inappropriate language, including in a community member's nickname 39 | * Any spamming, flaming, baiting or other attention-stealing behavior 40 | * Excessive or unwelcome helping; answering outside the scope of the question 41 | asked 42 | * Trolling, insulting/derogatory comments, and personal or political attacks 43 | * Promoting or spreading disinformation, lies, or conspiracy theories against 44 | a person, group, organisation, project, or community 45 | * Public or private harassment 46 | * Publishing others' private information, such as a physical or electronic 47 | address, without explicit permission 48 | * Other conduct which could reasonably be considered inappropriate 49 | 50 | The goal of the standards and moderation guidelines outlined here is to build 51 | and maintain a respectful community. We ask that you don’t just aim to be 52 | "technically unimpeachable", but rather try to be your best self. 53 | 54 | We value many things beyond technical expertise, including collaboration and 55 | supporting others within our community. Providing a positive experience for 56 | other community members can have a much more significant impact than simply 57 | providing the correct answer. 58 | 59 | ## Our Responsibilities 60 | 61 | Project leaders are responsible for clarifying the standards of acceptable 62 | behavior and are expected to take appropriate and fair corrective action in 63 | response to any instances of unacceptable behavior. 64 | 65 | Project leaders have the right and responsibility to remove, edit, or 66 | reject messages, comments, commits, code, issues, and other contributions 67 | that are not aligned to this Code of Conduct, or to ban temporarily or 68 | permanently any community member for other behaviors that they deem 69 | inappropriate, threatening, offensive, or harmful. 70 | 71 | ## Moderation 72 | 73 | Instances of behaviors that violate the Adafruit Community Code of Conduct 74 | may be reported by any member of the community. Community members are 75 | encouraged to report these situations, including situations they witness 76 | involving other community members. 77 | 78 | You may report in the following ways: 79 | 80 | In any situation, you may send an email to . 81 | 82 | On the Adafruit Discord, you may send an open message from any channel 83 | to all Community Moderators by tagging @community moderators. You may 84 | also send an open message from any channel, or a direct message to 85 | @kattni#1507, @tannewt#4653, @Dan Halbert#1614, @cater#2442, 86 | @sommersoft#0222, @Mr. Certainly#0472 or @Andon#8175. 87 | 88 | Email and direct message reports will be kept confidential. 89 | 90 | In situations on Discord where the issue is particularly egregious, possibly 91 | illegal, requires immediate action, or violates the Discord terms of service, 92 | you should also report the message directly to Discord. 93 | 94 | These are the steps for upholding our community’s standards of conduct. 95 | 96 | 1. Any member of the community may report any situation that violates the 97 | Adafruit Community Code of Conduct. All reports will be reviewed and 98 | investigated. 99 | 2. If the behavior is an egregious violation, the community member who 100 | committed the violation may be banned immediately, without warning. 101 | 3. Otherwise, moderators will first respond to such behavior with a warning. 102 | 4. Moderators follow a soft "three strikes" policy - the community member may 103 | be given another chance, if they are receptive to the warning and change their 104 | behavior. 105 | 5. If the community member is unreceptive or unreasonable when warned by a 106 | moderator, or the warning goes unheeded, they may be banned for a first or 107 | second offense. Repeated offenses will result in the community member being 108 | banned. 109 | 110 | ## Scope 111 | 112 | This Code of Conduct and the enforcement policies listed above apply to all 113 | Adafruit Community venues. This includes but is not limited to any community 114 | spaces (both public and private), the entire Adafruit Discord server, and 115 | Adafruit GitHub repositories. Examples of Adafruit Community spaces include 116 | but are not limited to meet-ups, audio chats on the Adafruit Discord, or 117 | interaction at a conference. 118 | 119 | This Code of Conduct applies both within project spaces and in public spaces 120 | when an individual is representing the project or its community. As a community 121 | member, you are representing our community, and are expected to behave 122 | accordingly. 123 | 124 | ## Attribution 125 | 126 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 127 | version 1.4, available at 128 | , 129 | and the [Rust Code of Conduct](https://www.rust-lang.org/en-US/conduct.html). 130 | 131 | For other projects adopting the Adafruit Community Code of 132 | Conduct, please contact the maintainers of those projects for enforcement. 133 | If you wish to use this code of conduct for your own project, consider 134 | explicitly mentioning your moderation policy or making a copy with your 135 | own moderation policy so as to avoid confusion. 136 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 ladyada for Adafruit Industries 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSES/CC-BY-4.0.txt: -------------------------------------------------------------------------------- 1 | Creative Commons Attribution 4.0 International Creative Commons Corporation 2 | ("Creative Commons") is not a law firm and does not provide legal services 3 | or legal advice. Distribution of Creative Commons public licenses does not 4 | create a lawyer-client or other relationship. Creative Commons makes its licenses 5 | and related information available on an "as-is" basis. Creative Commons gives 6 | no warranties regarding its licenses, any material licensed under their terms 7 | and conditions, or any related information. Creative Commons disclaims all 8 | liability for damages resulting from their use to the fullest extent possible. 9 | 10 | Using Creative Commons Public Licenses 11 | 12 | Creative Commons public licenses provide a standard set of terms and conditions 13 | that creators and other rights holders may use to share original works of 14 | authorship and other material subject to copyright and certain other rights 15 | specified in the public license below. The following considerations are for 16 | informational purposes only, are not exhaustive, and do not form part of our 17 | licenses. 18 | 19 | Considerations for licensors: Our public licenses are intended for use by 20 | those authorized to give the public permission to use material in ways otherwise 21 | restricted by copyright and certain other rights. Our licenses are irrevocable. 22 | Licensors should read and understand the terms and conditions of the license 23 | they choose before applying it. Licensors should also secure all rights necessary 24 | before applying our licenses so that the public can reuse the material as 25 | expected. Licensors should clearly mark any material not subject to the license. 26 | This includes other CC-licensed material, or material used under an exception 27 | or limitation to copyright. More considerations for licensors : wiki.creativecommons.org/Considerations_for_licensors 28 | 29 | Considerations for the public: By using one of our public licenses, a licensor 30 | grants the public permission to use the licensed material under specified 31 | terms and conditions. If the licensor's permission is not necessary for any 32 | reason–for example, because of any applicable exception or limitation to copyright–then 33 | that use is not regulated by the license. Our licenses grant only permissions 34 | under copyright and certain other rights that a licensor has authority to 35 | grant. Use of the licensed material may still be restricted for other reasons, 36 | including because others have copyright or other rights in the material. A 37 | licensor may make special requests, such as asking that all changes be marked 38 | or described. Although not required by our licenses, you are encouraged to 39 | respect those requests where reasonable. More considerations for the public 40 | : wiki.creativecommons.org/Considerations_for_licensees Creative Commons Attribution 41 | 4.0 International Public License 42 | 43 | By exercising the Licensed Rights (defined below), You accept and agree to 44 | be bound by the terms and conditions of this Creative Commons Attribution 45 | 4.0 International Public License ("Public License"). To the extent this Public 46 | License may be interpreted as a contract, You are granted the Licensed Rights 47 | in consideration of Your acceptance of these terms and conditions, and the 48 | Licensor grants You such rights in consideration of benefits the Licensor 49 | receives from making the Licensed Material available under these terms and 50 | conditions. 51 | 52 | Section 1 – Definitions. 53 | 54 | a. Adapted Material means material subject to Copyright and Similar Rights 55 | that is derived from or based upon the Licensed Material and in which the 56 | Licensed Material is translated, altered, arranged, transformed, or otherwise 57 | modified in a manner requiring permission under the Copyright and Similar 58 | Rights held by the Licensor. For purposes of this Public License, where the 59 | Licensed Material is a musical work, performance, or sound recording, Adapted 60 | Material is always produced where the Licensed Material is synched in timed 61 | relation with a moving image. 62 | 63 | b. Adapter's License means the license You apply to Your Copyright and Similar 64 | Rights in Your contributions to Adapted Material in accordance with the terms 65 | and conditions of this Public License. 66 | 67 | c. Copyright and Similar Rights means copyright and/or similar rights closely 68 | related to copyright including, without limitation, performance, broadcast, 69 | sound recording, and Sui Generis Database Rights, without regard to how the 70 | rights are labeled or categorized. For purposes of this Public License, the 71 | rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. 72 | 73 | d. Effective Technological Measures means those measures that, in the absence 74 | of proper authority, may not be circumvented under laws fulfilling obligations 75 | under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, 76 | and/or similar international agreements. 77 | 78 | e. Exceptions and Limitations means fair use, fair dealing, and/or any other 79 | exception or limitation to Copyright and Similar Rights that applies to Your 80 | use of the Licensed Material. 81 | 82 | f. Licensed Material means the artistic or literary work, database, or other 83 | material to which the Licensor applied this Public License. 84 | 85 | g. Licensed Rights means the rights granted to You subject to the terms and 86 | conditions of this Public License, which are limited to all Copyright and 87 | Similar Rights that apply to Your use of the Licensed Material and that the 88 | Licensor has authority to license. 89 | 90 | h. Licensor means the individual(s) or entity(ies) granting rights under this 91 | Public License. 92 | 93 | i. Share means to provide material to the public by any means or process that 94 | requires permission under the Licensed Rights, such as reproduction, public 95 | display, public performance, distribution, dissemination, communication, or 96 | importation, and to make material available to the public including in ways 97 | that members of the public may access the material from a place and at a time 98 | individually chosen by them. 99 | 100 | j. Sui Generis Database Rights means rights other than copyright resulting 101 | from Directive 96/9/EC of the European Parliament and of the Council of 11 102 | March 1996 on the legal protection of databases, as amended and/or succeeded, 103 | as well as other essentially equivalent rights anywhere in the world. 104 | 105 | k. You means the individual or entity exercising the Licensed Rights under 106 | this Public License. Your has a corresponding meaning. 107 | 108 | Section 2 – Scope. 109 | 110 | a. License grant. 111 | 112 | 1. Subject to the terms and conditions of this Public License, the Licensor 113 | hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, 114 | irrevocable license to exercise the Licensed Rights in the Licensed Material 115 | to: 116 | 117 | A. reproduce and Share the Licensed Material, in whole or in part; and 118 | 119 | B. produce, reproduce, and Share Adapted Material. 120 | 121 | 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions 122 | and Limitations apply to Your use, this Public License does not apply, and 123 | You do not need to comply with its terms and conditions. 124 | 125 | 3. Term. The term of this Public License is specified in Section 6(a). 126 | 127 | 4. Media and formats; technical modifications allowed. The Licensor authorizes 128 | You to exercise the Licensed Rights in all media and formats whether now known 129 | or hereafter created, and to make technical modifications necessary to do 130 | so. The Licensor waives and/or agrees not to assert any right or authority 131 | to forbid You from making technical modifications necessary to exercise the 132 | Licensed Rights, including technical modifications necessary to circumvent 133 | Effective Technological Measures. For purposes of this Public License, simply 134 | making modifications authorized by this Section 2(a)(4) never produces Adapted 135 | Material. 136 | 137 | 5. Downstream recipients. 138 | 139 | A. Offer from the Licensor – Licensed Material. Every recipient of the Licensed 140 | Material automatically receives an offer from the Licensor to exercise the 141 | Licensed Rights under the terms and conditions of this Public License. 142 | 143 | B. No downstream restrictions. You may not offer or impose any additional 144 | or different terms or conditions on, or apply any Effective Technological 145 | Measures to, the Licensed Material if doing so restricts exercise of the Licensed 146 | Rights by any recipient of the Licensed Material. 147 | 148 | 6. No endorsement. Nothing in this Public License constitutes or may be construed 149 | as permission to assert or imply that You are, or that Your use of the Licensed 150 | Material is, connected with, or sponsored, endorsed, or granted official status 151 | by, the Licensor or others designated to receive attribution as provided in 152 | Section 3(a)(1)(A)(i). 153 | 154 | b. Other rights. 155 | 156 | 1. Moral rights, such as the right of integrity, are not licensed under this 157 | Public License, nor are publicity, privacy, and/or other similar personality 158 | rights; however, to the extent possible, the Licensor waives and/or agrees 159 | not to assert any such rights held by the Licensor to the limited extent necessary 160 | to allow You to exercise the Licensed Rights, but not otherwise. 161 | 162 | 2. Patent and trademark rights are not licensed under this Public License. 163 | 164 | 3. To the extent possible, the Licensor waives any right to collect royalties 165 | from You for the exercise of the Licensed Rights, whether directly or through 166 | a collecting society under any voluntary or waivable statutory or compulsory 167 | licensing scheme. In all other cases the Licensor expressly reserves any right 168 | to collect such royalties. 169 | 170 | Section 3 – License Conditions. 171 | 172 | Your exercise of the Licensed Rights is expressly made subject to the following 173 | conditions. 174 | 175 | a. Attribution. 176 | 177 | 1. If You Share the Licensed Material (including in modified form), You must: 178 | 179 | A. retain the following if it is supplied by the Licensor with the Licensed 180 | Material: 181 | 182 | i. identification of the creator(s) of the Licensed Material and any others 183 | designated to receive attribution, in any reasonable manner requested by the 184 | Licensor (including by pseudonym if designated); 185 | 186 | ii. a copyright notice; 187 | 188 | iii. a notice that refers to this Public License; 189 | 190 | iv. a notice that refers to the disclaimer of warranties; 191 | 192 | v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; 193 | 194 | B. indicate if You modified the Licensed Material and retain an indication 195 | of any previous modifications; and 196 | 197 | C. indicate the Licensed Material is licensed under this Public License, and 198 | include the text of, or the URI or hyperlink to, this Public License. 199 | 200 | 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner 201 | based on the medium, means, and context in which You Share the Licensed Material. 202 | For example, it may be reasonable to satisfy the conditions by providing a 203 | URI or hyperlink to a resource that includes the required information. 204 | 205 | 3. If requested by the Licensor, You must remove any of the information required 206 | by Section 3(a)(1)(A) to the extent reasonably practicable. 207 | 208 | 4. If You Share Adapted Material You produce, the Adapter's License You apply 209 | must not prevent recipients of the Adapted Material from complying with this 210 | Public License. 211 | 212 | Section 4 – Sui Generis Database Rights. 213 | 214 | Where the Licensed Rights include Sui Generis Database Rights that apply to 215 | Your use of the Licensed Material: 216 | 217 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, 218 | reuse, reproduce, and Share all or a substantial portion of the contents of 219 | the database; 220 | 221 | b. if You include all or a substantial portion of the database contents in 222 | a database in which You have Sui Generis Database Rights, then the database 223 | in which You have Sui Generis Database Rights (but not its individual contents) 224 | is Adapted Material; and 225 | 226 | c. You must comply with the conditions in Section 3(a) if You Share all or 227 | a substantial portion of the contents of the database. 228 | 229 | For the avoidance of doubt, this Section 4 supplements and does not replace 230 | Your obligations under this Public License where the Licensed Rights include 231 | other Copyright and Similar Rights. 232 | 233 | Section 5 – Disclaimer of Warranties and Limitation of Liability. 234 | 235 | a. Unless otherwise separately undertaken by the Licensor, to the extent possible, 236 | the Licensor offers the Licensed Material as-is and as-available, and makes 237 | no representations or warranties of any kind concerning the Licensed Material, 238 | whether express, implied, statutory, or other. This includes, without limitation, 239 | warranties of title, merchantability, fitness for a particular purpose, non-infringement, 240 | absence of latent or other defects, accuracy, or the presence or absence of 241 | errors, whether or not known or discoverable. Where disclaimers of warranties 242 | are not allowed in full or in part, this disclaimer may not apply to You. 243 | 244 | b. To the extent possible, in no event will the Licensor be liable to You 245 | on any legal theory (including, without limitation, negligence) or otherwise 246 | for any direct, special, indirect, incidental, consequential, punitive, exemplary, 247 | or other losses, costs, expenses, or damages arising out of this Public License 248 | or use of the Licensed Material, even if the Licensor has been advised of 249 | the possibility of such losses, costs, expenses, or damages. Where a limitation 250 | of liability is not allowed in full or in part, this limitation may not apply 251 | to You. 252 | 253 | c. The disclaimer of warranties and limitation of liability provided above 254 | shall be interpreted in a manner that, to the extent possible, most closely 255 | approximates an absolute disclaimer and waiver of all liability. 256 | 257 | Section 6 – Term and Termination. 258 | 259 | a. This Public License applies for the term of the Copyright and Similar Rights 260 | licensed here. However, if You fail to comply with this Public License, then 261 | Your rights under this Public License terminate automatically. 262 | 263 | b. Where Your right to use the Licensed Material has terminated under Section 264 | 6(a), it reinstates: 265 | 266 | 1. automatically as of the date the violation is cured, provided it is cured 267 | within 30 days of Your discovery of the violation; or 268 | 269 | 2. upon express reinstatement by the Licensor. 270 | 271 | c. For the avoidance of doubt, this Section 6(b) does not affect any right 272 | the Licensor may have to seek remedies for Your violations of this Public 273 | License. 274 | 275 | d. For the avoidance of doubt, the Licensor may also offer the Licensed Material 276 | under separate terms or conditions or stop distributing the Licensed Material 277 | at any time; however, doing so will not terminate this Public License. 278 | 279 | e. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. 280 | 281 | Section 7 – Other Terms and Conditions. 282 | 283 | a. The Licensor shall not be bound by any additional or different terms or 284 | conditions communicated by You unless expressly agreed. 285 | 286 | b. Any arrangements, understandings, or agreements regarding the Licensed 287 | Material not stated herein are separate from and independent of the terms 288 | and conditions of this Public License. 289 | 290 | Section 8 – Interpretation. 291 | 292 | a. For the avoidance of doubt, this Public License does not, and shall not 293 | be interpreted to, reduce, limit, restrict, or impose conditions on any use 294 | of the Licensed Material that could lawfully be made without permission under 295 | this Public License. 296 | 297 | b. To the extent possible, if any provision of this Public License is deemed 298 | unenforceable, it shall be automatically reformed to the minimum extent necessary 299 | to make it enforceable. If the provision cannot be reformed, it shall be severed 300 | from this Public License without affecting the enforceability of the remaining 301 | terms and conditions. 302 | 303 | c. No term or condition of this Public License will be waived and no failure 304 | to comply consented to unless expressly agreed to by the Licensor. 305 | 306 | d. Nothing in this Public License constitutes or may be interpreted as a limitation 307 | upon, or waiver of, any privileges and immunities that apply to the Licensor 308 | or You, including from the legal processes of any jurisdiction or authority. 309 | 310 | Creative Commons is not a party to its public licenses. Notwithstanding, Creative 311 | Commons may elect to apply one of its public licenses to material it publishes 312 | and in those instances will be considered the "Licensor." The text of the 313 | Creative Commons public licenses is dedicated to the public domain under the 314 | CC0 Public Domain Dedication. Except for the limited purpose of indicating 315 | that material is shared under a Creative Commons public license or as otherwise 316 | permitted by the Creative Commons policies published at creativecommons.org/policies, 317 | Creative Commons does not authorize the use of the trademark "Creative Commons" 318 | or any other trademark or logo of Creative Commons without its prior written 319 | consent including, without limitation, in connection with any unauthorized 320 | modifications to any of its public licenses or any other arrangements, understandings, 321 | or agreements concerning use of licensed material. For the avoidance of doubt, 322 | this paragraph does not form part of the public licenses. 323 | 324 | Creative Commons may be contacted at creativecommons.org. 325 | -------------------------------------------------------------------------------- /LICENSES/MIT.txt: -------------------------------------------------------------------------------- 1 | MIT License Copyright (c) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice (including the next 11 | paragraph) shall be included in all copies or substantial portions of the 12 | Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS 17 | OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 18 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF 19 | OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /LICENSES/Unlicense.txt: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute 4 | this software, either in source code form or as a compiled binary, for any 5 | purpose, commercial or non-commercial, and by any means. 6 | 7 | In jurisdictions that recognize copyright laws, the author or authors of this 8 | software dedicate any and all copyright interest in the software to the public 9 | domain. We make this dedication for the benefit of the public at large and 10 | to the detriment of our heirs and successors. We intend this dedication to 11 | be an overt act of relinquishment in perpetuity of all present and future 12 | rights to this software under copyright law. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS 17 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 18 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH 19 | THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information, 20 | please refer to 21 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | 2 | Introduction 3 | ============ 4 | 5 | .. image:: https://readthedocs.org/projects/adafruit-circuitpython-fingerprint/badge/?version=latest 6 | :target: https://docs.circuitpython.org/projects/fingerprint/en/latest/ 7 | :alt: Documentation Status 8 | 9 | .. image:: https://raw.githubusercontent.com/adafruit/Adafruit_CircuitPython_Bundle/main/badges/adafruit_discord.svg 10 | :target: https://adafru.it/discord 11 | :alt: Discord 12 | 13 | .. image:: https://github.com/adafruit/Adafruit_CircuitPython_Fingerprint/workflows/Build%20CI/badge.svg 14 | :target: https://github.com/adafruit/Adafruit_CircuitPython_Fingerprint/actions/ 15 | :alt: Build Status 16 | 17 | .. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json 18 | :target: https://github.com/astral-sh/ruff 19 | :alt: Code Style: Ruff 20 | 21 | This library will let you use an Adafruit Fingerprint sensor on any UART to get, store, retreive and query fingerprints! Great for adding bio-sensing security to your next build. 22 | 23 | Dependencies 24 | ============= 25 | This driver depends on: 26 | 27 | * `Adafruit CircuitPython `_ 28 | 29 | Please ensure all dependencies are available on the CircuitPython filesystem. 30 | This is easily achieved by downloading 31 | `the Adafruit library and driver bundle `_. 32 | 33 | Installing from PyPI 34 | ==================== 35 | 36 | On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally `from 37 | PyPI `_. To install for current user: 38 | 39 | .. code-block:: shell 40 | 41 | pip3 install adafruit-circuitpython-fingerprint 42 | 43 | To install system-wide (this may be required in some cases): 44 | 45 | .. code-block:: shell 46 | 47 | sudo pip3 install adafruit-circuitpython-fingerprint 48 | 49 | To install in a virtual environment in your current project: 50 | 51 | .. code-block:: shell 52 | 53 | mkdir project-name && cd project-name 54 | python3 -m venv .venv 55 | source .venv/bin/activate 56 | pip3 install adafruit-circuitpython-fingerprint 57 | 58 | Usage Example 59 | ============= 60 | 61 | See 'examples' folder for full usage demo! 62 | 63 | 64 | Documentation 65 | ============= 66 | 67 | API documentation for this library can be found on `Read the Docs `_. 68 | 69 | For information on building library documentation, please check out `this guide `_. 70 | 71 | Contributing 72 | ============ 73 | 74 | Contributions are welcome! Please read our `Code of Conduct 75 | `_ 76 | before contributing to help this project stay welcoming. 77 | -------------------------------------------------------------------------------- /README.rst.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries 2 | 3 | SPDX-License-Identifier: MIT 4 | -------------------------------------------------------------------------------- /adafruit_fingerprint.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2017 ladyada for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | `adafruit_fingerprint` 7 | ==================================================== 8 | 9 | This library will let you use an Adafruit Fingerprint sensor on any UART to get, store, 10 | retreive and query fingerprints! Great for adding bio-sensing security to your next build. 11 | 12 | * Author(s): ladyada 13 | 14 | Implementation Notes 15 | -------------------- 16 | 17 | **Hardware:** 18 | 19 | * `Fingerprint sensor `_ (Product ID: 751) 20 | * `Panel Mount Fingerprint sensor `_ (Product ID: 4651) 21 | 22 | **Software and Dependencies:** 23 | 24 | * Adafruit CircuitPython firmware (2.2.0+) for the ESP8622 and M0-based boards: 25 | https://github.com/adafruit/circuitpython/releases 26 | """ 27 | 28 | try: 29 | from typing import List, Tuple, Union 30 | 31 | from serial import Serial 32 | except ImportError: 33 | pass 34 | 35 | import struct 36 | import time 37 | 38 | from busio import UART 39 | from micropython import const 40 | 41 | __version__ = "0.0.0+auto.0" 42 | __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Fingerprint.git" 43 | 44 | _STARTCODE = const(0xEF01) 45 | _COMMANDPACKET = const(0x1) 46 | _DATAPACKET = const(0x2) 47 | _ACKPACKET = const(0x7) 48 | _ENDDATAPACKET = const(0x8) 49 | 50 | _GETIMAGE = const(0x01) 51 | _IMAGE2TZ = const(0x02) 52 | _COMPARE = const(0x03) 53 | _FINGERPRINTSEARCH = const(0x04) 54 | _REGMODEL = const(0x05) 55 | _STORE = const(0x06) 56 | _LOAD = const(0x07) 57 | _UPLOAD = const(0x08) 58 | _DOWNLOAD = const(0x09) 59 | _UPLOADIMAGE = const(0x0A) 60 | _DOWNLOADIMAGE = const(0x0B) 61 | _DELETE = const(0x0C) 62 | _EMPTY = const(0x0D) 63 | _SETSYSPARA = const(0x0E) 64 | _READSYSPARA = const(0x0F) 65 | _HISPEEDSEARCH = const(0x1B) 66 | _VERIFYPASSWORD = const(0x13) 67 | _TEMPLATECOUNT = const(0x1D) 68 | _TEMPLATEREAD = const(0x1F) 69 | _SOFTRESET = const(0x3D) 70 | _GETECHO = const(0x53) 71 | _SETAURA = const(0x35) 72 | 73 | # Packet error code 74 | OK = const(0x0) 75 | PACKETRECIEVEERR = const(0x01) 76 | NOFINGER = const(0x02) 77 | IMAGEFAIL = const(0x03) 78 | IMAGEMESS = const(0x06) 79 | FEATUREFAIL = const(0x07) 80 | NOMATCH = const(0x08) 81 | NOTFOUND = const(0x09) 82 | ENROLLMISMATCH = const(0x0A) 83 | BADLOCATION = const(0x0B) 84 | DBRANGEFAIL = const(0x0C) 85 | UPLOADFEATUREFAIL = const(0x0D) 86 | PACKETRESPONSEFAIL = const(0x0E) 87 | UPLOADFAIL = const(0x0F) 88 | DELETEFAIL = const(0x10) 89 | DBCLEARFAIL = const(0x11) 90 | PASSFAIL = const(0x13) 91 | INVALIDIMAGE = const(0x15) 92 | FLASHERR = const(0x18) 93 | INVALIDREG = const(0x1A) 94 | ADDRCODE = const(0x20) 95 | PASSVERIFY = const(0x21) 96 | MODULEOK = const(0x55) 97 | 98 | 99 | class Adafruit_Fingerprint: 100 | """UART based fingerprint sensor.""" 101 | 102 | _debug = False 103 | _uart = None 104 | 105 | password = None 106 | address = [0xFF, 0xFF, 0xFF, 0xFF] 107 | finger_id = None 108 | confidence = None 109 | templates = [] 110 | template_count = None 111 | library_size = None 112 | security_level = None 113 | device_address = None 114 | data_packet_size = None 115 | baudrate = None 116 | system_id = None 117 | status_register = None 118 | 119 | def __init__( 120 | self, 121 | uart: Union[UART, "Serial"], 122 | passwd: Tuple[int, int, int, int] = (0, 0, 0, 0), 123 | ): 124 | # Create object with UART for interface, and default 32-bit password 125 | self.password = passwd 126 | self._uart = uart 127 | if self.verify_password() != OK: 128 | raise RuntimeError("Failed to find sensor, check wiring!") 129 | if self.read_sysparam() != OK: 130 | raise RuntimeError("Failed to read system parameters!") 131 | 132 | def check_module(self) -> bool: 133 | """Checks the state of the fingerprint scanner module. 134 | Returns OK or error.""" 135 | self._send_packet([_GETECHO]) 136 | if self._get_packet(12)[0] != MODULEOK: 137 | raise RuntimeError("Something is wrong with the sensor.") 138 | return True 139 | 140 | def verify_password(self) -> bool: 141 | """Checks if the password/connection is correct, returns True/False""" 142 | self._send_packet([_VERIFYPASSWORD] + list(self.password)) 143 | return self._get_packet(12)[0] 144 | 145 | def count_templates(self) -> int: 146 | """Requests the sensor to count the number of templates and stores it 147 | in ``self.template_count``. Returns the packet error code or OK success""" 148 | self._send_packet([_TEMPLATECOUNT]) 149 | r = self._get_packet(14) 150 | self.template_count = struct.unpack(">H", bytes(r[1:3]))[0] 151 | return r[0] 152 | 153 | def read_sysparam(self) -> int: 154 | """Returns the system parameters on success via attributes.""" 155 | self._send_packet([_READSYSPARA]) 156 | r = self._get_packet(28) 157 | if r[0] != OK: 158 | raise RuntimeError("Command failed.") 159 | self.status_register = struct.unpack(">H", bytes(r[1:3]))[0] 160 | self.system_id = struct.unpack(">H", bytes(r[3:5]))[0] 161 | self.library_size = struct.unpack(">H", bytes(r[5:7]))[0] 162 | self.security_level = struct.unpack(">H", bytes(r[7:9]))[0] 163 | self.device_address = bytes(r[9:13]) 164 | self.data_packet_size = struct.unpack(">H", bytes(r[13:15]))[0] 165 | self.baudrate = struct.unpack(">H", bytes(r[15:17]))[0] 166 | return r[0] 167 | 168 | def set_sysparam(self, param_num: int, param_val: int) -> int: 169 | """Set the system parameters (param_num)""" 170 | self._send_packet([_SETSYSPARA, param_num, param_val]) 171 | r = self._get_packet(12) 172 | if r[0] != OK: 173 | raise RuntimeError("Command failed.") 174 | if param_num == 4: 175 | self.baudrate = param_val 176 | elif param_num == 5: 177 | self.security_level = param_val 178 | elif param_num == 6: 179 | self.data_packet_size = param_val 180 | time.sleep(0.25) 181 | return r[0] 182 | 183 | def get_image(self) -> int: 184 | """Requests the sensor to take an image and store it memory, returns 185 | the packet error code or OK success""" 186 | self._send_packet([_GETIMAGE]) 187 | return self._get_packet(12)[0] 188 | 189 | def image_2_tz(self, slot: int = 1) -> int: 190 | """Requests the sensor convert the image to a template, returns 191 | the packet error code or OK success""" 192 | self._send_packet([_IMAGE2TZ, slot]) 193 | return self._get_packet(12)[0] 194 | 195 | def create_model(self) -> int: 196 | """Requests the sensor take the template data and turn it into a model 197 | returns the packet error code or OK success""" 198 | self._send_packet([_REGMODEL]) 199 | return self._get_packet(12)[0] 200 | 201 | def store_model(self, location: int, slot: int = 1) -> int: 202 | """Requests the sensor store the model into flash memory and assign 203 | a location. Returns the packet error code or OK success""" 204 | self._send_packet([_STORE, slot, location >> 8, location & 0xFF]) 205 | return self._get_packet(12)[0] 206 | 207 | def delete_model(self, location: int) -> int: 208 | """Requests the sensor delete a model from flash memory given by 209 | the argument location. Returns the packet error code or OK success""" 210 | self._send_packet([_DELETE, location >> 8, location & 0xFF, 0x00, 0x01]) 211 | return self._get_packet(12)[0] 212 | 213 | def load_model(self, location: int, slot: int = 1) -> int: 214 | """Requests the sensor to load a model from the given memory location 215 | to the given slot. Returns the packet error code or success""" 216 | self._send_packet([_LOAD, slot, location >> 8, location & 0xFF]) 217 | return self._get_packet(12)[0] 218 | 219 | def get_fpdata(self, sensorbuffer: str = "char", slot: int = 1) -> List[int]: 220 | """Requests the sensor to transfer the fingerprint image or 221 | template. Returns the data payload only.""" 222 | if slot not in {1, 2}: 223 | # raise error or use default value? 224 | slot = 2 225 | if sensorbuffer == "image": 226 | self._send_packet([_UPLOADIMAGE]) 227 | elif sensorbuffer == "char": 228 | self._send_packet([_UPLOAD, slot]) 229 | else: 230 | raise RuntimeError("Uknown sensor buffer type") 231 | if self._get_packet(12)[0] == 0: 232 | res = self._get_data(9) 233 | self._print_debug("get_fpdata data size:", str(len(res))) 234 | self._print_debug("get_fdata res:", res, data_type="hex") 235 | return res 236 | 237 | def send_fpdata(self, data: List[int], sensorbuffer: str = "char", slot: int = 1) -> bool: 238 | """Requests the sensor to receive data, either a fingerprint image or 239 | a character/template data. Data is the payload only.""" 240 | if slot not in {1, 2}: 241 | # raise error or use default value? 242 | slot = 2 243 | if sensorbuffer == "image": 244 | self._send_packet([_DOWNLOADIMAGE]) 245 | elif sensorbuffer == "char": 246 | self._send_packet([_DOWNLOAD, slot]) 247 | else: 248 | raise RuntimeError("Uknown sensor buffer type") 249 | if self._get_packet(12)[0] == 0: 250 | self._send_data(data) 251 | self._print_debug("send_fpdata data size:", str(len(data))) 252 | self._print_debug("sent_fdata data:", data, data_type="hex") 253 | return True 254 | 255 | def empty_library(self) -> int: 256 | """Requests the sensor to delete all models from flash memory. 257 | Returns the packet error code or OK success""" 258 | self._send_packet([_EMPTY]) 259 | return self._get_packet(12)[0] 260 | 261 | def read_templates(self) -> int: 262 | """Requests the sensor to list of all template locations in use and 263 | stores them in self.templates. Returns the packet error code or 264 | OK success""" 265 | from math import ceil # noqa: PLC0415 266 | 267 | self.templates = [] 268 | self.read_sysparam() 269 | temp_r = [ 270 | 0x0C, 271 | ] 272 | for j in range(ceil(self.library_size / 256)): 273 | self._send_packet([_TEMPLATEREAD, j]) 274 | r = self._get_packet(44) 275 | if r[0] == OK: 276 | for i in range(32): 277 | byte = r[i + 1] 278 | for bit in range(8): 279 | if byte & (1 << bit): 280 | self.templates.append((i * 8) + bit + (j * 256)) 281 | temp_r = r 282 | else: 283 | r = temp_r 284 | return r[0] 285 | 286 | def finger_fast_search(self) -> int: 287 | """Asks the sensor to search for a matching fingerprint template to the 288 | last model generated. Stores the location and confidence in self.finger_id 289 | and self.confidence. Returns the packet error code or OK success""" 290 | # high speed search of slot #1 starting at page 0x0000 and page #0x00A3 291 | # self._send_packet([_HISPEEDSEARCH, 0x01, 0x00, 0x00, 0x00, 0xA3]) 292 | # or page #0x03E9 to accommodate modules with up to 1000 capacity 293 | # self._send_packet([_HISPEEDSEARCH, 0x01, 0x00, 0x00, 0x03, 0xE9]) 294 | # or base the page on module's capacity 295 | self.read_sysparam() 296 | capacity = self.library_size 297 | self._send_packet([_HISPEEDSEARCH, 0x01, 0x00, 0x00, capacity >> 8, capacity & 0xFF]) 298 | r = self._get_packet(16) 299 | self.finger_id, self.confidence = struct.unpack(">HH", bytes(r[1:5])) 300 | self._print_debug("finger_fast_search packet:", r, data_type="hex") 301 | return r[0] 302 | 303 | def close_uart(self): 304 | """close serial port""" 305 | self._uart.close() 306 | 307 | def finger_search(self) -> int: 308 | """Asks the sensor to search for a matching fingerprint starting at 309 | slot 1. Stores the location and confidence in self.finger_id 310 | and self.confidence. Returns the packet error code or OK success""" 311 | self.read_sysparam() 312 | capacity = self.library_size 313 | self._send_packet([_FINGERPRINTSEARCH, 0x01, 0x00, 0x00, capacity >> 8, capacity & 0xFF]) 314 | r = self._get_packet(16) 315 | self.finger_id, self.confidence = struct.unpack(">HH", bytes(r[1:5])) 316 | self._print_debug("finger_search packet:", r, data_type="hex") 317 | return r[0] 318 | 319 | def compare_templates(self) -> int: 320 | """Compares two fingerprint templates in char buffers 1 and 2. Stores the confidence score 321 | in self.finger_id and self.confidence. Returns the packet error code or 322 | OK success""" 323 | self._send_packet([_COMPARE]) 324 | r = self._get_packet(14) 325 | self.confidence = struct.unpack(">H", bytes(r[1:3])) 326 | self._print_debug("compare_templates confidence:", self.confidence) 327 | return r[0] 328 | 329 | def set_led(self, color: int = 1, mode: int = 3, speed: int = 0x80, cycles: int = 0) -> int: 330 | """LED function -- only for R503 Sensor. 331 | Parameters: See User Manual for full details 332 | color: 1=red, 2=blue, 3=purple 333 | mode: 1-breathe, 2-flash, 3-on, 4-off, 5-fade_on, 6-fade-off 334 | speed: animation speed 0-255 335 | cycles: numbe of time to repeat 0=infinite or 1-255 336 | Returns the packet error code or success""" 337 | self._send_packet([_SETAURA, mode, speed, color, cycles]) 338 | return self._get_packet(12)[0] 339 | 340 | ################################################## 341 | 342 | def _get_packet(self, expected: int) -> List[int]: 343 | """Helper to parse out a packet from the UART and check structure. 344 | Returns just the data payload from the packet""" 345 | res = self._uart.read(expected) 346 | self._print_debug("_get_packet received data:", res, data_type="hex") 347 | if (not res) or (len(res) != expected): 348 | raise RuntimeError("Failed to read data from sensor") 349 | 350 | # first two bytes are start code 351 | start = struct.unpack(">H", res[0:2])[0] 352 | 353 | if start != _STARTCODE: 354 | raise RuntimeError("Incorrect packet data") 355 | # next 4 bytes are address 356 | addr = list(i for i in res[2:6]) 357 | if addr != self.address: 358 | raise RuntimeError("Incorrect address") 359 | 360 | packet_type, length = struct.unpack(">BH", res[6:9]) 361 | if packet_type != _ACKPACKET: 362 | raise RuntimeError("Incorrect packet data") 363 | 364 | # we should check the checksum 365 | # but i don't know how 366 | # not yet anyway 367 | # packet_sum = struct.unpack('>H', res[9+(length-2):9+length])[0] 368 | # print(packet_sum) 369 | # print(packet_type + length + struct.unpack('>HHHH', res[9:9+(length-2)])) 370 | 371 | reply = list(i for i in res[9 : 9 + (length - 2)]) 372 | self._print_debug("_get_packet reply:", reply, data_type="hex") 373 | return reply 374 | 375 | def _get_data(self, expected: int) -> List[int]: 376 | """Gets packet from serial and checks structure for _DATAPACKET 377 | and _ENDDATAPACKET. Alternate method for getting data such 378 | as fingerprint image, etc. Returns the data payload.""" 379 | res = self._uart.read(expected) 380 | self._print_debug("_get_data received data:", res, data_type="hex") 381 | if (not res) or (len(res) != expected): 382 | raise RuntimeError("Failed to read data from sensor") 383 | 384 | # first two bytes are start code 385 | start = struct.unpack(">H", res[0:2])[0] 386 | self._print_debug("_get_data received start pos:", start) 387 | if start != _STARTCODE: 388 | raise RuntimeError("Incorrect packet data") 389 | # next 4 bytes are address 390 | addr = list(i for i in res[2:6]) 391 | self._print_debug("_get_data received address:", addr) 392 | if addr != self.address: 393 | raise RuntimeError("Incorrect address") 394 | 395 | packet_type, length = struct.unpack(">BH", res[6:9]) 396 | self._print_debug("_get_data received packet_type:", packet_type) 397 | self._print_debug("_get_data received length:", length) 398 | 399 | # todo: check checksum 400 | 401 | if packet_type != _DATAPACKET: 402 | if packet_type != _ENDDATAPACKET: 403 | raise RuntimeError("Incorrect packet data") 404 | 405 | if packet_type == _DATAPACKET: 406 | res = self._uart.read(length - 2) 407 | # todo: we should really inspect the headers and checksum 408 | reply = list(i for i in res[0:length]) 409 | received_checksum = struct.unpack(">H", self._uart.read(2)) 410 | self._print_debug("_get_data received checksum:", received_checksum) 411 | 412 | reply += self._get_data(9) 413 | elif packet_type == _ENDDATAPACKET: 414 | res = self._uart.read(length - 2) 415 | # todo: we should really inspect the headers and checksum 416 | reply = list(i for i in res[0:length]) 417 | received_checksum = struct.unpack(">H", self._uart.read(2)) 418 | self._print_debug("_get_data received checksum:", received_checksum) 419 | 420 | self._print_debug("_get_data reply length:", len(reply)) 421 | self._print_debug("_get_data reply:", reply, data_type="hex") 422 | return reply 423 | 424 | def _send_packet(self, data: List[int]): 425 | packet = [_STARTCODE >> 8, _STARTCODE & 0xFF] 426 | packet = packet + self.address 427 | packet.append(_COMMANDPACKET) # the packet type 428 | 429 | length = len(data) + 2 430 | packet.append(length >> 8) 431 | packet.append(length & 0xFF) 432 | 433 | packet = packet + data 434 | 435 | checksum = sum(packet[6:]) 436 | packet.append(checksum >> 8) 437 | packet.append(checksum & 0xFF) 438 | 439 | self._print_debug("_send_packet length:", len(packet)) 440 | self._print_debug("_send_packet data:", packet, data_type="hex") 441 | self._uart.write(bytearray(packet)) 442 | 443 | def _send_data(self, data: List[int]): 444 | self._print_debug("_send_data length:", len(data)) 445 | self._print_debug("_send_data data:", data, data_type="hex") 446 | # self.read_sysparam() #moved this to init 447 | if self.data_packet_size == 0: 448 | data_length = 32 449 | elif self.data_packet_size == 1: 450 | data_length = 64 451 | elif self.data_packet_size == 2: 452 | data_length = 128 453 | elif self.data_packet_size == 3: 454 | data_length = 256 455 | self._print_debug("_send_data sensor data length:", data_length) 456 | i = 0 457 | left = len(data) 458 | for i in range(int(len(data) / data_length)): 459 | start = i * data_length 460 | end = (i + 1) * data_length 461 | left = left - data_length 462 | self._print_debug("_send_data data start:", start) 463 | self._print_debug("_send_data data end:", end) 464 | self._print_debug("_send_data i:", i) 465 | 466 | packet = [_STARTCODE >> 8, _STARTCODE & 0xFF] 467 | packet = packet + self.address 468 | 469 | if left <= 0: 470 | packet.append(_ENDDATAPACKET) 471 | else: 472 | packet.append(_DATAPACKET) 473 | 474 | length = len(data[start:end]) + 2 475 | self._print_debug("_send_data length:", length) 476 | packet.append(length >> 8) 477 | packet.append(length & 0xFF) 478 | if left <= 0: 479 | checksum = _ENDDATAPACKET + (length >> 8) + (length & 0xFF) 480 | else: 481 | checksum = _DATAPACKET + (length >> 8) + (length & 0xFF) 482 | 483 | # for j in range(len(data[start:end])): 484 | for j in range(start, end): 485 | packet.append(data[j]) 486 | checksum += data[j] 487 | 488 | packet.append(checksum >> 8) 489 | packet.append(checksum & 0xFF) 490 | 491 | self._print_debug("_send_data sending packet:", packet, data_type="hex") 492 | self._uart.write(packet) 493 | 494 | def soft_reset(self): 495 | """Performs a soft reset of the sensor""" 496 | self._send_packet([_SOFTRESET]) 497 | if self._get_packet(12)[0] == OK: 498 | if self._uart.read(1)[0] != MODULEOK: 499 | raise RuntimeError("Sensor did not send a handshake signal!") 500 | 501 | def _print_debug(self, info: str, data: Union[int, str], data_type: str = "str"): 502 | """Prints debugging information. This is activated 503 | by flag _debug""" 504 | if not self._debug: 505 | return 506 | 507 | if data_type == "hex": 508 | print("*** DEBUG ==>", info, [f"{i:02x}" for i in data]) 509 | elif data_type == "str": 510 | print("*** DEBUG ==>", info, data) 511 | -------------------------------------------------------------------------------- /docs/_static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_CircuitPython_Fingerprint/1fe67b1e67d5856d2a99d2993e1c8e028592684f/docs/_static/favicon.ico -------------------------------------------------------------------------------- /docs/_static/favicon.ico.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2018 Phillip Torrone for Adafruit Industries 2 | 3 | SPDX-License-Identifier: CC-BY-4.0 4 | -------------------------------------------------------------------------------- /docs/api.rst: -------------------------------------------------------------------------------- 1 | 2 | .. If you created a package, create one automodule per module in the package. 3 | 4 | API Reference 5 | ############# 6 | 7 | .. automodule:: adafruit_fingerprint 8 | :members: 9 | -------------------------------------------------------------------------------- /docs/api.rst.license: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 ladyada for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | import datetime 6 | import os 7 | import sys 8 | 9 | sys.path.insert(0, os.path.abspath("..")) 10 | 11 | # -- General configuration ------------------------------------------------ 12 | 13 | # Add any Sphinx extension module names here, as strings. They can be 14 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 15 | # ones. 16 | extensions = [ 17 | "sphinx.ext.autodoc", 18 | "sphinxcontrib.jquery", 19 | "sphinx.ext.intersphinx", 20 | "sphinx.ext.viewcode", 21 | ] 22 | 23 | # Uncomment the below if you use native CircuitPython modules such as 24 | # digitalio, micropython and busio. List the modules you use. Without it, the 25 | # autodoc module docs will fail to generate with a warning. 26 | # autodoc_mock_imports = ["micropython"] 27 | 28 | intersphinx_mapping = { 29 | "python": ("https://docs.python.org/3", None), 30 | "CircuitPython": ("https://docs.circuitpython.org/en/latest/", None), 31 | } 32 | 33 | # Add any paths that contain templates here, relative to this directory. 34 | templates_path = ["_templates"] 35 | 36 | source_suffix = ".rst" 37 | 38 | # The master toctree document. 39 | master_doc = "index" 40 | 41 | # General information about the project. 42 | project = "Adafruit Fingerprint Library" 43 | creation_year = "2017" 44 | current_year = str(datetime.datetime.now().year) 45 | year_duration = ( 46 | current_year if current_year == creation_year else creation_year + " - " + current_year 47 | ) 48 | copyright = year_duration + " ladyada" 49 | author = "ladyada" 50 | 51 | # The version info for the project you're documenting, acts as replacement for 52 | # |version| and |release|, also used in various other places throughout the 53 | # built documents. 54 | # 55 | # The short X.Y version. 56 | version = "1.0" 57 | # The full version, including alpha/beta/rc tags. 58 | release = "1.0" 59 | 60 | # The language for content autogenerated by Sphinx. Refer to documentation 61 | # for a list of supported languages. 62 | # 63 | # This is also used if you do content translation via gettext catalogs. 64 | # Usually you set "language" from the command line for these cases. 65 | language = "en" 66 | 67 | # List of patterns, relative to source directory, that match files and 68 | # directories to ignore when looking for source files. 69 | # This patterns also effect to html_static_path and html_extra_path 70 | exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", ".env", "CODE_OF_CONDUCT.md"] 71 | 72 | # The reST default role (used for this markup: `text`) to use for all 73 | # documents. 74 | # 75 | default_role = "any" 76 | 77 | # If true, '()' will be appended to :func: etc. cross-reference text. 78 | # 79 | add_function_parentheses = True 80 | 81 | # The name of the Pygments (syntax highlighting) style to use. 82 | pygments_style = "sphinx" 83 | 84 | # If true, `todo` and `todoList` produce output, else they produce nothing. 85 | todo_include_todos = False 86 | 87 | # If this is True, todo emits a warning for each TODO entries. The default is False. 88 | todo_emit_warnings = True 89 | 90 | 91 | # -- Options for HTML output ---------------------------------------------- 92 | 93 | # The theme to use for HTML and HTML Help pages. See the documentation for 94 | # a list of builtin themes. 95 | # 96 | import sphinx_rtd_theme 97 | 98 | html_theme = "sphinx_rtd_theme" 99 | 100 | # Add any paths that contain custom static files (such as style sheets) here, 101 | # relative to this directory. They are copied after the builtin static files, 102 | # so a file named "default.css" will overwrite the builtin "default.css". 103 | html_static_path = ["_static"] 104 | 105 | # The name of an image file (relative to this directory) to use as a favicon of 106 | # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 107 | # pixels large. 108 | # 109 | html_favicon = "_static/favicon.ico" 110 | 111 | # Output file base name for HTML help builder. 112 | htmlhelp_basename = "AdafruitFingerprintLibrarydoc" 113 | 114 | # -- Options for LaTeX output --------------------------------------------- 115 | 116 | latex_elements = { 117 | # The paper size ('letterpaper' or 'a4paper'). 118 | # 119 | # 'papersize': 'letterpaper', 120 | # The font size ('10pt', '11pt' or '12pt'). 121 | # 122 | # 'pointsize': '10pt', 123 | # Additional stuff for the LaTeX preamble. 124 | # 125 | # 'preamble': '', 126 | # Latex figure (float) alignment 127 | # 128 | # 'figure_align': 'htbp', 129 | } 130 | 131 | # Grouping the document tree into LaTeX files. List of tuples 132 | # (source start file, target name, title, 133 | # author, documentclass [howto, manual, or own class]). 134 | latex_documents = [ 135 | ( 136 | master_doc, 137 | "AdafruitFingerprintLibrary.tex", 138 | "AdafruitFingerprint Library Documentation", 139 | author, 140 | "manual", 141 | ), 142 | ] 143 | 144 | # -- Options for manual page output --------------------------------------- 145 | 146 | # One entry per manual page. List of tuples 147 | # (source start file, name, description, authors, manual section). 148 | man_pages = [ 149 | ( 150 | master_doc, 151 | "AdafruitFingerprintlibrary", 152 | "Adafruit Fingerprint Library Documentation", 153 | [author], 154 | 1, 155 | ) 156 | ] 157 | 158 | # -- Options for Texinfo output ------------------------------------------- 159 | 160 | # Grouping the document tree into Texinfo files. List of tuples 161 | # (source start file, target name, title, author, 162 | # dir menu entry, description, category) 163 | texinfo_documents = [ 164 | ( 165 | master_doc, 166 | "AdafruitFingerprintLibrary", 167 | "Adafruit Fingerprint Library Documentation", 168 | author, 169 | "AdafruitFingerprintLibrary", 170 | "One line description of project.", 171 | "Miscellaneous", 172 | ), 173 | ] 174 | -------------------------------------------------------------------------------- /docs/examples.rst: -------------------------------------------------------------------------------- 1 | Simple test 2 | ------------ 3 | 4 | Ensure your device works with this simple test. 5 | 6 | .. literalinclude:: ../examples/fingerprint_simpletest.py 7 | :caption: examples/fingerprint_simpletest.py 8 | :linenos: 9 | -------------------------------------------------------------------------------- /docs/examples.rst.license: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 ladyada for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../README.rst 2 | 3 | Table of Contents 4 | ================= 5 | 6 | .. toctree:: 7 | :maxdepth: 4 8 | :hidden: 9 | 10 | self 11 | 12 | .. toctree:: 13 | :caption: Examples 14 | 15 | examples 16 | 17 | .. toctree:: 18 | :caption: API Reference 19 | :maxdepth: 3 20 | 21 | api 22 | 23 | .. toctree:: 24 | :caption: Tutorials 25 | 26 | .. toctree:: 27 | :caption: Related Products 28 | 29 | Fingerprint sensor 30 | 31 | .. toctree:: 32 | :caption: Other Links 33 | 34 | Download from GitHub 35 | Download Library Bundle 36 | CircuitPython Reference Documentation 37 | CircuitPython Support Forum 38 | Discord Chat 39 | Adafruit Learning System 40 | Adafruit Blog 41 | Adafruit Store 42 | 43 | Indices and tables 44 | ================== 45 | 46 | * :ref:`genindex` 47 | * :ref:`modindex` 48 | * :ref:`search` 49 | -------------------------------------------------------------------------------- /docs/index.rst.license: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 ladyada for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: Unlicense 4 | 5 | sphinx 6 | sphinxcontrib-jquery 7 | sphinx-rtd-theme 8 | -------------------------------------------------------------------------------- /examples/fingerprint_picturetest.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2023 Jim McKeown 2 | # SPDX-License-Identifier: MIT 3 | 4 | """ 5 | This example shows the basic functionality to: 6 | Show the devices fingerprint slots that have fingerprints enrolled. 7 | Enroll a fingerprint in an existing or new fingerprint slot. 8 | Try to find a fingerprint in the existing list of enrolled fingerprints. 9 | Delete an enrolled fingerprint. 10 | View the image of a fingerprint. 11 | Preview the image of a fingerprint and then try to find the fingerprint 12 | in the existing list of enrolled fingerprints. 13 | 14 | Please note that this example only works on single board computers 15 | with the use of Blinka. 16 | 17 | This example is based on fingerprint_simpletest.py 18 | """ 19 | 20 | import time 21 | 22 | import numpy as np 23 | import serial 24 | from matplotlib import pyplot as plt 25 | 26 | import adafruit_fingerprint 27 | 28 | # led = DigitalInOut(board.D13) 29 | # led.direction = Direction.OUTPUT 30 | 31 | # This has not been tested: 32 | # uart = busio.UART(board.TX, board.RX, baudrate=57600) 33 | 34 | # If using with a computer such as Linux/RaspberryPi, Mac, Windows with USB/serial converter: 35 | # Edit ttyACM0 to your USB/serial port 36 | uart = serial.Serial("/dev/ttyACM0", baudrate=57600, timeout=1) 37 | 38 | # If using with Linux/Raspberry Pi and hardware UART: 39 | # import serial 40 | # uart = serial.Serial("/dev/ttyS0", baudrate=57600, timeout=1) 41 | 42 | finger = adafruit_fingerprint.Adafruit_Fingerprint(uart) 43 | 44 | ################################################## 45 | 46 | 47 | def get_fingerprint(): 48 | """Get a finger print image, template it, and see if it matches!""" 49 | print("Waiting for image...") 50 | while finger.get_image() != adafruit_fingerprint.OK: 51 | pass 52 | print("Templating...") 53 | if finger.image_2_tz(1) != adafruit_fingerprint.OK: 54 | return False 55 | print("Searching...") 56 | if finger.finger_search() != adafruit_fingerprint.OK: 57 | return False 58 | return True 59 | 60 | 61 | def get_fingerprint_detail(): 62 | """Get a finger print image, template it, and see if it matches! 63 | This time, print out each error instead of just returning on failure""" 64 | print("Getting image...", end="") 65 | i = finger.get_image() 66 | if i == adafruit_fingerprint.OK: 67 | print("Image taken") 68 | else: 69 | if i == adafruit_fingerprint.NOFINGER: 70 | print("No finger detected") 71 | elif i == adafruit_fingerprint.IMAGEFAIL: 72 | print("Imaging error") 73 | else: 74 | print("Other error") 75 | return False 76 | 77 | print("Templating...", end="") 78 | i = finger.image_2_tz(1) 79 | if i == adafruit_fingerprint.OK: 80 | print("Templated") 81 | else: 82 | if i == adafruit_fingerprint.IMAGEMESS: 83 | print("Image too messy") 84 | elif i == adafruit_fingerprint.FEATUREFAIL: 85 | print("Could not identify features") 86 | elif i == adafruit_fingerprint.INVALIDIMAGE: 87 | print("Image invalid") 88 | else: 89 | print("Other error") 90 | return False 91 | 92 | print("Searching...", end="") 93 | i = finger.finger_fast_search() 94 | # This block needs to be refactored when it can be tested. 95 | if i == adafruit_fingerprint.OK: 96 | print("Found fingerprint!") 97 | return True 98 | else: 99 | if i == adafruit_fingerprint.NOTFOUND: 100 | print("No match found") 101 | else: 102 | print("Other error") 103 | return False 104 | 105 | 106 | def get_fingerprint_photo(): 107 | """Get and show fingerprint image""" 108 | print("Waiting for image...") 109 | while finger.get_image() != adafruit_fingerprint.OK: 110 | pass 111 | print("Got image...Transferring image data...") 112 | imgList = finger.get_fpdata("image", 2) 113 | imgArray = np.zeros(73728, np.uint8) 114 | for i, val in enumerate(imgList): 115 | imgArray[(i * 2)] = val & 240 116 | imgArray[(i * 2) + 1] = (val & 15) * 16 117 | imgArray = np.reshape(imgArray, (288, 256)) 118 | plt.title("Fingerprint Image") 119 | plt.imshow(imgArray) 120 | plt.show(block=False) 121 | 122 | 123 | def get_fingerprint_preview(): 124 | """Get a finger print image, show it, template it, and see if it matches!""" 125 | print("Waiting for image...") 126 | while finger.get_image() != adafruit_fingerprint.OK: 127 | pass 128 | print("Got image...Transferring image data...") 129 | imgList = finger.get_fpdata("image", 2) 130 | imgArray = np.zeros(73728, np.uint8) 131 | for i, val in enumerate(imgList): 132 | imgArray[(i * 2)] = val & 240 133 | imgArray[(i * 2) + 1] = (val & 15) * 16 134 | imgArray = np.reshape(imgArray, (288, 256)) 135 | plt.title("Fingerprint Image") 136 | plt.imshow(imgArray) 137 | plt.show(block=False) 138 | print("Templating...") 139 | if finger.image_2_tz(1) != adafruit_fingerprint.OK: 140 | return False 141 | print("Searching...") 142 | if finger.finger_search() != adafruit_fingerprint.OK: 143 | return False 144 | return True 145 | 146 | 147 | def enroll_finger(location): 148 | """Take a 2 finger images and template it, then store in 'location'""" 149 | for fingerimg in range(1, 3): 150 | if fingerimg == 1: 151 | print("Place finger on sensor...", end="") 152 | else: 153 | print("Place same finger again...", end="") 154 | 155 | while True: 156 | i = finger.get_image() 157 | if i == adafruit_fingerprint.OK: 158 | print("Image taken") 159 | break 160 | if i == adafruit_fingerprint.NOFINGER: 161 | print(".", end="") 162 | elif i == adafruit_fingerprint.IMAGEFAIL: 163 | print("Imaging error") 164 | return False 165 | else: 166 | print("Other error") 167 | return False 168 | 169 | print("Templating...", end="") 170 | i = finger.image_2_tz(fingerimg) 171 | if i == adafruit_fingerprint.OK: 172 | print("Templated") 173 | else: 174 | if i == adafruit_fingerprint.IMAGEMESS: 175 | print("Image too messy") 176 | elif i == adafruit_fingerprint.FEATUREFAIL: 177 | print("Could not identify features") 178 | elif i == adafruit_fingerprint.INVALIDIMAGE: 179 | print("Image invalid") 180 | else: 181 | print("Other error") 182 | return False 183 | 184 | if fingerimg == 1: 185 | print("Remove finger") 186 | time.sleep(1) 187 | while i != adafruit_fingerprint.NOFINGER: 188 | i = finger.get_image() 189 | 190 | print("Creating model...", end="") 191 | i = finger.create_model() 192 | if i == adafruit_fingerprint.OK: 193 | print("Created") 194 | else: 195 | if i == adafruit_fingerprint.ENROLLMISMATCH: 196 | print("Prints did not match") 197 | else: 198 | print("Other error") 199 | return False 200 | 201 | print("Storing model #%d..." % location, end="") 202 | i = finger.store_model(location) 203 | if i == adafruit_fingerprint.OK: 204 | print("Stored") 205 | else: 206 | if i == adafruit_fingerprint.BADLOCATION: 207 | print("Bad storage location") 208 | elif i == adafruit_fingerprint.FLASHERR: 209 | print("Flash storage error") 210 | else: 211 | print("Other error") 212 | return False 213 | 214 | return True 215 | 216 | 217 | ################################################## 218 | 219 | 220 | def get_num(): 221 | """Use input() to get a valid number from 1 to 127. Retry till success!""" 222 | i = 0 223 | while (i > 127) or (i < 1): 224 | try: 225 | i = int(input("Enter ID # from 1-127: ")) 226 | except ValueError: 227 | pass 228 | return i 229 | 230 | 231 | while True: 232 | print("----------------") 233 | if finger.read_templates() != adafruit_fingerprint.OK: 234 | raise RuntimeError("Failed to read templates") 235 | print("Fingerprint templates:", finger.templates) 236 | print("e) enroll print") 237 | print("f) find print") 238 | print("d) delete print") 239 | print("v) view print") 240 | print("p) preview and find print") 241 | print("----------------") 242 | c = input("> ") 243 | 244 | if c == "e": 245 | enroll_finger(get_num()) 246 | if c == "f": 247 | if get_fingerprint(): 248 | print("Detected #", finger.finger_id, "with confidence", finger.confidence) 249 | else: 250 | print("Finger not found") 251 | if c == "d": 252 | if finger.delete_model(get_num()) == adafruit_fingerprint.OK: 253 | print("Deleted!") 254 | else: 255 | print("Failed to delete") 256 | if c == "v": 257 | get_fingerprint_photo() 258 | if c == "p": 259 | if get_fingerprint_preview(): 260 | print("Detected #", finger.finger_id, "with confidence", finger.confidence) 261 | else: 262 | print("Finger not found") 263 | -------------------------------------------------------------------------------- /examples/fingerprint_r503.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries 2 | # SPDX-License-Identifier: MIT 3 | 4 | import time 5 | 6 | import board 7 | import busio 8 | 9 | import adafruit_fingerprint 10 | 11 | uart = busio.UART(board.TX, board.RX, baudrate=57600) 12 | 13 | # If using with a computer such as Linux/RaspberryPi, Mac, Windows with USB/serial converter: 14 | # import serial 15 | # uart = serial.Serial("/dev/ttyUSB0", baudrate=57600, timeout=1) 16 | 17 | # If using with Linux/Raspberry Pi and hardware UART: 18 | # import serial 19 | # uart = serial.Serial("/dev/ttyS0", baudrate=57600, timeout=1) 20 | 21 | finger = adafruit_fingerprint.Adafruit_Fingerprint(uart) 22 | 23 | ################################################## 24 | 25 | 26 | def get_fingerprint(): 27 | """Get a finger print image, template it, and see if it matches!""" 28 | print("Waiting for image...") 29 | while finger.get_image() != adafruit_fingerprint.OK: 30 | pass 31 | print("Templating...") 32 | if finger.image_2_tz(1) != adafruit_fingerprint.OK: 33 | return False 34 | print("Searching...") 35 | if finger.finger_search() != adafruit_fingerprint.OK: 36 | return False 37 | return True 38 | 39 | 40 | def get_fingerprint_detail(): 41 | """Get a finger print image, template it, and see if it matches! 42 | This time, print out each error instead of just returning on failure""" 43 | print("Getting image...", end="") 44 | i = finger.get_image() 45 | if i == adafruit_fingerprint.OK: 46 | print("Image taken") 47 | else: 48 | if i == adafruit_fingerprint.NOFINGER: 49 | print("No finger detected") 50 | elif i == adafruit_fingerprint.IMAGEFAIL: 51 | print("Imaging error") 52 | else: 53 | print("Other error") 54 | return False 55 | 56 | print("Templating...", end="") 57 | i = finger.image_2_tz(1) 58 | if i == adafruit_fingerprint.OK: 59 | print("Templated") 60 | else: 61 | if i == adafruit_fingerprint.IMAGEMESS: 62 | print("Image too messy") 63 | elif i == adafruit_fingerprint.FEATUREFAIL: 64 | print("Could not identify features") 65 | elif i == adafruit_fingerprint.INVALIDIMAGE: 66 | print("Image invalid") 67 | else: 68 | print("Other error") 69 | return False 70 | 71 | print("Searching...", end="") 72 | i = finger.finger_fast_search() 73 | # This block needs to be refactored when it can be tested. 74 | if i == adafruit_fingerprint.OK: 75 | print("Found fingerprint!") 76 | return True 77 | else: 78 | if i == adafruit_fingerprint.NOTFOUND: 79 | print("No match found") 80 | else: 81 | print("Other error") 82 | return False 83 | 84 | 85 | def enroll_finger(location): 86 | """Take a 2 finger images and template it, then store in 'location'""" 87 | for fingerimg in range(1, 3): 88 | if fingerimg == 1: 89 | print("Place finger on sensor...", end="") 90 | else: 91 | print("Place same finger again...", end="") 92 | 93 | while True: 94 | i = finger.get_image() 95 | if i == adafruit_fingerprint.OK: 96 | print("Image taken") 97 | break 98 | if i == adafruit_fingerprint.NOFINGER: 99 | print(".", end="") 100 | elif i == adafruit_fingerprint.IMAGEFAIL: 101 | print("Imaging error") 102 | return False 103 | else: 104 | print("Other error") 105 | return False 106 | 107 | print("Templating...", end="") 108 | i = finger.image_2_tz(fingerimg) 109 | if i == adafruit_fingerprint.OK: 110 | print("Templated") 111 | else: 112 | if i == adafruit_fingerprint.IMAGEMESS: 113 | print("Image too messy") 114 | elif i == adafruit_fingerprint.FEATUREFAIL: 115 | print("Could not identify features") 116 | elif i == adafruit_fingerprint.INVALIDIMAGE: 117 | print("Image invalid") 118 | else: 119 | print("Other error") 120 | return False 121 | 122 | if fingerimg == 1: 123 | print("Remove finger") 124 | time.sleep(1) 125 | while i != adafruit_fingerprint.NOFINGER: 126 | i = finger.get_image() 127 | 128 | print("Creating model...", end="") 129 | i = finger.create_model() 130 | if i == adafruit_fingerprint.OK: 131 | print("Created") 132 | else: 133 | if i == adafruit_fingerprint.ENROLLMISMATCH: 134 | print("Prints did not match") 135 | else: 136 | print("Other error") 137 | return False 138 | 139 | print("Storing model #%d..." % location, end="") 140 | i = finger.store_model(location) 141 | if i == adafruit_fingerprint.OK: 142 | print("Stored") 143 | else: 144 | if i == adafruit_fingerprint.BADLOCATION: 145 | print("Bad storage location") 146 | elif i == adafruit_fingerprint.FLASHERR: 147 | print("Flash storage error") 148 | else: 149 | print("Other error") 150 | return False 151 | 152 | return True 153 | 154 | 155 | ################################################## 156 | 157 | 158 | def get_num(): 159 | """Use input() to get a valid number from 1 to 127. Retry till success!""" 160 | i = 0 161 | while (i > 127) or (i < 1): 162 | try: 163 | i = int(input("Enter ID # from 1-127: ")) 164 | except ValueError: 165 | pass 166 | return i 167 | 168 | 169 | # initialize LED color 170 | led_color = 1 171 | led_mode = 3 172 | while True: 173 | # Turn on LED 174 | finger.set_led(color=led_color, mode=led_mode) 175 | print("----------------") 176 | if finger.read_templates() != adafruit_fingerprint.OK: 177 | raise RuntimeError("Failed to read templates") 178 | print("Fingerprint templates:", finger.templates) 179 | print("e) enroll print") 180 | print("f) find print") 181 | print("d) delete print") 182 | print("l) set LED") 183 | print("----------------") 184 | c = input("> ") 185 | 186 | if c == "l": 187 | c = input("color(r,b,p anything else=off)> ") 188 | led_mode = 3 189 | if c == "r": 190 | led_color = 1 191 | elif c == "b": 192 | led_color = 2 193 | elif c == "p": 194 | led_color = 3 195 | else: 196 | led_color = 1 197 | led_mode = 4 198 | elif c == "e": 199 | enroll_finger(get_num()) 200 | elif c == "f": 201 | # breathing LED 202 | finger.set_led(color=3, mode=1) 203 | if get_fingerprint(): 204 | print("Detected #", finger.finger_id, "with confidence", finger.confidence) 205 | else: 206 | print("Finger not found") 207 | elif c == "d": 208 | if finger.delete_model(get_num()) == adafruit_fingerprint.OK: 209 | print("Deleted!") 210 | else: 211 | print("Failed to delete") 212 | else: 213 | print("Invalid choice: Try again") 214 | -------------------------------------------------------------------------------- /examples/fingerprint_r503_rpi.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries 2 | # SPDX-License-Identifier: MIT 3 | 4 | import time 5 | 6 | import serial 7 | 8 | import adafruit_fingerprint 9 | 10 | # import board 11 | # uart = busio.UART(board.TX, board.RX, baudrate=57600) 12 | 13 | # If using with a computer such as Linux/RaspberryPi, Mac, Windows with USB/serial converter: 14 | uart = serial.Serial("/dev/ttyUSB0", baudrate=57600, timeout=1) 15 | 16 | # If using with Linux/Raspberry Pi and hardware UART: 17 | # uart = serial.Serial("/dev/ttyS0", baudrate=57600, timeout=1) 18 | 19 | # If using with Linux/Raspberry Pi 3 with pi3-disable-bt 20 | # uart = serial.Serial("/dev/ttyAMA0", baudrate=57600, timeout=1) 21 | 22 | finger = adafruit_fingerprint.Adafruit_Fingerprint(uart) 23 | 24 | ################################################## 25 | 26 | 27 | def get_fingerprint(): 28 | """Get a finger print image, template it, and see if it matches!""" 29 | print("Waiting for image...") 30 | while finger.get_image() != adafruit_fingerprint.OK: 31 | pass 32 | print("Templating...") 33 | if finger.image_2_tz(1) != adafruit_fingerprint.OK: 34 | return False 35 | print("Searching...") 36 | if finger.finger_search() != adafruit_fingerprint.OK: 37 | return False 38 | return True 39 | 40 | 41 | def get_fingerprint_detail(): 42 | """Get a finger print image, template it, and see if it matches! 43 | This time, print out each error instead of just returning on failure""" 44 | print("Getting image...", end="") 45 | i = finger.get_image() 46 | if i == adafruit_fingerprint.OK: 47 | print("Image taken") 48 | else: 49 | if i == adafruit_fingerprint.NOFINGER: 50 | print("No finger detected") 51 | elif i == adafruit_fingerprint.IMAGEFAIL: 52 | print("Imaging error") 53 | else: 54 | print("Other error") 55 | return False 56 | 57 | print("Templating...", end="") 58 | i = finger.image_2_tz(1) 59 | if i == adafruit_fingerprint.OK: 60 | print("Templated") 61 | else: 62 | if i == adafruit_fingerprint.IMAGEMESS: 63 | print("Image too messy") 64 | elif i == adafruit_fingerprint.FEATUREFAIL: 65 | print("Could not identify features") 66 | elif i == adafruit_fingerprint.INVALIDIMAGE: 67 | print("Image invalid") 68 | else: 69 | print("Other error") 70 | return False 71 | 72 | print("Searching...", end="") 73 | i = finger.finger_fast_search() 74 | # This block needs to be refactored when it can be tested. 75 | if i == adafruit_fingerprint.OK: 76 | print("Found fingerprint!") 77 | return True 78 | else: 79 | if i == adafruit_fingerprint.NOTFOUND: 80 | print("No match found") 81 | else: 82 | print("Other error") 83 | return False 84 | 85 | 86 | def enroll_finger(location): 87 | """Take a 2 finger images and template it, then store in 'location'""" 88 | for fingerimg in range(1, 3): 89 | if fingerimg == 1: 90 | print("Place finger on sensor...", end="") 91 | else: 92 | print("Place same finger again...", end="") 93 | 94 | while True: 95 | i = finger.get_image() 96 | if i == adafruit_fingerprint.OK: 97 | print("Image taken") 98 | break 99 | if i == adafruit_fingerprint.NOFINGER: 100 | print(".", end="") 101 | elif i == adafruit_fingerprint.IMAGEFAIL: 102 | print("Imaging error") 103 | return False 104 | else: 105 | print("Other error") 106 | return False 107 | 108 | print("Templating...", end="") 109 | i = finger.image_2_tz(fingerimg) 110 | if i == adafruit_fingerprint.OK: 111 | print("Templated") 112 | else: 113 | if i == adafruit_fingerprint.IMAGEMESS: 114 | print("Image too messy") 115 | elif i == adafruit_fingerprint.FEATUREFAIL: 116 | print("Could not identify features") 117 | elif i == adafruit_fingerprint.INVALIDIMAGE: 118 | print("Image invalid") 119 | else: 120 | print("Other error") 121 | return False 122 | 123 | if fingerimg == 1: 124 | print("Remove finger") 125 | time.sleep(1) 126 | while i != adafruit_fingerprint.NOFINGER: 127 | i = finger.get_image() 128 | 129 | print("Creating model...", end="") 130 | i = finger.create_model() 131 | if i == adafruit_fingerprint.OK: 132 | print("Created") 133 | else: 134 | if i == adafruit_fingerprint.ENROLLMISMATCH: 135 | print("Prints did not match") 136 | else: 137 | print("Other error") 138 | return False 139 | 140 | print("Storing model #%d..." % location, end="") 141 | i = finger.store_model(location) 142 | if i == adafruit_fingerprint.OK: 143 | print("Stored") 144 | else: 145 | if i == adafruit_fingerprint.BADLOCATION: 146 | print("Bad storage location") 147 | elif i == adafruit_fingerprint.FLASHERR: 148 | print("Flash storage error") 149 | else: 150 | print("Other error") 151 | return False 152 | 153 | return True 154 | 155 | 156 | def save_fingerprint_image(filename): 157 | """Scan fingerprint then save image to filename.""" 158 | print("Place finger on sensor...", end="") 159 | while True: 160 | i = finger.get_image() 161 | if i == adafruit_fingerprint.OK: 162 | print("Image taken") 163 | break 164 | if i == adafruit_fingerprint.NOFINGER: 165 | print(".", end="") 166 | elif i == adafruit_fingerprint.IMAGEFAIL: 167 | print("Imaging error") 168 | return False 169 | else: 170 | print("Other error") 171 | return False 172 | 173 | # let PIL take care of the image headers and file structure 174 | from PIL import Image # noqa: PLC0415 175 | 176 | img = Image.new("L", (192, 192), "white") 177 | pixeldata = img.load() 178 | mask = 0b00001111 179 | result = finger.get_fpdata(sensorbuffer="image") 180 | 181 | # this block "unpacks" the data received from the fingerprint 182 | # module then copies the image data to the image placeholder "img" 183 | # pixel by pixel. please refer to section 4.2.1 of the manual for 184 | # more details. thanks to Bastian Raschke and Danylo Esterman. 185 | x = 0 186 | y = 0 187 | for i in range(len(result)): 188 | pixeldata[x, y] = (int(result[i]) >> 4) * 17 189 | x += 1 190 | pixeldata[x, y] = (int(result[i]) & mask) * 17 191 | if x == 191: 192 | x = 0 193 | y += 1 194 | else: 195 | x += 1 196 | 197 | if not img.save(filename): 198 | return True 199 | return False 200 | 201 | 202 | ################################################## 203 | 204 | 205 | def get_num(max_number): 206 | """Use input() to get a valid number from 0 to the maximum size 207 | of the library. Retry till success!""" 208 | i = -1 209 | while (i > max_number - 1) or (i < 0): 210 | try: 211 | i = int(input(f"Enter ID # from 0-{max_number - 1}: ")) 212 | except ValueError: 213 | pass 214 | return i 215 | 216 | 217 | # initialize LED color 218 | led_color = 1 219 | led_mode = 3 220 | while True: 221 | # Turn on LED 222 | finger.set_led(color=led_color, mode=led_mode) 223 | print("----------------") 224 | if finger.read_templates() != adafruit_fingerprint.OK: 225 | raise RuntimeError("Failed to read templates") 226 | print("Fingerprint templates: ", finger.templates) 227 | if finger.count_templates() != adafruit_fingerprint.OK: 228 | raise RuntimeError("Failed to read templates") 229 | print("Number of templates found: ", finger.template_count) 230 | if finger.read_sysparam() != adafruit_fingerprint.OK: 231 | raise RuntimeError("Failed to get system parameters") 232 | print("Size of template library: ", finger.library_size) 233 | print("e) enroll print") 234 | print("f) find print") 235 | print("d) delete print") 236 | print("s) save fingerprint image") 237 | print("r) reset library") 238 | print("l) set LED") 239 | print("q) quit") 240 | print("----------------") 241 | c = input("> ") 242 | 243 | if c == "l": 244 | c = input("color(r,b,p anything else=off)> ") 245 | led_mode = 3 246 | if c == "r": 247 | led_color = 1 248 | elif c == "b": 249 | led_color = 2 250 | elif c == "p": 251 | led_color = 3 252 | else: 253 | led_color = 1 254 | led_mode = 4 255 | elif c == "e": 256 | enroll_finger(get_num(finger.library_size)) 257 | elif c == "f": 258 | # breathing LED 259 | finger.set_led(color=3, mode=1) 260 | if get_fingerprint(): 261 | print("Detected #", finger.finger_id, "with confidence", finger.confidence) 262 | else: 263 | print("Finger not found") 264 | elif c == "d": 265 | if finger.delete_model(get_num(finger.library_size)) == adafruit_fingerprint.OK: 266 | print("Deleted!") 267 | else: 268 | print("Failed to delete") 269 | elif c == "s": 270 | if save_fingerprint_image("fingerprint.png"): 271 | print("Fingerprint image saved") 272 | else: 273 | print("Failed to save fingerprint image") 274 | elif c == "r": 275 | if finger.empty_library() == adafruit_fingerprint.OK: 276 | print("Library empty!") 277 | else: 278 | print("Failed to empty library") 279 | elif c == "q": 280 | print("Exiting fingerprint example program") 281 | # turn off LED 282 | finger.set_led(mode=4) 283 | raise SystemExit 284 | else: 285 | print("Invalid choice: Try again") 286 | -------------------------------------------------------------------------------- /examples/fingerprint_simpletest.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries 2 | # SPDX-License-Identifier: MIT 3 | 4 | import time 5 | 6 | import board 7 | import busio 8 | from digitalio import DigitalInOut, Direction 9 | 10 | import adafruit_fingerprint 11 | 12 | led = DigitalInOut(board.D13) 13 | led.direction = Direction.OUTPUT 14 | 15 | uart = busio.UART(board.TX, board.RX, baudrate=57600) 16 | 17 | # If using with a computer such as Linux/RaspberryPi, Mac, Windows with USB/serial converter: 18 | # import serial 19 | # uart = serial.Serial("/dev/ttyUSB0", baudrate=57600, timeout=1) 20 | 21 | # If using with Linux/Raspberry Pi and hardware UART: 22 | # import serial 23 | # uart = serial.Serial("/dev/ttyS0", baudrate=57600, timeout=1) 24 | 25 | finger = adafruit_fingerprint.Adafruit_Fingerprint(uart) 26 | 27 | ################################################## 28 | 29 | 30 | def get_fingerprint(): 31 | """Get a finger print image, template it, and see if it matches!""" 32 | print("Waiting for image...") 33 | while finger.get_image() != adafruit_fingerprint.OK: 34 | pass 35 | print("Templating...") 36 | if finger.image_2_tz(1) != adafruit_fingerprint.OK: 37 | return False 38 | print("Searching...") 39 | if finger.finger_search() != adafruit_fingerprint.OK: 40 | return False 41 | return True 42 | 43 | 44 | def get_fingerprint_detail(): 45 | """Get a finger print image, template it, and see if it matches! 46 | This time, print out each error instead of just returning on failure""" 47 | print("Getting image...", end="") 48 | i = finger.get_image() 49 | if i == adafruit_fingerprint.OK: 50 | print("Image taken") 51 | else: 52 | if i == adafruit_fingerprint.NOFINGER: 53 | print("No finger detected") 54 | elif i == adafruit_fingerprint.IMAGEFAIL: 55 | print("Imaging error") 56 | else: 57 | print("Other error") 58 | return False 59 | 60 | print("Templating...", end="") 61 | i = finger.image_2_tz(1) 62 | if i == adafruit_fingerprint.OK: 63 | print("Templated") 64 | else: 65 | if i == adafruit_fingerprint.IMAGEMESS: 66 | print("Image too messy") 67 | elif i == adafruit_fingerprint.FEATUREFAIL: 68 | print("Could not identify features") 69 | elif i == adafruit_fingerprint.INVALIDIMAGE: 70 | print("Image invalid") 71 | else: 72 | print("Other error") 73 | return False 74 | 75 | print("Searching...", end="") 76 | i = finger.finger_fast_search() 77 | # This block needs to be refactored when it can be tested. 78 | if i == adafruit_fingerprint.OK: 79 | print("Found fingerprint!") 80 | return True 81 | else: 82 | if i == adafruit_fingerprint.NOTFOUND: 83 | print("No match found") 84 | else: 85 | print("Other error") 86 | return False 87 | 88 | 89 | def enroll_finger(location): 90 | """Take a 2 finger images and template it, then store in 'location'""" 91 | for fingerimg in range(1, 3): 92 | if fingerimg == 1: 93 | print("Place finger on sensor...", end="") 94 | else: 95 | print("Place same finger again...", end="") 96 | 97 | while True: 98 | i = finger.get_image() 99 | if i == adafruit_fingerprint.OK: 100 | print("Image taken") 101 | break 102 | if i == adafruit_fingerprint.NOFINGER: 103 | print(".", end="") 104 | elif i == adafruit_fingerprint.IMAGEFAIL: 105 | print("Imaging error") 106 | return False 107 | else: 108 | print("Other error") 109 | return False 110 | 111 | print("Templating...", end="") 112 | i = finger.image_2_tz(fingerimg) 113 | if i == adafruit_fingerprint.OK: 114 | print("Templated") 115 | else: 116 | if i == adafruit_fingerprint.IMAGEMESS: 117 | print("Image too messy") 118 | elif i == adafruit_fingerprint.FEATUREFAIL: 119 | print("Could not identify features") 120 | elif i == adafruit_fingerprint.INVALIDIMAGE: 121 | print("Image invalid") 122 | else: 123 | print("Other error") 124 | return False 125 | 126 | if fingerimg == 1: 127 | print("Remove finger") 128 | time.sleep(1) 129 | while i != adafruit_fingerprint.NOFINGER: 130 | i = finger.get_image() 131 | 132 | print("Creating model...", end="") 133 | i = finger.create_model() 134 | if i == adafruit_fingerprint.OK: 135 | print("Created") 136 | else: 137 | if i == adafruit_fingerprint.ENROLLMISMATCH: 138 | print("Prints did not match") 139 | else: 140 | print("Other error") 141 | return False 142 | 143 | print("Storing model #%d..." % location, end="") 144 | i = finger.store_model(location) 145 | if i == adafruit_fingerprint.OK: 146 | print("Stored") 147 | else: 148 | if i == adafruit_fingerprint.BADLOCATION: 149 | print("Bad storage location") 150 | elif i == adafruit_fingerprint.FLASHERR: 151 | print("Flash storage error") 152 | else: 153 | print("Other error") 154 | return False 155 | 156 | return True 157 | 158 | 159 | ################################################## 160 | 161 | 162 | def get_num(): 163 | """Use input() to get a valid number from 1 to 127. Retry till success!""" 164 | i = 0 165 | while (i > 127) or (i < 1): 166 | try: 167 | i = int(input("Enter ID # from 1-127: ")) 168 | except ValueError: 169 | pass 170 | return i 171 | 172 | 173 | while True: 174 | print("----------------") 175 | if finger.read_templates() != adafruit_fingerprint.OK: 176 | raise RuntimeError("Failed to read templates") 177 | print("Fingerprint templates:", finger.templates) 178 | print("e) enroll print") 179 | print("f) find print") 180 | print("d) delete print") 181 | print("----------------") 182 | c = input("> ") 183 | 184 | if c == "e": 185 | enroll_finger(get_num()) 186 | if c == "f": 187 | if get_fingerprint(): 188 | print("Detected #", finger.finger_id, "with confidence", finger.confidence) 189 | else: 190 | print("Finger not found") 191 | if c == "d": 192 | if finger.delete_model(get_num()) == adafruit_fingerprint.OK: 193 | print("Deleted!") 194 | else: 195 | print("Failed to delete") 196 | -------------------------------------------------------------------------------- /examples/fingerprint_simpletest_rpi.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries 2 | # SPDX-License-Identifier: MIT 3 | 4 | import time 5 | 6 | import serial 7 | 8 | import adafruit_fingerprint 9 | 10 | # import board 11 | # uart = busio.UART(board.TX, board.RX, baudrate=57600) 12 | 13 | # If using with a computer such as Linux/RaspberryPi, Mac, Windows with USB/serial converter: 14 | uart = serial.Serial("/dev/ttyUSB0", baudrate=57600, timeout=1) 15 | 16 | # If using with Linux/Raspberry Pi and hardware UART: 17 | # uart = serial.Serial("/dev/ttyS0", baudrate=57600, timeout=1) 18 | 19 | # If using with Linux/Raspberry Pi 3 with pi3-disable-bt 20 | # uart = serial.Serial("/dev/ttyAMA0", baudrate=57600, timeout=1) 21 | 22 | finger = adafruit_fingerprint.Adafruit_Fingerprint(uart) 23 | 24 | ################################################## 25 | 26 | 27 | def get_fingerprint(): 28 | """Get a finger print image, template it, and see if it matches!""" 29 | print("Waiting for image...") 30 | while finger.get_image() != adafruit_fingerprint.OK: 31 | pass 32 | print("Templating...") 33 | if finger.image_2_tz(1) != adafruit_fingerprint.OK: 34 | return False 35 | print("Searching...") 36 | if finger.finger_search() != adafruit_fingerprint.OK: 37 | return False 38 | return True 39 | 40 | 41 | def get_fingerprint_detail(): 42 | """Get a finger print image, template it, and see if it matches! 43 | This time, print out each error instead of just returning on failure""" 44 | print("Getting image...", end="") 45 | i = finger.get_image() 46 | if i == adafruit_fingerprint.OK: 47 | print("Image taken") 48 | else: 49 | if i == adafruit_fingerprint.NOFINGER: 50 | print("No finger detected") 51 | elif i == adafruit_fingerprint.IMAGEFAIL: 52 | print("Imaging error") 53 | else: 54 | print("Other error") 55 | return False 56 | 57 | print("Templating...", end="") 58 | i = finger.image_2_tz(1) 59 | if i == adafruit_fingerprint.OK: 60 | print("Templated") 61 | else: 62 | if i == adafruit_fingerprint.IMAGEMESS: 63 | print("Image too messy") 64 | elif i == adafruit_fingerprint.FEATUREFAIL: 65 | print("Could not identify features") 66 | elif i == adafruit_fingerprint.INVALIDIMAGE: 67 | print("Image invalid") 68 | else: 69 | print("Other error") 70 | return False 71 | 72 | print("Searching...", end="") 73 | i = finger.finger_fast_search() 74 | # This block needs to be refactored when it can be tested. 75 | if i == adafruit_fingerprint.OK: 76 | print("Found fingerprint!") 77 | return True 78 | else: 79 | if i == adafruit_fingerprint.NOTFOUND: 80 | print("No match found") 81 | else: 82 | print("Other error") 83 | return False 84 | 85 | 86 | def enroll_finger(location): 87 | """Take a 2 finger images and template it, then store in 'location'""" 88 | for fingerimg in range(1, 3): 89 | if fingerimg == 1: 90 | print("Place finger on sensor...", end="") 91 | else: 92 | print("Place same finger again...", end="") 93 | 94 | while True: 95 | i = finger.get_image() 96 | if i == adafruit_fingerprint.OK: 97 | print("Image taken") 98 | break 99 | if i == adafruit_fingerprint.NOFINGER: 100 | print(".", end="") 101 | elif i == adafruit_fingerprint.IMAGEFAIL: 102 | print("Imaging error") 103 | return False 104 | else: 105 | print("Other error") 106 | return False 107 | 108 | print("Templating...", end="") 109 | i = finger.image_2_tz(fingerimg) 110 | if i == adafruit_fingerprint.OK: 111 | print("Templated") 112 | else: 113 | if i == adafruit_fingerprint.IMAGEMESS: 114 | print("Image too messy") 115 | elif i == adafruit_fingerprint.FEATUREFAIL: 116 | print("Could not identify features") 117 | elif i == adafruit_fingerprint.INVALIDIMAGE: 118 | print("Image invalid") 119 | else: 120 | print("Other error") 121 | return False 122 | 123 | if fingerimg == 1: 124 | print("Remove finger") 125 | time.sleep(1) 126 | while i != adafruit_fingerprint.NOFINGER: 127 | i = finger.get_image() 128 | 129 | print("Creating model...", end="") 130 | i = finger.create_model() 131 | if i == adafruit_fingerprint.OK: 132 | print("Created") 133 | else: 134 | if i == adafruit_fingerprint.ENROLLMISMATCH: 135 | print("Prints did not match") 136 | else: 137 | print("Other error") 138 | return False 139 | 140 | print("Storing model #%d..." % location, end="") 141 | i = finger.store_model(location) 142 | if i == adafruit_fingerprint.OK: 143 | print("Stored") 144 | else: 145 | if i == adafruit_fingerprint.BADLOCATION: 146 | print("Bad storage location") 147 | elif i == adafruit_fingerprint.FLASHERR: 148 | print("Flash storage error") 149 | else: 150 | print("Other error") 151 | return False 152 | 153 | return True 154 | 155 | 156 | def save_fingerprint_image(filename): 157 | """Scan fingerprint then save image to filename.""" 158 | while finger.get_image(): 159 | pass 160 | 161 | # let PIL take care of the image headers and file structure 162 | from PIL import Image # noqa: PLC0415 163 | 164 | img = Image.new("L", (256, 288), "white") 165 | pixeldata = img.load() 166 | mask = 0b00001111 167 | result = finger.get_fpdata(sensorbuffer="image") 168 | 169 | # this block "unpacks" the data received from the fingerprint 170 | # module then copies the image data to the image placeholder "img" 171 | # pixel by pixel. please refer to section 4.2.1 of the manual for 172 | # more details. thanks to Bastian Raschke and Danylo Esterman. 173 | x = 0 174 | y = 0 175 | for i in range(len(result)): 176 | pixeldata[x, y] = (int(result[i]) >> 4) * 17 177 | x += 1 178 | pixeldata[x, y] = (int(result[i]) & mask) * 17 179 | if x == 255: 180 | x = 0 181 | y += 1 182 | else: 183 | x += 1 184 | 185 | if not img.save(filename): 186 | return True 187 | return False 188 | 189 | 190 | ################################################## 191 | 192 | 193 | def get_num(max_number): 194 | """Use input() to get a valid number from 0 to the maximum size 195 | of the library. Retry till success!""" 196 | i = -1 197 | while (i > max_number - 1) or (i < 0): 198 | try: 199 | i = int(input(f"Enter ID # from 0-{max_number - 1}: ")) 200 | except ValueError: 201 | pass 202 | return i 203 | 204 | 205 | while True: 206 | print("----------------") 207 | if finger.read_templates() != adafruit_fingerprint.OK: 208 | raise RuntimeError("Failed to read templates") 209 | print("Fingerprint templates: ", finger.templates) 210 | if finger.count_templates() != adafruit_fingerprint.OK: 211 | raise RuntimeError("Failed to read templates") 212 | print("Number of templates found: ", finger.template_count) 213 | if finger.read_sysparam() != adafruit_fingerprint.OK: 214 | raise RuntimeError("Failed to get system parameters") 215 | print("Size of template library: ", finger.library_size) 216 | print("e) enroll print") 217 | print("f) find print") 218 | print("d) delete print") 219 | print("s) save fingerprint image") 220 | print("r) reset library") 221 | print("q) quit") 222 | print("----------------") 223 | c = input("> ") 224 | 225 | if c == "e": 226 | enroll_finger(get_num(finger.library_size)) 227 | if c == "f": 228 | if get_fingerprint(): 229 | print("Detected #", finger.finger_id, "with confidence", finger.confidence) 230 | else: 231 | print("Finger not found") 232 | if c == "d": 233 | if finger.delete_model(get_num(finger.library_size)) == adafruit_fingerprint.OK: 234 | print("Deleted!") 235 | else: 236 | print("Failed to delete") 237 | if c == "s": 238 | if save_fingerprint_image("fingerprint.png"): 239 | print("Fingerprint image saved") 240 | else: 241 | print("Failed to save fingerprint image") 242 | if c == "r": 243 | if finger.empty_library() == adafruit_fingerprint.OK: 244 | print("Library empty!") 245 | else: 246 | print("Failed to empty library") 247 | if c == "q": 248 | print("Exiting fingerprint example program") 249 | raise SystemExit 250 | -------------------------------------------------------------------------------- /examples/fingerprint_template_file_compare.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries 2 | # SPDX-License-Identifier: MIT 3 | 4 | """ 5 | `fingerprint_template_file_compare.py` 6 | ==================================================== 7 | 8 | This is an example program to demo storing fingerprint templates in a file. It also allows 9 | comparing a newly obtained print with one stored in the file in previous step. This is helpful 10 | when fingerprint templates are stored centrally (not on sensor's flash memory) and shared 11 | between multiple sensors. 12 | 13 | * Author(s): admiralmaggie 14 | 15 | Implementation Notes 16 | -------------------- 17 | 18 | **Hardware:** 19 | 20 | * `Fingerprint sensor `_ (Product ID: 751) 21 | * `Panel Mount Fingerprint sensor `_ (Product ID: 4651) 22 | """ 23 | 24 | import serial 25 | 26 | import adafruit_fingerprint 27 | 28 | # import board (if you are using a micropython board) 29 | # uart = busio.UART(board.TX, board.RX, baudrate=57600) 30 | 31 | # If using with a computer such as Linux/RaspberryPi, Mac, Windows with USB/serial converter: 32 | uart = serial.Serial("COM6", baudrate=57600, timeout=1) 33 | 34 | # If using with Linux/Raspberry Pi and hardware UART: 35 | # uart = serial.Serial("/dev/ttyS0", baudrate=57600, timeout=1) 36 | 37 | # If using with Linux/Raspberry Pi 3 with pi3-disable-bte 38 | # uart = serial.Serial("/dev/ttyAMA0", baudrate=57600, timeout=1) 39 | 40 | finger = adafruit_fingerprint.Adafruit_Fingerprint(uart) 41 | 42 | ################################################## 43 | 44 | 45 | def sensor_reset(): 46 | """Reset sensor""" 47 | print("Resetting sensor...") 48 | if finger.soft_reset() != adafruit_fingerprint.OK: 49 | print("Unable to reset sensor!") 50 | print("Sensor is reset.") 51 | 52 | 53 | def fingerprint_check_file(): 54 | """Compares a new fingerprint template to an existing template stored in a file 55 | This is useful when templates are stored centrally (i.e. in a database)""" 56 | print("Waiting for finger print...") 57 | set_led_local(color=3, mode=1) 58 | while finger.get_image() != adafruit_fingerprint.OK: 59 | pass 60 | print("Templating...") 61 | if finger.image_2_tz(1) != adafruit_fingerprint.OK: 62 | return False 63 | 64 | print("Loading file template...", end="") 65 | with open("template0.dat", "rb") as file: 66 | data = file.read() 67 | finger.send_fpdata(list(data), "char", 2) 68 | 69 | i = finger.compare_templates() 70 | if i == adafruit_fingerprint.OK: 71 | set_led_local(color=2, speed=150, mode=6) 72 | print("Fingerprint match template in file.") 73 | return True 74 | if i == adafruit_fingerprint.NOMATCH: 75 | set_led_local(color=1, mode=2, speed=20, cycles=10) 76 | print("Templates do not match!") 77 | else: 78 | print("Other error!") 79 | return False 80 | 81 | 82 | def enroll_save_to_file(): 83 | """Take a 2 finger images and template it, then store it in a file""" 84 | set_led_local(color=3, mode=1) 85 | for fingerimg in range(1, 3): 86 | if fingerimg == 1: 87 | print("Place finger on sensor...", end="") 88 | else: 89 | print("Place same finger again...", end="") 90 | 91 | while True: 92 | i = finger.get_image() 93 | if i == adafruit_fingerprint.OK: 94 | print("Image taken") 95 | break 96 | if i == adafruit_fingerprint.NOFINGER: 97 | print(".", end="") 98 | elif i == adafruit_fingerprint.IMAGEFAIL: 99 | set_led_local(color=1, mode=2, speed=20, cycles=10) 100 | print("Imaging error") 101 | return False 102 | else: 103 | set_led_local(color=1, mode=2, speed=20, cycles=10) 104 | print("Other error") 105 | return False 106 | 107 | print("Templating...", end="") 108 | i = finger.image_2_tz(fingerimg) 109 | if i == adafruit_fingerprint.OK: 110 | print("Templated") 111 | else: 112 | if i == adafruit_fingerprint.IMAGEMESS: 113 | set_led_local(color=1, mode=2, speed=20, cycles=10) 114 | print("Image too messy") 115 | elif i == adafruit_fingerprint.FEATUREFAIL: 116 | set_led_local(color=1, mode=2, speed=20, cycles=10) 117 | print("Could not identify features") 118 | elif i == adafruit_fingerprint.INVALIDIMAGE: 119 | set_led_local(color=1, mode=2, speed=20, cycles=10) 120 | print("Image invalid") 121 | else: 122 | set_led_local(color=1, mode=2, speed=20, cycles=10) 123 | print("Other error") 124 | return False 125 | 126 | if fingerimg == 1: 127 | print("Remove finger") 128 | while i != adafruit_fingerprint.NOFINGER: 129 | i = finger.get_image() 130 | 131 | print("Creating model...", end="") 132 | i = finger.create_model() 133 | if i == adafruit_fingerprint.OK: 134 | print("Created") 135 | else: 136 | if i == adafruit_fingerprint.ENROLLMISMATCH: 137 | set_led_local(color=1, mode=2, speed=20, cycles=10) 138 | print("Prints did not match") 139 | else: 140 | set_led_local(color=1, mode=2, speed=20, cycles=10) 141 | print("Other error") 142 | return False 143 | 144 | print("Downloading template...") 145 | data = finger.get_fpdata("char", 1) 146 | with open("template0.dat", "wb") as file: 147 | file.write(bytearray(data)) 148 | set_led_local(color=2, speed=150, mode=6) 149 | print("Template is saved in template0.dat file.") 150 | 151 | return True 152 | 153 | 154 | def set_led_local(color=1, mode=3, speed=0x80, cycles=0): 155 | """this is to make sure LED doesn't interfer with example 156 | running on models without LED support - needs testing""" 157 | try: 158 | finger.set_led(color, mode, speed, cycles) 159 | except Exception as exc: 160 | print("INFO: Sensor les not support LED. Error:", str(exc)) 161 | 162 | 163 | set_led_local(color=3, mode=2, speed=10, cycles=10) 164 | 165 | while True: 166 | print("----------------") 167 | if finger.read_templates() != adafruit_fingerprint.OK: 168 | raise RuntimeError("Failed to read templates") 169 | print("Fingerprint templates: ", finger.templates) 170 | if finger.count_templates() != adafruit_fingerprint.OK: 171 | raise RuntimeError("Failed to read templates") 172 | print("Number of templates found: ", finger.template_count) 173 | if finger.set_sysparam(6, 2) != adafruit_fingerprint.OK: 174 | raise RuntimeError("Unable to set package size to 128!") 175 | if finger.read_sysparam() != adafruit_fingerprint.OK: 176 | raise RuntimeError("Failed to get system parameters") 177 | print("Package size (x128):", finger.data_packet_size) 178 | print("Size of template library: ", finger.library_size) 179 | print("e) enroll print and save to file") 180 | print("c) compare print to file") 181 | print("r) soft reset") 182 | print("x) quit") 183 | print("----------------") 184 | c = input("> ") 185 | 186 | if c in {"x", "q"}: 187 | print("Exiting fingerprint example program") 188 | # turn off LED 189 | set_led_local(mode=4) 190 | raise SystemExit 191 | if c == "e": 192 | enroll_save_to_file() 193 | elif c == "c": 194 | fingerprint_check_file() 195 | elif c == "r": 196 | sensor_reset() 197 | else: 198 | print("Invalid choice: Try again") 199 | -------------------------------------------------------------------------------- /examples/fingerprint_template_folder_compare_with_file_rpi.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2024 itsFDavid 2 | # SPDX-License-Identifier: MIT 3 | """ 4 | `fingerprint_template_folder_compare_with_file.py` 5 | ==================================================== 6 | 7 | This is an example program to demo storing fingerprint templates in a folder. It also allows 8 | comparing a newly obtained print with one stored in the folder in the previous step. This is helpful 9 | when fingerprint templates are stored centrally (not on sensor's flash memory) and shared 10 | between multiple sensors. 11 | 12 | * Author(s): itsFDavid 13 | 14 | Implementation Notes 15 | -------------------- 16 | This program was used on other fingerprint sensors, 17 | and everything worked as expected, including testing with Raspberry Pi Zero 2W. 18 | 19 | To run the program: 20 | 1. Connect the fingerprint sensor to your Raspberry Pi. 21 | 2. Install required libraries. 22 | 3. Execute the script using Python. 23 | """ 24 | 25 | import os 26 | import time 27 | 28 | import serial 29 | from PIL import Image 30 | 31 | import adafruit_fingerprint 32 | 33 | # If using with a computer such as Linux/RaspberryPi, Mac, Windows with USB/serial converter: 34 | # uart = serial.Serial("COM6", baudrate=57600, timeout=1) 35 | 36 | # If using with Linux/Raspberry Pi and hardware UART: 37 | uart = serial.Serial("/dev/ttyUSB0", baudrate=57600, timeout=1) 38 | 39 | finger = adafruit_fingerprint.Adafruit_Fingerprint(uart) 40 | 41 | # Folder where fingerprint templates are stored 42 | FINGERPRINT_FOLDER = "fingerprint/" 43 | 44 | 45 | # Enroll and verification functions 46 | def get_num(max_num): 47 | """Prompts the user to enter a valid template number within the available range.""" 48 | while True: 49 | try: 50 | num = int(input(f"Enter a template number (0-{max_num}): ")) 51 | if 0 <= num <= max_num: 52 | return num 53 | print(f"Please enter a number between 0 and {max_num}.") 54 | except ValueError: 55 | print("Invalid input. Please enter a valid number.") 56 | 57 | 58 | def get_fingerprint(): 59 | """Get an image from the fingerprint sensor for search, process for a match.""" 60 | print("Waiting for finger...") 61 | while finger.get_image() != adafruit_fingerprint.OK: 62 | pass 63 | print("Processing image...") 64 | if finger.image_2_tz(1) != adafruit_fingerprint.OK: 65 | print("Error processing image.") 66 | return False 67 | print("Searching for matches...") 68 | return finger.finger_search() == adafruit_fingerprint.OK 69 | 70 | 71 | def enroll_finger(location): 72 | """Enroll a fingerprint and store it in the specified location.""" 73 | for fingerimg in range(1, 3): 74 | action = "Place finger on sensor" if fingerimg == 1 else "Same finger again" 75 | print(action, end="") 76 | while True: 77 | if finger.get_image() == adafruit_fingerprint.OK: 78 | print("Image captured") 79 | break 80 | print(".", end="") 81 | print("Processing image...", end="") 82 | if finger.image_2_tz(fingerimg) != adafruit_fingerprint.OK: 83 | print("Error processing image.") 84 | return False 85 | if fingerimg == 1: 86 | print("Remove finger") 87 | time.sleep(1) 88 | while finger.get_image() != adafruit_fingerprint.NOFINGER: 89 | pass 90 | print("Creating model...", end="") 91 | if finger.create_model() != adafruit_fingerprint.OK: 92 | print("Error creating model.") 93 | return False 94 | print(f"Storing model in location #{location}...", end="") 95 | if finger.store_model(location) != adafruit_fingerprint.OK: 96 | print("Error storing model.") 97 | return False 98 | print("Model stored.") 99 | return True 100 | 101 | 102 | def save_fingerprint_image(filename): 103 | """Capture a fingerprint and save the image to a file.""" 104 | print("Waiting for finger...") 105 | while finger.get_image() != adafruit_fingerprint.OK: 106 | pass 107 | img = Image.new("L", (256, 288), "white") 108 | pixeldata = img.load() 109 | mask = 0b00001111 110 | result = finger.get_fpdata(sensorbuffer="image") 111 | coor_x, coor_y = 0, 0 112 | for i, value in enumerate(result): 113 | if i % 100 == 0: 114 | print("", end="") 115 | pixeldata[coor_x, coor_y] = (int(value) >> 4) * 17 116 | coor_x += 1 117 | pixeldata[coor_x, coor_y] = (int(value) & mask) * 17 118 | if coor_x == 255: 119 | coor_x = 0 120 | coor_y += 1 121 | else: 122 | coor_x += 1 123 | img.save(filename) 124 | print(f"\nImage saved to {filename}") 125 | return True 126 | 127 | 128 | def enroll_save_to_file(): 129 | """Capture a fingerprint, create a model, and save it to a file.""" 130 | for fingerimg in range(1, 3): 131 | action = "Place finger on sensor" if fingerimg == 1 else "Same finger again" 132 | print(action, end="") 133 | while True: 134 | if finger.get_image() == adafruit_fingerprint.OK: 135 | print("Image captured") 136 | break 137 | print(".", end="") 138 | print("Processing image...", end="") 139 | if finger.image_2_tz(fingerimg) != adafruit_fingerprint.OK: 140 | print("Error processing image.") 141 | return False 142 | if fingerimg == 1: 143 | print("Remove finger") 144 | while finger.get_image() != adafruit_fingerprint.NOFINGER: 145 | pass 146 | print("Creating model...", end="") 147 | if finger.create_model() != adafruit_fingerprint.OK: 148 | print("Error creating model.") 149 | return False 150 | print("Storing template...") 151 | data = finger.get_fpdata("char", 1) 152 | filename = os.path.join(FINGERPRINT_FOLDER, f"template_{int(time.time())}.dat") 153 | with open(filename, "wb") as file: 154 | file.write(bytearray(data)) 155 | print(f"Template saved to {filename}") 156 | return True 157 | 158 | 159 | def fingerprint_check_folder(): 160 | """Compare a fingerprint with all files in the fingerprint folder.""" 161 | print("Waiting for fingerprint...") 162 | while finger.get_image() != adafruit_fingerprint.OK: 163 | pass 164 | print("Processing image...") 165 | if finger.image_2_tz(1) != adafruit_fingerprint.OK: 166 | print("Error processing image.") 167 | return False 168 | print("Searching for matches in the template folder...", end="") 169 | found_match = False 170 | matched_filename = None 171 | for filename in os.listdir(FINGERPRINT_FOLDER): 172 | if filename.endswith(".dat"): 173 | file_path = os.path.join(FINGERPRINT_FOLDER, filename) 174 | with open(file_path, "rb") as file: 175 | data = file.read() 176 | finger.send_fpdata(list(data), "char", 2) 177 | if finger.compare_templates() == adafruit_fingerprint.OK: 178 | matched_filename = filename 179 | found_match = True 180 | break 181 | if found_match: 182 | print(f"Fingerprint matches the template in the file {matched_filename}!") 183 | else: 184 | print("No match found.") 185 | return found_match 186 | 187 | 188 | def main(): 189 | """Main function to run the fingerprint enrollment and verification program. 190 | This function provides a menu for the user to enroll fingerprints, search for 191 | fingerprints, delete templates, save fingerprint images, and reset the fingerprint library. 192 | It interacts with the user via the console and performs the necessary actions based on 193 | user input. 194 | """ 195 | while True: 196 | print("----------------") 197 | if finger.read_templates() != adafruit_fingerprint.OK: 198 | raise RuntimeError("Could not read templates.") 199 | print("Stored fingerprint templates: ", finger.templates) 200 | 201 | if finger.count_templates() != adafruit_fingerprint.OK: 202 | raise RuntimeError("Could not count templates.") 203 | print("Number of templates found: ", finger.template_count) 204 | 205 | if finger.read_sysparam() != adafruit_fingerprint.OK: 206 | raise RuntimeError("Could not retrieve system parameters.") 207 | print("Template library size: ", finger.library_size) 208 | print("Options:") 209 | print("e) Enroll fingerprint") 210 | print("f) Search fingerprint") 211 | print("d) Delete fingerprint") 212 | print("s) Save fingerprint image") 213 | print("cf) Compare template with file") 214 | print("esf) Enroll and save to file") 215 | print("r) Reset library") 216 | print("q) Exit") 217 | print("----------------") 218 | user_choice = input("> ") 219 | match user_choice.lower(): 220 | case "e": 221 | enroll_finger(get_num(finger.library_size)) 222 | case "f": 223 | print_fingerprint() 224 | case "d": 225 | delete_fingerprint() 226 | case "s": 227 | save_fingerprint_image(f"fingerprint_{int(time.time())}.png") 228 | case "cf": 229 | fingerprint_check_folder() 230 | case "esf": 231 | enroll_save_to_file() 232 | case "r": 233 | reset_library() 234 | case "q": 235 | exit_program() 236 | case _: 237 | print("Invalid option.") 238 | 239 | 240 | def print_fingerprint(): 241 | """Prints the fingerprint detection result.""" 242 | if get_fingerprint(): 243 | output_finger_detected = f"Fingerprint detected with ID #{finger.finger_id}" 244 | output_finger_confidence = f"Confidence: {finger.confidence}" 245 | print(output_finger_detected) 246 | print(output_finger_confidence) 247 | else: 248 | print("Fingerprint not found.") 249 | 250 | 251 | def delete_fingerprint(): 252 | """Deletes a fingerprint model based on user input.""" 253 | if finger.delete_model(get_num(finger.library_size)) == adafruit_fingerprint.OK: 254 | print("Deleted successfully!") 255 | else: 256 | print("Failed to delete.") 257 | 258 | 259 | def reset_library(): 260 | """Resets the fingerprint library.""" 261 | if finger.empty_library() == adafruit_fingerprint.OK: 262 | print("Library reset.") 263 | else: 264 | print("Failed to reset library.") 265 | 266 | 267 | def exit_program(): 268 | """Exits the program.""" 269 | print("Exiting...") 270 | raise SystemExit 271 | 272 | 273 | if __name__ == "__main__": 274 | main() 275 | -------------------------------------------------------------------------------- /optional_requirements.txt: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2022 Alec Delaney, for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: Unlicense 4 | 5 | numpy 6 | matplotlib 7 | pyserial 8 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2022 Alec Delaney for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | [build-system] 6 | requires = [ 7 | "setuptools", 8 | "wheel", 9 | "setuptools-scm", 10 | ] 11 | 12 | [project] 13 | name = "adafruit-circuitpython-fingerprint" 14 | description = "CircuitPython library for UART fingerprint sensor." 15 | version = "0.0.0+auto.0" 16 | readme = "README.rst" 17 | authors = [ 18 | {name = "Adafruit Industries", email = "circuitpython@adafruit.com"} 19 | ] 20 | urls = {Homepage = "https://github.com/adafruit/Adafruit_CircuitPython_Fingerprint"} 21 | keywords = [ 22 | "adafruit", 23 | "uart", 24 | "fingerprint", 25 | "finger", 26 | "print", 27 | "sensor", 28 | "hardware", 29 | "micropython", 30 | "circuitpython", 31 | ] 32 | license = {text = "MIT"} 33 | classifiers = [ 34 | "Intended Audience :: Developers", 35 | "Topic :: Software Development :: Libraries", 36 | "Topic :: Software Development :: Embedded Systems", 37 | "Topic :: System :: Hardware", 38 | "License :: OSI Approved :: MIT License", 39 | "Programming Language :: Python :: 3", 40 | ] 41 | dynamic = ["dependencies", "optional-dependencies"] 42 | 43 | [tool.setuptools] 44 | py-modules = ["adafruit_fingerprint"] 45 | 46 | [tool.setuptools.dynamic] 47 | dependencies = {file = ["requirements.txt"]} 48 | optional-dependencies = {optional = {file = ["optional_requirements.txt"]}} 49 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2022 Alec Delaney, for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: Unlicense 4 | 5 | Adafruit-Blinka 6 | pyserial 7 | -------------------------------------------------------------------------------- /ruff.toml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2024 Tim Cocks for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | target-version = "py38" 6 | line-length = 100 7 | 8 | [lint] 9 | preview = true 10 | select = ["I", "PL", "UP"] 11 | 12 | extend-select = [ 13 | "D419", # empty-docstring 14 | "E501", # line-too-long 15 | "W291", # trailing-whitespace 16 | "PLC0414", # useless-import-alias 17 | "PLC2401", # non-ascii-name 18 | "PLC2801", # unnecessary-dunder-call 19 | "PLC3002", # unnecessary-direct-lambda-call 20 | "E999", # syntax-error 21 | "PLE0101", # return-in-init 22 | "F706", # return-outside-function 23 | "F704", # yield-outside-function 24 | "PLE0116", # continue-in-finally 25 | "PLE0117", # nonlocal-without-binding 26 | "PLE0241", # duplicate-bases 27 | "PLE0302", # unexpected-special-method-signature 28 | "PLE0604", # invalid-all-object 29 | "PLE0605", # invalid-all-format 30 | "PLE0643", # potential-index-error 31 | "PLE0704", # misplaced-bare-raise 32 | "PLE1141", # dict-iter-missing-items 33 | "PLE1142", # await-outside-async 34 | "PLE1205", # logging-too-many-args 35 | "PLE1206", # logging-too-few-args 36 | "PLE1307", # bad-string-format-type 37 | "PLE1310", # bad-str-strip-call 38 | "PLE1507", # invalid-envvar-value 39 | "PLE2502", # bidirectional-unicode 40 | "PLE2510", # invalid-character-backspace 41 | "PLE2512", # invalid-character-sub 42 | "PLE2513", # invalid-character-esc 43 | "PLE2514", # invalid-character-nul 44 | "PLE2515", # invalid-character-zero-width-space 45 | "PLR0124", # comparison-with-itself 46 | "PLR0202", # no-classmethod-decorator 47 | "PLR0203", # no-staticmethod-decorator 48 | "UP004", # useless-object-inheritance 49 | "PLR0206", # property-with-parameters 50 | "PLR0904", # too-many-public-methods 51 | "PLR0911", # too-many-return-statements 52 | "PLR0912", # too-many-branches 53 | "PLR0913", # too-many-arguments 54 | "PLR0914", # too-many-locals 55 | "PLR0915", # too-many-statements 56 | "PLR0916", # too-many-boolean-expressions 57 | "PLR1702", # too-many-nested-blocks 58 | "PLR1704", # redefined-argument-from-local 59 | "PLR1711", # useless-return 60 | "C416", # unnecessary-comprehension 61 | "PLR1733", # unnecessary-dict-index-lookup 62 | "PLR1736", # unnecessary-list-index-lookup 63 | 64 | # ruff reports this rule is unstable 65 | #"PLR6301", # no-self-use 66 | 67 | "PLW0108", # unnecessary-lambda 68 | "PLW0120", # useless-else-on-loop 69 | "PLW0127", # self-assigning-variable 70 | "PLW0129", # assert-on-string-literal 71 | "B033", # duplicate-value 72 | "PLW0131", # named-expr-without-context 73 | "PLW0245", # super-without-brackets 74 | "PLW0406", # import-self 75 | "PLW0602", # global-variable-not-assigned 76 | "PLW0603", # global-statement 77 | "PLW0604", # global-at-module-level 78 | 79 | # fails on the try: import typing used by libraries 80 | #"F401", # unused-import 81 | 82 | "F841", # unused-variable 83 | "E722", # bare-except 84 | "PLW0711", # binary-op-exception 85 | "PLW1501", # bad-open-mode 86 | "PLW1508", # invalid-envvar-default 87 | "PLW1509", # subprocess-popen-preexec-fn 88 | "PLW2101", # useless-with-lock 89 | "PLW3301", # nested-min-max 90 | ] 91 | 92 | ignore = [ 93 | "PLR2004", # magic-value-comparison 94 | "UP030", # format literals 95 | "PLW1514", # unspecified-encoding 96 | "PLR0913", # too-many-arguments 97 | "PLR0915", # too-many-statements 98 | "PLR0917", # too-many-positional-arguments 99 | "PLR0904", # too-many-public-methods 100 | "PLR0912", # too-many-branches 101 | "PLR0916", # too-many-boolean-expressions 102 | ] 103 | 104 | [format] 105 | line-ending = "lf" 106 | --------------------------------------------------------------------------------