From 26e106461e3320cc214c65c6a67905549785156e Mon Sep 17 00:00:00 2001 From: mrmcwethy Date: Fri, 29 Dec 2017 05:14:36 -0700 Subject: [PATCH] implemented a cut down version of driver and included examples --- .gitignore | 5 + .pylintrc | 432 ++++++++++++++++++++++++++++++ .travis.yml | 30 +++ adafruit_apds9960.py | 103 ------- adafruit_apds9960/__init__.py | 1 + adafruit_apds9960/apds9960.py | 386 ++++++++++++++++++++++++++ adafruit_apds9960/colorutility.py | 33 +++ examples/color.py | 31 +++ examples/gesture.py | 21 ++ examples/proximity.py | 22 ++ examples/test.py | 14 - 11 files changed, 961 insertions(+), 117 deletions(-) create mode 100644 .pylintrc create mode 100644 .travis.yml delete mode 100644 adafruit_apds9960.py create mode 100644 adafruit_apds9960/__init__.py create mode 100644 adafruit_apds9960/apds9960.py create mode 100644 adafruit_apds9960/colorutility.py create mode 100644 examples/color.py create mode 100644 examples/gesture.py create mode 100644 examples/proximity.py delete mode 100644 examples/test.py diff --git a/.gitignore b/.gitignore index bee8a64..0dd8629 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,6 @@ __pycache__ +_build +*.pyc +.env +build* +bundles diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..946d694 --- /dev/null +++ b/.pylintrc @@ -0,0 +1,432 @@ +[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 + + +[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= + +# 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/.travis.yml b/.travis.yml new file mode 100644 index 0000000..5153056 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,30 @@ +# This is a common .travis.yml for generating library release zip files for +# CircuitPython library releases using circuitpython-build-tools. +# See https://github.com/adafruit/circuitpython-build-tools for detailed setup +# instructions. + +dist: trusty +sudo: false +language: python +python: + - "3.6" + +cache: + pip: true + +deploy: + provider: releases + api_key: $GITHUB_TOKEN + file_glob: true + file: bundles/* + skip_cleanup: true + on: + tags: true + +install: + - pip install pylint circuitpython-build-tools + +script: + - pylint adafruit_apds9960.py + - ([[ ! -d "examples" ]] || pylint --disable=missing-docstring,invalid-name examples/*.py) + - circuitpython-build-bundles --filename_prefix adafruit-circuitpython-apds9960 --library_location . diff --git a/adafruit_apds9960.py b/adafruit_apds9960.py deleted file mode 100644 index 1f86d5a..0000000 --- a/adafruit_apds9960.py +++ /dev/null @@ -1,103 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2017 Michael McWethy for Adafruit Inc -# -# 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. -""" -`adafruit_APDS9960` -==================================================== - -TODO(description) - -* Author(s): Michael McWethy -""" -class APDS9960: - - UP = const(1) - DOWN = const(2) - RIGHT = const(3) - LEFT = const(4) - - def __init__(self, i2c, interrupt_pin=None): - self._i2c = i2c - self._interrupt_pin = interrupt_pin - self.enable_color = False - self.enable_proximity = False - self.enable_gesture = True - - - ## GESTURE DETECTION - @property - def enable_gesture(self): - return self._enable_gesture - - @enable_gesture.setter - def enable_gesture(self, enable_flag): - self._enable_gesture = enable_flag - - def gesture(self): - pass - - ## COLOR DETECTION - @property - def enable_color(self): - return self._enable_color - - @enable_color.setter - def enable_color(self, enable_flag): - self._enable_color = enable_flag - - @property - def color_data_ready(self): - return True - - @property - def color_data(self): - return [None, None, None, None] - - ### PROXIMITY - @property - def enable_proximity(self): - return self._enable_proximity - - @enable_proximity.setter - def enable_proximity(self, enable_flag): - self._enable_proximity = enable_flag - - @property - def proximity_interrupt_threshold(self): - return self._proximity_interrupt_threshold - - @proximity_interrupt_threshold.setter - def proximity_interrupt_threshold(self, setting_tuple): - self._proximity_interrupt_threshold = setting_tuple - - @property - def enable_proximity_interrupt(self): - return _enable_proximity_interrupt - - @enable_proximity_interrupt.setter - def enable_proximity_interrupt(self, enable_flag): - self._enable_proximity_interrupt = enable_flag - - def proximity(self): - return None - - def clear_interrupt(self): - pass diff --git a/adafruit_apds9960/__init__.py b/adafruit_apds9960/__init__.py new file mode 100644 index 0000000..7ecf29f --- /dev/null +++ b/adafruit_apds9960/__init__.py @@ -0,0 +1 @@ +"""common include for APDS9960 driver """ diff --git a/adafruit_apds9960/apds9960.py b/adafruit_apds9960/apds9960.py new file mode 100644 index 0000000..9bca8c6 --- /dev/null +++ b/adafruit_apds9960/apds9960.py @@ -0,0 +1,386 @@ +# The MIT License (MIT) +# +# Copyright (c) 2017 Michael McWethy for Adafruit Inc +# +# 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. +""" +`adafruit_APDS9960` +==================================================== + +TODO(description) + +* Author(s): Michael McWethy +""" +import time +import digitalio +from adafruit_register.i2c_bits import RWBits +from adafruit_register.i2c_bit import RWBit +from adafruit_bus_device.i2c_device import I2CDevice +from micropython import const + + +# ADDRESS_DEF = const(0x39) +# INTEGRATION_TIME_DEF = const(0x01) +# GAIN_DEF = const(0x01) + +#pylint: disable-msg=bad-whitespace +#APDS9960_RAM = const(0x00) +APDS9960_ENABLE = const(0x80) +APDS9960_ATIME = const(0x81) +#APDS9960_WTIME = const(0x83) +#APDS9960_AILTIL = const(0x84) +# APDS9960_AILTH = const(0x85) +# APDS9960_AIHTL = const(0x86) +# APDS9960_AIHTH = const(0x87) +APDS9960_PILT = const(0x89) +APDS9960_PIHT = const(0x8B) +APDS9960_PERS = const(0x8C) +# APDS9960_CONFIG1 = const(0x8D) +# APDS9960_PPULSE = const(0x8E) +APDS9960_CONTROL = const(0x8F) +# APDS9960_CONFIG2 = const(0x90) +APDS9960_ID = const(0x92) +APDS9960_STATUS = const(0x93) +APDS9960_CDATAL = const(0x94) +# APDS9960_CDATAH = const(0x95) +# APDS9960_RDATAL = const(0x96) +# APDS9960_RDATAH = const(0x97) +# APDS9960_GDATAL = const(0x98) +# APDS9960_GDATAH = const(0x99) +# APDS9960_BDATAL = const(0x9A) +# APDS9960_BDATAH = const(0x9B) +APDS9960_PDATA = const(0x9C) +# APDS9960_POFFSET_UR = const(0x9D) +# APDS9960_POFFSET_DL = const(0x9E) +# APDS9960_CONFIG3 = const(0x9F) +APDS9960_GPENTH = const(0xA0) +# APDS9960_GEXTH = const(0xA1) +APDS9960_GCONF1 = const(0xA2) +APDS9960_GCONF2 = const(0xA3) +# APDS9960_GOFFSET_U = const(0xA4) +# APDS9960_GOFFSET_D = const(0xA5) +# APDS9960_GOFFSET_L = const(0xA7) +# APDS9960_GOFFSET_R = const(0xA9) +APDS9960_GPULSE = const(0xA6) +APDS9960_GCONF3 = const(0xAA) +APDS9960_GCONF4 = const(0xAB) +# APDS9960_GFLVL = const(0xAE) +APDS9960_GSTATUS = const(0xAF) +# APDS9960_IFORCE = const(0xE4) +# APDS9960_PICLEAR = const(0xE5) +# APDS9960_CICLEAR = const(0xE6) +APDS9960_AICLEAR = const(0xE7) +APDS9960_GFIFO_U = const(0xFC) +# APDS9960_GFIFO_D = const(0xFD) +# APDS9960_GFIFO_L = const(0xFE) +# APDS9960_GFIFO_R = const(0xFF) +#pylint: enable-msg=bad-whitespace + + + +class APDS9960: + """ + APDS9900 provide basic driver services for the ASDS9960 breakout board + """ + + _enable = RWBit(APDS9960_ENABLE, 0) + _color_enable = RWBit(APDS9960_ENABLE, 1) + _proximity_enable = RWBit(APDS9960_ENABLE, 2) + _gesture_enable = RWBit(APDS9960_ENABLE, 6) + _gesture_fifo_threshold = RWBits(2, APDS9960_GCONF1, 6) + _gesture_gain = RWBits(2, APDS9960_GCONF2, 5) + _color_gain = RWBits(2, APDS9960_CONTROL, 0) + _gesture_valid = RWBit(APDS9960_GSTATUS, 0) + _gesture_mode = RWBit(APDS9960_GCONF4, 0) + _proximity_persistance = RWBits(4, APDS9960_PERS, 4) + _proximity_enable_interrupt = RWBit(APDS9960_ENABLE, 5) + + def __init__(self, + i2c, *, + interrupt_pin=None, + address=0x39, + integration_time=0x01, + gain=0x01): + + self.i2c_device = I2CDevice(i2c, address) + self._interrupt_pin = interrupt_pin + if interrupt_pin: + self._interrupt_pin.switch_to_input(pull=digitalio.Pull.UP) + + if self.read8(APDS9960_ID) != 0xAB: + raise RuntimeError() + + self.enable_gesture = False + self.enable_proximity = False + self.enable_color = False + self._enable_proximity_interrupt = False + self.enable_color_interrupt = False + self.proximity_interrupt = False + self.clear_interrupt() + + self.enable = False + time.sleep(0.010) + self.enable = True + time.sleep(0.010) + + self._color_gain = gain + self.integration_time = integration_time + self.gesture_dimensions = 0x00 # all + self.gesture_fifo_threshold = 0x01 # fifo 4 + self.gesture_gain = 0x02 # gain 4 + self.gesture_proximity_threshold = 50 + self.reset_counts() + + # gesture pulse length=0x2 pulse count=0x3 + self.write8(APDS9960_GPULSE, (0x2 << 6) | 0x3) + + + ## BOARD + @property + def enable(self): + return self._enable + + @enable.setter + def enable(self, enable_flag): + self._enable = enable_flag + + def reset_counts(self): + self._up_count = 0 + self._down_count = 0 + self._right_count = 0 + self._left_count = 0 + + ## GESTURE DETECTION + @property + def enable_gesture(self): + return self._gesture_mode, self._gesture_enable + + @enable_gesture.setter + def enable_gesture(self, enable_flag): + if not enable_flag: + self._gesture_mode = False + self._gesture_enable = enable_flag + + def gesture(self): + """Return a gesture code if detected. =0 if no gesture + =1 if an UP, =2 if a DOWN, =3 if an LEFT, =4 if a RIGHT + """ + buffer = bytearray(129) + buffer[0] = APDS9960_GFIFO_U + if not self._gesture_valid: + # print("no valid data") + return 0 + + time_mark = 0 + gesture_received = 0 + while True: + + up_down_diff = 0 + left_right_diff = 0 + gesture_received = 0 + + time.sleep(0.030) # 30 ms + + with self.i2c_device as i2c: + i2c.write(buffer, end=1, stop=False) + i2c.readinto(buffer, start=1) + upp, down, left, right = buffer[1:5] + + # upp, down, left, right = self._gesture_fifo_read + # if upp or down or left or right: + # print(upp, down, left, right) + + if abs(upp - down) > 13: + up_down_diff = upp - down + # print("up down diff {}".format(up_down_diff)) + + if abs(left - right) > 13: + left_right_diff = left - right + # print("left right diff {}".format(left_right_diff)) + + if up_down_diff != 0: + if up_down_diff < 0: + if self._down_count > 0: + gesture_received = 0x01 + else: + self._up_count += 1 + elif up_down_diff > 0: + if self._up_count > 0: + gesture_received = 0x02 + else: + self._down_count += 1 + + if left_right_diff != 0: + if left_right_diff < 0: + if self._right_count > 0: + gesture_received = 0x03 + else: + self._left_count += 1 + elif left_right_diff > 0: + if self._left_count > 0: + gesture_received = 0x04 + else: + self._right_count += 1 + + if up_down_diff != 0 or left_right_diff != 0: + time_mark = time.monotonic() + + if gesture_received or time.monotonic() - time_mark > 0.300: + self.reset_counts() + break + + return gesture_received + + + @property + def gesture_dimensions(self): + return self.read8(APDS9960_GCONF3) + + @gesture_dimensions.setter + def gesture_dimensions(self, dims): + self.write8(APDS9960_GCONF3, dims & 0xff) + + @property + def gesture_fifo_threshold(self): + return self._gesture_fifo_threshold + + @gesture_fifo_threshold.setter + def gesture_fifo_threshold(self, thresh): + self._gesture_fifo_threshold = thresh & 0x3 + + @property + def gesture_gain(self): + return self._gesture_gain + + @gesture_gain.setter + def gesture_gain(self, gain): + self._gesture_gain = gain + + + ## COLOR DETECTION + @property + def enable_color(self): + """returns True when color is enabled, else False""" + return self._color_enable + + @enable_color.setter + def enable_color(self, enable_flag): + self._color_enable = enable_flag + + @property + def color_data_ready(self): + return self.read8(APDS9960_STATUS) & 0x01 + + @property + def color_data(self): + """Returns tuple containing r, g, b, c values""" + return self.read16(APDS9960_CDATAL + 2), \ + self.read16(APDS9960_CDATAL + 4), \ + self.read16(APDS9960_CDATAL + 6), \ + self.read16(APDS9960_CDATAL) + ### PROXIMITY + @property + def enable_proximity(self): + """Returns or sets the enablement of proximity mode""" + return self._proximity_enable + + @enable_proximity.setter + def enable_proximity(self, enable_flag): + self._proximity_enable = enable_flag + + @property + def proximity_interrupt_threshold(self): + """Returns a tuple containing low and high threshold + followed by the proximity interrupt persistance. + Set the proximity interrupt threshold values using a tuple of zero to + three values: low threshold, high threshold, persistance """ + return self.read8(APDS9960_PILT), \ + self.read8(APDS9960_PIHT), \ + self._proximity_persistance + + @proximity_interrupt_threshold.setter + def proximity_interrupt_threshold(self, setting_tuple): + if setting_tuple: + self.write8(APDS9960_PILT, setting_tuple[0]) + if len(setting_tuple) > 1: + self.write8(APDS9960_PIHT, setting_tuple[1]) + if len(setting_tuple) > 2: + self._proximity_persistance = setting_tuple[2] + + + @property + def enable_proximity_interrupt(self): + return self._proximity_enable_interrupt + + @enable_proximity_interrupt.setter + def enable_proximity_interrupt(self, enable_flag): + self._proximity_enable_interrupt = enable_flag + + @property + def gesture_proximity_threshold(self): + return self.read8(APDS9960_GPENTH) + + @gesture_proximity_threshold.setter + def gesture_proximity_threshold(self, thresh): + self.write8(APDS9960_GPENTH, thresh & 0xff) + + def proximity(self): + return self.read8(APDS9960_PDATA) + + def clear_interrupt(self): + self.writecmdonly(APDS9960_AICLEAR) + + @property + def integration_time(self): + return self.read8(APDS9960_ATIME) + + @integration_time.setter + def integration_time(self, int_time): + self.write8(APDS9960_ATIME, int_time & 0xff) + + # method for reading and writing to I2C + def write8(self, command, abyte): + buf = bytearray(2) + buf[0] = command + buf[1] = abyte + print(buf) + with self.i2c_device as i2c: + i2c.write(buf) + + def writecmdonly(self, command): + buf = bytearray(1) + buf[0] = command + with self.i2c_device as i2c: + i2c.write(buf) + + def read8(self, command): + buf = bytearray(1) + buf[0] = command + with self.i2c_device as i2c: + i2c.write(buf) + i2c.readinto(buf) + return buf[0] + + def read16(self, command): + buf = bytearray(2) + buf[0] = command + with self.i2c_device as i2c: + i2c.write(buf, end=1, stop=False) + i2c.readinto(buf) + return buf[1] << 8 | buf[0] diff --git a/adafruit_apds9960/colorutility.py b/adafruit_apds9960/colorutility.py new file mode 100644 index 0000000..fea45db --- /dev/null +++ b/adafruit_apds9960/colorutility.py @@ -0,0 +1,33 @@ +"""Helper functions for color calculations""" + +def calculate_color_temperature(r, g, b): + """Converts the raw R/G/B values to color temperature in degrees Kelvin""" + + # 1. Map RGB values to their XYZ counterparts. + # Based on 6500K fluorescent, 3000K fluorescent + # and 60W incandescent values for a wide range. + # Note: Y = Illuminance or lux + x = (-0.14282 * r) + (1.54924 * g) + (-0.95641 * b) + y = (-0.32466 * r) + (1.57837 * g) + (-0.73191 * b) + z = (-0.68202 * r) + (0.77073 * g) + (0.56332 * b) + + # 2. Calculate the chromaticity co-ordinates + xchrome = x / (x + y + z) + ychrome = y / (x + y + z) + + # 3. Use McCamy's formula to determine the CCT + n = (xchrome - 0.3320) / (0.1858 - ychrome) + + # 4. Calculate the final CCT + cct = (449.0 * pow(n, 3)) + (3525.0 * pow(n, 2)) + (6823.3 * n) + 5520.33 + + # Return the results in degrees Kelvin + return cct + +def calculate_lux(r, g, b): + """Calculate ambient light values""" + # This only uses RGB ... how can we integrate clear or calculate lux + # based exclusively on clear since this might be more reliable? + illuminance = (-0.32466 * r) + (1.57837 * g) + (-0.73191 * b) + + return illuminance diff --git a/examples/color.py b/examples/color.py new file mode 100644 index 0000000..8f71e9f --- /dev/null +++ b/examples/color.py @@ -0,0 +1,31 @@ +import time +import board +import busio +import digitalio +from adafruit_apds9960.apds9960 import APDS9960 +from adafruit_apds9960 import colorutility + +i2c = busio.I2C(board.SCL, board.SDA) +int_pin = digitalio.DigitalInOut(board.A2) +apds = APDS9960(i2c) +apds.enable_color = True + + +while True: + #create some variables to store the color data in + + #wait for color data to be ready + while not apds.color_data_ready: + time.sleep(0.005) + + + #get the data and print the different channels + r, g, b, c = apds.color_data + print("red: ", r) + print("green: ", g) + print("blue: ", b) + print("clear: ", c) + + print("color temp {}".format(colorutility.calculate_color_temperature(r, b, g))) + print("light lux {}".format(colorutility.calculate_lux(r, b, g))) + time.sleep(0.5) \ No newline at end of file diff --git a/examples/gesture.py b/examples/gesture.py new file mode 100644 index 0000000..3cc239c --- /dev/null +++ b/examples/gesture.py @@ -0,0 +1,21 @@ +import board +import busio +from adafruit_apds9960.apds9960 import APDS9960 + +i2c = busio.I2C(board.SCL, board.SDA) + +apds = APDS9960(i2c) +apds.enable_proximity = True +apds.enable_gesture = True + +while True: + gesture = apds.gesture() + + if gesture == 0x01: + print("up") + elif gesture == 0x02: + print("down") + elif gesture == 0x03: + print("left") + elif gesture == 0x04: + print("right") \ No newline at end of file diff --git a/examples/proximity.py b/examples/proximity.py new file mode 100644 index 0000000..53fc2a2 --- /dev/null +++ b/examples/proximity.py @@ -0,0 +1,22 @@ +import board +import busio +import digitalio +from adafruit_apds9960.apds9960 import APDS9960 + +i2c = busio.I2C(board.SCL, board.SDA) +int_pin = digitalio.DigitalInOut(board.A2) +apds = APDS9960(i2c, interrupt_pin=int_pin) + +apds.enable_proximity = True +apds.proximity_interrupt_threshold = (10, 175) +apds.enable_proximity_interrupt = True + +while True: + # print the proximity reading when the interrupt pin goes low + if not int_pin.value: + prox_value = apds.proximity() + if prox_value: + print(prox_value) + + # clear the interrupt + apds.clear_interrupt() \ No newline at end of file diff --git a/examples/test.py b/examples/test.py deleted file mode 100644 index a4b8457..0000000 --- a/examples/test.py +++ /dev/null @@ -1,14 +0,0 @@ -import busio -import adafruit_apds9960 as apds9960 -from board import SCL, SDA, A1 -import digitalio - -int_pin = digitalio.DigitalInOut(A1) - -with busio.I2C(SCL, SDA) as i2c: - - apds = apds9960.APDS9960(i2c, interrupt_pin=int_pin) - print(apds.UP) - print(apds.DOWN) - print(apds.RIGHT) - print(apds.LEFT)