diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..21c125c --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: 2024 Justin Myers for Adafruit Industries +# +# SPDX-License-Identifier: Unlicense + +.py text eol=lf +.rst text eol=lf +.txt text eol=lf +.yaml text eol=lf +.toml text eol=lf +.license text eol=lf +.md text eol=lf diff --git a/.github/PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md b/.github/PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md new file mode 100644 index 0000000..8de294e --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/adafruit_circuitpython_pr.md @@ -0,0 +1,13 @@ +# SPDX-FileCopyrightText: 2021 Adafruit Industries +# +# SPDX-License-Identifier: MIT + +Thank you for contributing! Before you submit a pull request, please read the following. + +Make sure any changes you're submitting are in line with the CircuitPython Design Guide, available here: https://docs.circuitpython.org/en/latest/docs/design_guide.html + +If your changes are to documentation, please verify that the documentation builds locally by following the steps found here: https://adafru.it/build-docs + +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 + +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. diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..041a337 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +name: Build CI + +on: [pull_request, push] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Run Build CI workflow + uses: adafruit/workflows-circuitpython-libs/build@main diff --git a/.github/workflows/failure-help-text.yml b/.github/workflows/failure-help-text.yml new file mode 100644 index 0000000..0b1194f --- /dev/null +++ b/.github/workflows/failure-help-text.yml @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: 2021 Scott Shawcroft for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +name: Failure help text + +on: + workflow_run: + workflows: ["Build CI"] + types: + - completed + +jobs: + post-help: + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'failure' && github.event.workflow_run.event == 'pull_request' }} + steps: + - name: Post comment to help + uses: adafruit/circuitpython-action-library-ci-failed@v1 diff --git a/.github/workflows/release_gh.yml b/.github/workflows/release_gh.yml new file mode 100644 index 0000000..9acec60 --- /dev/null +++ b/.github/workflows/release_gh.yml @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +name: GitHub Release Actions + +on: + release: + types: [published] + +jobs: + upload-release-assets: + runs-on: ubuntu-latest + steps: + - name: Run GitHub Release CI workflow + uses: adafruit/workflows-circuitpython-libs/release-gh@main + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + upload-url: ${{ github.event.release.upload_url }} diff --git a/.github/workflows/release_pypi.yml b/.github/workflows/release_pypi.yml new file mode 100644 index 0000000..65775b7 --- /dev/null +++ b/.github/workflows/release_pypi.yml @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +name: PyPI Release Actions + +on: + release: + types: [published] + +jobs: + upload-release-assets: + runs-on: ubuntu-latest + steps: + - name: Run PyPI Release CI workflow + uses: adafruit/workflows-circuitpython-libs/release-pypi@main + with: + pypi-username: ${{ secrets.pypi_username }} + pypi-password: ${{ secrets.pypi_password }} diff --git a/.gitignore b/.gitignore index 0dd8629..db3d538 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,48 @@ +# SPDX-FileCopyrightText: 2022 Kattni Rembor, written for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +# Do not include files and directories created by your personal work environment, such as the IDE +# you use, except for those already listed here. Pull requests including changes to this file will +# not be accepted. + +# This .gitignore file contains rules for files generated by working with CircuitPython libraries, +# including building Sphinx, testing with pip, and creating a virual environment, as well as the +# MacOS and IDE-specific files generated by using MacOS in general, or the PyCharm or VSCode IDEs. + +# If you find that there are files being generated on your machine that should not be included in +# your git commit, you should create a .gitignore_global file on your computer to include the +# files created by your personal setup. To do so, follow the two steps below. + +# First, create a file called .gitignore_global somewhere convenient for you, and add rules for +# the files you want to exclude from git commits. + +# Second, configure Git to use the exclude file for all Git repositories by running the +# following via commandline, replacing "path/to/your/" with the actual path to your newly created +# .gitignore_global file: +# git config --global core.excludesfile path/to/your/.gitignore_global + +# CircuitPython-specific files +*.mpy + +# Python-specific files __pycache__ -_build *.pyc + +# Sphinx build-specific files +_build + +# This file results from running `pip -e install .` in a local repository +*.egg-info + +# Virtual environment-specific files .env -build* -bundles +.venv + +# MacOS-specific files +*.DS_Store + +# IDE-specific files +.idea +.vscode +*~ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..ff19dde --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: 2024 Justin Myers for Adafruit Industries +# +# SPDX-License-Identifier: Unlicense + +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: check-yaml + - id: end-of-file-fixer + - id: trailing-whitespace + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.3.4 + hooks: + - id: ruff-format + - id: ruff + args: ["--fix"] + - repo: https://github.com/fsfe/reuse-tool + rev: v3.0.1 + hooks: + - id: reuse diff --git a/.pylintrc b/.pylintrc deleted file mode 100644 index 039eaec..0000000 --- a/.pylintrc +++ /dev/null @@ -1,433 +0,0 @@ -[MASTER] - -# A comma-separated list of package or module names from where C extensions may -# be loaded. Extensions are loading into the active Python interpreter and may -# run arbitrary code -extension-pkg-whitelist= - -# Add files or directories to the blacklist. They should be base names, not -# paths. -ignore=CVS - -# Add files or directories matching the regex patterns to the blacklist. The -# regex matches against base names, not paths. -ignore-patterns= - -# Python code to execute, usually for sys.path manipulation such as -# pygtk.require(). -#init-hook= - -# Use multiple processes to speed up Pylint. -# jobs=1 -jobs=2 - -# List of plugins (as comma separated values of python modules names) to load, -# usually to register additional checkers. -load-plugins= - -# Pickle collected data for later comparisons. -persistent=yes - -# Specify a configuration file. -#rcfile= - -# Allow loading of arbitrary C extensions. Extensions are imported into the -# active Python interpreter and may run arbitrary code. -unsafe-load-any-extension=no - - -[MESSAGES CONTROL] - -# Only show warnings with the listed confidence levels. Leave empty to show -# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED -confidence= - -# Disable the message, report, category or checker with the given id(s). You -# can either give multiple identifiers separated by comma (,) or put this -# option multiple times (only on the command line, not in the configuration -# file where it should appear only once).You can also use "--disable=all" to -# disable everything first and then reenable specific checks. For example, if -# you want to run only the similarities checker, you can use "--disable=all -# --enable=similarities". If you want to run only the classes checker, but have -# no Warning level messages displayed, use"--disable=all --enable=classes -# --disable=W" -# disable=import-error,print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call -disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call,import-error - -# Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifier separated by comma (,) or put this option -# multiple time (only on the command line, not in the configuration file where -# it should appear only once). See also the "--disable" option for examples. -enable= - - -[REPORTS] - -# Python expression which should return a note less than 10 (10 is the highest -# note). You have access to the variables errors warning, statement which -# respectively contain the number of errors / warnings messages and the total -# number of statements analyzed. This is used by the global evaluation report -# (RP0004). -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) - -# Template used to display messages. This is a python new-style format string -# used to format the message information. See doc for all details -#msg-template= - -# Set the output format. Available formats are text, parseable, colorized, json -# and msvs (visual studio).You can also give a reporter class, eg -# mypackage.mymodule.MyReporterClass. -output-format=text - -# Tells whether to display a full report or only the messages -reports=no - -# Activate the evaluation score. -score=yes - - -[REFACTORING] - -# Maximum number of nested blocks for function / method body -max-nested-blocks=5 - - -[LOGGING] - -# Logging modules to check that the string format arguments are in logging -# function parameter format -logging-modules=logging - - -[SPELLING] - -# Spelling dictionary name. Available dictionaries: none. To make it working -# install python-enchant package. -spelling-dict= - -# List of comma separated words that should not be checked. -spelling-ignore-words= - -# A path to a file that contains private dictionary; one word per line. -spelling-private-dict-file= - -# Tells whether to store unknown words to indicated private dictionary in -# --spelling-private-dict-file option instead of raising a message. -spelling-store-unknown-words=no - - -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -# notes=FIXME,XXX,TODO -notes=FIXME,XXX - - -[TYPECHECK] - -# List of decorators that produce context managers, such as -# contextlib.contextmanager. Add to this list to register other decorators that -# produce valid context managers. -contextmanager-decorators=contextlib.contextmanager - -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E1101 when accessed. Python regular -# expressions are accepted. -generated-members= - -# Tells whether missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignore-mixin-members=yes - -# This flag controls whether pylint should warn about no-member and similar -# checks whenever an opaque object is returned when inferring. The inference -# can return multiple potential results while evaluating a Python object, but -# some branches might not be evaluated, which results in partial inference. In -# that case, it might be useful to still emit no-member and other checks for -# the rest of the inferred objects. -ignore-on-opaque-inference=yes - -# List of class names for which member attributes should not be checked (useful -# for classes with dynamically set attributes). This supports the use of -# qualified names. -ignored-classes=optparse.Values,thread._local,_thread._local - -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis. It -# supports qualified module names, as well as Unix pattern matching. -ignored-modules=board - -# Show a hint with possible names when a member name was not found. The aspect -# of finding the hint is based on edit distance. -missing-member-hint=yes - -# The minimum edit distance a name should have in order to be considered a -# similar match for a missing member name. -missing-member-hint-distance=1 - -# The total number of similar names that should be taken in consideration when -# showing a hint for a missing member. -missing-member-max-choices=1 - - -[VARIABLES] - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid to define new builtins when possible. -additional-builtins= - -# Tells whether unused global variables should be treated as a violation. -allow-global-unused-variables=yes - -# List of strings which can identify a callback function by name. A callback -# name must start or end with one of those strings. -callbacks=cb_,_cb - -# A regular expression matching the name of dummy variables (i.e. expectedly -# not used). -dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ - -# Argument names that match this expression will be ignored. Default to name -# with leading underscore -ignored-argument-names=_.*|^ignored_|^unused_ - -# Tells whether we should check for unused import in __init__ files. -init-import=no - -# List of qualified module names which can have objects that can redefine -# builtins. -redefining-builtins-modules=six.moves,future.builtins - - -[FORMAT] - -# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. -# expected-line-ending-format= -expected-line-ending-format=LF - -# Regexp for a line that is allowed to be longer than the limit. -ignore-long-lines=^\s*(# )??$ - -# Number of spaces of indent required inside a hanging or continued line. -indent-after-paren=4 - -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string=' ' - -# Maximum number of characters on a single line. -max-line-length=100 - -# Maximum number of lines in a module -max-module-lines=1000 - -# List of optional constructs for which whitespace checking is disabled. `dict- -# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. -# `trailing-comma` allows a space between comma and closing bracket: (a, ). -# `empty-line` allows space-only lines. -no-space-check=trailing-comma,dict-separator - -# Allow the body of a class to be on the same line as the declaration if body -# contains single statement. -single-line-class-stmt=no - -# Allow the body of an if to be on the same line as the test if there is no -# else. -single-line-if-stmt=no - - -[SIMILARITIES] - -# Ignore comments when computing similarities. -ignore-comments=yes - -# Ignore docstrings when computing similarities. -ignore-docstrings=yes - -# Ignore imports when computing similarities. -ignore-imports=no - -# Minimum lines number of a similarity. -min-similarity-lines=4 - - -[BASIC] - -# Naming hint for argument names -argument-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Regular expression matching correct argument names -argument-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Naming hint for attribute names -attr-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Regular expression matching correct attribute names -attr-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Bad variable names which should always be refused, separated by a comma -bad-names=foo,bar,baz,toto,tutu,tata - -# Naming hint for class attribute names -class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ - -# Regular expression matching correct class attribute names -class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ - -# Naming hint for class names -# class-name-hint=[A-Z_][a-zA-Z0-9]+$ -class-name-hint=[A-Z_][a-zA-Z0-9_]+$ - -# Regular expression matching correct class names -# class-rgx=[A-Z_][a-zA-Z0-9]+$ -class-rgx=[A-Z_][a-zA-Z0-9_]+$ - -# Naming hint for constant names -const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ - -# Regular expression matching correct constant names -const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ - -# Minimum line length for functions/classes that require docstrings, shorter -# ones are exempt. -docstring-min-length=-1 - -# Naming hint for function names -function-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Regular expression matching correct function names -function-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Good variable names which should always be accepted, separated by a comma -# good-names=i,j,k,ex,Run,_ -good-names=r,g,b,w,i,j,k,n,x,y,z,ex,ok,Run,_ - -# Include a hint for the correct naming format with invalid-name -include-naming-hint=no - -# Naming hint for inline iteration names -inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ - -# Regular expression matching correct inline iteration names -inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ - -# Naming hint for method names -method-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Regular expression matching correct method names -method-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Naming hint for module names -module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ - -# Regular expression matching correct module names -module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ - -# Colon-delimited sets of names that determine each other's naming style when -# the name regexes allow several styles. -name-group= - -# Regular expression which should only match function or class names that do -# not require a docstring. -no-docstring-rgx=^_ - -# List of decorators that produce properties, such as abc.abstractproperty. Add -# to this list to register other decorators that produce valid properties. -property-classes=abc.abstractproperty - -# Naming hint for variable names -variable-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Regular expression matching correct variable names -variable-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - - -[IMPORTS] - -# Allow wildcard imports from modules that define __all__. -allow-wildcard-with-all=no - -# Analyse import fallback blocks. This can be used to support both Python 2 and -# 3 compatible code, which means that the block might have code that exists -# only in one or another interpreter, leading to false positives when analysed. -analyse-fallback-blocks=no - -# Deprecated modules which should not be used, separated by a comma -deprecated-modules=optparse,tkinter.tix - -# Create a graph of external dependencies in the given file (report RP0402 must -# not be disabled) -ext-import-graph= - -# Create a graph of every (i.e. internal and external) dependencies in the -# given file (report RP0402 must not be disabled) -import-graph= - -# Create a graph of internal dependencies in the given file (report RP0402 must -# not be disabled) -int-import-graph= - -# Force import order to recognize a module as part of the standard -# compatibility libraries. -known-standard-library= - -# Force import order to recognize a module as part of a third party library. -known-third-party=enchant - - -[CLASSES] - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__,__new__,setUp - -# List of member names, which should be excluded from the protected access -# warning. -exclude-protected=_asdict,_fields,_replace,_source,_make - -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls - -# List of valid names for the first argument in a metaclass class method. -valid-metaclass-classmethod-first-arg=mcs - - -[DESIGN] - -# Maximum number of arguments for function / method -max-args=5 - -# Maximum number of attributes for a class (see R0902). -# max-attributes=7 -max-attributes=11 - -# Maximum number of boolean expressions in a if statement -max-bool-expr=5 - -# Maximum number of branch for function / method body -max-branches=12 - -# Maximum number of locals for function / method body -max-locals=15 - -# Maximum number of parents for a class (see R0901). -max-parents=7 - -# Maximum number of public methods for a class (see R0904). -max-public-methods=20 - -# Maximum number of return / yield for function / method body -max-returns=6 - -# Maximum number of statements in function / method body -max-statements=50 - -# Minimum number of public methods for a class (see R0903). -min-public-methods=1 - - -[EXCEPTIONS] - -# Exceptions that will emit a warning when being caught. Defaults to -# "Exception" -overgeneral-exceptions=Exception diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..88bca9f --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,22 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# +# SPDX-License-Identifier: Unlicense + +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +sphinx: + configuration: docs/conf.py + +build: + os: ubuntu-20.04 + tools: + python: "3" + +python: + install: + - requirements: docs/requirements.txt + - requirements: requirements.txt diff --git a/.readthedocs.yml b/.readthedocs.yml deleted file mode 100644 index f4243ad..0000000 --- a/.readthedocs.yml +++ /dev/null @@ -1,3 +0,0 @@ -python: - version: 3 -requirements_file: requirements.txt diff --git a/.travis.yml b/.travis.yml deleted file mode 100755 index 093c6bf..0000000 --- a/.travis.yml +++ /dev/null @@ -1,32 +0,0 @@ -dist: trusty -sudo: false -language: python -python: -- '3.6' -cache: - pip: true -deploy: -- provider: releases - api_key: "$GITHUB_TOKEN" - file_glob: true - file: "$TRAVIS_BUILD_DIR/bundles/*" - skip_cleanup: true - overwrite: true - on: - tags: true -- provider: pypi - user: adafruit-travis - on: - tags: true - password: - secure: oqlm4dJIzX2JH8Qbd+qU0g+M+1NSHD0f9pWQxvdhl+JlCtzRmBtyv6t24EN+SUGD3eKOK9A21/+QxsQJ2sB8/g8qh07OUNKw+erv9NqA2Wc0oHvOpwpN+TkS6hF0GI/Tn53hwm+qiTsXmBfbDoXDyu6Qe9rsht7Llv1wbHErCk7vkZ9zEL0zfhkgV5hzXPoRdwQclLeyleoJMyw0ZdfPqupMCGGAMPd6aTIYQfJ1PVmzYu9VqtK7yEB9lltc6U16dwp2huN2HBz5EzlRWszCli8mx1+MuZxNFlYkMo8vgWpSQmJpw22nSzI8ez57QibNGEm9u4Y9nb7PlzH9ZOeTVI7OZ9k3BPSN0db8WKGLz9VJxyTilqPNs737TCf36tdshPnuEtEDwk8YNReyK5uE5TnjnnXL2g3qSyhIniKLusY5d9cF3PEhwG6DcNqt39NrT6rG9PaeURYzKY19BgwhWD2CucEM6RDBRp+31citzM35NjET+5+xSOmDXdCdr+Ar+AWNxyfGn0N+Za/de8JyFjuk5TN4muB61eJPXPpinn8+egNyeiONPT96vJDVvFrEdG3bIEspYd6ZAMQ8Aou1Ii+CxOEXiY+57AK20LAieX8Nv2sTLLHo5wa7c4xghHVZdN187cesqm0LZtNYGhs/CeMP+9bC3bn7/IHGdcRZvg4= -install: -- pip install -r requirements.txt -- pip install pylint circuitpython-build-tools Sphinx sphinx-rtd-theme -- pip install --force-reinstall pylint==1.9.2 -script: -- pylint adafruit_seesaw/*.py -- ([[ ! -d "examples" ]] || pylint --disable=missing-docstring,invalid-name examples/*.py) -- circuitpython-build-bundles --filename_prefix adafruit-circuitpython-seesaw --library_location - . -- cd docs && sphinx-build -E -W -b html . _build/html && cd .. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 1617586..8a55c07 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,74 +1,135 @@ -# Contributor Covenant Code of Conduct + + +# Adafruit Community Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and +contributors and leaders pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, gender identity and expression, level of experience, -nationality, personal appearance, race, religion, or sexual identity and -orientation. +size, disability, ethnicity, gender identity and expression, level or type of +experience, education, socio-economic status, nationality, personal appearance, +race, religion, or sexual identity and orientation. ## Our Standards +We are committed to providing a friendly, safe and welcoming environment for +all. + Examples of behavior that contributes to creating a positive environment include: +* Be kind and courteous to others * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences +* Collaborating with other community members * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: -* The use of sexualized language or imagery and unwelcome sexual attention or -advances +* The use of sexualized language or imagery and sexual attention or advances +* The use of inappropriate images, including in a community member's avatar +* The use of inappropriate language, including in a community member's nickname +* Any spamming, flaming, baiting or other attention-stealing behavior +* Excessive or unwelcome helping; answering outside the scope of the question + asked * Trolling, insulting/derogatory comments, and personal or political attacks +* Promoting or spreading disinformation, lies, or conspiracy theories against + a person, group, organisation, project, or community * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting +* Other conduct which could reasonably be considered inappropriate + +The goal of the standards and moderation guidelines outlined here is to build +and maintain a respectful community. We ask that you don’t just aim to be +"technically unimpeachable", but rather try to be your best self. + +We value many things beyond technical expertise, including collaboration and +supporting others within our community. Providing a positive experience for +other community members can have a much more significant impact than simply +providing the correct answer. ## Our Responsibilities -Project maintainers are responsible for clarifying the standards of acceptable +Project leaders are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions +Project leaders have the right and responsibility to remove, edit, or +reject messages, comments, commits, code, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. +permanently any community member for other behaviors that they deem +inappropriate, threatening, offensive, or harmful. -## Scope +## Moderation -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. +Instances of behaviors that violate the Adafruit Community Code of Conduct +may be reported by any member of the community. Community members are +encouraged to report these situations, including situations they witness +involving other community members. + +You may report in the following ways: + +In any situation, you may send an email to . -## Enforcement +On the Adafruit Discord, you may send an open message from any channel +to all Community Moderators by tagging @community moderators. You may +also send an open message from any channel, or a direct message to +@kattni#1507, @tannewt#4653, @Dan Halbert#1614, @cater#2442, +@sommersoft#0222, @Mr. Certainly#0472 or @Andon#8175. -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at support@adafruit.com. All -complaints will be reviewed and investigated and will result in a response that -is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. +Email and direct message reports will be kept confidential. -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. +In situations on Discord where the issue is particularly egregious, possibly +illegal, requires immediate action, or violates the Discord terms of service, +you should also report the message directly to Discord. + +These are the steps for upholding our community’s standards of conduct. + +1. Any member of the community may report any situation that violates the +Adafruit Community Code of Conduct. All reports will be reviewed and +investigated. +2. If the behavior is an egregious violation, the community member who +committed the violation may be banned immediately, without warning. +3. Otherwise, moderators will first respond to such behavior with a warning. +4. Moderators follow a soft "three strikes" policy - the community member may +be given another chance, if they are receptive to the warning and change their +behavior. +5. If the community member is unreceptive or unreasonable when warned by a +moderator, or the warning goes unheeded, they may be banned for a first or +second offense. Repeated offenses will result in the community member being +banned. + +## Scope + +This Code of Conduct and the enforcement policies listed above apply to all +Adafruit Community venues. This includes but is not limited to any community +spaces (both public and private), the entire Adafruit Discord server, and +Adafruit GitHub repositories. Examples of Adafruit Community spaces include +but are not limited to meet-ups, audio chats on the Adafruit Discord, or +interaction at a conference. + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. As a community +member, you are representing our community, and are expected to behave +accordingly. ## Attribution -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at [http://contributor-covenant.org/version/1/4][version] +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 1.4, available at +, +and the [Rust Code of Conduct](https://www.rust-lang.org/en-US/conduct.html). -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ +For other projects adopting the Adafruit Community Code of +Conduct, please contact the maintainers of those projects for enforcement. +If you wish to use this code of conduct for your own project, consider +explicitly mentioning your moderation policy or making a copy with your +own moderation policy so as to avoid confusion. diff --git a/LICENSES/CC-BY-4.0.txt b/LICENSES/CC-BY-4.0.txt new file mode 100644 index 0000000..3f92dfc --- /dev/null +++ b/LICENSES/CC-BY-4.0.txt @@ -0,0 +1,324 @@ +Creative Commons Attribution 4.0 International Creative Commons Corporation +("Creative Commons") is not a law firm and does not provide legal services +or legal advice. Distribution of Creative Commons public licenses does not +create a lawyer-client or other relationship. Creative Commons makes its licenses +and related information available on an "as-is" basis. Creative Commons gives +no warranties regarding its licenses, any material licensed under their terms +and conditions, or any related information. Creative Commons disclaims all +liability for damages resulting from their use to the fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and conditions +that creators and other rights holders may use to share original works of +authorship and other material subject to copyright and certain other rights +specified in the public license below. The following considerations are for +informational purposes only, are not exhaustive, and do not form part of our +licenses. + +Considerations for licensors: Our public licenses are intended for use by +those authorized to give the public permission to use material in ways otherwise +restricted by copyright and certain other rights. Our licenses are irrevocable. +Licensors should read and understand the terms and conditions of the license +they choose before applying it. Licensors should also secure all rights necessary +before applying our licenses so that the public can reuse the material as +expected. Licensors should clearly mark any material not subject to the license. +This includes other CC-licensed material, or material used under an exception +or limitation to copyright. More considerations for licensors : wiki.creativecommons.org/Considerations_for_licensors + +Considerations for the public: By using one of our public licenses, a licensor +grants the public permission to use the licensed material under specified +terms and conditions. If the licensor's permission is not necessary for any +reason–for example, because of any applicable exception or limitation to copyright–then +that use is not regulated by the license. Our licenses grant only permissions +under copyright and certain other rights that a licensor has authority to +grant. Use of the licensed material may still be restricted for other reasons, +including because others have copyright or other rights in the material. A +licensor may make special requests, such as asking that all changes be marked +or described. Although not required by our licenses, you are encouraged to +respect those requests where reasonable. More considerations for the public +: wiki.creativecommons.org/Considerations_for_licensees Creative Commons Attribution +4.0 International Public License + +By exercising the Licensed Rights (defined below), You accept and agree to +be bound by the terms and conditions of this Creative Commons Attribution +4.0 International Public License ("Public License"). To the extent this Public +License may be interpreted as a contract, You are granted the Licensed Rights +in consideration of Your acceptance of these terms and conditions, and the +Licensor grants You such rights in consideration of benefits the Licensor +receives from making the Licensed Material available under these terms and +conditions. + +Section 1 – Definitions. + +a. Adapted Material means material subject to Copyright and Similar Rights +that is derived from or based upon the Licensed Material and in which the +Licensed Material is translated, altered, arranged, transformed, or otherwise +modified in a manner requiring permission under the Copyright and Similar +Rights held by the Licensor. For purposes of this Public License, where the +Licensed Material is a musical work, performance, or sound recording, Adapted +Material is always produced where the Licensed Material is synched in timed +relation with a moving image. + +b. Adapter's License means the license You apply to Your Copyright and Similar +Rights in Your contributions to Adapted Material in accordance with the terms +and conditions of this Public License. + +c. Copyright and Similar Rights means copyright and/or similar rights closely +related to copyright including, without limitation, performance, broadcast, +sound recording, and Sui Generis Database Rights, without regard to how the +rights are labeled or categorized. For purposes of this Public License, the +rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. + +d. Effective Technological Measures means those measures that, in the absence +of proper authority, may not be circumvented under laws fulfilling obligations +under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, +and/or similar international agreements. + +e. Exceptions and Limitations means fair use, fair dealing, and/or any other +exception or limitation to Copyright and Similar Rights that applies to Your +use of the Licensed Material. + +f. Licensed Material means the artistic or literary work, database, or other +material to which the Licensor applied this Public License. + +g. Licensed Rights means the rights granted to You subject to the terms and +conditions of this Public License, which are limited to all Copyright and +Similar Rights that apply to Your use of the Licensed Material and that the +Licensor has authority to license. + +h. Licensor means the individual(s) or entity(ies) granting rights under this +Public License. + +i. Share means to provide material to the public by any means or process that +requires permission under the Licensed Rights, such as reproduction, public +display, public performance, distribution, dissemination, communication, or +importation, and to make material available to the public including in ways +that members of the public may access the material from a place and at a time +individually chosen by them. + +j. Sui Generis Database Rights means rights other than copyright resulting +from Directive 96/9/EC of the European Parliament and of the Council of 11 +March 1996 on the legal protection of databases, as amended and/or succeeded, +as well as other essentially equivalent rights anywhere in the world. + +k. You means the individual or entity exercising the Licensed Rights under +this Public License. Your has a corresponding meaning. + +Section 2 – Scope. + + a. License grant. + +1. Subject to the terms and conditions of this Public License, the Licensor +hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, +irrevocable license to exercise the Licensed Rights in the Licensed Material +to: + + A. reproduce and Share the Licensed Material, in whole or in part; and + + B. produce, reproduce, and Share Adapted Material. + +2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions +and Limitations apply to Your use, this Public License does not apply, and +You do not need to comply with its terms and conditions. + + 3. Term. The term of this Public License is specified in Section 6(a). + +4. Media and formats; technical modifications allowed. The Licensor authorizes +You to exercise the Licensed Rights in all media and formats whether now known +or hereafter created, and to make technical modifications necessary to do +so. The Licensor waives and/or agrees not to assert any right or authority +to forbid You from making technical modifications necessary to exercise the +Licensed Rights, including technical modifications necessary to circumvent +Effective Technological Measures. For purposes of this Public License, simply +making modifications authorized by this Section 2(a)(4) never produces Adapted +Material. + + 5. Downstream recipients. + +A. Offer from the Licensor – Licensed Material. Every recipient of the Licensed +Material automatically receives an offer from the Licensor to exercise the +Licensed Rights under the terms and conditions of this Public License. + +B. No downstream restrictions. You may not offer or impose any additional +or different terms or conditions on, or apply any Effective Technological +Measures to, the Licensed Material if doing so restricts exercise of the Licensed +Rights by any recipient of the Licensed Material. + +6. No endorsement. Nothing in this Public License constitutes or may be construed +as permission to assert or imply that You are, or that Your use of the Licensed +Material is, connected with, or sponsored, endorsed, or granted official status +by, the Licensor or others designated to receive attribution as provided in +Section 3(a)(1)(A)(i). + + b. Other rights. + +1. Moral rights, such as the right of integrity, are not licensed under this +Public License, nor are publicity, privacy, and/or other similar personality +rights; however, to the extent possible, the Licensor waives and/or agrees +not to assert any such rights held by the Licensor to the limited extent necessary +to allow You to exercise the Licensed Rights, but not otherwise. + +2. Patent and trademark rights are not licensed under this Public License. + +3. To the extent possible, the Licensor waives any right to collect royalties +from You for the exercise of the Licensed Rights, whether directly or through +a collecting society under any voluntary or waivable statutory or compulsory +licensing scheme. In all other cases the Licensor expressly reserves any right +to collect such royalties. + +Section 3 – License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the following +conditions. + + a. Attribution. + +1. If You Share the Licensed Material (including in modified form), You must: + +A. retain the following if it is supplied by the Licensor with the Licensed +Material: + +i. identification of the creator(s) of the Licensed Material and any others +designated to receive attribution, in any reasonable manner requested by the +Licensor (including by pseudonym if designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of warranties; + +v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; + +B. indicate if You modified the Licensed Material and retain an indication +of any previous modifications; and + +C. indicate the Licensed Material is licensed under this Public License, and +include the text of, or the URI or hyperlink to, this Public License. + +2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner +based on the medium, means, and context in which You Share the Licensed Material. +For example, it may be reasonable to satisfy the conditions by providing a +URI or hyperlink to a resource that includes the required information. + +3. If requested by the Licensor, You must remove any of the information required +by Section 3(a)(1)(A) to the extent reasonably practicable. + +4. If You Share Adapted Material You produce, the Adapter's License You apply +must not prevent recipients of the Adapted Material from complying with this +Public License. + +Section 4 – Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that apply to +Your use of the Licensed Material: + +a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, +reuse, reproduce, and Share all or a substantial portion of the contents of +the database; + +b. if You include all or a substantial portion of the database contents in +a database in which You have Sui Generis Database Rights, then the database +in which You have Sui Generis Database Rights (but not its individual contents) +is Adapted Material; and + +c. You must comply with the conditions in Section 3(a) if You Share all or +a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not replace +Your obligations under this Public License where the Licensed Rights include +other Copyright and Similar Rights. + +Section 5 – Disclaimer of Warranties and Limitation of Liability. + +a. Unless otherwise separately undertaken by the Licensor, to the extent possible, +the Licensor offers the Licensed Material as-is and as-available, and makes +no representations or warranties of any kind concerning the Licensed Material, +whether express, implied, statutory, or other. This includes, without limitation, +warranties of title, merchantability, fitness for a particular purpose, non-infringement, +absence of latent or other defects, accuracy, or the presence or absence of +errors, whether or not known or discoverable. Where disclaimers of warranties +are not allowed in full or in part, this disclaimer may not apply to You. + +b. To the extent possible, in no event will the Licensor be liable to You +on any legal theory (including, without limitation, negligence) or otherwise +for any direct, special, indirect, incidental, consequential, punitive, exemplary, +or other losses, costs, expenses, or damages arising out of this Public License +or use of the Licensed Material, even if the Licensor has been advised of +the possibility of such losses, costs, expenses, or damages. Where a limitation +of liability is not allowed in full or in part, this limitation may not apply +to You. + +c. The disclaimer of warranties and limitation of liability provided above +shall be interpreted in a manner that, to the extent possible, most closely +approximates an absolute disclaimer and waiver of all liability. + +Section 6 – Term and Termination. + +a. This Public License applies for the term of the Copyright and Similar Rights +licensed here. However, if You fail to comply with this Public License, then +Your rights under this Public License terminate automatically. + +b. Where Your right to use the Licensed Material has terminated under Section +6(a), it reinstates: + +1. automatically as of the date the violation is cured, provided it is cured +within 30 days of Your discovery of the violation; or + + 2. upon express reinstatement by the Licensor. + +c. For the avoidance of doubt, this Section 6(b) does not affect any right +the Licensor may have to seek remedies for Your violations of this Public +License. + +d. For the avoidance of doubt, the Licensor may also offer the Licensed Material +under separate terms or conditions or stop distributing the Licensed Material +at any time; however, doing so will not terminate this Public License. + + e. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. + +Section 7 – Other Terms and Conditions. + +a. The Licensor shall not be bound by any additional or different terms or +conditions communicated by You unless expressly agreed. + +b. Any arrangements, understandings, or agreements regarding the Licensed +Material not stated herein are separate from and independent of the terms +and conditions of this Public License. + +Section 8 – Interpretation. + +a. For the avoidance of doubt, this Public License does not, and shall not +be interpreted to, reduce, limit, restrict, or impose conditions on any use +of the Licensed Material that could lawfully be made without permission under +this Public License. + +b. To the extent possible, if any provision of this Public License is deemed +unenforceable, it shall be automatically reformed to the minimum extent necessary +to make it enforceable. If the provision cannot be reformed, it shall be severed +from this Public License without affecting the enforceability of the remaining +terms and conditions. + +c. No term or condition of this Public License will be waived and no failure +to comply consented to unless expressly agreed to by the Licensor. + +d. Nothing in this Public License constitutes or may be interpreted as a limitation +upon, or waiver of, any privileges and immunities that apply to the Licensor +or You, including from the legal processes of any jurisdiction or authority. + +Creative Commons is not a party to its public licenses. Notwithstanding, Creative +Commons may elect to apply one of its public licenses to material it publishes +and in those instances will be considered the "Licensor." The text of the +Creative Commons public licenses is dedicated to the public domain under the +CC0 Public Domain Dedication. Except for the limited purpose of indicating +that material is shared under a Creative Commons public license or as otherwise +permitted by the Creative Commons policies published at creativecommons.org/policies, +Creative Commons does not authorize the use of the trademark "Creative Commons" +or any other trademark or logo of Creative Commons without its prior written +consent including, without limitation, in connection with any unauthorized +modifications to any of its public licenses or any other arrangements, understandings, +or agreements concerning use of licensed material. For the avoidance of doubt, +this paragraph does not form part of the public licenses. + +Creative Commons may be contacted at creativecommons.org. diff --git a/LICENSES/MIT.txt b/LICENSES/MIT.txt new file mode 100644 index 0000000..204b93d --- /dev/null +++ b/LICENSES/MIT.txt @@ -0,0 +1,19 @@ +MIT License Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice (including the next +paragraph) shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/LICENSES/Unlicense.txt b/LICENSES/Unlicense.txt new file mode 100644 index 0000000..24a8f90 --- /dev/null +++ b/LICENSES/Unlicense.txt @@ -0,0 +1,20 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or distribute +this software, either in source code form or as a compiled binary, for any +purpose, commercial or non-commercial, and by any means. + +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and +to the detriment of our heirs and successors. We intend this dedication to +be an overt act of relinquishment in perpetuity of all present and future +rights to this software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH +THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information, +please refer to diff --git a/README.rst b/README.rst index 145c6d6..2b8c4f7 100644 --- a/README.rst +++ b/README.rst @@ -3,17 +3,21 @@ Introduction ============ .. image:: https://readthedocs.org/projects/adafruit-circuitpython-seesaw/badge/?version=latest - :target: https://circuitpython.readthedocs.io/projects/seesaw/en/latest/ + :target: https://docs.circuitpython.org/projects/seesaw/en/latest/ :alt: Documentation Status -.. image :: https://img.shields.io/discord/327254708534116352.svg - :target: https://discord.gg/nBQh6qu +.. image:: https://raw.githubusercontent.com/adafruit/Adafruit_CircuitPython_Bundle/main/badges/adafruit_discord.svg + :target: https://adafru.it/discord :alt: Discord -.. image:: https://travis-ci.org/adafruit/Adafruit_CircuitPython_seesaw.svg?branch=master - :target: https://travis-ci.org/adafruit/Adafruit_CircuitPython_seesaw +.. image:: https://github.com/adafruit/Adafruit_CircuitPython_seesaw/workflows/Build%20CI/badge.svg + :target: https://github.com/adafruit/Adafruit_CircuitPython_seesaw/actions :alt: Build Status +.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json + :target: https://github.com/astral-sh/ruff + :alt: Code Style: Ruff + CircuitPython module for use with the Adafruit ATSAMD09 seesaw. Dependencies @@ -27,61 +31,46 @@ Please ensure all dependencies are available on the CircuitPython filesystem. This is easily achieved by downloading `the Adafruit library and driver bundle `_. -Usage Example -============= - -See examples/seesaw_simpletest.py for usage example. - -Contributing -============ - -Contributions are welcome! Please read our `Code of Conduct -`_ -before contributing to help this project stay welcoming. - -Building locally -================ +Installing from PyPI +==================== -To build this library locally you'll need to install the -`circuitpython-build-tools `_ package. +On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally `from +PyPI `_. To install for current user: .. code-block:: shell - python3 -m venv .env - source .env/bin/activate - pip install circuitpython-build-tools + pip3 install adafruit-circuitpython-seesaw -Once installed, make sure you are in the virtual environment: +To install system-wide (this may be required in some cases): .. code-block:: shell - source .env/bin/activate + sudo pip3 install adafruit-circuitpython-seesaw -Then run the build: +To install in a virtual environment in your current project: .. code-block:: shell - circuitpython-build-bundles --filename_prefix adafruit-circuitpython-seesaw --library_location . + mkdir project-name && cd project-name + python3 -m venv .venv + source .venv/bin/activate + pip3 install adafruit-circuitpython-seesaw -Sphinx documentation ------------------------ +Usage Example +============= -Sphinx is used to build the documentation based on rST files and comments in the code. First, -install dependencies (feel free to reuse the virtual environment from above): +See examples/seesaw_simpletest.py for usage example. -.. code-block:: shell +Documentation +============= - python3 -m venv .env - source .env/bin/activate - pip install Sphinx sphinx-rtd-theme +API documentation for this library can be found on `Read the Docs `_. -Now, once you have the virtual environment activated: +For information on building library documentation, please check out `this guide `_. -.. code-block:: shell - - cd docs - sphinx-build -E -W -b html . _build/html +Contributing +============ -This will output the documentation to ``docs/_build/html``. Open the index.html in your browser to -view them. It will also (due to -W) error out on any warning like Travis will. This is a good way to -locally verify it will pass. \ No newline at end of file +Contributions are welcome! Please read our `Code of Conduct +`_ +before contributing to help this project stay welcoming. diff --git a/README.rst.license b/README.rst.license new file mode 100644 index 0000000..11cd75d --- /dev/null +++ b/README.rst.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2017 Scott Shawcroft, written for Adafruit Industries + +SPDX-License-Identifier: MIT diff --git a/adafruit_seesaw/analoginput.py b/adafruit_seesaw/analoginput.py index 548eadd..4020079 100644 --- a/adafruit_seesaw/analoginput.py +++ b/adafruit_seesaw/analoginput.py @@ -1,41 +1,39 @@ -# The MIT License (MIT) +# SPDX-FileCopyrightText: 2017 Dean Miller for Adafruit Industries # -# Copyright (c) 2017 Dean Miller for Adafruit Industries -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# pylint: disable=missing-docstring,invalid-name,too-many-public-methods - -__version__ = "0.0.0-auto.0" +# SPDX-License-Identifier: MIT + + +""" +`adafruit_seesaw.analoginput` +==================================================== +""" + +__version__ = "0.0.0+auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_seesaw.git" + class AnalogInput: - def __init__(self, seesaw, pin): + """CircuitPython-compatible class for analog inputs + + This class is intended to be a compatible subset of `analogio.AnalogIn` + + :param ~adafruit_seesaw.seesaw.Seesaw seesaw: The device + :param int pin: The pin number on the device""" + + def __init__(self, seesaw, pin, delay=0.008): self._seesaw = seesaw self._pin = pin + self._delay = delay def deinit(self): pass @property def value(self): - return self._seesaw.analog_read(self._pin) + """The current analog value on the pin, as an integer from 0..65535 (inclusive)""" + return self._seesaw.analog_read(self._pin, self._delay) @property def reference_voltage(self): + """The reference voltage for the pin""" return 3.3 diff --git a/adafruit_seesaw/attiny8x7.py b/adafruit_seesaw/attiny8x7.py new file mode 100644 index 0000000..78356ca --- /dev/null +++ b/adafruit_seesaw/attiny8x7.py @@ -0,0 +1,32 @@ +# SPDX-FileCopyrightText: 2017 Dean Miller for Adafruit Industries +# +# SPDX-License-Identifier: MIT + + +""" +`adafruit_seesaw.attiny8x7` - Pin definition for Adafruit ATtiny8x7 Breakout with seesaw +================================================================================== +""" + +__version__ = "0.0.0+auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_seesaw.git" + + +class ATtiny8x7_Pinmap: + """This class is automatically used by `adafruit_seesaw.seesaw.Seesaw` when + a ATtiny8x7 Breakout is detected. + + It is also a reference for the capabilities of each pin.""" + + #: The pins capable of analog output + analog_pins = (0, 1, 2, 3, 6, 7, 18, 19, 20) + + """The effective bit resolution of the PWM pins""" + pwm_width = 16 # we dont actually use all 16 bits but whatever + + """The pins capable of PWM output""" + pwm_pins = (0, 1, 9, 12, 13) # 8 bit PWM mode + pwm_pins += (6, 7, 8) # 16 bit PWM mode + + """No pins on this board are capable of touch input""" + touch_pins = () diff --git a/adafruit_seesaw/attinyx16.py b/adafruit_seesaw/attinyx16.py new file mode 100644 index 0000000..b80db65 --- /dev/null +++ b/adafruit_seesaw/attinyx16.py @@ -0,0 +1,31 @@ +# SPDX-FileCopyrightText: 2017 Dean Miller for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +""" +`adafruit_seesaw.attinyx16` - Pin definition for Adafruit ATtinyx16 Breakout with seesaw +================================================================================== +""" + +__version__ = "0.0.0+auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_seesaw.git" + + +class ATtinyx16_Pinmap: + """This class is automatically used by `adafruit_seesaw.seesaw.Seesaw` when + a ATtinyx16 Breakout (PID 5690, PID 5681) is detected. + + It is also a reference for the capabilities of each pin.""" + + """The pins capable of analog output""" + analog_pins = (0, 1, 2, 3, 4, 5, 14, 15, 16) + + """The effective bit resolution of the PWM pins""" + pwm_width = 16 # we dont actually use all 16 bits but whatever + + """The pins capable of PWM output""" + pwm_pins = (0, 1, 7, 11, 16) # 8 bit PWM mode + pwm_pins += (4, 5, 6) # 16 bit PWM mode + + """No pins on this board are capable of touch input""" + touch_pins = () diff --git a/adafruit_seesaw/crickit.py b/adafruit_seesaw/crickit.py index 0e52d41..21bf5b3 100644 --- a/adafruit_seesaw/crickit.py +++ b/adafruit_seesaw/crickit.py @@ -1,29 +1,22 @@ -# The MIT License (MIT) +# SPDX-FileCopyrightText: 2017 Dean Miller for Adafruit Industries # -# Copyright (c) 2017 Dean Miller for Adafruit Industries -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# pylint: disable=missing-docstring,invalid-name,too-many-public-methods,too-few-public-methods - -from micropython import const - -__version__ = "0.0.0-auto.0" +# SPDX-License-Identifier: MIT + + +""" +`adafruit_seesaw.crickit` - Pin definition for Adafruit CRICKIT +=============================================================== +""" + +try: + from micropython import const +except ImportError: + + def const(x): + return x + + +__version__ = "0.0.0+auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_seesaw.git" # The ordering here reflects the seesaw firmware pinmap for crickit, @@ -66,15 +59,22 @@ # PA pins are nn # PB pins are 32+nn + class Crickit_Pinmap: # seesaw firmware analog pin map: # analog[0]: 2 analog[1]: 3 analog[2]:40 analog[3]:41 # analog[4]:11 analog[5]:10 analog[6]: 9 analog[7]: 8 # no special remapping: same order as constants above - analog_pins = (_CRICKIT_SIGNAL1, _CRICKIT_SIGNAL2, - _CRICKIT_SIGNAL3, _CRICKIT_SIGNAL4, - _CRICKIT_SIGNAL5, _CRICKIT_SIGNAL6, - _CRICKIT_SIGNAL7, _CRICKIT_SIGNAL8) + analog_pins = ( + _CRICKIT_SIGNAL1, + _CRICKIT_SIGNAL2, + _CRICKIT_SIGNAL3, + _CRICKIT_SIGNAL4, + _CRICKIT_SIGNAL5, + _CRICKIT_SIGNAL6, + _CRICKIT_SIGNAL7, + _CRICKIT_SIGNAL8, + ) pwm_width = 16 @@ -82,12 +82,26 @@ class Crickit_Pinmap: # pwm[0]:14 pwm[1]:15 pwm[2]:16 pwm[3]:17 pwm[4]:18 pwm[5]:19 # pwm[6]:22 pwm[7]:23 pwm[8]:42 pwm[9]:43 pwm[10]:12 pwm[11]:13 # Note that servo pins are in reverse order (17-14), and motor pins are shuffled. - pwm_pins = (_CRICKIT_SERVO4, _CRICKIT_SERVO3, _CRICKIT_SERVO2, _CRICKIT_SERVO1, - _CRICKIT_MOTOR2B, _CRICKIT_MOTOR2A, - _CRICKIT_MOTOR1A, _CRICKIT_MOTOR1B, - _CRICKIT_DRIVE4, _CRICKIT_DRIVE3, - _CRICKIT_DRIVE2, _CRICKIT_DRIVE1) + pwm_pins = ( + _CRICKIT_SERVO4, + _CRICKIT_SERVO3, + _CRICKIT_SERVO2, + _CRICKIT_SERVO1, + _CRICKIT_MOTOR2B, + _CRICKIT_MOTOR2A, + _CRICKIT_MOTOR1A, + _CRICKIT_MOTOR1B, + _CRICKIT_DRIVE4, + _CRICKIT_DRIVE3, + _CRICKIT_DRIVE2, + _CRICKIT_DRIVE1, + ) # seesaw firmware touch pin map: # touch[0]: 4 touch[1]: 5 touch[2]: 6 touch[3]: 7 - touch_pins = (_CRICKIT_CAPTOUCH1, _CRICKIT_CAPTOUCH2, _CRICKIT_CAPTOUCH3, _CRICKIT_CAPTOUCH4) + touch_pins = ( + _CRICKIT_CAPTOUCH1, + _CRICKIT_CAPTOUCH2, + _CRICKIT_CAPTOUCH3, + _CRICKIT_CAPTOUCH4, + ) diff --git a/adafruit_seesaw/digitalio.py b/adafruit_seesaw/digitalio.py index 45ccd4c..1033502 100644 --- a/adafruit_seesaw/digitalio.py +++ b/adafruit_seesaw/digitalio.py @@ -1,37 +1,34 @@ -# The MIT License (MIT) +# SPDX-FileCopyrightText: 2017 Dean Miller for Adafruit Industries # -# Copyright (c) 2017 Dean Miller for Adafruit Industries -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# pylint: disable=missing-docstring,invalid-name,too-many-public-methods +# SPDX-License-Identifier: MIT + + +""" +`adafruit_seesaw.digitalio` +==================================================== +""" import digitalio -__version__ = "0.0.0-auto.0" +__version__ = "0.0.0+auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_seesaw.git" + class DigitalIO: + """CircuitPython-compatible class for digital I/O pins + + This class is intended to be a compatible subset of `digitalio.DigitalInOut`. + + Due to technical limitations, PULL_DOWNs are not supported. + + :param ~adafruit_seesaw.seesaw.Seesaw seesaw: The device + :param int pin: The pin number on the device""" + def __init__(self, seesaw, pin): self._seesaw = seesaw self._pin = pin self._drive_mode = digitalio.DriveMode.PUSH_PULL - self._direction = False + self._direction = digitalio.Direction.INPUT self._pull = None self._value = False @@ -39,14 +36,16 @@ def deinit(self): pass def switch_to_output(self, value=False, drive_mode=digitalio.DriveMode.PUSH_PULL): + """Switch the pin to output mode""" self._seesaw.pin_mode(self._pin, self._seesaw.OUTPUT) self._seesaw.digital_write(self._pin, value) self._drive_mode = drive_mode self._pull = None def switch_to_input(self, pull=None): + """Switch the pin to input mode""" if pull == digitalio.Pull.DOWN: - raise ValueError("Pull Down currently not supported") + self._seesaw.pin_mode(self._pin, self._seesaw.INPUT_PULLDOWN) elif pull == digitalio.Pull.UP: self._seesaw.pin_mode(self._pin, self._seesaw.INPUT_PULLUP) else: @@ -55,6 +54,7 @@ def switch_to_input(self, pull=None): @property def direction(self): + """Retrieve or set the direction of the pin""" return self._direction @direction.setter @@ -69,6 +69,7 @@ def direction(self, value): @property def value(self): + """Retrieve or set the value of the pin""" if self._direction == digitalio.Direction.OUTPUT: return self._value return self._seesaw.digital_read(self._pin) @@ -82,6 +83,7 @@ def value(self, val): @property def drive_mode(self): + """Retrieve or set the drive mode of an output pin""" return self._drive_mode @drive_mode.setter @@ -90,14 +92,15 @@ def drive_mode(self, mode): @property def pull(self): + """Retrieve or set the pull mode of an input pin""" return self._pull @pull.setter def pull(self, mode): if self._direction == digitalio.Direction.OUTPUT: raise AttributeError("cannot set pull on an output pin") - elif mode == digitalio.Pull.DOWN: - raise ValueError("Pull Down currently not supported") + if mode == digitalio.Pull.DOWN: + self._seesaw.pin_mode(self._pin, self._seesaw.INPUT_PULLDOWN) elif mode == digitalio.Pull.UP: self._seesaw.pin_mode(self._pin, self._seesaw.INPUT_PULLUP) elif mode is None: diff --git a/adafruit_seesaw/keypad.py b/adafruit_seesaw/keypad.py index 414708b..2caf287 100644 --- a/adafruit_seesaw/keypad.py +++ b/adafruit_seesaw/keypad.py @@ -1,30 +1,24 @@ -# The MIT License (MIT) +# SPDX-FileCopyrightText: 2018 Dean Miller for Adafruit Industries # -# Copyright (c) 2018 Dean Miller for Adafruit Industries -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# pylint: disable=missing-docstring,invalid-name,too-many-public-methods - -from micropython import const +# SPDX-License-Identifier: MIT + + +""" +`adafruit_seesaw.keypad` +==================================================== +""" + +try: + from micropython import const +except ImportError: + + def const(x): + return x + + from adafruit_seesaw.seesaw import Seesaw -__version__ = "0.0.0-auto.0" +__version__ = "0.0.0+auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_seesaw.git" _KEYPAD_BASE = const(0x10) @@ -36,31 +30,50 @@ _KEYPAD_COUNT = const(0x04) _KEYPAD_FIFO = const(0x10) -# pylint: disable=too-few-public-methods + class KeyEvent: + """Holds information about a key event in its properties + + :param int num: The number of the key + :param int edge: One of the EDGE propertes of `adafruit_seesaw.keypad.Keypad` + """ + def __init__(self, num, edge): self.number = int(num) self.edge = int(edge) + + # pylint: enable=too-few-public-methods + class Keypad(Seesaw): + """On compatible SeeSaw devices, reads from a keypad. + :param ~busio.I2C i2c_bus: Bus the SeeSaw is connected to + :param int addr: I2C address of the SeeSaw device + :param ~digitalio.DigitalInOut drdy: Pin connected to SeeSaw's 'ready' output""" + + #: Indicates that the key is currently pressed EDGE_HIGH = 0 + #: Indicates that the key is currently released EDGE_LOW = 1 + #: Indicates that the key was recently pressed EDGE_FALLING = 2 + #: Indicates that the key was recently released EDGE_RISING = 3 def __init__(self, i2c_bus, addr=0x49, drdy=None): - super(Keypad, self).__init__(i2c_bus, addr, drdy) + super().__init__(i2c_bus, addr, drdy) self._interrupt_enabled = False @property def interrupt_enabled(self): + """Retrieve or set the interrupt enable flag""" return self._interrupt_enabled @interrupt_enabled.setter def interrupt_enabled(self, value): - if value not in (True, False): + if value not in {True, False}: raise ValueError("interrupt_enabled must be True or False") self._interrupt_enabled = value @@ -70,28 +83,36 @@ def interrupt_enabled(self, value): self.write8(_KEYPAD_BASE, _KEYPAD_INTENCLR, 1) @property - def count(self): + def count(self): # noqa: PLR6301 + """Retrieve or set the number of keys""" return self.read8(_KEYPAD_BASE, _KEYPAD_COUNT) - # pylint: disable=unused-argument, no-self-use @count.setter - def count(self, value): + def count(self, value): # noqa: PLR6301 raise AttributeError("count is read only") - # pylint: enable=unused-argument, no-self-use def set_event(self, key, edge, enable): - if enable not in (True, False): + """Control which kinds of events are set + + :param int key: The key number + :param int edge: The type of event + :param bool enable: True to enable the event, False to disable it""" + + if enable not in {True, False}: raise ValueError("event enable must be True or False") if edge > 3 or edge < 0: raise ValueError("invalid edge") cmd = bytearray(2) cmd[0] = key - cmd[1] = (1 << (edge+1)) | enable + cmd[1] = (1 << (edge + 1)) | enable self.write(_KEYPAD_BASE, _KEYPAD_EVENT, cmd) def read_keypad(self, num): + """Read data from the keypad + + :param int num: The number of bytes to read""" ret = bytearray(num) self.read(_KEYPAD_BASE, _KEYPAD_FIFO, ret) return ret diff --git a/adafruit_seesaw/neopixel.py b/adafruit_seesaw/neopixel.py index e3d0410..c9b470e 100644 --- a/adafruit_seesaw/neopixel.py +++ b/adafruit_seesaw/neopixel.py @@ -1,33 +1,32 @@ -# The MIT License (MIT) +# SPDX-FileCopyrightText: 2017 Dean Miller for Adafruit Industries # -# Copyright (c) 2017 Dean Miller for Adafruit Industries -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# pylint: disable=missing-docstring,invalid-name,too-many-public-methods +# SPDX-License-Identifier: MIT + + +""" +`adafruit_seesaw.neopixel` +==================================================== +""" + +import struct + +from adafruit_pixelbuf import PixelBuf try: - import struct + from micropython import const except ImportError: - import ustruct as struct -from micropython import const -__version__ = "1.2.3" + def const(x): + return x + + +### hack to make sure this module is not placed in root CIRCUITPY/lib folder +if "." not in __name__: + raise ImportError( + "seesaw neopixel being imported from unexpected location - is seesaw neopixel use intended?" + ) + +__version__ = "0.0.0+auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_seesaw.git" _NEOPIXEL_BASE = const(0x0E) @@ -39,102 +38,67 @@ _NEOPIXEL_BUF = const(0x04) _NEOPIXEL_SHOW = const(0x05) +# try lower values if IO errors +_OUTPUT_BUFFER_SIZE = const(24) + # Pixel color order constants -RGB = (0, 1, 2) +RGB = "RGB" """Red Green Blue""" -GRB = (1, 0, 2) +GRB = "GRB" """Green Red Blue""" -RGBW = (0, 1, 2, 3) +RGBW = "RGBW" """Red Green Blue White""" -GRBW = (1, 0, 2, 3) +GRBW = "GRBW" """Green Red Blue White""" -class NeoPixel: - def __init__(self, seesaw, pin, n, *, bpp=3, brightness=1.0, auto_write=True, pixel_order=None): - # TODO: brightness not yet implemented. + +class NeoPixel(PixelBuf): + """Control NeoPixels connected to a seesaw + + :param ~adafruit_seesaw.seesaw.Seesaw seesaw: The device + :param int pin: The pin number on the device + :param int n: The number of pixels + :param int bpp: The number of bytes per pixel + :param float brightness: The brightness, from 0.0 to 1.0 + :param bool auto_write: Automatically update the pixels when changed + :param tuple pixel_order: The layout of the pixels. + Use one of the order constants such as RGBW.""" + + def __init__( + self, seesaw, pin, n, *, bpp=None, brightness=1.0, auto_write=True, pixel_order="GRB" + ): self._seesaw = seesaw self._pin = pin - self._bpp = bpp - self.auto_write = auto_write - self._n = n - self._brightness = min(max(brightness, 0.0), 1.0) - self._pixel_order = GRBW if pixel_order is None else pixel_order + if not pixel_order: + pixel_order = GRB if bpp == 3 else GRBW + elif isinstance(pixel_order, tuple): + # convert legacy pixel order into PixelBuf pixel order + order_list = ["RGBW"[order] for order in pixel_order] + pixel_order = "".join(order_list) + + super().__init__( + n, + byteorder=pixel_order, + brightness=brightness, + auto_write=auto_write, + ) cmd = bytearray([pin]) self._seesaw.write(_NEOPIXEL_BASE, _NEOPIXEL_PIN, cmd) - cmd = struct.pack(">H", n*self._bpp) + cmd = struct.pack(">H", n * self.bpp) self._seesaw.write(_NEOPIXEL_BASE, _NEOPIXEL_BUF_LENGTH, cmd) + self.output_buffer = bytearray(_OUTPUT_BUFFER_SIZE) - @property - def brightness(self): - """Overall brightness of the pixel""" - return self._brightness + def _transmit(self, buffer: bytearray) -> None: + """Update the pixels even if auto_write is False""" - @brightness.setter - def brightness(self, brightness): - # pylint: disable=attribute-defined-outside-init - self._brightness = min(max(brightness, 0.0), 1.0) - if self.auto_write: - self.show() + step = _OUTPUT_BUFFER_SIZE - 2 + for i in range(0, len(buffer), step): + self.output_buffer[0:2] = struct.pack(">H", i) + self.output_buffer[2:] = buffer[i : i + step] + self._seesaw.write(_NEOPIXEL_BASE, _NEOPIXEL_BUF, self.output_buffer) - def deinit(self): - pass + self._seesaw.write(_NEOPIXEL_BASE, _NEOPIXEL_SHOW) - def __len__(self): - return self._n - - def __setitem__(self, key, color): - cmd = bytearray(2 + self._bpp) - struct.pack_into(">H", cmd, 0, key * self._bpp) - if isinstance(color, int): - w = color >> 24 - r = (color >> 16) & 0xff - g = (color >> 8) & 0xff - b = color & 0xff - else: - if self._bpp == 3: - r, g, b = color - else: - r, g, b, w = color - - # If all components are the same and we have a white pixel then use it - # instead of the individual components. - if self._bpp == 4 and r == g == b: - w = r - r = 0 - g = 0 - b = 0 - - if self.brightness < 0.99: - r = int(r * self.brightness) - g = int(g * self.brightness) - b = int(b * self.brightness) - if self._bpp == 4: - w = int(w * self.brightness) - - # Store colors in correct slots - cmd[2 + self._pixel_order[0]] = r - cmd[2 + self._pixel_order[1]] = g - cmd[2 + self._pixel_order[2]] = b - if self._bpp == 4: - cmd[2 + self._pixel_order[3]] = w - - self._seesaw.write(_NEOPIXEL_BASE, _NEOPIXEL_BUF, cmd) - if self.auto_write: - self.show() - - def __getitem__(self, key): + def deinit(self): pass - - def fill(self, color): - # Suppress auto_write while filling. - current_auto_write = self.auto_write - self.auto_write = False - for i in range(self._n): - self[i] = color - if current_auto_write: - self.show() - self.auto_write = current_auto_write - - def show(self): - self._seesaw.write(_NEOPIXEL_BASE, _NEOPIXEL_SHOW) diff --git a/adafruit_seesaw/pwmout.py b/adafruit_seesaw/pwmout.py index 1ab5457..869db25 100644 --- a/adafruit_seesaw/pwmout.py +++ b/adafruit_seesaw/pwmout.py @@ -1,31 +1,20 @@ -# The MIT License (MIT) +# SPDX-FileCopyrightText: 2017 Dean Miller for Adafruit Industries # -# Copyright (c) 2017 Dean Miller for Adafruit Industries -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# pylint: disable=missing-docstring,invalid-name,too-many-public-methods,too-few-public-methods +# SPDX-License-Identifier: MIT + -__version__ = "0.0.0-auto.0" +""" +`adafruit_seesaw.pwmout` +==================================================== +""" + +__version__ = "0.0.0+auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_seesaw.git" + class PWMOut: - """A single seesaw channel that matches the :py:class:`~pulseio.PWMOut` API.""" + """A single seesaw channel that matches the :py:class:`~pwmio.PWMOut` API.""" + def __init__(self, seesaw, pin): self._seesaw = seesaw self._pin = pin @@ -34,7 +23,7 @@ def __init__(self, seesaw, pin): @property def frequency(self): - """The overall PWM frequency in herz.""" + """The overall PWM frequency in Hertz.""" return self._frequency @frequency.setter @@ -52,7 +41,7 @@ def duty_cycle(self): @duty_cycle.setter def duty_cycle(self, value): - if not 0 <= value <= 0xffff: + if not 0 <= value <= 0xFFFF: raise ValueError("Must be 0 to 65535") self._seesaw.analog_write(self._pin, value) self._dc = value diff --git a/adafruit_seesaw/robohat.py b/adafruit_seesaw/robohat.py new file mode 100644 index 0000000..e8c0be8 --- /dev/null +++ b/adafruit_seesaw/robohat.py @@ -0,0 +1,108 @@ +# SPDX-FileCopyrightText: 2019 wallarug Robotics Masters +# +# SPDX-License-Identifier: MIT + + +""" +`adafruit_seesaw.robohat` - Pin definition for RoboHAT +====================================================== +""" + +try: + from micropython import const +except ImportError: + + def const(x): + return x + + +__version__ = "0.0.0+auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_seesaw.git" + +# Robo HAT MM1 Board: https://www.crowdsupply.com/robotics-masters/robo-hat-mm1 + +# The ordering here reflects the seesaw firmware (mm1_hat) pinmap for Robo HAT MM1, +# not logical ordering of the HAT terminals. + +_MM1_D0 = const(55) # (RX to RPI_TX) +_MM1_D1 = const(54) # (TX to RPI_RX) +_MM1_D2 = const(34) # ADC (GPS_TX) +_MM1_D3 = const(35) # ADC (GPS_RX) +_MM1_D4 = const(0) # (GPS_SDA) +_MM1_D5 = const(1) # (GPS_SCL) +_MM1_D6 = const(28) # (POWER_ENABLE) +_MM1_D7 = const(2) # (BATTERY) +_MM1_D8 = const(20) # (NEOPIXEL) +_MM1_D9 = const(43) # PWM (SPI_SCK) +_MM1_D10 = const(41) # PWM (SPI_SS) +_MM1_D11 = const(42) # PWM (SPI_MOSI) +_MM1_D12 = const(40) # PWM (SPI_MISO) +_MM1_D13 = const(21) # LED +_MM1_D14 = const(3) # (POWER_OFF) + +_MM1_SERVO8 = const(8) +_MM1_SERVO7 = const(9) +_MM1_SERVO6 = const(10) +_MM1_SERVO5 = const(11) +_MM1_SERVO4 = const(19) +_MM1_SERVO3 = const(18) +_MM1_SERVO2 = const(17) +_MM1_SERVO1 = const(16) + +_MM1_RCH1 = const(7) +_MM1_RCH2 = const(6) +_MM1_RCH3 = const(5) +_MM1_RCH4 = const(4) + + +# seesaw firmware has indexed lists of pins by function. +# These "pin" numbers map to real PAxx, PBxx pins on the board implementing seesaaw +# They may or may not match. +# See seesaw/include/SeesawConfig.h and seesaw/boards/robohatmm1/board_config.h for the pin choices. + +# You must look at both files and combine the defaults in SeesawConfig.h with the +# overrides in robohatmm1/board_config.h. +# PA pins are nn +# PB pins are 32+nn + + +class MM1_Pinmap: + """This class is automatically used by `adafruit_seesaw.seesaw.Seesaw` when + a RoboHAT board is detected. + + It is also a reference for the capabilities of each pin.""" + + # seesaw firmware (mm1_hat) analog pin map: + # analog[0]:47 analog[1]:48 analog[2]: analog[3]: + # analog[4]: analog[5]: analog[6]: analog[7]: + # + #: The pins capable of analog output + analog_pins = (_MM1_D3, _MM1_D2) + + #: The effective bit resolution of the PWM pins + pwm_width = 16 + + # seesaw firmware (mm1_hat) pwm pin map: + # pwm[0]:16 pwm[1]:17 pwm[2]:18 pwm[3]:19 pwm[4]:11 pwm[5]:10 + # pwm[6]:9 pwm[7]:8 pwm[8]:40 pwm[9]:41 pwm[10]:42 pwm[11]:43 + # + #: The pins capable of PWM output + pwm_pins = ( + _MM1_SERVO1, + _MM1_SERVO2, + _MM1_SERVO3, + _MM1_SERVO4, + _MM1_SERVO5, + _MM1_SERVO6, + _MM1_SERVO7, + _MM1_SERVO8, + _MM1_D12, + _MM1_D10, + _MM1_D11, + _MM1_D9, + ) + + # seesaw firmware touch pin map: + # touch[0]: 7 touch[1]: 6 touch[2]: 5 touch[3]: 4 + #: The pins capable of touch input + touch_pins = (_MM1_RCH1, _MM1_RCH2, _MM1_RCH3, _MM1_RCH4) diff --git a/adafruit_seesaw/rotaryio.py b/adafruit_seesaw/rotaryio.py new file mode 100644 index 0000000..8a23d90 --- /dev/null +++ b/adafruit_seesaw/rotaryio.py @@ -0,0 +1,33 @@ +# SPDX-FileCopyrightText: 2017 Dean Miller for Adafruit Industries +# +# SPDX-License-Identifier: MIT + + +""" +`adafruit_seesaw.rotaryio` +==================================================== +""" + +__version__ = "0.0.0+auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_seesaw.git" + + +class IncrementalEncoder: + """IncrementalEncoder determines the relative rotational position based + on two series of pulses.""" + + def __init__(self, seesaw, encoder=0): + """Create an IncrementalEncoder object associated with the given + eesaw device.""" + self._seesaw = seesaw + self._encoder = encoder + + @property + def position(self): + """The current position in terms of pulses. The number of pulses per + rotation is defined by the specific hardware.""" + return self._seesaw.encoder_position(self._encoder) + + @position.setter + def position(self, value): + self._seesaw.set_encoder_position(value, self._encoder) diff --git a/adafruit_seesaw/samd09.py b/adafruit_seesaw/samd09.py index dd19627..aaa119b 100644 --- a/adafruit_seesaw/samd09.py +++ b/adafruit_seesaw/samd09.py @@ -1,29 +1,22 @@ -# The MIT License (MIT) +# SPDX-FileCopyrightText: 2017 Dean Miller for Adafruit Industries # -# Copyright (c) 2017 Dean Miller for Adafruit Industries -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# pylint: disable=missing-docstring,invalid-name,too-many-public-methods,too-few-public-methods - -from micropython import const - -__version__ = "0.0.0-auto.0" +# SPDX-License-Identifier: MIT + + +""" +`adafruit_seesaw.samd09` - Pin definition for Adafruit SAMD09 Breakout with seesaw +================================================================================== +""" + +try: + from micropython import const +except ImportError: + + def const(x): + return x + + +__version__ = "0.0.0+auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_seesaw.git" _ADC_INPUT_0_PIN = const(0x02) @@ -36,12 +29,26 @@ _PWM_2_PIN = const(0x06) _PWM_3_PIN = const(0x07) + class SAMD09_Pinmap: - analog_pins = (_ADC_INPUT_0_PIN, _ADC_INPUT_1_PIN, - _ADC_INPUT_2_PIN, _ADC_INPUT_3_PIN) + """This class is automatically used by `adafruit_seesaw.seesaw.Seesaw` when + a SAMD09 Breakout is detected. + + It is also a reference for the capabilities of each pin.""" + + #: The pins capable of analog output + analog_pins = ( + _ADC_INPUT_0_PIN, + _ADC_INPUT_1_PIN, + _ADC_INPUT_2_PIN, + _ADC_INPUT_3_PIN, + ) + """The effective bit resolution of the PWM pins""" pwm_width = 8 + """The pins capable of PWM output""" pwm_pins = (_PWM_0_PIN, _PWM_1_PIN, _PWM_2_PIN, _PWM_3_PIN) + """No pins on this board are capable of touch input""" touch_pins = () diff --git a/adafruit_seesaw/seesaw.py b/adafruit_seesaw/seesaw.py index 63e3258..0ec2c89 100644 --- a/adafruit_seesaw/seesaw.py +++ b/adafruit_seesaw/seesaw.py @@ -1,26 +1,9 @@ -# The MIT License (MIT) +# SPDX-FileCopyrightText: 2017 Dean Miller for Adafruit Industries # -# Copyright (c) 2017 Dean Miller for Adafruit Industries -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. +# SPDX-License-Identifier: MIT + """ -`seesaw` +`adafruit_seesaw.seesaw` ==================================================== An I2C to whatever helper chip. @@ -37,25 +20,28 @@ **Software and Dependencies:** -* Adafruit CircuitPython firmware for the ESP8622 and M0-based boards: - https://github.com/adafruit/circuitpython/releases +* Adafruit CircuitPython firmware: https://circuitpython.org/ +* or Adafruit Blinka: https://circuitpython.org/blinka * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice """ # This code needs to be broken up into analogio, busio, digitalio, and pulseio # compatible classes so we won't bother with some lints until then. -# pylint: disable=missing-docstring,invalid-name,too-many-public-methods,no-name-in-module +import struct import time try: - import struct + from micropython import const except ImportError: - import ustruct as struct -from micropython import const + + def const(x): + return x + + from adafruit_bus_device.i2c_device import I2CDevice -__version__ = "0.0.0-auto.0" +__version__ = "0.0.0+auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_seesaw.git" _STATUS_BASE = const(0x00) @@ -71,6 +57,7 @@ _EEPROM_BASE = const(0x0D) _NEOPIXEL_BASE = const(0x0E) _TOUCH_BASE = const(0x0F) +_ENCODER_BASE = const(0x11) _GPIO_DIRSET_BULK = const(0x02) _GPIO_DIRCLR_BULK = const(0x03) @@ -116,134 +103,222 @@ _TOUCH_CHANNEL_OFFSET = const(0x10) -_HW_ID_CODE = const(0x55) -_EEPROM_I2C_ADDR = const(0x3F) - -#TODO: update when we get real PID +_SAMD09_HW_ID_CODE = const(0x55) +_ATTINY806_HW_ID_CODE = const(0x84) +_ATTINY807_HW_ID_CODE = const(0x85) +_ATTINY816_HW_ID_CODE = const(0x86) +_ATTINY817_HW_ID_CODE = const(0x87) +_ATTINY1616_HW_ID_CODE = const(0x88) +_ATTINY1617_HW_ID_CODE = const(0x89) + +_ENCODER_STATUS = const(0x00) +_ENCODER_INTENSET = const(0x10) +_ENCODER_INTENCLR = const(0x20) +_ENCODER_POSITION = const(0x30) +_ENCODER_DELTA = const(0x40) + +# TODO: update when we get real PID _CRICKIT_PID = const(9999) +_ROBOHATMM1_PID = const(9998) +_5690_PID = const(5690) +_5681_PID = const(5681) +_5743_PID = const(5743) + class Seesaw: """Driver for Seesaw i2c generic conversion trip - :param ~busio.I2C i2c_bus: Bus the SeeSaw is connected to - :param int addr: I2C address of the SeeSaw device""" + :param ~busio.I2C i2c_bus: Bus the SeeSaw is connected to + :param int addr: I2C address of the SeeSaw device + :param ~digitalio.DigitalInOut drdy: Pin connected to SeeSaw's 'ready' output + :param bool reset: Whether to do a software reset on init""" + INPUT = const(0x00) OUTPUT = const(0x01) INPUT_PULLUP = const(0x02) INPUT_PULLDOWN = const(0x03) - def __init__(self, i2c_bus, addr=0x49, drdy=None): + def __init__(self, i2c_bus, addr=0x49, drdy=None, reset=True): self._drdy = drdy if drdy is not None: drdy.switch_to_input() self.i2c_device = I2CDevice(i2c_bus, addr) - self.sw_reset() - - def sw_reset(self): - """Trigger a software reset of the SeeSaw chip""" - self.write8(_STATUS_BASE, _STATUS_SWRST, 0xFF) - time.sleep(.500) - - chip_id = self.read8(_STATUS_BASE, _STATUS_HW_ID) - - if chip_id != _HW_ID_CODE: - raise RuntimeError("Seesaw hardware ID returned (0x{:x}) is not " - "correct! Expected 0x{:x}. Please check your wiring." - .format(chip_id, _HW_ID_CODE)) + if reset: + self.sw_reset() + + self.chip_id = self.read8(_STATUS_BASE, _STATUS_HW_ID) + if self.chip_id not in { + _ATTINY806_HW_ID_CODE, + _ATTINY807_HW_ID_CODE, + _ATTINY816_HW_ID_CODE, + _ATTINY817_HW_ID_CODE, + _ATTINY1616_HW_ID_CODE, + _ATTINY1617_HW_ID_CODE, + _SAMD09_HW_ID_CODE, + }: + raise RuntimeError( + f"Seesaw hardware ID returned 0x{self.chip_id:x} is not " + "correct! Please check your wiring." + ) pid = self.get_version() >> 16 if pid == _CRICKIT_PID: - from adafruit_seesaw.crickit import Crickit_Pinmap + from adafruit_seesaw.crickit import Crickit_Pinmap # noqa: PLC0415 + self.pin_mapping = Crickit_Pinmap - else: - from adafruit_seesaw.samd09 import SAMD09_Pinmap + elif pid == _ROBOHATMM1_PID: + from adafruit_seesaw.robohat import MM1_Pinmap # noqa: PLC0415 + + self.pin_mapping = MM1_Pinmap + elif (pid in {_5690_PID, _5681_PID, _5743_PID}) or ( + self.chip_id in {_ATTINY816_HW_ID_CODE, _ATTINY806_HW_ID_CODE, _ATTINY1616_HW_ID_CODE} + ): + from adafruit_seesaw.attinyx16 import ATtinyx16_Pinmap # noqa: PLC0415 + + self.pin_mapping = ATtinyx16_Pinmap + elif self.chip_id == _SAMD09_HW_ID_CODE: + from adafruit_seesaw.samd09 import SAMD09_Pinmap # noqa: PLC0415 + self.pin_mapping = SAMD09_Pinmap + elif self.chip_id in { + _ATTINY817_HW_ID_CODE, + _ATTINY807_HW_ID_CODE, + _ATTINY1617_HW_ID_CODE, + }: + from adafruit_seesaw.attiny8x7 import ATtiny8x7_Pinmap # noqa: PLC0415 + + self.pin_mapping = ATtiny8x7_Pinmap + + def sw_reset(self, post_reset_delay=0.5): + """Trigger a software reset of the SeeSaw chip""" + self.write8(_STATUS_BASE, _STATUS_SWRST, 0xFF) + time.sleep(post_reset_delay) def get_options(self): + """Retrieve the 'options' word from the SeeSaw board""" buf = bytearray(4) self.read(_STATUS_BASE, _STATUS_OPTIONS, buf) ret = struct.unpack(">I", buf)[0] return ret def get_version(self): + """Retrieve the 'version' word from the SeeSaw board""" buf = bytearray(4) self.read(_STATUS_BASE, _STATUS_VERSION, buf) ret = struct.unpack(">I", buf)[0] return ret def pin_mode(self, pin, mode): + """Set the mode of a pin by number""" if pin >= 32: self.pin_mode_bulk_b(1 << (pin - 32), mode) else: self.pin_mode_bulk(1 << pin, mode) def digital_write(self, pin, value): + """Set the value of an output pin by number""" if pin >= 32: self.digital_write_bulk_b(1 << (pin - 32), value) else: self.digital_write_bulk(1 << pin, value) def digital_read(self, pin): + """Get the value of an input pin by number""" if pin >= 32: - return self.digital_read_bulk_b((1 << (pin - 32))) != 0 - return self.digital_read_bulk((1 << pin)) != 0 + return self.digital_read_bulk_b(1 << (pin - 32)) != 0 + return self.digital_read_bulk(1 << pin) != 0 - def digital_read_bulk(self, pins): + def digital_read_bulk(self, pins, delay=0.008): + """Get the values of all the pins on the 'A' port as a bitmask""" buf = bytearray(4) - self.read(_GPIO_BASE, _GPIO_BULK, buf) - buf[0] = buf[0] & 0x3F - ret = struct.unpack(">I", buf)[0] + self.read(_GPIO_BASE, _GPIO_BULK, buf, delay=delay) + try: + ret = struct.unpack(">I", buf)[0] + except OverflowError: + buf[0] = buf[0] & 0x3F + ret = struct.unpack(">I", buf)[0] return ret & pins - def digital_read_bulk_b(self, pins): + def digital_read_bulk_b(self, pins, delay=0.008): + """Get the values of all the pins on the 'B' port as a bitmask""" buf = bytearray(8) - self.read(_GPIO_BASE, _GPIO_BULK, buf) + self.read(_GPIO_BASE, _GPIO_BULK, buf, delay=delay) ret = struct.unpack(">I", buf[4:])[0] return ret & pins - def set_GPIO_interrupts(self, pins, enabled): + """Enable or disable the GPIO interrupt""" cmd = struct.pack(">I", pins) if enabled: self.write(_GPIO_BASE, _GPIO_INTENSET, cmd) else: self.write(_GPIO_BASE, _GPIO_INTENCLR, cmd) - def analog_read(self, pin): + def get_GPIO_interrupt_flag(self, delay=0.008): + """Read and clear GPIO interrupts that have fired""" + buf = bytearray(4) + self.read(_GPIO_BASE, _GPIO_INTFLAG, buf, delay=delay) + return struct.unpack(">I", buf)[0] + + def analog_read(self, pin, delay=0.008): + """Read the value of an analog pin by number""" buf = bytearray(2) if pin not in self.pin_mapping.analog_pins: raise ValueError("Invalid ADC pin") - self.read(_ADC_BASE, _ADC_CHANNEL_OFFSET + self.pin_mapping.analog_pins.index(pin), buf) + if self.chip_id == _SAMD09_HW_ID_CODE: + offset = self.pin_mapping.analog_pins.index(pin) + else: + offset = pin + + self.read(_ADC_BASE, _ADC_CHANNEL_OFFSET + offset, buf, delay) ret = struct.unpack(">H", buf)[0] - time.sleep(.001) return ret def touch_read(self, pin): + """Read the value of a touch pin by number""" buf = bytearray(2) if pin not in self.pin_mapping.touch_pins: raise ValueError("Invalid touch pin") - self.read(_TOUCH_BASE, _TOUCH_CHANNEL_OFFSET + self.pin_mapping.touch_pins.index(pin), buf) + self.read( + _TOUCH_BASE, + _TOUCH_CHANNEL_OFFSET + self.pin_mapping.touch_pins.index(pin), + buf, + ) ret = struct.unpack(">H", buf)[0] return ret def moisture_read(self): + """Read the value of the moisture sensor""" buf = bytearray(2) - self.read(_TOUCH_BASE, _TOUCH_CHANNEL_OFFSET, buf, .005) + self.read(_TOUCH_BASE, _TOUCH_CHANNEL_OFFSET, buf, 0.005) ret = struct.unpack(">H", buf)[0] - time.sleep(.001) + time.sleep(0.001) + + # retry if reading was bad + count = 0 + while ret > 4095: + self.read(_TOUCH_BASE, _TOUCH_CHANNEL_OFFSET, buf, 0.005) + ret = struct.unpack(">H", buf)[0] + time.sleep(0.001) + count += 1 + if count > 3: + raise RuntimeError("Could not get a valid moisture reading.") + return ret - def pin_mode_bulk(self, pins, mode): - cmd = struct.pack(">I", pins) + def _pin_mode_bulk_x(self, capacity, offset, pins, mode): + cmd = bytearray(capacity) + cmd[offset:] = struct.pack(">I", pins) if mode == self.OUTPUT: self.write(_GPIO_BASE, _GPIO_DIRSET_BULK, cmd) elif mode == self.INPUT: self.write(_GPIO_BASE, _GPIO_DIRCLR_BULK, cmd) + self.write(_GPIO_BASE, _GPIO_PULLENCLR, cmd) elif mode == self.INPUT_PULLUP: self.write(_GPIO_BASE, _GPIO_DIRCLR_BULK, cmd) @@ -258,36 +333,24 @@ def pin_mode_bulk(self, pins, mode): else: raise ValueError("Invalid pin mode") - def pin_mode_bulk_b(self, pins, mode): - cmd = bytearray(8) - cmd[4:] = struct.pack(">I", pins) - if mode == self.OUTPUT: - self.write(_GPIO_BASE, _GPIO_DIRSET_BULK, cmd) - elif mode == self.INPUT: - self.write(_GPIO_BASE, _GPIO_DIRCLR_BULK, cmd) - - elif mode == self.INPUT_PULLUP: - self.write(_GPIO_BASE, _GPIO_DIRCLR_BULK, cmd) - self.write(_GPIO_BASE, _GPIO_PULLENSET, cmd) - self.write(_GPIO_BASE, _GPIO_BULK_SET, cmd) - - elif mode == self.INPUT_PULLDOWN: - self.write(_GPIO_BASE, _GPIO_DIRCLR_BULK, cmd) - self.write(_GPIO_BASE, _GPIO_PULLENSET, cmd) - self.write(_GPIO_BASE, _GPIO_BULK_CLR, cmd) + def pin_mode_bulk(self, pins, mode): + """Set the mode of all the pins on the 'A' port as a bitmask""" + self._pin_mode_bulk_x(4, 0, pins, mode) - else: - raise ValueError("Invalid pin mode") + def pin_mode_bulk_b(self, pins, mode): + """Set the mode of all the pins on the 'B' port as a bitmask""" + self._pin_mode_bulk_x(8, 4, pins, mode) def digital_write_bulk(self, pins, value): + """Set the mode of pins on the 'A' port as a bitmask""" cmd = struct.pack(">I", pins) if value: self.write(_GPIO_BASE, _GPIO_BULK_SET, cmd) else: self.write(_GPIO_BASE, _GPIO_BULK_CLR, cmd) - def digital_write_bulk_b(self, pins, value): + """Set the mode of pins on the 'B' port as a bitmask""" cmd = bytearray(8) cmd[4:] = struct.pack(">I", pins) if value: @@ -296,35 +359,69 @@ def digital_write_bulk_b(self, pins, value): self.write(_GPIO_BASE, _GPIO_BULK_CLR, cmd) def analog_write(self, pin, value): - pin_found = False + """Set the value of an analog output by number""" + if pin not in self.pin_mapping.pwm_pins: + raise ValueError("Invalid PWM pin") + + if self.chip_id == _SAMD09_HW_ID_CODE: + offset = self.pin_mapping.pwm_pins.index(pin) + else: + offset = pin + if self.pin_mapping.pwm_width == 16: - if pin in self.pin_mapping.pwm_pins: - pin_found = True - cmd = bytearray([self.pin_mapping.pwm_pins.index(pin), (value >> 8), value & 0xFF]) + cmd = bytearray([offset, (value >> 8), value & 0xFF]) else: - if pin in self.pin_mapping.pwm_pins: - pin_found = True - cmd = bytearray([self.pin_mapping.pwm_pins.index(pin), value]) + cmd = bytearray([offset, value]) - if pin_found is False: - raise ValueError("Invalid PWM pin") self.write(_TIMER_BASE, _TIMER_PWM, cmd) - time.sleep(.001) + time.sleep(0.001) def get_temp(self): + """Read the temperature""" buf = bytearray(4) - self.read(_STATUS_BASE, _STATUS_TEMP, buf) + self.read(_STATUS_BASE, _STATUS_TEMP, buf, 0.005) buf[0] = buf[0] & 0x3F ret = struct.unpack(">I", buf)[0] return 0.00001525878 * ret def set_pwm_freq(self, pin, freq): - if pin in self.pin_mapping.pwm_pins: - cmd = bytearray([self.pin_mapping.pwm_pins.index(pin), (freq >> 8), freq & 0xFF]) - self.write(_TIMER_BASE, _TIMER_FREQ, cmd) - else: + """Set the PWM frequency of a pin by number""" + if pin not in self.pin_mapping.pwm_pins: raise ValueError("Invalid PWM pin") + if self.chip_id == _SAMD09_HW_ID_CODE: + offset = self.pin_mapping.pwm_pins.index(pin) + else: + offset = pin + + cmd = bytearray([offset, (freq >> 8), freq & 0xFF]) + self.write(_TIMER_BASE, _TIMER_FREQ, cmd) + + def encoder_position(self, encoder=0): + """The current position of the encoder""" + buf = bytearray(4) + self.read(_ENCODER_BASE, _ENCODER_POSITION + encoder, buf) + return struct.unpack(">i", buf)[0] + + def set_encoder_position(self, pos, encoder=0): + """Set the current position of the encoder""" + cmd = struct.pack(">i", pos) + self.write(_ENCODER_BASE, _ENCODER_POSITION + encoder, cmd) + + def encoder_delta(self, encoder=0): + """The change in encoder position since it was last read""" + buf = bytearray(4) + self.read(_ENCODER_BASE, _ENCODER_DELTA + encoder, buf) + return struct.unpack(">i", buf)[0] + + def enable_encoder_interrupt(self, encoder=0): + """Enable the interrupt to fire when the encoder changes position""" + self.write8(_ENCODER_BASE, _ENCODER_INTENSET + encoder, 0x01) + + def disable_encoder_interrupt(self, encoder=0): + """Disable the interrupt from firing when the encoder changes""" + self.write8(_ENCODER_BASE, _ENCODER_INTENCLR + encoder, 0x01) + # def enable_sercom_data_rdy_interrupt(self, sercom): # # _sercom_inten.DATA_RDY = 1 @@ -341,37 +438,62 @@ def set_pwm_freq(self, pin, freq): # # return self.read8(SEESAW_SERCOM0_BASE + sercom, SEESAW_SERCOM_DATA) + def _get_eeprom_i2c_addr(self): + """Return the EEPROM address used to store I2C address.""" + chip_id = self.chip_id + if chip_id in { + _ATTINY806_HW_ID_CODE, + _ATTINY807_HW_ID_CODE, + _ATTINY816_HW_ID_CODE, + _ATTINY817_HW_ID_CODE, + }: + return 0x7F + if chip_id in { + _ATTINY1616_HW_ID_CODE, + _ATTINY1617_HW_ID_CODE, + }: + return 0xFF + if chip_id in {_SAMD09_HW_ID_CODE}: + return 0x3F + return None + def set_i2c_addr(self, addr): - self.eeprom_write8(_EEPROM_I2C_ADDR, addr) - time.sleep(.250) - self.i2c_device.device_address = addr - self.sw_reset() + """Store a new address in the device's EEPROM and reboot it.""" + self.eeprom_write8(self._get_eeprom_i2c_addr(), addr) def get_i2c_addr(self): - return self.read8(_EEPROM_BASE, _EEPROM_I2C_ADDR) + """Return the device's I2C address stored in its EEPROM""" + return self.read8(_EEPROM_BASE, self._get_eeprom_i2c_addr()) def eeprom_write8(self, addr, val): + """Write a single byte directly to the device's EEPROM""" self.eeprom_write(addr, bytearray([val])) def eeprom_write(self, addr, buf): + """Write multiple bytes directly to the device's EEPROM""" self.write(_EEPROM_BASE, addr, buf) def eeprom_read8(self, addr): + """Read a single byte directly to the device's EEPROM""" return self.read8(_EEPROM_BASE, addr) def uart_set_baud(self, baud): + """Set the serial baudrate of the device""" cmd = struct.pack(">I", baud) self.write(_SERCOM0_BASE, _SERCOM_BAUD, cmd) def write8(self, reg_base, reg, value): + """Write an arbitrary I2C byte register on the device""" self.write(reg_base, reg, bytearray([value])) def read8(self, reg_base, reg): + """Read an arbitrary I2C byte register on the device""" ret = bytearray(1) self.read(reg_base, reg, ret) return ret[0] - def read(self, reg_base, reg, buf, delay=.001): + def read(self, reg_base, reg, buf, delay=0.008): + """Read an arbitrary I2C register range on the device""" self.write(reg_base, reg) if self._drdy is not None: while self._drdy.value is False: @@ -382,6 +504,7 @@ def read(self, reg_base, reg, buf, delay=.001): i2c.readinto(buf) def write(self, reg_base, reg, buf=None): + """Write an arbitrary I2C register range on the device""" full_buffer = bytearray([reg_base, reg]) if buf is not None: full_buffer += buf diff --git a/adafruit_seesaw/tftshield18.py b/adafruit_seesaw/tftshield18.py new file mode 100755 index 0000000..57f6d43 --- /dev/null +++ b/adafruit_seesaw/tftshield18.py @@ -0,0 +1,122 @@ +# SPDX-FileCopyrightText: 2018 Dean Miller for Adafruit Industries +# +# SPDX-License-Identifier: MIT + + +""" +`adafruit_seesaw.tftshield18` - Pin definitions for 1.8" TFT Shield V2 +====================================================================== +""" + +from collections import namedtuple + +import board + +try: + from micropython import const +except ImportError: + + def const(x): + return x + + +from adafruit_seesaw.seesaw import Seesaw + +__version__ = "0.0.0+auto.0" +__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_seesaw.git" + +_TIMER_BASE = const(0x08) +_TIMER_PWM = const(0x01) +_TIMER_FREQ = const(0x02) + +_TFTSHIELD_RESET_PIN = const(3) + +_BUTTON_UP = const(5) +_BUTTON_DOWN = const(8) +_BUTTON_LEFT = const(6) +_BUTTON_RIGHT = const(9) +_BUTTON_SELECT = const(7) +_BUTTON_A = const(10) +_BUTTON_B = const(11) +_BUTTON_C = const(14) + +Buttons = namedtuple("Buttons", "right down left up select a b c") + + +class TFTShield18(Seesaw): + _BACKLIGHT_ON = b"\xff\xff" + _BACKLIGHT_OFF = b"\x00\x00" + + try: + _button_mask = ( + (1 << _BUTTON_RIGHT) + | (1 << _BUTTON_DOWN) + | (1 << _BUTTON_LEFT) + | (1 << _BUTTON_UP) + | (1 << _BUTTON_SELECT) + | (1 << _BUTTON_A) + | (1 << _BUTTON_B) + | (1 << _BUTTON_C) + ) + except TypeError: + # During Sphinx build, the following error occurs: + # File ".../tftshield18.py", line 60, in TFTShield18 + # (1 << _BUTTON_B) | + # TypeError: unsupported operand type(s) for <<: 'int' and '_MockObject' + _button_mask = 0xFF + + def __init__(self, i2c_bus=None, addr=0x2E): + if i2c_bus is None: + try: + i2c_bus = board.I2C() + except AttributeError as attrError: + raise ValueError("Board has no default I2C bus.") from attrError + super().__init__(i2c_bus, addr) + self.pin_mode(_TFTSHIELD_RESET_PIN, self.OUTPUT) + self.pin_mode_bulk(self._button_mask, self.INPUT_PULLUP) + + def set_backlight(self, value): + """ + Set the backlight on + """ + if not isinstance(value, bool): + raise ValueError("Value must be of boolean type") + command = self._BACKLIGHT_ON if value else self._BACKLIGHT_OFF + self.write(_TIMER_BASE, _TIMER_PWM, b"\x00" + command) + + def set_backlight_freq(self, freq): + """ + Set the backlight frequency of the TFT Display + """ + if not isinstance(freq, int): + raise ValueError("Value must be of integer type") + value = b"\x00" + bytearray((freq >> 8) & 0xFF, freq & 0xFF) + self.write(_TIMER_BASE, _TIMER_FREQ, value) + + def tft_reset(self, rst=True): + """ + Reset the TFT Display + """ + self.digital_write(_TFTSHIELD_RESET_PIN, rst) + + @property + def buttons(self): + """ + Return a set of buttons with current push values + """ + button_values = self.digital_read_bulk(self._button_mask) + return Buttons( + *[ + not button_values & (1 << button) + for button in ( + _BUTTON_RIGHT, + _BUTTON_DOWN, + _BUTTON_LEFT, + _BUTTON_UP, + _BUTTON_SELECT, + _BUTTON_A, + _BUTTON_B, + _BUTTON_C, + ) + ] + ) diff --git a/docs/_static/favicon.ico.license b/docs/_static/favicon.ico.license new file mode 100644 index 0000000..86a3fbf --- /dev/null +++ b/docs/_static/favicon.ico.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2018 Phillip Torrone for Adafruit Industries + +SPDX-License-Identifier: CC-BY-4.0 diff --git a/docs/api.rst b/docs/api.rst index 88fde53..a694a79 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -1,5 +1,44 @@ .. If you created a package, create one automodule per module in the package. +API Reference +############# + +.. automodule:: adafruit_seesaw + :members: + .. automodule:: adafruit_seesaw.seesaw :members: + +.. automodule:: adafruit_seesaw.crickit + :members: + +.. automodule:: adafruit_seesaw.analoginput + :members: + +.. automodule:: adafruit_seesaw.digitalio + :members: + +.. automodule:: adafruit_seesaw.__init__ + :members: + +.. automodule:: adafruit_seesaw.keypad + :members: + +.. automodule:: adafruit_seesaw.neopixel + :members: + +.. automodule:: adafruit_seesaw.pwmout + :members: + +.. automodule:: adafruit_seesaw.robohat + :members: + +.. automodule:: adafruit_seesaw.rotaryio + :members: + +.. automodule:: adafruit_seesaw.samd09 + :members: + +.. automodule:: adafruit_seesaw.tftshield18 + :members: diff --git a/docs/api.rst.license b/docs/api.rst.license new file mode 100644 index 0000000..9aae48d --- /dev/null +++ b/docs/api.rst.license @@ -0,0 +1,3 @@ +# SPDX-FileCopyrightText: 2020 ladyada for Adafruit Industries +# +# SPDX-License-Identifier: MIT diff --git a/docs/conf.py b/docs/conf.py index 20732c4..2d5b517 100755 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,8 +1,12 @@ -# -*- coding: utf-8 -*- +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# +# SPDX-License-Identifier: MIT +import datetime import os import sys -sys.path.insert(0, os.path.abspath('..')) + +sys.path.insert(0, os.path.abspath("..")) # -- General configuration ------------------------------------------------ @@ -10,51 +14,80 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.intersphinx', - 'sphinx.ext.viewcode', + "sphinx.ext.autodoc", + "sphinxcontrib.jquery", + "sphinx.ext.intersphinx", + "sphinx.ext.viewcode", ] # Uncomment the below if you use native CircuitPython modules such as # digitalio, micropython and busio. List the modules you use. Without it, the # autodoc module docs will fail to generate with a warning. -autodoc_mock_imports = [] +autodoc_mock_imports = [ + "adafruit_bus_device", + "adafruit_pixelbuf", + "board", + "digitalio", +] -intersphinx_mapping = {'python': ('https://docs.python.org/3.4', None),'BusDevice': ('https://circuitpython.readthedocs.io/projects/busdevice/en/latest/', None),'Register': ('https://circuitpython.readthedocs.io/projects/register/en/latest/', None),'CircuitPython': ('https://circuitpython.readthedocs.io/en/latest/', None)} +autodoc_default_flags = ["special-members", "members"] + +autodoc_default_options = { + "member-order": "bysource", + "exclude-members": "__weakref__,__getnewargs__,__dict__,__module__,__init__", +} + +intersphinx_mapping = { + "python": ("https://docs.python.org/3", None), + "BusDevice": ( + "https://docs.circuitpython.org/projects/busdevice/en/latest/", + None, + ), + "Register": ( + "https://docs.circuitpython.org/projects/register/en/latest/", + None, + ), + "CircuitPython": ("https://docs.circuitpython.org/en/latest/", None), +} # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] -source_suffix = '.rst' +source_suffix = ".rst" # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = u'Adafruit SEESAW Library' -copyright = u'2017 Dean Miller' -author = u'Dean Miller' +project = "Adafruit seesaw Library" +creation_year = "2017" +current_year = str(datetime.datetime.now().year) +year_duration = ( + current_year if current_year == creation_year else creation_year + " - " + current_year +) +copyright = year_duration + " Dean Miller" +author = "Dean Miller" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = u'1.0' +version = "1.0" # The full version, including alpha/beta/rc tags. -release = u'1.0' +release = "1.0" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = None +language = "en" # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', '.env', 'CODE_OF_CONDUCT.md'] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", ".env", "CODE_OF_CONDUCT.md"] # The reST default role (used for this markup: `text`) to use for all # documents. @@ -66,7 +99,7 @@ add_function_parentheses = True # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False @@ -80,59 +113,52 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -on_rtd = os.environ.get('READTHEDOCS', None) == 'True' - -if not on_rtd: # only import and set the theme if we're building docs locally - try: - import sphinx_rtd_theme - html_theme = 'sphinx_rtd_theme' - html_theme_path = [sphinx_rtd_theme.get_html_theme_path(), '.'] - except: - html_theme = 'default' - html_theme_path = ['.'] -else: - html_theme_path = ['.'] +import sphinx_rtd_theme + +html_theme = "sphinx_rtd_theme" # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] # The name of an image file (relative to this directory) to use as a favicon of # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # -html_favicon = '_static/favicon.ico' +html_favicon = "_static/favicon.ico" # Output file base name for HTML help builder. -htmlhelp_basename = 'AdafruitSEESAWLibrarydoc' +htmlhelp_basename = "AdafruitseesawLibrarydoc" # -- Options for LaTeX output --------------------------------------------- latex_elements = { - # The paper size ('letterpaper' or 'a4paper'). - # - # 'papersize': 'letterpaper', - - # The font size ('10pt', '11pt' or '12pt'). - # - # 'pointsize': '10pt', - - # Additional stuff for the LaTeX preamble. - # - # 'preamble': '', - - # Latex figure (float) alignment - # - # 'figure_align': 'htbp', + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'AdafruitSEESAWLibrary.tex', u'Adafruit SEESAW Library Documentation', - author, 'manual'), + ( + master_doc, + "Adafruitseesawibrary.tex", + "Adafruit seesaw Library Documentation", + author, + "manual", + ), ] # -- Options for manual page output --------------------------------------- @@ -140,8 +166,13 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - (master_doc, 'adafruitSEESAWlibrary', u'Adafruit SEESAW Library Documentation', - [author], 1) + ( + master_doc, + "adafruitseesawlibrary", + "Adafruit seesaw Library Documentation", + [author], + 1, + ) ] # -- Options for Texinfo output ------------------------------------------- @@ -150,7 +181,13 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'AdafruitSEESAWLibrary', u'Adafruit SEESAW Library Documentation', - author, 'AdafruitSEESAWLibrary', 'One line description of project.', - 'Miscellaneous'), + ( + master_doc, + "AdafruitSEESAWLibrary", + "Adafruit SEESAW Library Documentation", + author, + "AdafruitSEESAWLibrary", + "One line description of project.", + "Miscellaneous", + ), ] diff --git a/docs/examples.rst b/docs/examples.rst index 3b2e7d0..f5912d7 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -6,3 +6,32 @@ Ensure your device works with this simple test. .. literalinclude:: ../examples/seesaw_simpletest.py :caption: examples/seesaw_simpletest.py :linenos: + +Other Examples +--------------- + +Here are some other examples using the Seesaw library + +.. literalinclude:: ../examples/seesaw_crickit_test.py + :caption: examples/seesaw_crickit_test.py + :linenos: + +.. literalinclude:: ../examples/seesaw_joy_featherwing.py + :caption: examples/seesaw_joy_featherwing.py + :linenos: + +.. literalinclude:: ../examples/seesaw_soil_simpletest.py + :caption: examples/seesaw_soil_simpletest.py + :linenos: + +.. literalinclude:: ../examples/seesaw_minitft_featherwing.py + :caption: examples/seesaw_minitft_featherwing.py + :linenos: + +.. literalinclude:: ../examples/seesaw_rotary_simpletest.py + :caption: examples/seesaw_rotary_simpletest.py + :linenos: + +.. literalinclude:: ../examples/seesaw_rotary_neopixel.py + :caption: examples/seesaw_rotary_neopixel.py + :linenos: diff --git a/docs/examples.rst.license b/docs/examples.rst.license new file mode 100644 index 0000000..9aae48d --- /dev/null +++ b/docs/examples.rst.license @@ -0,0 +1,3 @@ +# SPDX-FileCopyrightText: 2020 ladyada for Adafruit Industries +# +# SPDX-License-Identifier: MIT diff --git a/docs/index.rst b/docs/index.rst index 27840f3..173aa7a 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -23,16 +23,41 @@ Table of Contents .. toctree:: :caption: Tutorials + Adafruit seesaw + + Make it Move with Crickit + + Joy Featherwing + + Adafruit Mini TFT with Joystick Featherwing + + Adafruit I2C QT Rotary Encoder + + Adafruit STEMMA Soil Sensor - I2C Capacitive Moisture Sensor + + Discord and Slack Connected Smart Plant with Adafruit IO Triggers + .. toctree:: :caption: Related Products Adafruit ATSAMD09 Breakout with seesaw + Adafruit CRICKIT for Circuit Playground Express + + Adafruit Joy FeatherWing for all Feathers + + Adafruit Mini Color TFT with Joystick FeatherWing + + Adafruit I2C QT Rotary Encoder with NeoPixel + + Adafruit STEMMA Soil Sensor - I2C Capacitive Moisture Sensor + .. toctree:: :caption: Other Links - Download - CircuitPython Reference Documentation + Download from GitHub + Download Library Bundle + CircuitPython Reference Documentation CircuitPython Support Forum Discord Chat Adafruit Learning System diff --git a/docs/index.rst.license b/docs/index.rst.license new file mode 100644 index 0000000..9aae48d --- /dev/null +++ b/docs/index.rst.license @@ -0,0 +1,3 @@ +# SPDX-FileCopyrightText: 2020 ladyada for Adafruit Industries +# +# SPDX-License-Identifier: MIT diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..979f568 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries +# +# SPDX-License-Identifier: Unlicense + +sphinx +sphinxcontrib-jquery +sphinx-rtd-theme diff --git a/examples/seesaw_analogin_test.py b/examples/seesaw_analogin_test.py new file mode 100644 index 0000000..52cbed3 --- /dev/null +++ b/examples/seesaw_analogin_test.py @@ -0,0 +1,24 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Simple seesaw test reading analog value +# on SAMD09, analog in can be pins 2, 3, or 4 +# on Attiny8x7, analog in can be pins 0, 1, 2, 3, 6, 7, 18, 19, 20 + +import time + +import board + +from adafruit_seesaw.analoginput import AnalogInput +from adafruit_seesaw.seesaw import Seesaw + +i2c = board.I2C() # uses board.SCL and board.SDA +# i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller +ss = Seesaw(i2c) + +analogin_pin = 2 +analog_in = AnalogInput(ss, analogin_pin) + +while True: + print(analog_in.value) + time.sleep(0.1) diff --git a/examples/seesaw_ano_rotary_7segment_demo.py b/examples/seesaw_ano_rotary_7segment_demo.py new file mode 100644 index 0000000..862d92b --- /dev/null +++ b/examples/seesaw_ano_rotary_7segment_demo.py @@ -0,0 +1,68 @@ +# SPDX-FileCopyrightText: 2021 John Furcean +# SPDX-License-Identifier: MIT + +"""I2C ANO rotary encoder with 7 segment display example.""" + +import board +from adafruit_ht16k33 import segments + +from adafruit_seesaw import digitalio, rotaryio, seesaw + +# For use with the STEMMA connector on QT Py RP2040 +# import busio +# i2c = busio.I2C(board.SCL1, board.SDA1) +# seesaw = seesaw.Seesaw(i2c, 0x49) + +i2c = board.I2C() # uses board.SCL and board.SDA +# i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller +seesaw = seesaw.Seesaw(i2c, addr=0x49) +display = segments.Seg14x4(i2c, address=0x70) + +seesaw_product = (seesaw.get_version() >> 16) & 0xFFFF +print(f"Found product {seesaw_product}") +if seesaw_product != 5740: + print("Wrong firmware loaded? Expected 5740") + +seesaw.pin_mode(1, seesaw.INPUT_PULLUP) +seesaw.pin_mode(2, seesaw.INPUT_PULLUP) +seesaw.pin_mode(3, seesaw.INPUT_PULLUP) +seesaw.pin_mode(4, seesaw.INPUT_PULLUP) +seesaw.pin_mode(5, seesaw.INPUT_PULLUP) + +select = digitalio.DigitalIO(seesaw, 1) +select_held = False +up = digitalio.DigitalIO(seesaw, 2) +up_held = False +left = digitalio.DigitalIO(seesaw, 3) +left_held = False +down = digitalio.DigitalIO(seesaw, 4) +down_held = False +right = digitalio.DigitalIO(seesaw, 5) +right_held = False + +encoder = rotaryio.IncrementalEncoder(seesaw) +last_position = None + +buttons = [select, up, left, down, right] +button_names = ["Select", "Up", "Left", "Down", "Right"] +button_states = [select_held, up_held, left_held, down_held, right_held] +seven_segment_names = ["SELE", " UP ", "LEFT", "DOWN", "RIGH"] + +while True: + position = encoder.position + + if position != last_position: + last_position = position + display.print(f" {position}") + print(f"Position: {position}") + + for b in range(5): + if not buttons[b].value and button_states[b] is False: + button_states[b] = True + display.print(seven_segment_names[b]) + print(f"{button_names[b]} button pressed") + + if buttons[b].value and button_states[b] is True: + button_states[b] = False + display.print(" ") + print(f"{button_names[b]} button released") diff --git a/examples/seesaw_ano_rotary_simpletest.py b/examples/seesaw_ano_rotary_simpletest.py new file mode 100644 index 0000000..911c456 --- /dev/null +++ b/examples/seesaw_ano_rotary_simpletest.py @@ -0,0 +1,62 @@ +# SPDX-FileCopyrightText: 2021 John Furcean +# SPDX-License-Identifier: MIT + +"""I2C ANO rotary encoder simple test example.""" + +import board + +from adafruit_seesaw import digitalio, rotaryio, seesaw + +# For use with the STEMMA connector on QT Py RP2040 +# import busio +# i2c = busio.I2C(board.SCL1, board.SDA1) +# seesaw = seesaw.Seesaw(i2c, 0x49) + +i2c = board.I2C() # uses board.SCL and board.SDA +# i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller +seesaw = seesaw.Seesaw(i2c, addr=0x49) + +seesaw_product = (seesaw.get_version() >> 16) & 0xFFFF +print(f"Found product {seesaw_product}") +if seesaw_product != 5740: + print("Wrong firmware loaded? Expected 5740") + +seesaw.pin_mode(1, seesaw.INPUT_PULLUP) +seesaw.pin_mode(2, seesaw.INPUT_PULLUP) +seesaw.pin_mode(3, seesaw.INPUT_PULLUP) +seesaw.pin_mode(4, seesaw.INPUT_PULLUP) +seesaw.pin_mode(5, seesaw.INPUT_PULLUP) + +select = digitalio.DigitalIO(seesaw, 1) +select_held = False +up = digitalio.DigitalIO(seesaw, 2) +up_held = False +left = digitalio.DigitalIO(seesaw, 3) +left_held = False +down = digitalio.DigitalIO(seesaw, 4) +down_held = False +right = digitalio.DigitalIO(seesaw, 5) +right_held = False + +encoder = rotaryio.IncrementalEncoder(seesaw) +last_position = None + +buttons = [select, up, left, down, right] +button_names = ["Select", "Up", "Left", "Down", "Right"] +button_states = [select_held, up_held, left_held, down_held, right_held] + +while True: + position = encoder.position + + if position != last_position: + last_position = position + print(f"Position: {position}") + + for b in range(5): + if not buttons[b].value and button_states[b] is False: + button_states[b] = True + print(f"{button_names[b]} button pressed") + + if buttons[b].value and button_states[b] is True: + button_states[b] = False + print(f"{button_names[b]} button released") diff --git a/examples/seesaw_arcade_qt_multi_board.py b/examples/seesaw_arcade_qt_multi_board.py new file mode 100644 index 0000000..0bf2264 --- /dev/null +++ b/examples/seesaw_arcade_qt_multi_board.py @@ -0,0 +1,44 @@ +# SPDX-FileCopyrightText: 2022 Kattni Rembor for Adafruit Industries +# SPDX-License-Identifier: MIT +"""Arcade QT example for multiple boards that turns on button LED when button is pressed""" + +import board +import digitalio + +from adafruit_seesaw.digitalio import DigitalIO +from adafruit_seesaw.seesaw import Seesaw + +# For most boards. +i2c = board.I2C() # uses board.SCL and board.SDA +# i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller + +# For the QT Py RP2040, QT Py ESP32-S2, other boards that have SCL1/SDA1 as the STEMMA QT port. +# import busio +# i2c = busio.I2C(board.SCL1, board.SDA1) +arcade_qt_one = Seesaw(i2c, addr=0x3A) +arcade_qt_two = Seesaw(i2c, addr=0x3B) + +arcade_qts = (arcade_qt_one, arcade_qt_two) + +# Button pins in order (1, 2, 3, 4) +button_pins = (18, 19, 20, 2) +buttons = [] +for arcade_qt in arcade_qts: + for button_pin in button_pins: + button = DigitalIO(arcade_qt, button_pin) + button.direction = digitalio.Direction.INPUT + button.pull = digitalio.Pull.UP + buttons.append(button) + +# LED pins in order (1, 2, 3, 4) +led_pins = (12, 13, 0, 1) +leds = [] +for arcade_qt in arcade_qts: + for led_pin in led_pins: + led = DigitalIO(arcade_qt, led_pin) + led.direction = digitalio.Direction.OUTPUT + leds.append(led) + +while True: + for led_number, button in enumerate(buttons): + leds[led_number].value = not button.value diff --git a/examples/seesaw_arcade_qt_simpletest.py b/examples/seesaw_arcade_qt_simpletest.py new file mode 100644 index 0000000..29c0774 --- /dev/null +++ b/examples/seesaw_arcade_qt_simpletest.py @@ -0,0 +1,50 @@ +# SPDX-FileCopyrightText: 2022 Kattni Rembor for Adafruit Industries +# SPDX-License-Identifier: MIT +"""Arcade QT example that pulses the button LED on button press""" + +import time + +import board +import digitalio + +from adafruit_seesaw.digitalio import DigitalIO +from adafruit_seesaw.pwmout import PWMOut +from adafruit_seesaw.seesaw import Seesaw + +# The delay on the PWM cycles. Increase to slow down the LED pulsing, decrease to speed it up. +delay = 0.01 + +# For most boards. +i2c = board.I2C() # uses board.SCL and board.SDA +# i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller + +# For the QT Py RP2040, QT Py ESP32-S2, other boards that have SCL1/SDA1 as the STEMMA QT port. +# import busio +# i2c = busio.I2C(board.SCL1, board.SDA1) +arcade_qt = Seesaw(i2c, addr=0x3A) + +# Button pins in order (1, 2, 3, 4) +button_pins = (18, 19, 20, 2) +buttons = [] +for button_pin in button_pins: + button = DigitalIO(arcade_qt, button_pin) + button.direction = digitalio.Direction.INPUT + button.pull = digitalio.Pull.UP + buttons.append(button) + +# LED pins in order (1, 2, 3, 4) +led_pins = (12, 13, 0, 1) +leds = [] +for led_pin in led_pins: + led = PWMOut(arcade_qt, led_pin) + leds.append(led) + +while True: + for led_number, button in enumerate(buttons): + if not button.value: + for cycle in range(0, 65535, 8000): + leds[led_number].duty_cycle = cycle + time.sleep(delay) + for cycle in range(65534, 0, -8000): + leds[led_number].duty_cycle = cycle + time.sleep(delay) diff --git a/examples/seesaw_attiny_simpletest.py b/examples/seesaw_attiny_simpletest.py new file mode 100644 index 0000000..31dfb28 --- /dev/null +++ b/examples/seesaw_attiny_simpletest.py @@ -0,0 +1,23 @@ +# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries +# SPDX-License-Identifier: MIT +""" +Simple seesaw test for ATtiny8x7 breakout using built-in LED on pin 5. +""" + +import time + +import board + +from adafruit_seesaw.seesaw import Seesaw + +i2c = board.I2C() # uses board.SCL and board.SDA +# i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller +ss = Seesaw(i2c) + +ss.pin_mode(5, ss.OUTPUT) + +while True: + ss.digital_write(5, False) # Turn the LED on (the built-in LED is active low!) + time.sleep(1) # Wait for one second + ss.digital_write(5, True) # Turn the LED off + time.sleep(1) # Wait for one second diff --git a/examples/crickit_test.py b/examples/seesaw_crickit_test.py similarity index 81% rename from examples/crickit_test.py rename to examples/seesaw_crickit_test.py index 94887a4..b93d1c8 100644 --- a/examples/crickit_test.py +++ b/examples/seesaw_crickit_test.py @@ -1,13 +1,17 @@ -from board import SCL, SDA -import busio -from adafruit_seesaw.seesaw import Seesaw -from adafruit_seesaw.pwmout import PWMOut +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +import board from adafruit_motor import servo -#from analogio import AnalogOut -#import board +from adafruit_seesaw.pwmout import PWMOut +from adafruit_seesaw.seesaw import Seesaw -i2c_bus = busio.I2C(SCL, SDA) +# from analogio import AnalogOut +# import board + +i2c_bus = board.I2C() # uses board.SCL and board.SDA +# i2c_bus = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller ss = Seesaw(i2c_bus) pwm1 = PWMOut(ss, 17) pwm2 = PWMOut(ss, 16) @@ -53,8 +57,8 @@ counter = 0 -#analog_out = AnalogOut(board.A0) -#analog_out.value = 512 +# analog_out = AnalogOut(board.A0) +# analog_out.value = 512 while True: counter = (counter + 1) % 256 @@ -63,12 +67,11 @@ print("-------------------- analog -----------------------") str_out = "" for i in range(8): - val = ss.analog_read(CRCKit_adc[i]) * 3.3/1024 + val = ss.analog_read(CRCKit_adc[i]) * 3.3 / 1024 str_out = str_out + str(round(val, 2)) + "\t" print(str_out + "\n") - for i in range(4): val = ss.touch_read(i) cap_justtouched[i] = False @@ -116,7 +119,6 @@ else: print("Stopping speaker") - if test_servos: if counter % 32 == 0: print("-------------------- servos -----------------------") @@ -129,7 +131,6 @@ print("SER" + str(servonum) + " RIGHT") servos[servonum].angle = 180 - if test_drives: if counter % 32 == 0: print("-------------------- drives -----------------------") @@ -151,13 +152,12 @@ else: ss.analog_write(_CRCKIT_M1_A2, 0) ss.analog_write(_CRCKIT_M1_A1, counter * 512) + elif motor1_dir: + ss.analog_write(_CRCKIT_M1_A1, 0) + ss.analog_write(_CRCKIT_M1_A2, (255 - counter) * 512) else: - if motor1_dir: - ss.analog_write(_CRCKIT_M1_A1, 0) - ss.analog_write(_CRCKIT_M1_A2, (255-counter) * 512) - else: - ss.analog_write(_CRCKIT_M1_A2, 0) - ss.analog_write(_CRCKIT_M1_A1, (255-counter) * 512) + ss.analog_write(_CRCKIT_M1_A2, 0) + ss.analog_write(_CRCKIT_M1_A1, (255 - counter) * 512) if counter == 255: print("-------------------- motor 1 -----------------------") motor1_dir = not motor1_dir @@ -169,13 +169,12 @@ else: ss.analog_write(_CRCKIT_M1_B2, 0) ss.analog_write(_CRCKIT_M1_B1, counter * 512) + elif motor2_dir: + ss.analog_write(_CRCKIT_M1_B1, 0) + ss.analog_write(_CRCKIT_M1_B2, (255 - counter) * 512) else: - if motor2_dir: - ss.analog_write(_CRCKIT_M1_B1, 0) - ss.analog_write(_CRCKIT_M1_B2, (255-counter) * 512) - else: - ss.analog_write(_CRCKIT_M1_B2, 0) - ss.analog_write(_CRCKIT_M1_B1, (255-counter) * 512) + ss.analog_write(_CRCKIT_M1_B2, 0) + ss.analog_write(_CRCKIT_M1_B1, (255 - counter) * 512) if counter == 255: print("-------------------- motor 2 -----------------------") motor2_dir = not motor2_dir diff --git a/examples/seesaw_digitalio_test.py b/examples/seesaw_digitalio_test.py new file mode 100644 index 0000000..f2a4819 --- /dev/null +++ b/examples/seesaw_digitalio_test.py @@ -0,0 +1,37 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Simple seesaw test using an LED attached to Pin 5 and a button on pin 2 +# +# See the seesaw Learn Guide for wiring details. +# For SAMD09: +# https://learn.adafruit.com/adafruit-seesaw-atsamd09-breakout?view=all#circuitpython-wiring-and-test +# For ATtiny8x7: +# https://learn.adafruit.com/adafruit-attiny817-seesaw/digital-input + +import time + +import board +import digitalio + +from adafruit_seesaw.digitalio import DigitalIO +from adafruit_seesaw.seesaw import Seesaw + +i2c = board.I2C() # uses board.SCL and board.SDA +# i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller +ss = Seesaw(i2c) + +button_pin = 2 +led_pin = 5 + +button = DigitalIO(ss, button_pin) +button.direction = digitalio.Direction.INPUT +button.pull = digitalio.Pull.UP + +led = DigitalIO(ss, led_pin) +led.direction = digitalio.Direction.OUTPUT + +while True: + # simply set the LED to the same 'value' as the button pin + led.value = button.value + time.sleep(0.1) diff --git a/examples/seesaw_eeprom_test.py b/examples/seesaw_eeprom_test.py new file mode 100644 index 0000000..ae8cb5e --- /dev/null +++ b/examples/seesaw_eeprom_test.py @@ -0,0 +1,29 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Simple seesaw test reading and writing the internal EEPROM +# The ATtiny8xx series has a true 128 byte EEPROM, the SAMD09 mimics it in flash with 64 bytes +# THE LAST BYTE IS USED FOR I2C ADDRESS CHANGE! + +import time + +import board + +from adafruit_seesaw import seesaw + +i2c_bus = board.I2C() # uses board.SCL and board.SDA +# i2c_bus = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller +ss = seesaw.Seesaw(i2c_bus) + +value = ss.eeprom_read8(0x02) # Read from address 2 +print("Read 0x%02x from EEPROM address 0x02" % value) + +print("Incrementing value") +ss.eeprom_write8(0x02, (value + 1) % 0xFF) + +value = ss.eeprom_read8(0x02) # Read from address 2 +print("Second read 0x%02x from EEPROM address 0x02" % value) + +while True: + # Do not write EEPROM in a loop, it has 100k cycle life + time.sleep(1) diff --git a/examples/seesaw_gamepad_qt.py b/examples/seesaw_gamepad_qt.py new file mode 100644 index 0000000..adf9c4a --- /dev/null +++ b/examples/seesaw_gamepad_qt.py @@ -0,0 +1,67 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-FileCopyrightText: 2023 Kattni Rembor for Adafruit Industries + +# SPDX-License-Identifier: MIT + +import time + +import board +from micropython import const + +from adafruit_seesaw.seesaw import Seesaw + +BUTTON_X = const(6) +BUTTON_Y = const(2) +BUTTON_A = const(5) +BUTTON_B = const(1) +BUTTON_SELECT = const(0) +BUTTON_START = const(16) +button_mask = const( + (1 << BUTTON_X) + | (1 << BUTTON_Y) + | (1 << BUTTON_A) + | (1 << BUTTON_B) + | (1 << BUTTON_SELECT) + | (1 << BUTTON_START) +) + +i2c_bus = board.STEMMA_I2C() # The built-in STEMMA QT connector on the microcontroller +# i2c_bus = board.I2C() # Uses board.SCL and board.SDA. Use with breadboard. + +seesaw = Seesaw(i2c_bus, addr=0x50) + +seesaw.pin_mode_bulk(button_mask, seesaw.INPUT_PULLUP) + +last_x = 0 +last_y = 0 + +while True: + x = 1023 - seesaw.analog_read(14) + y = 1023 - seesaw.analog_read(15) + + if (abs(x - last_x) > 3) or (abs(y - last_y) > 3): + print(x, y) + last_x = x + last_y = y + + buttons = seesaw.digital_read_bulk(button_mask) + + if not buttons & (1 << BUTTON_X): + print("Button x pressed") + + if not buttons & (1 << BUTTON_Y): + print("Button Y pressed") + + if not buttons & (1 << BUTTON_A): + print("Button A pressed") + + if not buttons & (1 << BUTTON_B): + print("Button B pressed") + + if not buttons & (1 << BUTTON_SELECT): + print("Button Select pressed") + + if not buttons & (1 << BUTTON_START): + print("Button Start pressed") + + time.sleep(0.01) diff --git a/examples/Joy_Featherwing.py b/examples/seesaw_joy_featherwing.py similarity index 57% rename from examples/Joy_Featherwing.py rename to examples/seesaw_joy_featherwing.py index 7eb1a36..952051d 100755 --- a/examples/Joy_Featherwing.py +++ b/examples/seesaw_joy_featherwing.py @@ -1,25 +1,28 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + import time -from board import SCL, SDA -import busio +import board from micropython import const from adafruit_seesaw.seesaw import Seesaw -# pylint: disable=bad-whitespace BUTTON_RIGHT = const(6) -BUTTON_DOWN = const(7) -BUTTON_LEFT = const(9) -BUTTON_UP = const(10) -BUTTON_SEL = const(14) -# pylint: enable=bad-whitespace -button_mask = const((1 << BUTTON_RIGHT) | - (1 << BUTTON_DOWN) | - (1 << BUTTON_LEFT) | - (1 << BUTTON_UP) | - (1 << BUTTON_SEL)) - -i2c_bus = busio.I2C(SCL, SDA) +BUTTON_DOWN = const(7) +BUTTON_LEFT = const(9) +BUTTON_UP = const(10) +BUTTON_SEL = const(14) +button_mask = const( + (1 << BUTTON_RIGHT) + | (1 << BUTTON_DOWN) + | (1 << BUTTON_LEFT) + | (1 << BUTTON_UP) + | (1 << BUTTON_SEL) +) + +i2c_bus = board.I2C() # uses board.SCL and board.SDA +# i2c_bus = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller ss = Seesaw(i2c_bus) @@ -32,7 +35,7 @@ x = ss.analog_read(2) y = ss.analog_read(3) - if (abs(x - last_x) > 3) or (abs(y - last_y) > 3): + if (abs(x - last_x) > 3) or (abs(y - last_y) > 3): print(x, y) last_x = x last_y = y @@ -53,4 +56,4 @@ if not buttons & (1 << BUTTON_SEL): print("Button SEL pressed") - time.sleep(.01) + time.sleep(0.01) diff --git a/examples/seesaw_minitft_featherwing.py b/examples/seesaw_minitft_featherwing.py new file mode 100644 index 0000000..1103e98 --- /dev/null +++ b/examples/seesaw_minitft_featherwing.py @@ -0,0 +1,59 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +import time + +import board +from micropython import const + +from adafruit_seesaw.seesaw import Seesaw + +BUTTON_RIGHT = const(7) +BUTTON_DOWN = const(4) +BUTTON_LEFT = const(3) +BUTTON_UP = const(2) +BUTTON_SEL = const(11) +BUTTON_A = const(10) +BUTTON_B = const(9) + +button_mask = const( + (1 << BUTTON_RIGHT) + | (1 << BUTTON_DOWN) + | (1 << BUTTON_LEFT) + | (1 << BUTTON_UP) + | (1 << BUTTON_SEL) + | (1 << BUTTON_A) + | (1 << BUTTON_B) +) + +i2c_bus = board.I2C() # uses board.SCL and board.SDA +# i2c_bus = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller + +ss = Seesaw(i2c_bus, 0x5E) + +ss.pin_mode_bulk(button_mask, ss.INPUT_PULLUP) + +while True: + buttons = ss.digital_read_bulk(button_mask) + if not buttons & (1 << BUTTON_RIGHT): + print("Button RIGHT pressed") + + if not buttons & (1 << BUTTON_DOWN): + print("Button DOWN pressed") + + if not buttons & (1 << BUTTON_LEFT): + print("Button LEFT pressed") + + if not buttons & (1 << BUTTON_UP): + print("Button UP pressed") + + if not buttons & (1 << BUTTON_SEL): + print("Button SEL pressed") + + if not buttons & (1 << BUTTON_A): + print("Button A pressed") + + if not buttons & (1 << BUTTON_B): + print("Button B pressed") + + time.sleep(0.01) diff --git a/examples/seesaw_neopixel_test.py b/examples/seesaw_neopixel_test.py new file mode 100644 index 0000000..4fbb107 --- /dev/null +++ b/examples/seesaw_neopixel_test.py @@ -0,0 +1,38 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Simple seesaw test writing NeoPixels +# Can use any valid GPIO pin, up to 60 pixels! +# +# See the seesaw Learn Guide for wiring details. +# For SAMD09: +# https://learn.adafruit.com/adafruit-seesaw-atsamd09-breakout?view=all#circuitpython-wiring-and-test +# For ATtiny8x7: +# https://learn.adafruit.com/adafruit-attiny817-seesaw/neopixel + +import time + +import board +from rainbowio import colorwheel + +from adafruit_seesaw import neopixel, seesaw + +i2c = board.I2C() # uses board.SCL and board.SDA +# i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller +ss = seesaw.Seesaw(i2c) + +NEOPIXEL_PIN = 19 # Can be any pin +NEOPIXEL_NUM = 12 # No more than 60 pixels! + +pixels = neopixel.NeoPixel(ss, NEOPIXEL_PIN, NEOPIXEL_NUM) +pixels.brightness = 0.3 # Not so bright! + +color_offset = 0 # Start at red + +# Cycle through all colors along the ring +while True: + for i in range(NEOPIXEL_NUM): + rc_index = (i * 256 // NEOPIXEL_NUM) + color_offset + pixels[i] = colorwheel(rc_index & 255) + color_offset += 1 + time.sleep(0.01) diff --git a/examples/seesaw_pc_joystick.py b/examples/seesaw_pc_joystick.py new file mode 100644 index 0000000..3c5735a --- /dev/null +++ b/examples/seesaw_pc_joystick.py @@ -0,0 +1,72 @@ +# SPDX-FileCopyrightText: 2023 Limor Fried for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +import time + +import board +from micropython import const + +from adafruit_seesaw.seesaw import Seesaw + +BUTTON_1 = const(3) +BUTTON_2 = const(13) +BUTTON_3 = const(2) +BUTTON_4 = const(14) + +JOY1_X = const(1) +JOY1_Y = const(15) +JOY2_X = const(0) +JOY2_Y = const(16) + +button_mask = const((1 << BUTTON_1) | (1 << BUTTON_2) | (1 << BUTTON_3) | (1 << BUTTON_4)) + +i2c_bus = board.STEMMA_I2C() # The built-in STEMMA QT connector on the microcontroller +# i2c_bus = board.I2C() # Uses board.SCL and board.SDA. Use with breadboard. + +seesaw = Seesaw(i2c_bus, addr=0x49) + +seesaw.pin_mode_bulk(button_mask, seesaw.INPUT_PULLUP) + +last_x = 0 +last_y = 0 +x = 0 +y = 0 + +while True: + # These joysticks are really jittery so let's take 4 samples of each axis + for i in range(4): + x += seesaw.analog_read(JOY1_X) + y += seesaw.analog_read(JOY1_Y) + + # take average reading + x /= 4 + y /= 4 + + # PC joysticks aren't true voltage divider because we have a fixed 10K + # we dont know the normalized value so we're just going to give you + # the result in 'Kohms' for easier printing + + x = 1024 / x - 1 + y = 1024 / y - 1 + + if (abs(x - last_x) > 3) or (abs(y - last_y) > 3): + print(x, y) + last_x = x + last_y = y + + buttons = seesaw.digital_read_bulk(button_mask) + + if not buttons & (1 << BUTTON_1): + print("Button 1 pressed") + + if not buttons & (1 << BUTTON_2): + print("Button 2 pressed") + + if not buttons & (1 << BUTTON_3): + print("Button 3 pressed") + + if not buttons & (1 << BUTTON_4): + print("Button 4 pressed") + + time.sleep(0.01) diff --git a/examples/seesaw_pwmout_test.py b/examples/seesaw_pwmout_test.py new file mode 100644 index 0000000..ac98646 --- /dev/null +++ b/examples/seesaw_pwmout_test.py @@ -0,0 +1,36 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Simple seesaw test for writing PWM outputs +# On the SAMD09 breakout these are pins 5, 6, and 7 +# On the ATtiny8x7 breakout these are pins 0, 1, 9, 12, 13 +# +# See the seesaw Learn Guide for wiring details. +# For SAMD09: +# https://learn.adafruit.com/adafruit-seesaw-atsamd09-breakout?view=all#circuitpython-wiring-and-test +# For ATtiny8x7: +# https://learn.adafruit.com/adafruit-attiny817-seesaw/pwmout + +import time + +import board + +from adafruit_seesaw import pwmout, seesaw + +i2c = board.I2C() # uses board.SCL and board.SDA +# i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller +ss = seesaw.Seesaw(i2c) + +PWM_PIN = 12 # If desired, change to any valid PWM output! +led = pwmout.PWMOut(ss, PWM_PIN) + +delay = 0.01 +while True: + # The API PWM range is 0 to 65535, but we increment by 256 since our + # resolution is often only 8 bits underneath + for cycle in range(0, 65535, 256): + led.duty_cycle = cycle + time.sleep(delay) + for cycle in range(65534, 0, -256): + led.duty_cycle = cycle + time.sleep(delay) diff --git a/examples/seesaw_quadrotary.py b/examples/seesaw_quadrotary.py new file mode 100644 index 0000000..ca0cc2f --- /dev/null +++ b/examples/seesaw_quadrotary.py @@ -0,0 +1,54 @@ +# SPDX-FileCopyrightText: 2023 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""Quad I2C rotary encoder NeoPixel color picker example.""" + +import board +import digitalio +from rainbowio import colorwheel + +import adafruit_seesaw.digitalio +import adafruit_seesaw.neopixel +import adafruit_seesaw.rotaryio +import adafruit_seesaw.seesaw + +# For boards/chips that don't handle clock-stretching well, try running I2C at 50KHz +# import busio +# i2c = busio.I2C(board.SCL, board.SDA, frequency=50000) +# For using the built-in STEMMA QT connector on a microcontroller +i2c = board.STEMMA_I2C() +seesaw = adafruit_seesaw.seesaw.Seesaw(i2c, 0x49) + +encoders = [adafruit_seesaw.rotaryio.IncrementalEncoder(seesaw, n) for n in range(4)] +switches = [adafruit_seesaw.digitalio.DigitalIO(seesaw, pin) for pin in (12, 14, 17, 9)] +for switch in switches: + switch.switch_to_input(digitalio.Pull.UP) # input & pullup! + +# four neopixels per PCB +pixels = adafruit_seesaw.neopixel.NeoPixel(seesaw, 18, 4) +pixels.brightness = 0.5 + +last_positions = [-1, -1, -1, -1] +colors = [0, 0, 0, 0] # start at red + +while True: + # negate the position to make clockwise rotation positive + positions = [encoder.position for encoder in encoders] + print(positions) + for n, rotary_pos in enumerate(positions): + if rotary_pos != last_positions[n]: + if switches[n].value: # Change the LED color if switch is not pressed + if rotary_pos > last_positions[n]: # Advance forward through the colorwheel. + colors[n] += 8 + else: + colors[n] -= 8 # Advance backward through the colorwheel. + colors[n] = (colors[n] + 256) % 256 # wrap around to 0-256 + # Set last position to current position after evaluating + print(f"Rotary #{n}: {rotary_pos}") + last_positions[n] = rotary_pos + + # if switch is pressed, light up white, otherwise use the stored color + if not switches[n].value: + pixels[n] = 0xFFFFFF + else: + pixels[n] = colorwheel(colors[n]) diff --git a/examples/seesaw_rotary_multiples.py b/examples/seesaw_rotary_multiples.py new file mode 100644 index 0000000..2e9f21c --- /dev/null +++ b/examples/seesaw_rotary_multiples.py @@ -0,0 +1,71 @@ +# SPDX-FileCopyrightText: 2021 John Park +# SPDX-License-Identifier: MIT + +# I2C rotary encoder multiple test example. +# solder the A0 jumper on the second QT Rotary Encoder board + +import board + +from adafruit_seesaw import digitalio, neopixel, rotaryio, seesaw + +i2c = board.I2C() # uses board.SCL and board.SDA +# i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller + +qt_enc1 = seesaw.Seesaw(i2c, addr=0x36) +qt_enc2 = seesaw.Seesaw(i2c, addr=0x37) + +qt_enc1.pin_mode(24, qt_enc1.INPUT_PULLUP) +button1 = digitalio.DigitalIO(qt_enc1, 24) +button_held1 = False + +qt_enc2.pin_mode(24, qt_enc2.INPUT_PULLUP) +button2 = digitalio.DigitalIO(qt_enc2, 24) +button_held2 = False + +encoder1 = rotaryio.IncrementalEncoder(qt_enc1) +last_position1 = None + +encoder2 = rotaryio.IncrementalEncoder(qt_enc2) +last_position2 = None + +pixel1 = neopixel.NeoPixel(qt_enc1, 6, 1) +pixel1.brightness = 0.2 +pixel1.fill(0xFF0000) + +pixel2 = neopixel.NeoPixel(qt_enc2, 6, 1) +pixel2.brightness = 0.2 +pixel2.fill(0x0000FF) + + +while True: + # negate the position to make clockwise rotation positive + position1 = -encoder1.position + position2 = -encoder2.position + + if position1 != last_position1: + last_position1 = position1 + print(f"Position 1: {position1}") + + if not button1.value and not button_held1: + button_held1 = True + pixel1.brightness = 0.5 + print("Button 1 pressed") + + if button1.value and button_held1: + button_held1 = False + pixel1.brightness = 0.2 + print("Button 1 released") + + if position2 != last_position2: + last_position2 = position2 + print(f"Position 2: {position2}") + + if not button2.value and not button_held2: + button_held2 = True + pixel2.brightness = 0.5 + print("Button 2 pressed") + + if button2.value and button_held2: + button_held2 = False + pixel2.brightness = 0.2 + print("Button 2 released") diff --git a/examples/seesaw_rotary_neopixel.py b/examples/seesaw_rotary_neopixel.py new file mode 100644 index 0000000..4577a1b --- /dev/null +++ b/examples/seesaw_rotary_neopixel.py @@ -0,0 +1,51 @@ +# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""I2C rotary encoder NeoPixel color picker and brightness setting example.""" + +import board +from rainbowio import colorwheel + +from adafruit_seesaw import digitalio, neopixel, rotaryio, seesaw + +# For use with the STEMMA connector on QT Py RP2040 +# import busio +# i2c = busio.I2C(board.SCL1, board.SDA1) +# seesaw = seesaw.Seesaw(i2c, 0x36) + +i2c = board.I2C() # uses board.SCL and board.SDA +# i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller +seesaw = seesaw.Seesaw(i2c, 0x36) + +encoder = rotaryio.IncrementalEncoder(seesaw) +seesaw.pin_mode(24, seesaw.INPUT_PULLUP) +switch = digitalio.DigitalIO(seesaw, 24) + +pixel = neopixel.NeoPixel(seesaw, 6, 1) +pixel.brightness = 0.5 + +last_position = -1 +color = 0 # start at red + +while True: + # negate the position to make clockwise rotation positive + position = -encoder.position + + if position != last_position: + print(position) + + if switch.value: + # Change the LED color. + if position > last_position: # Advance forward through the colorwheel. + color += 1 + else: + color -= 1 # Advance backward through the colorwheel. + color = (color + 256) % 256 # wrap around to 0-256 + pixel.fill(colorwheel(color)) + + elif position > last_position: # Increase the brightness. + pixel.brightness = min(1.0, pixel.brightness + 0.1) + else: # Decrease the brightness. + pixel.brightness = max(0, pixel.brightness - 0.1) + + last_position = position diff --git a/examples/seesaw_rotary_simpletest.py b/examples/seesaw_rotary_simpletest.py new file mode 100644 index 0000000..3e929f9 --- /dev/null +++ b/examples/seesaw_rotary_simpletest.py @@ -0,0 +1,48 @@ +# SPDX-FileCopyrightText: 2021 John Furcean +# SPDX-License-Identifier: MIT + +"""I2C rotary encoder simple test example.""" + +import board + +from adafruit_seesaw import digitalio, rotaryio, seesaw + +# For use with the STEMMA connector on QT Py RP2040 +# import busio +# i2c = busio.I2C(board.SCL1, board.SDA1) +# seesaw = seesaw.Seesaw(i2c, 0x36) + +i2c = board.I2C() # uses board.SCL and board.SDA +# i2c = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller +seesaw = seesaw.Seesaw(i2c, addr=0x36) + +seesaw_product = (seesaw.get_version() >> 16) & 0xFFFF +print(f"Found product {seesaw_product}") +if seesaw_product != 4991: + print("Wrong firmware loaded? Expected 4991") + +# Configure seesaw pin used to read knob button presses +# The internal pull up is enabled to prevent floating input +seesaw.pin_mode(24, seesaw.INPUT_PULLUP) +button = digitalio.DigitalIO(seesaw, 24) + +button_held = False + +encoder = rotaryio.IncrementalEncoder(seesaw) +last_position = None + +while True: + # negate the position to make clockwise rotation positive + position = -encoder.position + + if position != last_position: + last_position = position + print(f"Position: {position}") + + if not button.value and not button_held: + button_held = True + print("Button pressed") + + if button.value and button_held: + button_held = False + print("Button released") diff --git a/examples/seesaw_simpletest.py b/examples/seesaw_simpletest.py index da37053..f0918f4 100644 --- a/examples/seesaw_simpletest.py +++ b/examples/seesaw_simpletest.py @@ -1,21 +1,25 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + # Simple seesaw test using an LED attached to Pin 15. # # See the seesaw Learn Guide for wiring details: # https://learn.adafruit.com/adafruit-seesaw-atsamd09-breakout?view=all#circuitpython-wiring-and-test import time -from board import SCL, SDA -import busio +import board + from adafruit_seesaw.seesaw import Seesaw -i2c_bus = busio.I2C(SCL, SDA) +i2c_bus = board.I2C() # uses board.SCL and board.SDA +# i2c_bus = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller ss = Seesaw(i2c_bus) ss.pin_mode(15, ss.OUTPUT) while True: - ss.digital_write(15, True) # turn the LED on (True is the voltage level) - time.sleep(1) # wait for a second + ss.digital_write(15, True) # turn the LED on (True is the voltage level) + time.sleep(1) # wait for a second ss.digital_write(15, False) # turn the LED off by making the voltage LOW time.sleep(1) diff --git a/examples/soil_simpletest.py b/examples/seesaw_soil_simpletest.py similarity index 57% rename from examples/soil_simpletest.py rename to examples/seesaw_soil_simpletest.py index 39c7626..735d0f5 100644 --- a/examples/soil_simpletest.py +++ b/examples/seesaw_soil_simpletest.py @@ -1,11 +1,14 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + import time -from board import SCL, SDA -import busio +import board from adafruit_seesaw.seesaw import Seesaw -i2c_bus = busio.I2C(SCL, SDA) +i2c_bus = board.I2C() # uses board.SCL and board.SDA +# i2c_bus = board.STEMMA_I2C() # For using the built-in STEMMA QT connector on a microcontroller ss = Seesaw(i2c_bus, addr=0x36) diff --git a/optional_requirements.txt b/optional_requirements.txt new file mode 100644 index 0000000..d4e27c4 --- /dev/null +++ b/optional_requirements.txt @@ -0,0 +1,3 @@ +# SPDX-FileCopyrightText: 2022 Alec Delaney, for Adafruit Industries +# +# SPDX-License-Identifier: Unlicense diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..b9ba298 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,44 @@ +# SPDX-FileCopyrightText: 2022 Alec Delaney for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +[build-system] +requires = [ + "setuptools", + "wheel", + "setuptools-scm", +] + +[project] +name = "adafruit-circuitpython-seesaw" +description = "CircuitPython library for controlling a SeeSaw helper chip." +version = "0.0.0+auto.0" +readme = "README.rst" +authors = [ + {name = "Adafruit Industries", email = "circuitpython@adafruit.com"} +] +urls = {Homepage = "https://github.com/adafruit/Adafruit_CircuitPython_seesaw"} +keywords = [ + "adafruit", + "seesaw", + "hardware", + "micropython", + "circuitpython", +] +license = {text = "MIT"} +classifiers = [ + "Intended Audience :: Developers", + "Topic :: Software Development :: Libraries", + "Topic :: Software Development :: Embedded Systems", + "Topic :: System :: Hardware", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3", +] +dynamic = ["dependencies", "optional-dependencies"] + +[tool.setuptools] +packages = ["adafruit_seesaw"] + +[tool.setuptools.dynamic] +dependencies = {file = ["requirements.txt"]} +optional-dependencies = {optional = {file = ["optional_requirements.txt"]}} diff --git a/requirements.txt b/requirements.txt index 465ff2f..03bd9cb 100755 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,7 @@ -adafruit-circuitpython-busdevice +# SPDX-FileCopyrightText: 2022 Alec Delaney, for Adafruit Industries +# +# SPDX-License-Identifier: Unlicense + Adafruit-Blinka +adafruit-circuitpython-busdevice +adafruit-circuitpython-pixelbuf diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 0000000..36332ff --- /dev/null +++ b/ruff.toml @@ -0,0 +1,105 @@ +# SPDX-FileCopyrightText: 2024 Tim Cocks for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +target-version = "py38" +line-length = 100 + +[lint] +preview = true +select = ["I", "PL", "UP"] + +extend-select = [ + "D419", # empty-docstring + "E501", # line-too-long + "W291", # trailing-whitespace + "PLC0414", # useless-import-alias + "PLC2401", # non-ascii-name + "PLC2801", # unnecessary-dunder-call + "PLC3002", # unnecessary-direct-lambda-call + "E999", # syntax-error + "PLE0101", # return-in-init + "F706", # return-outside-function + "F704", # yield-outside-function + "PLE0116", # continue-in-finally + "PLE0117", # nonlocal-without-binding + "PLE0241", # duplicate-bases + "PLE0302", # unexpected-special-method-signature + "PLE0604", # invalid-all-object + "PLE0605", # invalid-all-format + "PLE0643", # potential-index-error + "PLE0704", # misplaced-bare-raise + "PLE1141", # dict-iter-missing-items + "PLE1142", # await-outside-async + "PLE1205", # logging-too-many-args + "PLE1206", # logging-too-few-args + "PLE1307", # bad-string-format-type + "PLE1310", # bad-str-strip-call + "PLE1507", # invalid-envvar-value + "PLE2502", # bidirectional-unicode + "PLE2510", # invalid-character-backspace + "PLE2512", # invalid-character-sub + "PLE2513", # invalid-character-esc + "PLE2514", # invalid-character-nul + "PLE2515", # invalid-character-zero-width-space + "PLR0124", # comparison-with-itself + "PLR0202", # no-classmethod-decorator + "PLR0203", # no-staticmethod-decorator + "UP004", # useless-object-inheritance + "PLR0206", # property-with-parameters + "PLR0904", # too-many-public-methods + "PLR0911", # too-many-return-statements + "PLR0912", # too-many-branches + "PLR0913", # too-many-arguments + "PLR0914", # too-many-locals + "PLR0915", # too-many-statements + "PLR0916", # too-many-boolean-expressions + "PLR1702", # too-many-nested-blocks + "PLR1704", # redefined-argument-from-local + "PLR1711", # useless-return + "C416", # unnecessary-comprehension + "PLR1733", # unnecessary-dict-index-lookup + "PLR1736", # unnecessary-list-index-lookup + + # ruff reports this rule is unstable + #"PLR6301", # no-self-use + + "PLW0108", # unnecessary-lambda + "PLW0120", # useless-else-on-loop + "PLW0127", # self-assigning-variable + "PLW0129", # assert-on-string-literal + "B033", # duplicate-value + "PLW0131", # named-expr-without-context + "PLW0245", # super-without-brackets + "PLW0406", # import-self + "PLW0602", # global-variable-not-assigned + "PLW0603", # global-statement + "PLW0604", # global-at-module-level + + # fails on the try: import typing used by libraries + #"F401", # unused-import + + "F841", # unused-variable + "E722", # bare-except + "PLW0711", # binary-op-exception + "PLW1501", # bad-open-mode + "PLW1508", # invalid-envvar-default + "PLW1509", # subprocess-popen-preexec-fn + "PLW2101", # useless-with-lock + "PLW3301", # nested-min-max +] + +ignore = [ + "PLR2004", # magic-value-comparison + "UP030", # format literals + "PLW1514", # unspecified-encoding + "PLR0913", # too-many-arguments + "PLR0915", # too-many-statements + "PLR0917", # too-many-positional-arguments + "PLR0904", # too-many-public-methods + "PLR0912", # too-many-branches + "PLR0916", # too-many-boolean-expressions +] + +[format] +line-ending = "lf" diff --git a/setup.py b/setup.py deleted file mode 100755 index 2e453dc..0000000 --- a/setup.py +++ /dev/null @@ -1,60 +0,0 @@ -"""A setuptools based setup module. - -See: -https://packaging.python.org/en/latest/distributing.html -https://github.com/pypa/sampleproject -""" - -# Always prefer setuptools over distutils -from setuptools import setup, find_packages -# To use a consistent encoding -from codecs import open -from os import path - -here = path.abspath(path.dirname(__file__)) - -# Get the long description from the README file -with open(path.join(here, 'README.rst'), encoding='utf-8') as f: - long_description = f.read() - -setup( - name='adafruit-circuitpython-seesaw', - - use_scm_version=True, - setup_requires=['setuptools_scm'], - - description='CircuitPython library for controlling a SeeSaw helper chip.', - long_description=long_description, - long_description_content_type='text/x-rst', - - # The project's main homepage. - url='https://github.com/adafruit/Adafruit_CircuitPython_seesaw', - - # Author details - author='Adafruit Industries', - author_email='circuitpython@adafruit.com', - - install_requires=["Adafruit-Blinka", "adafruit-circuitpython-busdevice"], - - # Choose your license - license='MIT', - - # See https://pypi.python.org/pypi?%3Aaction=list_classifiers - classifiers=[ - 'Development Status :: 3 - Alpha', - 'Intended Audience :: Developers', - 'Topic :: Software Development :: Libraries', - 'Topic :: System :: Hardware', - 'License :: OSI Approved :: MIT License', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - ], - - # What does your project relate to? - keywords='adafruit seesaw hardware micropython circuitpython', - - # You can just specify the packages manually here if your project is - # simple. Or you can use find_packages(). - packages=['adafruit_seesaw'], -)