├── .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_led_animation ├── __init__.py ├── animation │ ├── __init__.py │ ├── blink.py │ ├── chase.py │ ├── colorcycle.py │ ├── comet.py │ ├── customcolorchase.py │ ├── grid_rain.py │ ├── multicolor_comet.py │ ├── pacman.py │ ├── pulse.py │ ├── rainbow.py │ ├── rainbowchase.py │ ├── rainbowcomet.py │ ├── rainbowsparkle.py │ ├── solid.py │ ├── sparkle.py │ ├── sparklepulse.py │ └── volume.py ├── color.py ├── grid.py ├── group.py ├── helper.py ├── pulse_generator.py ├── sequence.py └── timedsequence.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 ├── led_animation_all_animations.py ├── led_animation_basic_animations.py ├── led_animation_blink.py ├── led_animation_blink_with_background.py ├── led_animation_chase.py ├── led_animation_comet.py ├── led_animation_customcolorchase.py ├── led_animation_cycle_animations.py ├── led_animation_freeze_animation.py ├── led_animation_group.py ├── led_animation_multicolor_comet.py ├── led_animation_pacman.py ├── led_animation_pixel_map.py ├── led_animation_rainbow_animations.py ├── led_animation_resume_animation.py ├── led_animation_samd21_reset_interval.py ├── led_animation_sequence.py ├── led_animation_simpletest.py ├── led_animation_sparkle_animations.py ├── led_animation_sparkle_mask.py ├── led_animation_timedsequence.py └── led_animation_volume.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://circuitpython.readthedocs.io/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 Adam Patt 4 | Copyright (c) 2019-2020 Kattni Rembor 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /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 | Introduction 2 | ============ 3 | 4 | .. image:: https://readthedocs.org/projects/adafruit_circuitpython_led_animation/badge/?version=latest 5 | :target: https://docs.circuitpython.org/projects/led-animation/en/latest/ 6 | :alt: Documentation Status 7 | 8 | .. image:: https://raw.githubusercontent.com/adafruit/Adafruit_CircuitPython_Bundle/main/badges/adafruit_discord.svg 9 | :target: https://adafru.it/discord 10 | :alt: Discord 11 | 12 | .. image:: https://github.com/adafruit/Adafruit_CircuitPython_LED_Animation/workflows/Build%20CI/badge.svg 13 | :target: https://github.com/adafruit/Adafruit_CircuitPython_LED_Animation/actions 14 | :alt: Build Status 15 | 16 | .. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json 17 | :target: https://github.com/astral-sh/ruff 18 | :alt: Code Style: Ruff 19 | 20 | Perform a variety of LED animation tasks 21 | 22 | Dependencies 23 | ============= 24 | This driver depends on: 25 | 26 | * `Adafruit CircuitPython `_ 27 | 28 | Please ensure all dependencies are available on the CircuitPython filesystem. 29 | This is easily achieved by downloading 30 | `the Adafruit library and driver bundle `_. 31 | 32 | 33 | Installing from PyPI 34 | ===================== 35 | On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally `from 36 | PyPI `_. To install for current user: 37 | 38 | .. code-block:: shell 39 | 40 | pip3 install adafruit-circuitpython-led-animation 41 | 42 | To install system-wide (this may be required in some cases): 43 | 44 | .. code-block:: shell 45 | 46 | sudo pip3 install adafruit-circuitpython-led-animation 47 | 48 | To install in a virtual environment in your current project: 49 | 50 | .. code-block:: shell 51 | 52 | mkdir project-name && cd project-name 53 | python3 -m venv .venv 54 | source .venv/bin/activate 55 | pip3 install adafruit-circuitpython-led-animation 56 | 57 | Usage Example 58 | ============= 59 | 60 | .. code-block:: python 61 | 62 | import board 63 | import neopixel 64 | from adafruit_led_animation.animation.blink import Blink 65 | import adafruit_led_animation.color as color 66 | 67 | # Works on Circuit Playground Express and Bluefruit. 68 | # For other boards, change board.NEOPIXEL to match the pin to which the NeoPixels are attached. 69 | pixel_pin = board.NEOPIXEL 70 | # Change to match the number of pixels you have attached to your board. 71 | num_pixels = 10 72 | 73 | pixels = neopixel.NeoPixel(pixel_pin, num_pixels) 74 | blink = Blink(pixels, 0.5, color.PURPLE) 75 | 76 | while True: 77 | blink.animate() 78 | 79 | Documentation 80 | ============= 81 | 82 | API documentation for this library can be found on `Read the Docs `_. 83 | 84 | For information on building library documentation, please check out `this guide `_. 85 | 86 | Contributing 87 | ============ 88 | 89 | Contributions are welcome! Please read our `Code of Conduct 90 | `_ 91 | before contributing to help this project stay welcoming. 92 | 93 | Building locally 94 | ================ 95 | 96 | Zip release files 97 | ----------------- 98 | 99 | To build this library locally you'll need to install the 100 | `circuitpython-build-tools `_ package. 101 | 102 | .. code-block:: shell 103 | 104 | python3 -m venv .venv 105 | source .venv/bin/activate 106 | pip install circuitpython-build-tools 107 | 108 | Once installed, make sure you are in the virtual environment: 109 | 110 | .. code-block:: shell 111 | 112 | source .venv/bin/activate 113 | 114 | Then run the build: 115 | 116 | .. code-block:: shell 117 | 118 | circuitpython-build-bundles --filename_prefix circuitpython-led_animation --library_location . 119 | 120 | Sphinx documentation 121 | ----------------------- 122 | 123 | Sphinx is used to build the documentation based on rST files and comments in the code. First, 124 | install dependencies (feel free to reuse the virtual environment from above): 125 | 126 | .. code-block:: shell 127 | 128 | python3 -m venv .venv 129 | source .venv/bin/activate 130 | pip install Sphinx sphinx-rtd-theme 131 | 132 | Now, once you have the virtual environment activated: 133 | 134 | .. code-block:: shell 135 | 136 | cd docs 137 | sphinx-build -E -W -b html . _build/html 138 | 139 | This will output the documentation to ``docs/_build/html``. Open the index.html in your browser to 140 | view them. It will also (due to -W) error out on any warning like Travis will. This is a good way to 141 | locally verify it will pass. 142 | -------------------------------------------------------------------------------- /README.rst.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries 2 | 3 | SPDX-License-Identifier: MIT 4 | -------------------------------------------------------------------------------- /adafruit_led_animation/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 Kattni Rembor for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | Timing for Adafruit LED Animation library. 7 | 8 | Author(s): Kattni Rembor 9 | """ 10 | 11 | try: 12 | from micropython import const 13 | except ImportError: 14 | 15 | def const(value): 16 | return value 17 | 18 | 19 | try: 20 | from time import monotonic_ns 21 | 22 | monotonic_ns() # Test monotonic_ns in 6.x 23 | 24 | def monotonic_ms(): 25 | """ 26 | Return monotonic time in milliseconds. 27 | """ 28 | return monotonic_ns() // NANOS_PER_MS 29 | 30 | except (ImportError, NotImplementedError): 31 | import time 32 | 33 | def monotonic_ms(): 34 | """ 35 | Implementation of monotonic_ms for platforms without time.monotonic_ns 36 | """ 37 | return int(time.monotonic() * MS_PER_SECOND) 38 | 39 | 40 | NANOS_PER_MS = const(1000000) 41 | MS_PER_SECOND = const(1000) 42 | -------------------------------------------------------------------------------- /adafruit_led_animation/animation/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 Kattni Rembor for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | `adafruit_led_animation.animation` 7 | ================================================================================ 8 | 9 | Animation base class for CircuitPython helper library for LED animations. 10 | 11 | * Author(s): Kattni Rembor 12 | 13 | Implementation Notes 14 | -------------------- 15 | 16 | **Hardware:** 17 | 18 | * `Adafruit NeoPixels `_ 19 | * `Adafruit DotStars `_ 20 | 21 | **Software and Dependencies:** 22 | 23 | * Adafruit CircuitPython firmware for the supported boards: 24 | https://circuitpython.org/downloads 25 | 26 | """ 27 | 28 | __version__ = "0.0.0+auto.0" 29 | __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_LED_Animation.git" 30 | 31 | from adafruit_led_animation import MS_PER_SECOND, monotonic_ms 32 | 33 | 34 | class Animation: 35 | """ 36 | Base class for animations. 37 | """ 38 | 39 | on_cycle_complete_supported = False 40 | 41 | def __init__(self, pixel_object, speed, color, peers=None, paused=False, name=None): 42 | self.pixel_object = pixel_object 43 | self.pixel_object.auto_write = False 44 | self._peers = [self] + peers if peers is not None else [self] 45 | self._speed_ms = 0 46 | self._color = None 47 | self._paused = paused 48 | self._next_update = monotonic_ms() 49 | self._time_left_at_pause = 0 50 | self._also_notify = [] 51 | self.speed = speed # sets _speed_ms 52 | self.color = color # Triggers _set_color 53 | self.name = name 54 | self.cycle_complete = False 55 | self.notify_cycles = 1 56 | """Number of cycles to trigger additional cycle_done notifications after""" 57 | self.draw_count = 0 58 | """Number of animation frames drawn.""" 59 | self.cycle_count = 0 60 | """Number of animation cycles completed.""" 61 | 62 | def __str__(self): 63 | return f"<{self.__class__.__name__}: {self.name}>" 64 | 65 | def animate(self, show=True): 66 | """ 67 | Call animate() from your code's main loop. It will draw the animation draw() at intervals 68 | configured by the speed property (set from init). 69 | 70 | :param bool show: Whether to automatically call show on the pixel object when an animation 71 | fires. Default True. 72 | :return: True if the animation draw cycle was triggered, otherwise False. 73 | """ 74 | if self._paused: 75 | return False 76 | 77 | now = monotonic_ms() 78 | if now < self._next_update: 79 | return False 80 | 81 | # Draw related animations together 82 | for anim in self._peers: 83 | anim.draw_count += 1 84 | anim.draw() 85 | anim.after_draw() 86 | 87 | if show: 88 | for anim in self._peers: 89 | anim.show() 90 | 91 | # Note that the main animation cycle_complete flag is used, not the peer flag. 92 | for anim in self._peers: 93 | if anim.cycle_complete: 94 | anim.cycle_complete = False 95 | anim.on_cycle_complete() 96 | 97 | self._next_update = now + self._speed_ms 98 | return True 99 | 100 | def draw(self): 101 | """ 102 | Animation subclasses must implement draw() to render the animation sequence. 103 | Animations should not call show(), as animate() will do so, after after_draw(). 104 | Animations should set .cycle_done = True when an animation cycle is completed. 105 | """ 106 | raise NotImplementedError() 107 | 108 | def after_draw(self): 109 | """ 110 | Animation subclasses may implement after_draw() to do operations after the main draw() 111 | is called. 112 | """ 113 | 114 | def show(self): 115 | """ 116 | Displays the updated pixels. Called during animates with changes. 117 | """ 118 | self.pixel_object.show() 119 | 120 | @property 121 | def peers(self): 122 | """ 123 | Get the animation's peers. Peers are drawn, then shown together. 124 | """ 125 | return self._peers[1:] 126 | 127 | @peers.setter 128 | def peers(self, peer_list): 129 | """ 130 | Set the animation's peers. 131 | :param list peer_list: List of peer animations. 132 | """ 133 | if peer_list is not None: 134 | self._peers = [self] + list(peer_list) 135 | 136 | def freeze(self): 137 | """ 138 | Stops the animation until resumed. 139 | """ 140 | self._paused = True 141 | self._time_left_at_pause = max(0, monotonic_ms() - self._next_update) 142 | 143 | def resume(self): 144 | """ 145 | Resumes the animation. 146 | """ 147 | self._next_update = monotonic_ms() + self._time_left_at_pause 148 | self._time_left_at_pause = 0 149 | self._paused = False 150 | 151 | def fill(self, color): 152 | """ 153 | Fills the pixel object with a color. 154 | """ 155 | self.pixel_object.fill(color) 156 | self.pixel_object.show() 157 | 158 | @property 159 | def color(self): 160 | """ 161 | The current color. 162 | """ 163 | return self._color 164 | 165 | @color.setter 166 | def color(self, color): 167 | if self._color == color: 168 | return 169 | if isinstance(color, int): 170 | color = (color >> 16 & 0xFF, color >> 8 & 0xFF, color & 0xFF) 171 | self._set_color(color) 172 | 173 | def _set_color(self, color): 174 | """ 175 | Called after the color is changed, which includes at initialization. 176 | Override as needed. 177 | """ 178 | self._color = color 179 | 180 | @property 181 | def speed(self): 182 | """ 183 | The animation speed in fractional seconds. 184 | """ 185 | return self._speed_ms / MS_PER_SECOND 186 | 187 | @speed.setter 188 | def speed(self, seconds): 189 | self._speed_ms = int(seconds * MS_PER_SECOND) 190 | 191 | def on_cycle_complete(self): 192 | """ 193 | Called by some animations when they complete an animation cycle. 194 | Animations that support cycle complete notifications will have X property set to False. 195 | Override as needed. 196 | """ 197 | self.cycle_count += 1 198 | if self.cycle_count % self.notify_cycles == 0: 199 | for callback in self._also_notify: 200 | callback(self) 201 | 202 | def add_cycle_complete_receiver(self, callback): 203 | """ 204 | Adds an additional callback when the cycle completes. 205 | 206 | :param callback: Additional callback to trigger when a cycle completes. The callback 207 | is passed the animation object instance. 208 | """ 209 | self._also_notify.append(callback) 210 | 211 | def reset(self): 212 | """ 213 | Resets the animation sequence. 214 | """ 215 | -------------------------------------------------------------------------------- /adafruit_led_animation/animation/blink.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 Kattni Rembor for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | `adafruit_led_animation.animation.blink` 7 | ================================================================================ 8 | 9 | Blink animation for CircuitPython helper library for LED animations. 10 | 11 | * Author(s): Kattni Rembor 12 | 13 | Implementation Notes 14 | -------------------- 15 | 16 | **Hardware:** 17 | 18 | * `Adafruit NeoPixels `_ 19 | * `Adafruit DotStars `_ 20 | 21 | **Software and Dependencies:** 22 | 23 | * Adafruit CircuitPython firmware for the supported boards: 24 | https://circuitpython.org/downloads 25 | 26 | 27 | """ 28 | 29 | from adafruit_led_animation.animation.colorcycle import ColorCycle 30 | from adafruit_led_animation.color import BLACK 31 | 32 | 33 | class Blink(ColorCycle): 34 | """ 35 | Blink a color on and off. 36 | 37 | :param pixel_object: The initialised LED object. 38 | :param float speed: Animation speed in seconds, e.g. ``0.1``. 39 | :param color: Animation color in ``(r, g, b)`` tuple, or ``0x000000`` hex format. 40 | :param background_color: Background color in ``(r, g, b)`` tuple, or ``0x000000`` 41 | hex format. Defaults to BLACK. 42 | :param name: A human-readable name for the Animation. Used by the string function. 43 | """ 44 | 45 | def __init__(self, pixel_object, speed, color, background_color=BLACK, name=None): 46 | self._background_color = background_color 47 | super().__init__(pixel_object, speed, [color, background_color], name=name) 48 | 49 | def _set_color(self, color): 50 | self.colors = [color, self._background_color] 51 | -------------------------------------------------------------------------------- /adafruit_led_animation/animation/chase.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 Kattni Rembor for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | `adafruit_led_animation.animation.chase` 7 | ================================================================================ 8 | 9 | Theatre chase animation for CircuitPython helper library for LED animations. 10 | 11 | * Author(s): Kattni Rembor 12 | 13 | Implementation Notes 14 | -------------------- 15 | 16 | **Hardware:** 17 | 18 | * `Adafruit NeoPixels `_ 19 | * `Adafruit DotStars `_ 20 | 21 | **Software and Dependencies:** 22 | 23 | * Adafruit CircuitPython firmware for the supported boards: 24 | https://circuitpython.org/downloads 25 | 26 | 27 | """ 28 | 29 | from math import ceil 30 | 31 | from adafruit_led_animation.animation import Animation 32 | 33 | 34 | class Chase(Animation): 35 | """ 36 | Chase pixels in one direction in a single color, like a theater marquee sign. 37 | 38 | :param pixel_object: The initialised LED object. 39 | :param float speed: Animation speed rate in seconds, e.g. ``0.1``. 40 | :param color: Animation color in ``(r, g, b)`` tuple, or ``0x000000`` hex format. 41 | :param size: Number of pixels to turn on in a row. 42 | :param spacing: Number of pixels to turn off in a row. 43 | :param reverse: Reverse direction of movement. 44 | """ 45 | 46 | def __init__(self, pixel_object, speed, color, size=2, spacing=3, reverse=False, name=None): 47 | self._size = size 48 | self._spacing = spacing 49 | self._repeat_width = size + spacing 50 | self._num_repeats = ceil(len(pixel_object) / self._repeat_width) 51 | self._overflow = len(pixel_object) % self._repeat_width 52 | self._direction = 1 if not reverse else -1 53 | self._reverse = reverse 54 | self._offset = 0 55 | 56 | def _resetter(): 57 | self._offset = 0 58 | self._reverse = reverse 59 | self._direction = 1 if not reverse else -1 60 | 61 | self._reset = _resetter 62 | 63 | super().__init__(pixel_object, speed, color, name=name) 64 | 65 | on_cycle_complete_supported = True 66 | 67 | @property 68 | def reverse(self): 69 | """ 70 | Whether the animation is reversed 71 | """ 72 | return self._reverse 73 | 74 | @reverse.setter 75 | def reverse(self, value): 76 | self._reverse = value 77 | self._direction = -1 if self._reverse else 1 78 | 79 | def draw(self): 80 | def bar_colors(): 81 | bar_no = 0 82 | for i in range(self._offset, 0, -1): 83 | if i > self._spacing: 84 | yield self.bar_color(bar_no, i) 85 | else: 86 | yield self.space_color(bar_no, i) 87 | bar_no = 1 88 | while True: 89 | for bar_pixel in range(self._size): 90 | yield self.bar_color(bar_no, bar_pixel) 91 | for space_pixel in range(self._spacing): 92 | yield self.space_color(bar_no, space_pixel) 93 | bar_no += 1 94 | 95 | colorgen = bar_colors() 96 | self.pixel_object[:] = [next(colorgen) for _ in range(len(self.pixel_object))] 97 | 98 | if self.draw_count % len(self.pixel_object) == 0: 99 | self.cycle_complete = True 100 | self._offset = (self._offset + self._direction) % self._repeat_width 101 | 102 | def bar_color(self, n, pixel_no=0): 103 | """ 104 | Generate the color for the n'th bar_color in the Chase 105 | 106 | :param n: The pixel group to get the color for 107 | :param pixel_no: Which pixel in the group to get the color for 108 | """ 109 | return self.color 110 | 111 | @staticmethod 112 | def space_color(n, pixel_no=0): 113 | """ 114 | Generate the spacing color for the n'th bar_color in the Chase 115 | 116 | :param n: The pixel group to get the spacing color for 117 | :param pixel_no: Which pixel in the group to get the spacing color for 118 | """ 119 | return 0 120 | 121 | def reset(self): 122 | """ 123 | Reset the animation. 124 | """ 125 | self._reset() 126 | -------------------------------------------------------------------------------- /adafruit_led_animation/animation/colorcycle.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 Kattni Rembor for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | `adafruit_led_animation.animation.colorcycle` 7 | ================================================================================ 8 | 9 | Color cycle animation for CircuitPython helper library for LED animations. 10 | 11 | * Author(s): Kattni Rembor 12 | 13 | Implementation Notes 14 | -------------------- 15 | 16 | **Hardware:** 17 | 18 | * `Adafruit NeoPixels `_ 19 | * `Adafruit DotStars `_ 20 | 21 | **Software and Dependencies:** 22 | 23 | * Adafruit CircuitPython firmware for the supported boards: 24 | https://circuitpython.org/downloads 25 | 26 | 27 | """ 28 | 29 | from adafruit_led_animation.animation import Animation 30 | from adafruit_led_animation.color import RAINBOW 31 | 32 | 33 | class ColorCycle(Animation): 34 | """ 35 | Animate a sequence of one or more colors, cycling at the specified speed. 36 | 37 | :param pixel_object: The initialised LED object. 38 | :param float speed: Animation speed in seconds, e.g. ``0.1``. 39 | :param colors: A list of colors to cycle through in ``(r, g, b)`` tuple, or ``0x000000`` hex 40 | format. Defaults to a rainbow color cycle. 41 | :param start_color: An index (from 0) for which color to start from. Default 0 (first color). 42 | """ 43 | 44 | def __init__(self, pixel_object, speed, colors=RAINBOW, name=None, start_color=0): 45 | self.colors = colors 46 | self.start_color = start_color 47 | super().__init__(pixel_object, speed, colors[start_color], name=name) 48 | self._generator = self._color_generator(start_color) 49 | next(self._generator) 50 | 51 | on_cycle_complete_supported = True 52 | 53 | def draw(self): 54 | self.pixel_object.fill(self.color) 55 | next(self._generator) 56 | 57 | def _color_generator(self, start_color): 58 | index = start_color 59 | while True: 60 | self._color = self.colors[index] 61 | yield 62 | index = (index + 1) % len(self.colors) 63 | if index == start_color: 64 | self.cycle_complete = True 65 | 66 | def reset(self): 67 | """ 68 | Resets to the first color. 69 | """ 70 | self._generator = self._color_generator(self.start_color) 71 | -------------------------------------------------------------------------------- /adafruit_led_animation/animation/comet.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 Kattni Rembor for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | `adafruit_led_animation.animation.comet` 7 | ================================================================================ 8 | 9 | Comet animation for CircuitPython helper library for LED animations. 10 | 11 | * Author(s): Kattni Rembor 12 | 13 | Implementation Notes 14 | -------------------- 15 | 16 | **Hardware:** 17 | 18 | * `Adafruit NeoPixels `_ 19 | * `Adafruit DotStars `_ 20 | 21 | **Software and Dependencies:** 22 | 23 | * Adafruit CircuitPython firmware for the supported boards: 24 | https://circuitpython.org/downloads 25 | 26 | 27 | """ 28 | 29 | from adafruit_led_animation.animation import Animation 30 | from adafruit_led_animation.color import BLACK, calculate_intensity 31 | 32 | 33 | class Comet(Animation): 34 | """ 35 | A comet animation. 36 | 37 | :param pixel_object: The initialised LED object. 38 | :param float speed: Animation speed in seconds, e.g. ``0.1``. 39 | :param color: Animation color in ``(r, g, b)`` tuple, or ``0x000000`` hex format. 40 | :param background_color: Background color in ``(r, g, b)`` tuple, or ``0x000000`` hex format. 41 | Defaults to BLACK. 42 | :param int tail_length: The length of the comet. Defaults to 25% of the length of the 43 | ``pixel_object``. Automatically compensates for a minimum of 2 and a 44 | maximum of the length of the ``pixel_object``. 45 | :param bool reverse: Animates the comet in the reverse order. Defaults to ``False``. 46 | :param bool bounce: Comet will bounce back and forth. Defaults to ``False``. 47 | :param Optional[string] name: A human-readable name for the Animation. 48 | Used by the to string function. 49 | :param bool ring: Ring mode. Defaults to ``False``. 50 | """ 51 | 52 | def __init__( 53 | self, 54 | pixel_object, 55 | speed, 56 | color, 57 | background_color=BLACK, 58 | tail_length=0, 59 | reverse=False, 60 | bounce=False, 61 | name=None, 62 | ring=False, 63 | ): 64 | if tail_length == 0: 65 | tail_length = len(pixel_object) // 4 66 | if bounce and ring: 67 | raise ValueError("Cannot combine bounce and ring mode") 68 | self.bounce = bounce 69 | self._reverse = reverse 70 | self._initial_reverse = reverse 71 | self._tail_length = tail_length 72 | self._color_step = 0.95 / tail_length 73 | self._comet_colors = None 74 | self._computed_color = color 75 | self._background_color = background_color 76 | self._num_pixels = len(pixel_object) 77 | self._direction = -1 if reverse else 1 78 | self._left_side = -self._tail_length 79 | self._right_side = self._num_pixels 80 | self._tail_start = 0 81 | self._ring = ring 82 | if ring: 83 | self._left_side = 0 84 | self.reset() 85 | super().__init__(pixel_object, speed, color, name=name) 86 | 87 | on_cycle_complete_supported = True 88 | 89 | def _set_color(self, color): 90 | self._comet_colors = [self._background_color] 91 | for n in range(self._tail_length): 92 | self._comet_colors.append(calculate_intensity(color, n * self._color_step + 0.05)) 93 | self._computed_color = color 94 | 95 | @property 96 | def reverse(self): 97 | """ 98 | Whether the animation is reversed 99 | """ 100 | return self._reverse 101 | 102 | @reverse.setter 103 | def reverse(self, value): 104 | self._reverse = value 105 | self._direction = -1 if self._reverse else 1 106 | 107 | @property 108 | def ring(self): 109 | """ 110 | Ring mode. 111 | """ 112 | return self._ring 113 | 114 | @ring.setter 115 | def ring(self, value): 116 | if self.bounce and value: 117 | raise ValueError("Cannot combine bounce and ring mode") 118 | self._ring = value 119 | self._left_side = 0 if value else -self._tail_length 120 | self.reset() 121 | 122 | def draw(self): 123 | colors = self._comet_colors 124 | if self.reverse: 125 | colors = reversed(colors) 126 | 127 | pixels = self.pixel_object 128 | start = self._tail_start 129 | npixels = len(pixels) 130 | if self._ring: 131 | start %= npixels 132 | for color in colors: 133 | pixels[start] = color 134 | start += 1 135 | if start == npixels: 136 | start = 0 137 | else: 138 | for color in colors: 139 | if start >= npixels: 140 | break 141 | if start >= 0: 142 | pixels[start] = color 143 | start += 1 144 | 145 | self._tail_start += self._direction 146 | 147 | if self._tail_start < self._left_side or ( 148 | self._tail_start >= self._right_side and not self._reverse 149 | ): 150 | if self.bounce: 151 | self.reverse = not self.reverse 152 | elif self._ring: 153 | self._tail_start = self._tail_start % self._num_pixels 154 | else: 155 | self.reset() 156 | 157 | self.cycle_complete = True 158 | 159 | def reset(self): 160 | """ 161 | Resets to the first state. 162 | """ 163 | if self.reverse: 164 | self._tail_start = self._num_pixels + self._tail_length + 1 165 | else: 166 | self._tail_start = -self._tail_length - 1 167 | 168 | if self._ring: 169 | self._tail_start = self._tail_start % self._num_pixels 170 | -------------------------------------------------------------------------------- /adafruit_led_animation/animation/customcolorchase.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 Kattni Rembor for Adafruit Industries 2 | # SPDX-FileCopyrightText: 2020 Connie Sieh for Adafruit Industries 3 | # 4 | # SPDX-License-Identifier: MIT 5 | 6 | """ 7 | `adafruit_led_animation.animation.customcolorchase` 8 | ================================================================================ 9 | 10 | Custom color chase animation for CircuitPython helper library for LED animations. 11 | 12 | * Author(s): Kattni Rembor, Connie Sieh 13 | 14 | Implementation Notes 15 | -------------------- 16 | 17 | **Hardware:** 18 | 19 | * `Adafruit NeoPixels `_ 20 | * `Adafruit DotStars `_ 21 | 22 | **Software and Dependencies:** 23 | 24 | * Adafruit CircuitPython firmware for the supported boards: 25 | https://circuitpython.org/downloads 26 | 27 | 28 | """ 29 | 30 | from adafruit_led_animation.animation.chase import Chase 31 | from adafruit_led_animation.color import RAINBOW 32 | 33 | 34 | class CustomColorChase(Chase): 35 | """ 36 | Chase pixels in one direction, like a theater marquee with Custom Colors 37 | 38 | :param pixel_object: The initialised LED object. 39 | :param float speed: Animation speed rate in seconds, e.g. ``0.1``. 40 | :param colors: Animation colors in list of `(r, g, b)`` tuple, or ``0x000000`` hex format 41 | :param size: Number of pixels to turn on in a row. 42 | :param spacing: Number of pixels to turn off in a row. 43 | :param reverse: Reverse direction of movement. 44 | """ 45 | 46 | def __init__( 47 | self, 48 | pixel_object, 49 | speed, 50 | size=2, 51 | spacing=3, 52 | reverse=False, 53 | name=None, 54 | colors=RAINBOW, 55 | ): 56 | self._num_colors = len(colors) 57 | self._colors = colors 58 | self._color_idx = 0 59 | super().__init__(pixel_object, speed, 0, size, spacing, reverse, name) 60 | 61 | def bar_color(self, n, pixel_no=0): 62 | return self._colors[self._color_idx - (n % len(self._colors))] 63 | 64 | def on_cycle_complete(self): 65 | self._color_idx = (self._color_idx + self._direction) % len(self._colors) 66 | super().on_cycle_complete() 67 | -------------------------------------------------------------------------------- /adafruit_led_animation/animation/grid_rain.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 Kattni Rembor for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | `adafruit_led_animation.animation.grid_rain` 7 | ================================================================================ 8 | 9 | Rain animations for CircuitPython helper library for LED animations. 10 | 11 | * Author(s): Kattni Rembor 12 | 13 | Implementation Notes 14 | -------------------- 15 | 16 | **Hardware:** 17 | 18 | * `Adafruit NeoPixels `_ 19 | * `Adafruit DotStars `_ 20 | 21 | **Software and Dependencies:** 22 | 23 | * Adafruit CircuitPython firmware for the supported boards: 24 | https://circuitpython.org/downloads 25 | 26 | """ 27 | 28 | import random 29 | 30 | from adafruit_led_animation.animation import Animation 31 | 32 | __version__ = "0.0.0+auto.0" 33 | __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_LED_Animation.git" 34 | 35 | from adafruit_led_animation.color import BLACK, GREEN, calculate_intensity, colorwheel 36 | 37 | 38 | class Rain(Animation): 39 | """ 40 | Droplets of rain. 41 | 42 | :param grid_object: The initialised PixelGrid object. 43 | :param float speed: Animation speed in seconds, e.g. ``0.1``. 44 | :param color: Animation color in ``(r, g, b)`` tuple, or ``0x000000`` hex format. 45 | :param count: Number of sparkles to generate per animation cycle. 46 | :param length: Number of pixels per raindrop (Default 3) 47 | :param background: Background color (Default BLACK). 48 | """ 49 | 50 | def __init__(self, grid_object, speed, color, count=1, length=3, background=BLACK, name=None): 51 | self._count = count 52 | self._length = length 53 | self._background = background 54 | self._raindrops = [] 55 | super().__init__(grid_object, speed, color, name=name) 56 | 57 | def draw(self): 58 | # Move raindrops down 59 | keep = [] 60 | for raindrop in self._raindrops: 61 | pixels = [] 62 | if raindrop[1][0][0] >= 0: 63 | self.pixel_object[raindrop[0], raindrop[1][0][0]] = self._background 64 | for pixel in raindrop[1]: 65 | pixel[0] += 1 66 | if pixel[0] < self.pixel_object.height: 67 | pixels.append(pixel) 68 | if pixels: 69 | keep.append([raindrop[0], pixels]) 70 | self._raindrops = keep 71 | 72 | # Add a raindrop 73 | if len(self._raindrops) < self._count: 74 | x = random.randint(0, self.pixel_object.width - 1) 75 | self._raindrops.append([x, self._generate_droplet(x, self._length)]) 76 | 77 | # Draw raindrops 78 | for x, pixels in self._raindrops: 79 | for y, color in pixels: 80 | if y >= 0: 81 | self.pixel_object[x, y] = color 82 | 83 | def _generate_droplet(self, x, length): 84 | return [[n, self.color] for n in range(-length, 0)] 85 | 86 | 87 | class RainbowRain(Rain): 88 | """ 89 | Rainbow Rain animation. 90 | """ 91 | 92 | def __init__(self, grid_object, speed, count=1, length=3, background=BLACK, name=None): 93 | super().__init__(grid_object, speed, BLACK, count, length, background, name) 94 | 95 | def _generate_droplet(self, x, length): 96 | color = colorwheel(random.randint(0, 255)) 97 | return [ 98 | [n, calculate_intensity(color, 1.0 - -((n + 1) / (length + 1)))] 99 | for n in range(-length, 0) 100 | ] 101 | 102 | 103 | class MatrixRain(Rain): 104 | """ 105 | The Matrix style animation. 106 | """ 107 | 108 | def __init__( 109 | self, 110 | grid_object, 111 | speed, 112 | color=GREEN, 113 | count=1, 114 | length=6, 115 | background=(0, 32, 0), 116 | name=None, 117 | ): 118 | super().__init__(grid_object, speed, color, count, length, background, name) 119 | 120 | def _generate_droplet(self, x, length): 121 | return [ 122 | [n, calculate_intensity(self.color, random.randint(10, 100) * 1.0)] 123 | for n in range(-length, 0) 124 | ] 125 | -------------------------------------------------------------------------------- /adafruit_led_animation/animation/multicolor_comet.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2022 Tim Cocks 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | `adafruit_led_animation.animation.multicolor_comet` 7 | ================================================================================ 8 | 9 | Multi-color Comet animation for CircuitPython helper library for LED animations. 10 | 11 | * Author(s): Kattni Rembor, Tim Cocks 12 | 13 | Implementation Notes 14 | -------------------- 15 | 16 | **Hardware:** 17 | 18 | * `Adafruit NeoPixels `_ 19 | * `Adafruit DotStars `_ 20 | 21 | **Software and Dependencies:** 22 | 23 | * Adafruit CircuitPython firmware for the supported boards: 24 | https://circuitpython.org/downloads 25 | 26 | 27 | """ 28 | 29 | from adafruit_led_animation.animation.comet import Comet 30 | from adafruit_led_animation.color import BLACK 31 | 32 | 33 | class MulticolorComet(Comet): 34 | """ 35 | A multi-color comet animation. 36 | 37 | :param pixel_object: The initialised LED object. 38 | :param float speed: Animation speed in seconds, e.g. ``0.1``. 39 | :param colors: Animation colors in a list or tuple of entries in 40 | ``(r, g, b)`` tuple, or ``0x000000`` hex format. 41 | :param int tail_length: The length of the comet. Defaults to 25% of the length of the 42 | ``pixel_object``. Automatically compensates for a minimum of 2 and a 43 | maximum of the length of the ``pixel_object``. 44 | :param bool reverse: Animates the comet in the reverse order. Defaults to ``False``. 45 | :param bool bounce: Comet will bounce back and forth. Defaults to ``True``. 46 | :param Optional[string] name: A human-readable name for the Animation. 47 | Used by the to string function. 48 | :param bool ring: Ring mode. Defaults to ``False``. 49 | :param bool off_pixels: Turn pixels off after the animation passes them. Defaults to ``True``. 50 | Setting to False will result in all pixels not currently in the comet 51 | to remain on and set to a color after the comet passes. 52 | """ 53 | 54 | def __init__( 55 | self, 56 | pixel_object, 57 | speed, 58 | colors, 59 | *, 60 | tail_length=0, 61 | reverse=False, 62 | bounce=False, 63 | name=None, 64 | ring=False, 65 | off_pixels=True, 66 | ): 67 | if tail_length == 0: 68 | tail_length = len(pixel_object) // 4 69 | if bounce and ring: 70 | raise ValueError("Cannot combine bounce and ring mode") 71 | self.bounce = bounce 72 | self._reverse = reverse 73 | self._initial_reverse = reverse 74 | self._tail_length = tail_length 75 | 76 | self._comet_colors = None 77 | 78 | self._num_pixels = len(pixel_object) 79 | self._direction = -1 if reverse else 1 80 | self._left_side = -self._tail_length 81 | self._right_side = self._num_pixels 82 | self._tail_start = 0 83 | self._ring = ring 84 | self._colors = colors 85 | if colors is None or len(colors) < 2: 86 | raise ValueError("Must pass at least two colors.") 87 | 88 | self._off_pixels = off_pixels 89 | if ring: 90 | self._left_side = 0 91 | self.reset() 92 | super().__init__( 93 | pixel_object, 94 | speed, 95 | 0x0, 96 | name=name, 97 | tail_length=tail_length, 98 | bounce=bounce, 99 | ring=ring, 100 | reverse=reverse, 101 | ) 102 | 103 | on_cycle_complete_supported = True 104 | 105 | def _set_color(self, color): 106 | if self._off_pixels: 107 | self._comet_colors = [BLACK] 108 | else: 109 | self._comet_colors = [] 110 | 111 | for n in range(self._tail_length): 112 | _float_index = ((len(self._colors)) / self._tail_length) * n 113 | _color_index = int(_float_index) 114 | self._comet_colors.append(self._colors[_color_index]) 115 | -------------------------------------------------------------------------------- /adafruit_led_animation/animation/pacman.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2025 Bob Loeffler 2 | # SPDX-FileCopyrightText: 2025 Jose D. Montoya 3 | # 4 | # SPDX-License-Identifier: MIT 5 | 6 | """ 7 | `adafruit_led_animation.animation.pacman` 8 | ================================================================================ 9 | 10 | PacMan Animation for CircuitPython helper library for LED animations. 11 | PACMAN ANIMATION Adapted from https://github.com/wled-dev/WLED/pull/4536 # by BobLoeffler68 12 | 13 | * Author(s): Bob Loeffler, Jose D. Montoya 14 | 15 | Implementation Notes 16 | -------------------- 17 | 18 | **Software and Dependencies:** 19 | 20 | * Adafruit CircuitPython firmware for the supported boards: 21 | https://circuitpython.org/downloads 22 | 23 | 24 | """ 25 | 26 | import time 27 | 28 | from adafruit_led_animation.animation import Animation 29 | from adafruit_led_animation.color import ( 30 | BLACK, 31 | BLUE, 32 | CYAN, 33 | ORANGE, 34 | PURPLE, 35 | RED, 36 | WHITE, 37 | YELLOW, 38 | ) 39 | 40 | ORANGEYELLOW = (255, 136, 0) 41 | 42 | 43 | class Pacman(Animation): 44 | """ 45 | Simulate the Pacman game in a single led strip. 46 | 47 | :param pixel_object: The initialised LED object. 48 | :param float speed: Animation speed rate in seconds, e.g. ``0.1``. 49 | """ 50 | 51 | def __init__( 52 | self, 53 | pixel_object, 54 | speed, 55 | color=WHITE, 56 | name=None, 57 | ): 58 | self.num_leds = len(pixel_object) 59 | self.pacman = [YELLOW, 10] 60 | self.ghosts_original = [[RED, 6], [PURPLE, 4], [CYAN, 2], [ORANGE, 0]] 61 | self.ghosts = [[RED, 6], [PURPLE, 4], [CYAN, 2], [ORANGE, 0]] 62 | self.direction = 1 63 | self.black_dir = -1 64 | self.flag = "beep" 65 | self.power_pellet = [ORANGEYELLOW, self.num_leds] 66 | self.ghost_timer = time.monotonic() 67 | if self.num_leds > 150: 68 | self.start_blinking_ghosts = self.num_leds // 4 69 | else: 70 | self.start_blinking_ghosts = self.num_leds // 3 71 | 72 | super().__init__(pixel_object, speed, color, name=name) 73 | 74 | on_cycle_complete_supported = True 75 | 76 | def draw(self): 77 | """ 78 | Draw the Pacman animation. 79 | :param led_object: led object 80 | :param neopixel_list: list of neopixel colors 81 | :param int num_leds: number of leds. 82 | :param int duration: duration in seconds. Default is 15 seconds 83 | """ 84 | pixel_list = self.pixel_object 85 | pixel_list[-1] = self.power_pellet[0] 86 | 87 | delta = time.monotonic() - self.ghost_timer 88 | if delta > 1: 89 | if self.power_pellet[0] == ORANGEYELLOW: 90 | self.power_pellet[0] = BLACK 91 | else: 92 | self.power_pellet[0] = ORANGEYELLOW 93 | pixel_list[self.power_pellet[1] - 1] = self.power_pellet[0] 94 | 95 | self.ghost_timer = time.monotonic() 96 | 97 | if self.pacman[1] >= self.num_leds - 2: 98 | self.direction = self.direction * -1 99 | self.black_dir = self.black_dir * -1 100 | for ghost in self.ghosts: 101 | ghost[0] = BLUE 102 | 103 | pixel_list[self.pacman[1]] = self.pacman[0] 104 | pixel_list[self.pacman[1] + self.black_dir] = BLACK 105 | self.pacman[1] += self.direction 106 | 107 | if self.ghosts[3][1] <= self.start_blinking_ghosts and self.direction == -1: 108 | if self.flag == "beep": 109 | for i, ghost in enumerate(self.ghosts): 110 | ghost[0] = BLACK 111 | self.flag = "bop" 112 | else: 113 | for i, ghost in enumerate(self.ghosts): 114 | ghost[0] = self.ghosts_original[i][0] 115 | self.flag = "beep" 116 | 117 | for i, ghost in enumerate(self.ghosts): 118 | pixel_list[ghost[1]] = ghost[0] 119 | pixel_list[ghost[1] + self.black_dir] = BLACK 120 | ghost[1] += self.direction 121 | 122 | if self.ghosts[3][1] <= 0: 123 | self.direction = self.direction * -1 124 | self.black_dir = self.black_dir * -1 125 | for i, ghost in enumerate(self.ghosts): 126 | ghost[0] = self.ghosts_original[i][0] 127 | -------------------------------------------------------------------------------- /adafruit_led_animation/animation/pulse.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 Kattni Rembor for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | `adafruit_led_animation.animation.pulse` 7 | ================================================================================ 8 | 9 | Pulse animation for CircuitPython helper library for LED animations. 10 | 11 | * Author(s): Kattni Rembor 12 | 13 | Implementation Notes 14 | -------------------- 15 | 16 | **Hardware:** 17 | 18 | * `Adafruit NeoPixels `_ 19 | * `Adafruit DotStars `_ 20 | 21 | **Software and Dependencies:** 22 | 23 | * Adafruit CircuitPython firmware for the supported boards: 24 | https://circuitpython.org/downloads 25 | 26 | 27 | """ 28 | 29 | from adafruit_led_animation.animation import Animation 30 | 31 | 32 | class Pulse(Animation): 33 | """ 34 | Pulse all pixels a single color. 35 | 36 | :param pixel_object: The initialised LED object. 37 | :param float speed: Animation refresh rate in seconds, e.g. ``0.1``. 38 | :param color: Animation color in ``(r, g, b)`` tuple, or ``0x000000`` hex format. 39 | :param period: Period to pulse the LEDs over. Default 5. 40 | :param breath: Duration to hold minimum and maximum intensity levels. Default 0. 41 | :param min_intensity: Lowest brightness level of the pulse. Default 0. 42 | :param max_intensity: Highest brightness level of the pulse. Default 1. 43 | """ 44 | 45 | def __init__( 46 | self, 47 | pixel_object, 48 | speed, 49 | color, 50 | period=5, 51 | breath=0, 52 | min_intensity=0, 53 | max_intensity=1, 54 | name=None, 55 | ): 56 | super().__init__(pixel_object, speed, color, name=name) 57 | self._period = period 58 | self.breath = breath 59 | self.min_intensity = min_intensity 60 | self.max_intensity = max_intensity 61 | self._generator = None 62 | self.reset() 63 | 64 | on_cycle_complete_supported = True 65 | 66 | def draw(self): 67 | color = next(self._generator) 68 | self.pixel_object.fill(color) 69 | 70 | def reset(self): 71 | """ 72 | Resets the animation. 73 | """ 74 | dotstar = len(self.pixel_object[0]) == 4 and isinstance(self.pixel_object[0][-1], float) 75 | from adafruit_led_animation.pulse_generator import ( 76 | pulse_generator, 77 | ) 78 | 79 | self._generator = pulse_generator(self._period, self, dotstar_pwm=dotstar) 80 | 81 | @property 82 | def period(self): 83 | """ 84 | Period to pulse the LEDs over in seconds 85 | """ 86 | return self._period 87 | 88 | @period.setter 89 | def period(self, new_value): 90 | self._period = new_value 91 | self.reset() 92 | -------------------------------------------------------------------------------- /adafruit_led_animation/animation/rainbow.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 Kattni Rembor for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | `adafruit_led_animation.animation.rainbow` 7 | ================================================================================ 8 | 9 | Rainbow animation for CircuitPython helper library for LED animations. 10 | 11 | * Author(s): Kattni Rembor 12 | 13 | Implementation Notes 14 | -------------------- 15 | 16 | **Hardware:** 17 | 18 | * `Adafruit NeoPixels `_ 19 | * `Adafruit DotStars `_ 20 | 21 | **Software and Dependencies:** 22 | 23 | * Adafruit CircuitPython firmware for the supported boards: 24 | https://circuitpython.org/downloads 25 | 26 | """ 27 | 28 | from adafruit_led_animation import MS_PER_SECOND, monotonic_ms 29 | from adafruit_led_animation.animation import Animation 30 | from adafruit_led_animation.color import BLACK, colorwheel 31 | 32 | __version__ = "0.0.0+auto.0" 33 | __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_LED_Animation.git" 34 | 35 | 36 | class Rainbow(Animation): 37 | """ 38 | The classic rainbow color wheel. 39 | 40 | :param pixel_object: The initialised LED object. 41 | :param float speed: Animation refresh rate in seconds, e.g. ``0.1``. 42 | :param float period: Period to cycle the rainbow over in seconds. Default 5. 43 | :param float step: Color wheel step. Default 1. 44 | :param str name: Name of animation (optional, useful for sequences and debugging). 45 | :param bool precompute_rainbow: Whether to precompute the rainbow. Uses more memory. 46 | (default True). 47 | """ 48 | 49 | def __init__(self, pixel_object, speed, period=5, step=1, name=None, precompute_rainbow=True): 50 | super().__init__(pixel_object, speed, BLACK, name=name) 51 | self._period = period 52 | self._step = step 53 | self._wheel_index = 0 54 | self.colors = None 55 | self._generator = self._color_wheel_generator() 56 | if precompute_rainbow: 57 | self.generate_rainbow() 58 | 59 | def generate_rainbow(self): 60 | """Generates the rainbow.""" 61 | self.colors = [] 62 | i = 0 63 | while i < 256: 64 | self.colors.append(colorwheel(int(i))) 65 | i += self._step 66 | 67 | on_cycle_complete_supported = True 68 | 69 | def _color_wheel_generator(self): 70 | period = int(self._period * MS_PER_SECOND) 71 | 72 | num_pixels = len(self.pixel_object) 73 | last_update = monotonic_ms() 74 | cycle_position = 0 75 | last_pos = 0 76 | while True: 77 | cycle_completed = False 78 | now = monotonic_ms() 79 | time_since_last_draw = now - last_update 80 | last_update = now 81 | pos = cycle_position = (cycle_position + time_since_last_draw) % period 82 | if pos < last_pos: 83 | cycle_completed = True 84 | last_pos = pos 85 | 86 | if self.colors: 87 | wheel_index = int((pos / period) * len(self.colors)) 88 | self._draw_precomputed(num_pixels, wheel_index) 89 | else: 90 | wheel_index = int((pos / period) * 256) 91 | self.pixel_object[:] = [ 92 | colorwheel((i + wheel_index) % 255) for i in range(num_pixels) 93 | ] 94 | self._wheel_index = wheel_index 95 | if cycle_completed: 96 | self.cycle_complete = True 97 | yield 98 | 99 | def _draw_precomputed(self, num_pixels, wheel_index): 100 | for i in range(0, num_pixels, len(self.colors)): 101 | num = len(self.colors) 102 | if i + len(self.colors) > num_pixels: 103 | num = num_pixels - i 104 | if wheel_index + num > len(self.colors): 105 | colors_left = len(self.colors) - wheel_index 106 | self.pixel_object[i : i + colors_left] = self.colors[wheel_index:] 107 | self.pixel_object[i + colors_left : i + num] = self.colors[: num - colors_left] 108 | else: 109 | self.pixel_object[i : i + num] = self.colors[wheel_index : wheel_index + num] 110 | 111 | def draw(self): 112 | next(self._generator) 113 | 114 | def reset(self): 115 | """ 116 | Resets the animation. 117 | """ 118 | self._generator = self._color_wheel_generator() 119 | 120 | @property 121 | def period(self) -> float: 122 | """ 123 | Period to cycle the rainbow over in seconds. 124 | """ 125 | return self._period 126 | 127 | @period.setter 128 | def period(self, new_value: float) -> None: 129 | self._period = new_value 130 | self.reset() 131 | -------------------------------------------------------------------------------- /adafruit_led_animation/animation/rainbowchase.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 Kattni Rembor for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | `adafruit_led_animation.animation.rainbowchase` 7 | ================================================================================ 8 | 9 | Rainbow chase animation for CircuitPython helper library for LED animations. 10 | 11 | * Author(s): Kattni Rembor 12 | 13 | Implementation Notes 14 | -------------------- 15 | 16 | **Hardware:** 17 | 18 | * `Adafruit NeoPixels `_ 19 | * `Adafruit DotStars `_ 20 | 21 | **Software and Dependencies:** 22 | 23 | * Adafruit CircuitPython firmware for the supported boards: 24 | https://circuitpython.org/downloads 25 | 26 | 27 | """ 28 | 29 | from adafruit_led_animation.animation.chase import Chase 30 | from adafruit_led_animation.color import colorwheel 31 | 32 | 33 | class RainbowChase(Chase): 34 | """ 35 | Chase pixels in one direction, like a theater marquee but with rainbows! 36 | 37 | :param pixel_object: The initialised LED object. 38 | :param float speed: Animation speed rate in seconds, e.g. ``0.1``. 39 | :param color: Animation color in ``(r, g, b)`` tuple, or ``0x000000`` hex format. 40 | :param size: Number of pixels to turn on in a row. 41 | :param spacing: Number of pixels to turn off in a row. 42 | :param reverse: Reverse direction of movement. 43 | :param step: How many colors to skip in ``colorwheel`` per bar (default 8) 44 | """ 45 | 46 | def __init__( 47 | self, 48 | pixel_object, 49 | speed, 50 | size=2, 51 | spacing=3, 52 | reverse=False, 53 | name=None, 54 | step=8, 55 | ): 56 | self._num_colors = 256 // step 57 | self._colors = [colorwheel(n % 256) for n in range(0, 512, step)] 58 | self._color_idx = 0 59 | super().__init__(pixel_object, speed, 0, size, spacing, reverse, name) 60 | 61 | def bar_color(self, n, pixel_no=0): 62 | return self._colors[self._color_idx - (n % len(self._colors))] 63 | 64 | def on_cycle_complete(self): 65 | self._color_idx = (self._color_idx + self._direction) % len(self._colors) 66 | super().on_cycle_complete() 67 | -------------------------------------------------------------------------------- /adafruit_led_animation/animation/rainbowcomet.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 Kattni Rembor for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | `adafruit_led_animation.animation.rainbowcomet` 7 | ================================================================================ 8 | 9 | Rainbow comet for CircuitPython helper library for LED animations. 10 | 11 | * Author(s): Kattni Rembor 12 | 13 | Implementation Notes 14 | -------------------- 15 | 16 | **Hardware:** 17 | 18 | * `Adafruit NeoPixels `_ 19 | * `Adafruit DotStars `_ 20 | 21 | **Software and Dependencies:** 22 | 23 | * Adafruit CircuitPython firmware for the supported boards: 24 | https://circuitpython.org/downloads 25 | 26 | 27 | """ 28 | 29 | from adafruit_led_animation.animation.comet import Comet 30 | from adafruit_led_animation.color import BLACK, calculate_intensity, colorwheel 31 | 32 | 33 | class RainbowComet(Comet): 34 | """ 35 | A rainbow comet animation. 36 | 37 | :param pixel_object: The initialised LED object. 38 | :param float speed: Animation speed in seconds, e.g. ``0.1``. 39 | :param int tail_length: The length of the comet. Defaults to 10. Cannot exceed the number of 40 | pixels present in the pixel object, e.g. if the strip is 30 pixels 41 | long, the ``tail_length`` cannot exceed 30 pixels. 42 | :param bool reverse: Animates the comet in the reverse order. Defaults to ``False``. 43 | :param bool bounce: Comet will bounce back and forth. Defaults to ``False``. 44 | :param int colorwheel_offset: Offset from start of colorwheel (0-255). 45 | :param int step: Colorwheel step (defaults to automatic). 46 | :param bool ring: Ring mode. Defaults to ``False``. 47 | """ 48 | 49 | def __init__( 50 | self, 51 | pixel_object, 52 | speed, 53 | tail_length=10, 54 | reverse=False, 55 | bounce=False, 56 | colorwheel_offset=0, 57 | step=0, 58 | name=None, 59 | ring=False, 60 | ): 61 | if step == 0: 62 | self._colorwheel_step = max(256 // tail_length, 1) 63 | else: 64 | self._colorwheel_step = step 65 | self._colorwheel_offset = colorwheel_offset 66 | super().__init__(pixel_object, speed, 0, 0, tail_length, reverse, bounce, name, ring) 67 | 68 | def _set_color(self, color): 69 | self._comet_colors = [BLACK] 70 | for n in range(self._tail_length): 71 | invert = self._tail_length - n - 1 72 | self._comet_colors.append( 73 | calculate_intensity( 74 | colorwheel( 75 | int((invert * self._colorwheel_step) + self._colorwheel_offset) % 256 76 | ), 77 | n * self._color_step + 0.05, 78 | ) 79 | ) 80 | self._computed_color = color 81 | -------------------------------------------------------------------------------- /adafruit_led_animation/animation/rainbowsparkle.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 Kattni Rembor for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | `adafruit_led_animation.animation.rainbowsparkle` 7 | ================================================================================ 8 | 9 | Rainbow sparkle for CircuitPython helper library for LED animations. 10 | 11 | * Author(s): Kattni Rembor 12 | 13 | Implementation Notes 14 | -------------------- 15 | 16 | **Hardware:** 17 | 18 | * `Adafruit NeoPixels `_ 19 | * `Adafruit DotStars `_ 20 | 21 | **Software and Dependencies:** 22 | 23 | * Adafruit CircuitPython firmware for the supported boards: 24 | https://circuitpython.org/downloads 25 | 26 | """ 27 | 28 | import random 29 | 30 | from adafruit_led_animation.animation.rainbow import Rainbow 31 | 32 | 33 | class RainbowSparkle(Rainbow): 34 | """Rainbow sparkle animation. 35 | 36 | :param pixel_object: The initialised LED object. 37 | :param float speed: Animation refresh rate in seconds, e.g. ``0.1``. 38 | :param float period: Period to cycle the rainbow over in seconds. Default 5. 39 | :param int num_sparkles: The number of sparkles to display. Defaults to 1/20 of the pixel 40 | object length. 41 | :param float step: Color wheel step. Default 1. 42 | :param str name: Name of animation (optional, useful for sequences and debugging). 43 | :param float background_brightness: The brightness of the background rainbow. Defaults to 44 | ``0.2`` or 20 percent. 45 | :param bool precompute_rainbow: Whether to precompute the rainbow. Uses more memory. 46 | (default True). 47 | """ 48 | 49 | def __init__( 50 | self, 51 | pixel_object, 52 | speed, 53 | period=5, 54 | num_sparkles=None, 55 | step=1, 56 | name=None, 57 | background_brightness=0.2, 58 | precompute_rainbow=True, 59 | ): 60 | self._num_sparkles = num_sparkles 61 | if num_sparkles is None: 62 | self._num_sparkles = max(1, int(len(pixel_object) / 20)) 63 | self._sparkle_duration = 2 64 | self._background_brightness = background_brightness 65 | self._bright_colors = None 66 | super().__init__( 67 | pixel_object=pixel_object, 68 | speed=speed, 69 | period=period, 70 | step=step, 71 | name=name, 72 | precompute_rainbow=precompute_rainbow, 73 | ) 74 | 75 | def generate_rainbow(self): 76 | super().generate_rainbow() 77 | self._bright_colors = self.colors[:] 78 | for i, color in enumerate(self.colors): 79 | if isinstance(self.colors[i], int): 80 | self.colors[i] = ( 81 | int(self._background_brightness * ((color & 0xFF0000) >> 16)), 82 | int(self._background_brightness * ((color & 0xFF00) >> 8)), 83 | int(self._background_brightness * (color & 0xFF)), 84 | ) 85 | else: 86 | self.colors[i] = ( 87 | int(self._background_brightness * color[0]), 88 | int(self._background_brightness * color[1]), 89 | int(self._background_brightness * color[2]), 90 | ) 91 | 92 | def after_draw(self): 93 | self.show() 94 | pixels = [random.randint(0, len(self.pixel_object) - 1) for n in range(self._num_sparkles)] 95 | for pixel in pixels: 96 | self.pixel_object[pixel] = self._bright_colors[ 97 | (self._wheel_index + pixel) % len(self._bright_colors) 98 | ] 99 | -------------------------------------------------------------------------------- /adafruit_led_animation/animation/solid.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 Kattni Rembor for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | `adafruit_led_animation.animation.solid` 7 | ================================================================================ 8 | 9 | Solid animation for CircuitPython helper library for LED animations. 10 | 11 | * Author(s): Kattni Rembor 12 | 13 | Implementation Notes 14 | -------------------- 15 | 16 | **Hardware:** 17 | 18 | * `Adafruit NeoPixels `_ 19 | * `Adafruit DotStars `_ 20 | 21 | **Software and Dependencies:** 22 | 23 | * Adafruit CircuitPython firmware for the supported boards: 24 | https://circuitpython.org/downloads 25 | 26 | 27 | """ 28 | 29 | from adafruit_led_animation.animation.colorcycle import ColorCycle 30 | 31 | 32 | class Solid(ColorCycle): 33 | """ 34 | A solid color. 35 | 36 | :param pixel_object: The initialised LED object. 37 | :param color: Animation color in ``(r, g, b)`` tuple, or ``0x000000`` hex format. 38 | """ 39 | 40 | def __init__(self, pixel_object, color, name=None): 41 | super().__init__(pixel_object, speed=1, colors=[color], name=name) 42 | 43 | def _set_color(self, color): 44 | self.colors = [color] 45 | -------------------------------------------------------------------------------- /adafruit_led_animation/animation/sparkle.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 Kattni Rembor for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | `adafruit_led_animation.animation.sparkle` 7 | ================================================================================ 8 | 9 | Sparkle animation for CircuitPython helper library for LED animations. 10 | 11 | * Author(s): Kattni Rembor 12 | 13 | Implementation Notes 14 | -------------------- 15 | 16 | **Hardware:** 17 | 18 | * `Adafruit NeoPixels `_ 19 | * `Adafruit DotStars `_ 20 | 21 | **Software and Dependencies:** 22 | 23 | * Adafruit CircuitPython firmware for the supported boards: 24 | https://circuitpython.org/downloads 25 | 26 | """ 27 | 28 | import random 29 | 30 | from adafruit_led_animation.animation import Animation 31 | 32 | __version__ = "0.0.0+auto.0" 33 | __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_LED_Animation.git" 34 | 35 | 36 | class Sparkle(Animation): 37 | """ 38 | Sparkle animation of a single color. 39 | 40 | :param pixel_object: The initialised LED object. 41 | :param float speed: Animation speed in seconds, e.g. ``0.1``. 42 | :param color: Animation color in ``(r, g, b)`` tuple, or ``0x000000`` hex format. 43 | :param num_sparkles: Number of sparkles to generate per animation cycle. 44 | :param mask: array to limit sparkles within range of the mask 45 | """ 46 | 47 | def __init__(self, pixel_object, speed, color, num_sparkles=1, name=None, mask=None): 48 | if len(pixel_object) < 2: 49 | raise ValueError("Sparkle needs at least 2 pixels") 50 | if mask: 51 | self._mask = mask 52 | else: 53 | self._mask = [] 54 | if len(self._mask) >= len(pixel_object): 55 | raise ValueError("Sparkle mask should be smaller than number pixel array") 56 | self._half_color = color 57 | self._dim_color = color 58 | self._sparkle_color = color 59 | self._num_sparkles = num_sparkles 60 | self._num_pixels = len(pixel_object) 61 | self._pixels = [] 62 | super().__init__(pixel_object, speed, color, name=name) 63 | 64 | def _set_color(self, color): 65 | half_color = tuple(color[rgb] // 4 for rgb in range(len(color))) 66 | dim_color = tuple(color[rgb] // 10 for rgb in range(len(color))) 67 | for pixel in range(len(self.pixel_object)): 68 | if self.pixel_object[pixel] == self._half_color: 69 | self.pixel_object[pixel] = half_color 70 | elif self.pixel_object[pixel] == self._dim_color: 71 | self.pixel_object[pixel] = dim_color 72 | self._half_color = half_color 73 | self._dim_color = dim_color 74 | self._sparkle_color = color 75 | 76 | def _random_in_mask(self): 77 | if len(self._mask) == 0: 78 | return random.randint(0, (len(self.pixel_object) - 1)) 79 | return self._mask[random.randint(0, (len(self._mask) - 1))] 80 | 81 | def draw(self): 82 | self._pixels = [self._random_in_mask() for _ in range(self._num_sparkles)] 83 | for pixel in self._pixels: 84 | self.pixel_object[pixel] = self._sparkle_color 85 | 86 | def after_draw(self): 87 | self.show() 88 | for pixel in self._pixels: 89 | self.pixel_object[pixel % self._num_pixels] = self._half_color 90 | if (pixel + 1) % self._num_pixels in self._mask: 91 | self.pixel_object[(pixel + 1) % self._num_pixels] = self._dim_color 92 | -------------------------------------------------------------------------------- /adafruit_led_animation/animation/sparklepulse.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 Kattni Rembor for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | `adafruit_led_animation.animation.sparklepulse` 7 | ================================================================================ 8 | 9 | Sparkle-pulse animation for CircuitPython helper library for LED animations. 10 | 11 | * Author(s): dmolavi 12 | 13 | Implementation Notes 14 | -------------------- 15 | 16 | **Hardware:** 17 | 18 | * `Adafruit NeoPixels `_ 19 | * `Adafruit DotStars `_ 20 | 21 | **Software and Dependencies:** 22 | 23 | * Adafruit CircuitPython firmware for the supported boards: 24 | https://circuitpython.org/downloads 25 | 26 | 27 | """ 28 | 29 | from adafruit_led_animation.animation.sparkle import Sparkle 30 | from adafruit_led_animation.pulse_generator import pulse_generator 31 | 32 | 33 | class SparklePulse(Sparkle): 34 | """ 35 | Combination of the Sparkle and Pulse animations. 36 | 37 | :param pixel_object: The initialised LED object. 38 | :param int speed: Animation refresh rate in seconds, e.g. ``0.1``. 39 | :param color: Animation color in ``(r, g, b)`` tuple, or ``0x000000`` hex format. 40 | :param period: Period to pulse the LEDs over. Default 5. 41 | :param breath: Duration to hold minimum and maximum intensity. Default 0. 42 | :param max_intensity: The maximum intensity to pulse, between 0 and 1.0. Default 1. 43 | :param min_intensity: The minimum intensity to pulse, between 0 and 1.0. Default 0. 44 | """ 45 | 46 | def __init__( 47 | self, 48 | pixel_object, 49 | speed, 50 | color, 51 | period=5, 52 | breath=0, 53 | max_intensity=1, 54 | min_intensity=0, 55 | name=None, 56 | ): 57 | self._period = period 58 | self.breath = breath 59 | self.min_intensity = min_intensity 60 | self.max_intensity = max_intensity 61 | dotstar = len(pixel_object) == 4 and isinstance(pixel_object[0][-1], float) 62 | super().__init__(pixel_object, speed=speed, color=color, num_sparkles=1, name=name) 63 | self._generator = pulse_generator(self._period, self, dotstar_pwm=dotstar) 64 | 65 | def _set_color(self, color): 66 | self._color = color 67 | 68 | def draw(self): 69 | self._sparkle_color = next(self._generator) 70 | super().draw() 71 | 72 | def after_draw(self): 73 | self.show() 74 | 75 | @property 76 | def period(self): 77 | """ 78 | Period to pulse the LEDs over in seconds 79 | """ 80 | return self._period 81 | 82 | @period.setter 83 | def period(self, new_value): 84 | self._period = new_value 85 | self.reset() 86 | 87 | def reset(self): 88 | dotstar = len(self.pixel_object) == 4 and isinstance(self.pixel_object[0][-1], float) 89 | self._generator = pulse_generator(self._period, self, dotstar_pwm=dotstar) 90 | -------------------------------------------------------------------------------- /adafruit_led_animation/animation/volume.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 Gamblor21 2 | # 3 | # SPDX-License-Identifier: MIT 4 | """ 5 | `adafruit_led_animation.animation.volume` 6 | ================================================================================ 7 | Volume animation for CircuitPython helper library for LED animations. 8 | * Author(s): Mark Komus 9 | Implementation Notes 10 | -------------------- 11 | **Hardware:** 12 | * `Adafruit NeoPixels `_ 13 | * `Adafruit DotStars `_ 14 | **Software and Dependencies:** 15 | * Adafruit CircuitPython firmware for the supported boards: 16 | https://circuitpython.org/downloads 17 | """ 18 | 19 | from adafruit_led_animation.animation import Animation 20 | 21 | 22 | def map_range(x, in_min, in_max, out_min, out_max): 23 | """ 24 | Maps a number from one range to another. 25 | :return: Returns value mapped to new range 26 | :rtype: float 27 | """ 28 | mapped = (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min 29 | if out_min <= out_max: 30 | return max(min(mapped, out_max), out_min) 31 | 32 | return min(max(mapped, out_max), out_min) 33 | 34 | 35 | class Volume(Animation): 36 | """ 37 | Animate the brightness and number of pixels based on volume. 38 | :param pixel_object: The initialised LED object. 39 | :param float speed: Animation update speed in seconds, e.g. ``0.1``. 40 | :param brightest_color: Color at max volume ``(r, g, b)`` tuple, or ``0x000000`` hex format 41 | :param decoder: a MP3Decoder object that the volume will be taken from 42 | :param float max_volume: what volume is considered maximum where everything is lit up 43 | """ 44 | 45 | def __init__( 46 | self, 47 | pixel_object, 48 | speed, 49 | brightest_color, 50 | decoder, 51 | max_volume=500, 52 | name=None, 53 | ): 54 | self._decoder = decoder 55 | self._num_pixels = len(pixel_object) 56 | self._max_volume = max_volume 57 | self._brightest_color = brightest_color 58 | super().__init__(pixel_object, speed, brightest_color, name=name) 59 | 60 | def set_brightest_color(self, brightest_color): 61 | """ 62 | Animate the brightness and number of pixels based on volume. 63 | :param brightest_color: Color at max volume ``(r, g, b)`` tuple, or ``0x000000`` hex format 64 | """ 65 | self._brightest_color = brightest_color 66 | 67 | def draw(self): 68 | red = int( 69 | map_range( 70 | self._decoder.rms_level, 71 | 0, 72 | self._max_volume, 73 | 0, 74 | self._brightest_color[0], 75 | ) 76 | ) 77 | green = int( 78 | map_range( 79 | self._decoder.rms_level, 80 | 0, 81 | self._max_volume, 82 | 0, 83 | self._brightest_color[1], 84 | ) 85 | ) 86 | blue = int( 87 | map_range( 88 | self._decoder.rms_level, 89 | 0, 90 | self._max_volume, 91 | 0, 92 | self._brightest_color[2], 93 | ) 94 | ) 95 | 96 | lit_pixels = int( 97 | map_range( 98 | self._decoder.rms_level, 99 | 0, 100 | self._max_volume, 101 | 0, 102 | self._num_pixels, 103 | ) 104 | ) 105 | if lit_pixels > self._num_pixels: 106 | lit_pixels = self._num_pixels 107 | 108 | self.pixel_object[0:lit_pixels] = [(red, green, blue)] * lit_pixels 109 | self.pixel_object[lit_pixels : self._num_pixels] = [(0, 0, 0)] * ( 110 | self._num_pixels - lit_pixels 111 | ) 112 | -------------------------------------------------------------------------------- /adafruit_led_animation/color.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2019 Kattni Rembor for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | `adafruit_led_animation.color` 7 | ================================================================================ 8 | 9 | Color variables assigned to RGB values made available for import. 10 | 11 | * Author(s): Kattni Rembor 12 | 13 | Implementation Notes 14 | -------------------- 15 | 16 | **Hardware:** 17 | 18 | * `Adafruit NeoPixels `_ 19 | * `Adafruit DotStars `_ 20 | 21 | **Software and Dependencies:** 22 | 23 | * Adafruit CircuitPython firmware for the supported boards: 24 | https://circuitpython.org/downloads 25 | """ 26 | 27 | # Makes colorwheel() available. 28 | from rainbowio import colorwheel 29 | 30 | RED = (255, 0, 0) 31 | """Red.""" 32 | YELLOW = (255, 150, 0) 33 | """Yellow.""" 34 | ORANGE = (255, 40, 0) 35 | """Orange.""" 36 | GREEN = (0, 255, 0) 37 | """Green.""" 38 | TEAL = (0, 255, 120) 39 | """Teal.""" 40 | CYAN = (0, 255, 255) 41 | """Cyan.""" 42 | BLUE = (0, 0, 255) 43 | """Blue.""" 44 | PURPLE = (180, 0, 255) 45 | """Purple.""" 46 | MAGENTA = (255, 0, 20) 47 | """Magenta.""" 48 | WHITE = (255, 255, 255) 49 | """White.""" 50 | BLACK = (0, 0, 0) 51 | """Black or off.""" 52 | 53 | GOLD = (255, 222, 30) 54 | """Gold.""" 55 | PINK = (242, 90, 255) 56 | """Pink.""" 57 | AQUA = (50, 255, 255) 58 | """Aqua.""" 59 | JADE = (0, 255, 40) 60 | """Jade.""" 61 | AMBER = (255, 100, 0) 62 | """Amber.""" 63 | OLD_LACE = (253, 245, 230) # Warm white. 64 | """Old lace or warm white.""" 65 | 66 | RGBW_WHITE_RGB = (255, 255, 255, 0) 67 | """RGBW_WHITE_RGB is for RGBW strips to illuminate only the RGB diodes.""" 68 | RGBW_WHITE_W = (0, 0, 0, 255) 69 | """RGBW_WHITE_W is for RGBW strips to illuminate only White diode.""" 70 | RGBW_WHITE_RGBW = (255, 255, 255, 255) 71 | """RGBW_WHITE_RGBW is for RGBW strips to illuminate the RGB and White diodes.""" 72 | 73 | RAINBOW = (RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE) 74 | """RAINBOW is a list of colors to use for cycling through. 75 | Includes, in order: red, orange, yellow, green, blue, and purple.""" 76 | 77 | 78 | def calculate_intensity(color, intensity=1.0): 79 | """ 80 | Takes a RGB[W] color tuple and adjusts the intensity. 81 | :param float intensity: 82 | :param color: color value (tuple, list or int) 83 | :return: color 84 | """ 85 | # Note: This code intentionally avoids list comprehensions and intermediate variables 86 | # for an approximately 2x performance gain. 87 | if isinstance(color, int): 88 | return ( 89 | (int((color & 0xFF0000) * intensity) & 0xFF0000) 90 | | (int((color & 0xFF00) * intensity) & 0xFF00) 91 | | (int((color & 0xFF) * intensity) & 0xFF) 92 | ) 93 | 94 | if len(color) == 3: 95 | return ( 96 | int(color[0] * intensity), 97 | int(color[1] * intensity), 98 | int(color[2] * intensity), 99 | ) 100 | if len(color) == 4 and isinstance(color[3], float): 101 | return ( 102 | int(color[0] * intensity), 103 | int(color[1] * intensity), 104 | int(color[2] * intensity), 105 | color[3], 106 | ) 107 | return ( 108 | int(color[0] * intensity), 109 | int(color[1] * intensity), 110 | int(color[2] * intensity), 111 | int(color[3] * intensity), 112 | ) 113 | -------------------------------------------------------------------------------- /adafruit_led_animation/grid.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 Kattni Rembor for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | `adafruit_led_animation.grid` 7 | ================================================================================ 8 | 9 | PixelGrid helper for 2D animations. 10 | 11 | * Author(s): Kattni Rembor 12 | 13 | Implementation Notes 14 | -------------------- 15 | 16 | **Hardware:** 17 | 18 | * `Adafruit NeoPixels `_ 19 | * `Adafruit DotStars `_ 20 | 21 | **Software and Dependencies:** 22 | 23 | * Adafruit CircuitPython firmware for the supported boards: 24 | https://circuitpython.org/downloads 25 | 26 | """ 27 | 28 | from micropython import const 29 | 30 | from .helper import PixelMap, horizontal_strip_gridmap, vertical_strip_gridmap 31 | 32 | HORIZONTAL = const(1) 33 | VERTICAL = const(2) 34 | 35 | 36 | class PixelGrid: 37 | """ 38 | PixelGrid lets you address a pixel strip with x and y coordinates. 39 | 40 | :param strip: An object that implements the Neopixel or Dotstar protocol. 41 | :param width: Grid width. 42 | :param height: Grid height. 43 | :param orientation: Orientation of the strip pixels - HORIZONTAL (default) or VERTICAL. 44 | :param alternating: Whether the strip alternates direction from row to row (default True). 45 | :param reverse_x: Whether the strip X origin is on the right side (default False). 46 | :param reverse_y: Whether the strip Y origin is on the bottom (default False). 47 | :param tuple top: (x, y) coordinates of grid top left corner (Optional) 48 | :param tuple bottom: (x, y) coordinates of grid bottom right corner (Optional) 49 | 50 | To use with individual pixels: 51 | 52 | .. code-block:: python 53 | 54 | import board 55 | import neopixel 56 | import time 57 | from adafruit_led_animation.grid import PixelGrid, VERTICAL 58 | 59 | pixels = neopixel.NeoPixel(board.D11, 256, auto_write=False) 60 | 61 | grid = PixelGrid(pixels, 32, 8, orientation=VERTICAL, alternating=True) 62 | 63 | for x in range(32): 64 | for y in range(8): 65 | # pg[x, y] = (y*32) + x 66 | pg[x][y] = ((y*32) + x) << 8 67 | pg.show() 68 | 69 | """ 70 | 71 | def __init__( 72 | self, 73 | strip, 74 | width, 75 | height, 76 | orientation=HORIZONTAL, 77 | alternating=True, 78 | reverse_x=False, 79 | reverse_y=False, 80 | top=0, 81 | bottom=0, 82 | ): 83 | self._pixels = strip 84 | self._x = [] 85 | self.height = height 86 | self.width = width 87 | 88 | if orientation == HORIZONTAL: 89 | mapper = horizontal_strip_gridmap(width, alternating) 90 | else: 91 | mapper = vertical_strip_gridmap(height, alternating) 92 | 93 | if reverse_x: 94 | mapper = reverse_x_mapper(width, mapper) 95 | 96 | if reverse_y: 97 | mapper = reverse_y_mapper(height, mapper) 98 | 99 | x_start = 0 100 | x_end = width 101 | y_start = 0 102 | y_end = height 103 | if top: 104 | x_start, y_start = top 105 | if bottom: 106 | x_end, y_end = bottom 107 | 108 | self.height = y_end - y_start 109 | self.width = x_end - x_start 110 | 111 | for x in range(x_start, x_end): 112 | self._x.append( 113 | PixelMap( 114 | strip, 115 | [mapper(x, y) for y in range(y_start, y_end)], 116 | individual_pixels=True, 117 | ) 118 | ) 119 | self.n = len(self._x) 120 | 121 | def __repr__(self): 122 | return "[" + ", ".join([str(self[x]) for x in range(self.n)]) + "]" 123 | 124 | def __setitem__(self, index, val): 125 | if isinstance(index, slice): 126 | raise NotImplementedError("PixelGrid does not support slices") 127 | 128 | if isinstance(index, tuple): 129 | self._x[index[0]][index[1]] = val 130 | else: 131 | raise ValueError("PixelGrid assignment needs a sub-index or x,y coordinate") 132 | 133 | if self._pixels.auto_write: 134 | self.show() 135 | 136 | def __getitem__(self, index): 137 | if isinstance(index, slice): 138 | raise NotImplementedError("PixelGrid does not support slices") 139 | if index < 0: 140 | index += len(self) 141 | if index >= self.n or index < 0: 142 | raise IndexError("x is out of range") 143 | return self._x[index] 144 | 145 | def __len__(self): 146 | return self.n 147 | 148 | @property 149 | def brightness(self): 150 | """ 151 | brightness from the underlying strip. 152 | """ 153 | return self._pixels.brightness 154 | 155 | @brightness.setter 156 | def brightness(self, brightness): 157 | self._pixels.brightness = min(max(brightness, 0.0), 1.0) 158 | 159 | def fill(self, color): 160 | """ 161 | Fill the PixelGrid with the specified color. 162 | 163 | :param color: Color to use. 164 | """ 165 | for strip in self._x: 166 | strip.fill(color) 167 | 168 | def show(self): 169 | """ 170 | Shows the pixels on the underlying strip. 171 | """ 172 | self._pixels.show() 173 | 174 | @property 175 | def auto_write(self): 176 | """ 177 | auto_write from the underlying strip. 178 | """ 179 | return self._pixels.auto_write 180 | 181 | @auto_write.setter 182 | def auto_write(self, value): 183 | self._pixels.auto_write = value 184 | 185 | 186 | def reverse_x_mapper(width, mapper): 187 | """ 188 | Returns a coordinate mapper function for grids with reversed X coordinates. 189 | 190 | :param width: width of strip 191 | :param mapper: grid mapper to wrap 192 | :return: mapper(x, y) 193 | """ 194 | max_x = width - 1 195 | 196 | def x_mapper(x, y): 197 | return mapper(max_x - x, y) 198 | 199 | return x_mapper 200 | 201 | 202 | def reverse_y_mapper(height, mapper): 203 | """ 204 | Returns a coordinate mapper function for grids with reversed Y coordinates. 205 | 206 | :param height: width of strip 207 | :param mapper: grid mapper to wrap 208 | :return: mapper(x, y) 209 | """ 210 | max_y = height - 1 211 | 212 | def y_mapper(x, y): 213 | return mapper(x, max_y - y) 214 | 215 | return y_mapper 216 | -------------------------------------------------------------------------------- /adafruit_led_animation/group.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 Kattni Rembor for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | `adafruit_led_animation.group` 7 | ================================================================================ 8 | 9 | Animation group helper for CircuitPython helper library for LED animations.. 10 | 11 | 12 | * Author(s): Kattni Rembor 13 | 14 | Implementation Notes 15 | -------------------- 16 | 17 | **Hardware:** 18 | 19 | * `Adafruit NeoPixels `_ 20 | * `Adafruit DotStars `_ 21 | 22 | **Software and Dependencies:** 23 | 24 | * Adafruit CircuitPython firmware for the supported boards: 25 | https://circuitpython.org/downloads 26 | 27 | """ 28 | 29 | __version__ = "0.0.0+auto.0" 30 | __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_LED_Animation.git" 31 | 32 | from adafruit_led_animation.animation import Animation 33 | 34 | 35 | class AnimationGroup: 36 | """ 37 | AnimationGroup synchronizes multiple animations. Allows for multiple animations to be kept in 38 | sync, whether or not the same animation or pixel object is in use. 39 | 40 | :param members: The animation objects or groups. 41 | :param bool sync: Synchronises when draw is called for all members of the group to the settings 42 | of the first member of the group. Defaults to ``False``. 43 | 44 | 45 | Example: 46 | 47 | .. code-block:: python 48 | 49 | import board 50 | import neopixel 51 | from adafruit_circuitplayground import cp 52 | from adafruit_led_animation.animation.blink import Blink 53 | from adafruit_led_animation.animation.comet import Comet 54 | from adafruit_led_animation.animation.chase import Chase 55 | from adafruit_led_animation.group import AnimationGroup 56 | from adafruit_led_animation.sequence import AnimationSequence 57 | 58 | import adafruit_led_animation.color as color 59 | 60 | strip_pixels = neopixel.NeoPixel(board.A1, 30, brightness=0.5, auto_write=False) 61 | cp.pixels.brightness = 0.5 62 | 63 | animations = AnimationSequence( 64 | # Synchronized to 0.5 seconds. Ignores the second animation setting of 3 seconds. 65 | AnimationGroup( 66 | Blink(cp.pixels, 0.5, color.CYAN), 67 | Blink(strip_pixels, 3.0, color.AMBER), 68 | sync=True, 69 | ), 70 | # Different speeds 71 | AnimationGroup( 72 | Comet(cp.pixels, 0.1, color.MAGENTA, tail_length=5), 73 | Comet(strip_pixels, 0.01, color.MAGENTA, tail_length=15), 74 | ), 75 | # Sequential animations on the built-in NeoPixels then the NeoPixel strip 76 | Chase(cp.pixels, 0.05, size=2, spacing=3, color=color.PURPLE), 77 | Chase(strip_pixels, 0.05, size=2, spacing=3, color=color.PURPLE), 78 | advance_interval=3.0, 79 | auto_clear=True, 80 | auto_reset=True, 81 | ) 82 | 83 | while True: 84 | animations.animate() 85 | """ 86 | 87 | def __init__(self, *members, sync=False, name=None): 88 | if not members: 89 | raise ValueError("At least one member required in an AnimationGroup") 90 | self.draw_count = 0 91 | """Number of animation frames drawn.""" 92 | self.cycle_count = 0 93 | """Number of animation cycles completed.""" 94 | self.notify_cycles = 1 95 | """Number of cycles to trigger additional cycle_done notifications after""" 96 | self._members = list(members) 97 | self._sync = sync 98 | self._also_notify = [] 99 | self.cycle_count = 0 100 | self.name = name 101 | if sync: 102 | main = members[0] 103 | main.peers = members[1:] 104 | 105 | # Catch cycle_complete on the last animation. 106 | self._members[-1].add_cycle_complete_receiver(self._group_done) 107 | self.on_cycle_complete_supported = self._members[-1].on_cycle_complete_supported 108 | 109 | def __str__(self): 110 | return f"" 111 | 112 | def _group_done(self, animation): 113 | self.on_cycle_complete() 114 | 115 | def on_cycle_complete(self): 116 | """ 117 | Called by some animations when they complete an animation cycle. 118 | Animations that support cycle complete notifications will have X property set to False. 119 | Override as needed. 120 | """ 121 | self.cycle_count += 1 122 | if self.cycle_count % self.notify_cycles == 0: 123 | for callback in self._also_notify: 124 | callback(self) 125 | 126 | def add_cycle_complete_receiver(self, callback): 127 | """ 128 | Adds an additional callback when the cycle completes. 129 | 130 | :param callback: Additional callback to trigger when a cycle completes. The callback 131 | is passed the animation object instance. 132 | """ 133 | self._also_notify.append(callback) 134 | 135 | def animate(self, show=True): 136 | """ 137 | Call animate() from your code's main loop. It will draw all of the animations 138 | in the group. 139 | 140 | :return: True if any animation draw cycle was triggered, otherwise False. 141 | """ 142 | if self._sync: 143 | result = self._members[0].animate(show=False) 144 | if result and show: 145 | last_strip = None 146 | for member in self._members: 147 | if isinstance(member, Animation): 148 | if last_strip != member.pixel_object: 149 | member.pixel_object.show() 150 | last_strip = member.pixel_object 151 | else: 152 | member.show() 153 | return result 154 | 155 | ret = False 156 | for item in self._members: 157 | if item.animate(show): 158 | ret = True 159 | return ret 160 | 161 | @property 162 | def color(self): 163 | """ 164 | Use this property to change the color of all members of the animation group. 165 | """ 166 | return None 167 | 168 | @color.setter 169 | def color(self, color): 170 | for item in self._members: 171 | item.color = color 172 | 173 | def fill(self, color): 174 | """ 175 | Fills all pixel objects in the group with a color. 176 | """ 177 | for item in self._members: 178 | item.fill(color) 179 | 180 | def freeze(self): 181 | """ 182 | Freeze all animations in the group. 183 | """ 184 | for item in self._members: 185 | item.freeze() 186 | 187 | def resume(self): 188 | """ 189 | Resume all animations in the group. 190 | """ 191 | for item in self._members: 192 | item.resume() 193 | 194 | def reset(self): 195 | """ 196 | Resets the animations in the group. 197 | """ 198 | for item in self._members: 199 | item.reset() 200 | 201 | def show(self): 202 | """ 203 | Draws the current animation group members. 204 | """ 205 | for item in self._members: 206 | item.show() 207 | -------------------------------------------------------------------------------- /adafruit_led_animation/helper.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2019 Kattni Rembor for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | `adafruit_led_animation.helper` 7 | ================================================================================ 8 | 9 | Helper classes for making complex animations using CircuitPython LED animations library. 10 | 11 | * Author(s): Kattni Rembor 12 | 13 | Implementation Notes 14 | -------------------- 15 | 16 | **Hardware:** 17 | 18 | * `Adafruit NeoPixels `_ 19 | * `Adafruit DotStars `_ 20 | 21 | **Software and Dependencies:** 22 | 23 | * Adafruit CircuitPython firmware for the supported boards: 24 | https://circuitpython.org/downloads 25 | 26 | """ 27 | 28 | import math 29 | 30 | 31 | class PixelMap: 32 | """ 33 | PixelMap lets you treat ranges of pixels as single pixels for animation purposes. 34 | 35 | :param strip: An object that implements the Neopixel or Dotstar protocol. 36 | :param iterable pixel_ranges: Pixel ranges (or individual pixels). 37 | :param bool individual_pixels: Whether pixel_ranges are individual pixels. 38 | 39 | To use with ranges of pixels: 40 | 41 | .. code-block:: python 42 | 43 | import board 44 | import neopixel 45 | from adafruit_led_animation.helper import PixelMap 46 | pixels = neopixel.NeoPixel(board.D6, 32, auto_write=False) 47 | 48 | pixel_wing_horizontal = PixelMap(pixels, [(0, 8), (8, 16), (16, 24), (24, 32)]) 49 | 50 | pixel_wing_horizontal[0] = (255, 255, 0) 51 | pixel_wing_horizontal.show() 52 | 53 | To use with groups of individual pixels: 54 | 55 | .. code-block:: python 56 | 57 | import board 58 | import neopixel 59 | from adafruit_led_animation.helper import PixelMap 60 | pixels = neopixel.NeoPixel(board.D6, 32, auto_write=False) 61 | 62 | pixel_wing_vertical = PixelMap(pixels, [ 63 | (0, 8, 16, 24), 64 | (1, 9, 17, 25), 65 | (2, 10, 18, 26), 66 | (3, 11, 19, 27), 67 | (4, 12, 20, 28), 68 | (5, 13, 21, 29), 69 | (6, 14, 22, 30), 70 | (7, 15, 23, 31), 71 | ], individual_pixels=True) 72 | 73 | pixel_wing_vertical[0] = (255, 255, 0) 74 | pixel_wing_vertical.show() 75 | 76 | To use with individual pixels: 77 | 78 | .. code-block:: python 79 | 80 | import board 81 | import neopixel 82 | import time 83 | from adafruit_led_animation.helper import PixelMap 84 | 85 | pixels = neopixel.NeoPixel(board.D6, 8, auto_write=False) 86 | 87 | pixel_map = PixelMap(pixels, [ 88 | 0, 7, 1, 6, 2, 5, 3, 4 89 | ], individual_pixels=True) 90 | 91 | n = 0 92 | while True: 93 | pixel_map[n] = AMBER 94 | pixel_map.show() 95 | n = n + 1 96 | if n > 7: 97 | n = 0 98 | pixel_map.fill(0) 99 | time.sleep(0.25) 100 | 101 | 102 | """ 103 | 104 | def __init__(self, strip, pixel_ranges, individual_pixels=False): 105 | self._pixels = strip 106 | self._ranges = pixel_ranges 107 | 108 | self.n = len(self._ranges) 109 | if self.n == 0: 110 | raise ValueError("A PixelMap must have at least one pixel defined") 111 | self._individual_pixels = individual_pixels 112 | self._expand_ranges() 113 | 114 | def _expand_ranges(self): 115 | if not self._individual_pixels: 116 | self._ranges = [list(range(start, end)) for start, end in self._ranges] 117 | return 118 | if isinstance(self._ranges[0], int): 119 | self._ranges = [[n] for n in self._ranges] 120 | 121 | def __repr__(self): 122 | return "[" + ", ".join([str(self[x]) for x in range(self.n)]) + "]" 123 | 124 | def _set_pixels(self, index, val): 125 | for pixel in self._ranges[index]: 126 | self._pixels[pixel] = val 127 | 128 | def __setitem__(self, index, val): 129 | if isinstance(index, slice): 130 | start, stop, step = index.indices(len(self._ranges)) 131 | length = stop - start 132 | if step != 0: 133 | length = math.ceil(length / step) 134 | if len(val) != length: 135 | raise ValueError("Slice and input sequence size do not match.") 136 | for val_i, in_i in enumerate(range(start, stop, step)): 137 | self._set_pixels(in_i, val[val_i]) 138 | else: 139 | self._set_pixels(index, val) 140 | 141 | if self._pixels.auto_write: 142 | self.show() 143 | 144 | def __getitem__(self, index): 145 | if isinstance(index, slice): 146 | out = [] 147 | for in_i in range(*index.indices(len(self._ranges))): 148 | out.append(self._pixels[self._ranges[in_i][0]]) 149 | return out 150 | if index < 0: 151 | index += len(self) 152 | if index >= self.n or index < 0: 153 | raise IndexError 154 | return self._pixels[self._ranges[index][0]] 155 | 156 | def __len__(self): 157 | return len(self._ranges) 158 | 159 | @property 160 | def brightness(self): 161 | """ 162 | brightness from the underlying strip. 163 | """ 164 | return self._pixels.brightness 165 | 166 | @brightness.setter 167 | def brightness(self, brightness): 168 | self._pixels.brightness = min(max(brightness, 0.0), 1.0) 169 | 170 | def fill(self, color): 171 | """ 172 | Fill the used pixel ranges with color. 173 | 174 | :param color: Color to fill all pixels referenced by this PixelMap definition with. 175 | """ 176 | for pixels in self._ranges: 177 | for pixel in pixels: 178 | self._pixels[pixel] = color 179 | 180 | def show(self): 181 | """ 182 | Shows the pixels on the underlying strip. 183 | """ 184 | self._pixels.show() 185 | 186 | @property 187 | def auto_write(self): 188 | """ 189 | auto_write from the underlying strip. 190 | """ 191 | return self._pixels.auto_write 192 | 193 | @auto_write.setter 194 | def auto_write(self, value): 195 | self._pixels.auto_write = value 196 | 197 | @classmethod 198 | def vertical_lines(cls, pixel_object, width, height, gridmap): 199 | """ 200 | Generate a PixelMap of horizontal lines on a strip arranged in a grid. 201 | 202 | :param pixel_object: pixel object 203 | :param width: width of grid 204 | :param height: height of grid 205 | :param gridmap: a function to map x and y coordinates to the grid 206 | see vertical_strip_gridmap and horizontal_strip_gridmap 207 | :return: PixelMap 208 | 209 | Example: Vertical lines on a 32x8 grid with the pixel rows oriented vertically, 210 | alternating direction every row. 211 | 212 | .. code-block:: python 213 | 214 | PixelMap.vertical_lines(pixels, 32, 8, vertical_strip_gridmap(8)) 215 | 216 | """ 217 | if len(pixel_object) < width * height: 218 | raise ValueError("number of pixels is less than width x height") 219 | mapping = [] 220 | for x in range(width): 221 | mapping.append([gridmap(x, y) for y in range(height)]) 222 | return cls(pixel_object, mapping, individual_pixels=True) 223 | 224 | @classmethod 225 | def horizontal_lines(cls, pixel_object, width, height, gridmap): 226 | """ 227 | Generate a PixelMap of horizontal lines on a strip arranged in a grid. 228 | 229 | :param pixel_object: pixel object 230 | :param width: width of grid 231 | :param height: height of grid 232 | :param gridmap: a function to map x and y coordinates to the grid 233 | see vertical_strip_gridmap and horizontal_strip_gridmap 234 | :return: PixelMap 235 | 236 | Example: Horizontal lines on a 16x16 grid with the pixel rows oriented vertically, 237 | alternating direction every row. 238 | 239 | .. code-block:: python 240 | 241 | PixelMap.horizontal_lines(pixels, 16, 16, vertical_strip_gridmap(16)) 242 | """ 243 | if len(pixel_object) < width * height: 244 | raise ValueError("number of pixels is less than width x height") 245 | mapping = [] 246 | for y in range(height): 247 | mapping.append([gridmap(x, y) for x in range(width)]) 248 | return cls(pixel_object, mapping, individual_pixels=True) 249 | 250 | 251 | def vertical_strip_gridmap(height, alternating=True): 252 | """ 253 | Returns a function that determines the pixel number for a grid with strips arranged vertically. 254 | 255 | :param height: grid height in pixels 256 | :param alternating: Whether or not the lines in the grid run alternate directions in a zigzag 257 | :return: mapper(x, y) 258 | """ 259 | 260 | def mapper(x, y): 261 | if alternating and x % 2: 262 | return x * height + (height - 1 - y) 263 | return x * height + y 264 | 265 | return mapper 266 | 267 | 268 | def horizontal_strip_gridmap(width, alternating=True): 269 | """ 270 | Determines the pixel number for a grid with strips arranged horizontally. 271 | 272 | :param width: grid width in pixels 273 | :param alternating: Whether or not the lines in the grid run alternate directions in a zigzag 274 | :return: mapper(x, y) 275 | """ 276 | 277 | def mapper(x, y): 278 | if alternating and y % 2: 279 | return y * width + (width - 1 - x) 280 | return y * width + x 281 | 282 | return mapper 283 | 284 | 285 | class PixelSubset(PixelMap): 286 | """ 287 | PixelSubset lets you work with a subset of a pixel object. 288 | 289 | :param pixel_object: An object that implements the Neopixel or Dotstar protocol. 290 | :param int start: Starting pixel number. 291 | :param int end: Ending pixel number. 292 | 293 | .. code-block:: python 294 | 295 | import board 296 | import neopixel 297 | from adafruit_led_animation.helper import PixelSubset 298 | pixels = neopixel.NeoPixel(board.D12, 307, auto_write=False) 299 | 300 | star_start = 260 301 | star_arm = PixelSubset(pixels, star_start + 7, star_start + 15) 302 | star_arm.fill((255, 0, 255)) 303 | pixels.show() 304 | """ 305 | 306 | def __init__(self, pixel_object, start, end): 307 | super().__init__( 308 | pixel_object, 309 | pixel_ranges=[[n] for n in range(start, end)], 310 | individual_pixels=True, 311 | ) 312 | -------------------------------------------------------------------------------- /adafruit_led_animation/pulse_generator.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2019 Kattni Rembor for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | `adafruit_led_animation.pulse_generator` 7 | ================================================================================ 8 | 9 | Helper method for pulse generation 10 | 11 | * Author(s): Kattni Rembor 12 | 13 | Implementation Notes 14 | -------------------- 15 | 16 | **Hardware:** 17 | 18 | * `Adafruit NeoPixels `_ 19 | * `Adafruit DotStars `_ 20 | 21 | **Software and Dependencies:** 22 | 23 | * Adafruit CircuitPython firmware for the supported boards: 24 | https://circuitpython.org/downloads 25 | 26 | """ 27 | 28 | from . import MS_PER_SECOND, monotonic_ms 29 | from .color import calculate_intensity 30 | 31 | 32 | def pulse_generator(period: float, animation_object, dotstar_pwm=False): 33 | """ 34 | Generates a sequence of colors for a pulse, based on the time period specified. 35 | :param period: Pulse duration in seconds. 36 | :param animation_object: An animation object to interact with. 37 | :param dotstar_pwm: Whether to use the dostar per pixel PWM value for brightness control. 38 | """ 39 | period = int((period + (animation_object.breath * 2)) * MS_PER_SECOND) 40 | half_breath = int(animation_object.breath * MS_PER_SECOND // 2) 41 | half_period = period // 2 42 | 43 | last_update = monotonic_ms() 44 | cycle_position = 0 45 | last_pos = 0 46 | while True: 47 | now = monotonic_ms() 48 | time_since_last_draw = now - last_update 49 | last_update = now 50 | pos = cycle_position = (cycle_position + time_since_last_draw) % period 51 | if pos < last_pos: 52 | animation_object.cycle_complete = True 53 | last_pos = pos 54 | if pos > half_period: 55 | pos = period - pos 56 | if pos < half_breath: 57 | intensity = animation_object.min_intensity 58 | elif pos > (half_period - half_breath): 59 | intensity = animation_object.max_intensity 60 | else: 61 | intensity = animation_object.min_intensity + ( 62 | ((pos - half_breath) / (half_period - (half_breath * 2))) 63 | * (animation_object.max_intensity - animation_object.min_intensity) 64 | ) 65 | if dotstar_pwm: 66 | fill_color = ( 67 | animation_object.color[0], 68 | animation_object.color[1], 69 | animation_object.color[2], 70 | intensity, 71 | ) 72 | yield fill_color 73 | continue 74 | yield calculate_intensity(animation_object.color, intensity) 75 | -------------------------------------------------------------------------------- /adafruit_led_animation/sequence.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 Kattni Rembor for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | `adafruit_led_animation.sequence` 7 | ================================================================================ 8 | 9 | Animation sequence helper for CircuitPython helper library for LED animations. 10 | 11 | 12 | * Author(s): Kattni Rembor 13 | 14 | Implementation Notes 15 | -------------------- 16 | 17 | **Hardware:** 18 | 19 | * `Adafruit NeoPixels `_ 20 | * `Adafruit DotStars `_ 21 | 22 | **Software and Dependencies:** 23 | 24 | * Adafruit CircuitPython firmware for the supported boards: 25 | https://circuitpython.org/downloads 26 | 27 | """ 28 | 29 | import random 30 | 31 | from adafruit_led_animation.color import BLACK 32 | 33 | from . import MS_PER_SECOND, monotonic_ms 34 | 35 | __version__ = "0.0.0+auto.0" 36 | __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_LED_Animation.git" 37 | 38 | 39 | class AnimationSequence: 40 | """ 41 | A sequence of Animations to run in succession, looping forever. 42 | Advances manually, or at the specified interval. 43 | 44 | :param members: The animation objects or groups. 45 | :param int advance_interval: Time in seconds between animations if cycling 46 | automatically. Defaults to ``None``. 47 | :param bool auto_clear: Clear the pixels between animations. If ``True``, the current animation 48 | will be cleared from the pixels before the next one starts. 49 | Defaults to ``False``. 50 | :param bool random_order: Activate the animations in a random order. Defaults to ``False``. 51 | :param bool auto_reset: Automatically call reset() on animations when changing animations. 52 | :param bool advance_on_cycle_complete: Automatically advance when `on_cycle_complete` is 53 | triggered on member animations. All Animations must 54 | support on_cycle_complete to use this. 55 | 56 | .. code-block:: python 57 | 58 | import board 59 | import neopixel 60 | from adafruit_led_animation.sequence import AnimationSequence 61 | import adafruit_led_animation.animation.comet as comet_animation 62 | import adafruit_led_animation.animation.sparkle as sparkle_animation 63 | import adafruit_led_animation.animation.blink as blink_animation 64 | import adafruit_led_animation.color as color 65 | 66 | strip_pixels = neopixel.NeoPixel(board.A1, 30, brightness=1, auto_write=False) 67 | 68 | blink = blink_animation.Blink(strip_pixels, 0.2, color.RED) 69 | comet = comet_animation.Comet(strip_pixels, 0.1, color.BLUE) 70 | sparkle = sparkle_animation.Sparkle(strip_pixels, 0.05, color.GREEN) 71 | 72 | animations = AnimationSequence(blink, comet, sparkle, advance_interval=5) 73 | 74 | while True: 75 | animations.animate() 76 | """ 77 | 78 | def __init__( 79 | self, 80 | *members, 81 | advance_interval=None, 82 | auto_clear=True, 83 | random_order=False, 84 | auto_reset=False, 85 | advance_on_cycle_complete=False, 86 | name=None, 87 | ): 88 | if advance_interval and advance_on_cycle_complete: 89 | raise ValueError("Cannot use both advance_interval and advance_on_cycle_complete.") 90 | self._members = members 91 | self._advance_interval = advance_interval * MS_PER_SECOND if advance_interval else None 92 | self._last_advance = monotonic_ms() 93 | self._current = 0 94 | self.auto_clear = auto_clear 95 | self.auto_reset = auto_reset 96 | self.advance_on_cycle_complete = advance_on_cycle_complete 97 | self.clear_color = BLACK 98 | self._paused = False 99 | self._paused_at = 0 100 | self._random = random_order 101 | self._also_notify = [] 102 | self.cycle_count = 0 103 | self.notify_cycles = 1 104 | self.name = name 105 | if random_order: 106 | self._current = random.randint(0, len(self._members) - 1) 107 | self._color = None 108 | for member in self._members: 109 | member.add_cycle_complete_receiver(self._sequence_complete) 110 | self.on_cycle_complete_supported = self._members[-1].on_cycle_complete_supported 111 | 112 | on_cycle_complete_supported = True 113 | 114 | def __str__(self): 115 | return f"<{self.__class__.__name__}: {self.name}>" 116 | 117 | def on_cycle_complete(self): 118 | """ 119 | Called by some animations when they complete an animation cycle. 120 | Animations that support cycle complete notifications will have X property set to False. 121 | Override as needed. 122 | """ 123 | self.cycle_count += 1 124 | if self.cycle_count % self.notify_cycles == 0: 125 | for callback in self._also_notify: 126 | callback(self) 127 | 128 | def _sequence_complete(self, animation): 129 | if self.advance_on_cycle_complete: 130 | self._advance() 131 | 132 | def add_cycle_complete_receiver(self, callback): 133 | """ 134 | Adds an additional callback when the cycle completes. 135 | 136 | :param callback: Additional callback to trigger when a cycle completes. The callback 137 | is passed the animation object instance. 138 | """ 139 | self._also_notify.append(callback) 140 | 141 | def _auto_advance(self): 142 | if not self._advance_interval: 143 | return 144 | now = monotonic_ms() 145 | if now - self._last_advance > self._advance_interval: 146 | self._last_advance = now 147 | self._advance() 148 | 149 | def _advance(self): 150 | if self.auto_reset: 151 | self.current_animation.reset() 152 | if self.auto_clear: 153 | self.current_animation.fill(self.clear_color) 154 | if self._random: 155 | self.random() 156 | else: 157 | self.next() 158 | 159 | def activate(self, index): 160 | """ 161 | Activates a specific animation. 162 | """ 163 | if isinstance(index, str): 164 | self._current = [member.name for member in self._members].index(index) 165 | else: 166 | self._current = index 167 | if self._color: 168 | self.current_animation.color = self._color 169 | 170 | def next(self): 171 | """ 172 | Jump to the next animation. 173 | """ 174 | current = self._current + 1 175 | if current >= len(self._members): 176 | self.on_cycle_complete() 177 | self.activate(current % len(self._members)) 178 | 179 | def previous(self): 180 | """ 181 | Jump to the previous animation. 182 | """ 183 | current = self._current - 1 184 | self.activate(current % len(self._members)) 185 | 186 | def random(self): 187 | """ 188 | Jump to a random animation. 189 | """ 190 | self.activate(random.randint(0, len(self._members) - 1)) 191 | 192 | def animate(self, show=True): 193 | """ 194 | Call animate() from your code's main loop. It will draw the current animation 195 | or go to the next animation based on the advance_interval if set. 196 | 197 | :return: True if the animation draw cycle was triggered, otherwise False. 198 | """ 199 | if not self._paused and self._advance_interval: 200 | self._auto_advance() 201 | return self.current_animation.animate(show) 202 | 203 | @property 204 | def current_animation(self): 205 | """ 206 | Returns the current animation in the sequence. 207 | """ 208 | return self._members[self._current] 209 | 210 | @property 211 | def color(self): 212 | """ 213 | Use this property to change the color of all animations in the sequence. 214 | """ 215 | return self._color 216 | 217 | @color.setter 218 | def color(self, color): 219 | self._color = color 220 | self.current_animation.color = color 221 | 222 | def fill(self, color): 223 | """ 224 | Fills the current animation with a color. 225 | """ 226 | self.current_animation.fill(color) 227 | 228 | def freeze(self): 229 | """ 230 | Freeze the current animation in the sequence. 231 | Also stops auto_advance. 232 | """ 233 | if self._paused: 234 | return 235 | self._paused = True 236 | self._paused_at = monotonic_ms() 237 | self.current_animation.freeze() 238 | 239 | def resume(self): 240 | """ 241 | Resume the current animation in the sequence, and resumes auto advance if enabled. 242 | """ 243 | if not self._paused: 244 | return 245 | self._paused = False 246 | now = monotonic_ms() 247 | self._last_advance += now - self._paused_at 248 | self._paused_at = 0 249 | self.current_animation.resume() 250 | 251 | def reset(self): 252 | """ 253 | Resets the current animation. 254 | """ 255 | self.current_animation.reset() 256 | 257 | def show(self): 258 | """ 259 | Draws the current animation group members. 260 | """ 261 | self.current_animation.show() 262 | 263 | 264 | class AnimateOnce(AnimationSequence): 265 | """ 266 | Wrapper around AnimationSequence that returns False to animate() until a sequence has completed. 267 | Takes the same arguments as AnimationSequence, but overrides advance_on_cycle_complete=True 268 | and advance_interval=0 269 | 270 | Example: 271 | 272 | This example animates a comet in one direction then pulses red momentarily 273 | 274 | .. code-block:: python 275 | 276 | import board 277 | import neopixel 278 | from adafruit_led_animation.animation.comet import Comet 279 | from adafruit_led_animation.animation.pulse import Pulse 280 | from adafruit_led_animation.color import BLUE, RED 281 | from adafruit_led_animation.sequence import AnimateOnce 282 | 283 | strip_pixels = neopixel.NeoPixel(board.A1, 30, brightness=0.5, auto_write=False) 284 | 285 | comet = Comet(strip_pixels, 0.01, color=BLUE, bounce=False) 286 | pulse = Pulse(strip_pixels, 0.01, color=RED, period=2) 287 | 288 | animations = AnimateOnce(comet, pulse) 289 | 290 | while animations.animate(): 291 | pass 292 | 293 | """ 294 | 295 | def __init__(self, *members, **kwargs): 296 | kwargs["advance_on_cycle_complete"] = True 297 | kwargs["advance_interval"] = 0 298 | super().__init__(*members, **kwargs) 299 | self._running = True 300 | 301 | def on_cycle_complete(self): 302 | super().on_cycle_complete() 303 | self._running = False 304 | 305 | def animate(self, show=True): 306 | super().animate(show) 307 | return self._running 308 | -------------------------------------------------------------------------------- /adafruit_led_animation/timedsequence.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 Gamblor21 2 | # 3 | # SPDX-License-Identifier: MIT 4 | """ 5 | `adafruit_led_animation.timedsequence` 6 | ================================================================================ 7 | 8 | Animation timed sequence helper for CircuitPython helper library for LED animations. 9 | 10 | 11 | * Author(s): Mark Komus 12 | 13 | Implementation Notes 14 | -------------------- 15 | 16 | **Hardware:** 17 | 18 | * `Adafruit NeoPixels `_ 19 | * `Adafruit DotStars `_ 20 | 21 | **Software and Dependencies:** 22 | 23 | * Adafruit CircuitPython firmware for the supported boards: 24 | https://circuitpython.org/downloads 25 | 26 | """ 27 | 28 | from adafruit_led_animation.sequence import AnimationSequence 29 | 30 | from . import MS_PER_SECOND 31 | 32 | 33 | class TimedAnimationSequence(AnimationSequence): 34 | """ 35 | A sequence of Animations to run in succession, each animation running for an 36 | individual amount of time. 37 | :param members: The animation objects or groups followed by how long the animation 38 | should run in seconds. 39 | :param bool auto_clear: Clear the pixels between animations. If ``True``, the current animation 40 | will be cleared from the pixels before the next one starts. 41 | Defaults to ``False``. 42 | :param bool random_order: Activate the animations in a random order. Defaults to ``False``. 43 | :param bool auto_reset: Automatically call reset() on animations when changing animations. 44 | .. code-block:: python 45 | import board 46 | import neopixel 47 | from adafruit_led_animation.timedsequence import TimedAnimationSequence 48 | import adafruit_led_animation.animation.comet as comet_animation 49 | import adafruit_led_animation.animation.sparkle as sparkle_animation 50 | import adafruit_led_animation.animation.blink as blink_animation 51 | import adafruit_led_animation.color as color 52 | strip_pixels = neopixel.NeoPixel(board.A1, 30, brightness=1, auto_write=False) 53 | blink = blink_animation.Blink(strip_pixels, 0.2, color.RED) 54 | comet = comet_animation.Comet(strip_pixels, 0.1, color.BLUE) 55 | sparkle = sparkle_animation.Sparkle(strip_pixels, 0.05, color.GREEN) 56 | animations = TimedAnimationSequence(blink, 5, comet, 3, sparkle, 7) 57 | while True: 58 | animations.animate() 59 | """ 60 | 61 | def __init__(self, *members, auto_clear=True, random_order=False, auto_reset=False, name=None): 62 | self._animation_members = [] 63 | self._animation_timings = [] 64 | for x, item in enumerate(members): 65 | if not x % 2: 66 | self._animation_members.append(item) 67 | else: 68 | self._animation_timings.append(item) 69 | 70 | super().__init__( 71 | *self._animation_members, 72 | auto_clear=auto_clear, 73 | random_order=random_order, 74 | auto_reset=auto_reset, 75 | advance_on_cycle_complete=False, 76 | name=name, 77 | ) 78 | self._advance_interval = self._animation_timings[self._current] * MS_PER_SECOND 79 | 80 | def activate(self, index): 81 | super().activate(index) 82 | self._advance_interval = self._animation_timings[self._current] * MS_PER_SECOND 83 | -------------------------------------------------------------------------------- /docs/_static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_CircuitPython_LED_Animation/c76c94817528391f5ae6f82f9c15be37cb622be5/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 | .. If your library file(s) are nested in a directory (e.g. /adafruit_foo/foo.py) 5 | .. use this format as the module name: "adafruit_foo.foo" 6 | 7 | API Reference 8 | ############# 9 | 10 | .. automodule:: adafruit_led_animation.animation 11 | :members: 12 | 13 | .. automodule:: adafruit_led_animation.color 14 | :members: 15 | 16 | .. automodule:: adafruit_led_animation.helper 17 | :members: 18 | 19 | .. automodule:: adafruit_led_animation.group 20 | :members: 21 | 22 | .. automodule:: adafruit_led_animation.sequence 23 | :members: 24 | 25 | .. automodule:: adafruit_led_animation.animation.blink 26 | :members: 27 | 28 | .. automodule:: adafruit_led_animation.animation.solid 29 | :members: 30 | 31 | .. automodule:: adafruit_led_animation.animation.colorcycle 32 | :members: 33 | 34 | .. automodule:: adafruit_led_animation.animation.chase 35 | :members: 36 | 37 | .. automodule:: adafruit_led_animation.animation.comet 38 | :members: 39 | 40 | .. automodule:: adafruit_led_animation.animation.pulse 41 | :members: 42 | 43 | .. automodule:: adafruit_led_animation.animation.rainbow 44 | :members: 45 | 46 | .. automodule:: adafruit_led_animation.animation.sparkle 47 | :members: 48 | 49 | .. automodule:: adafruit_led_animation.animation.rainbowchase 50 | :members: 51 | 52 | .. automodule:: adafruit_led_animation.animation.rainbowcomet 53 | :members: 54 | 55 | .. automodule:: adafruit_led_animation.animation.rainbowsparkle 56 | :members: 57 | 58 | .. automodule:: adafruit_led_animation.animation.sparklepulse 59 | :members: 60 | -------------------------------------------------------------------------------- /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.napoleon", 21 | "sphinx.ext.todo", 22 | ] 23 | 24 | # TODO: Please Read! 25 | # Uncomment the below if you use native CircuitPython modules such as 26 | # digitalio, micropython and busio. List the modules you use. Without it, the 27 | # autodoc module docs will fail to generate with a warning. 28 | autodoc_mock_imports = ["rainbowio"] 29 | 30 | 31 | intersphinx_mapping = { 32 | "python": ("https://docs.python.org/3", None), 33 | "CircuitPython": ("https://docs.circuitpython.org/en/latest/", None), 34 | } 35 | 36 | # Add any paths that contain templates here, relative to this directory. 37 | templates_path = ["_templates"] 38 | 39 | source_suffix = ".rst" 40 | 41 | # The master toctree document. 42 | master_doc = "index" 43 | 44 | # General information about the project. 45 | project = "LED_Animation Library" 46 | creation_year = "2020" 47 | current_year = str(datetime.datetime.now().year) 48 | year_duration = ( 49 | current_year if current_year == creation_year else creation_year + " - " + current_year 50 | ) 51 | copyright = year_duration + " Kattni Rembor" 52 | author = "Kattni Rembor" 53 | 54 | # The version info for the project you're documenting, acts as replacement for 55 | # |version| and |release|, also used in various other places throughout the 56 | # built documents. 57 | # 58 | # The short X.Y version. 59 | version = "1.0" 60 | # The full version, including alpha/beta/rc tags. 61 | release = "1.0" 62 | 63 | # The language for content autogenerated by Sphinx. Refer to documentation 64 | # for a list of supported languages. 65 | # 66 | # This is also used if you do content translation via gettext catalogs. 67 | # Usually you set "language" from the command line for these cases. 68 | language = "en" 69 | 70 | # List of patterns, relative to source directory, that match files and 71 | # directories to ignore when looking for source files. 72 | # This patterns also effect to html_static_path and html_extra_path 73 | exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", ".env", "CODE_OF_CONDUCT.md"] 74 | 75 | # The reST default role (used for this markup: `text`) to use for all 76 | # documents. 77 | # 78 | default_role = "any" 79 | 80 | # If true, '()' will be appended to :func: etc. cross-reference text. 81 | # 82 | add_function_parentheses = True 83 | 84 | # The name of the Pygments (syntax highlighting) style to use. 85 | pygments_style = "sphinx" 86 | 87 | # If true, `todo` and `todoList` produce output, else they produce nothing. 88 | todo_include_todos = False 89 | 90 | # If this is True, todo emits a warning for each TODO entries. The default is False. 91 | todo_emit_warnings = True 92 | 93 | napoleon_numpy_docstring = False 94 | 95 | # -- Options for HTML output ---------------------------------------------- 96 | 97 | # The theme to use for HTML and HTML Help pages. See the documentation for 98 | # a list of builtin themes. 99 | # 100 | import sphinx_rtd_theme 101 | 102 | html_theme = "sphinx_rtd_theme" 103 | 104 | # Add any paths that contain custom static files (such as style sheets) here, 105 | # relative to this directory. They are copied after the builtin static files, 106 | # so a file named "default.css" will overwrite the builtin "default.css". 107 | html_static_path = ["_static"] 108 | 109 | # The name of an image file (relative to this directory) to use as a favicon of 110 | # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 111 | # pixels large. 112 | # 113 | html_favicon = "_static/favicon.ico" 114 | 115 | # Output file base name for HTML help builder. 116 | htmlhelp_basename = "Led_animationLibrarydoc" 117 | 118 | # -- Options for LaTeX output --------------------------------------------- 119 | 120 | latex_elements = { 121 | # The paper size ('letterpaper' or 'a4paper'). 122 | # 123 | # 'papersize': 'letterpaper', 124 | # The font size ('10pt', '11pt' or '12pt'). 125 | # 126 | # 'pointsize': '10pt', 127 | # Additional stuff for the LaTeX preamble. 128 | # 129 | # 'preamble': '', 130 | # Latex figure (float) alignment 131 | # 132 | # 'figure_align': 'htbp', 133 | } 134 | 135 | # Grouping the document tree into LaTeX files. List of tuples 136 | # (source start file, target name, title, 137 | # author, documentclass [howto, manual, or own class]). 138 | latex_documents = [ 139 | ( 140 | master_doc, 141 | "LED_AnimationLibrary.tex", 142 | "LED_Animation Library Documentation", 143 | author, 144 | "manual", 145 | ), 146 | ] 147 | 148 | # -- Options for manual page output --------------------------------------- 149 | 150 | # One entry per manual page. List of tuples 151 | # (source start file, name, description, authors, manual section). 152 | man_pages = [ 153 | ( 154 | master_doc, 155 | "LED_Animationlibrary", 156 | "LED_Animation Library Documentation", 157 | [author], 158 | 1, 159 | ) 160 | ] 161 | 162 | # -- Options for Texinfo output ------------------------------------------- 163 | 164 | # Grouping the document tree into Texinfo files. List of tuples 165 | # (source start file, target name, title, author, 166 | # dir menu entry, description, category) 167 | texinfo_documents = [ 168 | ( 169 | master_doc, 170 | "LED_AnimationLibrary", 171 | " LED_Animation Library Documentation", 172 | author, 173 | "LED_AnimationLibrary", 174 | "One line description of project.", 175 | "Miscellaneous", 176 | ), 177 | ] 178 | -------------------------------------------------------------------------------- /docs/examples.rst: -------------------------------------------------------------------------------- 1 | Simple test 2 | ------------ 3 | 4 | Ensure your device works with this simple test. 5 | 6 | .. literalinclude:: ../examples/led_animation_simpletest.py 7 | :caption: examples/led_animation_simpletest.py 8 | :linenos: 9 | 10 | Basic Animations 11 | ---------------- 12 | 13 | Demonstrates the basic animations. 14 | 15 | .. literalinclude:: ../examples/led_animation_basic_animations.py 16 | :caption: examples/led_animation_basic_animations.py 17 | :linenos: 18 | 19 | All Animations 20 | ---------------- 21 | 22 | Demonstrates the entire suite of animations. 23 | 24 | .. literalinclude:: ../examples/led_animation_all_animations.py 25 | :caption: examples/led_animation_all_animations.py 26 | :linenos: 27 | 28 | Pixel Map 29 | --------- 30 | 31 | Demonstrates the pixel mapping feature. 32 | 33 | .. literalinclude:: ../examples/led_animation_pixel_map.py 34 | :caption: examples/led_animation_pixel_map.py 35 | :linenos: 36 | 37 | Animation Sequence 38 | ------------------ 39 | 40 | Demonstrates the animation sequence feature. 41 | 42 | .. literalinclude:: ../examples/led_animation_sequence.py 43 | :caption: examples/led_animation_sequence.py 44 | :linenos: 45 | 46 | Animation Group 47 | --------------- 48 | 49 | Demonstrates the animation group feature. 50 | 51 | .. literalinclude:: ../examples/led_animation_group.py 52 | :caption: examples/led_animation_group.py 53 | :linenos: 54 | 55 | Blink 56 | ----- 57 | 58 | Demonstrates the blink animation. 59 | 60 | .. literalinclude:: ../examples/led_animation_blink.py 61 | :caption: examples/led_animation_blink.py 62 | :linenos: 63 | 64 | Blink with a selcted background color 65 | ---------------------------------------- 66 | Demonstrates the blink animation with an user defined background color. 67 | 68 | .. literalinclude:: ../examples/led_animation_blink_with_background.py 69 | :caption: examples/led_animation_blink_with_background.py 70 | :linenos: 71 | 72 | Comet 73 | ----- 74 | 75 | Demonstrates the comet animation. 76 | 77 | .. literalinclude:: ../examples/led_animation_comet.py 78 | :caption: examples/led_animation_comet.py 79 | :linenos: 80 | 81 | Chase 82 | ----- 83 | 84 | Demonstrates the chase animation. 85 | 86 | .. literalinclude:: ../examples/led_animation_chase.py 87 | :caption: examples/led_animation_chase.py 88 | :linenos: 89 | 90 | Rainbow 91 | ------- 92 | 93 | Demonstrates the rainbow animations. 94 | 95 | .. literalinclude:: ../examples/led_animation_rainbow_animations.py 96 | :caption: examples/led_animation_rainbow_animations.py 97 | :linenos: 98 | 99 | Sparkle 100 | ------- 101 | 102 | Demonstrates the sparkle animations. 103 | 104 | .. literalinclude:: ../examples/led_animation_sparkle_animations.py 105 | :caption: examples/led_animation_sparkle_animations.py 106 | :linenos: 107 | 108 | Pacman 109 | ------ 110 | 111 | Demonstrates the pacman animation. 112 | 113 | .. literalinclude:: ../examples/led_animation_pacman.py 114 | :caption: examples/led_animation_pacman.py 115 | :linenos: 116 | -------------------------------------------------------------------------------- /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 | 24 | .. toctree:: 25 | :caption: Tutorials 26 | 27 | CircuitPython LED Animations Learning Guide 28 | 29 | .. toctree:: 30 | :caption: Related Products 31 | 32 | Adafruit NeoPixel LEDs 33 | 34 | Adafruit DotStar LEDs 35 | 36 | .. toctree:: 37 | :caption: Other Links 38 | 39 | Download from GitHub 40 | Download Library Bundle 41 | CircuitPython Reference Documentation 42 | CircuitPython Support Forum 43 | Discord Chat 44 | Adafruit Learning System 45 | Adafruit Blog 46 | Adafruit Store 47 | 48 | Indices and tables 49 | ================== 50 | 51 | * :ref:`genindex` 52 | * :ref:`modindex` 53 | * :ref:`search` 54 | -------------------------------------------------------------------------------- /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/led_animation_all_animations.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries 2 | # SPDX-License-Identifier: MIT 3 | 4 | """ 5 | This example repeatedly displays all available animations, at a five second interval. 6 | 7 | For NeoPixel FeatherWing. Update pixel_pin and pixel_num to match your wiring if using 8 | a different form of NeoPixels. 9 | 10 | This example does not work on SAMD21 (M0) boards. 11 | """ 12 | 13 | import board 14 | import neopixel 15 | 16 | from adafruit_led_animation.animation.blink import Blink 17 | from adafruit_led_animation.animation.chase import Chase 18 | from adafruit_led_animation.animation.colorcycle import ColorCycle 19 | from adafruit_led_animation.animation.comet import Comet 20 | from adafruit_led_animation.animation.customcolorchase import CustomColorChase 21 | from adafruit_led_animation.animation.pulse import Pulse 22 | from adafruit_led_animation.animation.rainbow import Rainbow 23 | from adafruit_led_animation.animation.rainbowchase import RainbowChase 24 | from adafruit_led_animation.animation.rainbowcomet import RainbowComet 25 | from adafruit_led_animation.animation.rainbowsparkle import RainbowSparkle 26 | from adafruit_led_animation.animation.solid import Solid 27 | from adafruit_led_animation.animation.sparkle import Sparkle 28 | from adafruit_led_animation.animation.sparklepulse import SparklePulse 29 | from adafruit_led_animation.color import AMBER, JADE, MAGENTA, ORANGE, PURPLE, WHITE 30 | from adafruit_led_animation.sequence import AnimationSequence 31 | 32 | # Update to match the pin connected to your NeoPixels 33 | pixel_pin = board.D6 34 | # Update to match the number of NeoPixels you have connected 35 | pixel_num = 32 36 | 37 | pixels = neopixel.NeoPixel(pixel_pin, pixel_num, brightness=0.5, auto_write=False) 38 | 39 | blink = Blink(pixels, speed=0.5, color=JADE) 40 | colorcycle = ColorCycle(pixels, speed=0.4, colors=[MAGENTA, ORANGE]) 41 | comet = Comet(pixels, speed=0.01, color=PURPLE, tail_length=10, bounce=True) 42 | chase = Chase(pixels, speed=0.1, size=3, spacing=6, color=WHITE) 43 | pulse = Pulse(pixels, speed=0.1, period=3, color=AMBER) 44 | sparkle = Sparkle(pixels, speed=0.1, color=PURPLE, num_sparkles=10) 45 | solid = Solid(pixels, color=JADE) 46 | rainbow = Rainbow(pixels, speed=0.1, period=2) 47 | sparkle_pulse = SparklePulse(pixels, speed=0.1, period=3, color=JADE) 48 | rainbow_comet = RainbowComet(pixels, speed=0.1, tail_length=7, bounce=True) 49 | rainbow_chase = RainbowChase(pixels, speed=0.1, size=3, spacing=2, step=8) 50 | rainbow_sparkle = RainbowSparkle(pixels, speed=0.1, num_sparkles=15) 51 | custom_color_chase = CustomColorChase( 52 | pixels, speed=0.1, size=2, spacing=3, colors=[ORANGE, WHITE, JADE] 53 | ) 54 | 55 | 56 | animations = AnimationSequence( 57 | comet, 58 | blink, 59 | rainbow_sparkle, 60 | chase, 61 | pulse, 62 | sparkle, 63 | rainbow, 64 | solid, 65 | rainbow_comet, 66 | sparkle_pulse, 67 | rainbow_chase, 68 | custom_color_chase, 69 | advance_interval=5, 70 | auto_clear=True, 71 | ) 72 | 73 | while True: 74 | animations.animate() 75 | -------------------------------------------------------------------------------- /examples/led_animation_basic_animations.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries 2 | # SPDX-License-Identifier: MIT 3 | 4 | """ 5 | This example displays the basic animations in sequence, at a five second interval. 6 | 7 | For NeoPixel FeatherWing. Update pixel_pin and pixel_num to match your wiring if using 8 | a different form of NeoPixels. 9 | 10 | This example may not work on SAMD21 (M0) boards. 11 | """ 12 | 13 | import board 14 | import neopixel 15 | 16 | from adafruit_led_animation.animation.blink import Blink 17 | from adafruit_led_animation.animation.chase import Chase 18 | from adafruit_led_animation.animation.colorcycle import ColorCycle 19 | from adafruit_led_animation.animation.comet import Comet 20 | from adafruit_led_animation.animation.pulse import Pulse 21 | from adafruit_led_animation.animation.solid import Solid 22 | from adafruit_led_animation.color import ( 23 | AMBER, 24 | JADE, 25 | MAGENTA, 26 | ORANGE, 27 | PINK, 28 | PURPLE, 29 | TEAL, 30 | WHITE, 31 | ) 32 | from adafruit_led_animation.sequence import AnimationSequence 33 | 34 | # Update to match the pin connected to your NeoPixels 35 | pixel_pin = board.D6 36 | # Update to match the number of NeoPixels you have connected 37 | pixel_num = 32 38 | 39 | pixels = neopixel.NeoPixel(pixel_pin, pixel_num, brightness=0.5, auto_write=False) 40 | 41 | solid = Solid(pixels, color=PINK) 42 | blink = Blink(pixels, speed=0.5, color=JADE) 43 | colorcycle = ColorCycle(pixels, speed=0.4, colors=[MAGENTA, ORANGE, TEAL]) 44 | chase = Chase(pixels, speed=0.1, color=WHITE, size=3, spacing=6) 45 | comet = Comet(pixels, speed=0.01, color=PURPLE, tail_length=10, bounce=True) 46 | pulse = Pulse(pixels, speed=0.1, color=AMBER, period=3) 47 | 48 | 49 | animations = AnimationSequence( 50 | solid, 51 | blink, 52 | colorcycle, 53 | chase, 54 | comet, 55 | pulse, 56 | advance_interval=5, 57 | auto_clear=True, 58 | ) 59 | 60 | while True: 61 | animations.animate() 62 | -------------------------------------------------------------------------------- /examples/led_animation_blink.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries 2 | # SPDX-License-Identifier: MIT 3 | 4 | """ 5 | This example blinks the LEDs purple at a 0.5 second interval. 6 | 7 | For QT Py Haxpress and a NeoPixel strip. Update pixel_pin and pixel_num to match your wiring if 8 | using a different board or form of NeoPixels. 9 | 10 | This example will run on SAMD21 (M0) Express boards (such as Circuit Playground Express or QT Py 11 | Haxpress), but not on SAMD21 non-Express boards (such as QT Py or Trinket). 12 | """ 13 | 14 | import board 15 | import neopixel 16 | 17 | from adafruit_led_animation.animation.blink import Blink 18 | from adafruit_led_animation.color import PURPLE 19 | 20 | # Update to match the pin connected to your NeoPixels 21 | pixel_pin = board.A3 22 | # Update to match the number of NeoPixels you have connected 23 | pixel_num = 30 24 | 25 | pixels = neopixel.NeoPixel(pixel_pin, pixel_num, brightness=0.5, auto_write=False) 26 | 27 | blink = Blink(pixels, speed=0.5, color=PURPLE) 28 | 29 | while True: 30 | blink.animate() 31 | -------------------------------------------------------------------------------- /examples/led_animation_blink_with_background.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2025 Jose D. Montoya 2 | # SPDX-License-Identifier: MIT 3 | 4 | """ 5 | This example blinks the LEDs purple with a yellow background at a 0.5 second interval. 6 | 7 | For QT Py Haxpress and a NeoPixel strip. Update pixel_pin and pixel_num to match your wiring if 8 | using a different board or form of NeoPixels. 9 | 10 | This example will run on SAMD21 (M0) Express boards (such as Circuit Playground Express or QT Py 11 | Haxpress), but not on SAMD21 non-Express boards (such as QT Py or Trinket). 12 | """ 13 | 14 | import board 15 | import neopixel 16 | 17 | from adafruit_led_animation.animation.blink import Blink 18 | from adafruit_led_animation.color import PURPLE, YELLOW 19 | 20 | # Update to match the pin connected to your NeoPixels 21 | pixel_pin = board.A3 22 | # Update to match the number of NeoPixels you have connected 23 | pixel_num = 30 24 | 25 | pixels = neopixel.NeoPixel(pixel_pin, pixel_num, brightness=0.5, auto_write=False) 26 | 27 | blink = Blink(pixels, speed=0.5, color=PURPLE, background_color=YELLOW) 28 | 29 | while True: 30 | blink.animate() 31 | -------------------------------------------------------------------------------- /examples/led_animation_chase.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries 2 | # SPDX-License-Identifier: MIT 3 | 4 | """ 5 | This example animates a theatre chase style animation in white with a repeated 3 LEDs lit up at a 6 | spacing of six LEDs off. 7 | 8 | For QT Py Haxpress and a NeoPixel strip. Update pixel_pin and pixel_num to match your wiring if 9 | using a different board or form of NeoPixels. 10 | 11 | This example will run on SAMD21 (M0) Express boards (such as Circuit Playground Express or QT Py 12 | Haxpress), but not on SAMD21 non-Express boards (such as QT Py or Trinket). 13 | """ 14 | 15 | import board 16 | import neopixel 17 | 18 | from adafruit_led_animation.animation.chase import Chase 19 | from adafruit_led_animation.color import WHITE 20 | 21 | # Update to match the pin connected to your NeoPixels 22 | pixel_pin = board.A3 23 | # Update to match the number of NeoPixels you have connected 24 | pixel_num = 30 25 | 26 | pixels = neopixel.NeoPixel(pixel_pin, pixel_num, brightness=0.5, auto_write=False) 27 | 28 | chase = Chase(pixels, speed=0.1, size=3, spacing=6, color=WHITE) 29 | 30 | while True: 31 | chase.animate() 32 | -------------------------------------------------------------------------------- /examples/led_animation_comet.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries 2 | # SPDX-License-Identifier: MIT 3 | 4 | """ 5 | This example animates a jade comet that bounces from end to end of the strip. 6 | 7 | For QT Py Haxpress and a NeoPixel strip. Update pixel_pin and pixel_num to match your wiring if 8 | using a different board or form of NeoPixels. 9 | 10 | This example will run on SAMD21 (M0) Express boards (such as Circuit Playground Express or QT Py 11 | Haxpress), but not on SAMD21 non-Express boards (such as QT Py or Trinket). 12 | """ 13 | 14 | import board 15 | import neopixel 16 | 17 | from adafruit_led_animation.animation.comet import Comet 18 | from adafruit_led_animation.color import JADE 19 | 20 | # Update to match the pin connected to your NeoPixels 21 | pixel_pin = board.A3 22 | # Update to match the number of NeoPixels you have connected 23 | pixel_num = 30 24 | 25 | pixels = neopixel.NeoPixel(pixel_pin, pixel_num, brightness=0.5, auto_write=False) 26 | 27 | comet = Comet(pixels, speed=0.02, color=JADE, tail_length=10, bounce=True) 28 | 29 | while True: 30 | comet.animate() 31 | -------------------------------------------------------------------------------- /examples/led_animation_customcolorchase.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries 2 | # SPDX-License-Identifier: MIT 3 | 4 | """ 5 | This example displays custom color chase animations in sequence, at a six second interval. 6 | 7 | For NeoPixel FeatherWing. Update pixel_pin and pixel_num to match your wiring if using 8 | a different form of NeoPixels. 9 | 10 | This example may not work on SAMD21 (M0) boards. 11 | """ 12 | 13 | import board 14 | import neopixel 15 | 16 | from adafruit_led_animation.animation.customcolorchase import CustomColorChase 17 | 18 | # colorwheel only needed for rainbowchase example 19 | # Colors for customcolorchase examples 20 | from adafruit_led_animation.color import BLUE, GREEN, PINK, RED, colorwheel 21 | from adafruit_led_animation.sequence import AnimationSequence 22 | 23 | # Update to match the pin connected to your NeoPixels 24 | pixel_pin = board.D5 25 | # Update to match the number of NeoPixels you have connected 26 | pixel_num = 30 27 | brightness = 0.3 28 | 29 | pixels = neopixel.NeoPixel(pixel_pin, pixel_num, brightness=brightness, auto_write=False) 30 | 31 | # colors default to RAINBOW as defined in color.py 32 | custom_color_chase_rainbow = CustomColorChase(pixels, speed=0.1, size=2, spacing=3) 33 | custom_color_chase_rainbow_r = CustomColorChase(pixels, speed=0.1, size=3, spacing=3, reverse=True) 34 | 35 | # Example with same colors as RainbowChase 36 | steps = 30 37 | # This was taken from rainbowchase.py 38 | rainbow_colors = [colorwheel(n % 256) for n in range(0, 512, steps)] 39 | # Now use rainbow_colors with CustomColorChase 40 | custom_color_chase_rainbowchase = CustomColorChase( 41 | pixels, speed=0.1, colors=rainbow_colors, size=2, spacing=3 42 | ) 43 | 44 | custom_color_chase_bgp = CustomColorChase( 45 | pixels, speed=0.1, colors=[BLUE, GREEN, PINK], size=3, spacing=2 46 | ) 47 | 48 | # Can use integer values for color, 0 is black 49 | custom_color_chase_br = CustomColorChase( 50 | pixels, speed=0.1, colors=[BLUE, 0, RED, 0], size=2, spacing=0 51 | ) 52 | 53 | animations = AnimationSequence( 54 | custom_color_chase_rainbow, 55 | custom_color_chase_rainbow_r, 56 | custom_color_chase_rainbowchase, 57 | custom_color_chase_bgp, 58 | custom_color_chase_br, 59 | advance_interval=6, 60 | auto_clear=True, 61 | ) 62 | 63 | while True: 64 | animations.animate() 65 | -------------------------------------------------------------------------------- /examples/led_animation_cycle_animations.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Alec Delaney 2 | # SPDX-License-Identifier: MIT 3 | 4 | """ 5 | This example uses AnimationsSequence along with a connected push button to cycle through 6 | two animations 7 | 8 | For NeoPixel FeatherWing. Update pixel_pin and pixel_num to match your wiring if using 9 | a different form of NeoPixels. 10 | """ 11 | 12 | import time 13 | 14 | import board 15 | import neopixel 16 | from digitalio import DigitalInOut, Direction, Pull 17 | 18 | from adafruit_led_animation.animation.solid import Solid 19 | from adafruit_led_animation.color import BLUE, RED 20 | from adafruit_led_animation.sequence import AnimationSequence 21 | 22 | # Update to match the pin connected to your NeoPixels 23 | pixel_pin = board.D6 24 | # Update to match the number of NeoPixels you have connected 25 | pixel_num = 32 26 | 27 | # Update to matchpin connected to button that connect logic high when pushed 28 | button_pin = board.D3 29 | 30 | pixels = neopixel.NeoPixel(pixel_pin, pixel_num, brightness=0.5, auto_write=False) 31 | button = DigitalInOut(button_pin) 32 | button.direction = Direction.INPUT 33 | button.pull = Pull.UP 34 | 35 | solid_blue = Solid(pixels, color=BLUE) 36 | solid_red = Solid(pixels, color=RED) 37 | animation_sequence = AnimationSequence(solid_blue, solid_red, auto_clear=True) 38 | 39 | while True: 40 | animation_sequence.animate() 41 | 42 | # Pressing the button pauses the animation permanently 43 | if not button.value: 44 | animation_sequence.next() 45 | while button.value: 46 | time.sleep(0.1) # Used for button debouncing 47 | -------------------------------------------------------------------------------- /examples/led_animation_freeze_animation.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Alec Delaney 2 | # SPDX-License-Identifier: MIT 3 | 4 | """ 5 | This example uses Pulse animation along with a connected push button to freeze 6 | the animation permanently when pressed 7 | 8 | For NeoPixel FeatherWing. Update pixel_pin and pixel_num to match your wiring if using 9 | a different form of NeoPixels. 10 | """ 11 | 12 | import board 13 | import neopixel 14 | from digitalio import DigitalInOut, Direction, Pull 15 | 16 | from adafruit_led_animation.animation.pulse import Pulse 17 | from adafruit_led_animation.color import RED 18 | 19 | # Update to match the pin connected to your NeoPixels 20 | pixel_pin = board.D6 21 | # Update to match the number of NeoPixels you have connected 22 | pixel_num = 32 23 | 24 | # Update to matchpin connected to button that connect logic high when pushed 25 | button_pin = board.D3 26 | 27 | pixels = neopixel.NeoPixel(pixel_pin, pixel_num, brightness=0.5, auto_write=False) 28 | button = DigitalInOut(button_pin) 29 | button.direction = Direction.INPUT 30 | button.pull = Pull.UP 31 | 32 | pulse_animation = Pulse(pixels, speed=0.1, period=1, color=RED) 33 | 34 | while True: 35 | pulse_animation.animate() 36 | 37 | # Pressing the button pauses the animation permanently 38 | if not button.value: 39 | pulse_animation.freeze() 40 | -------------------------------------------------------------------------------- /examples/led_animation_group.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries 2 | # SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries 3 | # SPDX-License-Identifier: MIT 4 | 5 | """ 6 | This example shows three different ways to use AnimationGroup: syncing two animations, displaying 7 | two animations at different speeds, and displaying two animations sequentially, across two separate 8 | pixel objects such as the built-in NeoPixels on a Circuit Playground Bluefruit and a NeoPixel strip. 9 | 10 | This example is written for Circuit Playground Bluefruit and a 30-pixel NeoPixel strip connected to 11 | pad A1. It does not work on Circuit Playground Express. 12 | """ 13 | 14 | import board 15 | import neopixel 16 | from adafruit_circuitplayground import cp 17 | 18 | from adafruit_led_animation import color 19 | from adafruit_led_animation.animation.blink import Blink 20 | from adafruit_led_animation.animation.chase import Chase 21 | from adafruit_led_animation.animation.comet import Comet 22 | from adafruit_led_animation.group import AnimationGroup 23 | from adafruit_led_animation.sequence import AnimationSequence 24 | 25 | strip_pixels = neopixel.NeoPixel(board.A1, 30, brightness=0.5, auto_write=False) 26 | cp.pixels.brightness = 0.5 27 | 28 | animations = AnimationSequence( 29 | # Synchronized to 0.5 seconds. Ignores the second animation setting of 3 seconds. 30 | AnimationGroup( 31 | Blink(cp.pixels, 0.5, color.CYAN), 32 | Blink(strip_pixels, 3.0, color.AMBER), 33 | sync=True, 34 | ), 35 | # Different speeds 36 | AnimationGroup( 37 | Comet(cp.pixels, 0.1, color.MAGENTA, tail_length=5), 38 | Comet(strip_pixels, 0.01, color.MAGENTA, tail_length=15), 39 | ), 40 | # Different animations 41 | AnimationGroup( 42 | Blink(cp.pixels, 0.5, color.JADE), 43 | Comet(strip_pixels, 0.05, color.TEAL, tail_length=15), 44 | ), 45 | # Sequential animations on the built-in NeoPixels then the NeoPixel strip 46 | Chase(cp.pixels, 0.05, size=2, spacing=3, color=color.PURPLE), 47 | Chase(strip_pixels, 0.05, size=2, spacing=3, color=color.PURPLE), 48 | advance_interval=3.0, 49 | auto_clear=True, 50 | auto_reset=True, 51 | ) 52 | 53 | while True: 54 | animations.animate() 55 | -------------------------------------------------------------------------------- /examples/led_animation_multicolor_comet.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2022 Tim Cocks 2 | # 3 | # SPDX-License-Identifier: MIT 4 | """ 5 | This example animates a red, yellow, and green gradient comet that bounces 6 | from end to end of the strip. 7 | 8 | For QT Py Haxpress and a NeoPixel strip. Update pixel_pin and pixel_num to match your wiring if 9 | using a different board or form of NeoPixels. 10 | 11 | This example will run on SAMD21 (M0) Express boards (such as Circuit Playground Express or QT Py 12 | Haxpress), but not on SAMD21 non-Express boards (such as QT Py or Trinket). 13 | """ 14 | 15 | import board 16 | import neopixel 17 | 18 | from adafruit_led_animation.animation.multicolor_comet import MulticolorComet 19 | 20 | # Update to match the pin connected to your NeoPixels 21 | pixel_pin = board.D9 22 | # Update to match the number of NeoPixels you have connected 23 | pixel_num = 96 24 | brightness = 0.02 25 | 26 | pixels = neopixel.NeoPixel( 27 | pixel_pin, 28 | pixel_num, 29 | brightness=brightness, 30 | auto_write=True, 31 | pixel_order=neopixel.RGB, 32 | ) 33 | 34 | comet_colors = [ 35 | 0xFF0000, 36 | 0xFD2000, 37 | 0xF93E00, 38 | 0xF45B00, 39 | 0xEC7500, 40 | 0xE28D00, 41 | 0xD5A200, 42 | 0xC6B500, 43 | 0xB5C600, 44 | 0xA2D500, 45 | 0x8DE200, 46 | 0x75EC00, 47 | 0x5BF400, 48 | 0x3EF900, 49 | 0x20FD00, 50 | 0x00FF00, 51 | ] 52 | 53 | 54 | comet = MulticolorComet( 55 | pixels, 56 | colors=comet_colors, 57 | speed=0.01, 58 | tail_length=20, 59 | bounce=True, 60 | ring=False, 61 | reverse=False, 62 | ) 63 | 64 | while True: 65 | comet.animate() 66 | -------------------------------------------------------------------------------- /examples/led_animation_pacman.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2025 Jose D. Montoya 2 | # SPDX-License-Identifier: MIT 3 | 4 | """ 5 | This example animates a Pacman on a NeoPixel strip. 6 | """ 7 | 8 | import board 9 | import neopixel 10 | 11 | from adafruit_led_animation.animation.pacman import Pacman 12 | from adafruit_led_animation.color import WHITE 13 | 14 | # Update to match the pin connected to your NeoPixels 15 | pixel_pin = board.D6 16 | # Update to match the number of NeoPixels you have connected 17 | num_pixels = 50 18 | 19 | # Create the NeoPixel object 20 | ORDER = neopixel.GRB 21 | pixels = neopixel.NeoPixel( 22 | pixel_pin, 23 | num_pixels, 24 | brightness=0.5, 25 | auto_write=False, 26 | pixel_order=ORDER, 27 | ) 28 | 29 | # Create the Pacman animation object 30 | pacman = Pacman(pixels, speed=0.1, color=WHITE) 31 | 32 | # Main loop 33 | while True: 34 | pacman.animate() 35 | -------------------------------------------------------------------------------- /examples/led_animation_pixel_map.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries 2 | # SPDX-License-Identifier: MIT 3 | 4 | """ 5 | This example shows usage of the PixelMap helper to easily treat a single strip as a horizontal or 6 | vertical grid for animation purposes. 7 | 8 | For NeoPixel FeatherWing. Update pixel_pin and pixel_num to match your wiring if using 9 | a different form of NeoPixels. Note that if you are using a number of pixels other than 32, you 10 | will need to alter the PixelMap values as well for this example to work. 11 | 12 | This example does not work on SAMD21 (M0) boards. 13 | """ 14 | 15 | import board 16 | import neopixel 17 | 18 | from adafruit_led_animation import helper 19 | from adafruit_led_animation.animation.chase import Chase 20 | from adafruit_led_animation.animation.comet import Comet 21 | from adafruit_led_animation.animation.rainbow import Rainbow 22 | from adafruit_led_animation.animation.rainbowchase import RainbowChase 23 | from adafruit_led_animation.animation.rainbowcomet import RainbowComet 24 | from adafruit_led_animation.color import AMBER, JADE, PURPLE 25 | from adafruit_led_animation.sequence import AnimationSequence 26 | 27 | # Update to match the pin connected to your NeoPixels 28 | pixel_pin = board.D6 29 | # Update to match the number of NeoPixels you have connected 30 | pixel_num = 32 31 | 32 | pixels = neopixel.NeoPixel(pixel_pin, pixel_num, brightness=0.5, auto_write=False) 33 | 34 | pixel_wing_vertical = helper.PixelMap.vertical_lines( 35 | pixels, 8, 4, helper.horizontal_strip_gridmap(8, alternating=False) 36 | ) 37 | pixel_wing_horizontal = helper.PixelMap.horizontal_lines( 38 | pixels, 8, 4, helper.horizontal_strip_gridmap(8, alternating=False) 39 | ) 40 | 41 | comet_h = Comet(pixel_wing_horizontal, speed=0.1, color=PURPLE, tail_length=3, bounce=True) 42 | comet_v = Comet(pixel_wing_vertical, speed=0.1, color=AMBER, tail_length=6, bounce=True) 43 | chase_h = Chase(pixel_wing_horizontal, speed=0.1, size=3, spacing=6, color=JADE) 44 | rainbow_chase_v = RainbowChase(pixel_wing_vertical, speed=0.1, size=3, spacing=2, step=8) 45 | rainbow_comet_v = RainbowComet(pixel_wing_vertical, speed=0.1, tail_length=7, bounce=True) 46 | rainbow_v = Rainbow(pixel_wing_vertical, speed=0.1, period=2) 47 | rainbow_chase_h = RainbowChase(pixel_wing_horizontal, speed=0.1, size=3, spacing=3) 48 | 49 | animations = AnimationSequence( 50 | rainbow_v, 51 | comet_h, 52 | rainbow_comet_v, 53 | chase_h, 54 | rainbow_chase_v, 55 | comet_v, 56 | rainbow_chase_h, 57 | advance_interval=5, 58 | ) 59 | 60 | while True: 61 | animations.animate() 62 | -------------------------------------------------------------------------------- /examples/led_animation_rainbow_animations.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries 2 | # SPDX-License-Identifier: MIT 3 | 4 | """ 5 | This example uses AnimationsSequence to display multiple animations in sequence, at a five second 6 | interval. 7 | 8 | For NeoPixel FeatherWing. Update pixel_pin and pixel_num to match your wiring if using 9 | a different form of NeoPixels. 10 | 11 | This example does not work on SAMD21 (M0) boards. 12 | """ 13 | 14 | import board 15 | import neopixel 16 | 17 | from adafruit_led_animation.animation.rainbow import Rainbow 18 | from adafruit_led_animation.animation.rainbowchase import RainbowChase 19 | from adafruit_led_animation.animation.rainbowcomet import RainbowComet 20 | from adafruit_led_animation.animation.rainbowsparkle import RainbowSparkle 21 | from adafruit_led_animation.sequence import AnimationSequence 22 | 23 | # Update to match the pin connected to your NeoPixels 24 | pixel_pin = board.D6 25 | # Update to match the number of NeoPixels you have connected 26 | pixel_num = 32 27 | 28 | pixels = neopixel.NeoPixel(pixel_pin, pixel_num, brightness=0.5, auto_write=False) 29 | 30 | rainbow = Rainbow(pixels, speed=0.1, period=2) 31 | rainbow_chase = RainbowChase(pixels, speed=0.1, size=5, spacing=3) 32 | rainbow_comet = RainbowComet(pixels, speed=0.1, tail_length=7, bounce=True) 33 | rainbow_sparkle = RainbowSparkle(pixels, speed=0.1, num_sparkles=15) 34 | 35 | 36 | animations = AnimationSequence( 37 | rainbow, 38 | rainbow_chase, 39 | rainbow_comet, 40 | rainbow_sparkle, 41 | advance_interval=5, 42 | auto_clear=True, 43 | ) 44 | 45 | while True: 46 | animations.animate() 47 | -------------------------------------------------------------------------------- /examples/led_animation_resume_animation.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Alec Delaney 2 | # SPDX-License-Identifier: MIT 3 | 4 | """ 5 | This example uses Pulse animation along with a connected push button to start 6 | the animation when pressed 7 | 8 | For NeoPixel FeatherWing. Update pixel_pin and pixel_num to match your wiring if using 9 | a different form of NeoPixels. 10 | """ 11 | 12 | import board 13 | import neopixel 14 | from digitalio import DigitalInOut, Direction, Pull 15 | 16 | from adafruit_led_animation.animation.pulse import Pulse 17 | from adafruit_led_animation.color import RED 18 | 19 | # Update to match the pin connected to your NeoPixels 20 | pixel_pin = board.D6 21 | # Update to match the number of NeoPixels you have connected 22 | pixel_num = 32 23 | 24 | # Update to matchpin connected to button that connect logic high when pushed 25 | button_pin = board.D3 26 | 27 | pixels = neopixel.NeoPixel(pixel_pin, pixel_num, brightness=0.5, auto_write=False) 28 | button = DigitalInOut(button_pin) 29 | button.direction = Direction.INPUT 30 | button.pull = Pull.UP 31 | 32 | # Create the animation and freeze it afterwards 33 | pulse_animation = Pulse(pixels, speed=0.1, period=1, color=RED) 34 | pulse_animation.freeze() 35 | 36 | while True: 37 | pulse_animation.animate() 38 | 39 | # Pressing the button resumes (or in this case starts) the animation permanently 40 | if not button.value: 41 | pulse_animation.resume() 42 | -------------------------------------------------------------------------------- /examples/led_animation_samd21_reset_interval.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries 2 | # SPDX-License-Identifier: MIT 3 | 4 | """ 5 | This example shows how to reset the microcontroller to avoid the animation slowing down over time 6 | due to the limitations of CircuitPython for the SAMD21 (M0) microcontroller. The example 7 | animates a purple comet that bounces from end to end of the strip, and resets the board if the 8 | specified amount of time has passed since the board was last reset. 9 | 10 | See this FAQ for details: 11 | https://learn.adafruit.com/circuitpython-led-animations/faqs#on-the-samd21-non-express-board-why-does-my-animation-slow-down-if-i-leave-it-running-for-a-while-3074335-3 12 | 13 | For QT Py Haxpress and a NeoPixel strip. Update pixel_pin and pixel_num to match your wiring if 14 | using a different board or form of NeoPixels. 15 | 16 | This example will run on SAMD21 (M0) Express boards (such as Circuit Playground Express or QT Py 17 | Haxpress), but not on SAMD21 non-Express boards (such as QT Py or Trinket). 18 | """ 19 | 20 | import time 21 | 22 | import board 23 | import microcontroller 24 | import neopixel 25 | 26 | from adafruit_led_animation.animation.comet import Comet 27 | from adafruit_led_animation.color import PURPLE 28 | 29 | # Update to match the pin connected to your NeoPixels 30 | pixel_pin = board.A3 31 | # Update to match the number of NeoPixels you have connected 32 | pixel_num = 30 33 | 34 | pixels = neopixel.NeoPixel(pixel_pin, pixel_num, brightness=0.5, auto_write=False) 35 | 36 | comet = Comet(pixels, speed=0.02, color=PURPLE, tail_length=10, bounce=True) 37 | 38 | while True: 39 | comet.animate() 40 | 41 | if time.monotonic() > 3600: # After an hour passes, reset the board. 42 | microcontroller.reset() 43 | -------------------------------------------------------------------------------- /examples/led_animation_sequence.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries 2 | # SPDX-License-Identifier: MIT 3 | 4 | """ 5 | This example uses AnimationsSequence to display multiple animations in sequence, at a five second 6 | interval. 7 | 8 | For NeoPixel FeatherWing. Update pixel_pin and pixel_num to match your wiring if using 9 | a different form of NeoPixels. 10 | """ 11 | 12 | import board 13 | import neopixel 14 | 15 | from adafruit_led_animation.animation.blink import Blink 16 | from adafruit_led_animation.animation.chase import Chase 17 | from adafruit_led_animation.animation.comet import Comet 18 | from adafruit_led_animation.color import AMBER, JADE, PURPLE 19 | from adafruit_led_animation.sequence import AnimationSequence 20 | 21 | # Update to match the pin connected to your NeoPixels 22 | pixel_pin = board.D6 23 | # Update to match the number of NeoPixels you have connected 24 | pixel_num = 32 25 | 26 | pixels = neopixel.NeoPixel(pixel_pin, pixel_num, brightness=0.5, auto_write=False) 27 | 28 | blink = Blink(pixels, speed=0.5, color=JADE) 29 | comet = Comet(pixels, speed=0.01, color=PURPLE, tail_length=10, bounce=True) 30 | chase = Chase(pixels, speed=0.1, size=3, spacing=6, color=AMBER) 31 | 32 | 33 | animations = AnimationSequence(blink, comet, chase, advance_interval=3, auto_clear=True) 34 | 35 | while True: 36 | animations.animate() 37 | -------------------------------------------------------------------------------- /examples/led_animation_simpletest.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries 2 | # SPDX-License-Identifier: MIT 3 | 4 | """ 5 | This simpletest example displays the Blink animation. 6 | 7 | For NeoPixel FeatherWing. Update pixel_pin and pixel_num to match your wiring if using 8 | a different form of NeoPixels. 9 | """ 10 | 11 | import board 12 | import neopixel 13 | 14 | from adafruit_led_animation.animation.blink import Blink 15 | from adafruit_led_animation.color import RED 16 | 17 | # Update to match the pin connected to your NeoPixels 18 | pixel_pin = board.D6 19 | # Update to match the number of NeoPixels you have connected 20 | pixel_num = 32 21 | 22 | pixels = neopixel.NeoPixel(pixel_pin, pixel_num, brightness=0.5, auto_write=False) 23 | 24 | blink = Blink(pixels, speed=0.5, color=RED) 25 | 26 | while True: 27 | blink.animate() 28 | -------------------------------------------------------------------------------- /examples/led_animation_sparkle_animations.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries 2 | # SPDX-License-Identifier: MIT 3 | 4 | """ 5 | This example uses AnimationsSequence to display multiple animations in sequence, at a five second 6 | interval. 7 | 8 | For NeoPixel FeatherWing. Update pixel_pin and pixel_num to match your wiring if using 9 | a different form of NeoPixels. 10 | """ 11 | 12 | import board 13 | import neopixel 14 | 15 | from adafruit_led_animation.animation.sparkle import Sparkle 16 | from adafruit_led_animation.animation.sparklepulse import SparklePulse 17 | from adafruit_led_animation.color import AMBER, JADE 18 | from adafruit_led_animation.sequence import AnimationSequence 19 | 20 | # Update to match the pin connected to your NeoPixels 21 | pixel_pin = board.D6 22 | # Update to match the number of NeoPixels you have connected 23 | pixel_num = 32 24 | 25 | pixels = neopixel.NeoPixel(pixel_pin, pixel_num, brightness=0.5, auto_write=False) 26 | 27 | sparkle = Sparkle(pixels, speed=0.05, color=AMBER, num_sparkles=10) 28 | sparkle_pulse = SparklePulse(pixels, speed=0.05, period=3, color=JADE) 29 | 30 | animations = AnimationSequence( 31 | sparkle, 32 | sparkle_pulse, 33 | advance_interval=5, 34 | auto_clear=True, 35 | ) 36 | 37 | while True: 38 | animations.animate() 39 | -------------------------------------------------------------------------------- /examples/led_animation_sparkle_mask.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries, karan bhatia 2 | # SPDX-License-Identifier: MIT 3 | """ 4 | This example uses AnimationsSequence to display multiple animations in sequence, at a five second 5 | interval. 6 | 7 | For NeoPixel FeatherWing. Update pixel_pin and pixel_num to match your wiring if using 8 | a different form of NeoPixels. 9 | """ 10 | 11 | import board 12 | import neopixel 13 | 14 | from adafruit_led_animation.animation.sparkle import Sparkle 15 | from adafruit_led_animation.color import AQUA, JADE, PINK 16 | from adafruit_led_animation.sequence import AnimationSequence 17 | 18 | # Update to match the pin connected to your NeoPixels 19 | pixel_pin = board.A1 20 | # Update to match the number of NeoPixels you have connected 21 | pixel_num = 64 22 | # fmt: off 23 | heart_mask = [ 1, 2, 5, 6, 24 | 8, 9, 10, 11, 12, 13, 14, 15, 25 | 16, 17, 18, 19, 20, 21, 22, 23, 26 | 24, 25, 26, 27, 28, 29, 30, 31, 27 | 33, 34, 35, 36, 37, 38, 28 | 42, 43, 44, 45, 29 | 51, 52] 30 | unheart_mask = [0, 3, 4, 7, 31 | 32 | 33 | 34 | 32, 39, 35 | 40, 41, 46, 47, 36 | 48, 49, 50, 53, 54, 55, 37 | 56, 57, 58, 59, 60, 61, 62, 63] 38 | # fmt: on 39 | pixels = neopixel.NeoPixel(pixel_pin, pixel_num, brightness=0.9, auto_write=False) 40 | 41 | animations = AnimationSequence( 42 | Sparkle(pixels, speed=0.05, color=JADE, num_sparkles=1, mask=unheart_mask), 43 | Sparkle(pixels, speed=0.05, color=AQUA, num_sparkles=1), 44 | Sparkle(pixels, speed=0.05, color=PINK, num_sparkles=1, mask=heart_mask), 45 | advance_interval=5, 46 | auto_clear=False, 47 | ) 48 | 49 | while True: 50 | animations.animate() 51 | -------------------------------------------------------------------------------- /examples/led_animation_timedsequence.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 Gamblor21 2 | # 3 | # SPDX-License-Identifier: MIT 4 | """ 5 | Example for TimedSequence 6 | """ 7 | 8 | import board 9 | import neopixel 10 | 11 | import adafruit_led_animation.animation.blink as blink_animation 12 | import adafruit_led_animation.animation.comet as comet_animation 13 | import adafruit_led_animation.animation.sparkle as sparkle_animation 14 | from adafruit_led_animation import color 15 | from adafruit_led_animation.timedsequence import TimedAnimationSequence 16 | 17 | strip_pixels = neopixel.NeoPixel(board.D6, 32, brightness=0.1, auto_write=False) 18 | blink = blink_animation.Blink(strip_pixels, 0.3, color.RED) 19 | comet = comet_animation.Comet(strip_pixels, 0.1, color.BLUE) 20 | sparkle = sparkle_animation.Sparkle(strip_pixels, 0.05, color.GREEN) 21 | animations = TimedAnimationSequence(blink, 2, comet, 4, sparkle, 5) 22 | while True: 23 | animations.animate() 24 | -------------------------------------------------------------------------------- /examples/led_animation_volume.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2023 Tim Cocks 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | """Volume Animation Example""" 6 | 7 | import board 8 | import neopixel 9 | from audiomp3 import MP3Decoder 10 | 11 | from adafruit_led_animation.animation import volume 12 | 13 | try: 14 | from audioio import AudioOut 15 | except ImportError: 16 | try: 17 | from audiopwmio import PWMAudioOut as AudioOut 18 | except ImportError: 19 | pass # not always supported by every board! 20 | 21 | # Fill in your own MP3 file or use the one from the learn guide: 22 | # https://learn.adafruit.com/circuitpython-essentials/circuitpython-mp3-audio#installing-project-code-3067700 23 | mp3file = "happy.mp3" 24 | with open(mp3file, "rb") as mp3: 25 | decoder = MP3Decoder(mp3) 26 | audio = AudioOut(board.SPEAKER) 27 | 28 | strip_pixels = neopixel.NeoPixel(board.D4, 30, brightness=0.1, auto_write=False) 29 | volume_anim = volume.Volume(strip_pixels, 0.3, (0, 255, 0), decoder, 400) 30 | 31 | while True: 32 | audio.play(decoder) 33 | print("playing", mp3file) 34 | 35 | while audio.playing: 36 | volume_anim.animate() 37 | -------------------------------------------------------------------------------- /optional_requirements.txt: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2022 Alec Delaney, for Adafruit Industries 2 | # 3 | # SPDX-License-Identifier: Unlicense 4 | -------------------------------------------------------------------------------- /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-led-animation" 14 | description = "CircuitPython helper for LED colors and animations." 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_LED_Animation"} 21 | keywords = [ 22 | "adafruit", 23 | "blinka", 24 | "circuitpython", 25 | "micropython", 26 | "led", 27 | "animation", 28 | "led", 29 | "colors", 30 | "animations", 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 | packages = ["adafruit_led_animation"] 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 | -------------------------------------------------------------------------------- /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 | "PLR6301", # could-be-static no-self-use 103 | "PLC0415", # import outside toplevel 104 | "PLC2701", # private import 105 | ] 106 | 107 | [format] 108 | line-ending = "lf" 109 | --------------------------------------------------------------------------------