diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..aea4205
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+*.html linguist-language=Python
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5e8dc72
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,137 @@
+# Created by .ignore support plugin (hsz.mobi)
+### Python template
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+.idea/
+docs/images
+build/
+.idea/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+html/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+pip-wheel-metadata/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+.python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+!/html/
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..44ac8ba
--- /dev/null
+++ b/README.md
@@ -0,0 +1,31 @@
+## telemetrix-uno-r4
+
+# Telemetrix For The Arduino UNO R4 Minima and WIFI
+
+
+This Python package allows you to monitor and control an UNO R4 Minima or
+WIFI board by simply writing a Python script using one of the telemetrix--uno-r4 APIs.
+APIs are provided for both synchronous and asyncio implementations.
+
+| Feature | Minima | WIFI | Notes |
+|------------------------------------|:------:|:----:|:-------------------------------:|
+| Analog Input | X | X | |
+| Analog Output (PWM) | X | X | |
+| Digital Input | X | X | |
+| Digital Output | X | X | |
+| i2c Primitives | X | X | |
+| Servo Motor Control | X | X | Currently, BLE is not supported |
+| DHT Temperature/Humidity Sensor | X | X | |
+| HC-SR04 Sonar Distance Sensor | X | X | |
+| SPI Primitives | X | X | |
+| Scrolling Message Support | | X | |
+| Integrated Debugging Aids Provided | X | X | |
+| Examples ProvidedFor All Features | X | X | |
+
+For the Minima, communication with the Python client is supported by a USBSerial
+transport.
+
+You may choose a WIFI, USBSerial or BLE transport for the WIFI board.
+Please refer to the [User's Guide]() for further information, including installation
+instructions and client APIs.
+
diff --git a/license.txt b/license.txt
new file mode 100644
index 0000000..2def0e8
--- /dev/null
+++ b/license.txt
@@ -0,0 +1,661 @@
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU Affero General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+.
\ No newline at end of file
diff --git a/mkdocs.yml b/mkdocs.yml
new file mode 100644
index 0000000..8fb23c0
--- /dev/null
+++ b/mkdocs.yml
@@ -0,0 +1,70 @@
+# yaml-language-server: $schema=https://squidfunk.github.io/mkdocs-material/schema.json
+
+site_name: The Telemetrix User's Guide For The Raspberry Pi Pico W
+nav:
+ - Introduction: index.md
+ - Checking If You Have The Correct Version Of Python Installed: python_3_verify.md
+ - Installation Procedures:
+ - Installing And Upgrading The Client API On Your PC: install_telemetrix.md
+ - Installing And Upgrading The Pico Server Application: install_pico_server.md
+ - Telemetrix Design Concepts:
+ - Pin Modes: about_the_apis.md
+ - Callbacks: callbacks.md
+ - The APIs:
+ - telemetrix_rpi_pico_w:
+ - Importing The Package And Instantiating The Library: importing.md
+ - Analog Input Methods: analog_input.md
+ - Digital Input Methods: digital_input.md
+ - Digital Output Methods: digital_output.md
+ - DHT Humidity/Temperature Sensor Methods: dht.md
+ - HC-SR04 Sonar Distance Sensor Methods: sonar.md
+ - I2C Methods: i2c.md
+ - NeoPixel Methods: neopixel.md
+ - PWM Methods: pwm.md
+ - Servo Methods: servo.md
+ - SPI Methods: spi.md
+ - Stepper Motor Methods: stepper.md
+ - Shutting Down: management.md
+ - Diagnostic Aids: debug.md
+ - telemetrix_rpi_pico_w_aio:
+ - Importing The Package And Instantiating The Library: importing2.md
+ - Analog Input Methods: analog_input2.md
+ - Digital Input Methods: digital_input2.md
+ - Digital Output Methods: digital_output2.md
+ - DHT Humidity/Temperature Sensor Methods: dht2.md
+ - HC-SR04 Sonar Distance Sensor Methods: sonar2.md
+ - I2C Methods: i2c2.md
+ - NeoPixel Methods: neopixel2.md
+ - PWM Methods: pwm2.md
+ - Servo Methods: servo2.md
+ - SPI Methods: spi2.md
+ - Stepper Motor Methods: stepper2.md
+ - Shutting Down: management2.md
+ - Diagnostic Aids: debug2.md
+ - Downloading And Running The Examples: examples.md
+ - Client APIs:
+ - telemetrix_uno_r4_minima: telemetrix_minima_reference.md
+ - telemetrix_uno_r4_minima_aio: telemetrix_minima_reference_aio.md
+ - telemetrix_uno_r4_wifi: telemetrix_wifi_reference.md
+ - telemetrix_uno_r4_wifi_aio: telemetrix_wifi_reference_aio.md
+
+ - About: about.md
+ - License: license.md
+# theme: windmill
+
+theme:
+ name: 'material'
+
+
+plugins:
+ - search
+ - mkdocstrings:
+ handlers:
+ python:
+ paths: [ telemetrix_uno_r4/wifi/telemetrix_uno_r4_wifi,
+ telemetrix_uno_r4/wifi/telemetrix_uno_r4_wifi_aio,
+ telemetrix_uno_r4/minima/telemetrix_uno_r4_minima,
+ telemetrix_uno_r4/minima/telemetrix_uno_r4_minima_aio]
+
+
+
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..a9b1435
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,36 @@
+[build-system]
+requires = ["setuptools"]
+build-backend = "setuptools.build_meta"
+
+[tool.setuptools.packages]
+find = {} # Scan the project directory with the default parameters
+
+[project]
+name = "telemetrix_uno_r4"
+version = "1.0.0"
+authors = [
+ { name="Alan Yorinks", email="MisterYsLab@gmail.com" },
+]
+description = "Telemetrix Clients Supporting The Arduino Uno R4 Minima and Wifi"
+readme = "README.md"
+requires-python = ">=3.8"
+license = {text = "AGPL-3.0-or-later"}
+
+keywords=['telemetrix', 'Arduino', 'R4', 'Python']
+classifiers=[
+ 'Development Status :: 5 - Production/Stable',
+ 'Environment :: Other Environment',
+ 'Intended Audience :: Developers',
+ 'Intended Audience :: Education',
+ 'Operating System :: OS Independent',
+ 'Topic :: Software Development :: Libraries :: Python Modules'
+ ]
+
+dependencies = [
+ "pyserial", "bleak"
+]
+
+
+
+
+
diff --git a/telemetrix_uno_r4/__init__.py b/telemetrix_uno_r4/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/telemetrix_uno_r4/minima/__init__.py b/telemetrix_uno_r4/minima/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/telemetrix_uno_r4/minima/telemetrix_uno_r4_minima/__init__.py b/telemetrix_uno_r4/minima/telemetrix_uno_r4_minima/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/telemetrix_uno_r4/minima/telemetrix_uno_r4_minima/private_constants.py b/telemetrix_uno_r4/minima/telemetrix_uno_r4_minima/private_constants.py
new file mode 100644
index 0000000..bc9d68e
--- /dev/null
+++ b/telemetrix_uno_r4/minima/telemetrix_uno_r4_minima/private_constants.py
@@ -0,0 +1,148 @@
+"""
+ Copyright (c) 2015-2021 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+
+class PrivateConstants:
+ """
+ This class contains a set of constants for telemetrix internal use .
+ """
+
+ # commands
+ # send a loop back request - for debugging communications
+ LOOP_COMMAND = 0
+ SET_PIN_MODE = 1 # set a pin to INPUT/OUTPUT/PWM/etc
+ DIGITAL_WRITE = 2 # set a single digital pin value instead of entire port
+ ANALOG_WRITE = 3
+ MODIFY_REPORTING = 4
+ GET_FIRMWARE_VERSION = 5
+ ARE_U_THERE = 6 # Arduino ID query for auto-detect of telemetrix connected boards
+ SERVO_ATTACH = 7
+ SERVO_WRITE = 8
+ SERVO_DETACH = 9
+ I2C_BEGIN = 10
+ I2C_READ = 11
+ I2C_WRITE = 12
+ SONAR_NEW = 13
+ DHT_NEW = 14
+ STOP_ALL_REPORTS = 15
+ SET_ANALOG_SCANNING_INTERVAL = 16
+ ENABLE_ALL_REPORTS = 17
+ RESET = 18
+ SPI_INIT = 19
+ SPI_WRITE_BLOCKING = 20
+ SPI_READ_BLOCKING = 21
+ SPI_SET_FORMAT = 22
+ SPI_CS_CONTROL = 23
+ ONE_WIRE_INIT = 24
+ ONE_WIRE_RESET = 25
+ ONE_WIRE_SELECT = 26
+ ONE_WIRE_SKIP = 27
+ ONE_WIRE_WRITE = 28
+ ONE_WIRE_READ = 29
+ ONE_WIRE_RESET_SEARCH = 30
+ ONE_WIRE_SEARCH = 31
+ ONE_WIRE_CRC8 = 32
+ SET_PIN_MODE_STEPPER = 33
+ STEPPER_MOVE_TO = 34
+ STEPPER_MOVE = 35
+ STEPPER_RUN = 36
+ STEPPER_RUN_SPEED = 37
+ STEPPER_SET_MAX_SPEED = 38
+ STEPPER_SET_ACCELERATION = 39
+ STEPPER_SET_SPEED = 40
+ STEPPER_SET_CURRENT_POSITION = 41
+ STEPPER_RUN_SPEED_TO_POSITION = 42
+ STEPPER_STOP = 43
+ STEPPER_DISABLE_OUTPUTS = 44
+ STEPPER_ENABLE_OUTPUTS = 45
+ STEPPER_SET_MINIMUM_PULSE_WIDTH = 46
+ STEPPER_SET_ENABLE_PIN = 47
+ STEPPER_SET_3_PINS_INVERTED = 48
+ STEPPER_SET_4_PINS_INVERTED = 49
+ STEPPER_IS_RUNNING = 50
+ STEPPER_GET_CURRENT_POSITION = 51
+ STEPPER_GET_DISTANCE_TO_GO = 52
+ STEPPER_GET_TARGET_POSITION = 53
+ GET_FEATURES = 54
+ SONAR_DISABLE = 55
+ SONAR_ENABLE = 56
+ BOARD_HARD_RESET = 57
+
+ # reports
+ # debug data from Arduino
+ DIGITAL_REPORT = DIGITAL_WRITE
+ ANALOG_REPORT = ANALOG_WRITE
+ FIRMWARE_REPORT = GET_FIRMWARE_VERSION
+ I_AM_HERE_REPORT = ARE_U_THERE
+ SERVO_UNAVAILABLE = SERVO_ATTACH
+ I2C_TOO_FEW_BYTES_RCVD = 8
+ I2C_TOO_MANY_BYTES_RCVD = 9
+ I2C_READ_REPORT = 10
+ SONAR_DISTANCE = 11
+ DHT_REPORT = 12
+ SPI_REPORT = 13
+ ONE_WIRE_REPORT = 14
+ STEPPER_DISTANCE_TO_GO = 15
+ STEPPER_TARGET_POSITION = 16
+ STEPPER_CURRENT_POSITION = 17
+ STEPPER_RUNNING_REPORT = 18
+ STEPPER_RUN_COMPLETE_REPORT = 19
+ FEATURES = 20
+ DEBUG_PRINT = 99
+
+ TELEMETRIX_VERSION = "1.00"
+
+ # reporting control
+ REPORTING_DISABLE_ALL = 0
+ REPORTING_ANALOG_ENABLE = 1
+ REPORTING_DIGITAL_ENABLE = 2
+ REPORTING_ANALOG_DISABLE = 3
+ REPORTING_DIGITAL_DISABLE = 4
+
+ # Pin mode definitions
+ AT_INPUT = 0
+ AT_OUTPUT = 1
+ AT_INPUT_PULLUP = 2
+ AT_ANALOG = 3
+ AT_SERVO = 4
+ AT_SONAR = 5
+ AT_DHT = 6
+ AT_MODE_NOT_SET = 255
+
+ # maximum number of digital pins supported
+ NUMBER_OF_DIGITAL_PINS = 100
+
+ # maximum number of analog pins supported
+ NUMBER_OF_ANALOG_PINS = 20
+
+ # maximum number of sonars allowed
+ MAX_SONARS = 6
+
+ # maximum number of DHT devices allowed
+ MAX_DHTS = 6
+
+ # DHT Report sub-types
+ DHT_DATA = 0
+ DHT_ERROR = 1
+
+ # feature masks
+ ONEWIRE_FEATURE = 0x01
+ DHT_FEATURE = 0x02
+ STEPPERS_FEATURE = 0x04
+ SPI_FEATURE = 0x08
+ SERVO_FEATURE = 0x10
+ SONAR_FEATURE = 0x20
diff --git a/telemetrix_uno_r4/minima/telemetrix_uno_r4_minima/telemetrix_uno_r4_minima.py b/telemetrix_uno_r4/minima/telemetrix_uno_r4_minima/telemetrix_uno_r4_minima.py
new file mode 100644
index 0000000..d0769f4
--- /dev/null
+++ b/telemetrix_uno_r4/minima/telemetrix_uno_r4_minima/telemetrix_uno_r4_minima.py
@@ -0,0 +1,2554 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+"""
+import sys
+import threading
+import time
+from collections import deque
+
+import serial
+# noinspection PyPackageRequirementscd
+from serial.serialutil import SerialException
+# noinspection PyPackageRequirements
+from serial.tools import list_ports
+
+# noinspection PyUnresolvedReferences
+from telemetrix_uno_r4.minima.telemetrix_uno_r4_minima.private_constants import (
+ PrivateConstants)
+
+
+# noinspection PyPep8,PyMethodMayBeStatic,GrazieInspection,PyBroadException,PyCallingNonCallable
+class TelemetrixUnoR4Minima(threading.Thread):
+ """
+ This class exposes and implements the telemetrix API.
+ It uses threading to accommodate concurrency.
+ It includes the public API methods as well as
+ a set of private methods.
+
+ """
+
+ # noinspection PyPep8,PyPep8,PyPep8
+ def __init__(self, com_port=None, arduino_instance_id=1,
+ arduino_wait=1, sleep_tune=0.000001,
+ shutdown_on_exception=True, hard_reset_on_shutdown=True):
+
+ """
+
+ :param com_port: e.g. COM3 or /dev/ttyACM0.
+ Only use if you wish to bypass auto com port
+ detection.
+
+ :param arduino_instance_id: Match with the value installed on the
+ arduino-telemetrix sketch.
+
+ :param arduino_wait: Amount of time to wait for an Arduino to
+ fully reset itself.
+
+ :param sleep_tune: A tuning parameter (typically not changed by user)
+
+ :param shutdown_on_exception: call shutdown before raising
+ a RunTimeError exception, or
+ receiving a KeyboardInterrupt exception
+
+ :param hard_reset_on_shutdown: reset the board on shutdown
+
+ """
+
+ # initialize threading parent
+ threading.Thread.__init__(self)
+
+ # create the threads and set them as daemons so
+ # that they stop when the program is closed
+
+ # create a thread to interpret received serial data
+ self.the_reporter_thread = threading.Thread(target=self._reporter)
+ self.the_reporter_thread.daemon = True
+
+ self.the_data_receive_thread = threading.Thread(target=self._serial_receiver)
+
+ self.the_data_receive_thread.daemon = True
+
+ # flag to allow the reporter and receive threads to run.
+ self.run_event = threading.Event()
+
+ # check to make sure that Python interpreter is version 3.7 or greater
+ python_version = sys.version_info
+ if python_version[0] >= 3:
+ if python_version[1] >= 7:
+ pass
+ else:
+ raise RuntimeError("ERROR: Python 3.7 or greater is "
+ "required for use of this program.")
+
+ # save input parameters as instance variables
+ self.com_port = com_port
+ self.arduino_instance_id = arduino_instance_id
+ self.arduino_wait = arduino_wait
+ self.sleep_tune = sleep_tune
+ self.shutdown_on_exception = shutdown_on_exception
+ self.hard_reset_on_shutdown = hard_reset_on_shutdown
+
+ # create a deque to receive and process data from the arduino
+ self.the_deque = deque()
+
+ # The report_dispatch dictionary is used to process
+ # incoming report messages by looking up the report message
+ # and executing its associated processing method.
+
+ self.report_dispatch = {}
+
+ # To add a command to the command dispatch table, append here.
+ self.report_dispatch.update(
+ {PrivateConstants.LOOP_COMMAND: self._report_loop_data})
+ self.report_dispatch.update(
+ {PrivateConstants.DEBUG_PRINT: self._report_debug_data})
+ self.report_dispatch.update(
+ {PrivateConstants.DIGITAL_REPORT: self._digital_message})
+ self.report_dispatch.update(
+ {PrivateConstants.ANALOG_REPORT: self._analog_message})
+ self.report_dispatch.update(
+ {PrivateConstants.FIRMWARE_REPORT: self._firmware_message})
+ self.report_dispatch.update({PrivateConstants.I_AM_HERE_REPORT: self._i_am_here})
+ self.report_dispatch.update(
+ {PrivateConstants.SERVO_UNAVAILABLE: self._servo_unavailable})
+ self.report_dispatch.update(
+ {PrivateConstants.I2C_READ_REPORT: self._i2c_read_report})
+ self.report_dispatch.update(
+ {PrivateConstants.I2C_TOO_FEW_BYTES_RCVD: self._i2c_too_few})
+ self.report_dispatch.update(
+ {PrivateConstants.I2C_TOO_MANY_BYTES_RCVD: self._i2c_too_many})
+ self.report_dispatch.update(
+ {PrivateConstants.SONAR_DISTANCE: self._sonar_distance_report})
+ self.report_dispatch.update({PrivateConstants.DHT_REPORT: self._dht_report})
+ self.report_dispatch.update(
+ {PrivateConstants.SPI_REPORT: self._spi_report})
+ self.report_dispatch.update(
+ {PrivateConstants.ONE_WIRE_REPORT: self._onewire_report})
+ self.report_dispatch.update(
+ {PrivateConstants.STEPPER_DISTANCE_TO_GO:
+ self._stepper_distance_to_go_report})
+ self.report_dispatch.update(
+ {PrivateConstants.STEPPER_TARGET_POSITION:
+ self._stepper_target_position_report})
+ self.report_dispatch.update(
+ {PrivateConstants.STEPPER_CURRENT_POSITION:
+ self._stepper_current_position_report})
+ self.report_dispatch.update(
+ {PrivateConstants.STEPPER_RUNNING_REPORT:
+ self._stepper_is_running_report})
+ self.report_dispatch.update(
+ {PrivateConstants.STEPPER_RUN_COMPLETE_REPORT:
+ self._stepper_run_complete_report})
+
+ self.report_dispatch.update(
+ {PrivateConstants.STEPPER_DISTANCE_TO_GO:
+ self._stepper_distance_to_go_report})
+ self.report_dispatch.update(
+ {PrivateConstants.STEPPER_TARGET_POSITION:
+ self._stepper_target_position_report})
+ self.report_dispatch.update(
+ {PrivateConstants.FEATURES:
+ self._features_report})
+
+ # dictionaries to store the callbacks for each pin
+ self.analog_callbacks = {}
+
+ self.digital_callbacks = {}
+
+ self.i2c_callback = None
+ self.i2c_callback2 = None
+
+ self.i2c_1_active = False
+ self.i2c_2_active = False
+
+ self.spi_callback = None
+
+ self.onewire_callback = None
+
+ self.cs_pins_enabled = []
+
+ # the trigger pin will be the key to retrieve
+ # the callback for a specific HC-SR04
+ self.sonar_callbacks = {}
+
+ self.sonar_count = 0
+
+ self.dht_callbacks = {}
+
+ self.dht_count = 0
+
+ # serial port in use
+ self.serial_port = None
+
+ # flag to indicate we are in shutdown mode
+ self.shutdown_flag = False
+
+ # debug loopback callback method
+ self.loop_back_callback = None
+
+ # flag to indicate the start of a new report
+ # self.new_report_start = True
+
+ # firmware version to be stored here
+ self.firmware_version = []
+
+ # reported arduino instance id
+ self.reported_arduino_id = []
+
+ # reported features
+ self.reported_features = 0
+
+ # flag to indicate if i2c was previously enabled
+ self.i2c_enabled = False
+
+ # flag to indicate if spi is initialized
+ self.spi_enabled = False
+
+ # flag to indicate if onewire is initialized
+ self.onewire_enabled = False
+
+ # # stepper motor variables
+ #
+ # # updated when a new motor is added
+ # self.next_stepper_assigned = 0
+ #
+ # # valid list of stepper motor interface types
+ # self.valid_stepper_interfaces = [1, 2, 3, 4, 6, 8]
+ #
+ # # maximum number of steppers supported
+ # self.max_number_of_steppers = 4
+ #
+ # # number of steppers created - not to exceed the maximum
+ # self.number_of_steppers = 0
+ #
+ # # dictionary to hold stepper motor information
+ # self.stepper_info = {'instance': False, 'is_running': None,
+ # 'maximum_speed': 1, 'speed': 0, 'acceleration': 0,
+ # 'distance_to_go_callback': None,
+ # 'target_position_callback': None,
+ # 'current_position_callback': None,
+ # 'is_running_callback': None,
+ # 'motion_complete_callback': None,
+ # 'acceleration_callback': None}
+ #
+ # # build a list of stepper motor info items
+ # self.stepper_info_list = []
+ # # a list of dictionaries to hold stepper information
+ # for motor in range(self.max_number_of_steppers):
+ # self.stepper_info_list.append(self.stepper_info)
+
+ self.the_reporter_thread.start()
+ self.the_data_receive_thread.start()
+
+ print(f"telemetrix_uno_r4_minima: Version"
+ f" {PrivateConstants.TELEMETRIX_VERSION}\n\n"
+ f"Copyright (c) 2023 Alan Yorinks All Rights Reserved.\n")
+
+ # using the serial link
+ if not self.com_port:
+ # user did not specify a com_port
+ try:
+ self._find_arduino()
+ except KeyboardInterrupt:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ else:
+ # com_port specified - set com_port and baud rate
+ try:
+ self._manual_open()
+ except KeyboardInterrupt:
+ if self.shutdown_on_exception:
+ self.shutdown()
+
+ if self.serial_port:
+ print(
+ f"Arduino compatible device found and connected to {self.serial_port.port}")
+
+ self.serial_port.reset_input_buffer()
+ self.serial_port.reset_output_buffer()
+
+ # no com_port found - raise a runtime exception
+ else:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError('No Arduino Found or User Aborted Program')
+
+ # allow the threads to run
+ self._run_threads()
+ print(f'Waiting for Arduino to reset')
+ print(f'Reset Complete')
+
+ # get telemetrix firmware version and print it
+ print('\nRetrieving Telemetrix4UnoR4Minima firmware ID...')
+ self._get_firmware_version()
+ if not self.firmware_version:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(f'Telemetrix4UnoR4Minima firmware version')
+
+ else:
+
+ print(f'Telemetrix4UnoR4Minima firmware version: {self.firmware_version[0]}.'
+ f'{self.firmware_version[1]}.{self.firmware_version[2]}')
+ command = [PrivateConstants.ENABLE_ALL_REPORTS]
+ self._send_command(command)
+
+ # get the features list
+ command = [PrivateConstants.GET_FEATURES]
+ self._send_command(command)
+ time.sleep(.2)
+
+ # Have the server reset its data structures
+ command = [PrivateConstants.RESET]
+ self._send_command(command)
+
+ def _find_arduino(self):
+ """
+ This method will search all potential serial ports for an Arduino
+ containing a sketch that has a matching arduino_instance_id as
+ specified in the input parameters of this class.
+
+ This is used explicitly with the Telemetrix4Arduino sketch.
+ """
+
+ # a list of serial ports to be checked
+ serial_ports = []
+
+ print('Opening all potential serial ports...')
+ the_ports_list = list_ports.comports()
+ for port in the_ports_list:
+ if port.pid is None:
+ continue
+ try:
+ self.serial_port = serial.Serial(port.device, 115200,
+ timeout=1, writeTimeout=0)
+ except SerialException:
+ continue
+ # create a list of serial ports that we opened
+ serial_ports.append(self.serial_port)
+
+ # display to the user
+ print('\t' + port.device)
+
+ # clear out any possible data in the input buffer
+ # wait for arduino to reset
+ print(
+ f'\nWaiting {self.arduino_wait} seconds(arduino_wait) for Arduino devices to '
+ 'reset...')
+ # temporary for testing
+ time.sleep(self.arduino_wait)
+ self._run_threads()
+
+ for serial_port in serial_ports:
+ self.serial_port = serial_port
+
+ self._get_arduino_id()
+ if self.reported_arduino_id != self.arduino_instance_id:
+ continue
+ else:
+ print('Valid Arduino ID Found.')
+ self.serial_port.reset_input_buffer()
+ self.serial_port.reset_output_buffer()
+ return
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(f'Incorrect Arduino ID: {self.reported_arduino_id}')
+
+ def _manual_open(self):
+ """
+ Com port was specified by the user - try to open up that port
+
+ """
+ # if port is not found, a serial exception will be thrown
+ try:
+ print(f'Opening {self.com_port}...')
+ self.serial_port = serial.Serial(self.com_port, 115200,
+ timeout=1, writeTimeout=0)
+
+ print(
+ f'\nWaiting {self.arduino_wait} seconds(arduino_wait) for Arduino devices to '
+ 'reset...')
+ self._run_threads()
+ time.sleep(self.arduino_wait)
+
+ self._get_arduino_id()
+
+ if self.reported_arduino_id != self.arduino_instance_id:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(f'Incorrect Arduino ID: {self.reported_arduino_id}')
+ print('Valid Arduino ID Found.')
+ # get arduino firmware version and print it
+ print('\nRetrieving Telemetrix4Arduino firmware ID...')
+ self._get_firmware_version()
+
+ if not self.firmware_version:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(
+ f'Telemetrix4Arduino Sketch Firmware Version Not Found')
+
+ else:
+ print(f'Telemetrix4UnoR4 firmware version: {self.firmware_version[0]}.'
+ f'{self.firmware_version[1]}')
+ except KeyboardInterrupt:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError('User Hit Control-C')
+
+ def analog_write(self, pin, value):
+ """
+ Set the specified pin to the specified value.
+
+ :param pin: arduino pin number
+
+ :param value: pin value (maximum 16 bits)
+
+ """
+ value_msb = value >> 8
+ value_lsb = value & 0xff
+ command = [PrivateConstants.ANALOG_WRITE, pin, value_msb, value_lsb]
+ self._send_command(command)
+
+ def digital_write(self, pin, value):
+ """
+ Set the specified pin to the specified value.
+
+ :param pin: arduino pin number
+
+ :param value: pin value (1 or 0)
+
+ """
+
+ command = [PrivateConstants.DIGITAL_WRITE, pin, value]
+ self._send_command(command)
+
+ def disable_all_reporting(self):
+ """
+ Disable reporting for all digital and analog input pins
+ """
+ command = [PrivateConstants.MODIFY_REPORTING,
+ PrivateConstants.REPORTING_DISABLE_ALL, 0]
+ self._send_command(command)
+
+ def disable_analog_reporting(self, pin):
+ """
+ Disables analog reporting for a single analog pin.
+
+ :param pin: Analog pin number. For example for A0, the number is 0.
+
+ """
+ command = [PrivateConstants.MODIFY_REPORTING,
+ PrivateConstants.REPORTING_ANALOG_DISABLE, pin]
+ self._send_command(command)
+
+ def disable_digital_reporting(self, pin):
+ """
+ Disables digital reporting for a single digital input.
+
+ :param pin: Pin number.
+
+ """
+ command = [PrivateConstants.MODIFY_REPORTING,
+ PrivateConstants.REPORTING_DIGITAL_DISABLE, pin]
+ self._send_command(command)
+
+ def enable_analog_reporting(self, pin):
+ """
+ Enables analog reporting for the specified pin.
+
+ :param pin: Analog pin number. For example for A0, the number is 0.
+
+
+ """
+ command = [PrivateConstants.MODIFY_REPORTING,
+ PrivateConstants.REPORTING_ANALOG_ENABLE, pin]
+ self._send_command(command)
+
+ def enable_digital_reporting(self, pin):
+ """
+ Enable reporting on the specified digital pin.
+
+ :param pin: Pin number.
+ """
+
+ command = [PrivateConstants.MODIFY_REPORTING,
+ PrivateConstants.REPORTING_DIGITAL_ENABLE, pin]
+ self._send_command(command)
+
+ def _get_arduino_id(self):
+ """
+ Retrieve arduino-telemetrix arduino id
+
+ """
+ command = [PrivateConstants.ARE_U_THERE]
+ self._send_command(command)
+ # provide time for the reply
+ time.sleep(.5)
+
+ def _get_firmware_version(self):
+ """
+ This method retrieves the
+ arduino-telemetrix firmware version
+
+ """
+ command = [PrivateConstants.GET_FIRMWARE_VERSION]
+ self._send_command(command)
+ # provide time for the reply
+ time.sleep(.5)
+
+ def i2c_read(self, address, register, number_of_bytes,
+ callback=None, i2c_port=0,
+ write_register=True):
+ """
+ Read the specified number of bytes from the
+ specified register for the i2c device.
+
+
+ :param address: i2c device address
+
+ :param register: i2c register (or None if no register
+ selection is needed)
+
+ :param number_of_bytes: number of bytes to be read
+
+ :param callback: Required callback function to report
+ i2c data as a result of read command
+
+ :param i2c_port: 0 = default, 1 = secondary
+
+ :param write_register: If True, the register is written
+ before read
+ Else, the write is suppressed
+
+
+ callback returns a data list:
+
+ [I2C_READ_REPORT, address, register, count of data bytes,
+ data bytes, time-stamp]
+
+ """
+
+ self._i2c_read_request(address, register, number_of_bytes,
+ callback=callback, i2c_port=i2c_port,
+ write_register=write_register)
+
+ def i2c_read_restart_transmission(self, address, register,
+ number_of_bytes,
+ callback=None, i2c_port=0,
+ write_register=True):
+ """
+ Read the specified number of bytes from the specified
+ register for the i2c device. This restarts the transmission
+ after the read. It is required for some i2c devices such as the MMA8452Q
+ accelerometer.
+
+
+ :param address: i2c device address
+
+ :param register: i2c register (or None if no register
+ selection is needed)
+
+ :param number_of_bytes: number of bytes to be read
+
+ :param callback: Required callback function to report i2c
+ data as a result of read command
+
+ :param i2c_port: 0 = default 1 = secondary
+
+ :param write_register: If True, the register is written before read
+ Else, the write is suppressed
+
+
+
+ callback returns a data list:
+
+ [I2C_READ_REPORT, address, register, count of data bytes,
+ data bytes, time-stamp]
+
+ """
+
+ self._i2c_read_request(address, register, number_of_bytes,
+ stop_transmission=False,
+ callback=callback, i2c_port=i2c_port,
+ write_register=write_register)
+
+ def _i2c_read_request(self, address, register, number_of_bytes,
+ stop_transmission=True, callback=None, i2c_port=0,
+ write_register=True):
+ """
+ This method requests the read of an i2c device. Results are retrieved
+ via callback.
+
+ :param address: i2c device address
+
+ :param register: register number (or None if no register selection is needed)
+
+ :param number_of_bytes: number of bytes expected to be returned
+
+ :param stop_transmission: stop transmission after read
+
+ :param callback: Required callback function to report i2c data as a
+ result of read command.
+
+ :param write_register: If True, the register is written before read
+ Else, the write is suppressed
+
+ """
+ if not i2c_port:
+ if not self.i2c_1_active:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(
+ 'I2C Read: set_pin_mode i2c never called for i2c port 1.')
+
+ if i2c_port:
+ if not self.i2c_2_active:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(
+ 'I2C Read: set_pin_mode i2c never called for i2c port 2.')
+
+ if not callback:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError('I2C Read: A callback function must be specified.')
+
+ if not i2c_port:
+ self.i2c_callback = callback
+ else:
+ self.i2c_callback2 = callback
+
+ if not register:
+ register = 0
+
+ if write_register:
+ write_register = 1
+ else:
+ write_register = 0
+
+ # message contains:
+ # 1. address
+ # 2. register
+ # 3. number of bytes
+ # 4. restart_transmission - True or False
+ # 5. i2c port
+ # 6. suppress write flag
+
+ command = [PrivateConstants.I2C_READ, address, register, number_of_bytes,
+ stop_transmission, i2c_port, write_register]
+ self._send_command(command)
+
+ def i2c_write(self, address, args, i2c_port=0):
+ """
+ Write data to an i2c device.
+
+ :param address: i2c device address
+
+ :param i2c_port: 0= port 1, 1 = port 2
+
+ :param args: A variable number of bytes to be sent to the device
+ passed in as a list
+
+ """
+ if not i2c_port:
+ if not self.i2c_1_active:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(
+ 'I2C Write: set_pin_mode i2c never called for i2c port 1.')
+
+ if i2c_port:
+ if not self.i2c_2_active:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(
+ 'I2C Write: set_pin_mode i2c never called for i2c port 2.')
+
+ command = [PrivateConstants.I2C_WRITE, len(args), address, i2c_port]
+
+ for item in args:
+ command.append(item)
+
+ self._send_command(command)
+
+ def loop_back(self, start_character, callback=None):
+ """
+ This is a debugging method to send a character to the
+ Arduino device, and have the device loop it back.
+
+ :param start_character: The character to loop back. It should be
+ an integer.
+
+ :param callback: Looped back character will appear in the callback method
+
+ """
+ command = [PrivateConstants.LOOP_COMMAND, ord(start_character)]
+ self.loop_back_callback = callback
+ self._send_command(command)
+
+ def set_analog_scan_interval(self, interval):
+ """
+ Set the analog scanning interval.
+
+ :param interval: value of 0 - 255 - milliseconds
+ """
+
+ if 0 <= interval <= 255:
+ command = [PrivateConstants.SET_ANALOG_SCANNING_INTERVAL, interval]
+ self._send_command(command)
+ else:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError('Analog interval must be between 0 and 255')
+
+ def set_pin_mode_analog_output(self, pin_number):
+ """
+ Set a pin as a pwm (analog output) pin.
+
+ :param pin_number:arduino pin number
+
+ """
+ self._set_pin_mode(pin_number, PrivateConstants.AT_OUTPUT)
+
+ def set_pin_mode_analog_input(self, pin_number, differential=0, callback=None):
+ """
+ Set a pin as an analog input.
+
+ :param pin_number: arduino pin number
+
+ :param differential: difference in previous to current value before
+ report will be generated
+
+ :param callback: callback function
+
+
+ callback returns a data list:
+
+ [pin_type, pin_number, pin_value, raw_time_stamp]
+
+ The pin_type for analog input pins = 3
+
+ """
+ self._set_pin_mode(pin_number, PrivateConstants.AT_ANALOG, differential,
+ callback)
+
+ def set_pin_mode_digital_input(self, pin_number, callback=None):
+ """
+ Set a pin as a digital input.
+
+ :param pin_number: arduino pin number
+
+ :param callback: callback function
+
+
+ callback returns a data list:
+
+ [pin_type, pin_number, pin_value, raw_time_stamp]
+
+ The pin_type for all digital input pins = 2
+
+ """
+ self._set_pin_mode(pin_number, PrivateConstants.AT_INPUT, callback=callback)
+
+ def set_pin_mode_digital_input_pullup(self, pin_number, callback=None):
+ """
+ Set a pin as a digital input with pullup enabled.
+
+ :param pin_number: arduino pin number
+
+ :param callback: callback function
+
+
+ callback returns a data list:
+
+ [pin_type, pin_number, pin_value, raw_time_stamp]
+
+ The pin_type for all digital input pins = 2
+ """
+ self._set_pin_mode(pin_number, PrivateConstants.AT_INPUT_PULLUP,
+ callback=callback)
+
+ def set_pin_mode_digital_output(self, pin_number):
+ """
+ Set a pin as a digital output pin.
+
+ :param pin_number: arduino pin number
+ """
+
+ self._set_pin_mode(pin_number, PrivateConstants.AT_OUTPUT)
+
+ def set_pin_mode_i2c(self, i2c_port=0):
+ """
+ Establish the standard Arduino i2c pins for i2c utilization.
+
+ :param i2c_port: 0 = i2c1, 1 = i2c2
+
+ NOTES: 1. THIS METHOD MUST BE CALLED BEFORE ANY I2C REQUEST IS MADE
+ 2. Callbacks are set within the individual i2c read methods of this
+ API.
+
+ See i2c_read, or i2c_read_restart_transmission.
+
+ """
+ # test for i2c port 2
+ if i2c_port:
+ # if not previously activated set it to activated
+ # and the send a begin message for this port
+ if not self.i2c_2_active:
+ self.i2c_2_active = True
+ else:
+ return
+ # port 1
+ else:
+ if not self.i2c_1_active:
+ self.i2c_1_active = True
+ else:
+ return
+
+ command = [PrivateConstants.I2C_BEGIN, i2c_port]
+ self._send_command(command)
+
+ def set_pin_mode_dht(self, pin, callback=None, dht_type=22):
+ """
+
+ :param pin: connection pin
+
+ :param callback: callback function
+
+ :param dht_type: either 22 for DHT22 or 11 for DHT11
+
+ Error Callback: [DHT REPORT Type, DHT_ERROR_NUMBER, PIN, DHT_TYPE, Time]
+
+ Valid Data Callback: DHT REPORT Type, DHT_DATA=, PIN, DHT_TYPE, Humidity,
+ Temperature,
+ Time]
+
+ DHT_REPORT_TYPE = 12
+ """
+ if self.reported_features & PrivateConstants.DHT_FEATURE:
+ if not callback:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError('set_pin_mode_dht: A Callback must be specified')
+
+ if self.dht_count < PrivateConstants.MAX_DHTS - 1:
+ self.dht_callbacks[pin] = callback
+ self.dht_count += 1
+
+ if dht_type != 22 and dht_type != 11:
+ dht_type = 22
+
+ command = [PrivateConstants.DHT_NEW, pin, dht_type]
+ self._send_command(command)
+ else:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(
+ f'Maximum Number Of DHTs Exceeded - set_pin_mode_dht fails for pin {pin}')
+ else:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(f'The DHT feature is disabled in the server.')
+
+ # noinspection PyRedundantParentheses
+ def set_pin_mode_servo(self, pin_number, min_pulse=544, max_pulse=2400):
+ """
+
+ Attach a pin to a servo motor
+
+ :param pin_number: pin
+
+ :param min_pulse: minimum pulse width
+
+ :param max_pulse: maximum pulse width
+
+ """
+ if self.reported_features & PrivateConstants.SERVO_FEATURE:
+
+ minv = (min_pulse).to_bytes(2, byteorder="big")
+ maxv = (max_pulse).to_bytes(2, byteorder="big")
+
+ command = [PrivateConstants.SERVO_ATTACH, pin_number,
+ minv[0], minv[1], maxv[0], maxv[1]]
+ self._send_command(command)
+ else:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(f'The SERVO feature is disabled in the server.')
+
+ def set_pin_mode_sonar(self, trigger_pin, echo_pin,
+ callback=None):
+ """
+
+ :param trigger_pin:
+
+ :param echo_pin:
+
+ :param callback: callback
+
+ callback data: [PrivateConstants.SONAR_DISTANCE, trigger_pin, distance_value, time_stamp]
+
+ """
+ if self.reported_features & PrivateConstants.SONAR_FEATURE:
+
+ if not callback:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError('set_pin_mode_sonar: A Callback must be specified')
+
+ if self.sonar_count < PrivateConstants.MAX_SONARS - 1:
+ self.sonar_callbacks[trigger_pin] = callback
+ self.sonar_count += 1
+
+ command = [PrivateConstants.SONAR_NEW, trigger_pin, echo_pin]
+ self._send_command(command)
+ else:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(
+ f'Maximum Number Of Sonars Exceeded - set_pin_mode_sonar fails for pin {trigger_pin}')
+ else:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(f'The SONAR feature is disabled in the server.')
+
+ def set_pin_mode_spi(self, chip_select_list=None):
+ """
+ Specify the list of chip select pins.
+
+ Standard Arduino MISO, MOSI and CLK pins are used for the board in use.
+
+ Chip Select is any digital output capable pin.
+
+ :param chip_select_list: this is a list of pins to be used for chip select.
+ The pins will be configured as output, and set to high
+ ready to be used for chip select.
+ NOTE: You must specify the chips select pins here!
+
+
+ command message: [command, [cs pins...]]
+ """
+
+ if self.reported_features & PrivateConstants.SPI_FEATURE:
+ if type(chip_select_list) != list:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError('chip_select_list must be in the form of a list')
+ if not chip_select_list:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError('Chip select pins were not specified')
+
+ self.spi_enabled = True
+
+ command = [PrivateConstants.SPI_INIT, len(chip_select_list)]
+
+ for pin in chip_select_list:
+ command.append(pin)
+ self.cs_pins_enabled.append(pin)
+ self._send_command(command)
+ else:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(f'The SPI feature is disabled in the server.')
+
+ # def set_pin_mode_stepper(self, interface=1, pin1=2, pin2=3, pin3=4,
+ # pin4=5, enable=True):
+ # """
+ # Stepper motor support is implemented as a proxy for the
+ # the AccelStepper library for the Arduino.
+ #
+ # This feature is compatible with the TB6600 Motor Driver
+ #
+ # Note: It may not work for other driver types!
+ #
+ # https://github.com/waspinator/AccelStepper
+ #
+ # Instantiate a stepper motor.
+ #
+ # Initialize the interface and pins for a stepper motor.
+ #
+ # :param interface: Motor Interface Type:
+ #
+ # 1 = Stepper Driver, 2 driver pins required
+ #
+ # 2 = FULL2WIRE 2 wire stepper, 2 motor pins required
+ #
+ # 3 = FULL3WIRE 3 wire stepper, such as HDD spindle,
+ # 3 motor pins required
+ #
+ # 4 = FULL4WIRE, 4 wire full stepper, 4 motor pins
+ # required
+ #
+ # 6 = HALF3WIRE, 3 wire half stepper, such as HDD spindle,
+ # 3 motor pins required
+ #
+ # 8 = HALF4WIRE, 4 wire half stepper, 4 motor pins required
+ #
+ # :param pin1: Arduino digital pin number for motor pin 1
+ #
+ # :param pin2: Arduino digital pin number for motor pin 2
+ #
+ # :param pin3: Arduino digital pin number for motor pin 3
+ #
+ # :param pin4: Arduino digital pin number for motor pin 4
+ #
+ # :param enable: If this is true, the output pins at construction time.
+ #
+ # :return: Motor Reference number
+ # """
+ # if self.reported_features & PrivateConstants.STEPPERS_FEATURE:
+ #
+ # if self.number_of_steppers == self.max_number_of_steppers:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('Maximum number of steppers has already been assigned')
+ #
+ # if interface not in self.valid_stepper_interfaces:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('Invalid stepper interface')
+ #
+ # self.number_of_steppers += 1
+ #
+ # motor_id = self.next_stepper_assigned
+ # self.next_stepper_assigned += 1
+ # self.stepper_info_list[motor_id]['instance'] = True
+ #
+ # # build message and send message to server
+ # command = [PrivateConstants.SET_PIN_MODE_STEPPER, motor_id, interface, pin1,
+ # pin2, pin3, pin4, enable]
+ # self._send_command(command)
+ #
+ # # return motor id
+ # return motor_id
+ # else:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError(f'The Stepper feature is disabled in the server.')
+
+ def servo_write(self, pin_number, angle):
+ """
+
+ Set a servo attached to a pin to a given angle.
+
+ :param pin_number: pin
+
+ :param angle: angle (0-180)
+
+ """
+ command = [PrivateConstants.SERVO_WRITE, pin_number, angle]
+ self._send_command(command)
+
+ def servo_detach(self, pin_number):
+ """
+ Detach a servo for reuse
+
+ :param pin_number: attached pin
+
+ """
+ command = [PrivateConstants.SERVO_DETACH, pin_number]
+ self._send_command(command)
+
+ # def stepper_move_to(self, motor_id, position):
+ # """
+ # Set an absolution target position. If position is positive, the movement is
+ # clockwise, else it is counter-clockwise.
+ #
+ # The run() function (below) will try to move the motor (at most one step per call)
+ # from the current position to the target position set by the most
+ # recent call to this function. Caution: moveTo() also recalculates the
+ # speed for the next step.
+ # If you are trying to use constant speed movements, you should call setSpeed()
+ # after calling moveTo().
+ #
+ # :param motor_id: motor id: 0 - 3
+ #
+ # :param position: target position. Maximum value is 32 bits.
+ # """
+ # if position < 0:
+ # polarity = 1
+ # else:
+ # polarity = 0
+ # position = abs(position)
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_move_to: Invalid motor_id.')
+ #
+ # position_bytes = list(position.to_bytes(4, 'big', signed=True))
+ #
+ # command = [PrivateConstants.STEPPER_MOVE_TO, motor_id]
+ # for value in position_bytes:
+ # command.append(value)
+ # command.append(polarity)
+ # self._send_command(command)
+ #
+ # def stepper_move(self, motor_id, relative_position):
+ # """
+ # Set the target position relative to the current position.
+ #
+ # :param motor_id: motor id: 0 - 3
+ #
+ # :param relative_position: The desired position relative to the current
+ # position. Negative is anticlockwise from
+ # the current position. Maximum value is 32 bits.
+ # """
+ # if relative_position < 0:
+ # polarity = 1
+ # else:
+ # polarity = 0
+ #
+ # relative_position = abs(relative_position)
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_move: Invalid motor_id.')
+ #
+ # position_bytes = list(relative_position.to_bytes(4, 'big', signed=True))
+ #
+ # command = [PrivateConstants.STEPPER_MOVE, motor_id]
+ # for value in position_bytes:
+ # command.append(value)
+ # command.append(polarity)
+ # self._send_command(command)
+ #
+ # def stepper_run(self, motor_id, completion_callback=None):
+ # """
+ # This method steps the selected motor based on the current speed.
+ #
+ # Once called, the server will continuously attempt to step the motor.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param completion_callback: call back function to receive motion complete
+ # notification
+ #
+ # callback returns a data list:
+ #
+ # [report_type, motor_id, raw_time_stamp]
+ #
+ # The report_type = 19
+ # """
+ # if not completion_callback:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_run: A motion complete callback must be '
+ # 'specified.')
+ #
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_run: Invalid motor_id.')
+ #
+ # self.stepper_info_list[motor_id]['motion_complete_callback'] = completion_callback
+ # command = [PrivateConstants.STEPPER_RUN, motor_id]
+ # self._send_command(command)
+ #
+ # def stepper_run_speed(self, motor_id):
+ # """
+ # This method steps the selected motor based at a constant speed as set by the most
+ # recent call to stepper_set_max_speed(). The motor will run continuously.
+ #
+ # Once called, the server will continuously attempt to step the motor.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_run_speed: Invalid motor_id.')
+ #
+ # command = [PrivateConstants.STEPPER_RUN_SPEED, motor_id]
+ # self._send_command(command)
+ #
+ # def stepper_set_max_speed(self, motor_id, max_speed):
+ # """
+ # Sets the maximum permitted speed. The stepper_run() function will accelerate
+ # up to the speed set by this function.
+ #
+ # Caution: the maximum speed achievable depends on your processor and clock speed.
+ # The default maxSpeed is 1 step per second.
+ #
+ # Caution: Speeds that exceed the maximum speed supported by the processor may
+ # result in non-linear accelerations and decelerations.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param max_speed: 1 - 1000
+ # """
+ #
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_set_max_speed: Invalid motor_id.')
+ #
+ # if not 1 < max_speed <= 1000:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_set_max_speed: Speed range is 1 - 1000.')
+ #
+ # self.stepper_info_list[motor_id]['max_speed'] = max_speed
+ # max_speed_msb = (max_speed & 0xff00) >> 8
+ # max_speed_lsb = max_speed & 0xff
+ #
+ # command = [PrivateConstants.STEPPER_SET_MAX_SPEED, motor_id, max_speed_msb,
+ # max_speed_lsb]
+ # self._send_command(command)
+ #
+ # def stepper_get_max_speed(self, motor_id):
+ # """
+ # Returns the maximum speed configured for this stepper
+ # that was previously set by stepper_set_max_speed()
+ #
+ # Value is stored in the client, so no callback is required.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :return: The currently configured maximum speed.
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_max_speed: Invalid motor_id.')
+ #
+ # return self.stepper_info_list[motor_id]['max_speed']
+ #
+ # def stepper_set_acceleration(self, motor_id, acceleration):
+ # """
+ # Sets the acceleration/deceleration rate.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param acceleration: The desired acceleration in steps per second
+ # per second. Must be > 0.0. This is an
+ # expensive call since it requires a square
+ # root to be calculated on the server.
+ # Dont call more often than needed.
+ #
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_set_acceleration: Invalid motor_id.')
+ #
+ # if not 1 < acceleration <= 1000:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_set_acceleration: Acceleration range is 1 - '
+ # '1000.')
+ #
+ # self.stepper_info_list[motor_id]['acceleration'] = acceleration
+ #
+ # max_accel_msb = acceleration >> 8
+ # max_accel_lsb = acceleration & 0xff
+ #
+ # command = [PrivateConstants.STEPPER_SET_ACCELERATION, motor_id, max_accel_msb,
+ # max_accel_lsb]
+ # self._send_command(command)
+ #
+ # def stepper_set_speed(self, motor_id, speed):
+ # """
+ # Sets the desired constant speed for use with stepper_run_speed().
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param speed: 0 - 1000 The desired constant speed in steps per
+ # second. Positive is clockwise. Speeds of more than 1000 steps per
+ # second are unreliable. Speed accuracy depends on the Arduino
+ # crystal. Jitter depends on how frequently you call the
+ # stepper_run_speed() method.
+ # The speed will be limited by the current value of
+ # stepper_set_max_speed().
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_set_speed: Invalid motor_id.')
+ #
+ # if not 0 < speed <= 1000:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_set_speed: Speed range is 0 - '
+ # '1000.')
+ #
+ # self.stepper_info_list[motor_id]['speed'] = speed
+ #
+ # speed_msb = speed >> 8
+ # speed_lsb = speed & 0xff
+ #
+ # command = [PrivateConstants.STEPPER_SET_SPEED, motor_id, speed_msb, speed_lsb]
+ # self._send_command(command)
+ #
+ # def stepper_get_speed(self, motor_id):
+ # """
+ # Returns the most recently set speed.
+ # that was previously set by stepper_set_speed();
+ #
+ # Value is stored in the client, so no callback is required.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_get_speed: Invalid motor_id.')
+ #
+ # return self.stepper_info_list[motor_id]['speed']
+ #
+ # def stepper_get_distance_to_go(self, motor_id, distance_to_go_callback):
+ # """
+ # Request the distance from the current position to the target position
+ # from the server.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param distance_to_go_callback: required callback function to receive report
+ #
+ # :return: The distance to go is returned via the callback as a list:
+ #
+ # [REPORT_TYPE=15, motor_id, distance in steps, time_stamp]
+ #
+ # A positive distance is clockwise from the current position.
+ #
+ # """
+ # if not distance_to_go_callback:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_get_distance_to_go Read: A callback function must be specified.')
+ #
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_get_distance_to_go: Invalid motor_id.')
+ # self.stepper_info_list[motor_id][
+ # 'distance_to_go_callback'] = distance_to_go_callback
+ # command = [PrivateConstants.STEPPER_GET_DISTANCE_TO_GO, motor_id]
+ # self._send_command(command)
+ #
+ # def stepper_get_target_position(self, motor_id, target_callback):
+ # """
+ # Request the most recently set target position from the server.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param target_callback: required callback function to receive report
+ #
+ # :return: The distance to go is returned via the callback as a list:
+ #
+ # [REPORT_TYPE=16, motor_id, target position in steps, time_stamp]
+ #
+ # Positive is clockwise from the 0 position.
+ #
+ # """
+ # if not target_callback:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError(
+ # 'stepper_get_target_position Read: A callback function must be specified.')
+ #
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_get_target_position: Invalid motor_id.')
+ #
+ # self.stepper_info_list[motor_id][
+ # 'target_position_callback'] = target_callback
+ #
+ # command = [PrivateConstants.STEPPER_GET_TARGET_POSITION, motor_id]
+ # self._send_command(command)
+ #
+ # def stepper_get_current_position(self, motor_id, current_position_callback):
+ # """
+ # Request the current motor position from the server.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param current_position_callback: required callback function to receive report
+ #
+ # :return: The current motor position returned via the callback as a list:
+ #
+ # [REPORT_TYPE=17, motor_id, current position in steps, time_stamp]
+ #
+ # Positive is clockwise from the 0 position.
+ # """
+ # if not current_position_callback:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError(
+ # 'stepper_get_current_position Read: A callback function must be specified.')
+ #
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_get_current_position: Invalid motor_id.')
+ #
+ # self.stepper_info_list[motor_id]['current_position_callback'] = current_position_callback
+ #
+ # command = [PrivateConstants.STEPPER_GET_CURRENT_POSITION, motor_id]
+ # self._send_command(command)
+ #
+ # def stepper_set_current_position(self, motor_id, position):
+ # """
+ # Resets the current position of the motor, so that wherever the motor
+ # happens to be right now is considered to be the new 0 position. Useful
+ # for setting a zero position on a stepper after an initial hardware
+ # positioning move.
+ #
+ # Has the side effect of setting the current motor speed to 0.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param position: Position in steps. This is a 32 bit value
+ # """
+ #
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_set_current_position: Invalid motor_id.')
+ # position_bytes = list(position.to_bytes(4, 'big', signed=True))
+ #
+ # command = [PrivateConstants.STEPPER_SET_CURRENT_POSITION, motor_id]
+ # for value in position_bytes:
+ # command.append(value)
+ # self._send_command(command)
+ #
+ # def stepper_run_speed_to_position(self, motor_id, completion_callback=None):
+ # """
+ # Runs the motor at the currently selected speed until the target position is
+ # reached.
+ #
+ # Does not implement accelerations.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param completion_callback: call back function to receive motion complete
+ # notification
+ #
+ # callback returns a data list:
+ #
+ # [report_type, motor_id, raw_time_stamp]
+ #
+ # The report_type = 19
+ # """
+ # if not completion_callback:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_run_speed_to_position: A motion complete '
+ # 'callback must be '
+ # 'specified.')
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_run_speed_to_position: Invalid motor_id.')
+ #
+ # self.stepper_info_list[motor_id]['motion_complete_callback'] = completion_callback
+ # command = [PrivateConstants.STEPPER_RUN_SPEED_TO_POSITION, motor_id]
+ # self._send_command(command)
+ #
+ # def stepper_stop(self, motor_id):
+ # """
+ # Sets a new target position that causes the stepper
+ # to stop as quickly as possible, using the current speed and
+ # acceleration parameters.
+ #
+ # :param motor_id: 0 - 3
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_stop: Invalid motor_id.')
+ #
+ # command = [PrivateConstants.STEPPER_STOP, motor_id]
+ # self._send_command(command)
+ #
+ # def stepper_disable_outputs(self, motor_id):
+ # """
+ # Disable motor pin outputs by setting them all LOW.
+ #
+ # Depending on the design of your electronics this may turn off
+ # the power to the motor coils, saving power.
+ #
+ # This is useful to support Arduino low power modes: disable the outputs
+ # during sleep and then re-enable with enableOutputs() before stepping
+ # again.
+ #
+ # If the enable Pin is defined, sets it to OUTPUT mode and clears
+ # the pin to disabled.
+ #
+ # :param motor_id: 0 - 3
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_disable_outputs: Invalid motor_id.')
+ #
+ # command = [PrivateConstants.STEPPER_DISABLE_OUTPUTS, motor_id]
+ # self._send_command(command)
+ #
+ # def stepper_enable_outputs(self, motor_id):
+ # """
+ # Enable motor pin outputs by setting the motor pins to OUTPUT
+ # mode.
+ #
+ # If the enable Pin is defined, sets it to OUTPUT mode and sets
+ # the pin to enabled.
+ #
+ # :param motor_id: 0 - 3
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_enable_outputs: Invalid motor_id.')
+ #
+ # command = [PrivateConstants.STEPPER_ENABLE_OUTPUTS, motor_id]
+ # self._send_command(command)
+ #
+ # def stepper_set_min_pulse_width(self, motor_id, minimum_width):
+ # """
+ # Sets the minimum pulse width allowed by the stepper driver.
+ #
+ # The minimum practical pulse width is approximately 20 microseconds.
+ #
+ # Times less than 20 microseconds will usually result in 20 microseconds or so.
+ #
+ # :param motor_id: 0 -3
+ #
+ # :param minimum_width: A 16 bit unsigned value expressed in microseconds.
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_set_min_pulse_width: Invalid motor_id.')
+ #
+ # if not 0 < minimum_width <= 0xff:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_set_min_pulse_width: Pulse width range = '
+ # '0-0xffff.')
+ #
+ # width_msb = minimum_width >> 8
+ # width_lsb = minimum_width & 0xff
+ #
+ # command = [PrivateConstants.STEPPER_SET_MINIMUM_PULSE_WIDTH, motor_id, width_msb,
+ # width_lsb]
+ # self._send_command(command)
+ #
+ # def stepper_set_enable_pin(self, motor_id, pin=0xff):
+ # """
+ # Sets the enable pin number for stepper drivers.
+ # 0xFF indicates unused (default).
+ #
+ # Otherwise, if a pin is set, the pin will be turned on when
+ # enableOutputs() is called and switched off when disableOutputs()
+ # is called.
+ #
+ # :param motor_id: 0 - 4
+ # :param pin: 0-0xff
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_set_enable_pin: Invalid motor_id.')
+ #
+ # if not 0 < pin <= 0xff:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_set_enable_pin: Pulse width range = '
+ # '0-0xff.')
+ # command = [PrivateConstants.STEPPER_SET_ENABLE_PIN, motor_id, pin]
+ #
+ # self._send_command(command)
+ #
+ # def stepper_set_3_pins_inverted(self, motor_id, direction=False, step=False,
+ # enable=False):
+ # """
+ # Sets the inversion for stepper driver pins.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param direction: True=inverted or False
+ #
+ # :param step: True=inverted or False
+ #
+ # :param enable: True=inverted or False
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_set_3_pins_inverted: Invalid motor_id.')
+ #
+ # command = [PrivateConstants.STEPPER_SET_3_PINS_INVERTED, motor_id, direction,
+ # step, enable]
+ #
+ # self._send_command(command)
+ #
+ # def stepper_set_4_pins_inverted(self, motor_id, pin1_invert=False, pin2_invert=False,
+ # pin3_invert=False, pin4_invert=False, enable=False):
+ # """
+ # Sets the inversion for 2, 3 and 4 wire stepper pins
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param pin1_invert: True=inverted or False
+ #
+ # :param pin2_invert: True=inverted or False
+ #
+ # :param pin3_invert: True=inverted or False
+ #
+ # :param pin4_invert: True=inverted or False
+ #
+ # :param enable: True=inverted or False
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_set_4_pins_inverted: Invalid motor_id.')
+ #
+ # command = [PrivateConstants.STEPPER_SET_4_PINS_INVERTED, motor_id, pin1_invert,
+ # pin2_invert, pin3_invert, pin4_invert, enable]
+ #
+ # self._send_command(command)
+ #
+ # def stepper_is_running(self, motor_id, callback):
+ # """
+ # Checks to see if the motor is currently running to a target.
+ #
+ # Callback return True if the speed is not zero or not at the target position.
+ #
+ # :param motor_id: 0-4
+ #
+ # :param callback: required callback function to receive report
+ #
+ # :return: The current running state returned via the callback as a list:
+ #
+ # [REPORT_TYPE=18, motor_id, True or False for running state, time_stamp]
+ # """
+ # if not callback:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError(
+ # 'stepper_is_running: A callback function must be specified.')
+ #
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_is_running: Invalid motor_id.')
+ #
+ # self.stepper_info_list[motor_id]['is_running_callback'] = callback
+ #
+ # command = [PrivateConstants.STEPPER_IS_RUNNING, motor_id]
+ # self._send_command(command)
+
+ def _set_pin_mode(self, pin_number, pin_state, differential=0, callback=None):
+ """
+ A private method to set the various pin modes.
+
+ :param pin_number: arduino pin number
+
+ :param pin_state: INPUT/OUTPUT/ANALOG/PWM/PULLUP
+ For SERVO use: set_pin_mode_servo
+ For DHT use: set_pin_mode_dht
+
+ :param differential: for analog inputs - threshold
+ value to be achieved for report to
+ be generated
+
+ :param callback: A reference to a call back function to be
+ called when pin data value changes
+
+ """
+ if callback:
+ if pin_state == PrivateConstants.AT_INPUT:
+ self.digital_callbacks[pin_number] = callback
+ elif pin_state == PrivateConstants.AT_INPUT_PULLUP:
+ self.digital_callbacks[pin_number] = callback
+ elif pin_state == PrivateConstants.AT_ANALOG:
+ self.analog_callbacks[pin_number] = callback
+ else:
+ print('{} {}'.format('set_pin_mode: callback ignored for '
+ 'pin state:', pin_state))
+
+ if pin_state == PrivateConstants.AT_INPUT:
+ command = [PrivateConstants.SET_PIN_MODE, pin_number,
+ PrivateConstants.AT_INPUT, 1]
+
+ elif pin_state == PrivateConstants.AT_INPUT_PULLUP:
+ command = [PrivateConstants.SET_PIN_MODE, pin_number,
+ PrivateConstants.AT_INPUT_PULLUP, 1]
+
+ elif pin_state == PrivateConstants.AT_OUTPUT:
+ command = [PrivateConstants.SET_PIN_MODE, pin_number,
+ PrivateConstants.AT_OUTPUT]
+
+ elif pin_state == PrivateConstants.AT_ANALOG:
+ command = [PrivateConstants.SET_PIN_MODE, pin_number,
+ PrivateConstants.AT_ANALOG,
+ differential >> 8, differential & 0xff, 1]
+ else:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError('Unknown pin state')
+
+ if command:
+ self._send_command(command)
+
+ def shutdown(self):
+ """
+ This method attempts an orderly shutdown
+ If any exceptions are thrown, they are ignored.
+ """
+ self.shutdown_flag = True
+
+ self._stop_threads()
+
+ try:
+ command = [PrivateConstants.STOP_ALL_REPORTS]
+ self._send_command(command)
+ time.sleep(.5)
+
+ if self.hard_reset_on_shutdown:
+ self.r4_hard_reset()
+ else:
+ try:
+ self.serial_port.reset_input_buffer()
+ self.serial_port.reset_output_buffer()
+
+ self.serial_port.close()
+
+ except (RuntimeError, SerialException, OSError):
+ # ignore error on shutdown
+ pass
+ except Exception:
+ # raise RuntimeError('Shutdown failed - could not send stop streaming
+ # message')
+ pass
+
+ def sonar_disable(self):
+ """
+ Disable sonar scanning for all sonar sensors
+ """
+ command = [PrivateConstants.SONAR_DISABLE]
+ self._send_command(command)
+
+ def sonar_enable(self):
+ """
+ Enable sonar scanning for all sonar sensors
+ """
+ command = [PrivateConstants.SONAR_ENABLE]
+ self._send_command(command)
+
+ def spi_cs_control(self, chip_select_pin, select):
+ """
+ Control an SPI chip select line
+ :param chip_select_pin: pin connected to CS
+
+ :param select: 0=select, 1=deselect
+ """
+ if not self.spi_enabled:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(f'spi_cs_control: SPI interface is not enabled.')
+
+ if chip_select_pin not in self.cs_pins_enabled:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(f'spi_cs_control: chip select pin never enabled.')
+ command = [PrivateConstants.SPI_CS_CONTROL, chip_select_pin, select]
+ self._send_command(command)
+
+ def spi_read_blocking(self, chip_select, register_selection, number_of_bytes_to_read,
+ call_back=None):
+ """
+ Read the specified number of bytes from the specified SPI port and
+ call the callback function with the reported data.
+
+ :param chip_select: chip select pin
+
+ :param register_selection: Register to be selected for read.
+
+ :param number_of_bytes_to_read: Number of bytes to read
+
+ :param call_back: Required callback function to report spi data as a
+ result of read command
+
+
+ callback returns a data list:
+ [SPI_READ_REPORT, chip select pin, SPI Register, count of data bytes read,
+ data bytes, time-stamp]
+
+ SPI_READ_REPORT = 13
+
+ """
+
+ if not self.spi_enabled:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(f'spi_read_blocking: SPI interface is not enabled.')
+
+ if not call_back:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError('spi_read_blocking: A Callback must be specified')
+
+ self.spi_callback = call_back
+
+ command = [PrivateConstants.SPI_READ_BLOCKING, chip_select,
+ number_of_bytes_to_read,
+ register_selection]
+
+ self._send_command(command)
+
+ def spi_set_format(self, clock_divisor, bit_order, data_mode):
+ """
+ Configure how the SPI serializes and de-serializes data on the wire.
+
+ See Arduino SPI reference materials for details.
+
+ :param clock_divisor: 1 - 255
+
+ :param bit_order:
+
+ LSBFIRST = 0
+
+ MSBFIRST = 1 (default)
+
+ :param data_mode:
+
+ SPI_MODE0 = 0x00 (default)
+
+ SPI_MODE1 = 1
+
+ SPI_MODE2 = 2
+
+ SPI_MODE3 = 3
+
+ """
+
+ if not self.spi_enabled:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(f'spi_set_format: SPI interface is not enabled.')
+
+ if not 0 < clock_divisor <= 255:
+ raise RuntimeError(f'spi_set_format: illegal clock divisor selected.')
+ if bit_order not in [0, 1]:
+ raise RuntimeError(f'spi_set_format: illegal bit_order selected.')
+ if data_mode not in [0, 1, 2, 3]:
+ raise RuntimeError(f'spi_set_format: illegal data_order selected.')
+
+ command = [PrivateConstants.SPI_SET_FORMAT, clock_divisor, bit_order,
+ data_mode]
+ self._send_command(command)
+
+ def spi_write_blocking(self, chip_select, bytes_to_write):
+ """
+ Write a list of bytes to the SPI device.
+
+ :param chip_select: chip select pin
+
+ :param bytes_to_write: A list of bytes to write. This must
+ be in the form of a list.
+
+ """
+
+ if not self.spi_enabled:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(f'spi_write_blocking: SPI interface is not enabled.')
+
+ if type(bytes_to_write) is not list:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError('spi_write_blocking: bytes_to_write must be a list.')
+
+ command = [PrivateConstants.SPI_WRITE_BLOCKING, chip_select, len(bytes_to_write)]
+
+ for data in bytes_to_write:
+ command.append(data)
+
+ self._send_command(command)
+
+ # def set_pin_mode_one_wire(self, pin):
+ # """
+ # Initialize the one wire serial bus.
+ #
+ # :param pin: Data pin connected to the OneWire device
+ # """
+ # if self.reported_features & PrivateConstants.ONEWIRE_FEATURE:
+ # self.onewire_enabled = True
+ # command = [PrivateConstants.ONE_WIRE_INIT, pin]
+ # self._send_command(command)
+ # else:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError(f'The OneWire feature is disabled in the server.')
+ #
+ # def onewire_reset(self, callback=None):
+ # """
+ # Reset the onewire device
+ #
+ # :param callback: required function to report reset result
+ #
+ # callback returns a list:
+ # [ReportType = 14, Report Subtype = 25, reset result byte,
+ # timestamp]
+ # """
+ # if not self.onewire_enabled:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError(f'onewire_reset: OneWire interface is not enabled.')
+ # if not callback:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('onewire_reset: A Callback must be specified')
+ #
+ # self.onewire_callback = callback
+ #
+ # command = [PrivateConstants.ONE_WIRE_RESET]
+ # self._send_command(command)
+ #
+ # def onewire_select(self, device_address):
+ # """
+ # Select a device based on its address
+ # :param device_address: A bytearray of 8 bytes
+ # """
+ # if not self.onewire_enabled:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError(f'onewire_select: OneWire interface is not enabled.')
+ #
+ # if type(device_address) is not list:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('onewire_select: device address must be an array of 8 '
+ # 'bytes.')
+ #
+ # if len(device_address) != 8:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('onewire_select: device address must be an array of 8 '
+ # 'bytes.')
+ # command = [PrivateConstants.ONE_WIRE_SELECT]
+ # for data in device_address:
+ # command.append(data)
+ # self._send_command(command)
+ #
+ # def onewire_skip(self):
+ # """
+ # Skip the device selection. This only works if you have a
+ # single device, but you can avoid searching and use this to
+ # immediately access your device.
+ # """
+ # if not self.onewire_enabled:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError(f'onewire_skip: OneWire interface is not enabled.')
+ #
+ # command = [PrivateConstants.ONE_WIRE_SKIP]
+ # self._send_command(command)
+ #
+ # def onewire_write(self, data, power=0):
+ # """
+ # Write a byte to the onewire device. If 'power' is one
+ # then the wire is held high at the end for
+ # parasitically powered devices. You
+ # are responsible for eventually de-powering it by calling
+ # another read or write.
+ #
+ # :param data: byte to write.
+ # :param power: power control (see above)
+ # """
+ # if not self.onewire_enabled:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError(f'onewire_write: OneWire interface is not enabled.')
+ # if 0 < data < 255:
+ # command = [PrivateConstants.ONE_WIRE_WRITE, data, power]
+ # self._send_command(command)
+ # else:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('onewire_write: Data must be no larger than 255')
+ #
+ # def onewire_read(self, callback=None):
+ # """
+ # Read a byte from the onewire device
+ # :param callback: required function to report onewire data as a
+ # result of read command
+ #
+ #
+ # callback returns a data list:
+ # [ONEWIRE_REPORT, ONEWIRE_READ=29, data byte, time-stamp]
+ #
+ # ONEWIRE_REPORT = 14
+ # """
+ # if not self.onewire_enabled:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError(f'onewire_read: OneWire interface is not enabled.')
+ #
+ # if not callback:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('onewire_read A Callback must be specified')
+ #
+ # self.onewire_callback = callback
+ #
+ # command = [PrivateConstants.ONE_WIRE_READ]
+ # self._send_command(command)
+ #
+ # def onewire_reset_search(self):
+ # """
+ # Begin a new search. The next use of search will begin at the first device
+ # """
+ #
+ # if not self.onewire_enabled:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError(f'onewire_reset_search: OneWire interface is not '
+ # f'enabled.')
+ # else:
+ # command = [PrivateConstants.ONE_WIRE_RESET_SEARCH]
+ # self._send_command(command)
+ #
+ # def onewire_search(self, callback=None):
+ # """
+ # Search for the next device. The device address will returned in the callback.
+ # If a device is found, the 8 byte address is contained in the callback.
+ # If no more devices are found, the address returned contains all elements set
+ # to 0xff.
+ #
+ # :param callback: required function to report a onewire device address
+ #
+ # callback returns a data list:
+ # [ONEWIRE_REPORT, ONEWIRE_SEARCH=31, 8 byte address, time-stamp]
+ #
+ # ONEWIRE_REPORT = 14
+ # """
+ # if not self.onewire_enabled:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError(f'onewire_search: OneWire interface is not enabled.')
+ #
+ # if not callback:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('onewire_read A Callback must be specified')
+ #
+ # self.onewire_callback = callback
+ #
+ # command = [PrivateConstants.ONE_WIRE_SEARCH]
+ # self._send_command(command)
+ #
+ # def onewire_crc8(self, address_list, callback=None):
+ # """
+ # Compute a CRC check on an array of data.
+ # :param address_list:
+ #
+ # :param callback: required function to report a onewire device address
+ #
+ # callback returns a data list:
+ # [ONEWIRE_REPORT, ONEWIRE_CRC8=32, CRC, time-stamp]
+ #
+ # ONEWIRE_REPORT = 14
+ #
+ # """
+ #
+ # if not self.onewire_enabled:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError(f'onewire_crc8: OneWire interface is not enabled.')
+ #
+ # if not callback:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('onewire_crc8 A Callback must be specified')
+ #
+ # if type(address_list) is not list:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('onewire_crc8: address list must be a list.')
+ #
+ # self.onewire_callback = callback
+ #
+ # address_length = len(address_list)
+ #
+ # command = [PrivateConstants.ONE_WIRE_CRC8, address_length - 1]
+ #
+ # for data in address_list:
+ # command.append(data)
+ #
+ # self._send_command(command)
+
+ def r4_hard_reset(self):
+ """
+ Place the r4 into hard reset
+ """
+ command = [PrivateConstants.RESET, 1]
+ self._send_command(command)
+ time.sleep(.5)
+ command = [PrivateConstants.BOARD_HARD_RESET, 1]
+ self._send_command(command)
+ '''
+ report message handlers
+ '''
+
+ def _analog_message(self, data):
+ """
+ This is a private message handler method.
+ It is a message handler for analog messages.
+
+ :param data: message data
+
+ """
+ pin = data[0]
+ value = (data[1] << 8) + data[2]
+ # set the current value in the pin structure
+ time_stamp = time.time()
+ # self.digital_pins[pin].event_time = time_stamp
+ if self.analog_callbacks[pin]:
+ message = [PrivateConstants.ANALOG_REPORT, pin, value, time_stamp]
+ try:
+ self.analog_callbacks[pin](message)
+ except KeyError:
+ pass
+
+ def _dht_report(self, data):
+ """
+ This is the dht report handler method.
+
+ :param data: data[0] = report error return
+ No Errors = 0
+
+ Checksum Error = 1
+
+ Timeout Error = 2
+
+ Invalid Value = 999
+
+ data[1] = pin number
+
+ data[2] = dht type 11 or 22
+
+ data[3] = humidity positivity flag
+
+ data[4] = temperature positivity value
+
+ data[5] = humidity integer
+
+ data[6] = humidity fractional value
+
+ data[7] = temperature integer
+
+ data[8] = temperature fractional value
+
+
+ """
+ if data[0]: # DHT_ERROR
+ # error report
+ # data[0] = report sub type, data[1] = pin, data[2] = error message
+ if self.dht_callbacks[data[1]]:
+ # Callback 0=DHT REPORT, DHT_ERROR, PIN, Time
+ message = [PrivateConstants.DHT_REPORT, data[0], data[1], data[2],
+ time.time()]
+ try:
+ self.dht_callbacks[data[1]](message)
+ except KeyError:
+ pass
+ else:
+ # got valid data DHT_DATA
+ f_humidity = float(data[5] + data[6] / 100)
+ if data[3]:
+ f_humidity *= -1.0
+ f_temperature = float(data[7] + data[8] / 100)
+ if data[4]:
+ f_temperature *= -1.0
+ message = [PrivateConstants.DHT_REPORT, data[0], data[1], data[2],
+ f_humidity, f_temperature, time.time()]
+
+ try:
+ self.dht_callbacks[data[1]](message)
+ except KeyError:
+ pass
+
+ def _digital_message(self, data):
+ """
+ This is a private message handler method.
+ It is a message handler for Digital Messages.
+
+ :param data: digital message
+
+ """
+ pin = data[0]
+ value = data[1]
+
+ time_stamp = time.time()
+ if self.digital_callbacks[pin]:
+ message = [PrivateConstants.DIGITAL_REPORT, pin, value, time_stamp]
+ self.digital_callbacks[pin](message)
+
+ def _firmware_message(self, data):
+ """
+ Telemetrix4Arduino firmware version message
+
+ :param data: data[0] = major number, data[1] = minor number.
+
+ data[2] = patch number
+ """
+
+ self.firmware_version = [data[0], data[1], data[2]]
+
+ def _i2c_read_report(self, data):
+ """
+ Execute callback for i2c reads.
+
+ :param data: [I2C_READ_REPORT, i2c_port, number of bytes read, address, register, bytes read..., time-stamp]
+ """
+
+ # we receive [# data bytes, address, register, data bytes]
+ # number of bytes of data returned
+
+ # data[0] = number of bytes
+ # data[1] = i2c_port
+ # data[2] = number of bytes returned
+ # data[3] = address
+ # data[4] = register
+ # data[5] ... all the data bytes
+
+ cb_list = [PrivateConstants.I2C_READ_REPORT, data[0], data[1]] + data[2:]
+ cb_list.append(time.time())
+
+ if cb_list[1]:
+ self.i2c_callback2(cb_list)
+ else:
+ self.i2c_callback(cb_list)
+
+ def _i2c_too_few(self, data):
+ """
+ I2c reports too few bytes received
+
+ :param data: data[0] = device address
+ """
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(
+ f'i2c too few bytes received from i2c port {data[0]} i2c address {data[1]}')
+
+ def _i2c_too_many(self, data):
+ """
+ I2c reports too few bytes received
+
+ :param data: data[0] = device address
+ """
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(
+ f'i2c too many bytes received from i2c port {data[0]} i2c address {data[1]}')
+
+ def _i_am_here(self, data):
+ """
+ Reply to are_u_there message
+ :param data: arduino id
+ """
+ self.reported_arduino_id = data[0]
+
+ def _spi_report(self, report):
+
+ cb_list = [PrivateConstants.SPI_REPORT, report[0]] + report[1:]
+
+ cb_list.append(time.time())
+
+ self.spi_callback(cb_list)
+
+ def _onewire_report(self, report):
+ cb_list = [PrivateConstants.ONE_WIRE_REPORT, report[0]] + report[1:]
+ cb_list.append(time.time())
+ self.onewire_callback(cb_list)
+
+ def _report_debug_data(self, data):
+ """
+ Print debug data sent from Arduino
+ :param data: data[0] is a byte followed by 2
+ bytes that comprise an integer
+ :return:
+ """
+ value = (data[1] << 8) + data[2]
+ print(f'DEBUG ID: {data[0]} Value: {value}')
+
+ def _report_loop_data(self, data):
+ """
+ Print data that was looped back
+ :param data: byte of loop back data
+ :return:
+ """
+ if self.loop_back_callback:
+ self.loop_back_callback(data)
+
+ def _send_command(self, command):
+ """
+ This is a private utility method.
+
+
+ :param command: command data in the form of a list
+
+ """
+ # the length of the list is added at the head
+ command.insert(0, len(command))
+ send_message = bytes(command)
+
+ if self.serial_port:
+ try:
+ self.serial_port.write(send_message)
+ except SerialException:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError('write fail in _send_command')
+ else:
+ raise RuntimeError('No serial port set.')
+
+ def _servo_unavailable(self, report):
+ """
+ Message if no servos are available for use.
+ :param report: pin number
+ """
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(
+ f'Servo Attach For Pin {report[0]} Failed: No Available Servos')
+
+ def _sonar_distance_report(self, report):
+ """
+
+ :param report: data[0] = trigger pin, data[1] and data[2] = distance
+
+ callback report format: [PrivateConstants.SONAR_DISTANCE, trigger_pin, distance_value, time_stamp]
+ """
+
+ # get callback from pin number
+ cb = self.sonar_callbacks[report[0]]
+
+ # build report data
+ cb_list = [PrivateConstants.SONAR_DISTANCE, report[0],
+ ((report[1] << 8) + report[2]), time.time()]
+
+ cb(cb_list)
+
+ def _stepper_distance_to_go_report(self, report):
+ return # for now
+ # """
+ # Report stepper distance to go.
+ #
+ # :param report: data[0] = motor_id, data[1] = steps MSB, data[2] = steps byte 1,
+ # data[3] = steps bytes 2, data[4] = steps LSB
+ #
+ # callback report format: [PrivateConstants.STEPPER_DISTANCE_TO_GO, motor_id
+ # steps, time_stamp]
+ # """
+ #
+ # # get callback
+ # cb = self.stepper_info_list[report[0]]['distance_to_go_callback']
+ #
+ # # isolate the steps bytes and covert list to bytes
+ # steps = bytes(report[1:])
+ #
+ # # get value from steps
+ # num_steps = int.from_bytes(steps, byteorder='big', signed=True)
+ #
+ # cb_list = [PrivateConstants.STEPPER_DISTANCE_TO_GO, report[0], num_steps,
+ # time.time()]
+ #
+ # cb(cb_list)
+ #
+
+ def _stepper_target_position_report(self, report):
+ return # for now
+ # """
+ # Report stepper target position to go.
+ #
+ # :param report: data[0] = motor_id, data[1] = target position MSB,
+ # data[2] = target position byte MSB+1
+ # data[3] = target position byte MSB+2
+ # data[4] = target position LSB
+ #
+ # callback report format: [PrivateConstants.STEPPER_TARGET_POSITION, motor_id
+ # target_position, time_stamp]
+ # """
+ #
+ # # get callback
+ # cb = self.stepper_info_list[report[0]]['target_position_callback']
+ #
+ # # isolate the steps bytes and covert list to bytes
+ # target = bytes(report[1:])
+ #
+ # # get value from steps
+ # target_position = int.from_bytes(target, byteorder='big', signed=True)
+ #
+ # cb_list = [PrivateConstants.STEPPER_TARGET_POSITION, report[0], target_position,
+ # time.time()]
+ #
+ # cb(cb_list)
+ #
+
+ def _stepper_current_position_report(self, report):
+ return # for now
+ # """
+ # Report stepper current position.
+ #
+ # :param report: data[0] = motor_id, data[1] = current position MSB,
+ # data[2] = current position byte MSB+1
+ # data[3] = current position byte MSB+2
+ # data[4] = current position LSB
+ #
+ # callback report format: [PrivateConstants.STEPPER_CURRENT_POSITION, motor_id
+ # current_position, time_stamp]
+ # """
+ #
+ # # get callback
+ # cb = self.stepper_info_list[report[0]]['current_position_callback']
+ #
+ # # isolate the steps bytes and covert list to bytes
+ # position = bytes(report[1:])
+ #
+ # # get value from steps
+ # current_position = int.from_bytes(position, byteorder='big', signed=True)
+ #
+ # cb_list = [PrivateConstants.STEPPER_CURRENT_POSITION, report[0], current_position,
+ # time.time()]
+ #
+ # cb(cb_list)
+ #
+
+ def _stepper_is_running_report(self, report):
+ return # for now
+ # """
+ # Report if the motor is currently running
+ #
+ # :param report: data[0] = motor_id, True if motor is running or False if it is not.
+ #
+ # callback report format: [18, motor_id,
+ # running_state, time_stamp]
+ # """
+ #
+ # # get callback
+ # cb = self.stepper_info_list[report[0]]['is_running_callback']
+ #
+ # cb_list = [PrivateConstants.STEPPER_RUNNING_REPORT, report[0], time.time()]
+ #
+ # cb(cb_list)
+ #
+
+ def _stepper_run_complete_report(self, report):
+ return # for now
+ # """
+ # The motor completed it motion
+ #
+ # :param report: data[0] = motor_id
+ #
+ # callback report format: [PrivateConstants.STEPPER_RUN_COMPLETE_REPORT, motor_id,
+ # time_stamp]
+ # """
+ #
+ # # get callback
+ # cb = self.stepper_info_list[report[0]]['motion_complete_callback']
+ #
+ # cb_list = [PrivateConstants.STEPPER_RUN_COMPLETE_REPORT, report[0],
+ # time.time()]
+ #
+ # cb(cb_list)
+
+ def _features_report(self, report):
+ self.reported_features = report[0]
+
+ def _run_threads(self):
+ self.run_event.set()
+
+ def _is_running(self):
+ return self.run_event.is_set()
+
+ def _stop_threads(self):
+ self.run_event.clear()
+
+ def _reporter(self):
+ """
+ This is the reporter thread. It continuously pulls data from
+ the deque. When a full message is detected, that message is
+ processed.
+ """
+ self.run_event.wait()
+
+ while self._is_running() and not self.shutdown_flag:
+ if len(self.the_deque):
+ # response_data will be populated with the received data for the report
+ response_data = []
+ packet_length = self.the_deque.popleft()
+ if packet_length:
+ # get all the data for the report and place it into response_data
+ for i in range(packet_length):
+ while not len(self.the_deque):
+ time.sleep(self.sleep_tune)
+ data = self.the_deque.popleft()
+ response_data.append(data)
+
+ # print(f'response_data {response_data}')
+
+ # get the report type and look up its dispatch method
+ # here we pop the report type off of response_data
+ report_type = response_data.pop(0)
+ # print(f' reported type {report_type}')
+
+ # retrieve the report handler from the dispatch table
+ dispatch_entry = self.report_dispatch.get(report_type)
+
+ # if there is additional data for the report,
+ # it will be contained in response_data
+ # noinspection PyArgumentList
+ dispatch_entry(response_data)
+ continue
+ else:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(
+ 'A report with a packet length of zero was received.')
+ else:
+ time.sleep(self.sleep_tune)
+
+ def _serial_receiver(self):
+ """
+ Thread to continuously check for incoming data.
+ When a byte comes in, place it onto the deque.
+ """
+ self.run_event.wait()
+
+ # Don't start this thread if using a tcp/ip transport
+
+ while self._is_running() and not self.shutdown_flag:
+ # we can get an OSError: [Errno9] Bad file descriptor when shutting down
+ # just ignore it
+ try:
+ if self.serial_port.inWaiting():
+ c = self.serial_port.read()
+ self.the_deque.append(ord(c))
+ # print(ord(c))
+ else:
+ time.sleep(self.sleep_tune)
+ # continue
+ except OSError:
+ pass
diff --git a/telemetrix_uno_r4/minima/telemetrix_uno_r4_minima_aio/__init__.py b/telemetrix_uno_r4/minima/telemetrix_uno_r4_minima_aio/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/telemetrix_uno_r4/minima/telemetrix_uno_r4_minima_aio/private_constants.py b/telemetrix_uno_r4/minima/telemetrix_uno_r4_minima_aio/private_constants.py
new file mode 100644
index 0000000..c4b7f48
--- /dev/null
+++ b/telemetrix_uno_r4/minima/telemetrix_uno_r4_minima_aio/private_constants.py
@@ -0,0 +1,149 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+
+class PrivateConstants:
+ """
+ This class contains a set of constants for telemetrix internal use .
+ """
+
+ # commands
+ # send a loop back request - for debugging communications
+ LOOP_COMMAND = 0
+ SET_PIN_MODE = 1 # set a pin to INPUT/OUTPUT/PWM/etc
+ DIGITAL_WRITE = 2 # set a single digital pin value instead of entire port
+ ANALOG_WRITE = 3
+ MODIFY_REPORTING = 4
+ GET_FIRMWARE_VERSION = 5
+ ARE_U_THERE = 6 # Arduino ID query for auto-detect of telemetrix connected boards
+ SERVO_ATTACH = 7
+ SERVO_WRITE = 8
+ SERVO_DETACH = 9
+ I2C_BEGIN = 10
+ I2C_READ = 11
+ I2C_WRITE = 12
+ SONAR_NEW = 13
+ DHT_NEW = 14
+ STOP_ALL_REPORTS = 15
+ SET_ANALOG_SCANNING_INTERVAL = 16
+ ENABLE_ALL_REPORTS = 17
+ RESET = 18
+ SPI_INIT = 19
+ SPI_WRITE_BLOCKING = 20
+ SPI_READ_BLOCKING = 21
+ SPI_SET_FORMAT = 22
+ SPI_CS_CONTROL = 23
+ ONE_WIRE_INIT = 24
+ ONE_WIRE_RESET = 25
+ ONE_WIRE_SELECT = 26
+ ONE_WIRE_SKIP = 27
+ ONE_WIRE_WRITE = 28
+ ONE_WIRE_READ = 29
+ ONE_WIRE_RESET_SEARCH = 30
+ ONE_WIRE_SEARCH = 31
+ ONE_WIRE_CRC8 = 32
+ SET_PIN_MODE_STEPPER = 33
+ STEPPER_MOVE_TO = 34
+ STEPPER_MOVE = 35
+ STEPPER_RUN = 36
+ STEPPER_RUN_SPEED = 37
+ STEPPER_SET_MAX_SPEED = 38
+ STEPPER_SET_ACCELERATION = 39
+ STEPPER_SET_SPEED = 40
+ STEPPER_SET_CURRENT_POSITION = 41
+ STEPPER_RUN_SPEED_TO_POSITION = 42
+ STEPPER_STOP = 43
+ STEPPER_DISABLE_OUTPUTS = 44
+ STEPPER_ENABLE_OUTPUTS = 45
+ STEPPER_SET_MINIMUM_PULSE_WIDTH = 46
+ STEPPER_SET_ENABLE_PIN = 47
+ STEPPER_SET_3_PINS_INVERTED = 48
+ STEPPER_SET_4_PINS_INVERTED = 49
+ STEPPER_IS_RUNNING = 50
+ STEPPER_GET_CURRENT_POSITION = 51
+ STEPPER_GET_DISTANCE_TO_GO = 52
+ STEPPER_GET_TARGET_POSITION = 53
+ GET_FEATURES = 54
+ SONAR_DISABLE = 55
+ SONAR_ENABLE = 56
+ BOARD_HARD_RESET = 57
+
+ # reports
+ # debug data from Arduino
+ DIGITAL_REPORT = DIGITAL_WRITE
+ ANALOG_REPORT = ANALOG_WRITE
+ FIRMWARE_REPORT = GET_FIRMWARE_VERSION
+ I_AM_HERE_REPORT = ARE_U_THERE
+ SERVO_UNAVAILABLE = SERVO_ATTACH
+ I2C_TOO_FEW_BYTES_RCVD = 8
+ I2C_TOO_MANY_BYTES_RCVD = 9
+ I2C_READ_REPORT = 10
+ SONAR_DISTANCE = 11
+ DHT_REPORT = 12
+ SPI_REPORT = 13
+ ONE_WIRE_REPORT = 14
+ STEPPER_DISTANCE_TO_GO = 15
+ STEPPER_TARGET_POSITION = 16
+ STEPPER_CURRENT_POSITION = 17
+ STEPPER_RUNNING_REPORT = 18
+ STEPPER_RUN_COMPLETE_REPORT = 19
+ FEATURES = 20
+
+ DEBUG_PRINT = 99
+
+ TELEMETRIX_AIO_VERSION = "1.0.0"
+
+ # reporting control
+ REPORTING_DISABLE_ALL = 0
+ REPORTING_ANALOG_ENABLE = 1
+ REPORTING_DIGITAL_ENABLE = 2
+ REPORTING_ANALOG_DISABLE = 3
+ REPORTING_DIGITAL_DISABLE = 4
+
+ # Pin mode definitions
+ AT_INPUT = 0
+ AT_OUTPUT = 1
+ AT_INPUT_PULLUP = 2
+ AT_ANALOG = 3
+ AT_SERVO = 4
+ AT_SONAR = 5
+ AT_DHT = 6
+ AT_MODE_NOT_SET = 255
+
+ # maximum number of digital pins supported
+ NUMBER_OF_DIGITAL_PINS = 100
+
+ # maximum number of analog pins supported
+ NUMBER_OF_ANALOG_PINS = 20
+
+ # maximum number of sonars allowed
+ MAX_SONARS = 6
+
+ # maximum number of DHT devices allowed
+ MAX_DHTS = 6
+
+ # DHT Report sub-types
+ DHT_DATA = 0
+ DHT_ERROR = 1
+
+# feature masks
+ ONEWIRE_FEATURE = 0x01
+ DHT_FEATURE = 0x02
+ STEPPERS_FEATURE = 0x04
+ SPI_FEATURE = 0x08
+ SERVO_FEATURE = 0x10
+ SONAR_FEATURE = 0x20
\ No newline at end of file
diff --git a/telemetrix_uno_r4/minima/telemetrix_uno_r4_minima_aio/telemetrix_aio_serial.py b/telemetrix_uno_r4/minima/telemetrix_uno_r4_minima_aio/telemetrix_aio_serial.py
new file mode 100644
index 0000000..2fa6e05
--- /dev/null
+++ b/telemetrix_uno_r4/minima/telemetrix_uno_r4_minima_aio/telemetrix_aio_serial.py
@@ -0,0 +1,214 @@
+# -*- coding: utf-8 -*-
+"""
+ Copyright (c) 2015-2020 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+import serial
+import time
+
+LF = 0x0a
+
+
+# noinspection PyStatementEffect,PyUnresolvedReferences,PyUnresolvedReferences
+class TelemetrixAioSerial:
+ """
+ This class encapsulates management of the serial port that communicates
+ with the Arduino Firmata
+ It provides a 'futures' interface to make Pyserial compatible with asyncio
+ """
+
+ def __init__(self, com_port='/dev/ttyACM0', baud_rate=115200, sleep_tune=.0001,
+ telemetrix_aio_instance=None, close_loop_on_error=True):
+
+ """
+ This is the constructor for the aio serial handler
+
+ :param com_port: Com port designator
+
+ :param baud_rate: UART baud rate
+
+ :param telemetrix_aio_instance: reference to caller
+
+ :return: None
+ """
+ # print('Initializing Arduino - Please wait...', end=" ")
+ sys.stdout.flush()
+ self.my_serial = serial.Serial(com_port, baud_rate, timeout=1,
+ writeTimeout=1)
+
+ self.com_port = com_port
+ self.sleep_tune = sleep_tune
+ self.telemetrix_aio_instance = telemetrix_aio_instance
+ self.close_loop_on_error = close_loop_on_error
+
+ # used by read_until
+ self.start_time = None
+
+ async def get_serial(self):
+ """
+ This method returns a reference to the serial port in case the
+ user wants to call pyserial methods directly
+
+ :return: pyserial instance
+ """
+ return self.my_serial
+
+ async def write(self, data):
+ """
+ This is an asyncio adapted version of pyserial write. It provides a
+ non-blocking write and returns the number of bytes written upon
+ completion
+
+ :param data: Data to be written
+ :return: Number of bytes written
+ """
+ # the secret sauce - it is in your future
+ future = asyncio.Future()
+ result = None
+ try:
+ # result = self.my_serial.write(bytes([ord(data)]))
+ result = self.my_serial.write(bytes(data))
+
+ except serial.SerialException:
+ # noinspection PyBroadException
+ loop = None
+ await self.close()
+ future.cancel()
+ if self.close_loop_on_error:
+ loop = asyncio.get_event_loop()
+ loop.stop()
+
+ if self.telemetrix_aio_instance.the_task:
+ self.telemetrix_aio_instance.the_task.cancel()
+ await asyncio.sleep(1)
+ if self.close_loop_on_error:
+ loop.close()
+
+ if result:
+ future.set_result(result)
+ while True:
+ if not future.done():
+ # spin our asyncio wheels until future completes
+ await asyncio.sleep(self.sleep_tune)
+
+ else:
+ return future.result()
+
+ async def read(self, size=1):
+ """
+ This is an asyncio adapted version of pyserial read
+ that provides non-blocking read.
+
+ :return: One character
+ """
+
+ # create an asyncio Future
+ future = asyncio.Future()
+
+ # create a flag to indicate when data becomes available
+ data_available = False
+
+ # wait for a character to become available and read from
+ # the serial port
+ while True:
+ if not data_available:
+ # test to see if a character is waiting to be read.
+ # if not, relinquish control back to the event loop through the
+ # short sleep
+ if not self.my_serial.in_waiting:
+ await asyncio.sleep(self.sleep_tune*2)
+
+ # data is available.
+ # set the flag to true so that the future can "wait" until the
+ # read is completed.
+ else:
+ data_available = True
+ data = self.my_serial.read(size)
+ # set future result to make the character available
+ if size == 1:
+ future.set_result(ord(data))
+ else:
+ future.set_result(list(data))
+ else:
+ # wait for the future to complete
+ if not future.done():
+ await asyncio.sleep(self.sleep_tune)
+ else:
+ # future is done, so return the character
+ return future.result()
+
+ async def read_until(self, expected=LF, size=None, timeout=1):
+ """
+ This is an asyncio adapted version of pyserial read
+ that provides non-blocking read.
+
+ :return: Data delimited by expected
+ """
+
+ expected = str(expected).encode()
+ # create an asyncio Future
+ future = asyncio.Future()
+
+ # create a flag to indicate when data becomes available
+ data_available = False
+
+ if timeout:
+ self.start_time = time.time()
+
+ # wait for a character to become available and read from
+ # the serial port
+ while True:
+ if not data_available:
+ # test to see if a character is waiting to be read.
+ # if not, relinquish control back to the event loop through the
+ # short sleep
+ if not self.my_serial.in_waiting:
+ if timeout:
+ elapsed_time = time.time() - self.start_time
+ if elapsed_time > timeout:
+ return None
+ await asyncio.sleep(self.sleep_tune)
+ # data is available.
+ # set the flag to true so that the future can "wait" until the
+ # read is completed.
+ else:
+ data_available = True
+ data = self.my_serial.read_until(expected, size)
+ # set future result to make the character available
+ return_value = list(data)
+ future.set_result(return_value)
+ else:
+ # wait for the future to complete
+ if not future.done():
+ await asyncio.sleep(self.sleep_tune)
+ else:
+ # future is done, so return the character
+ return future.result()
+
+ async def reset_input_buffer(self):
+ """
+ Reset the input buffer
+ """
+ self.my_serial.reset_input_buffer()
+
+ async def close(self):
+ """
+ Close the serial port
+ """
+ if self.my_serial:
+ self.my_serial.close()
diff --git a/telemetrix_uno_r4/minima/telemetrix_uno_r4_minima_aio/telemetrix_uno_r4_minima_aio.py b/telemetrix_uno_r4/minima/telemetrix_uno_r4_minima_aio/telemetrix_uno_r4_minima_aio.py
new file mode 100644
index 0000000..d550c0c
--- /dev/null
+++ b/telemetrix_uno_r4/minima/telemetrix_uno_r4_minima_aio/telemetrix_uno_r4_minima_aio.py
@@ -0,0 +1,2500 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+import time
+
+# noinspection PyPackageRequirementscd
+from serial.serialutil import SerialException
+# noinspection PyPackageRequirements
+from serial.tools import list_ports
+
+# noinspection PyUnresolvedReferences
+from telemetrix_uno_r4.minima.telemetrix_uno_r4_minima_aio.private_constants import PrivateConstants
+from telemetrix_uno_r4.minima.telemetrix_uno_r4_minima_aio.telemetrix_aio_serial import TelemetrixAioSerial
+
+
+# noinspection GrazieInspection,PyArgumentList,PyMethodMayBeStatic,PyRedundantParentheses
+class TelemetrixUnoR4MinimaAio:
+ """
+ This class exposes and implements the TelemetrixAIO API.
+ It includes the public API methods as well as
+ a set of private methods. This is an asyncio API.
+
+ """
+
+ # noinspection PyPep8,PyPep8
+ def __init__(self, com_port=None,
+ arduino_instance_id=1, arduino_wait=1,
+ sleep_tune=0.0001, autostart=True,
+ loop=None, shutdown_on_exception=True,
+ close_loop_on_shutdown=True, hard_reset_on_shutdown=True):
+
+ """
+ If you have a single Arduino connected to your computer,
+ then you may accept all the default values.
+
+ Otherwise, specify a unique arduino_instance id for each board in use.
+
+ :param com_port: e.g. COM3 or /dev/ttyACM0.
+
+ :param arduino_instance_id: Must match value in the Telemetrix4Arduino sketch
+
+ :param arduino_wait: Amount of time to wait for an Arduino to
+ fully reset itself.
+
+ :param sleep_tune: A tuning parameter (typically not changed by user)
+
+ :param autostart: If you wish to call the start method within
+ your application, then set this to False.
+
+ :param loop: optional user provided event loop
+
+ :param shutdown_on_exception: call shutdown before raising
+ a RunTimeError exception, or
+ receiving a KeyboardInterrupt exception
+
+ :param close_loop_on_shutdown: stop and close the event loop loop
+ when a shutdown is called or a serial
+ error occurs
+
+ :param hard_reset_on_shutdown: reset the board on shutdown
+
+ """
+ # check to make sure that Python interpreter is version 3.8.3 or greater
+ python_version = sys.version_info
+ if python_version[0] >= 3:
+ if python_version[1] >= 8:
+ if python_version[2] >= 3:
+ pass
+ else:
+ raise RuntimeError("ERROR: Python 3.7 or greater is "
+ "required for use of this program.")
+
+ # save input parameters
+ self.com_port = com_port
+ self.arduino_instance_id = arduino_instance_id
+ self.arduino_wait = arduino_wait
+ self.sleep_tune = sleep_tune
+ self.autostart = autostart
+ self.hard_reset_on_shutdown = hard_reset_on_shutdown
+
+ # set the event loop
+ if loop is None:
+ self.loop = asyncio.get_event_loop()
+ else:
+ self.loop = loop
+
+ self.shutdown_on_exception = shutdown_on_exception
+ self.close_loop_on_shutdown = close_loop_on_shutdown
+
+ # dictionaries to store the callbacks for each pin
+ self.analog_callbacks = {}
+
+ self.digital_callbacks = {}
+
+ self.i2c_callback = None
+ self.i2c_callback2 = None
+
+ self.i2c_1_active = False
+ self.i2c_2_active = False
+
+ self.spi_callback = None
+
+ self.onewire_callback = None
+
+ # debug loopback callback method
+ self.loop_back_callback = None
+
+ # the trigger pin will be the key to retrieve
+ # the callback for a specific HC-SR04
+ self.sonar_callbacks = {}
+
+ self.sonar_count = 0
+
+ self.dht_callbacks = {}
+
+ self.dht_count = 0
+
+ # serial port in use
+ self.serial_port = None
+
+ # generic asyncio task holder
+ self.the_task = None
+
+ # flag to indicate we are in shutdown mode
+ self.shutdown_flag = False
+
+ self.report_dispatch = {}
+
+ # reported features
+ self.reported_features = 0
+
+ # To add a command to the command dispatch table, append here.
+ self.report_dispatch.update(
+ {PrivateConstants.LOOP_COMMAND: self._report_loop_data})
+ self.report_dispatch.update(
+ {PrivateConstants.DEBUG_PRINT: self._report_debug_data})
+ self.report_dispatch.update(
+ {PrivateConstants.DIGITAL_REPORT: self._digital_message})
+ self.report_dispatch.update(
+ {PrivateConstants.ANALOG_REPORT: self._analog_message})
+ self.report_dispatch.update(
+ {PrivateConstants.SERVO_UNAVAILABLE: self._servo_unavailable})
+ self.report_dispatch.update(
+ {PrivateConstants.I2C_READ_REPORT: self._i2c_read_report})
+ self.report_dispatch.update(
+ {PrivateConstants.I2C_TOO_FEW_BYTES_RCVD: self._i2c_too_few})
+ self.report_dispatch.update(
+ {PrivateConstants.I2C_TOO_MANY_BYTES_RCVD: self._i2c_too_many})
+ self.report_dispatch.update(
+ {PrivateConstants.SONAR_DISTANCE: self._sonar_distance_report})
+ self.report_dispatch.update({PrivateConstants.DHT_REPORT: self._dht_report})
+ self.report_dispatch.update(
+ {PrivateConstants.SPI_REPORT: self._spi_report})
+ self.report_dispatch.update(
+ {PrivateConstants.ONE_WIRE_REPORT: self._onewire_report})
+ self.report_dispatch.update(
+ {PrivateConstants.STEPPER_DISTANCE_TO_GO:
+ self._stepper_distance_to_go_report})
+ self.report_dispatch.update(
+ {PrivateConstants.STEPPER_TARGET_POSITION:
+ self._stepper_target_position_report})
+ self.report_dispatch.update(
+ {PrivateConstants.STEPPER_CURRENT_POSITION:
+ self._stepper_current_position_report})
+ self.report_dispatch.update(
+ {PrivateConstants.STEPPER_RUNNING_REPORT:
+ self._stepper_is_running_report})
+ self.report_dispatch.update(
+ {PrivateConstants.STEPPER_RUN_COMPLETE_REPORT:
+ self._stepper_run_complete_report})
+ self.report_dispatch.update(
+ {PrivateConstants.STEPPER_DISTANCE_TO_GO:
+ self._stepper_distance_to_go_report})
+ self.report_dispatch.update(
+ {PrivateConstants.STEPPER_TARGET_POSITION:
+ self._stepper_target_position_report})
+ self.report_dispatch.update(
+ {PrivateConstants.FEATURES:
+ self._features_report})
+
+ # dictionaries to store the callbacks for each pin
+ self.analog_callbacks = {}
+
+ self.digital_callbacks = {}
+
+ self.i2c_callback = None
+ self.i2c_callback2 = None
+
+ self.i2c_1_active = False
+ self.i2c_2_active = False
+
+ self.spi_callback = None
+
+ self.onewire_callback = None
+
+ self.cs_pins_enabled = []
+
+ # flag to indicate if spi is initialized
+ self.spi_enabled = False
+
+ # flag to indicate if onewire is initialized
+ self.onewire_enabled = False
+
+ # the trigger pin will be the key to retrieve
+ # the callback for a specific HC-SR04
+ self.sonar_callbacks = {}
+
+ self.sonar_count = 0
+
+ self.dht_callbacks = {}
+
+ # # stepper motor variables
+ #
+ # # updated when a new motor is added
+ # self.next_stepper_assigned = 0
+ #
+ # # valid list of stepper motor interface types
+ # self.valid_stepper_interfaces = [1, 2, 3, 4, 6, 8]
+ #
+ # # maximum number of steppers supported
+ # self.max_number_of_steppers = 4
+ #
+ # # number of steppers created - not to exceed the maximum
+ # self.number_of_steppers = 0
+ #
+ # # dictionary to hold stepper motor information
+ # self.stepper_info = {'instance': False, 'is_running': None,
+ # 'maximum_speed': 1, 'speed': 0, 'acceleration': 0,
+ # 'distance_to_go_callback': None,
+ # 'target_position_callback': None,
+ # 'current_position_callback': None,
+ # 'is_running_callback': None,
+ # 'motion_complete_callback': None,
+ # 'acceleration_callback': None}
+ #
+ # # build a list of stepper motor info items
+ # self.stepper_info_list = []
+ # # a list of dictionaries to hold stepper information
+ # for motor in range(self.max_number_of_steppers):
+ # self.stepper_info_list.append(self.stepper_info)
+
+ print(f'telemetrix_uno_r4_minima_aio Version:'
+ f' {PrivateConstants.TELEMETRIX_AIO_VERSION}')
+ print(f'Copyright (c) 2023 Alan Yorinks All rights reserved.\n')
+
+ if autostart:
+ self.loop.run_until_complete(self.start_aio())
+
+ async def start_aio(self):
+ """
+ This method may be called directly, if the autostart
+ parameter in __init__ is set to false.
+
+ This method instantiates the serial interface and then performs auto pin
+ discovery if using a serial interface, or creates and connects to
+ a TCP/IP enabled device running StandardFirmataWiFi.
+
+ Use this method if you wish to start TelemetrixAIO manually from
+ an asyncio function.
+ """
+
+ if not self.com_port:
+ # user did not specify a com_port
+ try:
+ await self._find_arduino()
+ except KeyboardInterrupt:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ else:
+ # com_port specified - set com_port and baud rate
+ try:
+ await self._manual_open()
+ except KeyboardInterrupt:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+
+ if self.com_port:
+ print(f'Telemetrix4UnoR4 found and connected to {self.com_port}')
+
+ # no com_port found - raise a runtime exception
+ else:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError('No Arduino Found or User Aborted Program')
+
+ # get arduino firmware version and print it
+ firmware_version = await self._get_firmware_version()
+ if not firmware_version:
+ print('*** Firmware Version retrieval timed out. ***')
+ print('\nDo you have Arduino connectivity and do you have the ')
+ print('Telemetrix4UnoR4 sketch uploaded to the board and are connected')
+ print('to the correct serial port.\n')
+ print('To see a list of serial ports, type: '
+ '"list_serial_ports" in your console.')
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError
+ else:
+
+ print(f'Telemetrix4UnoR4 Version Number: {firmware_version[2]}.'
+ f'{firmware_version[3]}.{firmware_version[4]}')
+ # start the command dispatcher loop
+ command = [PrivateConstants.ENABLE_ALL_REPORTS]
+ await self._send_command(command)
+ if not self.loop:
+ self.loop = asyncio.get_event_loop()
+ self.the_task = self.loop.create_task(self._arduino_report_dispatcher())
+
+ # get the features list
+ command = [PrivateConstants.GET_FEATURES]
+ await self._send_command(command)
+ await asyncio.sleep(.5)
+
+ # Have the server reset its data structures
+ command = [PrivateConstants.RESET]
+ await self._send_command(command)
+
+ async def get_event_loop(self):
+ """
+ Return the currently active asyncio event loop
+
+ :return: Active event loop
+
+ """
+ return self.loop
+
+ async def _find_arduino(self):
+ """
+ This method will search all potential serial ports for an Arduino
+ containing a sketch that has a matching arduino_instance_id as
+ specified in the input parameters of this class.
+
+ This is used explicitly with the FirmataExpress sketch.
+ """
+
+ # a list of serial ports to be checked
+ serial_ports = []
+
+ print('Opening all potential serial ports...')
+ the_ports_list = list_ports.comports()
+ for port in the_ports_list:
+ if port.pid is None:
+ continue
+ print('\nChecking {}'.format(port.device))
+ try:
+ self.serial_port = TelemetrixAioSerial(port.device, 115200,
+ telemetrix_aio_instance=self,
+ close_loop_on_error=self.close_loop_on_shutdown)
+ except SerialException:
+ continue
+ # create a list of serial ports that we opened
+ serial_ports.append(self.serial_port)
+
+ # display to the user
+ print('\t' + port.device)
+
+ # clear out any possible data in the input buffer
+ await self.serial_port.reset_input_buffer()
+
+ # wait for arduino to reset
+ print('\nWaiting {} seconds(arduino_wait) for Arduino devices to '
+ 'reset...'.format(self.arduino_wait))
+ await asyncio.sleep(self.arduino_wait)
+
+ print('\nSearching for an Arduino configured with an arduino_instance = ',
+ self.arduino_instance_id)
+
+ for serial_port in serial_ports:
+ self.serial_port = serial_port
+
+ command = [PrivateConstants.ARE_U_THERE]
+ await self._send_command(command)
+ # provide time for the reply
+ await asyncio.sleep(.1)
+
+ i_am_here = await self.serial_port.read(3)
+
+ if not i_am_here:
+ continue
+
+ # got an I am here message - is it the correct ID?
+ if i_am_here[2] == self.arduino_instance_id:
+ self.com_port = serial_port.com_port
+ return
+
+ async def _manual_open(self):
+ """
+ Com port was specified by the user - try to open up that port
+
+ """
+ # if port is not found, a serial exception will be thrown
+ print('Opening {} ...'.format(self.com_port))
+ self.serial_port = TelemetrixAioSerial(self.com_port, 115200,
+ telemetrix_aio_instance=self,
+ close_loop_on_error=self.close_loop_on_shutdown)
+
+ print('Waiting {} seconds for the Arduino To Reset.'
+ .format(self.arduino_wait))
+ await asyncio.sleep(self.arduino_wait)
+ command = [PrivateConstants.ARE_U_THERE]
+ await self._send_command(command)
+ # provide time for the reply
+ await asyncio.sleep(.1)
+
+ print(f'Searching for correct arduino_instance_id: {self.arduino_instance_id}')
+ i_am_here = await self.serial_port.read(3)
+
+ if not i_am_here:
+ print(f'ERROR: correct arduino_instance_id not found')
+
+ print('Correct arduino_instance_id found')
+
+ async def _get_firmware_version(self):
+ """
+ This method retrieves the Arduino4Telemetrix firmware version
+
+ :returns: Firmata firmware version
+ """
+ command = [PrivateConstants.GET_FIRMWARE_VERSION]
+ await self._send_command(command)
+ # provide time for the reply
+ await asyncio.sleep(.1)
+ firmware_version = await self.serial_port.read(5)
+
+ return firmware_version
+
+ async def analog_write(self, pin, value):
+ """
+ Set the specified pin to the specified value.
+
+ :param pin: arduino pin number
+
+ :param value: pin value (maximum 16 bits)
+
+ """
+ value_msb = value >> 8
+ value_lsb = value & 0xff
+ command = [PrivateConstants.ANALOG_WRITE, pin, value_msb, value_lsb]
+ await self._send_command(command)
+
+ async def digital_write(self, pin, value):
+ """
+ Set the specified pin to the specified value.
+
+ :param pin: arduino pin number
+
+ :param value: pin value (1 or 0)
+
+ """
+ command = [PrivateConstants.DIGITAL_WRITE, pin, value]
+ await self._send_command(command)
+
+ async def i2c_read(self, address, register, number_of_bytes,
+ callback, i2c_port=0,
+ write_register=True):
+ """
+ Read the specified number of bytes from the specified register for
+ the i2c device.
+
+
+ :param address: i2c device address
+
+ :param register: i2c register (or None if no register selection is needed)
+
+ :param number_of_bytes: number of bytes to be read
+
+ :param callback: Required callback function to report i2c data as a
+ result of read command
+
+ :param i2c_port: select the default port (0) or secondary port (1)
+
+ :param write_register: If True, the register is written
+ before read
+ Else, the write is suppressed
+
+
+ callback returns a data list:
+
+ [I2C_READ_REPORT, address, register, count of data bytes,
+ data bytes, time-stamp]
+
+ """
+ if not callback:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError('i2c_read: A Callback must be specified')
+
+ await self._i2c_read_request(address, register, number_of_bytes,
+ callback=callback, i2c_port=i2c_port,
+ write_register=write_register)
+
+ async def i2c_read_restart_transmission(self, address, register,
+ number_of_bytes,
+ callback, i2c_port=0,
+ write_register=True):
+ """
+ Read the specified number of bytes from the specified register for
+ the i2c device. This restarts the transmission after the read. It is
+ required for some i2c devices such as the MMA8452Q accelerometer.
+
+
+ :param address: i2c device address
+
+ :param register: i2c register (or None if no register
+ selection is needed)
+
+ :param number_of_bytes: number of bytes to be read
+
+ :param callback: Required callback function to report i2c data as a
+ result of read command
+
+ :param i2c_port: select the default port (0) or secondary port (1)
+
+ :param write_register: If True, the register is written
+ before read
+ Else, the write is suppressed
+
+ callback returns a data list:
+
+ [I2C_READ_REPORT, address, register, count of data bytes,
+ data bytes, time-stamp]
+
+ """
+ if not callback:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(
+ 'i2c_read_restart_transmission: A Callback must be specified')
+
+ await self._i2c_read_request(address, register, number_of_bytes,
+ stop_transmission=False,
+ callback=callback, i2c_port=i2c_port,
+ write_register=write_register)
+
+ async def _i2c_read_request(self, address, register, number_of_bytes,
+ stop_transmission=True, callback=None,
+ i2c_port=0, write_register=True):
+ """
+ This method requests the read of an i2c device. Results are retrieved
+ via callback.
+
+ :param address: i2c device address
+
+ :param register: register number (or None if no register selection is needed)
+
+ :param number_of_bytes: number of bytes expected to be returned
+
+ :param stop_transmission: stop transmission after read
+
+ :param callback: Required callback function to report i2c data as a
+ result of read command.
+
+ :param i2c_port: select the default port (0) or secondary port (1)
+
+ :param write_register: If True, the register is written
+ before read
+ Else, the write is suppressed
+
+ """
+ if not i2c_port:
+ if not self.i2c_1_active:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(
+ 'I2C Read: set_pin_mode i2c never called for i2c port 1.')
+
+ if i2c_port:
+ if not self.i2c_2_active:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(
+ 'I2C Read: set_pin_mode i2c never called for i2c port 2.')
+
+ if not callback:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError('I2C Read: A callback function must be specified.')
+
+ if not i2c_port:
+ self.i2c_callback = callback
+ else:
+ self.i2c_callback2 = callback
+
+ if not register:
+ register = 0
+
+ if write_register:
+ write_register = 1
+ else:
+ write_register = 0
+
+ # message contains:
+ # 1. address
+ # 2. register
+ # 3. number of bytes
+ # 4. restart_transmission - True or False
+ # 5. i2c port
+ # 6. suppress write flag
+
+ command = [PrivateConstants.I2C_READ, address, register, number_of_bytes,
+ stop_transmission, i2c_port, write_register]
+ await self._send_command(command)
+
+ async def i2c_write(self, address, args, i2c_port=0):
+ """
+ Write data to an i2c device.
+
+ :param address: i2c device address
+
+ :param i2c_port: 0= port 1, 1 = port 2
+
+ :param args: A variable number of bytes to be sent to the device
+ passed in as a list
+
+ """
+ if not i2c_port:
+ if not self.i2c_1_active:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(
+ 'I2C Write: set_pin_mode i2c never called for i2c port 1.')
+
+ if i2c_port:
+ if not self.i2c_2_active:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(
+ 'I2C Write: set_pin_mode i2c never called for i2c port 2.')
+
+ command = [PrivateConstants.I2C_WRITE, len(args), address, i2c_port]
+
+ for item in args:
+ command.append(item)
+
+ await self._send_command(command)
+
+ async def loop_back(self, start_character, callback):
+ """
+ This is a debugging method to send a character to the
+ Arduino device, and have the device loop it back.
+
+ :param start_character: The character to loop back. It should be
+ an integer.
+
+ :param callback: Looped back character will appear in the callback method
+
+ """
+
+ if not callback:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError('loop_back: A callback function must be specified.')
+ command = [PrivateConstants.LOOP_COMMAND, ord(start_character)]
+ self.loop_back_callback = callback
+ await self._send_command(command)
+
+ async def set_analog_scan_interval(self, interval):
+ """
+ Set the analog scanning interval.
+
+ :param interval: value of 0 - 255 - milliseconds
+ """
+
+ if 0 <= interval <= 255:
+ command = [PrivateConstants.SET_ANALOG_SCANNING_INTERVAL, interval]
+ await self._send_command(command)
+ else:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError('Analog interval must be between 0 and 255')
+
+ async def set_pin_mode_analog_input(self, pin_number, differential=0, callback=None):
+ """
+ Set a pin as an analog input.
+
+ :param pin_number: arduino pin number
+
+ :param callback: async callback function
+
+ :param differential: difference in previous to current value before
+ report will be generated
+
+ callback returns a data list:
+
+ [pin_type, pin_number, pin_value, raw_time_stamp]
+
+ The pin_type for analog input pins = 3
+
+ """
+
+ if not callback:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(
+ 'set_pin_mode_analog_input: A callback function must be specified.')
+
+ await self._set_pin_mode(pin_number, PrivateConstants.AT_ANALOG,
+ differential, callback=callback)
+
+ async def set_pin_mode_analog_output(self, pin_number):
+ """
+
+ Set a pin as a pwm (analog output) pin.
+
+ :param pin_number:arduino pin number
+
+ """
+
+ await self._set_pin_mode(pin_number, PrivateConstants.AT_OUTPUT, differential=0,
+ callback=None)
+
+ async def set_pin_mode_digital_input(self, pin_number, callback):
+ """
+ Set a pin as a digital input.
+
+ :param pin_number: arduino pin number
+
+ :param callback: async callback function
+
+ callback returns a data list:
+
+ [pin_type, pin_number, pin_value, raw_time_stamp]
+
+ The pin_type for all digital input pins = 2
+
+ """
+ await self._set_pin_mode(pin_number, PrivateConstants.AT_INPUT, differential=0,
+ callback=callback)
+
+ async def set_pin_mode_digital_input_pullup(self, pin_number, callback):
+ """
+ Set a pin as a digital input with pullup enabled.
+
+ :param pin_number: arduino pin number
+
+ :param callback: async callback function
+
+ callback returns a data list:
+
+ [pin_type, pin_number, pin_value, raw_time_stamp]
+
+ The pin_type for all digital input pins = 2
+
+ """
+ if not callback:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(
+ 'set_pin_mode_digital_input_pullup: A callback function must be specified.')
+
+ await self._set_pin_mode(pin_number, PrivateConstants.AT_INPUT_PULLUP,
+ differential=0, callback=callback)
+
+ async def set_pin_mode_digital_output(self, pin_number):
+ """
+ Set a pin as a digital output pin.
+
+ :param pin_number: arduino pin number
+ """
+
+ await self._set_pin_mode(pin_number, PrivateConstants.AT_OUTPUT, differential=0,
+ callback=None)
+
+ # noinspection PyIncorrectDocstring
+ async def set_pin_mode_i2c(self, i2c_port=0):
+ """
+ Establish the standard Arduino i2c pins for i2c utilization.
+
+ :param i2c_port: 0 = i2c1, 1 = i2c2
+
+ NOTES: 1. THIS METHOD MUST BE CALLED BEFORE ANY I2C REQUEST IS MADE
+ 2. Callbacks are set within the individual i2c read methods of this
+ API.
+
+ See i2c_read, or i2c_read_restart_transmission.
+
+ """
+ # test for i2c port 2
+ if i2c_port:
+ # if not previously activated set it to activated
+ # and the send a begin message for this port
+ if not self.i2c_2_active:
+ self.i2c_2_active = True
+ else:
+ return
+ # port 1
+ else:
+ if not self.i2c_1_active:
+ self.i2c_1_active = True
+ else:
+ return
+
+ command = [PrivateConstants.I2C_BEGIN, i2c_port]
+ await self._send_command(command)
+
+ async def set_pin_mode_dht(self, pin, callback=None, dht_type=22):
+ """
+
+ :param pin: connection pin
+
+ :param callback: callback function
+
+ :param dht_type: either 22 for DHT22 or 11 for DHT11
+
+ Error Callback: [DHT REPORT Type, DHT_ERROR_NUMBER, PIN, DHT_TYPE, Time]
+
+ Valid Data Callback: DHT REPORT Type, DHT_DATA=, PIN, DHT_TYPE, Humidity,
+ Temperature,
+ Time]
+
+ """
+ if self.reported_features & PrivateConstants.DHT_FEATURE:
+
+ if not callback:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError('set_pin_mode_dht: A Callback must be specified')
+
+ if self.dht_count < PrivateConstants.MAX_DHTS - 1:
+ self.dht_callbacks[pin] = callback
+ self.dht_count += 1
+
+ if dht_type != 22 and dht_type != 11:
+ dht_type = 22
+
+ command = [PrivateConstants.DHT_NEW, pin, dht_type]
+ await self._send_command(command)
+ else:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(
+ f'Maximum Number Of DHTs Exceeded - set_pin_mode_dht fails for pin {pin}')
+
+ else:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(f'The DHT feature is disabled in the server.')
+
+ async def set_pin_mode_servo(self, pin_number, min_pulse=544, max_pulse=2400):
+ """
+
+ Attach a pin to a servo motor
+
+ :param pin_number: pin
+
+ :param min_pulse: minimum pulse width
+
+ :param max_pulse: maximum pulse width
+
+ """
+ if self.reported_features & PrivateConstants.SERVO_FEATURE:
+
+ minv = (min_pulse).to_bytes(2, byteorder="big")
+ maxv = (max_pulse).to_bytes(2, byteorder="big")
+
+ command = [PrivateConstants.SERVO_ATTACH, pin_number,
+ minv[0], minv[1], maxv[0], maxv[1]]
+ await self._send_command(command)
+ else:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(f'The SERVO feature is disabled in the server.')
+
+ async def set_pin_mode_sonar(self, trigger_pin, echo_pin,
+ callback):
+ """
+
+ :param trigger_pin:
+
+ :param echo_pin:
+
+ :param callback: callback
+
+ """
+ if self.reported_features & PrivateConstants.SONAR_FEATURE:
+
+ if not callback:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError('set_pin_mode_sonar: A Callback must be specified')
+
+ if self.sonar_count < PrivateConstants.MAX_SONARS - 1:
+ self.sonar_callbacks[trigger_pin] = callback
+ self.sonar_count += 1
+
+ command = [PrivateConstants.SONAR_NEW, trigger_pin, echo_pin]
+ await self._send_command(command)
+ else:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(
+ f'Maximum Number Of Sonars Exceeded - set_pin_mode_sonar fails for pin {trigger_pin}')
+ else:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(f'The SONAR feature is disabled in the server.')
+
+ async def set_pin_mode_spi(self, chip_select_list=None):
+ """
+ Specify the list of chip select pins.
+
+ Standard Arduino MISO, MOSI and CLK pins are used for the board in use.
+
+ Chip Select is any digital output capable pin.
+
+ :param chip_select_list: this is a list of pins to be used for chip select.
+ The pins will be configured as output, and set to high
+ ready to be used for chip select.
+ NOTE: You must specify the chips select pins here!
+
+
+ command message: [command, number of cs pins, [cs pins...]]
+ """
+ if self.reported_features & PrivateConstants.SPI_FEATURE:
+
+ if type(chip_select_list) != list:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError('chip_select_list must be in the form of a list')
+ if not chip_select_list:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError('Chip select pins were not specified')
+
+ self.spi_enabled = True
+
+ command = [PrivateConstants.SPI_INIT, len(chip_select_list)]
+
+ for pin in chip_select_list:
+ command.append(pin)
+ self.cs_pins_enabled.append(pin)
+ await self._send_command(command)
+ else:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(f'The SPI feature is disabled in the server.')
+
+ # async def set_pin_mode_stepper(self, interface=1, pin1=2, pin2=3, pin3=4,
+ # pin4=5, enable=True):
+ # """
+ # Stepper motor support is implemented as a proxy for the
+ # the AccelStepper library for the Arduino.
+ #
+ # This feature is compatible with the TB6600 Motor Driver
+ #
+ # Note: It may not work for other driver types!
+ #
+ # https://github.com/waspinator/AccelStepper
+ #
+ # Instantiate a stepper motor.
+ #
+ # Initialize the interface and pins for a stepper motor.
+ #
+ # :param interface: Motor Interface Type:
+ #
+ # 1 = Stepper Driver, 2 driver pins required
+ #
+ # 2 = FULL2WIRE 2 wire stepper, 2 motor pins required
+ #
+ # 3 = FULL3WIRE 3 wire stepper, such as HDD spindle,
+ # 3 motor pins required
+ #
+ # 4 = FULL4WIRE, 4 wire full stepper, 4 motor pins
+ # required
+ #
+ # 6 = HALF3WIRE, 3 wire half stepper, such as HDD spindle,
+ # 3 motor pins required
+ #
+ # 8 = HALF4WIRE, 4 wire half stepper, 4 motor pins required
+ #
+ # :param pin1: Arduino digital pin number for motor pin 1
+ #
+ # :param pin2: Arduino digital pin number for motor pin 2
+ #
+ # :param pin3: Arduino digital pin number for motor pin 3
+ #
+ # :param pin4: Arduino digital pin number for motor pin 4
+ #
+ # :param enable: If this is true, the output pins at construction time.
+ #
+ # :return: Motor Reference number
+ # """
+ # if self.reported_features & PrivateConstants.STEPPERS_FEATURE:
+ #
+ # if self.number_of_steppers == self.max_number_of_steppers:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('Maximum number of steppers has already been assigned')
+ #
+ # if interface not in self.valid_stepper_interfaces:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('Invalid stepper interface')
+ #
+ # self.number_of_steppers += 1
+ #
+ # motor_id = self.next_stepper_assigned
+ # self.next_stepper_assigned += 1
+ # self.stepper_info_list[motor_id]['instance'] = True
+ #
+ # # build message and send message to server
+ # command = [PrivateConstants.SET_PIN_MODE_STEPPER, motor_id, interface, pin1,
+ # pin2, pin3, pin4, enable]
+ # await self._send_command(command)
+ #
+ # # return motor id
+ # return motor_id
+ # else:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError(f'The Stepper feature is disabled in the server.')
+
+ async def sonar_disable(self):
+ """
+ Disable sonar scanning for all sonar sensors
+ """
+ command = [PrivateConstants.SONAR_DISABLE]
+ await self._send_command(command)
+
+ async def sonar_enable(self):
+ """
+ Enable sonar scanning for all sonar sensors
+ """
+ command = [PrivateConstants.SONAR_ENABLE]
+ await self._send_command(command)
+
+ async def spi_cs_control(self, chip_select_pin, select):
+ """
+ Control an SPI chip select line
+ :param chip_select_pin: pin connected to CS
+
+ :param select: 0=select, 1=deselect
+ """
+ if not self.spi_enabled:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(f'spi_cs_control: SPI interface is not enabled.')
+
+ if chip_select_pin not in self.cs_pins_enabled:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(f'spi_cs_control: chip select pin never enabled.')
+ command = [PrivateConstants.SPI_CS_CONTROL, chip_select_pin, select]
+ await self._send_command(command)
+
+ async def spi_read_blocking(self, chip_select, register_selection,
+ number_of_bytes_to_read,
+ call_back=None):
+ """
+ Read the specified number of bytes from the specified SPI port and
+ call the callback function with the reported data.
+
+ :param chip_select: chip select pin
+
+ :param register_selection: Register to be selected for read.
+
+ :param number_of_bytes_to_read: Number of bytes to read
+
+ :param call_back: Required callback function to report spi data as a
+ result of read command
+
+
+ callback returns a data list:
+ [SPI_READ_REPORT, chip select pin, SPI Register, count of data bytes read,
+ data bytes, time-stamp]
+ SPI_READ_REPORT = 13
+
+ """
+
+ if not self.spi_enabled:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(f'spi_read_blocking: SPI interface is not enabled.')
+
+ if not call_back:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError('spi_read_blocking: A Callback must be specified')
+
+ self.spi_callback = call_back
+
+ command = [PrivateConstants.SPI_READ_BLOCKING, chip_select,
+ number_of_bytes_to_read,
+ register_selection]
+
+ await self._send_command(command)
+
+ async def spi_set_format(self, clock_divisor, bit_order, data_mode):
+ """
+ Configure how the SPI serializes and de-serializes data on the wire.
+
+ See Arduino SPI reference materials for details.
+
+ :param clock_divisor: 1 - 255
+
+ :param bit_order:
+
+ LSBFIRST = 0
+
+ MSBFIRST = 1 (default)
+
+ :param data_mode:
+
+ SPI_MODE0 = 0x00 (default)
+
+ SPI_MODE1 = 1
+
+ SPI_MODE2 = 2
+
+ SPI_MODE3 = 3
+
+ """
+
+ if not self.spi_enabled:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(f'spi_set_format: SPI interface is not enabled.')
+
+ if not 0 < clock_divisor <= 255:
+ raise RuntimeError(f'spi_set_format: illegal clock divisor selected.')
+ if bit_order not in [0, 1]:
+ raise RuntimeError(f'spi_set_format: illegal bit_order selected.')
+ if data_mode not in [0, 1, 2, 3]:
+ raise RuntimeError(f'spi_set_format: illegal data_order selected.')
+
+ command = [PrivateConstants.SPI_SET_FORMAT, clock_divisor, bit_order,
+ data_mode]
+ await self._send_command(command)
+
+ async def spi_write_blocking(self, chip_select, bytes_to_write):
+ """
+ Write a list of bytes to the SPI device.
+
+ :param chip_select: chip select pin
+
+ :param bytes_to_write: A list of bytes to write. This must
+ be in the form of a list.
+
+ """
+
+ if not self.spi_enabled:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(f'spi_write_blocking: SPI interface is not enabled.')
+
+ if type(bytes_to_write) is not list:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError('spi_write_blocking: bytes_to_write must be a list.')
+
+ command = [PrivateConstants.SPI_WRITE_BLOCKING, chip_select, len(bytes_to_write)]
+
+ for data in bytes_to_write:
+ command.append(data)
+
+ await self._send_command(command)
+
+ # async def set_pin_mode_one_wire(self, pin):
+ # """
+ # Initialize the one wire serial bus.
+ #
+ # :param pin: Data pin connected to the OneWire device
+ # """
+ # self.onewire_enabled = True
+ # command = [PrivateConstants.ONE_WIRE_INIT, pin]
+ # await self._send_command(command)
+ #
+ # async def onewire_reset(self, callback=None):
+ # """
+ # Reset the onewire device
+ #
+ # :param callback: required function to report reset result
+ #
+ # callback returns a list:
+ # [ReportType = 14, Report Subtype = 25, reset result byte,
+ # timestamp]
+ # """
+ # if not self.onewire_enabled:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError(f'onewire_reset: OneWire interface is not enabled.')
+ # if not callback:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('onewire_reset: A Callback must be specified')
+ #
+ # self.onewire_callback = callback
+ #
+ # command = [PrivateConstants.ONE_WIRE_RESET]
+ # await self._send_command(command)
+ #
+ # async def onewire_select(self, device_address):
+ # """
+ # Select a device based on its address
+ # :param device_address: A bytearray of 8 bytes
+ # """
+ # if not self.onewire_enabled:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError(f'onewire_select: OneWire interface is not enabled.')
+ #
+ # if type(device_address) is not list:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('onewire_select: device address must be an array of 8 '
+ # 'bytes.')
+ #
+ # if len(device_address) != 8:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('onewire_select: device address must be an array of 8 '
+ # 'bytes.')
+ # command = [PrivateConstants.ONE_WIRE_SELECT]
+ # for data in device_address:
+ # command.append(data)
+ # await self._send_command(command)
+ #
+ # async def onewire_skip(self):
+ # """
+ # Skip the device selection. This only works if you have a
+ # single device, but you can avoid searching and use this to
+ # immediately access your device.
+ # """
+ # if not self.onewire_enabled:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError(f'onewire_skip: OneWire interface is not enabled.')
+ #
+ # command = [PrivateConstants.ONE_WIRE_SKIP]
+ # await self._send_command(command)
+ #
+ # async def onewire_write(self, data, power=0):
+ # """
+ # Write a byte to the onewire device. If 'power' is one
+ # then the wire is held high at the end for
+ # parasitically powered devices. You
+ # are responsible for eventually de-powering it by calling
+ # another read or write.
+ #
+ # :param data: byte to write.
+ # :param power: power control (see above)
+ # """
+ # if not self.onewire_enabled:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError(f'onewire_write: OneWire interface is not enabled.')
+ # if 0 < data < 255:
+ # command = [PrivateConstants.ONE_WIRE_WRITE, data, power]
+ # await self._send_command(command)
+ # else:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('onewire_write: Data must be no larger than 255')
+ #
+ # async def onewire_read(self, callback=None):
+ # """
+ # Read a byte from the onewire device
+ # :param callback: required function to report onewire data as a
+ # result of read command
+ #
+ #
+ # callback returns a data list:
+ # [ONEWIRE_REPORT, ONEWIRE_READ=29, data byte, time-stamp]
+ #
+ # ONEWIRE_REPORT = 14
+ # """
+ # if not self.onewire_enabled:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError(f'onewire_read: OneWire interface is not enabled.')
+ #
+ # if not callback:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('onewire_read A Callback must be specified')
+ #
+ # self.onewire_callback = callback
+ #
+ # command = [PrivateConstants.ONE_WIRE_READ]
+ # await self._send_command(command)
+ #
+ # async def onewire_reset_search(self):
+ # """
+ # Begin a new search. The next use of search will begin at the first device
+ # """
+ #
+ # if not self.onewire_enabled:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError(f'onewire_reset_search: OneWire interface is not '
+ # f'enabled.')
+ # else:
+ # command = [PrivateConstants.ONE_WIRE_RESET_SEARCH]
+ # await self._send_command(command)
+ #
+ # async def onewire_search(self, callback=None):
+ # """
+ # Search for the next device. The device address will returned in the callback.
+ # If a device is found, the 8 byte address is contained in the callback.
+ # If no more devices are found, the address returned contains all elements set
+ # to 0xff.
+ #
+ # :param callback: required function to report a onewire device address
+ #
+ # callback returns a data list:
+ # [ONEWIRE_REPORT, ONEWIRE_SEARCH=31, 8 byte address, time-stamp]
+ #
+ # ONEWIRE_REPORT = 14
+ # """
+ # if not self.onewire_enabled:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError(f'onewire_search: OneWire interface is not enabled.')
+ #
+ # if not callback:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('onewire_read A Callback must be specified')
+ #
+ # self.onewire_callback = callback
+ #
+ # command = [PrivateConstants.ONE_WIRE_SEARCH]
+ # await self._send_command(command)
+ #
+ # async def onewire_crc8(self, address_list, callback=None):
+ # """
+ # Compute a CRC check on an array of data.
+ # :param address_list:
+ #
+ # :param callback: required function to report a onewire device address
+ #
+ # callback returns a data list:
+ # [ONEWIRE_REPORT, ONEWIRE_CRC8=32, CRC, time-stamp]
+ #
+ # ONEWIRE_REPORT = 14
+ #
+ # """
+ #
+ # if not self.onewire_enabled:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError(f'onewire_crc8: OneWire interface is not enabled.')
+ #
+ # if not callback:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('onewire_crc8 A Callback must be specified')
+ #
+ # if type(address_list) is not list:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('onewire_crc8: address list must be a list.')
+ #
+ # self.onewire_callback = callback
+ #
+ # address_length = len(address_list)
+ #
+ # command = [PrivateConstants.ONE_WIRE_CRC8, address_length - 1]
+ #
+ # for data in address_list:
+ # command.append(data)
+ #
+ # await self._send_command(command)
+
+ async def _set_pin_mode(self, pin_number, pin_state, differential, callback):
+ """
+ A private method to set the various pin modes.
+
+ :param pin_number: arduino pin number
+
+ :param pin_state: INPUT/OUTPUT/ANALOG/PWM/PULLUP - for SERVO use
+ servo_config()
+ For DHT use: set_pin_mode_dht
+
+ :param differential: for analog inputs - threshold
+ value to be achieved for report to
+ be generated
+
+ :param callback: A reference to an async call back function to be
+ called when pin data value changes
+
+ """
+ if not callback and pin_state != PrivateConstants.AT_OUTPUT:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError('_set_pin_mode: A Callback must be specified')
+ else:
+ if pin_state == PrivateConstants.AT_INPUT:
+ command = [PrivateConstants.SET_PIN_MODE, pin_number,
+ PrivateConstants.AT_INPUT, 1]
+ self.digital_callbacks[pin_number] = callback
+ elif pin_state == PrivateConstants.AT_INPUT_PULLUP:
+ command = [PrivateConstants.SET_PIN_MODE, pin_number,
+ PrivateConstants.AT_INPUT_PULLUP, 1]
+ self.digital_callbacks[pin_number] = callback
+ elif pin_state == PrivateConstants.AT_ANALOG:
+ command = [PrivateConstants.SET_PIN_MODE, pin_number,
+ PrivateConstants.AT_ANALOG,
+ differential >> 8, differential & 0xff, 1]
+ self.analog_callbacks[pin_number] = callback
+ elif pin_state == PrivateConstants.AT_OUTPUT:
+ command = [PrivateConstants.SET_PIN_MODE, pin_number,
+ PrivateConstants.AT_OUTPUT, 1]
+ else:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError('Unknown pin state')
+
+ if command:
+ await self._send_command(command)
+
+ await asyncio.sleep(.05)
+
+ async def servo_detach(self, pin_number):
+ """
+ Detach a servo for reuse
+ :param pin_number: attached pin
+ """
+ command = [PrivateConstants.SERVO_DETACH, pin_number]
+ await self._send_command(command)
+
+ async def servo_write(self, pin_number, angle):
+ """
+
+ Set a servo attached to a pin to a given angle.
+
+ :param pin_number: pin
+
+ :param angle: angle (0-180)
+
+ """
+ command = [PrivateConstants.SERVO_WRITE, pin_number, angle]
+ await self._send_command(command)
+
+ # async def stepper_move_to(self, motor_id, position):
+ # """
+ # Set an absolution target position. If position is positive, the movement is
+ # clockwise, else it is counter-clockwise.
+ #
+ # The run() function (below) will try to move the motor (at most one step per call)
+ # from the current position to the target position set by the most
+ # recent call to this function. Caution: moveTo() also recalculates the
+ # speed for the next step.
+ # If you are trying to use constant speed movements, you should call setSpeed()
+ # after calling moveTo().
+ #
+ # :param motor_id: motor id: 0 - 3
+ #
+ # :param position: target position. Maximum value is 32 bits.
+ # """
+ #
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_move_to: Invalid motor_id.')
+ #
+ # if position < 0:
+ # polarity = 1
+ # else:
+ # polarity = 0
+ # position = abs(position)
+ #
+ # position_bytes = list(position.to_bytes(4, 'big', signed=True))
+ #
+ # command = [PrivateConstants.STEPPER_MOVE_TO, motor_id]
+ # for value in position_bytes:
+ # command.append(value)
+ # command.append(polarity)
+ #
+ # await self._send_command(command)
+ #
+ # async def stepper_move(self, motor_id, relative_position):
+ # """
+ # Set the target position relative to the current position.
+ #
+ # :param motor_id: motor id: 0 - 3
+ #
+ # :param relative_position: The desired position relative to the current
+ # position. Negative is anticlockwise from
+ # the current position. Maximum value is 32 bits.
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_move: Invalid motor_id.')
+ #
+ # if relative_position < 0:
+ # polarity = 1
+ # else:
+ # polarity = 0
+ # position = abs(relative_position)
+ #
+ # position_bytes = list(position.to_bytes(4, 'big', signed=True))
+ #
+ # command = [PrivateConstants.STEPPER_MOVE, motor_id]
+ # for value in position_bytes:
+ # command.append(value)
+ # command.append(polarity)
+ # await self._send_command(command)
+ #
+ # async def stepper_run(self, motor_id, completion_callback=None):
+ # """
+ # This method steps the selected motor based on the current speed.
+ #
+ # Once called, the server will continuously attempt to step the motor.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param completion_callback: call back function to receive motion complete
+ # notification
+ #
+ # callback returns a data list:
+ #
+ # [report_type, motor_id, raw_time_stamp]
+ #
+ # The report_type = 19
+ # """
+ # if not completion_callback:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_run: A motion complete callback must be '
+ # 'specified.')
+ #
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_run: Invalid motor_id.')
+ #
+ # self.stepper_info_list[motor_id]['motion_complete_callback'] = completion_callback
+ # command = [PrivateConstants.STEPPER_RUN, motor_id]
+ # await self._send_command(command)
+ #
+ # async def stepper_run_speed(self, motor_id):
+ # """
+ # This method steps the selected motor based at a constant speed as set by the most
+ # recent call to stepper_set_max_speed(). The motor will run continuously.
+ #
+ # Once called, the server will continuously attempt to step the motor.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_run_speed: Invalid motor_id.')
+ #
+ # command = [PrivateConstants.STEPPER_RUN_SPEED, motor_id]
+ # await self._send_command(command)
+ #
+ # async def stepper_set_max_speed(self, motor_id, max_speed):
+ # """
+ # Sets the maximum permitted speed. The stepper_run() function will accelerate
+ # up to the speed set by this function.
+ #
+ # Caution: the maximum speed achievable depends on your processor and clock speed.
+ # The default maxSpeed is 1 step per second.
+ #
+ # Caution: Speeds that exceed the maximum speed supported by the processor may
+ # result in non-linear accelerations and decelerations.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param max_speed: 1 - 1000
+ # """
+ #
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_set_max_speed: Invalid motor_id.')
+ #
+ # if not 1 < max_speed <= 1000:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_set_max_speed: Speed range is 1 - 1000.')
+ #
+ # self.stepper_info_list[motor_id]['max_speed'] = max_speed
+ # max_speed_msb = (max_speed & 0xff00) >> 8
+ # max_speed_lsb = max_speed & 0xff
+ #
+ # command = [PrivateConstants.STEPPER_SET_MAX_SPEED, motor_id, max_speed_msb,
+ # max_speed_lsb]
+ # await self._send_command(command)
+ #
+ # async def stepper_get_max_speed(self, motor_id):
+ # """
+ # Returns the maximum speed configured for this stepper
+ # that was previously set by stepper_set_max_speed()
+ #
+ # Value is stored in the client, so no callback is required.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :return: The currently configured maximum speed.
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_max_speed: Invalid motor_id.')
+ #
+ # return self.stepper_info_list[motor_id]['max_speed']
+ #
+ # async def stepper_set_acceleration(self, motor_id, acceleration):
+ # """
+ # Sets the acceleration/deceleration rate.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param acceleration: The desired acceleration in steps per second
+ # per second. Must be > 0.0. This is an
+ # expensive call since it requires a square
+ # root to be calculated on the server.
+ # Dont call more often than needed.
+ #
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_set_acceleration: Invalid motor_id.')
+ #
+ # if not 1 < acceleration <= 1000:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_set_acceleration: Acceleration range is 1 - '
+ # '1000.')
+ #
+ # self.stepper_info_list[motor_id]['acceleration'] = acceleration
+ #
+ # max_accel_msb = acceleration >> 8
+ # max_accel_lsb = acceleration & 0xff
+ #
+ # command = [PrivateConstants.STEPPER_SET_ACCELERATION, motor_id, max_accel_msb,
+ # max_accel_lsb]
+ # await self._send_command(command)
+ #
+ # async def stepper_set_speed(self, motor_id, speed):
+ # """
+ # Sets the desired constant speed for use with stepper_run_speed().
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param speed: 0 - 1000 The desired constant speed in steps per
+ # second. Positive is clockwise. Speeds of more than 1000 steps per
+ # second are unreliable. Speed accuracy depends on the Arduino
+ # crystal. Jitter depends on how frequently you call the
+ # stepper_run_speed() method.
+ # The speed will be limited by the current value of
+ # stepper_set_max_speed().
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_set_speed: Invalid motor_id.')
+ #
+ # if not 0 < speed <= 1000:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_set_speed: Speed range is 0 - '
+ # '1000.')
+ #
+ # self.stepper_info_list[motor_id]['speed'] = speed
+ #
+ # speed_msb = speed >> 8
+ # speed_lsb = speed & 0xff
+ #
+ # command = [PrivateConstants.STEPPER_SET_SPEED, motor_id, speed_msb, speed_lsb]
+ # await self._send_command(command)
+ #
+ # async def stepper_get_speed(self, motor_id):
+ # """
+ # Returns the most recently set speed.
+ # that was previously set by stepper_set_speed();
+ #
+ # Value is stored in the client, so no callback is required.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_get_speed: Invalid motor_id.')
+ #
+ # return self.stepper_info_list[motor_id]['speed']
+ #
+ # async def stepper_get_distance_to_go(self, motor_id, distance_to_go_callback):
+ # """
+ # Request the distance from the current position to the target position
+ # from the server.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param distance_to_go_callback: required callback function to receive report
+ #
+ # :return: The distance to go is returned via the callback as a list:
+ #
+ # [REPORT_TYPE=15, motor_id, distance in steps, time_stamp]
+ #
+ # A positive distance is clockwise from the current position.
+ #
+ # """
+ # if not distance_to_go_callback:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_get_distance_to_go Read: A callback function must be specified.')
+ #
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_get_distance_to_go: Invalid motor_id.')
+ # self.stepper_info_list[motor_id][
+ # 'distance_to_go_callback'] = distance_to_go_callback
+ # command = [PrivateConstants.STEPPER_GET_DISTANCE_TO_GO, motor_id]
+ # await self._send_command(command)
+ #
+ # async def stepper_get_target_position(self, motor_id, target_callback):
+ # """
+ # Request the most recently set target position from the server.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param target_callback: required callback function to receive report
+ #
+ # :return: The distance to go is returned via the callback as a list:
+ #
+ # [REPORT_TYPE=16, motor_id, target position in steps, time_stamp]
+ #
+ # Positive is clockwise from the 0 position.
+ #
+ # """
+ # if not target_callback:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError(
+ # 'stepper_get_target_position Read: A callback function must be specified.')
+ #
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_get_target_position: Invalid motor_id.')
+ #
+ # self.stepper_info_list[motor_id][
+ # 'target_position_callback'] = target_callback
+ #
+ # command = [PrivateConstants.STEPPER_GET_TARGET_POSITION, motor_id]
+ # await self._send_command(command)
+ #
+ # async def stepper_get_current_position(self, motor_id, current_position_callback):
+ # """
+ # Request the current motor position from the server.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param current_position_callback: required callback function to receive report
+ #
+ # :return: The current motor position returned via the callback as a list:
+ #
+ # [REPORT_TYPE=17, motor_id, current position in steps, time_stamp]
+ #
+ # Positive is clockwise from the 0 position.
+ # """
+ # if not current_position_callback:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError(
+ # 'stepper_get_current_position Read: A callback function must be specified.')
+ #
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_get_current_position: Invalid motor_id.')
+ #
+ # self.stepper_info_list[motor_id]['current_position_callback'] = current_position_callback
+ #
+ # command = [PrivateConstants.STEPPER_GET_CURRENT_POSITION, motor_id]
+ # await self._send_command(command)
+ #
+ # async def stepper_set_current_position(self, motor_id, position):
+ # """
+ # Resets the current position of the motor, so that wherever the motor
+ # happens to be right now is considered to be the new 0 position. Useful
+ # for setting a zero position on a stepper after an initial hardware
+ # positioning move.
+ #
+ # Has the side effect of setting the current motor speed to 0.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param position: Position in steps. This is a 32 bit value
+ # """
+ #
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_set_current_position: Invalid motor_id.')
+ # position_bytes = list(position.to_bytes(4, 'big', signed=True))
+ #
+ # command = [PrivateConstants.STEPPER_SET_CURRENT_POSITION, motor_id]
+ # for value in position_bytes:
+ # command.append(value)
+ # await self._send_command(command)
+ #
+ # async def stepper_run_speed_to_position(self, motor_id, completion_callback=None):
+ # """
+ # Runs the motor at the currently selected speed until the target position is
+ # reached.
+ #
+ # Does not implement accelerations.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param completion_callback: call back function to receive motion complete
+ # notification
+ #
+ # callback returns a data list:
+ #
+ # [report_type, motor_id, raw_time_stamp]
+ #
+ # The report_type = 19
+ # """
+ # if not completion_callback:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_run_speed_to_position: A motion complete '
+ # 'callback must be '
+ # 'specified.')
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_run_speed_to_position: Invalid motor_id.')
+ #
+ # self.stepper_info_list[motor_id]['motion_complete_callback'] = completion_callback
+ # command = [PrivateConstants.STEPPER_RUN_SPEED_TO_POSITION, motor_id]
+ # await self._send_command(command)
+ #
+ # async def stepper_stop(self, motor_id):
+ # """
+ # Sets a new target position that causes the stepper
+ # to stop as quickly as possible, using the current speed and
+ # acceleration parameters.
+ #
+ # :param motor_id: 0 - 3
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_stop: Invalid motor_id.')
+ #
+ # command = [PrivateConstants.STEPPER_STOP, motor_id]
+ # await self._send_command(command)
+ #
+ # async def stepper_disable_outputs(self, motor_id):
+ # """
+ # Disable motor pin outputs by setting them all LOW.
+ #
+ # Depending on the design of your electronics this may turn off
+ # the power to the motor coils, saving power.
+ #
+ # This is useful to support Arduino low power modes: disable the outputs
+ # during sleep and then re-enable with enableOutputs() before stepping
+ # again.
+ #
+ # If the enable Pin is defined, sets it to OUTPUT mode and clears
+ # the pin to disabled.
+ #
+ # :param motor_id: 0 - 3
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_disable_outputs: Invalid motor_id.')
+ #
+ # command = [PrivateConstants.STEPPER_DISABLE_OUTPUTS, motor_id]
+ # await self._send_command(command)
+ #
+ # async def stepper_enable_outputs(self, motor_id):
+ # """
+ # Enable motor pin outputs by setting the motor pins to OUTPUT
+ # mode.
+ #
+ # If the enable Pin is defined, sets it to OUTPUT mode and sets
+ # the pin to enabled.
+ #
+ # :param motor_id: 0 - 3
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_enable_outputs: Invalid motor_id.')
+ #
+ # command = [PrivateConstants.STEPPER_ENABLE_OUTPUTS, motor_id]
+ # await self._send_command(command)
+ #
+ # async def stepper_set_min_pulse_width(self, motor_id, minimum_width):
+ # """
+ # Sets the minimum pulse width allowed by the stepper driver.
+ #
+ # The minimum practical pulse width is approximately 20 microseconds.
+ #
+ # Times less than 20 microseconds will usually result in 20 microseconds or so.
+ #
+ # :param motor_id: 0 -3
+ #
+ # :param minimum_width: A 16 bit unsigned value expressed in microseconds.
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_set_min_pulse_width: Invalid motor_id.')
+ #
+ # if not 0 < minimum_width <= 0xff:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_set_min_pulse_width: Pulse width range = '
+ # '0-0xffff.')
+ #
+ # width_msb = minimum_width >> 8
+ # width_lsb = minimum_width & 0xff
+ #
+ # command = [PrivateConstants.STEPPER_SET_MINIMUM_PULSE_WIDTH, motor_id, width_msb,
+ # width_lsb]
+ # await self._send_command(command)
+ #
+ # async def stepper_set_enable_pin(self, motor_id, pin=0xff):
+ # """
+ # Sets the enable pin number for stepper drivers.
+ # 0xFF indicates unused (default).
+ #
+ # Otherwise, if a pin is set, the pin will be turned on when
+ # enableOutputs() is called and switched off when disableOutputs()
+ # is called.
+ #
+ # :param motor_id: 0 - 4
+ # :param pin: 0-0xff
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_set_enable_pin: Invalid motor_id.')
+ #
+ # if not 0 < pin <= 0xff:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_set_enable_pin: Pulse width range = '
+ # '0-0xff.')
+ # command = [PrivateConstants.STEPPER_SET_ENABLE_PIN, motor_id, pin]
+ #
+ # await self._send_command(command)
+ #
+ # async def stepper_set_3_pins_inverted(self, motor_id, direction=False, step=False,
+ # enable=False):
+ # """
+ # Sets the inversion for stepper driver pins.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param direction: True=inverted or False
+ #
+ # :param step: True=inverted or False
+ #
+ # :param enable: True=inverted or False
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_set_3_pins_inverted: Invalid motor_id.')
+ #
+ # command = [PrivateConstants.STEPPER_SET_3_PINS_INVERTED, motor_id, direction,
+ # step, enable]
+ #
+ # await self._send_command(command)
+ #
+ # async def stepper_set_4_pins_inverted(self, motor_id, pin1_invert=False,
+ # pin2_invert=False,
+ # pin3_invert=False, pin4_invert=False, enable=False):
+ # """
+ # Sets the inversion for 2, 3 and 4 wire stepper pins
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param pin1_invert: True=inverted or False
+ #
+ # :param pin2_invert: True=inverted or False
+ #
+ # :param pin3_invert: True=inverted or False
+ #
+ # :param pin4_invert: True=inverted or False
+ #
+ # :param enable: True=inverted or False
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_set_4_pins_inverted: Invalid motor_id.')
+ #
+ # command = [PrivateConstants.STEPPER_SET_4_PINS_INVERTED, motor_id, pin1_invert,
+ # pin2_invert, pin3_invert, pin4_invert, enable]
+ #
+ # await self._send_command(command)
+ #
+ # async def stepper_is_running(self, motor_id, callback):
+ # """
+ # Checks to see if the motor is currently running to a target.
+ #
+ # Callback return True if the speed is not zero or not at the target position.
+ #
+ # :param motor_id: 0-4
+ #
+ # :param callback: required callback function to receive report
+ #
+ # :return: The current running state returned via the callback as a list:
+ #
+ # [REPORT_TYPE=18, motor_id, True or False for running state, time_stamp]
+ # """
+ # if not callback:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError(
+ # 'stepper_is_running: A callback function must be specified.')
+ #
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_is_running: Invalid motor_id.')
+ #
+ # self.stepper_info_list[motor_id]['is_running_callback'] = callback
+ #
+ # command = [PrivateConstants.STEPPER_IS_RUNNING, motor_id]
+ # await self._send_command(command)
+
+ async def shutdown(self):
+ """
+ This method attempts an orderly shutdown
+ If any exceptions are thrown, they are ignored.
+
+ """
+ self.shutdown_flag = True
+
+ if self.hard_reset_on_shutdown:
+ await self.r4_hard_reset()
+ # stop all reporting - both analog and digital
+ else:
+ try:
+ command = [PrivateConstants.STOP_ALL_REPORTS]
+ await self._send_command(command)
+
+ await asyncio.sleep(.5)
+ await self.serial_port.reset_input_buffer()
+ await self.serial_port.close()
+ if self.close_loop_on_shutdown:
+ self.loop.stop()
+ except (RuntimeError, SerialException):
+ pass
+
+ async def r4_hard_reset(self):
+ """
+ Place the r4 into hard reset
+ """
+ command = [PrivateConstants.BOARD_HARD_RESET, 1]
+ await self._send_command(command)
+
+ async def disable_all_reporting(self):
+ """
+ Disable reporting for all digital and analog input pins
+ """
+ command = [PrivateConstants.MODIFY_REPORTING,
+ PrivateConstants.REPORTING_DISABLE_ALL, 0]
+ await self._send_command(command)
+
+ async def disable_analog_reporting(self, pin):
+ """
+ Disables analog reporting for a single analog pin.
+
+ :param pin: Analog pin number. For example for A0, the number is 0.
+
+ """
+ command = [PrivateConstants.MODIFY_REPORTING,
+ PrivateConstants.REPORTING_ANALOG_DISABLE, pin]
+ await self._send_command(command)
+
+ async def disable_digital_reporting(self, pin):
+ """
+ Disables digital reporting for a single digital pin
+
+
+ :param pin: pin number
+
+ """
+ command = [PrivateConstants.MODIFY_REPORTING,
+ PrivateConstants.REPORTING_DIGITAL_DISABLE, pin]
+ await self._send_command(command)
+
+ async def enable_analog_reporting(self, pin):
+ """
+ Enables analog reporting for the specified pin.
+
+ :param pin: Analog pin number. For example for A0, the number is 0.
+
+
+ """
+ command = [PrivateConstants.MODIFY_REPORTING,
+ PrivateConstants.REPORTING_ANALOG_ENABLE, pin]
+ await self._send_command(command)
+
+ async def enable_digital_reporting(self, pin):
+ """
+ Enable reporting on the specified digital pin.
+
+ :param pin: Pin number.
+ """
+
+ command = [PrivateConstants.MODIFY_REPORTING,
+ PrivateConstants.REPORTING_DIGITAL_ENABLE, pin]
+ await self._send_command(command)
+
+ async def _arduino_report_dispatcher(self):
+ """
+ This is a private method.
+ It continually accepts and interprets data coming from Telemetrix4Arduino,and then
+ dispatches the correct handler to process the data.
+
+ It first receives the length of the packet, and then reads in the rest of the
+ packet. A packet consists of a length, report identifier and then the report data.
+ Using the report identifier, the report handler is fetched from report_dispatch.
+
+ :returns: This method never returns
+ """
+
+ while True:
+ if self.shutdown_flag:
+ break
+ try:
+ packet_length = await self.serial_port.read()
+ except TypeError:
+ continue
+
+ # get the rest of the packet
+ packet = await self.serial_port.read(packet_length)
+
+ report = packet[0]
+ # print(report)
+ # handle all other messages by looking them up in the
+ # command dictionary
+
+ await self.report_dispatch[report](packet[1:])
+ await asyncio.sleep(self.sleep_tune)
+
+ '''
+ Report message handlers
+ '''
+
+ async def _report_loop_data(self, data):
+ """
+ Print data that was looped back
+
+ :param data: byte of loop back data
+ """
+ if self.loop_back_callback:
+ await self.loop_back_callback(data)
+
+ async def _spi_report(self, report):
+
+ cb_list = [PrivateConstants.SPI_REPORT, report[0]] + report[1:]
+
+ cb_list.append(time.time())
+
+ await self.spi_callback(cb_list)
+
+ async def _onewire_report(self, report):
+ cb_list = [PrivateConstants.ONE_WIRE_REPORT, report[0]] + report[1:]
+ cb_list.append(time.time())
+ await self.onewire_callback(cb_list)
+
+ async def _report_debug_data(self, data):
+ """
+ Print debug data sent from Arduino
+
+ :param data: data[0] is a byte followed by 2
+ bytes that comprise an integer
+ """
+ value = (data[1] << 8) + data[2]
+ print(f'DEBUG ID: {data[0]} Value: {value}')
+
+ async def _analog_message(self, data):
+ """
+ This is a private message handler method.
+ It is a message handler for analog messages.
+
+ :param data: message data
+
+ """
+ pin = data[0]
+ value = (data[1] << 8) + data[2]
+
+ time_stamp = time.time()
+
+ # append pin number, pin value, and pin type to return value and return as a list
+ message = [PrivateConstants.AT_ANALOG, pin, value, time_stamp]
+
+ await self.analog_callbacks[pin](message)
+
+ async def _dht_report(self, data):
+ """
+ This is a private message handler for dht reports
+
+ :param data: data[0] = report error return
+ No Errors = 0
+
+ Checksum Error = 1
+
+ Timeout Error = 2
+
+ Invalid Value = 999
+
+ data[1] = pin number
+
+ data[2] = dht type 11 or 22
+
+ data[3] = humidity positivity flag
+
+ data[4] = temperature positivity value
+
+ data[5] = humidity integer
+
+ data[6] = humidity fractional value
+
+ data[7] = temperature integer
+
+ data[8] = temperature fractional value
+ """
+ if data[0]: # DHT_ERROR
+ # error report
+ # data[0] = report sub type, data[1] = pin, data[2] = error message
+ if self.dht_callbacks[data[1]]:
+ # Callback 0=DHT REPORT, DHT_ERROR, PIN, Time
+ message = [PrivateConstants.DHT_REPORT, data[0], data[1], data[2],
+ time.time()]
+ await self.dht_callbacks[data[1]](message)
+ else:
+ # got valid data DHT_DATA
+ f_humidity = float(data[5] + data[6] / 100)
+ if data[3]:
+ f_humidity *= -1.0
+ f_temperature = float(data[7] + data[8] / 100)
+ if data[4]:
+ f_temperature *= -1.0
+ message = [PrivateConstants.DHT_REPORT, data[0], data[1], data[2],
+ f_humidity, f_temperature, time.time()]
+
+ await self.dht_callbacks[data[1]](message)
+
+ async def _digital_message(self, data):
+ """
+ This is a private message handler method.
+ It is a message handler for Digital Messages.
+
+ :param data: digital message
+
+ """
+ pin = data[0]
+ value = data[1]
+
+ time_stamp = time.time()
+ if self.digital_callbacks[pin]:
+ message = [PrivateConstants.DIGITAL_REPORT, pin, value, time_stamp]
+ await self.digital_callbacks[pin](message)
+
+ async def _servo_unavailable(self, report):
+ """
+ Message if no servos are available for use.
+
+ :param report: pin number
+ """
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(
+ f'Servo Attach For Pin {report[0]} Failed: No Available Servos')
+
+ async def _i2c_read_report(self, data):
+ """
+ Execute callback for i2c reads.
+
+ :param data: [I2C_READ_REPORT, i2c_port, number of bytes read, address, register, bytes read..., time-stamp]
+ """
+
+ # we receive [# data bytes, address, register, data bytes]
+ # number of bytes of data returned
+
+ # data[0] = number of bytes
+ # data[1] = i2c_port
+ # data[2] = number of bytes returned
+ # data[3] = address
+ # data[4] = register
+ # data[5] ... all the data bytes
+
+ cb_list = [PrivateConstants.I2C_READ_REPORT, data[0], data[1]] + data[2:]
+ cb_list.append(time.time())
+
+ if cb_list[1]:
+ await self.i2c_callback2(cb_list)
+ else:
+ await self.i2c_callback(cb_list)
+
+ async def _i2c_too_few(self, data):
+ """
+ I2c reports too few bytes received
+
+ :param data: data[0] = device address
+ """
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(
+ f'i2c too few bytes received from i2c port {data[0]} i2c address {data[1]}')
+
+ async def _i2c_too_many(self, data):
+ """
+ I2c reports too few bytes received
+
+ :param data: data[0] = device address
+ """
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(
+ f'i2c too many bytes received from i2c port {data[0]} i2c address {data[1]}')
+
+ async def _sonar_distance_report(self, report):
+ """
+
+ :param report: data[0] = trigger pin, data[1] and data[2] = distance
+
+ callback report format: [PrivateConstants.SONAR_DISTANCE, trigger_pin, distance_value, time_stamp]
+ """
+
+ # get callback from pin number
+ cb = self.sonar_callbacks[report[0]]
+
+ # build report data
+ cb_list = [PrivateConstants.SONAR_DISTANCE, report[0],
+ ((report[1] << 8) + report[2]), time.time()]
+
+ await cb(cb_list)
+
+ async def _stepper_distance_to_go_report(self, report):
+ return # for now
+ # """
+ # Report stepper distance to go.
+ #
+ # :param report: data[0] = motor_id, data[1] = steps MSB, data[2] = steps byte 1,
+ # data[3] = steps bytes 2, data[4] = steps LSB
+ #
+ # callback report format: [PrivateConstants.STEPPER_DISTANCE_TO_GO, motor_id
+ # steps, time_stamp]
+ # """
+ #
+ # # get callback
+ # cb = self.stepper_info_list[report[0]]['distance_to_go_callback']
+ #
+ # # isolate the steps bytes and covert list to bytes
+ # steps = bytes(report[1:])
+ #
+ # # get value from steps
+ # num_steps = int.from_bytes(steps, byteorder='big', signed=True)
+ #
+ # cb_list = [PrivateConstants.STEPPER_DISTANCE_TO_GO, report[0], num_steps,
+ # time.time()]
+ #
+ # await cb(cb_list)
+ #
+
+ async def _stepper_target_position_report(self, report):
+ return # for now
+
+ # """
+ # Report stepper target position to go.
+ #
+ # :param report: data[0] = motor_id, data[1] = target position MSB,
+ # data[2] = target position byte MSB+1
+ # data[3] = target position byte MSB+2
+ # data[4] = target position LSB
+ #
+ # callback report format: [PrivateConstants.STEPPER_TARGET_POSITION, motor_id
+ # target_position, time_stamp]
+ # """
+ #
+ # # get callback
+ # cb = self.stepper_info_list[report[0]]['target_position_callback']
+ #
+ # # isolate the steps bytes and covert list to bytes
+ # target = bytes(report[1:])
+ #
+ # # get value from steps
+ # target_position = int.from_bytes(target, byteorder='big', signed=True)
+ #
+ # cb_list = [PrivateConstants.STEPPER_TARGET_POSITION, report[0], target_position,
+ # time.time()]
+ #
+ # await cb(cb_list)
+ #
+ async def _stepper_current_position_report(self, report):
+ return # for now
+
+ # """
+ # Report stepper current position.
+ #
+ # :param report: data[0] = motor_id, data[1] = current position MSB,
+ # data[2] = current position byte MSB+1
+ # data[3] = current position byte MSB+2
+ # data[4] = current position LSB
+ #
+ # callback report format: [PrivateConstants.STEPPER_CURRENT_POSITION, motor_id
+ # current_position, time_stamp]
+ # """
+ #
+ # # get callback
+ # cb = self.stepper_info_list[report[0]]['current_position_callback']
+ #
+ # # isolate the steps bytes and covert list to bytes
+ # position = bytes(report[1:])
+ #
+ # # get value from steps
+ # current_position = int.from_bytes(position, byteorder='big', signed=True)
+ #
+ # cb_list = [PrivateConstants.STEPPER_CURRENT_POSITION, report[0], current_position,
+ # time.time()]
+ #
+ # await cb(cb_list)
+ #
+ async def _stepper_is_running_report(self, report):
+ return # for now
+
+ # """
+ # Report if the motor is currently running
+ #
+ # :param report: data[0] = motor_id, True if motor is running or False if it is not.
+ #
+ # callback report format: [18, motor_id,
+ # running_state, time_stamp]
+ # """
+ #
+ # # get callback
+ # cb = self.stepper_info_list[report[0]]['is_running_callback']
+ #
+ # cb_list = [PrivateConstants.STEPPER_RUNNING_REPORT, report[0], time.time()]
+ #
+ # await cb(cb_list)
+ #
+ async def _stepper_run_complete_report(self, report):
+ return # for now
+
+ # """
+ # The motor completed it motion
+ #
+ # :param report: data[0] = motor_id
+ #
+ # callback report format: [PrivateConstants.STEPPER_RUN_COMPLETE_REPORT, motor_id,
+ # time_stamp]
+ # """
+ #
+ # # get callback
+ # cb = self.stepper_info_list[report[0]]['motion_complete_callback']
+ #
+ # cb_list = [PrivateConstants.STEPPER_RUN_COMPLETE_REPORT, report[0],
+ # time.time()]
+ #
+ # await cb(cb_list)
+
+ async def _features_report(self, report):
+ self.reported_features = report[0]
+
+ async def _send_command(self, command):
+ """
+ This is a private utility method.
+
+
+ :param command: command data in the form of a list
+
+ :returns: number of bytes sent
+ """
+ # the length of the list is added at the head
+ command.insert(0, len(command))
+ # print(command)
+ send_message = bytes(command)
+
+ await self.serial_port.write(send_message)
diff --git a/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_analog_input.py b/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_analog_input.py
new file mode 100644
index 0000000..479c50f
--- /dev/null
+++ b/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_analog_input.py
@@ -0,0 +1,100 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+import time
+
+from telemetrix_uno_r4.minima.telemetrix_uno_r4_minima_aio import telemetrix_uno_r4_minima_aio
+
+"""
+This file demonstrates analog input using both callbacks and
+polling. Time stamps are provided in both "cooked" and raw form
+"""
+
+# Set up a pin for analog input and monitor its changes
+ANALOG_PIN = 2 # arduino pin number
+
+# Callback data indices
+CB_PIN_MODE = 0
+CB_PIN = 1
+CB_VALUE = 2
+CB_TIME = 3
+
+
+async def the_callback(data):
+ """
+ A callback function to report data changes.
+
+ :param data: [pin_mode, pin, current_reported_value, timestamp]
+ """
+
+ formatted_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[CB_TIME]))
+ print(f'Analog Call Input Callback: pin={data[CB_PIN]}, '
+ f'Value={data[CB_VALUE]} Time={formatted_time} '
+ f'(Raw Time={data[CB_TIME]})')
+
+
+async def analog_in(my_board, pin):
+ """
+ This function establishes the pin as an
+ analog input. Any changes on this pin will
+ be reported through the call back function.
+
+ Every 5 seconds the last value and time stamp is polled
+ and printed.
+
+ Also, the differential parameter is being used.
+ The callback will only be called when there is
+ difference of 5 or more between the current and
+ last value reported.
+
+ :param my_board: a telemetrix_aio instance
+
+ :param pin: Arduino pin number
+ """
+ await my_board.set_pin_mode_analog_input(pin, 5, the_callback)
+
+ # await asyncio.sleep(5)
+ # await my_board.disable_analog_reporting()
+ # await asyncio.sleep(5)
+ # await my_board.enable_analog_reporting()
+
+ # run forever waiting for input changes
+ try:
+ while True:
+ await asyncio.sleep(.001)
+
+ except KeyboardInterrupt:
+ await my_board.shutdown()
+ sys.exit(0)
+
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_minima_aio.TelemetrixUnoR4MinimaAio()
+
+try:
+ # start the main function
+ loop.run_until_complete(analog_in(board, ANALOG_PIN))
+ loop.run_until_complete(board.shutdown())
+except (KeyboardInterrupt, RuntimeError) as e:
+ loop.run_until_complete(board.shutdown())
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_blink.py b/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_blink.py
new file mode 100644
index 0000000..fc954df
--- /dev/null
+++ b/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_blink.py
@@ -0,0 +1,67 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+import asyncio
+import sys
+
+from telemetrix_uno_r4.minima.telemetrix_uno_r4_minima_aio import telemetrix_uno_r4_minima_aio
+
+"""
+Setup a pin for digital output and output a signal
+and toggle the pin. Do this 4 times.
+"""
+
+# some globals
+DIGITAL_PIN = 13 # arduino pin number
+
+
+async def blink(my_board, pin):
+ """
+ This function will to toggle a digital pin.
+
+ :param my_board: a telemetrix_aio instance
+ :param pin: pin to be controlled
+ """
+
+ # set the pin mode
+ await my_board.set_pin_mode_digital_output(pin)
+
+ # toggle the pin 4 times and exit
+ for x in range(4):
+ print('ON')
+ await my_board.digital_write(pin, 1)
+ await asyncio.sleep(1)
+ print('OFF')
+ await my_board.digital_write(pin, 0)
+ await asyncio.sleep(1)
+
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_minima_aio.TelemetrixUnoR4MinimaAio()
+
+try:
+ # start the main function
+ loop.run_until_complete(blink(board, DIGITAL_PIN))
+except KeyboardInterrupt:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_dht.py b/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_dht.py
new file mode 100644
index 0000000..5420c24
--- /dev/null
+++ b/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_dht.py
@@ -0,0 +1,108 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+import time
+
+from telemetrix_uno_r4.minima.telemetrix_uno_r4_minima_aio import telemetrix_uno_r4_minima_aio
+
+"""
+This program monitors two DHT22 and two DHT11 sensors.
+"""
+
+
+# indices into callback data for valid data
+# REPORT_TYPE = 0
+# READ_RESULT = 1
+# PIN = 2
+# DHT_TYPE = 3
+# HUMIDITY = 4
+# TEMPERATURE = 5
+# TIME = 6
+
+# indices into callback data for error report
+# REPORT_TYPE = 0
+# READ_RESULT = 1
+# PIN = 2
+# DHT_TYPE = 3
+# TIME = 4
+
+# Arduino Pin Number
+DHT_PIN = 8
+
+
+# A callback function to display the distance
+# noinspection GrazieInspection
+async def the_callback(data):
+ # noinspection GrazieInspection
+ """
+ The callback function to display the change in distance
+ :param data: [report_type = PrivateConstants.DHT, error = 0, pin number,
+ dht_type, humidity, temperature timestamp]
+ if this is an error report:
+ [report_type = PrivateConstants.DHT, error != 0, pin number, dht_type
+ timestamp]
+ """
+ if data[1]:
+ # error message
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[4]))
+ print(f'DHT Error Report:'
+ f'Pin: {data[2]} DHT Type: {data[3]} Error: {data[1]} Time: {date}')
+ else:
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[6]))
+ print(f'DHT Valid Data Report:'
+ f'Pin: {data[2]} DHT Type: {data[3]} Humidity: {data[4]} Temperature:'
+ f' {data[5]} Time: {date}')
+
+
+async def dht(my_board):
+ # noinspection GrazieInspection
+ """
+ Set the pin mode for a DHT 22 device. Results will appear via the
+ callback.
+
+ :param my_board: a telemetrix instance
+
+ """
+
+ # set the pin mode for a DHT 22
+ await my_board.set_pin_mode_dht(DHT_PIN, callback=the_callback, dht_type=11)
+
+ # just sit in a loop waiting for the reports to come in
+ while True:
+ try:
+ await asyncio.sleep(.1)
+ except KeyboardInterrupt:
+ my_board.shutdown()
+ sys.exit(0)
+
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_minima_aio.TelemetrixUnoR4MinimaAio()
+try:
+ loop.run_until_complete(dht(board))
+except (KeyboardInterrupt, RuntimeError):
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_digital_input.py b/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_digital_input.py
new file mode 100644
index 0000000..5f982e7
--- /dev/null
+++ b/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_digital_input.py
@@ -0,0 +1,115 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,f
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ DHT support courtesy of Martyn Wheeler
+ Based on the DHTNew library - https://github.com/RobTillaart/DHTNew
+"""
+
+import asyncio
+import sys
+import time
+
+from telemetrix_uno_r4.minima.telemetrix_uno_r4_minima_aio import telemetrix_uno_r4_minima_aio
+
+"""
+Monitor a digital input pin
+"""
+
+"""
+Setup a pin for digital input and monitor its changes
+"""
+
+# Set up a pin for analog input and monitor its changes
+DIGITAL_PIN = 12 # arduino pin number
+
+# Callback data indices
+CB_PIN_MODE = 0
+CB_PIN = 1
+CB_VALUE = 2
+CB_TIME = 3
+
+# variable to hold the last time a button state changed
+debounce_time = time.time()
+
+
+async def the_callback(data):
+ """
+ A callback function to report data changes.
+ This will print the pin number, its reported value and
+ the date and time when the change occurred
+
+ :param data: [pin_mode, pin, current reported value, timestamp]
+ """
+ global debounce_time
+
+ # if the time from the last event change is > .2 seconds, the input is debounced
+ if data[CB_TIME] - debounce_time > .2:
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[CB_TIME]))
+ print(f'Pin: {data[CB_PIN]} Value: {data[CB_VALUE]} Time Stamp: {date}')
+ debounce_time = data[CB_TIME]
+
+
+async def digital_in(my_board, pin):
+ """
+ This function establishes the pin as a
+ digital input. Any changes on this pin will
+ be reported through the call back function.
+
+ :param my_board: a telemetrix_aio instance
+ :param pin: Arduino pin number
+ """
+
+ # set the pin mode
+ await my_board.set_pin_mode_digital_input(pin, the_callback)
+
+ # uncomment to try out report enable/disable
+ # await asyncio.sleep(1)
+ # await my_board.disable_all_reporting()
+ # await asyncio.sleep(4)
+ # await my_board.enable_digital_reporting(12)
+
+ # await asyncio.sleep(3)
+ # await my_board.enable_digital_reporting(pin)
+ # await asyncio.sleep(1)
+
+ while True:
+ try:
+ await asyncio.sleep(.001)
+ except KeyboardInterrupt:
+ await board.shutdown()
+ sys.exit(0)
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_minima_aio.TelemetrixUnoR4MinimaAio()
+
+try:
+ # start the main function
+ loop.run_until_complete(digital_in(board, 12))
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
+except (KeyboardInterrupt, RuntimeError) as e:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_digital_input_pullup.py b/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_digital_input_pullup.py
new file mode 100644
index 0000000..c05857d
--- /dev/null
+++ b/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_digital_input_pullup.py
@@ -0,0 +1,91 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+import time
+
+from telemetrix_uno_r4.minima.telemetrix_uno_r4_minima_aio import telemetrix_uno_r4_minima_aio
+
+"""
+Setup a digital pin for input pullup and monitor its changes.
+"""
+
+# some globals
+DIGITAL_PIN = 12 # arduino pin number
+KILL_TIME = 5 # sleep time to keep forever loop open
+
+# Callback data indices
+# Callback data indices
+CB_PIN_MODE = 0
+CB_PIN = 1
+CB_VALUE = 2
+CB_TIME = 3
+
+
+# Set up a pin for digital pin input and monitor its changes
+
+async def the_callback(data):
+ """
+ A callback function to report data changes.
+ This will print the pin number, its reported value and
+ the date and time when the change occurred
+
+ :param data: [pin, current reported value, pin_mode, timestamp]
+ """
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[CB_TIME]))
+ print(f'Pin: {data[CB_PIN]} Value: {data[CB_VALUE]} Time Stamp: {date}')
+
+
+async def digital_in_pullup(my_board, pin):
+ """
+ This function establishes the pin as a
+ digital input. Any changes on this pin will
+ be reported through the call back function.
+
+ :param my_board: a telemetrix_aio instance
+ :param pin: Arduino pin number
+ """
+
+ # start monitoring the pin by setting its mode
+ await my_board.set_pin_mode_digital_input_pullup(pin, the_callback)
+
+ # get pin changes forever
+ while True:
+ try:
+ await asyncio.sleep(KILL_TIME)
+ except KeyboardInterrupt:
+ await board.shutdown()
+ sys.exit(0)
+
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_minima_aio.TelemetrixUnoR4MinimaAio()
+
+try:
+ # start the main function
+ loop.run_until_complete(digital_in_pullup(board, 12))
+except (KeyboardInterrupt, RuntimeError) as e:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_fade.py b/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_fade.py
new file mode 100644
index 0000000..944f024
--- /dev/null
+++ b/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_fade.py
@@ -0,0 +1,70 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,f
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+"""
+
+import sys
+import asyncio
+
+from telemetrix_uno_r4.minima.telemetrix_uno_r4_minima_aio import telemetrix_uno_r4_minima_aio
+
+"""
+Setup a pin for output and fade its intensity
+"""
+
+# some globals
+# make sure to select a PWM pin
+DIGITAL_PIN = 13
+
+
+async def fade(the_board, pin):
+ # Set the DIGITAL_PIN as an output pin
+ await the_board.set_pin_mode_analog_output(pin)
+
+ # When hitting control-c to end the program
+ # in this loop, we are likely to get a KeyboardInterrupt
+ # exception. Catch the exception and exit gracefully.
+
+ try:
+ print('Fading up...')
+ for i in range(255):
+ await the_board.analog_write(DIGITAL_PIN, i)
+ await asyncio.sleep(.005)
+ print('Fading down...')
+ for i in range(255, -1, -1):
+ await the_board.analog_write(DIGITAL_PIN, i)
+ await asyncio.sleep(.005)
+ except KeyboardInterrupt:
+ the_board.shutdown()
+ sys.exit(0)
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_minima_aio.TelemetrixUnoR4MinimaAio()
+
+try:
+ # start the main function
+ loop.run_until_complete(fade(board, DIGITAL_PIN))
+except KeyboardInterrupt:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_hc-sr04_distance_sensor.py b/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_hc-sr04_distance_sensor.py
new file mode 100644
index 0000000..8a9fed9
--- /dev/null
+++ b/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_hc-sr04_distance_sensor.py
@@ -0,0 +1,84 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+import time
+from telemetrix_uno_r4.minima.telemetrix_uno_r4_minima_aio import telemetrix_uno_r4_minima_aio
+
+"""
+This program continuously monitors an HC-SR04 Ultrasonic Sensor
+It reports changes to the distance sensed.
+"""
+TRIGGER_PIN = 9
+ECHO_PIN = 10
+
+# indices into callback data
+REPORT_TYPE = 0
+TRIG_PIN = 1
+DISTANCE = 2
+TIME = 3
+
+
+# A callback function to display the distance
+async def the_callback(data):
+ """
+ The callback function to display the change in distance
+ :param data: [report_type = PrivateConstants.SONAR_DISTANCE, trigger pin number, distance, timestamp]
+ """
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[3]))
+ print(f'Sonar Report: Trigger Pin: {data[1]} Distance: {data[2]} Time: {date}')
+
+
+async def sonar(my_board, trigger_pin, echo_pin, callback):
+ """
+ Set the pin mode for a sonar device. Results will appear via the
+ callback.
+
+ :param my_board: a telemetrix_aio instance
+ :param trigger_pin: Arduino pin number
+ :param echo_pin: Arduino pin number
+ :param callback: The callback function
+ """
+
+ # set the pin mode for the trigger and echo pins
+ await my_board.set_pin_mode_sonar(trigger_pin, echo_pin, callback)
+ # wait forever
+ while True:
+ try:
+ await asyncio.sleep(.1)
+ except KeyboardInterrupt:
+ await my_board.shutdown()
+ sys.exit(0)
+
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_minima_aio.TelemetrixUnoR4MinimaAio()
+
+try:
+ # start the main function
+ loop.run_until_complete(sonar(board, TRIGGER_PIN, ECHO_PIN, the_callback))
+except KeyboardInterrupt:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_i2c_adxl345_accelerometer.py b/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_i2c_adxl345_accelerometer.py
new file mode 100644
index 0000000..aa8a08e
--- /dev/null
+++ b/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_i2c_adxl345_accelerometer.py
@@ -0,0 +1,104 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+from telemetrix_uno_r4.minima.telemetrix_uno_r4_minima_aio import telemetrix_uno_r4_minima_aio
+
+"""
+This example sets up and control an ADXL345 i2c accelerometer.
+It will continuously print data the raw xyz data from the device.
+"""
+
+
+# the call back function to print the adxl345 data
+async def the_callback(data):
+ """
+
+ :param data: [pin_type, Device address, device read register, x data pair, y data pair, z data pair]
+ :return:
+ """
+ report_type = data[0]
+ number_bytes_read = data[2]
+ i2c_device_address = data[3]
+ i2c_register = data[4]
+ x_msb = data[5]
+ x_lsb = data[6]
+ y_msb = data[7]
+ y_lsb = data[8]
+ z_msb = data[9]
+ z_lsb = data[10]
+
+ x_data = (x_msb << 8) + x_lsb
+ y_data = (y_msb << 8) + y_lsb
+ z_data = (z_msb << 8) + z_lsb
+
+ # test report type for SPI report
+ if report_type == 10:
+ print(f'i2c Report: i2c device address: {i2c_device_address} i2c '
+ f'Register: '
+ f'{i2c_register} Number Of Bytes Read: {number_bytes_read} x: '
+ f'{x_data} y: {y_data} z: {z_data}')
+ else:
+ print(f'unexpected report type: {report_type}')
+
+
+async def adxl345(my_board):
+ # setup adxl345
+ # device address = 83
+ await my_board.set_pin_mode_i2c()
+
+ # set up power and control register
+ await my_board.i2c_write(83, [45, 0])
+ await asyncio.sleep(.1)
+ await my_board.i2c_write(83, [45, 8])
+ await asyncio.sleep(.1)
+
+ # set up the data format register
+ await my_board.i2c_write(83, [49, 8])
+ await asyncio.sleep(.1)
+ await my_board.i2c_write(83, [49, 3])
+ await asyncio.sleep(.1)
+
+ # read_count = 20
+ while True:
+ # read 6 bytes from the data register
+ try:
+ await my_board.i2c_read(83, 50, 6, the_callback)
+ await asyncio.sleep(.1)
+
+ except (KeyboardInterrupt, RuntimeError):
+ await my_board.shutdown()
+ sys.exit(0)
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_minima_aio.TelemetrixUnoR4MinimaAio()
+
+try:
+ # start the main function
+ loop.run_until_complete(adxl345(board))
+except KeyboardInterrupt:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
+
diff --git a/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_loop_back.py b/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_loop_back.py
new file mode 100644
index 0000000..34fbb53
--- /dev/null
+++ b/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_loop_back.py
@@ -0,0 +1,73 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+from telemetrix_uno_r4.minima.telemetrix_uno_r4_minima_aio import telemetrix_uno_r4_minima_aio
+
+"""
+Loopback some data to assure that data can be sent and received between
+the Telemetrix client and arduino-telemetrix server.
+"""
+
+
+async def the_callback(data):
+ """
+ A callback function to report receive the looped back data
+
+ :param data: [looped back data]
+ """
+ print(f'Looped back: {chr(data[0])}')
+
+
+async def loop_back(my_board, loop_back_data):
+ """
+ This function will request that the supplied characters be
+ sent to the board and looped back and printed out to the console.
+
+ :param my_board: a telemetrix_aio instance
+ :param loop_back_data: A list of characters to have looped back
+ """
+ try:
+ for data in loop_back_data:
+ await my_board.loop_back(data, callback=the_callback)
+ print(f'Sending: {data}')
+ await asyncio.sleep(.1)
+ except KeyboardInterrupt:
+ my_board.shutdown()
+ sys.exit(0)
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_minima_aio.TelemetrixUnoR4MinimaAio()
+char_list = ['A', 'B', 'Z']
+try:
+ # start the main function
+ loop.run_until_complete(loop_back(board, char_list))
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+except KeyboardInterrupt:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_servo.py b/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_servo.py
new file mode 100644
index 0000000..689b851
--- /dev/null
+++ b/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_servo.py
@@ -0,0 +1,66 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+
+from telemetrix_uno_r4.minima.telemetrix_uno_r4_minima_aio import telemetrix_uno_r4_minima_aio
+
+"""
+This example will set a servo to 0, 90 and 180 degree
+positions.
+"""
+
+
+async def servo(my_board, pin):
+ """
+ Set a pin to servo mode and then adjust
+ its position.
+
+ :param my_board: telemetrix_aio instance
+ :param pin: pin to be controlled
+ """
+
+ # set the pin mode
+ await my_board.set_pin_mode_servo(pin)
+
+ await asyncio.sleep(1)
+
+ await my_board.servo_write(pin, 0)
+ await asyncio.sleep(1)
+ await my_board.servo_write(pin, 90)
+ await asyncio.sleep(1)
+ await my_board.servo_write(pin, 180)
+ await my_board.servo_detach(pin)
+
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+board = telemetrix_uno_r4_minima_aio.TelemetrixUnoR4MinimaAio()
+try:
+ loop.run_until_complete(servo(board, 5))
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
+except KeyboardInterrupt:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_sonar_disable.py b/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_sonar_disable.py
new file mode 100644
index 0000000..3dd426c
--- /dev/null
+++ b/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_sonar_disable.py
@@ -0,0 +1,85 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+import time
+from telemetrix_uno_r4.minima.telemetrix_uno_r4_minima_aio import telemetrix_uno_r4_minima_aio
+
+"""
+This program continuously monitors an HC-SR04 Ultrasonic Sensor
+It reports changes to the distance sensed.
+"""
+TRIGGER_PIN = 9
+ECHO_PIN = 10
+
+# indices into callback data
+REPORT_TYPE = 0
+TRIG_PIN = 1
+DISTANCE = 2
+TIME = 3
+
+
+# A callback function to display the distance
+async def the_callback(data):
+ """
+ The callback function to display the change in distance
+ :param data: [report_type = PrivateConstants.SONAR_DISTANCE, trigger pin number, distance, timestamp]
+ """
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[3]))
+ print(f'Sonar Report: Trigger Pin: {data[1]} Distance: {data[2]} Time: {date}')
+
+
+async def sonar(my_board, trigger_pin, echo_pin, callback):
+ """
+ Set the pin mode for a sonar device. Results will appear via the
+ callback.
+
+ :param my_board: a telemetrix_aio instance
+ :param trigger_pin: Arduino pin number
+ :param echo_pin: Arduino pin number
+ :param callback: The callback function
+ """
+
+ # set the pin mode for the trigger and echo pins
+ await my_board.set_pin_mode_sonar(trigger_pin, echo_pin, callback)
+ # wait forever
+ while True:
+ try:
+ await asyncio.sleep(5)
+ await my_board.sonar_disable()
+ await asyncio.sleep(5)
+ await my_board.sonar_enable()
+
+ except KeyboardInterrupt:
+ await my_board.shutdown()
+ sys.exit(0)
+
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_minima_aio.TelemetrixUnoR4MinimaAio()
+
+try:
+ # start the main function
+ loop.run_until_complete(sonar(board, TRIGGER_PIN, ECHO_PIN, the_callback))
+except KeyboardInterrupt:
+ loop.run_until_complete(board.shutdown())
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_spi_adxl345_accelerometer.py b/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_spi_adxl345_accelerometer.py
new file mode 100644
index 0000000..593c785
--- /dev/null
+++ b/telemetrix_uno_r4/r4_minima_examples/asyncio/msa_spi_adxl345_accelerometer.py
@@ -0,0 +1,123 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+from telemetrix_uno_r4.minima.telemetrix_uno_r4_minima_aio import telemetrix_uno_r4_minima_aio
+
+
+"""
+This program reads x, y, and z registers of an ADXL345 using 4 Wire SPI interface
+"""
+
+"""
+Connection scheme:
+CS = pin 10
+SDA = pin 11
+SDO = pin 12
+SCL = pin 13
+
+"""
+
+
+async def the_callback(data):
+ """
+ :param data: [pin_type, chip select pin, device read register, x data pair,
+ y data pair, z data pair, time stamp]
+
+ """
+ report_type = data[0]
+ chip_select_pin = data[1]
+ device_read_register = data[2] & 0x3f # strip off read command bits
+ number_bytes_read = data[3]
+ x_msb = data[4]
+ x_lsb = data[5]
+ y_msb = data[6]
+ y_lsb = data[7]
+ z_msb = data[8]
+ z_lsb = data[9]
+
+ x_data = (x_msb << 8) + x_lsb
+ y_data = (y_msb << 8) + y_lsb
+ z_data = (z_msb << 8) + z_lsb
+
+ # test report type for SPI report
+ if report_type == 13:
+ print(f'SPI Report: CS Pin: {chip_select_pin} SPI Register: '
+ f'{device_read_register} Number Of Bytes Read: {number_bytes_read} x: '
+ f'{x_data} y: {y_data} z: {z_data}')
+ else:
+ print(f'unexpected report type: {report_type}')
+
+
+async def adxl345(my_board):
+ """
+
+ :type my_board: object
+ """
+ # initialize spi mode for chipselect on pin 10
+ await my_board.set_pin_mode_spi([10])
+ await asyncio.sleep(.3)
+
+ # set the SPI format
+ # spi speed is FPU frequency divided by 4
+ # data order is MSB
+ # mode is MODE3
+ await my_board.spi_set_format(8, 1, 3)
+ await asyncio.sleep(.3)
+
+ # set up power and control register
+ await my_board.spi_write_blocking(10, [45, 0])
+ await asyncio.sleep(.3)
+
+ await my_board.spi_write_blocking(10, [45, 8])
+ await asyncio.sleep(.3)
+
+ # set up data format register for 4 wire spi
+ await my_board.spi_write_blocking(10, [49, 0])
+ await asyncio.sleep(.3)
+
+ # read 6 bytes from the data register
+ # for a multibyte read, we need to OR in a 0x40 into the register value
+ while True:
+ # read 6 bytes from the data register
+ try:
+ await my_board.spi_read_blocking(10, 50 | 0x40, 6, the_callback)
+
+ await asyncio.sleep(.5)
+
+ except (KeyboardInterrupt, RuntimeError):
+ await my_board.shutdown()
+ sys.exit(0)
+
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_minima_aio.TelemetrixUnoR4MinimaAio()
+
+try:
+ # start the main function
+ loop.run_until_complete(adxl345(board))
+except KeyboardInterrupt:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_minima_examples/threaded/mst_analog_input.py b/telemetrix_uno_r4/r4_minima_examples/threaded/mst_analog_input.py
new file mode 100644
index 0000000..bc5bacb
--- /dev/null
+++ b/telemetrix_uno_r4/r4_minima_examples/threaded/mst_analog_input.py
@@ -0,0 +1,89 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,f
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+"""
+
+import sys
+import time
+
+from telemetrix_uno_r4.minima.telemetrix_uno_r4_minima import telemetrix_uno_r4_minima
+"""
+Monitor an analog input pin
+"""
+
+"""
+Setup a pin for analog input and monitor its changes
+"""
+
+# Set up a pin for analog input and monitor its changes
+ANALOG_PIN = 2 # arduino pin number (A2)
+
+# Callback data indices
+CB_PIN_MODE = 0
+CB_PIN = 1
+CB_VALUE = 2
+CB_TIME = 3
+
+
+def the_callback(data):
+ """
+ A callback function to report data changes.
+ This will print the pin number, its reported value and
+ the date and time when the change occurred
+
+ :param data: [pin, current reported value, pin_mode, timestamp]
+ """
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[CB_TIME]))
+ print(f'Pin Mode: {data[CB_PIN_MODE]} Pin: {data[CB_PIN]} Value: {data[CB_VALUE]} Time Stamp: {date}')
+
+
+def analog_in(my_board, pin):
+ """
+ This function establishes the pin as an
+ analog input. Any changes on this pin will
+ be reported through the call back function.
+
+ :param my_board: a telemetrix instance
+ :param pin: Arduino pin number
+ """
+
+ # set the pin mode
+ my_board.set_pin_mode_analog_input(pin, differential=5, callback=the_callback)
+
+ # time.sleep(5)
+ # my_board.disable_analog_reporting()
+ # time.sleep(5)
+ # my_board.enable_analog_reporting()
+
+ print('Enter Control-C to quit.')
+ try:
+ while True:
+ try:
+ time.sleep(1)
+ except KeyboardInterrupt:
+ sys.exit(0)
+ except KeyboardInterrupt:
+ board.shutdown()
+ sys.exit(0)
+
+
+board = telemetrix_uno_r4_minima.TelemetrixUnoR4Minima()
+try:
+ analog_in(board, ANALOG_PIN)
+except KeyboardInterrupt:
+ board.shutdown()
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_minima_examples/threaded/mst_blink.py b/telemetrix_uno_r4/r4_minima_examples/threaded/mst_blink.py
new file mode 100644
index 0000000..5b5c33d
--- /dev/null
+++ b/telemetrix_uno_r4/r4_minima_examples/threaded/mst_blink.py
@@ -0,0 +1,53 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,f
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ """
+
+import sys
+import time
+
+from telemetrix_uno_r4.minima.telemetrix_uno_r4_minima import telemetrix_uno_r4_minima
+"""
+Setup a pin for digital output
+and toggle the pin 5 times.
+"""
+
+# some globals
+DIGITAL_PIN = 13 # the board LED
+
+# Create a Telemetrix instance.
+# board = telemetrix_uno_r4_minima.TelemetrixUnoR4Minima()
+board = telemetrix_uno_r4_minima.TelemetrixUnoR4Minima()
+
+# Set the DIGITAL_PIN as an output pin
+board.set_pin_mode_digital_output(DIGITAL_PIN)
+
+# Blink the LED and provide feedback as
+# to the LED state on the console.
+for blink in range(5):
+ # When hitting control-c to end the program
+ # in this loop, we are likely to get a KeyboardInterrupt
+ # exception. Catch the exception and exit gracefully.
+ try:
+ print('1')
+ board.digital_write(DIGITAL_PIN, 1)
+ time.sleep(1)
+ print('0')
+ board.digital_write(DIGITAL_PIN, 0)
+ time.sleep(1)
+ except KeyboardInterrupt:
+ board.shutdown()
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_minima_examples/threaded/mst_dht.py b/telemetrix_uno_r4/r4_minima_examples/threaded/mst_dht.py
new file mode 100644
index 0000000..8271794
--- /dev/null
+++ b/telemetrix_uno_r4/r4_minima_examples/threaded/mst_dht.py
@@ -0,0 +1,100 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import sys
+import time
+
+from telemetrix_uno_r4.minima.telemetrix_uno_r4_minima import telemetrix_uno_r4_minima
+
+"""
+This program monitors a DHT22 sensor.
+"""
+
+# Arduino pin number
+DHT_PIN = 8
+
+# indices into callback data for valid data
+# REPORT_TYPE = 0
+# READ_RESULT = 1
+# PIN = 2
+# DHT_TYPE = 3
+# HUMIDITY = 4
+# TEMPERATURE = 5
+# TIME = 6
+
+# indices into callback data for error report
+# REPORT_TYPE = 0
+# READ_RESULT = 1
+# PIN = 2
+# DHT_TYPE = 3
+# TIME = 4
+
+
+# A callback function to display the distance
+# noinspection GrazieInspection
+def the_callback(data):
+ # noinspection GrazieInspection
+ """
+ The callback function to display the change in distance
+ :param data: [report_type = PrivateConstants.DHT, error = 0, pin number,
+ dht_type, humidity, temperature timestamp]
+ if this is an error report:
+ [report_type = PrivateConstants.DHT, error != 0, pin number, dht_type
+ timestamp]
+ """
+ if data[1]:
+ # error message
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[4]))
+ print(f'DHT Error Report:'
+ f'Pin: {data[2]} DHT Type: {data[3]} Error: {data[1]} Time: {date}')
+ else:
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[6]))
+ print(f'DHT Valid Data Report:'
+ f'Pin: {data[2]} DHT Type: {data[3]} Humidity: {data[4]} Temperature:'
+ f' {data[5]} Time: {date}')
+
+
+def dht(my_board, pin, callback, dht_type):
+ # noinspection GrazieInspection
+ """
+ Set the pin mode for a DHT 22 device. Results will appear via the
+ callback.
+
+ :param my_board: an telemetrix instance
+ :param pin: Arduino pin number
+ :param callback: The callback function
+ :param dht_type: 22 or 11
+ """
+
+ # set the pin mode for the DHT device
+ my_board.set_pin_mode_dht(pin, callback, dht_type)
+
+
+board = telemetrix_uno_r4_minima.TelemetrixUnoR4Minima()
+try:
+ dht(board, DHT_PIN, callback=the_callback, dht_type=11)
+
+ # wait forever
+ while True:
+ try:
+ time.sleep(.01)
+ except KeyboardInterrupt:
+ board.shutdown()
+ sys.exit(0)
+except (KeyboardInterrupt, RuntimeError):
+ board.shutdown()
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_minima_examples/threaded/mst_digital_input.py b/telemetrix_uno_r4/r4_minima_examples/threaded/mst_digital_input.py
new file mode 100644
index 0000000..03234f4
--- /dev/null
+++ b/telemetrix_uno_r4/r4_minima_examples/threaded/mst_digital_input.py
@@ -0,0 +1,99 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,f
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ DHT support courtesy of Martyn Wheeler
+ Based on the DHTNew library - https://github.com/RobTillaart/DHTNew
+"""
+
+import sys
+import time
+
+from telemetrix_uno_r4.minima.telemetrix_uno_r4_minima import telemetrix_uno_r4_minima
+"""
+Monitor a digital input pin
+"""
+
+"""
+Setup a pin for digital input and monitor its changes
+"""
+
+# Set up a pin for analog input and monitor its changes
+DIGITAL_PIN = 12 # arduino pin number
+
+# Callback data indices
+CB_PIN_MODE = 0
+CB_PIN = 1
+CB_VALUE = 2
+CB_TIME = 3
+
+# variable to hold the last time a button state changed
+debounce_time = time.time()
+
+
+def the_callback(data):
+ """
+ A callback function to report data changes.
+ This will print the pin number, its reported value and
+ the date and time when the change occurred
+
+ :param data: [pin, current reported value, pin_mode, timestamp]
+ """
+ global debounce_time
+
+ # if the time from the last event change is > .2 seconds, the input is debounced
+ if data[CB_TIME] - debounce_time > .3:
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[CB_TIME]))
+ print(f'Pin: {data[CB_PIN]} Value: {data[CB_VALUE]} Time Stamp: {date}')
+ debounce_time = data[CB_TIME]
+
+
+def digital_in(my_board, pin):
+ """
+ This function establishes the pin as a
+ digital input. Any changes on this pin will
+ be reported through the call back function.
+
+ :param my_board: a telemetrix instance
+ :param pin: Arduino pin number
+ """
+
+ # set the pin mode
+ my_board.set_pin_mode_digital_input(pin, the_callback)
+ # time.sleep(1)
+ # my_board.disable_all_reporting()
+ # time.sleep(4)
+ # my_board.enable_digital_reporting(12)
+
+ # time.sleep(3)
+ # my_board.enable_digital_reporting(pin)
+ # time.sleep(1)
+
+ print('Enter Control-C to quit.')
+ # my_board.enable_digital_reporting(12)
+ try:
+ while True:
+ time.sleep(.0001)
+ except KeyboardInterrupt:
+ board.shutdown()
+ sys.exit(0)
+
+
+board = telemetrix_uno_r4_minima.TelemetrixUnoR4Minima()
+try:
+ digital_in(board, DIGITAL_PIN)
+except KeyboardInterrupt:
+ board.shutdown()
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_minima_examples/threaded/mst_digital_input_pullup.py b/telemetrix_uno_r4/r4_minima_examples/threaded/mst_digital_input_pullup.py
new file mode 100644
index 0000000..076ac3f
--- /dev/null
+++ b/telemetrix_uno_r4/r4_minima_examples/threaded/mst_digital_input_pullup.py
@@ -0,0 +1,93 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,f
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ DHT support courtesy of Martyn Wheeler
+ Based on the DHTNew library - https://github.com/RobTillaart/DHTNew
+"""
+
+import sys
+import time
+
+from telemetrix_uno_r4.minima.telemetrix_uno_r4_minima import telemetrix_uno_r4_minima
+
+"""
+Monitor a digital input pin with pullup enabled
+"""
+
+"""
+Setup a pin for digital input and monitor its changes
+"""
+
+# Set up a pin for analog input and monitor its changes
+DIGITAL_PIN = 12 # arduino pin number
+
+# Callback data indices
+CB_PIN_MODE = 0
+CB_PIN = 1
+CB_VALUE = 2
+CB_TIME = 3
+
+
+def the_callback(data):
+ """
+ A callback function to report data changes.
+ This will print the pin number, its reported value and
+ the date and time when the change occurred
+
+ :param data: [pin, current reported value, pin_mode, timestamp]
+ """
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[CB_TIME]))
+ print(f'Pin Mode: {data[CB_PIN_MODE]} Pin: {data[CB_PIN]} Value: {data[CB_VALUE]} Time Stamp: {date}')
+
+
+def digital_in_pullup(my_board, pin):
+ """
+ This function establishes the pin as a
+ digital input. Any changes on this pin will
+ be reported through the call back function.
+
+ :param my_board: a telemetrix instance
+ :param pin: Arduino pin number
+ """
+
+ # set the pin mode
+ my_board.set_pin_mode_digital_input_pullup(pin, the_callback)
+ # time.sleep(1)
+ # my_board.disable_all_reporting()
+ # time.sleep(4)
+ # my_board.enable_digital_reporting(12)
+
+ # time.sleep(3)
+ # my_board.enable_digital_reporting(pin)
+ # time.sleep(1)
+
+ print('Enter Control-C to quit.')
+ # my_board.enable_digital_reporting(12)
+ try:
+ while True:
+ time.sleep(.0001)
+ except KeyboardInterrupt:
+ board.shutdown()
+ sys.exit(0)
+
+
+board = telemetrix_uno_r4_minima.TelemetrixUnoR4Minima()
+
+try:
+ digital_in_pullup(board, DIGITAL_PIN)
+except KeyboardInterrupt:
+ board.shutdown()
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_minima_examples/threaded/mst_fade.py b/telemetrix_uno_r4/r4_minima_examples/threaded/mst_fade.py
new file mode 100644
index 0000000..345d095
--- /dev/null
+++ b/telemetrix_uno_r4/r4_minima_examples/threaded/mst_fade.py
@@ -0,0 +1,59 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,f
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+"""
+
+import sys
+import time
+
+from telemetrix_uno_r4.minima.telemetrix_uno_r4_minima import telemetrix_uno_r4_minima
+
+"""
+Setup a pin for output and fade its intensity
+"""
+
+# some globals
+# make sure to select a PWM pin
+DIGITAL_PIN = 13
+
+# Create a Telemetrix instance.
+board = telemetrix_uno_r4_minima.TelemetrixUnoR4Minima()
+
+# Set the DIGITAL_PIN as an output pin
+board.set_pin_mode_analog_output(DIGITAL_PIN)
+# board.set_pin_mode_analog_output(DIGITAL_PIN)
+
+
+# When hitting control-c to end the program
+# in this loop, we are likely to get a KeyboardInterrupt
+# exception. Catch the exception and exit gracefully.
+
+try:
+ print('Fading up...')
+ for i in range(255):
+ board.analog_write(DIGITAL_PIN, i)
+ time.sleep(.005)
+ print('Fading down...')
+ for i in range(255, -1, -1):
+ board.analog_write(DIGITAL_PIN, i)
+ time.sleep(.005)
+
+ board.set_pin_mode_digital_output(DIGITAL_PIN)
+
+except KeyboardInterrupt:
+ board.shutdown()
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_minima_examples/threaded/mst_hc-sr04_distance_sensor.py b/telemetrix_uno_r4/r4_minima_examples/threaded/mst_hc-sr04_distance_sensor.py
new file mode 100644
index 0000000..ac1fb7a
--- /dev/null
+++ b/telemetrix_uno_r4/r4_minima_examples/threaded/mst_hc-sr04_distance_sensor.py
@@ -0,0 +1,75 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import sys
+import time
+from telemetrix_uno_r4.minima.telemetrix_uno_r4_minima import telemetrix_uno_r4_minima
+
+"""
+This program continuously monitors an HC-SR04 Ultrasonic Sensor
+It reports changes to the distance sensed.
+"""
+TRIGGER_PIN = 9
+ECHO_PIN = 10
+
+# indices into callback data
+REPORT_TYPE = 0
+TRIG_PIN = 1
+DISTANCE = 2
+TIME = 3
+
+
+# A callback function to display the distance
+def the_callback(data):
+ """
+ The callback function to display the change in distance
+ :param data: [report_type = PrivateConstants.SONAR_DISTANCE, trigger pin number, distance, timestamp]
+ """
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[3]))
+ print(f'Sonar Report: Trigger Pin: {data[1]} Distance: {data[2]} Time: {date}')
+
+
+def sonar(my_board, trigger_pin, echo_pin, callback):
+ """
+ Set the pin mode for a sonar device. Results will appear via the
+ callback.
+
+ :param my_board: a telemetrix instance
+ :param trigger_pin: Arduino pin number
+ :param echo_pin: Arduino pin number
+ :param callback: The callback function
+ """
+
+ # set the pin mode for the trigger and echo pins
+ my_board.set_pin_mode_sonar(trigger_pin, echo_pin, callback)
+ # wait forever
+ while True:
+ try:
+ time.sleep(.01)
+ except KeyboardInterrupt:
+ my_board.shutdown()
+ time.sleep(1)
+ sys.exit(0)
+
+
+board = telemetrix_uno_r4_minima.TelemetrixUnoR4Minima()
+try:
+ sonar(board, TRIGGER_PIN, ECHO_PIN, the_callback)
+ board.shutdown()
+except (KeyboardInterrupt, RuntimeError):
+ board.shutdown()
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_minima_examples/threaded/mst_i2c_adxl345_accelerometer.py b/telemetrix_uno_r4/r4_minima_examples/threaded/mst_i2c_adxl345_accelerometer.py
new file mode 100644
index 0000000..5e11fee
--- /dev/null
+++ b/telemetrix_uno_r4/r4_minima_examples/threaded/mst_i2c_adxl345_accelerometer.py
@@ -0,0 +1,98 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import sys
+import time
+from telemetrix_uno_r4.minima.telemetrix_uno_r4_minima import telemetrix_uno_r4_minima
+
+"""
+This example sets up and control an ADXL345 i2c accelerometer.
+It will continuously print data the raw xyz data from the device.
+"""
+
+
+# the call back function to print the adxl345 data
+def the_callback(data):
+ """
+
+ :param data: [pin_type, Device address, device read register, x data pair, y data pair, z data pair]
+ :return:
+ """
+ report_type = data[0]
+ number_bytes_read = data[2]
+ i2c_device_address = data[3]
+ i2c_register = data[4]
+ x_msb = data[5]
+ x_lsb = data[6]
+ y_msb = data[7]
+ y_lsb = data[8]
+ z_msb = data[9]
+ z_lsb = data[10]
+
+ x_data = (x_msb << 8) + x_lsb
+ y_data = (y_msb << 8) + y_lsb
+ z_data = (z_msb << 8) + z_lsb
+
+ # test report type for SPI report
+ if report_type == 10:
+ print(f'i2c Report: i2c device address: {i2c_device_address } i2c '
+ f'Register: '
+ f'{i2c_register} Number Of Bytes Read: {number_bytes_read} x: '
+ f'{x_data} y: {y_data} z: {z_data}')
+ else:
+ print(f'unexpected report type: {report_type}')
+
+
+def adxl345(my_board):
+ # setup adxl345
+ # device address = 83
+ my_board.set_pin_mode_i2c()
+
+ # set up power and control register
+ my_board.i2c_write(83, [45, 0])
+ time.sleep(.1)
+ my_board.i2c_write(83, [45, 8])
+ time.sleep(.1)
+
+ # set up the data format register
+ my_board.i2c_write(83, [49, 8])
+ time.sleep(.1)
+ my_board.i2c_write(83, [49, 3])
+ time.sleep(.1)
+
+ # read_count = 20
+ while True:
+ # read 6 bytes from the data register
+ try:
+ my_board.i2c_read(83, 50, 6, the_callback)
+ time.sleep(.1)
+
+ except (KeyboardInterrupt, RuntimeError):
+ my_board.shutdown()
+ sys.exit(0)
+
+
+board = telemetrix_uno_r4_minima.TelemetrixUnoR4Minima()
+
+try:
+ adxl345(board)
+except KeyboardInterrupt:
+ try:
+ board.shutdown()
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_minima_examples/threaded/mst_loop_back.py b/telemetrix_uno_r4/r4_minima_examples/threaded/mst_loop_back.py
new file mode 100644
index 0000000..41f398c
--- /dev/null
+++ b/telemetrix_uno_r4/r4_minima_examples/threaded/mst_loop_back.py
@@ -0,0 +1,61 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import time
+import sys
+from telemetrix_uno_r4.minima.telemetrix_uno_r4_minima import telemetrix_uno_r4_minima
+
+"""
+Loopback some data to assure that data can be sent and received between
+the Telemetrix client and arduino-telemetrix server.
+"""
+
+
+def the_callback(data):
+ """
+ A callback function to report receive the looped back data
+
+ :param data: [looped back data]
+ """
+ print(f'Looped back: {chr(data[0])}')
+
+
+def loop_back(my_board, loop_back_data):
+ """
+ This function will request that the supplied characters be
+ sent to the board and looped back and printed out to the console.
+
+ :param my_board: a telemetrix instance
+ :param loop_back_data: A list of characters to have looped back
+ """
+ try:
+ for data in loop_back_data:
+ my_board.loop_back(data, callback=the_callback)
+ print(f'Sending: {data}')
+ except KeyboardInterrupt:
+ board.shutdown()
+ sys.exit(0)
+
+
+board = telemetrix_uno_r4_minima.TelemetrixUnoR4Minima()
+char_list = ['A', 'B', 'Z']
+try:
+ loop_back(board, char_list)
+ time.sleep(1)
+except KeyboardInterrupt:
+ board.shutdown()
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_minima_examples/threaded/mst_servo.py b/telemetrix_uno_r4/r4_minima_examples/threaded/mst_servo.py
new file mode 100644
index 0000000..0fc1251
--- /dev/null
+++ b/telemetrix_uno_r4/r4_minima_examples/threaded/mst_servo.py
@@ -0,0 +1,49 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,f
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+"""
+
+import sys
+import time
+
+from telemetrix_uno_r4.minima.telemetrix_uno_r4_minima import telemetrix_uno_r4_minima
+
+"""
+Attach a pin to a servo and move it about.
+"""
+
+# some globals
+SERVO_PIN = 5
+
+# Create a Telemetrix instance.
+board = telemetrix_uno_r4_minima.TelemetrixUnoR4Minima()
+try:
+ board.set_pin_mode_servo(SERVO_PIN, 100, 3000)
+ time.sleep(.2)
+ board.servo_write(SERVO_PIN, 90)
+ time.sleep(1)
+ board.servo_write(SERVO_PIN, 0)
+ time.sleep(1)
+ board.servo_write(SERVO_PIN, 180)
+ time.sleep(1)
+ board.servo_write(SERVO_PIN, 90)
+ time.sleep(1)
+
+ board.servo_detach(SERVO_PIN)
+ time.sleep(.2)
+ board.shutdown()
+except KeyboardInterrupt:
+ board.shutdown()
diff --git a/telemetrix_uno_r4/r4_minima_examples/threaded/mst_sonar_disable.py b/telemetrix_uno_r4/r4_minima_examples/threaded/mst_sonar_disable.py
new file mode 100644
index 0000000..bcca169
--- /dev/null
+++ b/telemetrix_uno_r4/r4_minima_examples/threaded/mst_sonar_disable.py
@@ -0,0 +1,81 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+import time
+from telemetrix_uno_r4.minima.telemetrix_uno_r4_minima import telemetrix_uno_r4_minima
+
+"""
+This program continuously monitors an HC-SR04 Ultrasonic Sensor
+It reports changes to the distance sensed.
+"""
+TRIGGER_PIN = 9
+ECHO_PIN = 10
+
+# indices into callback data
+REPORT_TYPE = 0
+TRIG_PIN = 1
+DISTANCE = 2
+TIME = 3
+
+
+# A callback function to display the distance
+def the_callback(data):
+ """
+ The callback function to display the change in distance
+ :param data: [report_type = PrivateConstants.SONAR_DISTANCE, trigger pin number, distance, timestamp]
+ """
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[3]))
+ print(f'Sonar Report: Trigger Pin: {data[1]} Distance: {data[2]} Time: {date}')
+
+
+def sonar(my_board, trigger_pin, echo_pin, callback):
+ """
+ Set the pin mode for a sonar device. Results will appear via the
+ callback.
+
+ :param my_board: a telemetrix_aio instance
+ :param trigger_pin: Arduino pin number
+ :param echo_pin: Arduino pin number
+ :param callback: The callback function
+ """
+
+ # set the pin mode for the trigger and echo pins
+ my_board.set_pin_mode_sonar(trigger_pin, echo_pin, callback)
+ # wait forever
+ while True:
+ try:
+ time.sleep(5)
+ my_board.sonar_disable()
+ time.sleep(5)
+ my_board.sonar_enable()
+
+ except KeyboardInterrupt:
+ my_board.shutdown()
+ sys.exit(0)
+
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_minima.TelemetrixUnoR4Minima()
+try:
+ sonar(board, TRIGGER_PIN, ECHO_PIN, the_callback)
+ board.shutdown()
+except (KeyboardInterrupt, RuntimeError):
+ board.shutdown()
+ sys.exit(0)
+
diff --git a/telemetrix_uno_r4/r4_minima_examples/threaded/mst_spi_adxl345_accelerometer.py b/telemetrix_uno_r4/r4_minima_examples/threaded/mst_spi_adxl345_accelerometer.py
new file mode 100644
index 0000000..10123b9
--- /dev/null
+++ b/telemetrix_uno_r4/r4_minima_examples/threaded/mst_spi_adxl345_accelerometer.py
@@ -0,0 +1,115 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import sys
+import time
+from telemetrix_uno_r4.minima.telemetrix_uno_r4_minima import telemetrix_uno_r4_minima
+
+"""
+This program reads x, y, and z registers of an ADXL345 using 4 Wire SPI interface
+"""
+
+"""
+Connection scheme:
+CS = pin 10
+SDA = pin 11
+SDO = pin 12
+SCL = pin 13
+
+"""
+
+
+def the_callback(data):
+ """
+ :param data: [pin_type, chip select pin, device read register, x data pair,
+ y data pair, z data pair, time stamp]
+
+ """
+ report_type = data[0]
+ chip_select_pin = data[1]
+ device_read_register = data[2] & 0x3f # strip off read command bits
+ number_bytes_read = data[3]
+ x_msb = data[4]
+ x_lsb = data[5]
+ y_msb = data[6]
+ y_lsb = data[7]
+ z_msb = data[8]
+ z_lsb = data[9]
+
+ x_data = (x_msb << 8) + x_lsb
+ y_data = (y_msb << 8) + y_lsb
+ z_data = (z_msb << 8) + z_lsb
+
+ # test report type for SPI report
+ if report_type == 13:
+ print(f'SPI Report: CS Pin: {chip_select_pin} SPI Register: '
+ f'{device_read_register} Number Of Bytes Read: {number_bytes_read} x: '
+ f'{x_data} y: {y_data} z: {z_data}')
+ else:
+ print(f'unexpected report type: {report_type}')
+
+
+def adxl345(my_board):
+ """
+
+ :type my_board: object
+ """
+ # initialize spi mode for chipselect on pin 10
+ my_board.set_pin_mode_spi([10])
+ time.sleep(.3)
+
+ # set the SPI format
+ # spi speed is FPU frequency divided by 4
+ # data order is MSB
+ # mode is MODE3
+ my_board.spi_set_format(8, 1, 3)
+ time.sleep(.3)
+
+ # set up power and control register
+ my_board.spi_write_blocking(10, [45, 0])
+ time.sleep(.3)
+
+ my_board.spi_write_blocking(10, [45, 8])
+ time.sleep(.3)
+
+ # set up data format register for 4 wire spi
+ my_board.spi_write_blocking(10, [49, 0])
+ time.sleep(.3)
+
+ # read 6 bytes from the data register
+ # for a multibyte read, we need to OR in a 0x40 into the register value
+ while True:
+ # read 6 bytes from the data register
+ try:
+ my_board.spi_read_blocking(10, 50 | 0x40, 6, the_callback)
+
+ time.sleep(.5)
+
+ except (KeyboardInterrupt, RuntimeError):
+ my_board.shutdown()
+ sys.exit(0)
+
+
+board = telemetrix_uno_r4_minima.TelemetrixUnoR4Minima()
+try:
+ adxl345(board)
+except KeyboardInterrupt:
+ try:
+ board.shutdown()
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_analog_input.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_analog_input.py
new file mode 100644
index 0000000..667de88
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_analog_input.py
@@ -0,0 +1,102 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+import time
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+
+"""
+This file demonstrates analog input using both callbacks and
+polling. Time stamps are provided in both "cooked" and raw form
+"""
+
+# Set up a pin for analog input and monitor its changes
+ANALOG_PIN = 2 # arduino pin number
+
+# Callback data indices
+CB_PIN_MODE = 0
+CB_PIN = 1
+CB_VALUE = 2
+CB_TIME = 3
+
+
+async def the_callback(data):
+ """
+ A callback function to report data changes.
+
+ :param data: [pin_mode, pin, current_reported_value, timestamp]
+ """
+
+ formatted_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[CB_TIME]))
+ print(f'Analog Call Input Callback: pin={data[CB_PIN]}, '
+ f'Value={data[CB_VALUE]} Time={formatted_time} '
+ f'(Raw Time={data[CB_TIME]})')
+
+
+async def analog_in(my_board, pin):
+ """
+ This function establishes the pin as an
+ analog input. Any changes on this pin will
+ be reported through the call back function.
+
+ Every 5 seconds the last value and time stamp is polled
+ and printed.
+
+ Also, the differential parameter is being used.
+ The callback will only be called when there is
+ difference of 5 or more between the current and
+ last value reported.
+
+ :param my_board: a telemetrix_aio instance
+
+ :param pin: Arduino pin number
+ """
+ await my_board.start_aio()
+ await my_board.set_pin_mode_analog_input(pin, 5, the_callback)
+
+ # run forever waiting for input changes
+ try:
+ while True:
+ await asyncio.sleep(.001)
+
+ except KeyboardInterrupt:
+ await my_board.shutdown()
+ sys.exit(0)
+
+# instantiate telemetrix_aio
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(autostart=False,
+ transport_type=2)
+
+try:
+ # start the main function
+ loop.run_until_complete(analog_in(board, ANALOG_PIN))
+except KeyboardInterrupt:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
+
+
+
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_blink.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_blink.py
new file mode 100644
index 0000000..80a7bad
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_blink.py
@@ -0,0 +1,71 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+import asyncio
+import sys
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+s
+"""
+Setup a pin for digital output and output a signal
+and toggle the pin. Do this 4 times.
+"""
+
+# some globals
+DIGITAL_PIN = 13 # arduino pin number
+
+
+async def blink(pin, my_board):
+ """
+ This function will to toggle a digital pin.
+
+ :param pin: pin to be controlled
+
+ :param my_board: telemetrix instance
+ """
+
+ # set the pin mode
+
+ await my_board.start_aio()
+ await my_board.set_pin_mode_digital_output(pin)
+
+ # toggle the pin 4 times and exit
+ for x in range(4):
+ print('ON')
+ await my_board.digital_write(pin, 1)
+ await asyncio.sleep(1)
+ print('OFF')
+ await my_board.digital_write(pin, 0)
+ await asyncio.sleep(1)
+
+ await my_board.shutdown()
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(autostart=False,
+ transport_type=2)
+try:
+ # start the main function
+ loop.run_until_complete(blink(DIGITAL_PIN, board))
+except KeyboardInterrupt:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_dht.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_dht.py
new file mode 100644
index 0000000..0bc57b4
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_dht.py
@@ -0,0 +1,114 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+import time
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+
+"""
+This program monitors two DHT22 and two DHT11 sensors.
+"""
+
+
+# indices into callback data for valid data
+# REPORT_TYPE = 0
+# READ_RESULT = 1
+# PIN = 2
+# DHT_TYPE = 3
+# HUMIDITY = 4
+# TEMPERATURE = 5
+# TIME = 6
+
+# indices into callback data for error report
+# REPORT_TYPE = 0
+# READ_RESULT = 1
+# PIN = 2
+# DHT_TYPE = 3
+# TIME = 4
+
+# Arduino Pin Number
+DHT_PIN = 8
+
+
+# A callback function to display the distance
+# noinspection GrazieInspection
+async def the_callback(data):
+ # noinspection GrazieInspection
+ """
+ The callback function to display the change in distance
+ :param data: [report_type = PrivateConstants.DHT, error = 0, pin number,
+ dht_type, humidity, temperature timestamp]
+ if this is an error report:
+ [report_type = PrivateConstants.DHT, error != 0, pin number, dht_type
+ timestamp]
+ """
+ if data[1]:
+ # error message
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[4]))
+ print(f'DHT Error Report:'
+ f'Pin: {data[2]} DHT Type: {data[3]} Error: {data[1]} Time: {date}')
+ else:
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[6]))
+ print(f'DHT Valid Data Report:'
+ f'Pin: {data[2]} DHT Type: {data[3]} Humidity: {data[4]} Temperature:'
+ f' {data[5]} Time: {date}')
+
+
+async def dht(my_board, pin):
+ # noinspection GrazieInspection
+ """
+ Set the pin mode for a DHT 22 device. Results will appear via the
+ callback.
+
+ :param my_board: a telemetrix instance
+
+ :param pin: DHT data pin
+
+ """
+ await my_board.start_aio()
+ # set the pin mode for a DHT 11 or 22
+ await my_board.set_pin_mode_dht(pin, the_callback, dht_type=11)
+
+ # just sit in a loop waiting for the reports to come in
+ while True:
+ try:
+ await asyncio.sleep(.001)
+ except KeyboardInterrupt:
+ my_board.shutdown()
+ sys.exit(0)
+
+# instantiate telemetrix_aio
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(autostart=False,
+ transport_type=2)
+
+try:
+ # start the main function
+ loop.run_until_complete(dht(board, DHT_PIN))
+except KeyboardInterrupt:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
+
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_digital_input.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_digital_input.py
new file mode 100644
index 0000000..5cdd513
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_digital_input.py
@@ -0,0 +1,106 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,f
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ DHT support courtesy of Martyn Wheeler
+ Based on the DHTNew library - https://github.com/RobTillaart/DHTNew
+"""
+
+import asyncio
+import sys
+import time
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+
+"""
+Monitor a digital input pin
+"""
+
+"""
+Setup a pin for digital input and monitor its changes
+"""
+
+# Set up a pin for analog input and monitor its changes
+DIGITAL_PIN = 12 # arduino pin number
+
+# Callback data indices
+CB_PIN_MODE = 0
+CB_PIN = 1
+CB_VALUE = 2
+CB_TIME = 3
+
+
+async def the_callback(data):
+ """
+ A callback function to report data changes.
+ This will print the pin number, its reported value and
+ the date and time when the change occurred
+
+ :param data: [pin_mode, pin, current reported value, timestamp]
+ """
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[CB_TIME]))
+ print(f'Pin: {data[CB_PIN]} Value: {data[CB_VALUE]} Time Stamp: {date}')
+
+
+async def digital_in(my_board, pin):
+ """
+ This function establishes the pin as a
+ digital input. Any changes on this pin will
+ be reported through the call back function.
+
+ :param my_board: a telemetrix_aio instance
+ :param pin: Arduino pin number
+ """
+
+ # set the pin mode
+ await my_board.start_aio()
+ await my_board.set_pin_mode_digital_input(pin, the_callback)
+
+ # uncomment to try out report enable/disable
+ # await asyncio.sleep(1)
+ # await my_board.disable_all_reporting()
+ # await asyncio.sleep(4)
+ # await my_board.enable_digital_reporting(12)
+
+ # await asyncio.sleep(3)
+ # await my_board.enable_digital_reporting(pin)
+ # await asyncio.sleep(1)
+
+ while True:
+ try:
+ await asyncio.sleep(.001)
+ except KeyboardInterrupt:
+ await board.shutdown()
+ sys.exit(0)
+# instantiate telemetrix_aio
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(autostart=False,
+ transport_type=2)
+
+try:
+ # start the main function
+ loop.run_until_complete(digital_in(board, DIGITAL_PIN))
+except KeyboardInterrupt:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
+
+
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_digital_input_pullup.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_digital_input_pullup.py
new file mode 100644
index 0000000..c15e4d1
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_digital_input_pullup.py
@@ -0,0 +1,95 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+import time
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+
+"""
+Setup a digital pin for input pullup and monitor its changes.
+"""
+
+# some globals
+DIGITAL_PIN = 12 # arduino pin number
+KILL_TIME = 5 # sleep time to keep forever loop open
+
+# Callback data indices
+# Callback data indices
+CB_PIN_MODE = 0
+CB_PIN = 1
+CB_VALUE = 2
+CB_TIME = 3
+
+
+# Set up a pin for digital pin input and monitor its changes
+
+async def the_callback(data):
+ """
+ A callback function to report data changes.
+ This will print the pin number, its reported value and
+ the date and time when the change occurred
+
+ :param data: [pin, current reported value, pin_mode, timestamp]
+ """
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[CB_TIME]))
+ print(f'Pin: {data[CB_PIN]} Value: {data[CB_VALUE]} Time Stamp: {date}')
+
+
+async def digital_in_pullup(my_board, pin):
+ """
+ This function establishes the pin as a
+ digital input. Any changes on this pin will
+ be reported through the call back function.
+
+ :param my_board: a telemetrix_aio instance
+ :param pin: Arduino pin number
+ """
+
+ # start monitoring the pin by setting its mode
+ await my_board.start_aio()
+ await my_board.set_pin_mode_digital_input_pullup(pin, the_callback)
+
+ # get pin changes forever
+ while True:
+ try:
+ await asyncio.sleep(KILL_TIME)
+ except KeyboardInterrupt:
+ await board.shutdown()
+ sys.exit(0)
+
+
+# instantiate telemetrix_aio
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(autostart=False,
+ transport_type=2)
+
+try:
+ # start the main function
+ loop.run_until_complete(digital_in_pullup(board, DIGITAL_PIN))
+except KeyboardInterrupt:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
+
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_fade.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_fade.py
new file mode 100644
index 0000000..548ec9f
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_fade.py
@@ -0,0 +1,74 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,f
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ DHT support courtesy of Martyn Wheeler
+ Based on the DHTNew library - https://github.com/RobTillaart/DHTNew
+"""
+
+import sys
+import time
+import asyncio
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+"""
+Setup a pin for output and fade its intensity
+"""
+
+# some globals
+# make sure to select a PWM pin
+DIGITAL_PIN = 13
+
+
+async def fade(the_board, pin):
+ await the_board.start_aio()
+ # Set the DIGITAL_PIN as an output pin
+ await the_board.set_pin_mode_analog_output(pin)
+
+ # When hitting control-c to end the program
+ # in this loop, we are likely to get a KeyboardInterrupt
+ # exception. Catch the exception and exit gracefully.
+
+ try:
+ print('Fading up...')
+ for i in range(255):
+ await the_board.analog_write(DIGITAL_PIN, i)
+ await asyncio.sleep(.009)
+ print('Fading down...')
+ for i in range(255, -1, -2):
+ await the_board.analog_write(DIGITAL_PIN, i)
+ await asyncio.sleep(.009)
+ await asyncio.sleep(4)
+ await the_board.shutdown()
+ except KeyboardInterrupt:
+ await the_board.shutdown()
+ except Exception as e:
+ await the_board.shutdown()
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(autostart=False,
+ transport_type=2)
+try:
+ # start the main function
+ loop.run_until_complete(fade(board, DIGITAL_PIN))
+except KeyboardInterrupt:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
\ No newline at end of file
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_hc-sr04_distance_sensor.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_hc-sr04_distance_sensor.py
new file mode 100644
index 0000000..1cb4782
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_hc-sr04_distance_sensor.py
@@ -0,0 +1,86 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+import time
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+
+"""
+This program continuously monitors an HC-SR04 Ultrasonic Sensor
+It reports changes to the distance sensed.
+"""
+TRIGGER_PIN = 9
+ECHO_PIN = 10
+
+# indices into callback data
+REPORT_TYPE = 0
+TRIG_PIN = 1
+DISTANCE = 2
+TIME = 3
+
+
+# A callback function to display the distance
+async def the_callback(data):
+ """
+ The callback function to display the change in distance
+ :param data: [report_type = PrivateConstants.SONAR_DISTANCE, trigger pin number, distance, timestamp]
+ """
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[3]))
+ print(f'Sonar Report: Trigger Pin: {data[1]} Distance: {data[2]} Time: {date}')
+
+
+async def sonar(my_board, trigger_pin, echo_pin, callback):
+ """
+ Set the pin mode for a sonar device. Results will appear via the
+ callback.
+
+ :param my_board: a telemetrix_aio instance
+ :param trigger_pin: Arduino pin number
+ :param echo_pin: Arduino pin number
+ :param callback: The callback function
+ """
+
+ await my_board.start_aio()
+ # set the pin mode for the trigger and echo pins
+ await my_board.set_pin_mode_sonar(trigger_pin, echo_pin, callback)
+ # wait forever
+ while True:
+ try:
+ await asyncio.sleep(.1)
+ except KeyboardInterrupt:
+ await my_board.shutdown()
+ sys.exit(0)
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(autostart=False,
+ transport_type=2)
+
+try:
+ # start the main function
+ loop.run_until_complete(sonar(board, TRIGGER_PIN, ECHO_PIN, the_callback))
+
+except KeyboardInterrupt:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_i2c_adxl345_accelerometer.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_i2c_adxl345_accelerometer.py
new file mode 100644
index 0000000..f53ccbf
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_i2c_adxl345_accelerometer.py
@@ -0,0 +1,104 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+
+"""
+This example sets up and control an ADXL345 i2c accelerometer.
+It will continuously print data the raw xyz data from the device.
+"""
+
+
+# the call back function to print the adxl345 data
+async def the_callback(data):
+ """
+
+ :param data: [pin_type, Device address, device read register, x data pair, y data pair, z data pair]
+ :return:
+ """
+ report_type = data[0]
+ number_bytes_read = data[2]
+ i2c_device_address = data[3]
+ i2c_register = data[4]
+ x_msb = data[5]
+ x_lsb = data[6]
+ y_msb = data[7]
+ y_lsb = data[8]
+ z_msb = data[9]
+ z_lsb = data[10]
+
+ x_data = (x_msb << 8) + x_lsb
+ y_data = (y_msb << 8) + y_lsb
+ z_data = (z_msb << 8) + z_lsb
+
+ # test report type for SPI report
+ if report_type == 10:
+ print(f'i2c Report: i2c device address: {i2c_device_address} i2c '
+ f'Register: '
+ f'{i2c_register} Number Of Bytes Read: {number_bytes_read} x: '
+ f'{x_data} y: {y_data} z: {z_data}')
+ else:
+ print(f'unexpected report type: {report_type}')
+
+
+async def adxl345(my_board):
+ await my_board.start_aio()
+ # setup adxl345
+ # device address = 83
+ await my_board.set_pin_mode_i2c()
+
+ # set up power and control register
+ await my_board.i2c_write(83, [45, 0])
+ await asyncio.sleep(.1)
+ await my_board.i2c_write(83, [45, 8])
+ await asyncio.sleep(.1)
+
+ # set up the data format register
+ await my_board.i2c_write(83, [49, 8])
+ await asyncio.sleep(.1)
+ await my_board.i2c_write(83, [49, 3])
+ await asyncio.sleep(.1)
+
+ # read_count = 20
+ while True:
+ # read 6 bytes from the data register
+ try:
+ await my_board.i2c_read(83, 50, 6, the_callback)
+ await asyncio.sleep(.5)
+
+ except (KeyboardInterrupt, RuntimeError):
+ await my_board.shutdown()
+ sys.exit(0)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(autostart=False,
+ transport_type=2)
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+try:
+ # start the main function
+ loop.run_until_complete(adxl345(board))
+except KeyboardInterrupt:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_loop_back.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_loop_back.py
new file mode 100644
index 0000000..6c1a047
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_loop_back.py
@@ -0,0 +1,74 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+"""
+Loopback some data to assure that data can be sent and received between
+the Telemetrix client and arduino-telemetrix server.
+"""
+
+
+async def the_callback(data):
+ """
+ A callback function to report receive the looped back data
+
+ :param data: [looped back data]
+ """
+ print(f'Looped back: {chr(data[0])}')
+
+
+async def loop_back(my_board, loop_back_data):
+ """
+ This function will request that the supplied characters be
+ sent to the board and looped back and printed out to the console.
+
+ :param my_board: a telemetrix_aio instance
+ :param loop_back_data: A list of characters to have looped back
+ """
+ await my_board.start_aio()
+ try:
+ for data in loop_back_data:
+ await my_board.loop_back(data, callback=the_callback)
+ print(f'Sending: {data}')
+ await asyncio.sleep(.3)
+ await my_board.shutdown()
+ except KeyboardInterrupt:
+ my_board.shutdown()
+ sys.exit(0)
+
+# instantiate telemetrix_aio
+char_list = ['A', 'B', 'Z']
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(autostart=False,
+ transport_type=2)
+
+try:
+ # start the main function
+ loop.run_until_complete(loop_back(board, char_list))
+except KeyboardInterrupt:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_scroll_message.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_scroll_message.py
new file mode 100644
index 0000000..dcb5ccb
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_scroll_message.py
@@ -0,0 +1,55 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,f
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+"""
+
+import sys
+import asyncio
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+
+"""
+Scroll the message across the LED screen
+"""
+
+
+async def test_scroll(the_message):
+ await board.start_aio()
+ await board.enable_scroll_message(the_message)
+ await asyncio.sleep(5)
+ await board.disable_scroll_message()
+ await board.shutdown()
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(autostart=False,
+ transport_type=2)
+
+try:
+ # start the main function
+ loop.run_until_complete(test_scroll("Hello World"))
+except KeyboardInterrupt:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
+
+
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_sonar_disable.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_sonar_disable.py
new file mode 100644
index 0000000..fcfd69a
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_sonar_disable.py
@@ -0,0 +1,90 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+import time
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+
+"""
+This program continuously monitors an HC-SR04 Ultrasonic Sensor
+It reports changes to the distance sensed.
+"""
+TRIGGER_PIN = 9
+ECHO_PIN = 10
+
+# indices into callback data
+REPORT_TYPE = 0
+TRIG_PIN = 1
+DISTANCE = 2
+TIME = 3
+
+
+# A callback function to display the distance
+async def the_callback(data):
+ """
+ The callback function to display the change in distance
+ :param data: [report_type = PrivateConstants.SONAR_DISTANCE, trigger pin number, distance, timestamp]
+ """
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[3]))
+ print(f'Sonar Report: Trigger Pin: {data[1]} Distance: {data[2]} Time: {date}')
+
+
+async def sonar(my_board, trigger_pin, echo_pin, callback):
+ """
+ Set the pin mode for a sonar device. Results will appear via the
+ callback.
+
+ :param my_board: a telemetrix_aio instance
+ :param trigger_pin: Arduino pin number
+ :param echo_pin: Arduino pin number
+ :param callback: The callback function
+ """
+
+ await my_board.start_aio()
+ # set the pin mode for the trigger and echo pins
+ await my_board.set_pin_mode_sonar(trigger_pin, echo_pin, callback)
+ # wait forever
+ while True:
+ try:
+ await asyncio.sleep(5)
+ await my_board.sonar_disable()
+ await asyncio.sleep(5)
+ await my_board.sonar_enable()
+
+ except KeyboardInterrupt:
+ await my_board.shutdown()
+ sys.exit(0)
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(autostart=False,
+ transport_type=2)
+
+try:
+ # start the main function
+ loop.run_until_complete(sonar(board, TRIGGER_PIN, ECHO_PIN, the_callback))
+
+except KeyboardInterrupt:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
\ No newline at end of file
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_spi_adxl345_accelerometer.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_spi_adxl345_accelerometer.py
new file mode 100644
index 0000000..97e1c7b
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/BLE/wba_spi_adxl345_accelerometer.py
@@ -0,0 +1,126 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+
+
+"""
+This program reads x, y, and z registers of an ADXL345 using 4 Wire SPI interface
+"""
+
+"""
+Connection scheme:
+CS = pin 10
+SDA = pin 11
+SDO = pin 12
+SCL = pin 13
+
+"""
+
+
+async def the_callback(data):
+ """
+ :param data: [pin_type, chip select pin, device read register, x data pair,
+ y data pair, z data pair, time stamp]
+
+ """
+ report_type = data[0]
+ chip_select_pin = data[1]
+ device_read_register = data[2] & 0x3f # strip off read command bits
+ number_bytes_read = data[3]
+ x_msb = data[4]
+ x_lsb = data[5]
+ y_msb = data[6]
+ y_lsb = data[7]
+ z_msb = data[8]
+ z_lsb = data[9]
+
+ x_data = (x_msb << 8) + x_lsb
+ y_data = (y_msb << 8) + y_lsb
+ z_data = (z_msb << 8) + z_lsb
+
+ # test report type for SPI report
+ if report_type == 13:
+ print(f'SPI Report: CS Pin: {chip_select_pin} SPI Register: '
+ f'{device_read_register} Number Of Bytes Read: {number_bytes_read} x: '
+ f'{x_data} y: {y_data} z: {z_data}')
+ else:
+ print(f'unexpected report type: {report_type}')
+
+
+async def adxl345(my_board):
+ """
+
+ :type my_board: object
+ """
+ await my_board.start_aio()
+ # initialize spi mode for chipselect on pin 10
+ await my_board.set_pin_mode_spi([10])
+ await asyncio.sleep(.3)
+
+ # set the SPI format
+ # spi speed is FPU frequency divided by 4
+ # data order is MSB
+ # mode is MODE3
+ await my_board.spi_set_format(8, 1, 3)
+ await asyncio.sleep(.3)
+
+ # set up power and control register
+ await my_board.spi_write_blocking(10, [45, 0])
+ await asyncio.sleep(.3)
+
+ await my_board.spi_write_blocking(10, [45, 8])
+ await asyncio.sleep(.3)
+
+ # set up data format register for 4 wire spi
+ await my_board.spi_write_blocking(10, [49, 0])
+ await asyncio.sleep(.3)
+
+ # read 6 bytes from the data register
+ # for a multibyte read, we need to OR in a 0x40 into the register value
+ while True:
+ # read 6 bytes from the data register
+ try:
+ await my_board.spi_read_blocking(10, 50 | 0x40, 6, the_callback)
+
+ await asyncio.sleep(.5)
+
+ except (KeyboardInterrupt, RuntimeError):
+ await my_board.shutdown()
+ sys.exit(0)
+
+
+# instantiate telemetrix_aio
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(autostart=False,
+ transport_type=2)
+
+try:
+ # start the main function
+ loop.run_until_complete(adxl345(board))
+except KeyboardInterrupt:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_analog_input.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_analog_input.py
new file mode 100644
index 0000000..ed0b9af
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_analog_input.py
@@ -0,0 +1,100 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+import time
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+
+"""
+This file demonstrates analog input using both callbacks and
+polling. Time stamps are provided in both "cooked" and raw form
+"""
+
+# Set up a pin for analog input and monitor its changes
+ANALOG_PIN = 2 # arduino pin number
+
+# Callback data indices
+CB_PIN_MODE = 0
+CB_PIN = 1
+CB_VALUE = 2
+CB_TIME = 3
+
+
+async def the_callback(data):
+ """
+ A callback function to report data changes.
+
+ :param data: [pin_mode, pin, current_reported_value, timestamp]
+ """
+
+ formatted_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[CB_TIME]))
+ print(f'Analog Call Input Callback: pin={data[CB_PIN]}, '
+ f'Value={data[CB_VALUE]} Time={formatted_time} '
+ f'(Raw Time={data[CB_TIME]})')
+
+
+async def analog_in(my_board, pin):
+ """
+ This function establishes the pin as an
+ analog input. Any changes on this pin will
+ be reported through the call back function.
+
+ Every 5 seconds the last value and time stamp is polled
+ and printed.
+
+ Also, the differential parameter is being used.
+ The callback will only be called when there is
+ difference of 5 or more between the current and
+ last value reported.
+
+ :param my_board: a telemetrix_aio instance
+
+ :param pin: Arduino pin number
+ """
+
+ await my_board.set_pin_mode_analog_input(pin, 5, the_callback)
+
+ # await asyncio.sleep(5)
+ # await my_board.disable_analog_reporting()
+ # await asyncio.sleep(5)
+ # await my_board.enable_analog_reporting()
+
+ # run forever waiting for input changes
+ try:
+ while True:
+ await asyncio.sleep(.001)
+
+ except KeyboardInterrupt:
+ await my_board.shutdown()
+ sys.exit(0)
+
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(transport_address='192.168.2.118')
+
+try:
+ # start the main function
+ loop.run_until_complete(analog_in(board, ANALOG_PIN))
+except (KeyboardInterrupt, RuntimeError) as e:
+ loop.run_until_complete(board.shutdown())
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_blink.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_blink.py
new file mode 100644
index 0000000..4d7302d
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_blink.py
@@ -0,0 +1,69 @@
+"""
+ Copyright (c) 2020 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+import asyncio
+import sys
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+
+"""
+Setup a pin for digital output and output a signal
+and toggle the pin. Do this 4 times.
+"""
+
+# some globals
+DIGITAL_PIN = 13 # arduino pin number
+
+
+async def blink(my_board, pin):
+ """
+ This function will to toggle a digital pin.
+
+ :param my_board: a telemetrix_aio instance
+ :param pin: pin to be controlled
+ """
+
+ # set the pin mode
+ await my_board.set_pin_mode_digital_output(pin)
+
+ # toggle the pin 4 times and exit
+ for x in range(4):
+ print('ON')
+ await my_board.digital_write(pin, 0)
+ await asyncio.sleep(1)
+ print('OFF')
+ await my_board.digital_write(pin, 1)
+ await asyncio.sleep(1)
+
+ await my_board.shutdown()
+
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(
+ transport_address='192.168.2.118')
+
+try:
+ # start the main function
+ loop.run_until_complete(blink(board, DIGITAL_PIN))
+ # asyncio.run(blink(board, DIGITAL_PIN))
+
+except KeyboardInterrupt:
+ loop.run_until_complete(board.shutdown())
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_dht.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_dht.py
new file mode 100644
index 0000000..b1c30bc
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_dht.py
@@ -0,0 +1,109 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+import time
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+
+"""
+This program monitors two DHT22 and two DHT11 sensors.
+"""
+
+
+# indices into callback data for valid data
+# REPORT_TYPE = 0
+# READ_RESULT = 1
+# PIN = 2
+# DHT_TYPE = 3
+# HUMIDITY = 4
+# TEMPERATURE = 5
+# TIME = 6
+
+# indices into callback data for error report
+# REPORT_TYPE = 0
+# READ_RESULT = 1
+# PIN = 2
+# DHT_TYPE = 3
+# TIME = 4
+
+# Arduino Pin Number
+DHT_PIN = 8
+
+
+# A callback function to display the distance
+# noinspection GrazieInspection
+async def the_callback(data):
+ # noinspection GrazieInspection
+ """
+ The callback function to display the change in distance
+ :param data: [report_type = PrivateConstants.DHT, error = 0, pin number,
+ dht_type, humidity, temperature timestamp]
+ if this is an error report:
+ [report_type = PrivateConstants.DHT, error != 0, pin number, dht_type
+ timestamp]
+ """
+ if data[1]:
+ pass # ignore errors
+ # error message
+ # date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[4]))
+ # print(f'DHT Error Report:'
+ # f'Pin: {data[2]} DHT Type: {data[3]} Error: {data[1]} Time: {date}')
+ else:
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[6]))
+ print(f'DHT Valid Data Report:'
+ f'Pin: {data[2]} DHT Type: {data[3]} Humidity: {data[4]} Temperature:'
+ f' {data[5]} Time: {date}')
+
+
+async def dht(my_board):
+ # noinspection GrazieInspection
+ """
+ Set the pin mode for a DHT 22 device. Results will appear via the
+ callback.
+
+ :param my_board: a telemetrix instance
+
+ """
+
+ # set the pin mode for a DHT 11
+ await my_board.set_pin_mode_dht(DHT_PIN, callback=the_callback, dht_type=11)
+
+ # just sit in a loop waiting for the reports to come in
+ while True:
+ try:
+ await asyncio.sleep(.001)
+ except KeyboardInterrupt:
+ my_board.shutdown()
+ sys.exit(0)
+
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(transport_address='192.168.2.118')
+try:
+ loop.run_until_complete(dht(board))
+except (KeyboardInterrupt, RuntimeError):
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_digital_input.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_digital_input.py
new file mode 100644
index 0000000..0dbd0a5
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_digital_input.py
@@ -0,0 +1,110 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,f
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ DHT support courtesy of Martyn Wheeler
+ Based on the DHTNew library - https://github.com/RobTillaart/DHTNew
+"""
+
+import asyncio
+import sys
+import time
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+
+"""
+Monitor a digital input pin
+"""
+
+"""
+Setup a pin for digital input and monitor its changes
+"""
+
+# Set up a pin for analog input and monitor its changes
+DIGITAL_PIN = 12 # arduino pin number
+
+# Callback data indices
+CB_PIN_MODE = 0
+CB_PIN = 1
+CB_VALUE = 2
+CB_TIME = 3
+
+# variable to hold the last time a button state changed
+debounce_time = time.time()
+
+
+async def the_callback(data):
+ """
+ A callback function to report data changes.
+ This will print the pin number, its reported value and
+ the date and time when the change occurred
+
+ :param data: [pin_mode, pin, current reported value, timestamp]
+ """
+ global debounce_time
+
+ # if the time from the last event change is > .2 seconds, the input is debounced
+ if data[CB_TIME] - debounce_time > .2:
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[CB_TIME]))
+ print(f'Pin: {data[CB_PIN]} Value: {data[CB_VALUE]} Time Stamp: {date}')
+ debounce_time = data[CB_TIME]
+
+
+async def digital_in(my_board, pin):
+ """
+ This function establishes the pin as a
+ digital input. Any changes on this pin will
+ be reported through the call back function.
+
+ :param my_board: a telemetrix_aio instance
+ :param pin: Arduino pin number
+ """
+
+ # set the pin mode
+ await my_board.set_pin_mode_digital_input(pin, the_callback)
+
+ # uncomment to try out report enable/disable
+ # await asyncio.sleep(1)
+ # await my_board.disable_all_reporting()
+ # await asyncio.sleep(4)
+ # await my_board.enable_digital_reporting(12)
+
+ # await asyncio.sleep(3)
+ # await my_board.enable_digital_reporting(pin)
+ # await asyncio.sleep(1)
+
+ while True:
+ try:
+ await asyncio.sleep(.001)
+ except KeyboardInterrupt:
+ await board.shutdown()
+ sys.exit(0)
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(transport_address='192.168.2.118')
+
+try:
+ # start the main function
+ loop.run_until_complete(digital_in(board, 12))
+except (KeyboardInterrupt, RuntimeError) as e:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_digital_input_pullup.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_digital_input_pullup.py
new file mode 100644
index 0000000..9df4463
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_digital_input_pullup.py
@@ -0,0 +1,91 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+import time
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+
+"""
+Setup a digital pin for input pullup and monitor its changes.
+"""
+
+# some globals
+DIGITAL_PIN = 12 # arduino pin number
+KILL_TIME = 5 # sleep time to keep forever loop open
+
+# Callback data indices
+# Callback data indices
+CB_PIN_MODE = 0
+CB_PIN = 1
+CB_VALUE = 2
+CB_TIME = 3
+
+
+# Set up a pin for digital pin input and monitor its changes
+
+async def the_callback(data):
+ """
+ A callback function to report data changes.
+ This will print the pin number, its reported value and
+ the date and time when the change occurred
+
+ :param data: [pin, current reported value, pin_mode, timestamp]
+ """
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[CB_TIME]))
+ print(f'Pin: {data[CB_PIN]} Value: {data[CB_VALUE]} Time Stamp: {date}')
+
+
+async def digital_in_pullup(my_board, pin):
+ """
+ This function establishes the pin as a
+ digital input. Any changes on this pin will
+ be reported through the call back function.
+
+ :param my_board: a telemetrix_aio instance
+ :param pin: Arduino pin number
+ """
+
+ # start monitoring the pin by setting its mode
+ await my_board.set_pin_mode_digital_input_pullup(pin, the_callback)
+
+ # get pin changes forever
+ while True:
+ try:
+ await asyncio.sleep(KILL_TIME)
+ except KeyboardInterrupt:
+ await board.shutdown()
+ sys.exit(0)
+
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(transport_address='192.168.2.118')
+
+try:
+ # start the main function
+ loop.run_until_complete(digital_in_pullup(board, 12))
+except (KeyboardInterrupt, RuntimeError) as e:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_fade.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_fade.py
new file mode 100644
index 0000000..882ea99
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_fade.py
@@ -0,0 +1,73 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,f
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ DHT support courtesy of Martyn Wheeler
+ Based on the DHTNew library - https://github.com/RobTillaart/DHTNew
+"""
+
+import sys
+import time
+import asyncio
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+
+"""
+Setup a pin for output and fade its intensity
+"""
+
+# some globals
+# make sure to select a PWM pin
+DIGITAL_PIN = 13
+
+
+async def fade(the_board, pin):
+ # Set the DIGITAL_PIN as an output pin
+ await the_board.set_pin_mode_analog_output(pin)
+
+ # When hitting control-c to end the program
+ # in this loop, we are likely to get a KeyboardInterrupt
+ # exception. Catch the exception and exit gracefully.
+
+ try:
+ print('Fading up...')
+ for i in range(255):
+ await the_board.analog_write(DIGITAL_PIN, i)
+ await asyncio.sleep(.005)
+ print('Fading down...')
+ for i in range(255, -1, -1):
+ await the_board.analog_write(DIGITAL_PIN, i)
+ await asyncio.sleep(.005)
+ await the_board.shutdown()
+ except KeyboardInterrupt:
+ the_board.shutdown()
+ sys.exit(0)
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(transport_address='192.168.2.118')
+
+try:
+ # start the main function
+ loop.run_until_complete(fade(board, DIGITAL_PIN))
+except KeyboardInterrupt:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_hc-sr04_distance_sensor.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_hc-sr04_distance_sensor.py
new file mode 100644
index 0000000..175c044
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_hc-sr04_distance_sensor.py
@@ -0,0 +1,84 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+import time
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+
+"""
+This program continuously monitors an HC-SR04 Ultrasonic Sensor
+It reports changes to the distance sensed.
+"""
+TRIGGER_PIN = 9
+ECHO_PIN = 10
+
+# indices into callback data
+REPORT_TYPE = 0
+TRIG_PIN = 1
+DISTANCE = 2
+TIME = 3
+
+
+# A callback function to display the distance
+async def the_callback(data):
+ """
+ The callback function to display the change in distance
+ :param data: [report_type = PrivateConstants.SONAR_DISTANCE, trigger pin number, distance, timestamp]
+ """
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[3]))
+ print(f'Sonar Report: Trigger Pin: {data[1]} Distance: {data[2]} Time: {date}')
+
+
+async def sonar(my_board, trigger_pin, echo_pin, callback):
+ """
+ Set the pin mode for a sonar device. Results will appear via the
+ callback.
+
+ :param my_board: a telemetrix_aio instance
+ :param trigger_pin: Arduino pin number
+ :param echo_pin: Arduino pin number
+ :param callback: The callback function
+ """
+
+ # set the pin mode for the trigger and echo pins
+ await my_board.set_pin_mode_sonar(trigger_pin, echo_pin, callback)
+ # wait forever
+ while True:
+ try:
+ await asyncio.sleep(.1)
+ except KeyboardInterrupt:
+ await my_board.shutdown()
+ sys.exit(0)
+
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(transport_address='192.168.2.118')
+
+try:
+ # start the main function
+ loop.run_until_complete(sonar(board, TRIGGER_PIN, ECHO_PIN, the_callback))
+except KeyboardInterrupt:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_i2c_adxl345_accelerometer.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_i2c_adxl345_accelerometer.py
new file mode 100644
index 0000000..d843583
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_i2c_adxl345_accelerometer.py
@@ -0,0 +1,104 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+
+"""
+This example sets up and control an ADXL345 i2c accelerometer.
+It will continuously print data the raw xyz data from the device.
+"""
+
+
+# the call back function to print the adxl345 data
+async def the_callback(data):
+ """
+
+ :param data: [pin_type, Device address, device read register, x data pair, y data pair, z data pair]
+ :return:
+ """
+ report_type = data[0]
+ number_bytes_read = data[2]
+ i2c_device_address = data[3]
+ i2c_register = data[4]
+ x_msb = data[5]
+ x_lsb = data[6]
+ y_msb = data[7]
+ y_lsb = data[8]
+ z_msb = data[9]
+ z_lsb = data[10]
+
+ x_data = int((x_msb << 8) + x_lsb)
+ y_data = int((y_msb << 8) + y_lsb)
+ z_data = int((z_msb << 8) + z_lsb)
+
+ # test report type for SPI report
+ if report_type == 10:
+ print(f'i2c Report: i2c device address: {i2c_device_address} i2c '
+ f'Register: '
+ f'{i2c_register} Number Of Bytes Read: {number_bytes_read} x: '
+ f'{x_data} y: {y_data} z: {z_data}')
+ else:
+ print(f'unexpected report type: {report_type}')
+
+
+async def adxl345(my_board):
+ # setup adxl345
+ # device address = 83
+ await my_board.set_pin_mode_i2c()
+
+ # set up power and control register
+ await my_board.i2c_write(83, [45, 0])
+ await asyncio.sleep(.1)
+ await my_board.i2c_write(83, [45, 8])
+ await asyncio.sleep(.1)
+
+ # set up the data format register
+ await my_board.i2c_write(83, [49, 8])
+ await asyncio.sleep(.1)
+ await my_board.i2c_write(83, [49, 3])
+ await asyncio.sleep(.1)
+
+ # read_count = 20
+ while True:
+ # read 6 bytes from the data register
+ try:
+ await my_board.i2c_read(83, 50, 6, the_callback)
+ await asyncio.sleep(.3)
+
+ except (KeyboardInterrupt, RuntimeError):
+ await my_board.shutdown()
+ sys.exit(0)
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(transport_address='192.168.2.118')
+
+try:
+ # start the main function
+ loop.run_until_complete(adxl345(board))
+except KeyboardInterrupt:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
+
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_loop_back.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_loop_back.py
new file mode 100644
index 0000000..baeb038
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_loop_back.py
@@ -0,0 +1,78 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+
+"""
+Loopback some data to assure that data can be sent and received between
+the Telemetrix client and arduino-telemetrix server.
+"""
+
+
+async def the_callback(data):
+ """
+ A callback function to report receive the looped back data
+
+ :param data: [looped back data]
+ """
+ print(f'Looped back: {chr(data[0])}')
+
+
+async def loop_back(my_board, loop_back_data):
+ """
+ This function will request that the supplied characters be
+ sent to the board and looped back and printed out to the console.
+
+ :param my_board: a telemetrix_aio instance
+ :param loop_back_data: A list of characters to have looped back
+ """
+ # await my_board.start_aio()
+ try:
+ for data in loop_back_data:
+ await my_board.loop_back(data, callback=the_callback)
+ print(f'Sending: {data}')
+ await asyncio.sleep(1)
+ except KeyboardInterrupt:
+ my_board.shutdown()
+ sys.exit(0)
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(
+ transport_address='192.168.2.118')
+char_list = ['A', 'B', 'Z']
+
+try:
+ # start the main function
+ loop.run_until_complete(loop_back(board, char_list))
+
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+except KeyboardInterrupt:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_scroll_message.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_scroll_message.py
new file mode 100644
index 0000000..0f38aa7
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_scroll_message.py
@@ -0,0 +1,54 @@
+"""
+ Copyright (c) 2020 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,f
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+"""
+
+import sys
+import asyncio
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+
+"""
+Setup a pin for digital output
+and toggle the pin 5 times.
+"""
+
+# some globals
+message = 'Hello World'
+
+# Create a Telemetrix instance.
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(transport_address='192.168.2.118')
+
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+
+async def test_scroll(the_message):
+ try:
+ await board.enable_scroll_message(the_message)
+ await asyncio.sleep(5)
+ await board.disable_scroll_message()
+ await asyncio.sleep(5)
+ await board.shutdown()
+ sys.exit(0)
+ except KeyboardInterrupt:
+ await board.shutdown()
+
+try:
+ loop.run_until_complete(test_scroll(message))
+except KeyboardInterrupt:
+ loop.run_until_complete(board.shutdown())
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_servo.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_servo.py
new file mode 100644
index 0000000..2b2ead2
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_servo.py
@@ -0,0 +1,66 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+
+"""
+This example will set a servo to 0, 90 and 180 degree
+positions.
+"""
+
+
+async def servo(my_board, pin):
+ """
+ Set a pin to servo mode and then adjust
+ its position.
+
+ :param my_board: telemetrix_aio instance
+ :param pin: pin to be controlled
+ """
+
+ # set the pin mode
+ await my_board.set_pin_mode_servo(pin)
+
+ await asyncio.sleep(1)
+
+ await my_board.servo_write(pin, 0)
+ await asyncio.sleep(1)
+ await my_board.servo_write(pin, 90)
+ await asyncio.sleep(1)
+ await my_board.servo_write(pin, 180)
+ await my_board.servo_detach(pin)
+
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(transport_address='192.168.2.118')
+try:
+ loop.run_until_complete(servo(board, 5))
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
+except KeyboardInterrupt:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_sonar_disable.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_sonar_disable.py
new file mode 100644
index 0000000..9286f38
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_sonar_disable.py
@@ -0,0 +1,85 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+import time
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+
+"""
+This program continuously monitors an HC-SR04 Ultrasonic Sensor
+It reports changes to the distance sensed.
+"""
+TRIGGER_PIN = 9
+ECHO_PIN = 10
+
+# indices into callback data
+REPORT_TYPE = 0
+TRIG_PIN = 1
+DISTANCE = 2
+TIME = 3
+
+
+# A callback function to display the distance
+async def the_callback(data):
+ """
+ The callback function to display the change in distance
+ :param data: [report_type = PrivateConstants.SONAR_DISTANCE, trigger pin number, distance, timestamp]
+ """
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[3]))
+ print(f'Sonar Report: Trigger Pin: {data[1]} Distance: {data[2]} Time: {date}')
+
+
+async def sonar(my_board, trigger_pin, echo_pin, callback):
+ """
+ Set the pin mode for a sonar device. Results will appear via the
+ callback.
+
+ :param my_board: a telemetrix_aio instance
+ :param trigger_pin: Arduino pin number
+ :param echo_pin: Arduino pin number
+ :param callback: The callback function
+ """
+
+ # set the pin mode for the trigger and echo pins
+ await my_board.set_pin_mode_sonar(trigger_pin, echo_pin, callback)
+ # wait forever
+ while True:
+ try:
+ await asyncio.sleep(5)
+ await my_board.sonar_disable()
+ await asyncio.sleep(5)
+ await my_board.sonar_enable()
+
+ except KeyboardInterrupt:
+ await my_board.shutdown()
+ sys.exit(0)
+
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(transport_address='192.168.2.118')
+
+try:
+ # start the main function
+ loop.run_until_complete(sonar(board, TRIGGER_PIN, ECHO_PIN, the_callback))
+except KeyboardInterrupt:
+ loop.run_until_complete(board.shutdown())
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_spi_adxl345_accelerometer.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_spi_adxl345_accelerometer.py
new file mode 100644
index 0000000..3250e60
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_spi_adxl345_accelerometer.py
@@ -0,0 +1,122 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+
+
+"""
+This program reads x, y, and z registers of an ADXL345 using 4 Wire SPI interface
+"""
+
+"""
+Connection scheme:
+CS = pin 10
+SDA = pin 11
+SDO = pin 12
+SCL = pin 13
+
+"""
+
+
+async def the_callback(data):
+ """
+ :param data: [pin_type, chip select pin, device read register, x data pair,
+ y data pair, z data pair, time stamp]
+
+ """
+ report_type = data[0]
+ chip_select_pin = data[1]
+ device_read_register = data[2] & 0x3f # strip off read command bits
+ number_bytes_read = data[3]
+ x_msb = data[4]
+ x_lsb = data[5]
+ y_msb = data[6]
+ y_lsb = data[7]
+ z_msb = data[8]
+ z_lsb = data[9]
+
+ x_data = (x_msb << 8) + x_lsb
+ y_data = (y_msb << 8) + y_lsb
+ z_data = (z_msb << 8) + z_lsb
+
+ # test report type for SPI report
+ if report_type == 13:
+ print(f'SPI Report: CS Pin: {chip_select_pin} SPI Register: '
+ f'{device_read_register} Number Of Bytes Read: {number_bytes_read} x: '
+ f'{x_data} y: {y_data} z: {z_data}')
+ else:
+ print(f'unexpected report type: {report_type}')
+
+
+async def adxl345(my_board):
+ """
+
+ :type my_board: object
+ """
+ # initialize spi mode for chipselect on pin 10
+ await my_board.set_pin_mode_spi([10])
+
+ # set the SPI format
+ # spi speed is FPU frequency divided by 4
+ # data order is MSB
+ # mode is MODE3
+ await my_board.spi_set_format(8, 1, 3)
+ await asyncio.sleep(.3)
+
+ # set up power and control register
+ await my_board.spi_write_blocking(10, [45, 0])
+ await asyncio.sleep(.3)
+
+ await my_board.spi_write_blocking(10, [45, 8])
+ await asyncio.sleep(.3)
+
+ # set up data format register for 4 wire spi
+ await my_board.spi_write_blocking(10, [49, 0])
+ await asyncio.sleep(.3)
+
+ # read 6 bytes from the data register
+ # for a multibyte read, we need to OR in a 0x40 into the register value
+ while True:
+ # read 6 bytes from the data register
+ try:
+ await my_board.spi_read_blocking(10, 50 | 0x40, 6, the_callback)
+
+ await asyncio.sleep(.5)
+
+ except (KeyboardInterrupt, RuntimeError):
+ await my_board.shutdown()
+ sys.exit(0)
+
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(transport_address='192.168.2.118')
+
+try:
+ # start the main function
+ loop.run_until_complete(adxl345(board))
+except KeyboardInterrupt:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_using_class.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_using_class.py
new file mode 100644
index 0000000..e78d658
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/WIFI/wwa_using_class.py
@@ -0,0 +1,89 @@
+import asyncio
+import sys
+import time
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+
+"""
+Establishing a digital and analog input pin and monitoring their values.
+"""
+
+
+class PinsWithinClass:
+ def __init__(self, board, d_pin, a_pin):
+ """
+
+ :param board: a telemetrix aio instance
+ :param d_pin: digital input pin number
+ :param a_pin: analog input pin number
+
+ """
+ self.board = board
+ self.d_pin = d_pin
+ self.a_pin = a_pin
+
+ self.last_analog_value = 0
+
+ async def run_it(self):
+ """
+ Initialize pin modes with the callbacks
+ """
+
+ await self.board.set_pin_mode_digital_input(self.d_pin, callback=self.callback)
+ await self.board.set_pin_mode_analog_input(self.a_pin,
+ differential=3, callback=self.callback)
+
+ async def callback(self, data):
+ """
+
+ :param data: data[0] = report type 2 = digital input
+ report type 3 = analog input
+ data[1] = pin number
+ data[2] = reported value
+ """
+ if data[0] == 2:
+ if data[2] == self.last_analog_value:
+ pass
+ else:
+ self.last_analog_value = data[2]
+ print(f'digital input pin {data[1]} reports a value of {data[2]}')
+ elif data[0] == 3:
+ print(f'analog input pin {data[1]} reports a value of {data[2]}')
+
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+the_board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(
+ transport_address='192.168.2.118')
+
+
+async def monitor(my_board, digital_pin, analog_pin):
+ """
+ Set the pin modes for the pins
+
+ :param my_board: telemetrix aio instance
+ :param digital_pin: Arduino digital input pin number
+ :param analog_pin: Arduino analog input pin number
+
+ """
+
+ pwc = PinsWithinClass(my_board, digital_pin, analog_pin)
+ await pwc.run_it()
+ # wait forever
+ while True:
+ try:
+ await asyncio.sleep(.00001)
+ except KeyboardInterrupt:
+ await my_board.shutdown()
+ sys.exit(0)
+
+
+try:
+ # start the main function
+ loop.run_until_complete(monitor(the_board, 12, 2))
+except KeyboardInterrupt:
+ loop.run_until_complete(the_board.shutdown())
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_analog_input.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_analog_input.py
new file mode 100644
index 0000000..f0c9250
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_analog_input.py
@@ -0,0 +1,100 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+import time
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+
+"""
+This file demonstrates analog input using both callbacks and
+polling. Time stamps are provided in both "cooked" and raw form
+"""
+
+# Set up a pin for analog input and monitor its changes
+ANALOG_PIN = 2 # arduino pin number
+
+# Callback data indices
+CB_PIN_MODE = 0
+CB_PIN = 1
+CB_VALUE = 2
+CB_TIME = 3
+
+
+async def the_callback(data):
+ """
+ A callback function to report data changes.
+
+ :param data: [pin_mode, pin, current_reported_value, timestamp]
+ """
+
+ formatted_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[CB_TIME]))
+ print(f'Analog Call Input Callback: pin={data[CB_PIN]}, '
+ f'Value={data[CB_VALUE]} Time={formatted_time} '
+ f'(Raw Time={data[CB_TIME]})')
+
+
+async def analog_in(my_board, pin):
+ """
+ This function establishes the pin as an
+ analog input. Any changes on this pin will
+ be reported through the call back function.
+
+ Every 5 seconds the last value and time stamp is polled
+ and printed.
+
+ Also, the differential parameter is being used.
+ The callback will only be called when there is
+ difference of 5 or more between the current and
+ last value reported.
+
+ :param my_board: a telemetrix_aio instance
+
+ :param pin: Arduino pin number
+ """
+ await my_board.set_pin_mode_analog_input(pin, 5, the_callback)
+
+ # await asyncio.sleep(5)
+ # await my_board.disable_analog_reporting()
+ # await asyncio.sleep(5)
+ # await my_board.enable_analog_reporting()
+
+ # run forever waiting for input changes
+ try:
+ while True:
+ await asyncio.sleep(.001)
+
+ except KeyboardInterrupt:
+ await my_board.shutdown()
+ sys.exit(0)
+
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(transport_type=1)
+
+try:
+ # start the main function
+ loop.run_until_complete(analog_in(board, ANALOG_PIN))
+ loop.run_until_complete(board.shutdown())
+except (KeyboardInterrupt, RuntimeError) as e:
+ loop.run_until_complete(board.shutdown())
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_blink.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_blink.py
new file mode 100644
index 0000000..671578c
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_blink.py
@@ -0,0 +1,68 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+import asyncio
+import sys
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+
+"""
+Setup a pin for digital output and output a signal
+and toggle the pin. Do this 4 times.
+"""
+
+# some globals
+DIGITAL_PIN = 13 # arduino pin number
+
+
+async def blink(my_board, pin):
+ """
+ This function will to toggle a digital pin.
+
+ :param my_board: a telemetrix_aio instance
+ :param pin: pin to be controlled
+ """
+
+ # set the pin mode
+ await my_board.set_pin_mode_digital_output(pin)
+
+ # toggle the pin 4 times and exit
+ for x in range(4):
+ print('ON')
+ await my_board.digital_write(pin, 1)
+ await asyncio.sleep(1)
+ print('OFF')
+ await my_board.digital_write(pin, 0)
+ await asyncio.sleep(1)
+
+ await my_board.shutdown()
+
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(transport_type=1)
+try:
+ # start the main function
+ loop.run_until_complete(blink(board, DIGITAL_PIN))
+except KeyboardInterrupt:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_dht.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_dht.py
new file mode 100644
index 0000000..1649a62
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_dht.py
@@ -0,0 +1,109 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+import time
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+
+"""
+This program monitors two DHT22 and two DHT11 sensors.
+"""
+
+
+# indices into callback data for valid data
+# REPORT_TYPE = 0
+# READ_RESULT = 1
+# PIN = 2
+# DHT_TYPE = 3
+# HUMIDITY = 4
+# TEMPERATURE = 5
+# TIME = 6
+
+# indices into callback data for error report
+# REPORT_TYPE = 0
+# READ_RESULT = 1
+# PIN = 2
+# DHT_TYPE = 3
+# TIME = 4
+
+# Arduino Pin Number
+DHT_PIN = 8
+
+
+# A callback function to display the distance
+# noinspection GrazieInspection
+async def the_callback(data):
+ # noinspection GrazieInspection
+ """
+ The callback function to display the change in distance
+ :param data: [report_type = PrivateConstants.DHT, error = 0, pin number,
+ dht_type, humidity, temperature timestamp]
+ if this is an error report:
+ [report_type = PrivateConstants.DHT, error != 0, pin number, dht_type
+ timestamp]
+ """
+ if data[1]:
+ pass # ignore error messages
+ # error message
+ # date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[4]))
+ # print(f'DHT Error Report:'
+ # f'Pin: {data[2]} DHT Type: {data[3]} Error: {data[1]} Time: {date}')
+ else:
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[6]))
+ print(f'DHT Valid Data Report:'
+ f'Pin: {data[2]} DHT Type: {data[3]} Humidity: {data[4]} Temperature:'
+ f' {data[5]} Time: {date}')
+
+
+async def dht(my_board):
+ # noinspection GrazieInspection
+ """
+ Set the pin mode for a DHT 22 device. Results will appear via the
+ callback.
+
+ :param my_board: a telemetrix instance
+
+ """
+
+ # set the pin mode for a DHT 22
+ await my_board.set_pin_mode_dht(DHT_PIN, callback=the_callback, dht_type=11)
+
+ # just sit in a loop waiting for the reports to come in
+ while True:
+ try:
+ await asyncio.sleep(.1)
+ except KeyboardInterrupt:
+ my_board.shutdown()
+ sys.exit(0)
+
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(transport_type=1)
+try:
+ loop.run_until_complete(dht(board))
+except (KeyboardInterrupt, RuntimeError):
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_digital_input.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_digital_input.py
new file mode 100644
index 0000000..61ad947
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_digital_input.py
@@ -0,0 +1,107 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,f
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ DHT support courtesy of Martyn Wheeler
+ Based on the DHTNew library - https://github.com/RobTillaart/DHTNew
+"""
+
+import asyncio
+import sys
+import time
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+
+"""
+Monitor a digital input pin
+"""
+
+"""
+Setup a pin for digital input and monitor its changes
+"""
+
+# Set up a pin for analog input and monitor its changes
+DIGITAL_PIN = 12 # arduino pin number
+
+# Callback data indices
+CB_PIN_MODE = 0
+CB_PIN = 1
+CB_VALUE = 2
+CB_TIME = 3
+
+
+async def the_callback(data):
+ """
+ A callback function to report data changes.
+ This will print the pin number, its reported value and
+ the date and time when the change occurred
+
+ :param data: [pin_mode, pin, current reported value, timestamp]
+ """
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[CB_TIME]))
+ print(f'Pin: {data[CB_PIN]} Value: {data[CB_VALUE]} Time Stamp: {date}')
+
+
+async def digital_in(my_board, pin):
+ """
+ This function establishes the pin as a
+ digital input. Any changes on this pin will
+ be reported through the call back function.
+
+ :param my_board: a telemetrix_aio instance
+ :param pin: Arduino pin number
+ """
+
+ # set the pin mode
+ await my_board.set_pin_mode_digital_input(pin, the_callback)
+
+ # uncomment to try out report enable/disable
+ # await asyncio.sleep(1)
+ # await my_board.disable_all_reporting()
+ # await asyncio.sleep(4)
+ # await my_board.enable_digital_reporting(12)
+
+ # await asyncio.sleep(3)
+ # await my_board.enable_digital_reporting(pin)
+ # await asyncio.sleep(1)
+
+ while True:
+ try:
+ await asyncio.sleep(.001)
+ except KeyboardInterrupt:
+ await board.shutdown()
+ sys.exit(0)
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(transport_type=1)
+
+try:
+ # start the main function
+ loop.run_until_complete(digital_in(board, 12))
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
+except (KeyboardInterrupt, RuntimeError) as e:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_digital_input_pullup.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_digital_input_pullup.py
new file mode 100644
index 0000000..8f35297
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_digital_input_pullup.py
@@ -0,0 +1,91 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+import time
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+
+"""
+Setup a digital pin for input pullup and monitor its changes.
+"""
+
+# some globals
+DIGITAL_PIN = 12 # arduino pin number
+KILL_TIME = 5 # sleep time to keep forever loop open
+
+# Callback data indices
+# Callback data indices
+CB_PIN_MODE = 0
+CB_PIN = 1
+CB_VALUE = 2
+CB_TIME = 3
+
+
+# Set up a pin for digital pin input and monitor its changes
+
+async def the_callback(data):
+ """
+ A callback function to report data changes.
+ This will print the pin number, its reported value and
+ the date and time when the change occurred
+
+ :param data: [pin, current reported value, pin_mode, timestamp]
+ """
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[CB_TIME]))
+ print(f'Pin: {data[CB_PIN]} Value: {data[CB_VALUE]} Time Stamp: {date}')
+
+
+async def digital_in_pullup(my_board, pin):
+ """
+ This function establishes the pin as a
+ digital input. Any changes on this pin will
+ be reported through the call back function.
+
+ :param my_board: a telemetrix_aio instance
+ :param pin: Arduino pin number
+ """
+
+ # start monitoring the pin by setting its mode
+ await my_board.set_pin_mode_digital_input_pullup(pin, the_callback)
+
+ # get pin changes forever
+ while True:
+ try:
+ await asyncio.sleep(KILL_TIME)
+ except KeyboardInterrupt:
+ await board.shutdown()
+ sys.exit(0)
+
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(transport_type=1)
+
+try:
+ # start the main function
+ loop.run_until_complete(digital_in_pullup(board, 12))
+except (KeyboardInterrupt, RuntimeError) as e:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_fade.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_fade.py
new file mode 100644
index 0000000..5975e26
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_fade.py
@@ -0,0 +1,71 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,f
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ DHT support courtesy of Martyn Wheeler
+ Based on the DHTNew library - https://github.com/RobTillaart/DHTNew
+"""
+
+import sys
+import time
+import asyncio
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+"""
+Setup a pin for output and fade its intensity
+"""
+
+# some globals
+# make sure to select a PWM pin
+DIGITAL_PIN = 13
+
+
+async def fade(the_board, pin):
+ # Set the DIGITAL_PIN as an output pin
+ await the_board.set_pin_mode_analog_output(pin)
+
+ # When hitting control-c to end the program
+ # in this loop, we are likely to get a KeyboardInterrupt
+ # exception. Catch the exception and exit gracefully.
+
+ try:
+ print('Fading up...')
+ for i in range(255):
+ await the_board.analog_write(DIGITAL_PIN, i)
+ await asyncio.sleep(.005)
+ print('Fading down...')
+ for i in range(255, -1, -1):
+ await the_board.analog_write(DIGITAL_PIN, i)
+ await asyncio.sleep(.005)
+ except KeyboardInterrupt:
+ the_board.shutdown()
+ sys.exit(0)
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(transport_type=1)
+
+try:
+ # start the main function
+ loop.run_until_complete(fade(board, DIGITAL_PIN))
+except KeyboardInterrupt:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_hc-sr04_distance_sensor.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_hc-sr04_distance_sensor.py
new file mode 100644
index 0000000..54b861d
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_hc-sr04_distance_sensor.py
@@ -0,0 +1,84 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+import time
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+
+"""
+This program continuously monitors an HC-SR04 Ultrasonic Sensor
+It reports changes to the distance sensed.
+"""
+TRIGGER_PIN = 9
+ECHO_PIN = 10
+
+# indices into callback data
+REPORT_TYPE = 0
+TRIG_PIN = 1
+DISTANCE = 2
+TIME = 3
+
+
+# A callback function to display the distance
+async def the_callback(data):
+ """
+ The callback function to display the change in distance
+ :param data: [report_type = PrivateConstants.SONAR_DISTANCE, trigger pin number, distance, timestamp]
+ """
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[3]))
+ print(f'Sonar Report: Trigger Pin: {data[1]} Distance: {data[2]} Time: {date}')
+
+
+async def sonar(my_board, trigger_pin, echo_pin, callback):
+ """
+ Set the pin mode for a sonar device. Results will appear via the
+ callback.
+
+ :param my_board: a telemetrix_aio instance
+ :param trigger_pin: Arduino pin number
+ :param echo_pin: Arduino pin number
+ :param callback: The callback function
+ """
+
+ # set the pin mode for the trigger and echo pins
+ await my_board.set_pin_mode_sonar(trigger_pin, echo_pin, callback)
+ # wait forever
+ while True:
+ try:
+ await asyncio.sleep(.1)
+ except KeyboardInterrupt:
+ await my_board.shutdown()
+ sys.exit(0)
+
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(transport_type=1)
+
+try:
+ # start the main function
+ loop.run_until_complete(sonar(board, TRIGGER_PIN, ECHO_PIN, the_callback))
+except KeyboardInterrupt:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_i2c_adxl345_accelerometer.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_i2c_adxl345_accelerometer.py
new file mode 100644
index 0000000..c62c20a
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_i2c_adxl345_accelerometer.py
@@ -0,0 +1,104 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+
+"""
+This example sets up and control an ADXL345 i2c accelerometer.
+It will continuously print data the raw xyz data from the device.
+"""
+
+
+# the call back function to print the adxl345 data
+async def the_callback(data):
+ """
+
+ :param data: [pin_type, Device address, device read register, x data pair, y data pair, z data pair]
+ :return:
+ """
+ report_type = data[0]
+ number_bytes_read = data[2]
+ i2c_device_address = data[3]
+ i2c_register = data[4]
+ x_msb = data[5]
+ x_lsb = data[6]
+ y_msb = data[7]
+ y_lsb = data[8]
+ z_msb = data[9]
+ z_lsb = data[10]
+
+ x_data = (x_msb << 8) + x_lsb
+ y_data = (y_msb << 8) + y_lsb
+ z_data = (z_msb << 8) + z_lsb
+
+ # test report type for SPI report
+ if report_type == 10:
+ print(f'i2c Report: i2c device address: {i2c_device_address} i2c '
+ f'Register: '
+ f'{i2c_register} Number Of Bytes Read: {number_bytes_read} x: '
+ f'{x_data} y: {y_data} z: {z_data}')
+ else:
+ print(f'unexpected report type: {report_type}')
+
+
+async def adxl345(my_board):
+ # setup adxl345
+ # device address = 83
+ await my_board.set_pin_mode_i2c()
+
+ # set up power and control register
+ await my_board.i2c_write(83, [45, 0])
+ await asyncio.sleep(.1)
+ await my_board.i2c_write(83, [45, 8])
+ await asyncio.sleep(.1)
+
+ # set up the data format register
+ await my_board.i2c_write(83, [49, 8])
+ await asyncio.sleep(.1)
+ await my_board.i2c_write(83, [49, 3])
+ await asyncio.sleep(.1)
+
+ # read_count = 20
+ while True:
+ # read 6 bytes from the data register
+ try:
+ await my_board.i2c_read(83, 50, 6, the_callback)
+ await asyncio.sleep(.1)
+
+ except (KeyboardInterrupt, RuntimeError):
+ await my_board.shutdown()
+ sys.exit(0)
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(transport_type=1)
+
+try:
+ # start the main function
+ loop.run_until_complete(adxl345(board))
+except KeyboardInterrupt:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
+
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_loop_back.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_loop_back.py
new file mode 100644
index 0000000..e52d8d1
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_loop_back.py
@@ -0,0 +1,74 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+"""
+Loopback some data to assure that data can be sent and received between
+the Telemetrix client and arduino-telemetrix server.
+"""
+
+
+async def the_callback(data):
+ """
+ A callback function to report receive the looped back data
+
+ :param data: [looped back data]
+ """
+ print(f'Looped back: {chr(data[0])}')
+
+
+async def loop_back(my_board, loop_back_data):
+ """
+ This function will request that the supplied characters be
+ sent to the board and looped back and printed out to the console.
+
+ :param my_board: a telemetrix_aio instance
+ :param loop_back_data: A list of characters to have looped back
+ """
+ try:
+ for data in loop_back_data:
+ await my_board.loop_back(data, callback=the_callback)
+ print(f'Sending: {data}')
+ await asyncio.sleep(.1)
+ await asyncio.sleep(1)
+ await my_board.shutdown()
+ except KeyboardInterrupt:
+ my_board.shutdown()
+ sys.exit(0)
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(transport_type=1)
+char_list = ['A', 'B', 'Z']
+try:
+ # start the main function
+ loop.run_until_complete(loop_back(board, char_list))
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+except KeyboardInterrupt:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_scroll_message.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_scroll_message.py
new file mode 100644
index 0000000..e03423d
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_scroll_message.py
@@ -0,0 +1,51 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,f
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+"""
+
+import sys
+import asyncio
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+
+"""
+Setup a pin for digital output
+and toggle the pin 5 times.
+"""
+
+# some globals
+message = 'Hello World'
+
+# Create a Telemetrix instance.
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(transport_type=1)
+
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+
+async def test_scroll(the_message):
+ await board.enable_scroll_message(the_message)
+ await asyncio.sleep(5)
+ await board.disable_scroll_message()
+ await asyncio.sleep(5)
+ await board.shutdown()
+ sys.exit(0)
+
+try:
+ loop.run_until_complete(test_scroll(message))
+except KeyboardInterrupt:
+ loop.run_until_complete(board.shutdown())
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_servo.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_servo.py
new file mode 100644
index 0000000..04b35d6
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_servo.py
@@ -0,0 +1,66 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+
+"""
+This example will set a servo to 0, 90 and 180 degree
+positions.
+"""
+
+
+async def servo(my_board, pin):
+ """
+ Set a pin to servo mode and then adjust
+ its position.
+
+ :param my_board: telemetrix_aio instance
+ :param pin: pin to be controlled
+ """
+
+ # set the pin mode
+ await my_board.set_pin_mode_servo(pin)
+
+ await asyncio.sleep(1)
+
+ await my_board.servo_write(pin, 0)
+ await asyncio.sleep(1)
+ await my_board.servo_write(pin, 90)
+ await asyncio.sleep(1)
+ await my_board.servo_write(pin, 180)
+ await my_board.servo_detach(pin)
+
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(transport_type=1)
+try:
+ loop.run_until_complete(servo(board, 5))
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
+except KeyboardInterrupt:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_sonar_disable.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_sonar_disable.py
new file mode 100644
index 0000000..057bd95
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_sonar_disable.py
@@ -0,0 +1,85 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+import time
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+
+"""
+This program continuously monitors an HC-SR04 Ultrasonic Sensor
+It reports changes to the distance sensed.
+"""
+TRIGGER_PIN = 9
+ECHO_PIN = 10
+
+# indices into callback data
+REPORT_TYPE = 0
+TRIG_PIN = 1
+DISTANCE = 2
+TIME = 3
+
+
+# A callback function to display the distance
+async def the_callback(data):
+ """
+ The callback function to display the change in distance
+ :param data: [report_type = PrivateConstants.SONAR_DISTANCE, trigger pin number, distance, timestamp]
+ """
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[3]))
+ print(f'Sonar Report: Trigger Pin: {data[1]} Distance: {data[2]} Time: {date}')
+
+
+async def sonar(my_board, trigger_pin, echo_pin, callback):
+ """
+ Set the pin mode for a sonar device. Results will appear via the
+ callback.
+
+ :param my_board: a telemetrix_aio instance
+ :param trigger_pin: Arduino pin number
+ :param echo_pin: Arduino pin number
+ :param callback: The callback function
+ """
+
+ # set the pin mode for the trigger and echo pins
+ await my_board.set_pin_mode_sonar(trigger_pin, echo_pin, callback)
+ # wait forever
+ while True:
+ try:
+ await asyncio.sleep(5)
+ await my_board.sonar_disable()
+ await asyncio.sleep(5)
+ await my_board.sonar_enable()
+
+ except KeyboardInterrupt:
+ await my_board.shutdown()
+ sys.exit(0)
+
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(transport_type=1)
+
+try:
+ # start the main function
+ loop.run_until_complete(sonar(board, TRIGGER_PIN, ECHO_PIN, the_callback))
+except KeyboardInterrupt:
+ loop.run_until_complete(board.shutdown())
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_spi_adxl345_accelerometer.py b/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_spi_adxl345_accelerometer.py
new file mode 100644
index 0000000..2151fef
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/asyncio/usbSerial/wsa_spi_adxl345_accelerometer.py
@@ -0,0 +1,123 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio import telemetrix_uno_r4_wifi_aio
+
+
+"""
+This program reads x, y, and z registers of an ADXL345 using 4 Wire SPI interface
+"""
+
+"""
+Connection scheme:
+CS = pin 10
+SDA = pin 11
+SDO = pin 12
+SCL = pin 13
+
+"""
+
+
+async def the_callback(data):
+ """
+ :param data: [pin_type, chip select pin, device read register, x data pair,
+ y data pair, z data pair, time stamp]
+
+ """
+ report_type = data[0]
+ chip_select_pin = data[1]
+ device_read_register = data[2] & 0x3f # strip off read command bits
+ number_bytes_read = data[3]
+ x_msb = data[4]
+ x_lsb = data[5]
+ y_msb = data[6]
+ y_lsb = data[7]
+ z_msb = data[8]
+ z_lsb = data[9]
+
+ x_data = (x_msb << 8) + x_lsb
+ y_data = (y_msb << 8) + y_lsb
+ z_data = (z_msb << 8) + z_lsb
+
+ # test report type for SPI report
+ if report_type == 13:
+ print(f'SPI Report: CS Pin: {chip_select_pin} SPI Register: '
+ f'{device_read_register} Number Of Bytes Read: {number_bytes_read} x: '
+ f'{x_data} y: {y_data} z: {z_data}')
+ else:
+ print(f'unexpected report type: {report_type}')
+
+
+async def adxl345(my_board):
+ """
+
+ :type my_board: object
+ """
+ # initialize spi mode for chipselect on pin 10
+ await my_board.set_pin_mode_spi([10])
+ await asyncio.sleep(.3)
+
+ # set the SPI format
+ # spi speed is FPU frequency divided by 4
+ # data order is MSB
+ # mode is MODE3
+ await my_board.spi_set_format(8, 1, 3)
+ await asyncio.sleep(.3)
+
+ # set up power and control register
+ await my_board.spi_write_blocking(10, [45, 0])
+ await asyncio.sleep(.3)
+
+ await my_board.spi_write_blocking(10, [45, 8])
+ await asyncio.sleep(.3)
+
+ # set up data format register for 4 wire spi
+ await my_board.spi_write_blocking(10, [49, 0])
+ await asyncio.sleep(.3)
+
+ # read 6 bytes from the data register
+ # for a multibyte read, we need to OR in a 0x40 into the register value
+ while True:
+ # read 6 bytes from the data register
+ try:
+ await my_board.spi_read_blocking(10, 50 | 0x40, 6, the_callback)
+
+ await asyncio.sleep(.5)
+
+ except (KeyboardInterrupt, RuntimeError):
+ await my_board.shutdown()
+ sys.exit(0)
+
+
+# get the event loop
+loop = asyncio.new_event_loop()
+asyncio.set_event_loop(loop)
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi_aio.TelemetrixUnoR4WiFiAio(transport_type=1)
+
+try:
+ # start the main function
+ loop.run_until_complete(adxl345(board))
+except KeyboardInterrupt:
+ try:
+ loop.run_until_complete(board.shutdown())
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_analog_input.py b/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_analog_input.py
new file mode 100644
index 0000000..ba5108d
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_analog_input.py
@@ -0,0 +1,89 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,f
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+"""
+
+import sys
+import time
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi import telemetrix_uno_r4_wifi
+"""
+Monitor an analog input pin
+"""
+
+"""
+Setup a pin for analog input and monitor its changes
+"""
+
+# Set up a pin for analog input and monitor its changes
+ANALOG_PIN = 2 # arduino pin number (A2)
+
+# Callback data indices
+CB_PIN_MODE = 0
+CB_PIN = 1
+CB_VALUE = 2
+CB_TIME = 3
+
+
+def the_callback(data):
+ """
+ A callback function to report data changes.
+ This will print the pin number, its reported value and
+ the date and time when the change occurred
+
+ :param data: [pin, current reported value, pin_mode, timestamp]
+ """
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[CB_TIME]))
+ print(f'Pin Mode: {data[CB_PIN_MODE]} Pin: {data[CB_PIN]} Value: {data[CB_VALUE]} Time Stamp: {date}')
+
+
+def analog_in(my_board, pin):
+ """
+ This function establishes the pin as an
+ analog input. Any changes on this pin will
+ be reported through the call back function.
+
+ :param my_board: a telemetrix instance
+ :param pin: Arduino pin number
+ """
+
+ # set the pin mode
+ my_board.set_pin_mode_analog_input(pin, differential=5, callback=the_callback)
+
+ # time.sleep(5)
+ # my_board.disable_analog_reporting()
+ # time.sleep(5)
+ # my_board.enable_analog_reporting()
+
+ print('Enter Control-C to quit.')
+ try:
+ while True:
+ try:
+ time.sleep(1)
+ except KeyboardInterrupt:
+ sys.exit(0)
+ except KeyboardInterrupt:
+ board.shutdown()
+ sys.exit(0)
+
+
+board = telemetrix_uno_r4_wifi.TelemetrixUnoR4WiFi(transport_address='192.168.2.118')
+try:
+ analog_in(board, ANALOG_PIN)
+except KeyboardInterrupt:
+ board.shutdown()
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_blink.py b/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_blink.py
new file mode 100644
index 0000000..c14b701
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_blink.py
@@ -0,0 +1,55 @@
+"""
+ Copyright (c) 2020 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,f
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+"""
+
+import sys
+import time
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi import telemetrix_uno_r4_wifi
+"""
+Setup a pin for digital output
+and toggle the pin 5 times.
+"""
+
+# some globals
+DIGITAL_PIN = 13 # the board LED
+
+# Create a Telemetrix instance.
+board = telemetrix_uno_r4_wifi.TelemetrixUnoR4WiFi(transport_address='192.168.2.118')
+
+
+# Set the DIGITAL_PIN as an output pin
+board.set_pin_mode_digital_output(DIGITAL_PIN)
+
+# Blink the LED and provide feedback as
+# to the LED state on the console.
+for blink in range(5):
+ # When hitting control-c to end the program
+ # in this loop, we are likely to get a KeyboardInterrupt
+ # exception. Catch the exception and exit gracefully.
+ try:
+ print('On')
+ board.digital_write(DIGITAL_PIN, 0)
+ time.sleep(1)
+ print('Off')
+ board.digital_write(DIGITAL_PIN, 1)
+ time.sleep(1)
+ except KeyboardInterrupt:
+ board.shutdown()
+ sys.exit(0)
+board.shutdown()
diff --git a/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_dht.py b/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_dht.py
new file mode 100644
index 0000000..cb9b4f0
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_dht.py
@@ -0,0 +1,100 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import sys
+import time
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi import telemetrix_uno_r4_wifi
+
+"""
+This program monitors a DHT22 sensor.
+"""
+
+# Arduino pin number
+DHT_PIN = 8
+
+# indices into callback data for valid data
+# REPORT_TYPE = 0
+# READ_RESULT = 1
+# PIN = 2
+# DHT_TYPE = 3
+# HUMIDITY = 4
+# TEMPERATURE = 5
+# TIME = 6
+
+# indices into callback data for error report
+# REPORT_TYPE = 0
+# READ_RESULT = 1
+# PIN = 2
+# DHT_TYPE = 3
+# TIME = 4
+
+
+# A callback function to display the distance
+# noinspection GrazieInspection
+def the_callback(data):
+ # noinspection GrazieInspection
+ """
+ The callback function to display the change in distance
+ :param data: [report_type = PrivateConstants.DHT, error = 0, pin number,
+ dht_type, humidity, temperature timestamp]
+ if this is an error report:
+ [report_type = PrivateConstants.DHT, error != 0, pin number, dht_type
+ timestamp]
+ """
+ if data[1]:
+ # error message
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[4]))
+ print(f'DHT Error Report:'
+ f'Pin: {data[2]} DHT Type: {data[3]} Error: {data[1]} Time: {date}')
+ else:
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[6]))
+ print(f'DHT Valid Data Report:'
+ f'Pin: {data[2]} DHT Type: {data[3]} Humidity: {data[4]} Temperature:'
+ f' {data[5]} Time: {date}')
+
+
+def dht(my_board, pin, callback, dht_type):
+ # noinspection GrazieInspection
+ """
+ Set the pin mode for a DHT 22 device. Results will appear via the
+ callback.
+
+ :param my_board: an telemetrix instance
+ :param pin: Arduino pin number
+ :param callback: The callback function
+ :param dht_type: 22 or 11
+ """
+
+ # set the pin mode for the DHT device
+ my_board.set_pin_mode_dht(pin, callback=callback, dht_type=dht_type)
+
+
+board = telemetrix_uno_r4_wifi.TelemetrixUnoR4WiFi(transport_address='192.168.2.118')
+try:
+ dht(board, DHT_PIN, the_callback, 11)
+
+ # wait forever
+ while True:
+ try:
+ time.sleep(2)
+ except KeyboardInterrupt:
+ board.shutdown()
+ sys.exit(0)
+except (KeyboardInterrupt, RuntimeError):
+ board.shutdown()
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_digital_input.py b/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_digital_input.py
new file mode 100644
index 0000000..7899b7d
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_digital_input.py
@@ -0,0 +1,99 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,f
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ DHT support courtesy of Martyn Wheeler
+ Based on the DHTNew library - https://github.com/RobTillaart/DHTNew
+"""
+
+import sys
+import time
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi import telemetrix_uno_r4_wifi
+"""
+Monitor a digital input pin
+"""
+
+"""
+Setup a pin for digital input and monitor its changes
+"""
+
+# Set up a pin for analog input and monitor its changes
+DIGITAL_PIN = 12 # arduino pin number
+
+# Callback data indices
+CB_PIN_MODE = 0
+CB_PIN = 1
+CB_VALUE = 2
+CB_TIME = 3
+
+# variable to hold the last time a button state changed
+debounce_time = time.time()
+
+
+def the_callback(data):
+ """
+ A callback function to report data changes.
+ This will print the pin number, its reported value and
+ the date and time when the change occurred
+
+ :param data: [pin, current reported value, pin_mode, timestamp]
+ """
+ global debounce_time
+
+ # if the time from the last event change is > .2 seconds, the input is debounced
+ if data[CB_TIME] - debounce_time > .3:
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[CB_TIME]))
+ print(f'Pin: {data[CB_PIN]} Value: {data[CB_VALUE]} Time Stamp: {date}')
+ debounce_time = data[CB_TIME]
+
+
+def digital_in(my_board, pin):
+ """
+ This function establishes the pin as a
+ digital input. Any changes on this pin will
+ be reported through the call back function.
+
+ :param my_board: a telemetrix instance
+ :param pin: Arduino pin number
+ """
+
+ # set the pin mode
+ my_board.set_pin_mode_digital_input(pin, the_callback)
+ # time.sleep(1)
+ # my_board.disable_all_reporting()
+ # time.sleep(4)
+ # my_board.enable_digital_reporting(12)
+
+ # time.sleep(3)
+ # my_board.enable_digital_reporting(pin)
+ # time.sleep(1)
+
+ print('Enter Control-C to quit.')
+ # my_board.enable_digital_reporting(12)
+ try:
+ while True:
+ time.sleep(.0001)
+ except KeyboardInterrupt:
+ board.shutdown()
+ sys.exit(0)
+
+
+board = telemetrix_uno_r4_wifi.TelemetrixUnoR4WiFi(transport_address='192.168.2.118')
+try:
+ digital_in(board, DIGITAL_PIN)
+except KeyboardInterrupt:
+ board.shutdown()
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_digital_input_pullup.py b/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_digital_input_pullup.py
new file mode 100644
index 0000000..7a18782
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_digital_input_pullup.py
@@ -0,0 +1,93 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,f
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ DHT support courtesy of Martyn Wheeler
+ Based on the DHTNew library - https://github.com/RobTillaart/DHTNew
+"""
+
+import sys
+import time
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi import telemetrix_uno_r4_wifi
+
+"""
+Monitor a digital input pin with pullup enabled
+"""
+
+"""
+Setup a pin for digital input and monitor its changes
+"""
+
+# Set up a pin for analog input and monitor its changes
+DIGITAL_PIN = 12 # arduino pin number
+
+# Callback data indices
+CB_PIN_MODE = 0
+CB_PIN = 1
+CB_VALUE = 2
+CB_TIME = 3
+
+
+def the_callback(data):
+ """
+ A callback function to report data changes.
+ This will print the pin number, its reported value and
+ the date and time when the change occurred
+
+ :param data: [pin, current reported value, pin_mode, timestamp]
+ """
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[CB_TIME]))
+ print(f'Pin Mode: {data[CB_PIN_MODE]} Pin: {data[CB_PIN]} Value: {data[CB_VALUE]} Time Stamp: {date}')
+
+
+def digital_in_pullup(my_board, pin):
+ """
+ This function establishes the pin as a
+ digital input. Any changes on this pin will
+ be reported through the call back function.
+
+ :param my_board: a telemetrix instance
+ :param pin: Arduino pin number
+ """
+
+ # set the pin mode
+ my_board.set_pin_mode_digital_input_pullup(pin, the_callback)
+ # time.sleep(1)
+ # my_board.disable_all_reporting()
+ # time.sleep(4)
+ # my_board.enable_digital_reporting(12)
+
+ # time.sleep(3)
+ # my_board.enable_digital_reporting(pin)
+ # time.sleep(1)
+
+ print('Enter Control-C to quit.')
+ # my_board.enable_digital_reporting(12)
+ try:
+ while True:
+ time.sleep(.0001)
+ except KeyboardInterrupt:
+ board.shutdown()
+ sys.exit(0)
+
+
+board = telemetrix_uno_r4_wifi.TelemetrixUnoR4WiFi(transport_address='192.168.2.118')
+
+try:
+ digital_in_pullup(board, DIGITAL_PIN)
+except KeyboardInterrupt:
+ board.shutdown()
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_fade.py b/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_fade.py
new file mode 100644
index 0000000..0ad9f33
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_fade.py
@@ -0,0 +1,59 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,f
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+"""
+
+import sys
+import time
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi import telemetrix_uno_r4_wifi
+
+"""
+Setup a pin for output and fade its intensity
+"""
+
+# some globals
+# make sure to select a PWM pin
+DIGITAL_PIN = 13
+
+# Create a Telemetrix instance.
+board = telemetrix_uno_r4_wifi.TelemetrixUnoR4WiFi(transport_address='192.168.2.118')
+
+# Set the DIGITAL_PIN as an output pin
+board.set_pin_mode_analog_output(DIGITAL_PIN)
+# board.set_pin_mode_analog_output(DIGITAL_PIN)
+
+
+# When hitting control-c to end the program
+# in this loop, we are likely to get a KeyboardInterrupt
+# exception. Catch the exception and exit gracefully.
+
+try:
+ print('Fading up...')
+ for i in range(255):
+ board.analog_write(DIGITAL_PIN, i)
+ time.sleep(.005)
+ print('Fading down...')
+ for i in range(255, -1, -1):
+ board.analog_write(DIGITAL_PIN, i)
+ time.sleep(.005)
+
+ board.set_pin_mode_digital_output(DIGITAL_PIN)
+
+except KeyboardInterrupt:
+ board.shutdown()
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_hc-sr04_distance_sensor.py b/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_hc-sr04_distance_sensor.py
new file mode 100644
index 0000000..8315ef3
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_hc-sr04_distance_sensor.py
@@ -0,0 +1,75 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import sys
+import time
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi import telemetrix_uno_r4_wifi
+
+"""
+This program continuously monitors an HC-SR04 Ultrasonic Sensor
+It reports changes to the distance sensed.
+"""
+TRIGGER_PIN = 9
+ECHO_PIN = 10
+
+# indices into callback data
+REPORT_TYPE = 0
+TRIG_PIN = 1
+DISTANCE = 2
+TIME = 3
+
+
+# A callback function to display the distance
+def the_callback(data):
+ """
+ The callback function to display the change in distance
+ :param data: [report_type = PrivateConstants.SONAR_DISTANCE, trigger pin number, distance, timestamp]
+ """
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[3]))
+ print(f'Sonar Report: Trigger Pin: {data[1]} Distance: {data[2]} Time: {date}')
+
+
+def sonar(my_board, trigger_pin, echo_pin, callback):
+ """
+ Set the pin mode for a sonar device. Results will appear via the
+ callback.
+
+ :param my_board: a telemetrix instance
+ :param trigger_pin: Arduino pin number
+ :param echo_pin: Arduino pin number
+ :param callback: The callback function
+ """
+
+ # set the pin mode for the trigger and echo pins
+ my_board.set_pin_mode_sonar(trigger_pin, echo_pin, callback)
+ # wait forever
+ while True:
+ try:
+ time.sleep(.01)
+ except KeyboardInterrupt:
+ my_board.shutdown()
+ time.sleep(1)
+ sys.exit(0)
+
+
+board = telemetrix_uno_r4_wifi.TelemetrixUnoR4WiFi(transport_address='192.168.2.118')
+try:
+ sonar(board, TRIGGER_PIN, ECHO_PIN, the_callback)
+ board.shutdown()
+except (KeyboardInterrupt, RuntimeError):
+ board.shutdown()
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_i2c_adxl345_accelerometer.py b/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_i2c_adxl345_accelerometer.py
new file mode 100644
index 0000000..258d949
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_i2c_adxl345_accelerometer.py
@@ -0,0 +1,98 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import sys
+import time
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi import telemetrix_uno_r4_wifi
+
+"""
+This example sets up and control an ADXL345 i2c accelerometer.
+It will continuously print data the raw xyz data from the device.
+"""
+
+
+# the call back function to print the adxl345 data
+def the_callback(data):
+ """
+
+ :param data: [pin_type, Device address, device read register, x data pair, y data pair, z data pair]
+ :return:
+ """
+ report_type = data[0]
+ number_bytes_read = data[2]
+ i2c_device_address = data[3]
+ i2c_register = data[4]
+ x_msb = data[5]
+ x_lsb = data[6]
+ y_msb = data[7]
+ y_lsb = data[8]
+ z_msb = data[9]
+ z_lsb = data[10]
+
+ x_data = (x_msb << 8) + x_lsb
+ y_data = (y_msb << 8) + y_lsb
+ z_data = (z_msb << 8) + z_lsb
+
+ # test report type for SPI report
+ if report_type == 10:
+ print(f'i2c Report: i2c device address: {i2c_device_address } i2c '
+ f'Register: '
+ f'{i2c_register} Number Of Bytes Read: {number_bytes_read} x: '
+ f'{x_data} y: {y_data} z: {z_data}')
+ else:
+ print(f'unexpected report type: {report_type}')
+
+
+def adxl345(my_board):
+ # setup adxl345
+ # device address = 83
+ my_board.set_pin_mode_i2c()
+
+ # set up power and control register
+ my_board.i2c_write(83, [45, 0])
+ time.sleep(.1)
+ my_board.i2c_write(83, [45, 8])
+ time.sleep(.1)
+
+ # set up the data format register
+ my_board.i2c_write(83, [49, 8])
+ time.sleep(.1)
+ my_board.i2c_write(83, [49, 3])
+ time.sleep(.1)
+
+ # read_count = 20
+ while True:
+ # read 6 bytes from the data register
+ try:
+ my_board.i2c_read(83, 50, 6, the_callback)
+ time.sleep(.1)
+
+ except (KeyboardInterrupt, RuntimeError):
+ my_board.shutdown()
+ sys.exit(0)
+
+
+board = telemetrix_uno_r4_wifi.TelemetrixUnoR4WiFi(transport_address='192.168.2.118')
+
+try:
+ adxl345(board)
+except KeyboardInterrupt:
+ try:
+ board.shutdown()
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_loop_back.py b/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_loop_back.py
new file mode 100644
index 0000000..8d637e1
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_loop_back.py
@@ -0,0 +1,61 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import time
+import sys
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi import telemetrix_uno_r4_wifi
+
+"""
+Loopback some data to assure that data can be sent and received between
+the Telemetrix client and arduino-telemetrix server.
+"""
+
+
+def the_callback(data):
+ """
+ A callback function to report receive the looped back data
+
+ :param data: [looped back data]
+ """
+ print(f'Looped back: {chr(data[0])}')
+
+
+def loop_back(my_board, loop_back_data):
+ """
+ This function will request that the supplied characters be
+ sent to the board and looped back and printed out to the console.
+
+ :param my_board: a telemetrix instance
+ :param loop_back_data: A list of characters to have looped back
+ """
+ try:
+ for data in loop_back_data:
+ my_board.loop_back(data, callback=the_callback)
+ print(f'Sending: {data}')
+ except KeyboardInterrupt:
+ board.shutdown()
+ sys.exit(0)
+
+
+board = telemetrix_uno_r4_wifi.TelemetrixUnoR4WiFi(transport_address='192.168.2.118')
+char_list = ['A', 'B', 'Z']
+try:
+ loop_back(board, char_list)
+ time.sleep(1.5)
+except KeyboardInterrupt:
+ board.shutdown()
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_scroll_message.py b/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_scroll_message.py
new file mode 100644
index 0000000..b9612cb
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_scroll_message.py
@@ -0,0 +1,47 @@
+"""
+ Copyright (c) 2020 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,f
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+"""
+
+import sys
+import time
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi import telemetrix_uno_r4_wifi
+
+"""
+Setup a pin for digital output
+and toggle the pin 5 times.
+"""
+
+# some globals
+message = 'Hello World'
+
+# Create a Telemetrix instance.
+board = telemetrix_uno_r4_wifi.TelemetrixUnoR4WiFi(transport_address='192.168.2.118')
+
+try:
+ # Set the DIGITAL_PIN as an output pin
+ board.enable_scroll_message(message)
+ time.sleep(5)
+ board.disable_scroll_message()
+ time.sleep(5)
+ board.shutdown()
+ sys.exit(0)
+except:
+ board.shutdown()
+ sys.exit(0)
+
diff --git a/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_servo.py b/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_servo.py
new file mode 100644
index 0000000..c5fc090
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_servo.py
@@ -0,0 +1,50 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,f
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+"""
+
+import sys
+import time
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi import telemetrix_uno_r4_wifi
+
+"""
+Attach a pin to a servo and move it about.
+"""
+
+# some globals
+SERVO_PIN = 5
+
+# Create a Telemetrix instance.
+board = telemetrix_uno_r4_wifi.TelemetrixUnoR4WiFi(transport_address='192.168.2.118')
+
+try:
+ board.set_pin_mode_servo(SERVO_PIN, 100, 3000)
+ time.sleep(.2)
+ board.servo_write(SERVO_PIN, 90)
+ time.sleep(1)
+ board.servo_write(SERVO_PIN, 0)
+ time.sleep(1)
+ board.servo_write(SERVO_PIN, 180)
+ time.sleep(1)
+ board.servo_write(SERVO_PIN, 90)
+ time.sleep(1)
+
+ board.servo_detach(SERVO_PIN)
+ time.sleep(.2)
+ board.shutdown()
+except KeyboardInterrupt:
+ board.shutdown()
diff --git a/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_sonar_disable.py b/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_sonar_disable.py
new file mode 100644
index 0000000..397bf10
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_sonar_disable.py
@@ -0,0 +1,81 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+import time
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi import telemetrix_uno_r4_wifi
+
+"""
+This program continuously monitors an HC-SR04 Ultrasonic Sensor
+It reports changes to the distance sensed.
+"""
+TRIGGER_PIN = 9
+ECHO_PIN = 10
+
+# indices into callback data
+REPORT_TYPE = 0
+TRIG_PIN = 1
+DISTANCE = 2
+TIME = 3
+
+
+# A callback function to display the distance
+def the_callback(data):
+ """
+ The callback function to display the change in distance
+ :param data: [report_type = PrivateConstants.SONAR_DISTANCE, trigger pin number, distance, timestamp]
+ """
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[3]))
+ print(f'Sonar Report: Trigger Pin: {data[1]} Distance: {data[2]} Time: {date}')
+
+
+def sonar(my_board, trigger_pin, echo_pin, callback):
+ """
+ Set the pin mode for a sonar device. Results will appear via the
+ callback.
+
+ :param my_board: a telemetrix_aio instance
+ :param trigger_pin: Arduino pin number
+ :param echo_pin: Arduino pin number
+ :param callback: The callback function
+ """
+
+ # set the pin mode for the trigger and echo pins
+ my_board.set_pin_mode_sonar(trigger_pin, echo_pin, callback)
+ # wait forever
+ while True:
+ try:
+ time.sleep(5)
+ my_board.sonar_disable()
+ time.sleep(5)
+ my_board.sonar_enable()
+
+ except KeyboardInterrupt:
+ my_board.shutdown()
+ sys.exit(0)
+
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi.TelemetrixUnoR4WiFi(transport_address='192.168.2.118')
+try:
+ sonar(board, TRIGGER_PIN, ECHO_PIN, the_callback)
+ board.shutdown()
+except (KeyboardInterrupt, RuntimeError):
+ board.shutdown()
+ sys.exit(0)
+
diff --git a/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_spi_adxl345_accelerometer.py b/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_spi_adxl345_accelerometer.py
new file mode 100644
index 0000000..7da3975
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/threaded/WIFI/wwt_spi_adxl345_accelerometer.py
@@ -0,0 +1,115 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import sys
+import time
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi import telemetrix_uno_r4_wifi
+
+"""
+This program reads x, y, and z registers of an ADXL345 using 4 Wire SPI interface
+"""
+
+"""
+Connection scheme:
+CS = pin 10
+SDA = pin 11
+SDO = pin 12
+SCL = pin 13
+
+"""
+
+
+def the_callback(data):
+ """
+ :param data: [pin_type, chip select pin, device read register, x data pair,
+ y data pair, z data pair, time stamp]
+
+ """
+ report_type = data[0]
+ chip_select_pin = data[1]
+ device_read_register = data[2] & 0x3f # strip off read command bits
+ number_bytes_read = data[3]
+ x_msb = data[4]
+ x_lsb = data[5]
+ y_msb = data[6]
+ y_lsb = data[7]
+ z_msb = data[8]
+ z_lsb = data[9]
+
+ x_data = (x_msb << 8) + x_lsb
+ y_data = (y_msb << 8) + y_lsb
+ z_data = (z_msb << 8) + z_lsb
+
+ # test report type for SPI report
+ if report_type == 13:
+ print(f'SPI Report: CS Pin: {chip_select_pin} SPI Register: '
+ f'{device_read_register} Number Of Bytes Read: {number_bytes_read} x: '
+ f'{x_data} y: {y_data} z: {z_data}')
+ else:
+ print(f'unexpected report type: {report_type}')
+
+
+def adxl345(my_board):
+ """
+
+ :type my_board: object
+ """
+ # initialize spi mode for chipselect on pin 10
+ my_board.set_pin_mode_spi([10])
+ time.sleep(.3)
+
+ # set the SPI format
+ # spi speed is FPU frequency divided by 4
+ # data order is MSB
+ # mode is MODE3
+ my_board.spi_set_format(8, 1, 3)
+ time.sleep(.3)
+
+ # set up power and control register
+ my_board.spi_write_blocking(10, [45, 0])
+ time.sleep(.3)
+
+ my_board.spi_write_blocking(10, [45, 8])
+ time.sleep(.3)
+
+ # set up data format register for 4 wire spi
+ my_board.spi_write_blocking(10, [49, 0])
+ time.sleep(.3)
+
+ # read 6 bytes from the data register
+ # for a multibyte read, we need to OR in a 0x40 into the register value
+ while True:
+ # read 6 bytes from the data register
+ try:
+ my_board.spi_read_blocking(10, 50 | 0x40, 6, the_callback)
+
+ time.sleep(.5)
+
+ except (KeyboardInterrupt, RuntimeError):
+ my_board.shutdown()
+ sys.exit(0)
+
+
+board = telemetrix_uno_r4_wifi.TelemetrixUnoR4WiFi(transport_address='192.168.2.118')
+try:
+ adxl345(board)
+except KeyboardInterrupt:
+ try:
+ board.shutdown()
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_analog_input.py b/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_analog_input.py
new file mode 100644
index 0000000..57b6aba
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_analog_input.py
@@ -0,0 +1,89 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,f
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+"""
+
+import sys
+import time
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi import telemetrix_uno_r4_wifi
+"""
+Monitor an analog input pin
+"""
+
+"""
+Setup a pin for analog input and monitor its changes
+"""
+
+# Set up a pin for analog input and monitor its changes
+ANALOG_PIN = 2 # arduino pin number (A2)
+
+# Callback data indices
+CB_PIN_MODE = 0
+CB_PIN = 1
+CB_VALUE = 2
+CB_TIME = 3
+
+
+def the_callback(data):
+ """
+ A callback function to report data changes.
+ This will print the pin number, its reported value and
+ the date and time when the change occurred
+
+ :param data: [pin, current reported value, pin_mode, timestamp]
+ """
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[CB_TIME]))
+ print(f'Pin Mode: {data[CB_PIN_MODE]} Pin: {data[CB_PIN]} Value: {data[CB_VALUE]} Time Stamp: {date}')
+
+
+def analog_in(my_board, pin):
+ """
+ This function establishes the pin as an
+ analog input. Any changes on this pin will
+ be reported through the call back function.
+
+ :param my_board: a telemetrix instance
+ :param pin: Arduino pin number
+ """
+
+ # set the pin mode
+ my_board.set_pin_mode_analog_input(pin, differential=5, callback=the_callback)
+
+ # time.sleep(5)
+ # my_board.disable_analog_reporting()
+ # time.sleep(5)
+ # my_board.enable_analog_reporting()
+
+ print('Enter Control-C to quit.')
+ try:
+ while True:
+ try:
+ time.sleep(1)
+ except KeyboardInterrupt:
+ sys.exit(0)
+ except KeyboardInterrupt:
+ board.shutdown()
+ sys.exit(0)
+
+
+board = telemetrix_uno_r4_wifi.TelemetrixUnoR4WiFi(transport_type=1)
+try:
+ analog_in(board, ANALOG_PIN)
+except KeyboardInterrupt:
+ board.shutdown()
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_blink.py b/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_blink.py
new file mode 100644
index 0000000..ee92420
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_blink.py
@@ -0,0 +1,55 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,f
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ """
+
+import sys
+import time
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi import telemetrix_uno_r4_wifi
+
+
+"""
+Setup a pin for digital output
+and toggle the pin 5 times.
+"""
+
+# some globals
+DIGITAL_PIN = 13 # the board LED
+
+# Create a Telemetrix instance.
+board = telemetrix_uno_r4_wifi.TelemetrixUnoR4WiFi(transport_type=1)
+
+# Set the DIGITAL_PIN as an output pin
+board.set_pin_mode_digital_output(DIGITAL_PIN)
+
+# Blink the LED and provide feedback as
+# to the LED state on the console.
+for blink in range(5):
+ # When hitting control-c to end the program
+ # in this loop, we are likely to get a KeyboardInterrupt
+ # exception. Catch the exception and exit gracefully.
+ try:
+ print('1')
+ board.digital_write(DIGITAL_PIN, 1)
+ time.sleep(1)
+ print('0')
+ board.digital_write(DIGITAL_PIN, 0)
+ time.sleep(1)
+ except KeyboardInterrupt:
+ board.shutdown()
+ sys.exit(0)
+board.shutdown()
diff --git a/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_dht.py b/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_dht.py
new file mode 100644
index 0000000..4595e53
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_dht.py
@@ -0,0 +1,100 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import sys
+import time
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi import telemetrix_uno_r4_wifi
+
+"""
+This program monitors a DHT22 sensor.
+"""
+
+# Arduino pin number
+DHT_PIN = 8
+
+# indices into callback data for valid data
+# REPORT_TYPE = 0
+# READ_RESULT = 1
+# PIN = 2
+# DHT_TYPE = 3
+# HUMIDITY = 4
+# TEMPERATURE = 5
+# TIME = 6
+
+# indices into callback data for error report
+# REPORT_TYPE = 0
+# READ_RESULT = 1
+# PIN = 2
+# DHT_TYPE = 3
+# TIME = 4
+
+
+# A callback function to display the distance
+# noinspection GrazieInspection
+def the_callback(data):
+ # noinspection GrazieInspection
+ """
+ The callback function to display the change in distance
+ :param data: [report_type = PrivateConstants.DHT, error = 0, pin number,
+ dht_type, humidity, temperature timestamp]
+ if this is an error report:
+ [report_type = PrivateConstants.DHT, error != 0, pin number, dht_type
+ timestamp]
+ """
+ if data[1]:
+ # error message
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[4]))
+ print(f'DHT Error Report:'
+ f'Pin: {data[2]} DHT Type: {data[3]} Error: {data[1]} Time: {date}')
+ else:
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[6]))
+ print(f'DHT Valid Data Report:'
+ f'Pin: {data[2]} DHT Type: {data[3]} Humidity: {data[4]} Temperature:'
+ f' {data[5]} Time: {date}')
+
+
+def dht(my_board, pin, callback, dht_type):
+ # noinspection GrazieInspection
+ """
+ Set the pin mode for a DHT 22 device. Results will appear via the
+ callback.
+
+ :param my_board: an telemetrix instance
+ :param pin: Arduino pin number
+ :param callback: The callback function
+ :param dht_type: 22 or 11
+ """
+
+ # set the pin mode for the DHT device
+ my_board.set_pin_mode_dht(pin, callback, dht_type)
+
+
+board = telemetrix_uno_r4_wifi.TelemetrixUnoR4WiFi(transport_type=1)
+try:
+ dht(board, DHT_PIN, callback=the_callback, dht_type=11)
+
+ # wait forever
+ while True:
+ try:
+ time.sleep(.01)
+ except KeyboardInterrupt:
+ board.shutdown()
+ sys.exit(0)
+except (KeyboardInterrupt, RuntimeError):
+ board.shutdown()
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_digital_input.py b/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_digital_input.py
new file mode 100644
index 0000000..756e7aa
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_digital_input.py
@@ -0,0 +1,92 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,f
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ DHT support courtesy of Martyn Wheeler
+ Based on the DHTNew library - https://github.com/RobTillaart/DHTNew
+"""
+
+import sys
+import time
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi import telemetrix_uno_r4_wifi
+"""
+Monitor a digital input pin
+"""
+
+"""
+Setup a pin for digital input and monitor its changes
+"""
+
+# Set up a pin for analog input and monitor its changes
+DIGITAL_PIN = 12 # arduino pin number
+
+# Callback data indices
+CB_PIN_MODE = 0
+CB_PIN = 1
+CB_VALUE = 2
+CB_TIME = 3
+
+
+def the_callback(data):
+ """
+ A callback function to report data changes.
+ This will print the pin number, its reported value and
+ the date and time when the change occurred
+
+ :param data: [pin, current reported value, pin_mode, timestamp]
+ """
+
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[CB_TIME]))
+ print(f'Pin: {data[CB_PIN]} Value: {data[CB_VALUE]} Time Stamp: {date}')
+
+
+def digital_in(my_board, pin):
+ """
+ This function establishes the pin as a
+ digital input. Any changes on this pin will
+ be reported through the call back function.
+
+ :param my_board: a telemetrix instance
+ :param pin: Arduino pin number
+ """
+
+ # set the pin mode
+ my_board.set_pin_mode_digital_input(pin, the_callback)
+ # time.sleep(1)
+ # my_board.disable_all_reporting()
+ # time.sleep(4)
+ # my_board.enable_digital_reporting(12)
+
+ # time.sleep(3)
+ # my_board.enable_digital_reporting(pin)
+ # time.sleep(1)
+
+ print('Enter Control-C to quit.')
+ # my_board.enable_digital_reporting(12)
+ try:
+ while True:
+ time.sleep(.0001)
+ except KeyboardInterrupt:
+ board.shutdown()
+ sys.exit(0)
+
+
+board = telemetrix_uno_r4_wifi.TelemetrixUnoR4WiFi(transport_type=1)
+try:
+ digital_in(board, DIGITAL_PIN)
+except KeyboardInterrupt:
+ board.shutdown()
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_digital_input_pullup.py b/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_digital_input_pullup.py
new file mode 100644
index 0000000..1f2d4c4
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_digital_input_pullup.py
@@ -0,0 +1,93 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,f
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ DHT support courtesy of Martyn Wheeler
+ Based on the DHTNew library - https://github.com/RobTillaart/DHTNew
+"""
+
+import sys
+import time
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi import telemetrix_uno_r4_wifi
+
+"""
+Monitor a digital input pin with pullup enabled
+"""
+
+"""
+Setup a pin for digital input and monitor its changes
+"""
+
+# Set up a pin for analog input and monitor its changes
+DIGITAL_PIN = 12 # arduino pin number
+
+# Callback data indices
+CB_PIN_MODE = 0
+CB_PIN = 1
+CB_VALUE = 2
+CB_TIME = 3
+
+
+def the_callback(data):
+ """
+ A callback function to report data changes.
+ This will print the pin number, its reported value and
+ the date and time when the change occurred
+
+ :param data: [pin, current reported value, pin_mode, timestamp]
+ """
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[CB_TIME]))
+ print(f'Pin Mode: {data[CB_PIN_MODE]} Pin: {data[CB_PIN]} Value: {data[CB_VALUE]} Time Stamp: {date}')
+
+
+def digital_in_pullup(my_board, pin):
+ """
+ This function establishes the pin as a
+ digital input. Any changes on this pin will
+ be reported through the call back function.
+
+ :param my_board: a telemetrix instance
+ :param pin: Arduino pin number
+ """
+
+ # set the pin mode
+ my_board.set_pin_mode_digital_input_pullup(pin, the_callback)
+ # time.sleep(1)
+ # my_board.disable_all_reporting()
+ # time.sleep(4)
+ # my_board.enable_digital_reporting(12)
+
+ # time.sleep(3)
+ # my_board.enable_digital_reporting(pin)
+ # time.sleep(1)
+
+ print('Enter Control-C to quit.')
+ # my_board.enable_digital_reporting(12)
+ try:
+ while True:
+ time.sleep(.0011)
+ except KeyboardInterrupt:
+ board.shutdown()
+ sys.exit(0)
+
+
+board = telemetrix_uno_r4_wifi.TelemetrixUnoR4WiFi(transport_type=1)
+
+try:
+ digital_in_pullup(board, DIGITAL_PIN)
+except KeyboardInterrupt:
+ board.shutdown()
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_fade.py b/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_fade.py
new file mode 100644
index 0000000..f9742f6
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_fade.py
@@ -0,0 +1,59 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,f
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+"""
+
+import sys
+import time
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi import telemetrix_uno_r4_wifi
+
+"""
+Setup a pin for output and fade its intensity
+"""
+
+# some globals
+# make sure to select a PWM pin
+DIGITAL_PIN = 13
+
+# Create a Telemetrix instance.
+board = telemetrix_uno_r4_wifi.TelemetrixUnoR4WiFi(transport_type=1)
+
+# Set the DIGITAL_PIN as an output pin
+board.set_pin_mode_analog_output(DIGITAL_PIN)
+# board.set_pin_mode_analog_output(DIGITAL_PIN)
+
+
+# When hitting control-c to end the program
+# in this loop, we are likely to get a KeyboardInterrupt
+# exception. Catch the exception and exit gracefully.
+
+try:
+ print('Fading up...')
+ for i in range(255):
+ board.analog_write(DIGITAL_PIN, i)
+ time.sleep(.005)
+ print('Fading down...')
+ for i in range(255, -1, -1):
+ board.analog_write(DIGITAL_PIN, i)
+ time.sleep(.005)
+
+ board.set_pin_mode_digital_output(DIGITAL_PIN)
+
+except KeyboardInterrupt:
+ board.shutdown()
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_hc-sr04_distance_sensor.py b/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_hc-sr04_distance_sensor.py
new file mode 100644
index 0000000..617306e
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_hc-sr04_distance_sensor.py
@@ -0,0 +1,75 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import sys
+import time
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi import telemetrix_uno_r4_wifi
+
+"""
+This program continuously monitors an HC-SR04 Ultrasonic Sensor
+It reports changes to the distance sensed.
+"""
+TRIGGER_PIN = 9
+ECHO_PIN = 10
+
+# indices into callback data
+REPORT_TYPE = 0
+TRIG_PIN = 1
+DISTANCE = 2
+TIME = 3
+
+
+# A callback function to display the distance
+def the_callback(data):
+ """
+ The callback function to display the change in distance
+ :param data: [report_type = PrivateConstants.SONAR_DISTANCE, trigger pin number, distance, timestamp]
+ """
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[3]))
+ print(f'Sonar Report: Trigger Pin: {data[1]} Distance: {data[2]} Time: {date}')
+
+
+def sonar(my_board, trigger_pin, echo_pin, callback):
+ """
+ Set the pin mode for a sonar device. Results will appear via the
+ callback.
+
+ :param my_board: a telemetrix instance
+ :param trigger_pin: Arduino pin number
+ :param echo_pin: Arduino pin number
+ :param callback: The callback function
+ """
+
+ # set the pin mode for the trigger and echo pins
+ my_board.set_pin_mode_sonar(trigger_pin, echo_pin, callback)
+ # wait forever
+ while True:
+ try:
+ time.sleep(.01)
+ except KeyboardInterrupt:
+ my_board.shutdown()
+ time.sleep(1)
+ sys.exit(0)
+
+
+board = telemetrix_uno_r4_wifi.TelemetrixUnoR4WiFi(transport_type=1)
+try:
+ sonar(board, TRIGGER_PIN, ECHO_PIN, the_callback)
+ board.shutdown()
+except (KeyboardInterrupt, RuntimeError):
+ board.shutdown()
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_i2c_adxl345_accelerometer.py b/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_i2c_adxl345_accelerometer.py
new file mode 100644
index 0000000..94b132e
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_i2c_adxl345_accelerometer.py
@@ -0,0 +1,98 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import sys
+import time
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi import telemetrix_uno_r4_wifi
+
+"""
+This example sets up and control an ADXL345 i2c accelerometer.
+It will continuously print data the raw xyz data from the device.
+"""
+
+
+# the call back function to print the adxl345 data
+def the_callback(data):
+ """
+
+ :param data: [pin_type, Device address, device read register, x data pair, y data pair, z data pair]
+ :return:
+ """
+ report_type = data[0]
+ number_bytes_read = data[2]
+ i2c_device_address = data[3]
+ i2c_register = data[4]
+ x_msb = data[5]
+ x_lsb = data[6]
+ y_msb = data[7]
+ y_lsb = data[8]
+ z_msb = data[9]
+ z_lsb = data[10]
+
+ x_data = (x_msb << 8) + x_lsb
+ y_data = (y_msb << 8) + y_lsb
+ z_data = (z_msb << 8) + z_lsb
+
+ # test report type for SPI report
+ if report_type == 10:
+ print(f'i2c Report: i2c device address: {i2c_device_address } i2c '
+ f'Register: '
+ f'{i2c_register} Number Of Bytes Read: {number_bytes_read} x: '
+ f'{x_data} y: {y_data} z: {z_data}')
+ else:
+ print(f'unexpected report type: {report_type}')
+
+
+def adxl345(my_board):
+ # setup adxl345
+ # device address = 83
+ my_board.set_pin_mode_i2c()
+
+ # set up power and control register
+ my_board.i2c_write(83, [45, 0])
+ time.sleep(.1)
+ my_board.i2c_write(83, [45, 8])
+ time.sleep(.1)
+
+ # set up the data format register
+ my_board.i2c_write(83, [49, 8])
+ time.sleep(.1)
+ my_board.i2c_write(83, [49, 3])
+ time.sleep(.1)
+
+ # read_count = 20
+ while True:
+ # read 6 bytes from the data register
+ try:
+ my_board.i2c_read(83, 50, 6, the_callback)
+ time.sleep(.1)
+
+ except (KeyboardInterrupt, RuntimeError):
+ my_board.shutdown()
+ sys.exit(0)
+
+
+board = telemetrix_uno_r4_wifi.TelemetrixUnoR4WiFi(transport_type=1)
+
+try:
+ adxl345(board)
+except KeyboardInterrupt:
+ try:
+ board.shutdown()
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_loop_back.py b/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_loop_back.py
new file mode 100644
index 0000000..196f724
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_loop_back.py
@@ -0,0 +1,62 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import time
+import sys
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi import telemetrix_uno_r4_wifi
+
+"""
+Loopback some data to assure that data can be sent and received between
+the Telemetrix client and arduino-telemetrix server.
+"""
+
+
+def the_callback(data):
+ """
+ A callback function to report receive the looped back data
+
+ :param data: [looped back data]
+ """
+ print(f'Looped back: {chr(data[0])}')
+
+
+def loop_back(my_board, loop_back_data):
+ """
+ This function will request that the supplied characters be
+ sent to the board and looped back and printed out to the console.
+
+ :param my_board: a telemetrix instance
+ :param loop_back_data: A list of characters to have looped back
+ """
+ try:
+ for data in loop_back_data:
+ my_board.loop_back(data, callback=the_callback)
+ print(f'Sending: {data}')
+ time.sleep(.1)
+ except KeyboardInterrupt:
+ board.shutdown()
+ sys.exit(0)
+
+
+board = telemetrix_uno_r4_wifi.TelemetrixUnoR4WiFi(transport_type=1)
+char_list = ['A', 'B', 'Z']
+try:
+ loop_back(board, char_list)
+ time.sleep(.1)
+except KeyboardInterrupt:
+ board.shutdown()
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_scroll_message.py b/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_scroll_message.py
new file mode 100644
index 0000000..90eb88e
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_scroll_message.py
@@ -0,0 +1,46 @@
+"""
+ Copyright (c) 2020 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,f
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+"""
+
+import sys
+import time
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi import telemetrix_uno_r4_wifi
+
+"""
+Setup a pin for digital output
+and toggle the pin 5 times.
+"""
+
+# some globals
+message = 'Hello World'
+
+# Create a Telemetrix instance.
+board = telemetrix_uno_r4_wifi.TelemetrixUnoR4WiFi(transport_type=1)
+
+
+# Set the DIGITAL_PIN as an output pin
+try:
+ board.enable_scroll_message(message)
+ time.sleep(5)
+ board.disable_scroll_message()
+ time.sleep(5)
+ board.shutdown()
+except KeyboardInterrupt:
+ board.shutdown()
+
diff --git a/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_servo.py b/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_servo.py
new file mode 100644
index 0000000..b5ef344
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_servo.py
@@ -0,0 +1,49 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,f
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+"""
+
+import sys
+import time
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi import telemetrix_uno_r4_wifi
+
+"""
+Attach a pin to a servo and move it about.
+"""
+
+# some globals
+SERVO_PIN = 5
+
+# Create a Telemetrix instance.
+board = telemetrix_uno_r4_wifi.TelemetrixUnoR4WiFi(transport_type=1)
+try:
+ board.set_pin_mode_servo(SERVO_PIN, 100, 3000)
+ time.sleep(.2)
+ board.servo_write(SERVO_PIN, 90)
+ time.sleep(1)
+ board.servo_write(SERVO_PIN, 0)
+ time.sleep(1)
+ board.servo_write(SERVO_PIN, 180)
+ time.sleep(1)
+ board.servo_write(SERVO_PIN, 90)
+ time.sleep(1)
+
+ board.servo_detach(SERVO_PIN)
+ time.sleep(.2)
+ board.shutdown()
+except KeyboardInterrupt:
+ board.shutdown()
diff --git a/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_sonar_disable.py b/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_sonar_disable.py
new file mode 100644
index 0000000..972f68a
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_sonar_disable.py
@@ -0,0 +1,81 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+import time
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi import telemetrix_uno_r4_wifi
+
+"""
+This program continuously monitors an HC-SR04 Ultrasonic Sensor
+It reports changes to the distance sensed.
+"""
+TRIGGER_PIN = 9
+ECHO_PIN = 10
+
+# indices into callback data
+REPORT_TYPE = 0
+TRIG_PIN = 1
+DISTANCE = 2
+TIME = 3
+
+
+# A callback function to display the distance
+def the_callback(data):
+ """
+ The callback function to display the change in distance
+ :param data: [report_type = PrivateConstants.SONAR_DISTANCE, trigger pin number, distance, timestamp]
+ """
+ date = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(data[3]))
+ print(f'Sonar Report: Trigger Pin: {data[1]} Distance: {data[2]} Time: {date}')
+
+
+def sonar(my_board, trigger_pin, echo_pin, callback):
+ """
+ Set the pin mode for a sonar device. Results will appear via the
+ callback.
+
+ :param my_board: a telemetrix_aio instance
+ :param trigger_pin: Arduino pin number
+ :param echo_pin: Arduino pin number
+ :param callback: The callback function
+ """
+
+ # set the pin mode for the trigger and echo pins
+ my_board.set_pin_mode_sonar(trigger_pin, echo_pin, callback)
+ # wait forever
+ while True:
+ try:
+ time.sleep(5)
+ my_board.sonar_disable()
+ time.sleep(5)
+ my_board.sonar_enable()
+
+ except KeyboardInterrupt:
+ my_board.shutdown()
+ sys.exit(0)
+
+
+# instantiate telemetrix_aio
+board = telemetrix_uno_r4_wifi.TelemetrixUnoR4WiFi(transport_type=1)
+try:
+ sonar(board, TRIGGER_PIN, ECHO_PIN, the_callback)
+ board.shutdown()
+except (KeyboardInterrupt, RuntimeError):
+ board.shutdown()
+ sys.exit(0)
+
diff --git a/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_spi_adxl345_accelerometer.py b/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_spi_adxl345_accelerometer.py
new file mode 100644
index 0000000..722574a
--- /dev/null
+++ b/telemetrix_uno_r4/r4_wifi_examples/threaded/usbSerial/wst_spi_adxl345_accelerometer.py
@@ -0,0 +1,115 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import sys
+import time
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi import telemetrix_uno_r4_wifi
+
+"""
+This program reads x, y, and z registers of an ADXL345 using 4 Wire SPI interface
+"""
+
+"""
+Connection scheme:
+CS = pin 10
+SDA = pin 11
+SDO = pin 12
+SCL = pin 13
+
+"""
+
+
+def the_callback(data):
+ """
+ :param data: [pin_type, chip select pin, device read register, x data pair,
+ y data pair, z data pair, time stamp]
+
+ """
+ report_type = data[0]
+ chip_select_pin = data[1]
+ device_read_register = data[2] & 0x3f # strip off read command bits
+ number_bytes_read = data[3]
+ x_msb = data[4]
+ x_lsb = data[5]
+ y_msb = data[6]
+ y_lsb = data[7]
+ z_msb = data[8]
+ z_lsb = data[9]
+
+ x_data = (x_msb << 8) + x_lsb
+ y_data = (y_msb << 8) + y_lsb
+ z_data = (z_msb << 8) + z_lsb
+
+ # test report type for SPI report
+ if report_type == 13:
+ print(f'SPI Report: CS Pin: {chip_select_pin} SPI Register: '
+ f'{device_read_register} Number Of Bytes Read: {number_bytes_read} x: '
+ f'{x_data} y: {y_data} z: {z_data}')
+ else:
+ print(f'unexpected report type: {report_type}')
+
+
+def adxl345(my_board):
+ """
+
+ :type my_board: object
+ """
+ # initialize spi mode for chipselect on pin 10
+ my_board.set_pin_mode_spi([10])
+ time.sleep(.3)
+
+ # set the SPI format
+ # spi speed is FPU frequency divided by 4
+ # data order is MSB
+ # mode is MODE3
+ my_board.spi_set_format(8, 1, 3)
+ time.sleep(.3)
+
+ # set up power and control register
+ my_board.spi_write_blocking(10, [45, 0])
+ time.sleep(.3)
+
+ my_board.spi_write_blocking(10, [45, 8])
+ time.sleep(.3)
+
+ # set up data format register for 4 wire spi
+ my_board.spi_write_blocking(10, [49, 0])
+ time.sleep(.3)
+
+ # read 6 bytes from the data register
+ # for a multibyte read, we need to OR in a 0x40 into the register value
+ while True:
+ # read 6 bytes from the data register
+ try:
+ my_board.spi_read_blocking(10, 50 | 0x40, 6, the_callback)
+
+ time.sleep(.5)
+
+ except (KeyboardInterrupt, RuntimeError):
+ my_board.shutdown()
+ sys.exit(0)
+
+
+board = telemetrix_uno_r4_wifi.TelemetrixUnoR4WiFi(transport_type=1)
+try:
+ adxl345(board)
+except KeyboardInterrupt:
+ try:
+ board.shutdown()
+ except:
+ pass
+ sys.exit(0)
diff --git a/telemetrix_uno_r4/wifi/telemetrix_uno_r4_wifi/__init__.py b/telemetrix_uno_r4/wifi/telemetrix_uno_r4_wifi/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/telemetrix_uno_r4/wifi/telemetrix_uno_r4_wifi/private_constants.py b/telemetrix_uno_r4/wifi/telemetrix_uno_r4_wifi/private_constants.py
new file mode 100644
index 0000000..ea6d662
--- /dev/null
+++ b/telemetrix_uno_r4/wifi/telemetrix_uno_r4_wifi/private_constants.py
@@ -0,0 +1,155 @@
+"""
+ Copyright (c) 2015-2021 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+
+class PrivateConstants:
+ """
+ This class contains a set of constants for telemetrix internal use .
+ """
+
+ # commands
+ # send a loop back request - for debugging communications
+ LOOP_COMMAND = 0
+ SET_PIN_MODE = 1 # set a pin to INPUT/OUTPUT/PWM/etc
+ DIGITAL_WRITE = 2 # set a single digital pin value instead of entire port
+ ANALOG_WRITE = 3
+ MODIFY_REPORTING = 4
+ GET_FIRMWARE_VERSION = 5
+ ARE_U_THERE = 6 # Arduino ID query for auto-detect of telemetrix connected boards
+ SERVO_ATTACH = 7
+ SERVO_WRITE = 8
+ SERVO_DETACH = 9
+ I2C_BEGIN = 10
+ I2C_READ = 11
+ I2C_WRITE = 12
+ SONAR_NEW = 13
+ DHT_NEW = 14
+ STOP_ALL_REPORTS = 15
+ SET_ANALOG_SCANNING_INTERVAL = 16
+ ENABLE_ALL_REPORTS = 17
+ RESET = 18
+ SPI_INIT = 19
+ SPI_WRITE_BLOCKING = 20
+ SPI_READ_BLOCKING = 21
+ SPI_SET_FORMAT = 22
+ SPI_CS_CONTROL = 23
+ ONE_WIRE_INIT = 24
+ ONE_WIRE_RESET = 25
+ ONE_WIRE_SELECT = 26
+ ONE_WIRE_SKIP = 27
+ ONE_WIRE_WRITE = 28
+ ONE_WIRE_READ = 29
+ ONE_WIRE_RESET_SEARCH = 30
+ ONE_WIRE_SEARCH = 31
+ ONE_WIRE_CRC8 = 32
+ SET_PIN_MODE_STEPPER = 33
+ STEPPER_MOVE_TO = 34
+ STEPPER_MOVE = 35
+ STEPPER_RUN = 36
+ STEPPER_RUN_SPEED = 37
+ STEPPER_SET_MAX_SPEED = 38
+ STEPPER_SET_ACCELERATION = 39
+ STEPPER_SET_SPEED = 40
+ STEPPER_SET_CURRENT_POSITION = 41
+ STEPPER_RUN_SPEED_TO_POSITION = 42
+ STEPPER_STOP = 43
+ STEPPER_DISABLE_OUTPUTS = 44
+ STEPPER_ENABLE_OUTPUTS = 45
+ STEPPER_SET_MINIMUM_PULSE_WIDTH = 46
+ STEPPER_SET_ENABLE_PIN = 47
+ STEPPER_SET_3_PINS_INVERTED = 48
+ STEPPER_SET_4_PINS_INVERTED = 49
+ STEPPER_IS_RUNNING = 50
+ STEPPER_GET_CURRENT_POSITION = 51
+ STEPPER_GET_DISTANCE_TO_GO = 52
+ STEPPER_GET_TARGET_POSITION = 53
+ GET_FEATURES = 54
+ SONAR_DISABLE = 55
+ SONAR_ENABLE = 56
+ BOARD_HARD_RESET = 57
+ SCROLL_MESSAGE_ON = 58
+ SCROLL_MESSAGE_OFF = 59
+
+ # reports
+ # debug data from Arduino
+ DIGITAL_REPORT = DIGITAL_WRITE
+ ANALOG_REPORT = ANALOG_WRITE
+ FIRMWARE_REPORT = GET_FIRMWARE_VERSION
+ I_AM_HERE_REPORT = ARE_U_THERE
+ SERVO_UNAVAILABLE = SERVO_ATTACH
+ I2C_TOO_FEW_BYTES_RCVD = 8
+ I2C_TOO_MANY_BYTES_RCVD = 9
+ I2C_READ_REPORT = 10
+ SONAR_DISTANCE = 11
+ DHT_REPORT = 12
+ SPI_REPORT = 13
+ ONE_WIRE_REPORT = 14
+ STEPPER_DISTANCE_TO_GO = 15
+ STEPPER_TARGET_POSITION = 16
+ STEPPER_CURRENT_POSITION = 17
+ STEPPER_RUNNING_REPORT = 18
+ STEPPER_RUN_COMPLETE_REPORT = 19
+ FEATURES = 20
+ DEBUG_PRINT = 99
+
+ TELEMETRIX_VERSION = "1.00"
+
+ # reporting control
+ REPORTING_DISABLE_ALL = 0
+ REPORTING_ANALOG_ENABLE = 1
+ REPORTING_DIGITAL_ENABLE = 2
+ REPORTING_ANALOG_DISABLE = 3
+ REPORTING_DIGITAL_DISABLE = 4
+
+ # Pin mode definitions
+ AT_INPUT = 0
+ AT_OUTPUT = 1
+ AT_INPUT_PULLUP = 2
+ AT_ANALOG = 3
+ AT_SERVO = 4
+ AT_SONAR = 5
+ AT_DHT = 6
+ AT_MODE_NOT_SET = 255
+
+ # maximum number of digital pins supported
+ NUMBER_OF_DIGITAL_PINS = 100
+
+ # maximum number of analog pins supported
+ NUMBER_OF_ANALOG_PINS = 20
+
+ # maximum number of sonars allowed
+ MAX_SONARS = 6
+
+ # maximum number of DHT devices allowed
+ MAX_DHTS = 6
+
+ # DHT Report sub-types
+ DHT_DATA = 0
+ DHT_ERROR = 1
+
+ # feature masks
+ ONEWIRE_FEATURE = 0x01
+ DHT_FEATURE = 0x02
+ STEPPERS_FEATURE = 0x04
+ SPI_FEATURE = 0x08
+ SERVO_FEATURE = 0x10
+ SONAR_FEATURE = 0x20
+
+ # transport types
+ TRANSPORT_WIFI = 1
+ TRANSPORT_USB_SERIAL = 2
+ TRANSPORT_BLE = 3
diff --git a/telemetrix_uno_r4/wifi/telemetrix_uno_r4_wifi/telemetrix_uno_r4_wifi.py b/telemetrix_uno_r4/wifi/telemetrix_uno_r4_wifi/telemetrix_uno_r4_wifi.py
new file mode 100644
index 0000000..e06fb2a
--- /dev/null
+++ b/telemetrix_uno_r4/wifi/telemetrix_uno_r4_wifi/telemetrix_uno_r4_wifi.py
@@ -0,0 +1,2645 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+"""
+import socket
+import sys
+import threading
+import time
+from collections import deque
+
+import serial
+# noinspection PyPackageRequirementscd
+from serial.serialutil import SerialException
+# noinspection PyPackageRequirements
+from serial.tools import list_ports
+
+# noinspection PyUnresolvedReferences
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi.private_constants import PrivateConstants
+
+
+# noinspection PyPep8,PyMethodMayBeStatic,GrazieInspection,PyBroadException,PyCallingNonCallable
+class TelemetrixUnoR4WiFi(threading.Thread):
+ """
+ This class exposes and implements the telemetrix API.
+ It uses threading to accommodate concurrency.
+ It includes the public API methods as well as
+ a set of private methods.
+
+ """
+
+ # noinspection PyPep8,PyPep8,PyPep8
+ def __init__(self, com_port=None, arduino_instance_id=1,
+ arduino_wait=1, sleep_tune=0.000001,
+ shutdown_on_exception=True, hard_reset_on_shutdown=True,
+ transport_address=None, ip_port=31336, transport_type=0):
+
+ """
+
+ :param com_port: e.g. COM3 or /dev/ttyACM0.
+ Only use if you wish to bypass auto com port
+ detection.
+
+ :param arduino_instance_id: Match with the value installed on the
+ arduino-telemetrix sketch.
+
+ :param arduino_wait: Amount of time to wait for an Arduino to
+ fully reset itself.
+
+ :param sleep_tune: A tuning parameter (typically not changed by user)
+
+ :param shutdown_on_exception: call shutdown before raising
+ a RunTimeError exception, or
+ receiving a KeyboardInterrupt exception
+
+ :param hard_reset_on_shutdown: reset the board on shutdown
+
+ :param transport_address: ip address of tcp/ip connected device.
+
+ :param ip_port: ip port of tcp/ip connected device
+
+ :param transport_type: 0 = WiFI
+ 1 = USBSerial
+ 2 = BLE
+
+
+ """
+
+ # initialize threading parent
+ threading.Thread.__init__(self)
+
+ # create the threads and set them as daemons so
+ # that they stop when the program is closed
+
+ # create a thread to interpret received serial data
+ self.the_reporter_thread = threading.Thread(target=self._reporter)
+ self.the_reporter_thread.daemon = True
+
+ self.transport_address = transport_address
+ self.ip_port = ip_port
+
+ if transport_type not in [0, 1, 2]:
+ raise RuntimeError("Valid transport_type value is 0, 1, or 2")
+
+ self.transport_type = transport_type
+
+ if transport_type == 0:
+ if not transport_address:
+ raise RuntimeError("An IP address must be specified.")
+
+ if not self.transport_address:
+ self.the_data_receive_thread = threading.Thread(target=self._serial_receiver)
+ else:
+ self.the_data_receive_thread = threading.Thread(target=self._tcp_receiver)
+
+ self.the_data_receive_thread.daemon = True
+
+ # flag to allow the reporter and receive threads to run.
+ self.run_event = threading.Event()
+
+ # check to make sure that Python interpreter is version 3.7 or greater
+ python_version = sys.version_info
+ if python_version[0] >= 3:
+ if python_version[1] >= 7:
+ pass
+ else:
+ raise RuntimeError("ERROR: Python 3.7 or greater is "
+ "required for use of this program.")
+
+ # save input parameters as instance variables
+ self.com_port = com_port
+ self.arduino_instance_id = arduino_instance_id
+ self.arduino_wait = arduino_wait
+ self.sleep_tune = sleep_tune
+ self.shutdown_on_exception = shutdown_on_exception
+ self.hard_reset_on_shutdown = hard_reset_on_shutdown
+
+ # create a deque to receive and process data from the arduino
+ self.the_deque = deque()
+
+ # The report_dispatch dictionary is used to process
+ # incoming report messages by looking up the report message
+ # and executing its associated processing method.
+
+ self.report_dispatch = {}
+
+ # To add a command to the command dispatch table, append here.
+ self.report_dispatch.update(
+ {PrivateConstants.LOOP_COMMAND: self._report_loop_data})
+ self.report_dispatch.update(
+ {PrivateConstants.DEBUG_PRINT: self._report_debug_data})
+ self.report_dispatch.update(
+ {PrivateConstants.DIGITAL_REPORT: self._digital_message})
+ self.report_dispatch.update(
+ {PrivateConstants.ANALOG_REPORT: self._analog_message})
+ self.report_dispatch.update(
+ {PrivateConstants.FIRMWARE_REPORT: self._firmware_message})
+ self.report_dispatch.update({PrivateConstants.I_AM_HERE_REPORT: self._i_am_here})
+ self.report_dispatch.update(
+ {PrivateConstants.SERVO_UNAVAILABLE: self._servo_unavailable})
+ self.report_dispatch.update(
+ {PrivateConstants.I2C_READ_REPORT: self._i2c_read_report})
+ self.report_dispatch.update(
+ {PrivateConstants.I2C_TOO_FEW_BYTES_RCVD: self._i2c_too_few})
+ self.report_dispatch.update(
+ {PrivateConstants.I2C_TOO_MANY_BYTES_RCVD: self._i2c_too_many})
+ self.report_dispatch.update(
+ {PrivateConstants.SONAR_DISTANCE: self._sonar_distance_report})
+ self.report_dispatch.update({PrivateConstants.DHT_REPORT: self._dht_report})
+ self.report_dispatch.update(
+ {PrivateConstants.SPI_REPORT: self._spi_report})
+ self.report_dispatch.update(
+ {PrivateConstants.ONE_WIRE_REPORT: self._onewire_report})
+ self.report_dispatch.update(
+ {PrivateConstants.STEPPER_DISTANCE_TO_GO:
+ self._stepper_distance_to_go_report})
+ self.report_dispatch.update(
+ {PrivateConstants.STEPPER_TARGET_POSITION:
+ self._stepper_target_position_report})
+ self.report_dispatch.update(
+ {PrivateConstants.STEPPER_CURRENT_POSITION:
+ self._stepper_current_position_report})
+ self.report_dispatch.update(
+ {PrivateConstants.STEPPER_RUNNING_REPORT:
+ self._stepper_is_running_report})
+ self.report_dispatch.update(
+ {PrivateConstants.STEPPER_RUN_COMPLETE_REPORT:
+ self._stepper_run_complete_report})
+
+ self.report_dispatch.update(
+ {PrivateConstants.STEPPER_DISTANCE_TO_GO:
+ self._stepper_distance_to_go_report})
+ self.report_dispatch.update(
+ {PrivateConstants.STEPPER_TARGET_POSITION:
+ self._stepper_target_position_report})
+ self.report_dispatch.update(
+ {PrivateConstants.FEATURES:
+ self._features_report})
+
+ # dictionaries to store the callbacks for each pin
+ self.analog_callbacks = {}
+
+ self.digital_callbacks = {}
+
+ self.i2c_callback = None
+ self.i2c_callback2 = None
+
+ self.i2c_1_active = False
+ self.i2c_2_active = False
+
+ self.spi_callback = None
+
+ self.onewire_callback = None
+
+ self.cs_pins_enabled = []
+
+ # the trigger pin will be the key to retrieve
+ # the callback for a specific HC-SR04
+ self.sonar_callbacks = {}
+
+ self.sonar_count = 0
+
+ self.dht_callbacks = {}
+
+ self.dht_count = 0
+
+ # serial port in use
+ self.serial_port = None
+
+ # socket for tcp/ip communications
+ self.sock = None
+
+ # flag to indicate we are in shutdown mode
+ self.shutdown_flag = False
+
+ # debug loopback callback method
+ self.loop_back_callback = None
+
+ # flag to indicate the start of a new report
+ # self.new_report_start = True
+
+ # firmware version to be stored here
+ self.firmware_version = []
+
+ # reported arduino instance id
+ self.reported_arduino_id = []
+
+ # reported features
+ self.reported_features = 0
+
+ # flag to indicate if i2c was previously enabled
+ self.i2c_enabled = False
+
+ # flag to indicate if spi is initialized
+ self.spi_enabled = False
+
+ # flag to indicate if onewire is initialized
+ self.onewire_enabled = False
+
+ # stepper motor variables
+
+ # updated when a new motor is added
+ # self.next_stepper_assigned = 0
+ #
+ # # valid list of stepper motor interface types
+ # self.valid_stepper_interfaces = [1, 2, 3, 4, 6, 8]
+ #
+ # # maximum number of steppers supported
+ # self.max_number_of_steppers = 4
+ #
+ # # number of steppers created - not to exceed the maximum
+ # self.number_of_steppers = 0
+ #
+ # # dictionary to hold stepper motor information
+ # self.stepper_info = {'instance': False, 'is_running': None,
+ # 'maximum_speed': 1, 'speed': 0, 'acceleration': 0,
+ # 'distance_to_go_callback': None,
+ # 'target_position_callback': None,
+ # 'current_position_callback': None,
+ # 'is_running_callback': None,
+ # 'motion_complete_callback': None,
+ # 'acceleration_callback': None}
+ #
+ # # build a list of stepper motor info items
+ # self.stepper_info_list = []
+ # # a list of dictionaries to hold stepper information
+ # for motor in range(self.max_number_of_steppers):
+ # self.stepper_info_list.append(self.stepper_info)
+
+ self.the_reporter_thread.start()
+ self.the_data_receive_thread.start()
+
+ print(f"telemetrix_uno_r4_wifi: Version"
+ f" {PrivateConstants.TELEMETRIX_VERSION}\n\n"
+ f"Copyright (c) 2023 Alan Yorinks All Rights Reserved.\n")
+
+ # using the serial link
+ if not self.transport_address:
+ if not self.com_port:
+ # user did not specify a com_port
+ try:
+ self._find_arduino()
+ except KeyboardInterrupt:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ else:
+ # com_port specified - set com_port and baud rate
+ try:
+ self._manual_open()
+ except KeyboardInterrupt:
+ if self.shutdown_on_exception:
+ self.shutdown()
+
+ if self.serial_port:
+ print(
+ f"Arduino compatible device found and connected to {self.serial_port.port}")
+
+ self.serial_port.reset_input_buffer()
+ self.serial_port.reset_output_buffer()
+ self.disable_scroll_message()
+
+ # no com_port found - raise a runtime exception
+ else:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError('No Arduino Found or User Aborted Program')
+ else:
+ self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ self.sock.connect((self.transport_address, self.ip_port))
+ print(f'Successfully connected to: {self.transport_address}:{self.ip_port}')
+
+ # allow the threads to run
+ self._run_threads()
+ print(f'Waiting for Arduino to reset')
+ print(f'Reset Complete')
+
+ # get telemetrix firmware version and print it
+ print('\nRetrieving Telemetrix4UnoR4WiFi firmware ID...')
+ self._get_firmware_version()
+ if not self.firmware_version:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(f'Telemetrix4UnoR4WiFi firmware version')
+
+ else:
+
+ print(f'Telemetrix4UnoR4WiFi firmware version: {self.firmware_version[0]}.'
+ f'{self.firmware_version[1]}.{self.firmware_version[2]}')
+ command = [PrivateConstants.ENABLE_ALL_REPORTS]
+ self._send_command(command)
+
+ # get the features list
+ command = [PrivateConstants.GET_FEATURES]
+ self._send_command(command)
+ time.sleep(.2)
+
+ # Have the server reset its data structures
+ command = [PrivateConstants.RESET]
+ self._send_command(command)
+ time.sleep(.2)
+
+ def _find_arduino(self):
+ """
+ This method will search all potential serial ports for an Arduino
+ containing a sketch that has a matching arduino_instance_id as
+ specified in the input parameters of this class.
+
+ This is used explicitly with the Telemetrix4Arduino sketch.
+ """
+
+ # a list of serial ports to be checked
+ serial_ports = []
+
+ print('Opening all potential serial ports...')
+ the_ports_list = list_ports.comports()
+ for port in the_ports_list:
+ if port.pid is None:
+ continue
+ try:
+ self.serial_port = serial.Serial(port.device, 115200,
+ timeout=1, writeTimeout=0)
+ except SerialException:
+ continue
+ # create a list of serial ports that we opened
+ serial_ports.append(self.serial_port)
+
+ # display to the user
+ print('\t' + port.device)
+
+ # clear out any possible data in the input buffer
+ # wait for arduino to reset
+ print(
+ f'\nWaiting {self.arduino_wait} seconds(arduino_wait) for Arduino devices to '
+ 'reset...')
+ # temporary for testing
+ time.sleep(self.arduino_wait)
+ self._run_threads()
+
+ for serial_port in serial_ports:
+ self.serial_port = serial_port
+
+ self._get_arduino_id()
+ if self.reported_arduino_id != self.arduino_instance_id:
+ continue
+ else:
+ print('Valid Arduino ID Found.')
+ self.serial_port.reset_input_buffer()
+ self.serial_port.reset_output_buffer()
+ return
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(f'Incorrect Arduino ID: {self.reported_arduino_id}')
+
+ def _manual_open(self):
+ """
+ Com port was specified by the user - try to open up that port
+
+ """
+ # if port is not found, a serial exception will be thrown
+ try:
+ print(f'Opening {self.com_port}...')
+ self.serial_port = serial.Serial(self.com_port, 115200,
+ timeout=1, writeTimeout=0)
+
+ print(
+ f'\nWaiting {self.arduino_wait} seconds(arduino_wait) for Arduino devices to '
+ 'reset...')
+ self._run_threads()
+ time.sleep(self.arduino_wait)
+
+ self._get_arduino_id()
+
+ if self.reported_arduino_id != self.arduino_instance_id:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(f'Incorrect Arduino ID: {self.reported_arduino_id}')
+ print('Valid Arduino ID Found.')
+ # get arduino firmware version and print it
+ print('\nRetrieving Telemetrix4Arduino firmware ID...')
+ self._get_firmware_version()
+
+ if not self.firmware_version:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(
+ f'Telemetrix4Arduino Sketch Firmware Version Not Found')
+
+ else:
+ print(f'Telemetrix4UnoR4 firmware version: {self.firmware_version[0]}.'
+ f'{self.firmware_version[1]}')
+ except KeyboardInterrupt:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError('User Hit Control-C')
+
+ def analog_write(self, pin, value):
+ """
+ Set the specified pin to the specified value.
+
+ :param pin: arduino pin number
+
+ :param value: pin value (maximum 16 bits)
+
+ """
+ value_msb = value >> 8
+ value_lsb = value & 0xff
+ command = [PrivateConstants.ANALOG_WRITE, pin, value_msb, value_lsb]
+ self._send_command(command)
+
+ def digital_write(self, pin, value):
+ """
+ Set the specified pin to the specified value.
+
+ :param pin: arduino pin number
+
+ :param value: pin value (1 or 0)
+
+ """
+
+ command = [PrivateConstants.DIGITAL_WRITE, pin, value]
+ self._send_command(command)
+
+ def disable_all_reporting(self):
+ """
+ Disable reporting for all digital and analog input pins
+ """
+ command = [PrivateConstants.MODIFY_REPORTING,
+ PrivateConstants.REPORTING_DISABLE_ALL, 0]
+ self._send_command(command)
+
+ def disable_analog_reporting(self, pin):
+ """
+ Disables analog reporting for a single analog pin.
+
+ :param pin: Analog pin number. For example for A0, the number is 0.
+
+ """
+ command = [PrivateConstants.MODIFY_REPORTING,
+ PrivateConstants.REPORTING_ANALOG_DISABLE, pin]
+ self._send_command(command)
+
+ def disable_digital_reporting(self, pin):
+ """
+ Disables digital reporting for a single digital input.
+
+ :param pin: Pin number.
+
+ """
+ command = [PrivateConstants.MODIFY_REPORTING,
+ PrivateConstants.REPORTING_DIGITAL_DISABLE, pin]
+ self._send_command(command)
+
+ def enable_analog_reporting(self, pin):
+ """
+ Enables analog reporting for the specified pin.
+
+ :param pin: Analog pin number. For example for A0, the number is 0.
+
+
+ """
+ command = [PrivateConstants.MODIFY_REPORTING,
+ PrivateConstants.REPORTING_ANALOG_ENABLE, pin]
+ self._send_command(command)
+
+ def enable_digital_reporting(self, pin):
+ """
+ Enable reporting on the specified digital pin.
+
+ :param pin: Pin number.
+ """
+
+ command = [PrivateConstants.MODIFY_REPORTING,
+ PrivateConstants.REPORTING_DIGITAL_ENABLE, pin]
+ self._send_command(command)
+
+ def _get_arduino_id(self):
+ """
+ Retrieve arduino-telemetrix arduino id
+
+ """
+ command = [PrivateConstants.ARE_U_THERE]
+ self._send_command(command)
+ # provide time for the reply
+ time.sleep(.5)
+
+ def _get_firmware_version(self):
+ """
+ This method retrieves the
+ arduino-telemetrix firmware version
+
+ """
+ command = [PrivateConstants.GET_FIRMWARE_VERSION]
+ self._send_command(command)
+ # provide time for the reply
+ time.sleep(.5)
+
+ def i2c_read(self, address, register, number_of_bytes,
+ callback=None, i2c_port=0,
+ write_register=True):
+ """
+ Read the specified number of bytes from the
+ specified register for the i2c device.
+
+
+ :param address: i2c device address
+
+ :param register: i2c register (or None if no register
+ selection is needed)
+
+ :param number_of_bytes: number of bytes to be read
+
+ :param callback: Required callback function to report
+ i2c data as a result of read command
+
+ :param i2c_port: 0 = default, 1 = secondary
+
+ :param write_register: If True, the register is written
+ before read
+ Else, the write is suppressed
+
+
+ callback returns a data list:
+
+ [I2C_READ_REPORT, address, register, count of data bytes,
+ data bytes, time-stamp]
+
+ """
+
+ self._i2c_read_request(address, register, number_of_bytes,
+ callback=callback, i2c_port=i2c_port,
+ write_register=write_register)
+
+ def i2c_read_restart_transmission(self, address, register,
+ number_of_bytes,
+ callback=None, i2c_port=0,
+ write_register=True):
+ """
+ Read the specified number of bytes from the specified
+ register for the i2c device. This restarts the transmission
+ after the read. It is required for some i2c devices such as the MMA8452Q
+ accelerometer.
+
+
+ :param address: i2c device address
+
+ :param register: i2c register (or None if no register
+ selection is needed)
+
+ :param number_of_bytes: number of bytes to be read
+
+ :param callback: Required callback function to report i2c
+ data as a result of read command
+
+ :param i2c_port: 0 = default 1 = secondary
+
+ :param write_register: If True, the register is written before read
+ Else, the write is suppressed
+
+
+
+ callback returns a data list:
+
+ [I2C_READ_REPORT, address, register, count of data bytes,
+ data bytes, time-stamp]
+
+ """
+
+ self._i2c_read_request(address, register, number_of_bytes,
+ stop_transmission=False,
+ callback=callback, i2c_port=i2c_port,
+ write_register=write_register)
+
+ def _i2c_read_request(self, address, register, number_of_bytes,
+ stop_transmission=True, callback=None, i2c_port=0,
+ write_register=True):
+ """
+ This method requests the read of an i2c device. Results are retrieved
+ via callback.
+
+ :param address: i2c device address
+
+ :param register: register number (or None if no register selection is needed)
+
+ :param number_of_bytes: number of bytes expected to be returned
+
+ :param stop_transmission: stop transmission after read
+
+ :param callback: Required callback function to report i2c data as a
+ result of read command.
+
+ :param write_register: If True, the register is written before read
+ Else, the write is suppressed
+
+ """
+ if not i2c_port:
+ if not self.i2c_1_active:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(
+ 'I2C Read: set_pin_mode i2c never called for i2c port 1.')
+
+ if i2c_port:
+ if not self.i2c_2_active:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(
+ 'I2C Read: set_pin_mode i2c never called for i2c port 2.')
+
+ if not callback:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError('I2C Read: A callback function must be specified.')
+
+ if not i2c_port:
+ self.i2c_callback = callback
+ else:
+ self.i2c_callback2 = callback
+
+ if not register:
+ register = 0
+
+ if write_register:
+ write_register = 1
+ else:
+ write_register = 0
+
+ # message contains:
+ # 1. address
+ # 2. register
+ # 3. number of bytes
+ # 4. restart_transmission - True or False
+ # 5. i2c port
+ # 6. suppress write flag
+
+ command = [PrivateConstants.I2C_READ, address, register, number_of_bytes,
+ stop_transmission, i2c_port, write_register]
+ self._send_command(command)
+
+ def i2c_write(self, address, args, i2c_port=0):
+ """
+ Write data to an i2c device.
+
+ :param address: i2c device address
+
+ :param i2c_port: 0= port 1, 1 = port 2
+
+ :param args: A variable number of bytes to be sent to the device
+ passed in as a list
+
+ """
+ if not i2c_port:
+ if not self.i2c_1_active:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(
+ 'I2C Write: set_pin_mode i2c never called for i2c port 1.')
+
+ if i2c_port:
+ if not self.i2c_2_active:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(
+ 'I2C Write: set_pin_mode i2c never called for i2c port 2.')
+
+ command = [PrivateConstants.I2C_WRITE, len(args), address, i2c_port]
+
+ for item in args:
+ command.append(item)
+
+ self._send_command(command)
+
+ def loop_back(self, start_character, callback=None):
+ """
+ This is a debugging method to send a character to the
+ Arduino device, and have the device loop it back.
+
+ :param start_character: The character to loop back. It should be
+ an integer.
+
+ :param callback: Looped back character will appear in the callback method
+
+ """
+ command = [PrivateConstants.LOOP_COMMAND, ord(start_character)]
+ self.loop_back_callback = callback
+ self._send_command(command)
+
+ def set_analog_scan_interval(self, interval):
+ """
+ Set the analog scanning interval.
+
+ :param interval: value of 0 - 255 - milliseconds
+ """
+
+ if 0 <= interval <= 255:
+ command = [PrivateConstants.SET_ANALOG_SCANNING_INTERVAL, interval]
+ self._send_command(command)
+ else:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError('Analog interval must be between 0 and 255')
+
+ def set_pin_mode_analog_output(self, pin_number):
+ """
+ Set a pin as a pwm (analog output) pin.
+
+ :param pin_number:arduino pin number
+
+ """
+ self._set_pin_mode(pin_number, PrivateConstants.AT_OUTPUT)
+
+ def set_pin_mode_analog_input(self, pin_number, differential=0, callback=None):
+ """
+ Set a pin as an analog input.
+
+ :param pin_number: arduino pin number
+
+ :param differential: difference in previous to current value before
+ report will be generated
+
+ :param callback: callback function
+
+
+ callback returns a data list:
+
+ [pin_type, pin_number, pin_value, raw_time_stamp]
+
+ The pin_type for analog input pins = 3
+
+ """
+ self._set_pin_mode(pin_number, PrivateConstants.AT_ANALOG, differential,
+ callback)
+
+ def set_pin_mode_digital_input(self, pin_number, callback=None):
+ """
+ Set a pin as a digital input.
+
+ :param pin_number: arduino pin number
+
+ :param callback: callback function
+
+
+ callback returns a data list:
+
+ [pin_type, pin_number, pin_value, raw_time_stamp]
+
+ The pin_type for all digital input pins = 2
+
+ """
+ self._set_pin_mode(pin_number, PrivateConstants.AT_INPUT, callback=callback)
+
+ def set_pin_mode_digital_input_pullup(self, pin_number, callback=None):
+ """
+ Set a pin as a digital input with pullup enabled.
+
+ :param pin_number: arduino pin number
+
+ :param callback: callback function
+
+
+ callback returns a data list:
+
+ [pin_type, pin_number, pin_value, raw_time_stamp]
+
+ The pin_type for all digital input pins = 2
+ """
+ self._set_pin_mode(pin_number, PrivateConstants.AT_INPUT_PULLUP,
+ callback=callback)
+
+ def set_pin_mode_digital_output(self, pin_number):
+ """
+ Set a pin as a digital output pin.
+
+ :param pin_number: arduino pin number
+ """
+
+ self._set_pin_mode(pin_number, PrivateConstants.AT_OUTPUT)
+
+ def set_pin_mode_i2c(self, i2c_port=0):
+ """
+ Establish the standard Arduino i2c pins for i2c utilization.
+
+ :param i2c_port: 0 = i2c1, 1 = i2c2
+
+ NOTES: 1. THIS METHOD MUST BE CALLED BEFORE ANY I2C REQUEST IS MADE
+ 2. Callbacks are set within the individual i2c read methods of this
+ API.
+
+ See i2c_read, or i2c_read_restart_transmission.
+
+ """
+ # test for i2c port 2
+ if i2c_port:
+ # if not previously activated set it to activated
+ # and the send a begin message for this port
+ if not self.i2c_2_active:
+ self.i2c_2_active = True
+ else:
+ return
+ # port 1
+ else:
+ if not self.i2c_1_active:
+ self.i2c_1_active = True
+ else:
+ return
+
+ command = [PrivateConstants.I2C_BEGIN, i2c_port]
+ self._send_command(command)
+
+ def set_pin_mode_dht(self, pin, callback=None, dht_type=22):
+ """
+
+ :param pin: connection pin
+
+ :param callback: callback function
+
+ :param dht_type: either 22 for DHT22 or 11 for DHT11
+
+ Error Callback: [DHT REPORT Type, DHT_ERROR_NUMBER, PIN, DHT_TYPE, Time]
+
+ Valid Data Callback: DHT REPORT Type, DHT_DATA=, PIN, DHT_TYPE, Humidity,
+ Temperature,
+ Time]
+
+ DHT_REPORT_TYPE = 12
+ """
+ if self.reported_features & PrivateConstants.DHT_FEATURE:
+ if not callback:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError('set_pin_mode_dht: A Callback must be specified')
+
+ if self.dht_count < PrivateConstants.MAX_DHTS - 1:
+ self.dht_callbacks[pin] = callback
+ self.dht_count += 1
+
+ if dht_type != 22 and dht_type != 11:
+ dht_type = 22
+
+ command = [PrivateConstants.DHT_NEW, pin, dht_type]
+ self._send_command(command)
+ else:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(
+ f'Maximum Number Of DHTs Exceeded - set_pin_mode_dht fails for pin {pin}')
+ else:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(f'The DHT feature is disabled in the server.')
+
+ # noinspection PyRedundantParentheses
+ def set_pin_mode_servo(self, pin_number, min_pulse=544, max_pulse=2400):
+ """
+
+ Attach a pin to a servo motor
+
+ :param pin_number: pin
+
+ :param min_pulse: minimum pulse width
+
+ :param max_pulse: maximum pulse width
+
+ """
+ if self.reported_features & PrivateConstants.SERVO_FEATURE:
+
+ minv = (min_pulse).to_bytes(2, byteorder="big")
+ maxv = (max_pulse).to_bytes(2, byteorder="big")
+
+ command = [PrivateConstants.SERVO_ATTACH, pin_number,
+ minv[0], minv[1], maxv[0], maxv[1]]
+ self._send_command(command)
+ else:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(f'The SERVO feature is disabled in the server.')
+
+ def set_pin_mode_sonar(self, trigger_pin, echo_pin,
+ callback=None):
+ """
+
+ :param trigger_pin:
+
+ :param echo_pin:
+
+ :param callback: callback
+
+ callback data: [PrivateConstants.SONAR_DISTANCE, trigger_pin, distance_value, time_stamp]
+
+ """
+ if self.reported_features & PrivateConstants.SONAR_FEATURE:
+
+ if not callback:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError('set_pin_mode_sonar: A Callback must be specified')
+
+ if self.sonar_count < PrivateConstants.MAX_SONARS - 1:
+ self.sonar_callbacks[trigger_pin] = callback
+ self.sonar_count += 1
+
+ command = [PrivateConstants.SONAR_NEW, trigger_pin, echo_pin]
+ self._send_command(command)
+ else:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(
+ f'Maximum Number Of Sonars Exceeded - set_pin_mode_sonar fails for pin {trigger_pin}')
+ else:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(f'The SONAR feature is disabled in the server.')
+
+ def set_pin_mode_spi(self, chip_select_list=None):
+ """
+ Specify the list of chip select pins.
+
+ Standard Arduino MISO, MOSI and CLK pins are used for the board in use.
+
+ Chip Select is any digital output capable pin.
+
+ :param chip_select_list: this is a list of pins to be used for chip select.
+ The pins will be configured as output, and set to high
+ ready to be used for chip select.
+ NOTE: You must specify the chips select pins here!
+
+
+ command message: [command, [cs pins...]]
+ """
+
+ if self.reported_features & PrivateConstants.SPI_FEATURE:
+ if type(chip_select_list) != list:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError('chip_select_list must be in the form of a list')
+ if not chip_select_list:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError('Chip select pins were not specified')
+
+ self.spi_enabled = True
+
+ command = [PrivateConstants.SPI_INIT, len(chip_select_list)]
+
+ for pin in chip_select_list:
+ command.append(pin)
+ self.cs_pins_enabled.append(pin)
+ self._send_command(command)
+ else:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(f'The SPI feature is disabled in the server.')
+
+ # def set_pin_mode_stepper(self, interface=1, pin1=2, pin2=3, pin3=4,
+ # pin4=5, enable=True):
+ # """
+ # Stepper motor support is implemented as a proxy for the
+ # the AccelStepper library for the Arduino.
+ #
+ # This feature is compatible with the TB6600 Motor Driver
+ #
+ # Note: It may not work for other driver types!
+ #
+ # https://github.com/waspinator/AccelStepper
+ #
+ # Instantiate a stepper motor.
+ #
+ # Initialize the interface and pins for a stepper motor.
+ #
+ # :param interface: Motor Interface Type:
+ #
+ # 1 = Stepper Driver, 2 driver pins required
+ #
+ # 2 = FULL2WIRE 2 wire stepper, 2 motor pins required
+ #
+ # 3 = FULL3WIRE 3 wire stepper, such as HDD spindle,
+ # 3 motor pins required
+ #
+ # 4 = FULL4WIRE, 4 wire full stepper, 4 motor pins
+ # required
+ #
+ # 6 = HALF3WIRE, 3 wire half stepper, such as HDD spindle,
+ # 3 motor pins required
+ #
+ # 8 = HALF4WIRE, 4 wire half stepper, 4 motor pins required
+ #
+ # :param pin1: Arduino digital pin number for motor pin 1
+ #
+ # :param pin2: Arduino digital pin number for motor pin 2
+ #
+ # :param pin3: Arduino digital pin number for motor pin 3
+ #
+ # :param pin4: Arduino digital pin number for motor pin 4
+ #
+ # :param enable: If this is true, the output pins at construction time.
+ #
+ # :return: Motor Reference number
+ # """
+ # if self.reported_features & PrivateConstants.STEPPERS_FEATURE:
+ #
+ # if self.number_of_steppers == self.max_number_of_steppers:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('Maximum number of steppers has already been assigned')
+ #
+ # if interface not in self.valid_stepper_interfaces:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('Invalid stepper interface')
+ #
+ # self.number_of_steppers += 1
+ #
+ # motor_id = self.next_stepper_assigned
+ # self.next_stepper_assigned += 1
+ # self.stepper_info_list[motor_id]['instance'] = True
+ #
+ # # build message and send message to server
+ # command = [PrivateConstants.SET_PIN_MODE_STEPPER, motor_id, interface, pin1,
+ # pin2, pin3, pin4, enable]
+ # self._send_command(command)
+ #
+ # # return motor id
+ # return motor_id
+ # else:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError(f'The Stepper feature is disabled in the server.')
+
+ def servo_write(self, pin_number, angle):
+ """
+
+ Set a servo attached to a pin to a given angle.
+
+ :param pin_number: pin
+
+ :param angle: angle (0-180)
+
+ """
+ command = [PrivateConstants.SERVO_WRITE, pin_number, angle]
+ self._send_command(command)
+
+ def servo_detach(self, pin_number):
+ """
+ Detach a servo for reuse
+
+ :param pin_number: attached pin
+
+ """
+ command = [PrivateConstants.SERVO_DETACH, pin_number]
+ self._send_command(command)
+
+ # def stepper_move_to(self, motor_id, position):
+ # """
+ # Set an absolution target position. If position is positive, the movement is
+ # clockwise, else it is counter-clockwise.
+ #
+ # The run() function (below) will try to move the motor (at most one step per call)
+ # from the current position to the target position set by the most
+ # recent call to this function. Caution: moveTo() also recalculates the
+ # speed for the next step.
+ # If you are trying to use constant speed movements, you should call setSpeed()
+ # after calling moveTo().
+ #
+ # :param motor_id: motor id: 0 - 3
+ #
+ # :param position: target position. Maximum value is 32 bits.
+ # """
+ # if position < 0:
+ # polarity = 1
+ # else:
+ # polarity = 0
+ # position = abs(position)
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_move_to: Invalid motor_id.')
+ #
+ # position_bytes = list(position.to_bytes(4, 'big', signed=True))
+ #
+ # command = [PrivateConstants.STEPPER_MOVE_TO, motor_id]
+ # for value in position_bytes:
+ # command.append(value)
+ # command.append(polarity)
+ # self._send_command(command)
+ #
+ # def stepper_move(self, motor_id, relative_position):
+ # """
+ # Set the target position relative to the current position.
+ #
+ # :param motor_id: motor id: 0 - 3
+ #
+ # :param relative_position: The desired position relative to the current
+ # position. Negative is anticlockwise from
+ # the current position. Maximum value is 32 bits.
+ # """
+ # if relative_position < 0:
+ # polarity = 1
+ # else:
+ # polarity = 0
+ #
+ # relative_position = abs(relative_position)
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_move: Invalid motor_id.')
+ #
+ # position_bytes = list(relative_position.to_bytes(4, 'big', signed=True))
+ #
+ # command = [PrivateConstants.STEPPER_MOVE, motor_id]
+ # for value in position_bytes:
+ # command.append(value)
+ # command.append(polarity)
+ # self._send_command(command)
+ #
+ # def stepper_run(self, motor_id, completion_callback=None):
+ # """
+ # This method steps the selected motor based on the current speed.
+ #
+ # Once called, the server will continuously attempt to step the motor.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param completion_callback: call back function to receive motion complete
+ # notification
+ #
+ # callback returns a data list:
+ #
+ # [report_type, motor_id, raw_time_stamp]
+ #
+ # The report_type = 19
+ # """
+ # if not completion_callback:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_run: A motion complete callback must be '
+ # 'specified.')
+ #
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_run: Invalid motor_id.')
+ #
+ # self.stepper_info_list[motor_id]['motion_complete_callback'] = completion_callback
+ # command = [PrivateConstants.STEPPER_RUN, motor_id]
+ # self._send_command(command)
+ #
+ # def stepper_run_speed(self, motor_id):
+ # """
+ # This method steps the selected motor based at a constant speed as set by the most
+ # recent call to stepper_set_max_speed(). The motor will run continuously.
+ #
+ # Once called, the server will continuously attempt to step the motor.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_run_speed: Invalid motor_id.')
+ #
+ # command = [PrivateConstants.STEPPER_RUN_SPEED, motor_id]
+ # self._send_command(command)
+ #
+ # def stepper_set_max_speed(self, motor_id, max_speed):
+ # """
+ # Sets the maximum permitted speed. The stepper_run() function will accelerate
+ # up to the speed set by this function.
+ #
+ # Caution: the maximum speed achievable depends on your processor and clock speed.
+ # The default maxSpeed is 1 step per second.
+ #
+ # Caution: Speeds that exceed the maximum speed supported by the processor may
+ # result in non-linear accelerations and decelerations.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param max_speed: 1 - 1000
+ # """
+ #
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_set_max_speed: Invalid motor_id.')
+ #
+ # if not 1 < max_speed <= 1000:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_set_max_speed: Speed range is 1 - 1000.')
+ #
+ # self.stepper_info_list[motor_id]['max_speed'] = max_speed
+ # max_speed_msb = (max_speed & 0xff00) >> 8
+ # max_speed_lsb = max_speed & 0xff
+ #
+ # command = [PrivateConstants.STEPPER_SET_MAX_SPEED, motor_id, max_speed_msb,
+ # max_speed_lsb]
+ # self._send_command(command)
+ #
+ # def stepper_get_max_speed(self, motor_id):
+ # """
+ # Returns the maximum speed configured for this stepper
+ # that was previously set by stepper_set_max_speed()
+ #
+ # Value is stored in the client, so no callback is required.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :return: The currently configured maximum speed.
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_max_speed: Invalid motor_id.')
+ #
+ # return self.stepper_info_list[motor_id]['max_speed']
+ #
+ # def stepper_set_acceleration(self, motor_id, acceleration):
+ # """
+ # Sets the acceleration/deceleration rate.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param acceleration: The desired acceleration in steps per second
+ # per second. Must be > 0.0. This is an
+ # expensive call since it requires a square
+ # root to be calculated on the server.
+ # Dont call more often than needed.
+ #
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_set_acceleration: Invalid motor_id.')
+ #
+ # if not 1 < acceleration <= 1000:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_set_acceleration: Acceleration range is 1 - '
+ # '1000.')
+ #
+ # self.stepper_info_list[motor_id]['acceleration'] = acceleration
+ #
+ # max_accel_msb = acceleration >> 8
+ # max_accel_lsb = acceleration & 0xff
+ #
+ # command = [PrivateConstants.STEPPER_SET_ACCELERATION, motor_id, max_accel_msb,
+ # max_accel_lsb]
+ # self._send_command(command)
+ #
+ # def stepper_set_speed(self, motor_id, speed):
+ # """
+ # Sets the desired constant speed for use with stepper_run_speed().
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param speed: 0 - 1000 The desired constant speed in steps per
+ # second. Positive is clockwise. Speeds of more than 1000 steps per
+ # second are unreliable. Speed accuracy depends on the Arduino
+ # crystal. Jitter depends on how frequently you call the
+ # stepper_run_speed() method.
+ # The speed will be limited by the current value of
+ # stepper_set_max_speed().
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_set_speed: Invalid motor_id.')
+ #
+ # if not 0 < speed <= 1000:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_set_speed: Speed range is 0 - '
+ # '1000.')
+ #
+ # self.stepper_info_list[motor_id]['speed'] = speed
+ #
+ # speed_msb = speed >> 8
+ # speed_lsb = speed & 0xff
+ #
+ # command = [PrivateConstants.STEPPER_SET_SPEED, motor_id, speed_msb, speed_lsb]
+ # self._send_command(command)
+ #
+ # def stepper_get_speed(self, motor_id):
+ # """
+ # Returns the most recently set speed.
+ # that was previously set by stepper_set_speed();
+ #
+ # Value is stored in the client, so no callback is required.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_get_speed: Invalid motor_id.')
+ #
+ # return self.stepper_info_list[motor_id]['speed']
+ #
+ # def stepper_get_distance_to_go(self, motor_id, distance_to_go_callback):
+ # """
+ # Request the distance from the current position to the target position
+ # from the server.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param distance_to_go_callback: required callback function to receive report
+ #
+ # :return: The distance to go is returned via the callback as a list:
+ #
+ # [REPORT_TYPE=15, motor_id, distance in steps, time_stamp]
+ #
+ # A positive distance is clockwise from the current position.
+ #
+ # """
+ # if not distance_to_go_callback:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_get_distance_to_go Read: A callback function must be specified.')
+ #
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_get_distance_to_go: Invalid motor_id.')
+ # self.stepper_info_list[motor_id][
+ # 'distance_to_go_callback'] = distance_to_go_callback
+ # command = [PrivateConstants.STEPPER_GET_DISTANCE_TO_GO, motor_id]
+ # self._send_command(command)
+ #
+ # def stepper_get_target_position(self, motor_id, target_callback):
+ # """
+ # Request the most recently set target position from the server.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param target_callback: required callback function to receive report
+ #
+ # :return: The distance to go is returned via the callback as a list:
+ #
+ # [REPORT_TYPE=16, motor_id, target position in steps, time_stamp]
+ #
+ # Positive is clockwise from the 0 position.
+ #
+ # """
+ # if not target_callback:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError(
+ # 'stepper_get_target_position Read: A callback function must be specified.')
+ #
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_get_target_position: Invalid motor_id.')
+ #
+ # self.stepper_info_list[motor_id][
+ # 'target_position_callback'] = target_callback
+ #
+ # command = [PrivateConstants.STEPPER_GET_TARGET_POSITION, motor_id]
+ # self._send_command(command)
+ #
+ # def stepper_get_current_position(self, motor_id, current_position_callback):
+ # """
+ # Request the current motor position from the server.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param current_position_callback: required callback function to receive report
+ #
+ # :return: The current motor position returned via the callback as a list:
+ #
+ # [REPORT_TYPE=17, motor_id, current position in steps, time_stamp]
+ #
+ # Positive is clockwise from the 0 position.
+ # """
+ # if not current_position_callback:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError(
+ # 'stepper_get_current_position Read: A callback function must be specified.')
+ #
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_get_current_position: Invalid motor_id.')
+ #
+ # self.stepper_info_list[motor_id]['current_position_callback'] = current_position_callback
+ #
+ # command = [PrivateConstants.STEPPER_GET_CURRENT_POSITION, motor_id]
+ # self._send_command(command)
+ #
+ # def stepper_set_current_position(self, motor_id, position):
+ # """
+ # Resets the current position of the motor, so that wherever the motor
+ # happens to be right now is considered to be the new 0 position. Useful
+ # for setting a zero position on a stepper after an initial hardware
+ # positioning move.
+ #
+ # Has the side effect of setting the current motor speed to 0.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param position: Position in steps. This is a 32 bit value
+ # """
+ #
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_set_current_position: Invalid motor_id.')
+ # position_bytes = list(position.to_bytes(4, 'big', signed=True))
+ #
+ # command = [PrivateConstants.STEPPER_SET_CURRENT_POSITION, motor_id]
+ # for value in position_bytes:
+ # command.append(value)
+ # self._send_command(command)
+ #
+ # def stepper_run_speed_to_position(self, motor_id, completion_callback=None):
+ # """
+ # Runs the motor at the currently selected speed until the target position is
+ # reached.
+ #
+ # Does not implement accelerations.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param completion_callback: call back function to receive motion complete
+ # notification
+ #
+ # callback returns a data list:
+ #
+ # [report_type, motor_id, raw_time_stamp]
+ #
+ # The report_type = 19
+ # """
+ # if not completion_callback:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_run_speed_to_position: A motion complete '
+ # 'callback must be '
+ # 'specified.')
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_run_speed_to_position: Invalid motor_id.')
+ #
+ # self.stepper_info_list[motor_id]['motion_complete_callback'] = completion_callback
+ # command = [PrivateConstants.STEPPER_RUN_SPEED_TO_POSITION, motor_id]
+ # self._send_command(command)
+ #
+ # def stepper_stop(self, motor_id):
+ # """
+ # Sets a new target position that causes the stepper
+ # to stop as quickly as possible, using the current speed and
+ # acceleration parameters.
+ #
+ # :param motor_id: 0 - 3
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_stop: Invalid motor_id.')
+ #
+ # command = [PrivateConstants.STEPPER_STOP, motor_id]
+ # self._send_command(command)
+ #
+ # def stepper_disable_outputs(self, motor_id):
+ # """
+ # Disable motor pin outputs by setting them all LOW.
+ #
+ # Depending on the design of your electronics this may turn off
+ # the power to the motor coils, saving power.
+ #
+ # This is useful to support Arduino low power modes: disable the outputs
+ # during sleep and then re-enable with enableOutputs() before stepping
+ # again.
+ #
+ # If the enable Pin is defined, sets it to OUTPUT mode and clears
+ # the pin to disabled.
+ #
+ # :param motor_id: 0 - 3
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_disable_outputs: Invalid motor_id.')
+ #
+ # command = [PrivateConstants.STEPPER_DISABLE_OUTPUTS, motor_id]
+ # self._send_command(command)
+ #
+ # def stepper_enable_outputs(self, motor_id):
+ # """
+ # Enable motor pin outputs by setting the motor pins to OUTPUT
+ # mode.
+ #
+ # If the enable Pin is defined, sets it to OUTPUT mode and sets
+ # the pin to enabled.
+ #
+ # :param motor_id: 0 - 3
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_enable_outputs: Invalid motor_id.')
+ #
+ # command = [PrivateConstants.STEPPER_ENABLE_OUTPUTS, motor_id]
+ # self._send_command(command)
+ #
+ # def stepper_set_min_pulse_width(self, motor_id, minimum_width):
+ # """
+ # Sets the minimum pulse width allowed by the stepper driver.
+ #
+ # The minimum practical pulse width is approximately 20 microseconds.
+ #
+ # Times less than 20 microseconds will usually result in 20 microseconds or so.
+ #
+ # :param motor_id: 0 -3
+ #
+ # :param minimum_width: A 16 bit unsigned value expressed in microseconds.
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_set_min_pulse_width: Invalid motor_id.')
+ #
+ # if not 0 < minimum_width <= 0xff:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_set_min_pulse_width: Pulse width range = '
+ # '0-0xffff.')
+ #
+ # width_msb = minimum_width >> 8
+ # width_lsb = minimum_width & 0xff
+ #
+ # command = [PrivateConstants.STEPPER_SET_MINIMUM_PULSE_WIDTH, motor_id, width_msb,
+ # width_lsb]
+ # self._send_command(command)
+ #
+ # def stepper_set_enable_pin(self, motor_id, pin=0xff):
+ # """
+ # Sets the enable pin number for stepper drivers.
+ # 0xFF indicates unused (default).
+ #
+ # Otherwise, if a pin is set, the pin will be turned on when
+ # enableOutputs() is called and switched off when disableOutputs()
+ # is called.
+ #
+ # :param motor_id: 0 - 4
+ # :param pin: 0-0xff
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_set_enable_pin: Invalid motor_id.')
+ #
+ # if not 0 < pin <= 0xff:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_set_enable_pin: Pulse width range = '
+ # '0-0xff.')
+ # command = [PrivateConstants.STEPPER_SET_ENABLE_PIN, motor_id, pin]
+ #
+ # self._send_command(command)
+ #
+ # def stepper_set_3_pins_inverted(self, motor_id, direction=False, step=False,
+ # enable=False):
+ # """
+ # Sets the inversion for stepper driver pins.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param direction: True=inverted or False
+ #
+ # :param step: True=inverted or False
+ #
+ # :param enable: True=inverted or False
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_set_3_pins_inverted: Invalid motor_id.')
+ #
+ # command = [PrivateConstants.STEPPER_SET_3_PINS_INVERTED, motor_id, direction,
+ # step, enable]
+ #
+ # self._send_command(command)
+ #
+ # def stepper_set_4_pins_inverted(self, motor_id, pin1_invert=False, pin2_invert=False,
+ # pin3_invert=False, pin4_invert=False, enable=False):
+ # """
+ # Sets the inversion for 2, 3 and 4 wire stepper pins
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param pin1_invert: True=inverted or False
+ #
+ # :param pin2_invert: True=inverted or False
+ #
+ # :param pin3_invert: True=inverted or False
+ #
+ # :param pin4_invert: True=inverted or False
+ #
+ # :param enable: True=inverted or False
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_set_4_pins_inverted: Invalid motor_id.')
+ #
+ # command = [PrivateConstants.STEPPER_SET_4_PINS_INVERTED, motor_id, pin1_invert,
+ # pin2_invert, pin3_invert, pin4_invert, enable]
+ #
+ # self._send_command(command)
+ #
+ # def stepper_is_running(self, motor_id, callback):
+ # """
+ # Checks to see if the motor is currently running to a target.
+ #
+ # Callback return True if the speed is not zero or not at the target position.
+ #
+ # :param motor_id: 0-4
+ #
+ # :param callback: required callback function to receive report
+ #
+ # :return: The current running state returned via the callback as a list:
+ #
+ # [REPORT_TYPE=18, motor_id, True or False for running state, time_stamp]
+ # """
+ # if not callback:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError(
+ # 'stepper_is_running: A callback function must be specified.')
+ #
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('stepper_is_running: Invalid motor_id.')
+ #
+ # self.stepper_info_list[motor_id]['is_running_callback'] = callback
+ #
+ # command = [PrivateConstants.STEPPER_IS_RUNNING, motor_id]
+ # self._send_command(command)
+
+ def _set_pin_mode(self, pin_number, pin_state, differential=0, callback=None):
+ """
+ A private method to set the various pin modes.
+
+ :param pin_number: arduino pin number
+
+ :param pin_state: INPUT/OUTPUT/ANALOG/PWM/PULLUP
+ For SERVO use: set_pin_mode_servo
+ For DHT use: set_pin_mode_dht
+
+ :param differential: for analog inputs - threshold
+ value to be achieved for report to
+ be generated
+
+ :param callback: A reference to a call back function to be
+ called when pin data value changes
+
+ """
+ if callback:
+ if pin_state == PrivateConstants.AT_INPUT:
+ self.digital_callbacks[pin_number] = callback
+ elif pin_state == PrivateConstants.AT_INPUT_PULLUP:
+ self.digital_callbacks[pin_number] = callback
+ elif pin_state == PrivateConstants.AT_ANALOG:
+ self.analog_callbacks[pin_number] = callback
+ else:
+ print('{} {}'.format('set_pin_mode: callback ignored for '
+ 'pin state:', pin_state))
+
+ if pin_state == PrivateConstants.AT_INPUT:
+ command = [PrivateConstants.SET_PIN_MODE, pin_number,
+ PrivateConstants.AT_INPUT, 1]
+
+ elif pin_state == PrivateConstants.AT_INPUT_PULLUP:
+ command = [PrivateConstants.SET_PIN_MODE, pin_number,
+ PrivateConstants.AT_INPUT_PULLUP, 1]
+
+ elif pin_state == PrivateConstants.AT_OUTPUT:
+ command = [PrivateConstants.SET_PIN_MODE, pin_number,
+ PrivateConstants.AT_OUTPUT]
+
+ elif pin_state == PrivateConstants.AT_ANALOG:
+ command = [PrivateConstants.SET_PIN_MODE, pin_number,
+ PrivateConstants.AT_ANALOG,
+ differential >> 8, differential & 0xff, 1]
+ else:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError('Unknown pin state')
+
+ if command:
+ self._send_command(command)
+
+ def shutdown(self):
+ """
+ This method attempts an orderly shutdown
+ If any exceptions are thrown, they are ignored.
+ """
+ self.shutdown_flag = True
+
+ self._stop_threads()
+
+ try:
+ command = [PrivateConstants.STOP_ALL_REPORTS]
+ self._send_command(command)
+ time.sleep(.5)
+
+ if self.hard_reset_on_shutdown:
+ self.r4_hard_reset()
+
+ if self.transport_address:
+ try:
+ self.sock.shutdown(socket.SHUT_RDWR)
+ self.sock.close()
+ except Exception:
+ pass
+ else:
+ try:
+ self.serial_port.reset_input_buffer()
+ self.serial_port.reset_output_buffer()
+
+ self.serial_port.close()
+
+ except (RuntimeError, SerialException, OSError):
+ # ignore error on shutdown
+ pass
+ except Exception:
+ # raise RuntimeError('Shutdown failed - could not send stop streaming
+ # message')
+ pass
+
+ def sonar_disable(self):
+ """
+ Disable sonar scanning for all sonar sensors
+ """
+ command = [PrivateConstants.SONAR_DISABLE]
+ self._send_command(command)
+
+ def sonar_enable(self):
+ """
+ Enable sonar scanning for all sonar sensors
+ """
+ command = [PrivateConstants.SONAR_ENABLE]
+ self._send_command(command)
+
+ def spi_cs_control(self, chip_select_pin, select):
+ """
+ Control an SPI chip select line
+ :param chip_select_pin: pin connected to CS
+
+ :param select: 0=select, 1=deselect
+ """
+ if not self.spi_enabled:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(f'spi_cs_control: SPI interface is not enabled.')
+
+ if chip_select_pin not in self.cs_pins_enabled:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(f'spi_cs_control: chip select pin never enabled.')
+ command = [PrivateConstants.SPI_CS_CONTROL, chip_select_pin, select]
+ self._send_command(command)
+
+ def spi_read_blocking(self, chip_select, register_selection, number_of_bytes_to_read,
+ call_back=None):
+ """
+ Read the specified number of bytes from the specified SPI port and
+ call the callback function with the reported data.
+
+ :param chip_select: chip select pin
+
+ :param register_selection: Register to be selected for read.
+
+ :param number_of_bytes_to_read: Number of bytes to read
+
+ :param call_back: Required callback function to report spi data as a
+ result of read command
+
+
+ callback returns a data list:
+ [SPI_READ_REPORT, chip select pin, SPI Register, count of data bytes read,
+ data bytes, time-stamp]
+
+ SPI_READ_REPORT = 13
+
+ """
+
+ if not self.spi_enabled:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(f'spi_read_blocking: SPI interface is not enabled.')
+
+ if not call_back:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError('spi_read_blocking: A Callback must be specified')
+
+ self.spi_callback = call_back
+
+ command = [PrivateConstants.SPI_READ_BLOCKING, chip_select,
+ number_of_bytes_to_read,
+ register_selection]
+
+ self._send_command(command)
+
+ def spi_set_format(self, clock_divisor, bit_order, data_mode):
+ """
+ Configure how the SPI serializes and de-serializes data on the wire.
+
+ See Arduino SPI reference materials for details.
+
+ :param clock_divisor: 1 - 255
+
+ :param bit_order:
+
+ LSBFIRST = 0
+
+ MSBFIRST = 1 (default)
+
+ :param data_mode:
+
+ SPI_MODE0 = 0x00 (default)
+
+ SPI_MODE1 = 1
+
+ SPI_MODE2 = 2
+
+ SPI_MODE3 = 3
+
+ """
+
+ if not self.spi_enabled:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(f'spi_set_format: SPI interface is not enabled.')
+
+ if not 0 < clock_divisor <= 255:
+ raise RuntimeError(f'spi_set_format: illegal clock divisor selected.')
+ if bit_order not in [0, 1]:
+ raise RuntimeError(f'spi_set_format: illegal bit_order selected.')
+ if data_mode not in [0, 1, 2, 3]:
+ raise RuntimeError(f'spi_set_format: illegal data_order selected.')
+
+ command = [PrivateConstants.SPI_SET_FORMAT, clock_divisor, bit_order,
+ data_mode]
+ self._send_command(command)
+
+ def spi_write_blocking(self, chip_select, bytes_to_write):
+ """
+ Write a list of bytes to the SPI device.
+
+ :param chip_select: chip select pin
+
+ :param bytes_to_write: A list of bytes to write. This must
+ be in the form of a list.
+
+ """
+
+ if not self.spi_enabled:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(f'spi_write_blocking: SPI interface is not enabled.')
+
+ if type(bytes_to_write) is not list:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError('spi_write_blocking: bytes_to_write must be a list.')
+
+ command = [PrivateConstants.SPI_WRITE_BLOCKING, chip_select, len(bytes_to_write)]
+
+ for data in bytes_to_write:
+ command.append(data)
+
+ self._send_command(command)
+
+ # def set_pin_mode_one_wire(self, pin):
+ # """
+ # Initialize the one wire serial bus.
+ #
+ # :param pin: Data pin connected to the OneWire device
+ # """
+ # if self.reported_features & PrivateConstants.ONEWIRE_FEATURE:
+ # self.onewire_enabled = True
+ # command = [PrivateConstants.ONE_WIRE_INIT, pin]
+ # self._send_command(command)
+ # else:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError(f'The OneWire feature is disabled in the server.')
+ #
+ # def onewire_reset(self, callback=None):
+ # """
+ # Reset the onewire device
+ #
+ # :param callback: required function to report reset result
+ #
+ # callback returns a list:
+ # [ReportType = 14, Report Subtype = 25, reset result byte,
+ # timestamp]
+ # """
+ # if not self.onewire_enabled:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError(f'onewire_reset: OneWire interface is not enabled.')
+ # if not callback:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('onewire_reset: A Callback must be specified')
+ #
+ # self.onewire_callback = callback
+ #
+ # command = [PrivateConstants.ONE_WIRE_RESET]
+ # self._send_command(command)
+ #
+ # def onewire_select(self, device_address):
+ # """
+ # Select a device based on its address
+ # :param device_address: A bytearray of 8 bytes
+ # """
+ # if not self.onewire_enabled:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError(f'onewire_select: OneWire interface is not enabled.')
+ #
+ # if type(device_address) is not list:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('onewire_select: device address must be an array of 8 '
+ # 'bytes.')
+ #
+ # if len(device_address) != 8:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('onewire_select: device address must be an array of 8 '
+ # 'bytes.')
+ # command = [PrivateConstants.ONE_WIRE_SELECT]
+ # for data in device_address:
+ # command.append(data)
+ # self._send_command(command)
+ #
+ # def onewire_skip(self):
+ # """
+ # Skip the device selection. This only works if you have a
+ # single device, but you can avoid searching and use this to
+ # immediately access your device.
+ # """
+ # if not self.onewire_enabled:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError(f'onewire_skip: OneWire interface is not enabled.')
+ #
+ # command = [PrivateConstants.ONE_WIRE_SKIP]
+ # self._send_command(command)
+ #
+ # def onewire_write(self, data, power=0):
+ # """
+ # Write a byte to the onewire device. If 'power' is one
+ # then the wire is held high at the end for
+ # parasitically powered devices. You
+ # are responsible for eventually de-powering it by calling
+ # another read or write.
+ #
+ # :param data: byte to write.
+ # :param power: power control (see above)
+ # """
+ # if not self.onewire_enabled:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError(f'onewire_write: OneWire interface is not enabled.')
+ # if 0 < data < 255:
+ # command = [PrivateConstants.ONE_WIRE_WRITE, data, power]
+ # self._send_command(command)
+ # else:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('onewire_write: Data must be no larger than 255')
+ #
+ # def onewire_read(self, callback=None):
+ # """
+ # Read a byte from the onewire device
+ # :param callback: required function to report onewire data as a
+ # result of read command
+ #
+ #
+ # callback returns a data list:
+ # [ONEWIRE_REPORT, ONEWIRE_READ=29, data byte, time-stamp]
+ #
+ # ONEWIRE_REPORT = 14
+ # """
+ # if not self.onewire_enabled:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError(f'onewire_read: OneWire interface is not enabled.')
+ #
+ # if not callback:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('onewire_read A Callback must be specified')
+ #
+ # self.onewire_callback = callback
+ #
+ # command = [PrivateConstants.ONE_WIRE_READ]
+ # self._send_command(command)
+ # time.sleep(.2)
+ #
+ # def onewire_reset_search(self):
+ # """
+ # Begin a new search. The next use of search will begin at the first device
+ # """
+ #
+ # if not self.onewire_enabled:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError(f'onewire_reset_search: OneWire interface is not '
+ # f'enabled.')
+ # else:
+ # command = [PrivateConstants.ONE_WIRE_RESET_SEARCH]
+ # self._send_command(command)
+ #
+ # def onewire_search(self, callback=None):
+ # """
+ # Search for the next device. The device address will returned in the callback.
+ # If a device is found, the 8 byte address is contained in the callback.
+ # If no more devices are found, the address returned contains all elements set
+ # to 0xff.
+ #
+ # :param callback: required function to report a onewire device address
+ #
+ # callback returns a data list:
+ # [ONEWIRE_REPORT, ONEWIRE_SEARCH=31, 8 byte address, time-stamp]
+ #
+ # ONEWIRE_REPORT = 14
+ # """
+ # if not self.onewire_enabled:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError(f'onewire_search: OneWire interface is not enabled.')
+ #
+ # if not callback:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('onewire_read A Callback must be specified')
+ #
+ # self.onewire_callback = callback
+ #
+ # command = [PrivateConstants.ONE_WIRE_SEARCH]
+ # self._send_command(command)
+ #
+ # def onewire_crc8(self, address_list, callback=None):
+ # """
+ # Compute a CRC check on an array of data.
+ # :param address_list:
+ #
+ # :param callback: required function to report a onewire device address
+ #
+ # callback returns a data list:
+ # [ONEWIRE_REPORT, ONEWIRE_CRC8=32, CRC, time-stamp]
+ #
+ # ONEWIRE_REPORT = 14
+ #
+ # """
+ #
+ # if not self.onewire_enabled:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError(f'onewire_crc8: OneWire interface is not enabled.')
+ #
+ # if not callback:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('onewire_crc8 A Callback must be specified')
+ #
+ # if type(address_list) is not list:
+ # if self.shutdown_on_exception:
+ # self.shutdown()
+ # raise RuntimeError('onewire_crc8: address list must be a list.')
+ #
+ # self.onewire_callback = callback
+ #
+ # address_length = len(address_list)
+ #
+ # command = [PrivateConstants.ONE_WIRE_CRC8, address_length - 1]
+ #
+ # for data in address_list:
+ # command.append(data)
+ #
+ # self._send_command(command)
+
+ def r4_hard_reset(self):
+ """
+ Place the r4 into hard reset
+ """
+ command = [PrivateConstants.RESET, 1]
+ self._send_command(command)
+ time.sleep(.5)
+ command = [PrivateConstants.BOARD_HARD_RESET, 1]
+ self._send_command(command)
+
+ def enable_scroll_message(self, message, scroll_speed=50):
+ """
+
+ :param message: Message with maximum length of 25
+ :param scroll_speed: in milliseconds (maximum of 255)
+ """
+ if len(message) > 25:
+ raise RuntimeError("Scroll message size is maximum of 25 characters.")
+
+ if scroll_speed > 255:
+ raise RuntimeError("Scroll speed maximum of 255 milliseconds.")
+
+ message = message.encode()
+ command = [PrivateConstants.SCROLL_MESSAGE_ON, len(message), scroll_speed]
+ for x in message:
+ command.append(x)
+ self._send_command(command)
+
+ def disable_scroll_message(self):
+ """
+ Turn off a scrolling message
+ """
+
+ command = [PrivateConstants.SCROLL_MESSAGE_OFF]
+ self._send_command(command)
+
+ '''
+ report message handlers
+ '''
+
+ def _analog_message(self, data):
+ """
+ This is a private message handler method.
+ It is a message handler for analog messages.
+
+ :param data: message data
+
+ """
+ pin = data[0]
+ value = (data[1] << 8) + data[2]
+ # set the current value in the pin structure
+ time_stamp = time.time()
+ # self.digital_pins[pin].event_time = time_stamp
+ if self.analog_callbacks[pin]:
+ message = [PrivateConstants.ANALOG_REPORT, pin, value, time_stamp]
+ try:
+ self.analog_callbacks[pin](message)
+ except KeyError:
+ pass
+
+ def _dht_report(self, data):
+ """
+ This is the dht report handler method.
+
+ :param data: data[0] = report error return
+ No Errors = 0
+
+ Checksum Error = 1
+
+ Timeout Error = 2
+
+ Invalid Value = 999
+
+ data[1] = pin number
+
+ data[2] = dht type 11 or 22
+
+ data[3] = humidity positivity flag
+
+ data[4] = temperature positivity value
+
+ data[5] = humidity integer
+
+ data[6] = humidity fractional value
+
+ data[7] = temperature integer
+
+ data[8] = temperature fractional value
+
+
+ """
+ if data[0]: # DHT_ERROR
+ # error report
+ # data[0] = report sub type, data[1] = pin, data[2] = error message
+ if self.dht_callbacks[data[1]]:
+ # Callback 0=DHT REPORT, DHT_ERROR, PIN, Time
+ message = [PrivateConstants.DHT_REPORT, data[0], data[1], data[2],
+ time.time()]
+ try:
+ self.dht_callbacks[data[1]](message)
+ except KeyError:
+ pass
+ else:
+ # got valid data DHT_DATA
+ f_humidity = float(data[5] + data[6] / 100)
+ if data[3]:
+ f_humidity *= -1.0
+ f_temperature = float(data[7] + data[8] / 100)
+ if data[4]:
+ f_temperature *= -1.0
+ message = [PrivateConstants.DHT_REPORT, data[0], data[1], data[2],
+ f_humidity, f_temperature, time.time()]
+
+ try:
+ self.dht_callbacks[data[1]](message)
+ except KeyError:
+ pass
+
+ def _digital_message(self, data):
+ """
+ This is a private message handler method.
+ It is a message handler for Digital Messages.
+
+ :param data: digital message
+
+ """
+ pin = data[0]
+ value = data[1]
+
+ time_stamp = time.time()
+ if self.digital_callbacks[pin]:
+ message = [PrivateConstants.DIGITAL_REPORT, pin, value, time_stamp]
+ self.digital_callbacks[pin](message)
+
+ def _firmware_message(self, data):
+ """
+ Telemetrix4Arduino firmware version message
+
+ :param data: data[0] = major number, data[1] = minor number.
+
+ data[2] = patch number
+ """
+
+ self.firmware_version = [data[0], data[1], data[2]]
+
+ def _i2c_read_report(self, data):
+ """
+ Execute callback for i2c reads.
+
+ :param data: [I2C_READ_REPORT, i2c_port, number of bytes read, address, register, bytes read..., time-stamp]
+ """
+
+ # we receive [# data bytes, address, register, data bytes]
+ # number of bytes of data returned
+
+ # data[0] = number of bytes
+ # data[1] = i2c_port
+ # data[2] = number of bytes returned
+ # data[3] = address
+ # data[4] = register
+ # data[5] ... all the data bytes
+
+ cb_list = [PrivateConstants.I2C_READ_REPORT, data[0], data[1]] + data[2:]
+ cb_list.append(time.time())
+
+ if cb_list[1]:
+ self.i2c_callback2(cb_list)
+ else:
+ self.i2c_callback(cb_list)
+
+ def _i2c_too_few(self, data):
+ """
+ I2c reports too few bytes received
+
+ :param data: data[0] = device address
+ """
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(
+ f'i2c too few bytes received from i2c port {data[0]} i2c address {data[1]}')
+
+ def _i2c_too_many(self, data):
+ """
+ I2c reports too few bytes received
+
+ :param data: data[0] = device address
+ """
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(
+ f'i2c too many bytes received from i2c port {data[0]} i2c address {data[1]}')
+
+ def _i_am_here(self, data):
+ """
+ Reply to are_u_there message
+ :param data: arduino id
+ """
+ self.reported_arduino_id = data[0]
+
+ def _spi_report(self, report):
+
+ cb_list = [PrivateConstants.SPI_REPORT, report[0]] + report[1:]
+
+ cb_list.append(time.time())
+
+ self.spi_callback(cb_list)
+
+ def _onewire_report(self, report):
+ cb_list = [PrivateConstants.ONE_WIRE_REPORT, report[0]] + report[1:]
+ cb_list.append(time.time())
+ self.onewire_callback(cb_list)
+
+ def _report_debug_data(self, data):
+ """
+ Print debug data sent from Arduino
+ :param data: data[0] is a byte followed by 2
+ bytes that comprise an integer
+ :return:
+ """
+ value = (data[1] << 8) + data[2]
+ print(f'DEBUG ID: {data[0]} Value: {value}')
+
+ def _report_loop_data(self, data):
+ """
+ Print data that was looped back
+ :param data: byte of loop back data
+ :return:
+ """
+ if self.loop_back_callback:
+ self.loop_back_callback(data)
+
+ def _send_command(self, command):
+ """
+ This is a private utility method.
+
+
+ :param command: command data in the form of a list
+
+ """
+ # the length of the list is added at the head
+ command.insert(0, len(command))
+ send_message = bytes(command)
+
+ if self.serial_port:
+ try:
+ self.serial_port.write(send_message)
+ except SerialException:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError('write fail in _send_command')
+ elif self.transport_address:
+ self.sock.sendall(send_message)
+ else:
+ raise RuntimeError('No serial port or ip address set.')
+
+ def _servo_unavailable(self, report):
+ """
+ Message if no servos are available for use.
+ :param report: pin number
+ """
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(
+ f'Servo Attach For Pin {report[0]} Failed: No Available Servos')
+
+ def _sonar_distance_report(self, report):
+ """
+
+ :param report: data[0] = trigger pin, data[1] and data[2] = distance
+
+ callback report format: [PrivateConstants.SONAR_DISTANCE, trigger_pin, distance_value, time_stamp]
+ """
+
+ # get callback from pin number
+ cb = self.sonar_callbacks[report[0]]
+
+ # build report data
+ cb_list = [PrivateConstants.SONAR_DISTANCE, report[0],
+ ((report[1] << 8) + report[2]), time.time()]
+
+ cb(cb_list)
+
+ def _stepper_distance_to_go_report(self, report):
+ return # for now
+ # """
+ # Report stepper distance to go.
+ #
+ # :param report: data[0] = motor_id, data[1] = steps MSB, data[2] = steps byte 1,
+ # data[3] = steps bytes 2, data[4] = steps LSB
+ #
+ # callback report format: [PrivateConstants.STEPPER_DISTANCE_TO_GO, motor_id
+ # steps, time_stamp]
+ # """
+ #
+ # # get callback
+ # cb = self.stepper_info_list[report[0]]['distance_to_go_callback']
+ #
+ # # isolate the steps bytes and covert list to bytes
+ # steps = bytes(report[1:])
+ #
+ # # get value from steps
+ # num_steps = int.from_bytes(steps, byteorder='big', signed=True)
+ #
+ # cb_list = [PrivateConstants.STEPPER_DISTANCE_TO_GO, report[0], num_steps,
+ # time.time()]
+ #
+ # cb(cb_list)
+ #
+ def _stepper_target_position_report(self, report):
+ return # for now
+ # """
+ # Report stepper target position to go.
+ #
+ # :param report: data[0] = motor_id, data[1] = target position MSB,
+ # data[2] = target position byte MSB+1
+ # data[3] = target position byte MSB+2
+ # data[4] = target position LSB
+ #
+ # callback report format: [PrivateConstants.STEPPER_TARGET_POSITION, motor_id
+ # target_position, time_stamp]
+ # """
+ #
+ # # get callback
+ # cb = self.stepper_info_list[report[0]]['target_position_callback']
+ #
+ # # isolate the steps bytes and covert list to bytes
+ # target = bytes(report[1:])
+ #
+ # # get value from steps
+ # target_position = int.from_bytes(target, byteorder='big', signed=True)
+ #
+ # cb_list = [PrivateConstants.STEPPER_TARGET_POSITION, report[0], target_position,
+ # time.time()]
+ #
+ # cb(cb_list)
+ #
+ def _stepper_current_position_report(self, report):
+ return # for now
+ # """
+ # Report stepper current position.
+ #
+ # :param report: data[0] = motor_id, data[1] = current position MSB,
+ # data[2] = current position byte MSB+1
+ # data[3] = current position byte MSB+2
+ # data[4] = current position LSB
+ #
+ # callback report format: [PrivateConstants.STEPPER_CURRENT_POSITION, motor_id
+ # current_position, time_stamp]
+ # """
+ #
+ # # get callback
+ # cb = self.stepper_info_list[report[0]]['current_position_callback']
+ #
+ # # isolate the steps bytes and covert list to bytes
+ # position = bytes(report[1:])
+ #
+ # # get value from steps
+ # current_position = int.from_bytes(position, byteorder='big', signed=True)
+ #
+ # cb_list = [PrivateConstants.STEPPER_CURRENT_POSITION, report[0], current_position,
+ # time.time()]
+ #
+ # cb(cb_list)
+ #
+ def _stepper_is_running_report(self, report):
+ return # for now
+ # """
+ # Report if the motor is currently running
+ #
+ # :param report: data[0] = motor_id, True if motor is running or False if it is not.
+ #
+ # callback report format: [18, motor_id,
+ # running_state, time_stamp]
+ # """
+ #
+ # # get callback
+ # cb = self.stepper_info_list[report[0]]['is_running_callback']
+ #
+ # cb_list = [PrivateConstants.STEPPER_RUNNING_REPORT, report[0], time.time()]
+ #
+ # cb(cb_list)
+ #
+ def _stepper_run_complete_report(self, report):
+ return # for now
+ # """
+ # The motor completed it motion
+ #
+ # :param report: data[0] = motor_id
+ #
+ # callback report format: [PrivateConstants.STEPPER_RUN_COMPLETE_REPORT, motor_id,
+ # time_stamp]
+ # """
+ #
+ # # get callback
+ # cb = self.stepper_info_list[report[0]]['motion_complete_callback']
+ #
+ # cb_list = [PrivateConstants.STEPPER_RUN_COMPLETE_REPORT, report[0],
+ # time.time()]
+ #
+ # cb(cb_list)
+
+ def _features_report(self, report):
+ self.reported_features = report[0]
+
+ def _run_threads(self):
+ self.run_event.set()
+
+ def _is_running(self):
+ return self.run_event.is_set()
+
+ def _stop_threads(self):
+ self.run_event.clear()
+
+ def _reporter(self):
+ """
+ This is the reporter thread. It continuously pulls data from
+ the deque. When a full message is detected, that message is
+ processed.
+ """
+ self.run_event.wait()
+
+ while self._is_running() and not self.shutdown_flag:
+ if len(self.the_deque):
+ # response_data will be populated with the received data for the report
+ response_data = []
+ packet_length = self.the_deque.popleft()
+ # print(f'packet_length {packet_length}')
+ if packet_length:
+ # get all the data for the report and place it into response_data
+ for i in range(packet_length):
+ while not len(self.the_deque):
+ time.sleep(self.sleep_tune)
+ data = self.the_deque.popleft()
+ response_data.append(data)
+
+ # print(f'response_data {response_data}')
+
+ # get the report type and look up its dispatch method
+ # here we pop the report type off of response_data
+ report_type = response_data.pop(0)
+ # print(f' reported type {report_type}')
+
+ # retrieve the report handler from the dispatch table
+ dispatch_entry = self.report_dispatch.get(report_type)
+
+ # if there is additional data for the report,
+ # it will be contained in response_data
+ # noinspection PyArgumentList
+ dispatch_entry(response_data)
+ continue
+ else:
+ if self.shutdown_on_exception:
+ self.shutdown()
+ raise RuntimeError(
+ 'A report with a packet length of zero was received.')
+ else:
+ time.sleep(self.sleep_tune)
+
+ def _serial_receiver(self):
+ """
+ Thread to continuously check for incoming data.
+ When a byte comes in, place it onto the deque.
+ """
+ self.run_event.wait()
+
+ # Don't start this thread if using a tcp/ip transport
+ if self.transport_address:
+ return
+
+ while self._is_running() and not self.shutdown_flag:
+ # we can get an OSError: [Errno9] Bad file descriptor when shutting down
+ # just ignore it
+ try:
+ if self.serial_port.inWaiting():
+ c = self.serial_port.read()
+ self.the_deque.append(ord(c))
+ # print(ord(c))
+ else:
+ time.sleep(self.sleep_tune)
+ # continue
+ except OSError:
+ pass
+
+ def _tcp_receiver(self):
+ """
+ Thread to continuously check for incoming data.
+ When a byte comes in, place it onto the deque.
+ """
+ self.run_event.wait()
+
+ # Start this thread only if transport_address is set
+
+ if self.transport_address:
+
+ while self._is_running() and not self.shutdown_flag:
+ try:
+ payload = self.sock.recv(1)
+ self.the_deque.append(ord(payload))
+ except Exception:
+ pass
+ else:
+ return
diff --git a/telemetrix_uno_r4/wifi/telemetrix_uno_r4_wifi_aio/__init__.py b/telemetrix_uno_r4/wifi/telemetrix_uno_r4_wifi_aio/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/telemetrix_uno_r4/wifi/telemetrix_uno_r4_wifi_aio/private_constants.py b/telemetrix_uno_r4/wifi/telemetrix_uno_r4_wifi_aio/private_constants.py
new file mode 100644
index 0000000..b7fdbf5
--- /dev/null
+++ b/telemetrix_uno_r4/wifi/telemetrix_uno_r4_wifi_aio/private_constants.py
@@ -0,0 +1,155 @@
+"""
+ Copyright (c) 2015-2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+
+class PrivateConstants:
+ """
+ This class contains a set of constants for telemetrix internal use .
+ """
+
+ # transport types
+ WIFI_TRANSPORT = 0
+ SERIAL_TRANSPORT = 1
+ BLE_TRANSPORT = 2
+
+ # commands
+ # send a loop back request - for debugging communications
+ LOOP_COMMAND = 0
+ SET_PIN_MODE = 1 # set a pin to INPUT/OUTPUT/PWM/etc
+ DIGITAL_WRITE = 2 # set a single digital pin value instead of entire port
+ ANALOG_WRITE = 3
+ MODIFY_REPORTING = 4
+ GET_FIRMWARE_VERSION = 5
+ ARE_U_THERE = 6 # Arduino ID query for auto-detect of telemetrix connected boards
+ SERVO_ATTACH = 7
+ SERVO_WRITE = 8
+ SERVO_DETACH = 9
+ I2C_BEGIN = 10
+ I2C_READ = 11
+ I2C_WRITE = 12
+ SONAR_NEW = 13
+ DHT_NEW = 14
+ STOP_ALL_REPORTS = 15
+ SET_ANALOG_SCANNING_INTERVAL = 16
+ ENABLE_ALL_REPORTS = 17
+ RESET = 18
+ SPI_INIT = 19
+ SPI_WRITE_BLOCKING = 20
+ SPI_READ_BLOCKING = 21
+ SPI_SET_FORMAT = 22
+ SPI_CS_CONTROL = 23
+ ONE_WIRE_INIT = 24
+ ONE_WIRE_RESET = 25
+ ONE_WIRE_SELECT = 26
+ ONE_WIRE_SKIP = 27
+ ONE_WIRE_WRITE = 28
+ ONE_WIRE_READ = 29
+ ONE_WIRE_RESET_SEARCH = 30
+ ONE_WIRE_SEARCH = 31
+ ONE_WIRE_CRC8 = 32
+ SET_PIN_MODE_STEPPER = 33
+ STEPPER_MOVE_TO = 34
+ STEPPER_MOVE = 35
+ STEPPER_RUN = 36
+ STEPPER_RUN_SPEED = 37
+ STEPPER_SET_MAX_SPEED = 38
+ STEPPER_SET_ACCELERATION = 39
+ STEPPER_SET_SPEED = 40
+ STEPPER_SET_CURRENT_POSITION = 41
+ STEPPER_RUN_SPEED_TO_POSITION = 42
+ STEPPER_STOP = 43
+ STEPPER_DISABLE_OUTPUTS = 44
+ STEPPER_ENABLE_OUTPUTS = 45
+ STEPPER_SET_MINIMUM_PULSE_WIDTH = 46
+ STEPPER_SET_ENABLE_PIN = 47
+ STEPPER_SET_3_PINS_INVERTED = 48
+ STEPPER_SET_4_PINS_INVERTED = 49
+ STEPPER_IS_RUNNING = 50
+ STEPPER_GET_CURRENT_POSITION = 51
+ STEPPER_GET_DISTANCE_TO_GO = 52
+ STEPPER_GET_TARGET_POSITION = 53
+ GET_FEATURES = 54
+ SONAR_DISABLE = 55
+ SONAR_ENABLE = 56
+ BOARD_HARD_RESET = 57
+ SCROLL_MESSAGE_ON = 58
+ SCROLL_MESSAGE_OFF = 59
+
+ # reports
+ # debug data from Arduino
+ DIGITAL_REPORT = DIGITAL_WRITE
+ ANALOG_REPORT = ANALOG_WRITE
+ FIRMWARE_REPORT = GET_FIRMWARE_VERSION
+ I_AM_HERE_REPORT = ARE_U_THERE
+ SERVO_UNAVAILABLE = SERVO_ATTACH
+ I2C_TOO_FEW_BYTES_RCVD = 8
+ I2C_TOO_MANY_BYTES_RCVD = 9
+ I2C_READ_REPORT = 10
+ SONAR_DISTANCE = 11
+ DHT_REPORT = 12
+ SPI_REPORT = 13
+ ONE_WIRE_REPORT = 14
+ STEPPER_DISTANCE_TO_GO = 15
+ STEPPER_TARGET_POSITION = 16
+ STEPPER_CURRENT_POSITION = 17
+ STEPPER_RUNNING_REPORT = 18
+ STEPPER_RUN_COMPLETE_REPORT = 19
+ FEATURES = 20
+ DEBUG_PRINT = 99
+
+ TELEMETRIX_VERSION = "1.00"
+
+ # reporting control
+ REPORTING_DISABLE_ALL = 0
+ REPORTING_ANALOG_ENABLE = 1
+ REPORTING_DIGITAL_ENABLE = 2
+ REPORTING_ANALOG_DISABLE = 3
+ REPORTING_DIGITAL_DISABLE = 4
+
+ # Pin mode definitions
+ AT_INPUT = 0
+ AT_OUTPUT = 1
+ AT_INPUT_PULLUP = 2
+ AT_ANALOG = 3
+ AT_SERVO = 4
+ AT_SONAR = 5
+ AT_DHT = 6
+ AT_MODE_NOT_SET = 255
+
+ # maximum number of digital pins supported
+ NUMBER_OF_DIGITAL_PINS = 100
+
+ # maximum number of analog pins supported
+ NUMBER_OF_ANALOG_PINS = 20
+
+ # maximum number of sonars allowed
+ MAX_SONARS = 6
+
+ # maximum number of DHT devices allowed
+ MAX_DHTS = 6
+
+ # DHT Report sub-types
+ DHT_DATA = 0
+ DHT_ERROR = 1
+
+ # feature masks
+ ONEWIRE_FEATURE = 0x01
+ DHT_FEATURE = 0x02
+ STEPPERS_FEATURE = 0x04
+ SPI_FEATURE = 0x08
+ SERVO_FEATURE = 0x10
+ SONAR_FEATURE = 0x20
diff --git a/telemetrix_uno_r4/wifi/telemetrix_uno_r4_wifi_aio/telemetrix_aio_ble.py b/telemetrix_uno_r4/wifi/telemetrix_uno_r4_wifi_aio/telemetrix_aio_ble.py
new file mode 100644
index 0000000..56529dc
--- /dev/null
+++ b/telemetrix_uno_r4/wifi/telemetrix_uno_r4_wifi_aio/telemetrix_aio_ble.py
@@ -0,0 +1,72 @@
+
+"""
+ Copyright (c) 2020-2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+
+import asyncio
+import sys
+import bleak
+
+from bleak import BleakClient, BleakScanner
+from bleak.backends.characteristic import BleakGATTCharacteristic
+from bleak.backends.device import BLEDevice
+from bleak.backends.scanner import AdvertisementData
+
+UART_SERVICE_UUID = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
+UART_RX_CHAR_UUID = "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
+UART_TX_CHAR_UUID = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
+
+
+class TelemetrixAioBle:
+ """
+ This class encapsulates management of a BLE UART connection that communicates
+ with an Arduino UNO R4 WIFI
+ """
+ def __init__(self, ble_device_name, receive_notification_callback):
+ self.ble_device_name = ble_device_name
+ self.receive_notification_callback = receive_notification_callback
+ self.ble_device = None
+ self.bleak_client = None
+ self.nus = None
+ self.rx_char = None
+ self.tx_char = None
+
+ async def connect(self):
+ """
+ This method connects to a device matching the ble_device_name
+
+ :return:
+ """
+ print(f'Scanning for BLE device {self.ble_device_name}. Please wait...')
+
+ self.ble_device = await BleakScanner.find_device_by_name(self.ble_device_name)
+ if self.ble_device is None:
+ raise RuntimeError('Did not find the BLE device. Please check name.')
+ print(f'Found {self.ble_device_name} address: {self.ble_device.address}')
+ self.bleak_client = BleakClient(self.ble_device.address)
+ await self.bleak_client.connect()
+ await self.bleak_client.start_notify(UART_TX_CHAR_UUID,
+ self.receive_notification_callback)
+
+ async def write(self, data):
+ """
+ This method writes data to the IP device
+ :param data:
+
+ :return: None
+ """
+ await self.bleak_client.write_gatt_char(UART_RX_CHAR_UUID, data)
diff --git a/telemetrix_uno_r4/wifi/telemetrix_uno_r4_wifi_aio/telemetrix_aio_serial.py b/telemetrix_uno_r4/wifi/telemetrix_uno_r4_wifi_aio/telemetrix_aio_serial.py
new file mode 100644
index 0000000..c3c8ce0
--- /dev/null
+++ b/telemetrix_uno_r4/wifi/telemetrix_uno_r4_wifi_aio/telemetrix_aio_serial.py
@@ -0,0 +1,214 @@
+# -*- coding: utf-8 -*-
+"""
+ Copyright (c) 2015-2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+import serial
+import time
+
+LF = 0x0a
+
+
+# noinspection PyStatementEffect,PyUnresolvedReferences,PyUnresolvedReferences
+class TelemetrixAioSerial:
+ """
+ This class encapsulates management of the serial port that communicates
+ with the Arduino Firmata
+ It provides a 'futures' interface to make Pyserial compatible with asyncio
+ """
+
+ def __init__(self, com_port='/dev/ttyACM0', baud_rate=115200, sleep_tune=.0001,
+ telemetrix_aio_instance=None, close_loop_on_error=True):
+
+ """
+ This is the constructor for the aio serial handler
+
+ :param com_port: Com port designator
+
+ :param baud_rate: UART baud rate
+
+ :param telemetrix_aio_instance: reference to caller
+
+ :return: None
+ """
+ # print('Initializing Arduino - Please wait...', end=" ")
+ sys.stdout.flush()
+ self.my_serial = serial.Serial(com_port, baud_rate, timeout=1,
+ writeTimeout=1)
+
+ self.com_port = com_port
+ self.sleep_tune = sleep_tune
+ self.telemetrix_aio_instance = telemetrix_aio_instance
+ self.close_loop_on_error = close_loop_on_error
+
+ # used by read_until
+ self.start_time = None
+
+ async def get_serial(self):
+ """
+ This method returns a reference to the serial port in case the
+ user wants to call pyserial methods directly
+
+ :return: pyserial instance
+ """
+ return self.my_serial
+
+ async def write(self, data):
+ """
+ This is an asyncio adapted version of pyserial write. It provides a
+ non-blocking write and returns the number of bytes written upon
+ completion
+
+ :param data: Data to be written
+ :return: Number of bytes written
+ """
+ # the secret sauce - it is in your future
+ future = asyncio.Future()
+ result = None
+ try:
+ # result = self.my_serial.write(bytes([ord(data)]))
+ result = self.my_serial.write(bytes(data))
+
+ except serial.SerialException:
+ # noinspection PyBroadException
+ loop = None
+ await self.close()
+ future.cancel()
+ if self.close_loop_on_error:
+ loop = asyncio.get_event_loop()
+ loop.stop()
+
+ if self.telemetrix_aio_instance.the_task:
+ self.telemetrix_aio_instance.the_task.cancel()
+ await asyncio.sleep(1)
+ if self.close_loop_on_error:
+ loop.close()
+
+ if result:
+ future.set_result(result)
+ while True:
+ if not future.done():
+ # spin our asyncio wheels until future completes
+ await asyncio.sleep(self.sleep_tune)
+
+ else:
+ return future.result()
+
+ async def read(self, size=1):
+ """
+ This is an asyncio adapted version of pyserial read
+ that provides non-blocking read.
+
+ :return: One character
+ """
+
+ # create an asyncio Future
+ future = asyncio.Future()
+
+ # create a flag to indicate when data becomes available
+ data_available = False
+
+ # wait for a character to become available and read from
+ # the serial port
+ while True:
+ if not data_available:
+ # test to see if a character is waiting to be read.
+ # if not, relinquish control back to the event loop through the
+ # short sleep
+ if not self.my_serial.in_waiting:
+ await asyncio.sleep(self.sleep_tune*2)
+
+ # data is available.
+ # set the flag to true so that the future can "wait" until the
+ # read is completed.
+ else:
+ data_available = True
+ data = self.my_serial.read(size)
+ # set future result to make the character available
+ if size == 1:
+ future.set_result(ord(data))
+ else:
+ future.set_result(list(data))
+ else:
+ # wait for the future to complete
+ if not future.done():
+ await asyncio.sleep(self.sleep_tune)
+ else:
+ # future is done, so return the character
+ return future.result()
+
+ async def read_until(self, expected=LF, size=None, timeout=1):
+ """
+ This is an asyncio adapted version of pyserial read
+ that provides non-blocking read.
+
+ :return: Data delimited by expected
+ """
+
+ expected = str(expected).encode()
+ # create an asyncio Future
+ future = asyncio.Future()
+
+ # create a flag to indicate when data becomes available
+ data_available = False
+
+ if timeout:
+ self.start_time = time.time()
+
+ # wait for a character to become available and read from
+ # the serial port
+ while True:
+ if not data_available:
+ # test to see if a character is waiting to be read.
+ # if not, relinquish control back to the event loop through the
+ # short sleep
+ if not self.my_serial.in_waiting:
+ if timeout:
+ elapsed_time = time.time() - self.start_time
+ if elapsed_time > timeout:
+ return None
+ await asyncio.sleep(self.sleep_tune)
+ # data is available.
+ # set the flag to true so that the future can "wait" until the
+ # read is completed.
+ else:
+ data_available = True
+ data = self.my_serial.read_until(expected, size)
+ # set future result to make the character available
+ return_value = list(data)
+ future.set_result(return_value)
+ else:
+ # wait for the future to complete
+ if not future.done():
+ await asyncio.sleep(self.sleep_tune)
+ else:
+ # future is done, so return the character
+ return future.result()
+
+ async def reset_input_buffer(self):
+ """
+ Reset the input buffer
+ """
+ self.my_serial.reset_input_buffer()
+
+ async def close(self):
+ """
+ Close the serial port
+ """
+ if self.my_serial:
+ self.my_serial.close()
diff --git a/telemetrix_uno_r4/wifi/telemetrix_uno_r4_wifi_aio/telemetrix_aio_socket.py b/telemetrix_uno_r4/wifi/telemetrix_uno_r4_wifi_aio/telemetrix_aio_socket.py
new file mode 100644
index 0000000..ac11ce1
--- /dev/null
+++ b/telemetrix_uno_r4/wifi/telemetrix_uno_r4_wifi_aio/telemetrix_aio_socket.py
@@ -0,0 +1,80 @@
+
+"""
+ Copyright (c) 2020-2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+
+import asyncio
+import sys
+
+
+# noinspection PyStatementEffect,PyUnresolvedReferences,PyUnresolvedReferences
+class TelemetrixAioSocket:
+ """
+ This class encapsulates management of a tcp/ip connection that communicates
+ with an Arduino UNO R4 WIFI
+ """
+ def __init__(self, ip_address, ip_port, loop):
+ self.ip_address = ip_address
+ self.ip_port = ip_port
+ self.loop = loop
+ self.reader = None
+ self.writer = None
+
+ async def start(self):
+ """
+ This method opens an IP connection on the IP device
+
+ :return: None
+ """
+ try:
+ self.reader, self.writer = await asyncio.open_connection(
+ self.ip_address, self.ip_port)
+ print(f'Successfully connected to: {self.ip_address}:{self.ip_port}')
+ except OSError:
+ print("Can't open connection to " + self.ip_address)
+ sys.exit(0)
+
+ async def write(self, data):
+ """
+ This method writes data to the IP device
+ :param data:
+
+ :return: None
+ """
+ # we need to convert data formats,
+ # so all of the below.
+ output_list = []
+
+ # create an array of integers from the data to be sent
+ for x in data:
+ # output_list.append((ord(x)))
+ output_list.append(x)
+
+ # now convert the integer list to a bytearray
+ to_wifi = bytearray(output_list)
+ self.writer.write(to_wifi)
+ await self.writer.drain()
+
+ async def read(self, num_bytes=1):
+ """
+ This method reads one byte of data from IP device
+
+ :return: Next byte
+ """
+ buffer = await self.reader.read(num_bytes)
+ # await asyncio.sleep(num_bytes * 0.02)
+ return buffer
diff --git a/telemetrix_uno_r4/wifi/telemetrix_uno_r4_wifi_aio/telemetrix_uno_r4_wifi_aio.py b/telemetrix_uno_r4/wifi/telemetrix_uno_r4_wifi_aio/telemetrix_uno_r4_wifi_aio.py
new file mode 100644
index 0000000..0cab0ca
--- /dev/null
+++ b/telemetrix_uno_r4/wifi/telemetrix_uno_r4_wifi_aio/telemetrix_uno_r4_wifi_aio.py
@@ -0,0 +1,2629 @@
+"""
+ Copyright (c) 2023 Alan Yorinks All rights reserved.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3 as published by the Free Software Foundation; either
+ or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+"""
+
+import asyncio
+import sys
+import time
+
+# noinspection PyPackageRequirementscd
+from serial.serialutil import SerialException
+# noinspection PyPackageRequirements
+from serial.tools import list_ports
+
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio.private_constants import (
+ PrivateConstants)
+# noinspection PyUnresolvedReferences
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio.telemetrix_aio_serial import (
+ TelemetrixAioSerial)
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio.telemetrix_aio_socket import (
+ TelemetrixAioSocket)
+from telemetrix_uno_r4.wifi.telemetrix_uno_r4_wifi_aio.telemetrix_aio_ble import (
+ TelemetrixAioBle)
+
+
+# noinspection GrazieInspection,PyArgumentList,PyMethodMayBeStatic,PyRedundantParentheses
+class TelemetrixUnoR4WiFiAio:
+ """
+ This class exposes and implements the TelemetrixUnoR4WifiAio API.
+ It includes the public API methods as well as
+ a set of private methods. This is an asyncio API.
+
+ """
+
+ # noinspection PyPep8,PyPep8
+ def __init__(self, com_port=None,
+ arduino_instance_id=1, arduino_wait=1,
+ sleep_tune=0.0001, autostart=True,
+ loop=None, shutdown_on_exception=True,
+ close_loop_on_shutdown=True, hard_reset_on_shutdown=True,
+ transport_address=None, ip_port=31336, transport_type=0,
+ ble_device_name='Telemetrix4UnoR4 BLE'):
+
+ """
+ If you have a single Arduino connected to your computer,
+ then you may accept all the default values.
+
+ Otherwise, specify a unique arduino_instance id for each board in use.
+
+ :param com_port: e.g. COM3 or /dev/ttyACM0.
+
+ :param arduino_instance_id: Must match value in the Telemetrix4Arduino sketch
+
+ :param arduino_wait: Amount of time to wait for an Arduino to
+ fully reset itself.
+
+ :param sleep_tune: A tuning parameter (typically not changed by user)
+
+ :param autostart: If you wish to call the start method within
+ your application, then set this to False.
+
+ :param loop: optional user provided event loop
+
+ :param shutdown_on_exception: call shutdown before raising
+ a RunTimeError exception, or
+ receiving a KeyboardInterrupt exception
+
+ :param close_loop_on_shutdown: stop and close the event loop loop
+ when a shutdown is called or a serial
+ error occurs
+
+ :param hard_reset_on_shutdown: reset the board on shutdown
+
+ :param transport_address: ip address of tcp/ip connected device.
+
+ :param ip_port: ip port of tcp/ip connected device
+
+ :param transport_type: 0 = WiFi
+ 1 = SerialUSB
+ 2 = BLE
+
+ :param ble_device_name: name of Arduino UNO R4 WIFI BLE device.
+ It must match that of Telemetrix4UnoR4BLE.ino
+
+ """
+ # check to make sure that Python interpreter is version 3.8.3 or greater
+ python_version = sys.version_info
+ if python_version[0] >= 3:
+ if python_version[1] >= 8:
+ if python_version[2] >= 3:
+ pass
+ else:
+ raise RuntimeError("ERROR: Python 3.7 or greater is "
+ "required for use of this program.")
+
+ # save input parameters
+ self.com_port = com_port
+ self.arduino_instance_id = arduino_instance_id
+ self.arduino_wait = arduino_wait
+ self.sleep_tune = sleep_tune
+ self.autostart = autostart
+ self.hard_reset_on_shutdown = hard_reset_on_shutdown
+
+ self.transport_address = transport_address
+ self.ip_port = ip_port
+ if transport_type not in [0, 1, 2]:
+ raise RuntimeError('Invalid transport type')
+ self.transport_type = transport_type
+ self.firmware_version = None
+ # if tcp, this variable is set to the connected socket
+ self.sock = None
+
+ self.ble_device_name = ble_device_name
+
+ # instance of telemetrix_aio_ble
+ self.ble_instance = None
+
+ # set the event loop
+ if loop is None:
+ self.loop = asyncio.get_event_loop()
+ else:
+ self.loop = loop
+
+ self.shutdown_on_exception = shutdown_on_exception
+ self.close_loop_on_shutdown = close_loop_on_shutdown
+
+ # dictionaries to store the callbacks for each pin
+ self.analog_callbacks = {}
+
+ self.digital_callbacks = {}
+
+ self.i2c_callback = None
+ self.i2c_callback2 = None
+
+ self.i2c_1_active = False
+ self.i2c_2_active = False
+
+ self.spi_callback = None
+
+ self.onewire_callback = None
+
+ # debug loopback callback method
+ self.loop_back_callback = None
+
+ # the trigger pin will be the key to retrieve
+ # the callback for a specific HC-SR04
+ self.sonar_callbacks = {}
+
+ self.sonar_count = 0
+
+ self.dht_callbacks = {}
+
+ self.dht_count = 0
+
+ # serial port in use
+ self.serial_port = None
+
+ # generic asyncio task holder
+ self.the_task = None
+
+ # flag to indicate we are in shutdown mode
+ self.shutdown_flag = False
+
+ self.report_dispatch = {}
+
+ # reported features
+ self.reported_features = 0
+
+ # To add a command to the command dispatch table, append here.
+ self.report_dispatch.update(
+ {PrivateConstants.LOOP_COMMAND: self._report_loop_data})
+ self.report_dispatch.update(
+ {PrivateConstants.DEBUG_PRINT: self._report_debug_data})
+ self.report_dispatch.update(
+ {PrivateConstants.DIGITAL_REPORT: self._digital_message})
+ self.report_dispatch.update(
+ {PrivateConstants.ANALOG_REPORT: self._analog_message})
+ self.report_dispatch.update(
+ {PrivateConstants.SERVO_UNAVAILABLE: self._servo_unavailable})
+ self.report_dispatch.update(
+ {PrivateConstants.I2C_READ_REPORT: self._i2c_read_report})
+ self.report_dispatch.update(
+ {PrivateConstants.I2C_TOO_FEW_BYTES_RCVD: self._i2c_too_few})
+ self.report_dispatch.update(
+ {PrivateConstants.I2C_TOO_MANY_BYTES_RCVD: self._i2c_too_many})
+ self.report_dispatch.update(
+ {PrivateConstants.SONAR_DISTANCE: self._sonar_distance_report})
+ self.report_dispatch.update({PrivateConstants.DHT_REPORT: self._dht_report})
+ self.report_dispatch.update(
+ {PrivateConstants.SPI_REPORT: self._spi_report})
+ self.report_dispatch.update(
+ {PrivateConstants.ONE_WIRE_REPORT: self._onewire_report})
+ self.report_dispatch.update(
+ {PrivateConstants.STEPPER_DISTANCE_TO_GO:
+ self._stepper_distance_to_go_report})
+ self.report_dispatch.update(
+ {PrivateConstants.STEPPER_TARGET_POSITION:
+ self._stepper_target_position_report})
+ self.report_dispatch.update(
+ {PrivateConstants.STEPPER_CURRENT_POSITION:
+ self._stepper_current_position_report})
+ self.report_dispatch.update(
+ {PrivateConstants.STEPPER_RUNNING_REPORT:
+ self._stepper_is_running_report})
+ self.report_dispatch.update(
+ {PrivateConstants.STEPPER_RUN_COMPLETE_REPORT:
+ self._stepper_run_complete_report})
+ self.report_dispatch.update(
+ {PrivateConstants.STEPPER_DISTANCE_TO_GO:
+ self._stepper_distance_to_go_report})
+ self.report_dispatch.update(
+ {PrivateConstants.STEPPER_TARGET_POSITION:
+ self._stepper_target_position_report})
+ self.report_dispatch.update(
+ {PrivateConstants.FEATURES:
+ self._features_report})
+
+ # dictionaries to store the callbacks for each pin
+ self.analog_callbacks = {}
+
+ self.digital_callbacks = {}
+
+ self.i2c_callback = None
+ self.i2c_callback2 = None
+
+ self.i2c_1_active = False
+ self.i2c_2_active = False
+
+ self.spi_callback = None
+
+ self.onewire_callback = None
+
+ self.cs_pins_enabled = []
+
+ # flag to indicate if spi is initialized
+ self.spi_enabled = False
+
+ # flag to indicate if onewire is initialized
+ self.onewire_enabled = False
+
+ # the trigger pin will be the key to retrieve
+ # the callback for a specific HC-SR04
+ self.sonar_callbacks = {}
+
+ self.sonar_count = 0
+
+ self.dht_callbacks = {}
+
+ # # stepper motor variables
+ #
+ # # updated when a new motor is added
+ # self.next_stepper_assigned = 0
+ #
+ # # valid list of stepper motor interface types
+ # self.valid_stepper_interfaces = [1, 2, 3, 4, 6, 8]
+ #
+ # # maximum number of steppers supported
+ # self.max_number_of_steppers = 4
+ #
+ # # number of steppers created - not to exceed the maximum
+ # self.number_of_steppers = 0
+ #
+ # # dictionary to hold stepper motor information
+ # self.stepper_info = {'instance': False, 'is_running': None,
+ # 'maximum_speed': 1, 'speed': 0, 'acceleration': 0,
+ # 'distance_to_go_callback': None,
+ # 'target_position_callback': None,
+ # 'current_position_callback': None,
+ # 'is_running_callback': None,
+ # 'motion_complete_callback': None,
+ # 'acceleration_callback': None}
+ #
+ # # build a list of stepper motor info items
+ # self.stepper_info_list = []
+ # # a list of dictionaries to hold stepper information
+ # for motor in range(self.max_number_of_steppers):
+ # self.stepper_info_list.append(self.stepper_info)
+
+ print(f'telemetrix_uno_r4_wifi_aio Version:'
+ f' {PrivateConstants.TELEMETRIX_VERSION}')
+ print(f'Copyright (c) 2023 Alan Yorinks All rights reserved.\n')
+
+ if autostart:
+ self.loop.run_until_complete(self.start_aio())
+
+ async def start_aio(self):
+ """
+ This method may be called directly, if the autostart
+ parameter in __init__ is set to false.
+
+ This method instantiates the serial interface and then performs auto pin
+ discovery if using a serial interface, or creates and connects to
+ a TCP/IP enabled device running StandardFirmataWiFi.
+
+ Use this method if you wish to start TelemetrixAIO manually from
+ an asyncio function.
+ """
+
+ if self.transport_type == PrivateConstants.SERIAL_TRANSPORT:
+ if not self.com_port:
+ # user did not specify a com_port
+ try:
+ await self._find_arduino()
+ except KeyboardInterrupt:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ else:
+ # com_port specified - set com_port and baud rate
+ try:
+ await self._manual_open()
+ except KeyboardInterrupt:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+
+ if self.com_port:
+ print(f'Telemetrix4UnoR4WIFI found and connected to {self.com_port}')
+
+ # no com_port found - raise a runtime exception
+ else:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError('No Arduino Found or User Aborted Program')
+ await self.disable_scroll_message()
+ # using tcp/ip
+ elif self.transport_type == PrivateConstants.WIFI_TRANSPORT:
+ self.sock = TelemetrixAioSocket(self.transport_address, self.ip_port, self.loop)
+ await self.sock.start()
+ else: # ble
+ self.ble_instance = TelemetrixAioBle(self.ble_device_name, self._ble_report_dispatcher)
+ await self.ble_instance.connect()
+
+ # get arduino firmware version and print it
+ firmware_version = await self._get_firmware_version()
+ if not firmware_version:
+ print('*** Firmware Version retrieval timed out. ***')
+ print('\nDo you have Arduino connectivity and do you have the ')
+ print('Telemetrix4UnoR4 sketch uploaded to the board and are connected')
+ print('to the correct serial port.\n')
+ print('To see a list of serial ports, type: '
+ '"list_serial_ports" in your console.')
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError
+ else:
+
+ print(f'Telemetrix4UnoR4 Version Number: {firmware_version[2]}.'
+ f'{firmware_version[3]}.{firmware_version[4]}')
+ # start the command dispatcher loop
+ command = [PrivateConstants.ENABLE_ALL_REPORTS]
+ await self._send_command(command)
+ if not self.loop:
+ self.loop = asyncio.get_event_loop()
+ self.the_task = self.loop.create_task(self._arduino_report_dispatcher())
+
+ # get the features list
+ command = [PrivateConstants.GET_FEATURES]
+ await self._send_command(command)
+ await asyncio.sleep(.5)
+
+ # Have the server reset its data structures
+ command = [PrivateConstants.RESET]
+ await self._send_command(command)
+ await asyncio.sleep(.1)
+
+ async def get_event_loop(self):
+ """
+ Return the currently active asyncio event loop
+
+ :return: Active event loop
+
+ """
+ return self.loop
+
+ async def _find_arduino(self):
+ """
+ This method will search all potential serial ports for an Arduino
+ containing a sketch that has a matching arduino_instance_id as
+ specified in the input parameters of this class.
+
+ This is used explicitly with the FirmataExpress sketch.
+ """
+
+ # a list of serial ports to be checked
+ serial_ports = []
+
+ print('Opening all potential serial ports...')
+ the_ports_list = list_ports.comports()
+ for port in the_ports_list:
+ if port.pid is None:
+ continue
+ print('\nChecking {}'.format(port.device))
+ try:
+ self.serial_port = TelemetrixAioSerial(port.device, 115200,
+ telemetrix_aio_instance=self,
+ close_loop_on_error=self.close_loop_on_shutdown)
+ except SerialException:
+ continue
+ # create a list of serial ports that we opened
+ serial_ports.append(self.serial_port)
+
+ # display to the user
+ print('\t' + port.device)
+
+ # clear out any possible data in the input buffer
+ await self.serial_port.reset_input_buffer()
+
+ # wait for arduino to reset
+ print('\nWaiting {} seconds(arduino_wait) for Arduino devices to '
+ 'reset...'.format(self.arduino_wait))
+ await asyncio.sleep(self.arduino_wait)
+
+ print('\nSearching for an Arduino configured with an arduino_instance = ',
+ self.arduino_instance_id)
+
+ for serial_port in serial_ports:
+ self.serial_port = serial_port
+
+ command = [PrivateConstants.ARE_U_THERE]
+ await self._send_command(command)
+ # provide time for the reply
+ await asyncio.sleep(.1)
+
+ i_am_here = await self.serial_port.read(3)
+
+ if not i_am_here:
+ continue
+
+ # got an I am here message - is it the correct ID?
+ if i_am_here[2] == self.arduino_instance_id:
+ self.com_port = serial_port.com_port
+ return
+
+ async def _manual_open(self):
+ """
+ Com port was specified by the user - try to open up that port
+
+ """
+ # if port is not found, a serial exception will be thrown
+ print('Opening {} ...'.format(self.com_port))
+ self.serial_port = TelemetrixAioSerial(self.com_port, 115200,
+ telemetrix_aio_instance=self,
+ close_loop_on_error=self.close_loop_on_shutdown)
+
+ print('Waiting {} seconds for the Arduino To Reset.'
+ .format(self.arduino_wait))
+ await asyncio.sleep(self.arduino_wait)
+ command = [PrivateConstants.ARE_U_THERE]
+ await self._send_command(command)
+ # provide time for the reply
+ await asyncio.sleep(.1)
+
+ print(f'Searching for correct arduino_instance_id: {self.arduino_instance_id}')
+ i_am_here = await self.serial_port.read(3)
+
+ if not i_am_here:
+ print(f'ERROR: correct arduino_instance_id not found')
+
+ print('Correct arduino_instance_id found')
+
+ async def _get_firmware_version(self):
+ """
+ This method retrieves the Arduino4Telemetrix firmware version
+
+ :returns: Firmata firmware version
+ """
+ self.firmware_version = None
+ command = [PrivateConstants.GET_FIRMWARE_VERSION]
+ await self._send_command(command)
+ # provide time for the reply
+ await asyncio.sleep(.3)
+ if self.transport_type == PrivateConstants.SERIAL_TRANSPORT:
+ self.firmware_version = await self.serial_port.read(5)
+ elif self.transport_type == PrivateConstants.WIFI_TRANSPORT:
+ self.firmware_version = list(await self.sock.read(5))
+ else:
+ pass
+ return self.firmware_version
+
+ async def analog_write(self, pin, value):
+ """
+ Set the specified pin to the specified value.
+
+ :param pin: arduino pin number
+
+ :param value: pin value (maximum 16 bits)
+
+ """
+ value_msb = value >> 8
+ value_lsb = value & 0xff
+ command = [PrivateConstants.ANALOG_WRITE, pin, value_msb, value_lsb]
+ await self._send_command(command)
+
+ async def digital_write(self, pin, value):
+ """
+ Set the specified pin to the specified value.
+
+ :param pin: arduino pin number
+
+ :param value: pin value (1 or 0)
+
+ """
+ command = [PrivateConstants.DIGITAL_WRITE, pin, value]
+ await self._send_command(command)
+
+ async def i2c_read(self, address, register, number_of_bytes,
+ callback, i2c_port=0,
+ write_register=True):
+ """
+ Read the specified number of bytes from the specified register for
+ the i2c device.
+
+
+ :param address: i2c device address
+
+ :param register: i2c register (or None if no register selection is needed)
+
+ :param number_of_bytes: number of bytes to be read
+
+ :param callback: Required callback function to report i2c data as a
+ result of read command
+
+ :param i2c_port: select the default port (0) or secondary port (1)
+
+ :param write_register: If True, the register is written
+ before read
+ Else, the write is suppressed
+
+
+ callback returns a data list:
+
+ [I2C_READ_REPORT, address, register, count of data bytes,
+ data bytes, time-stamp]
+
+ """
+ if not callback:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError('i2c_read: A Callback must be specified')
+
+ await self._i2c_read_request(address, register, number_of_bytes,
+ callback=callback, i2c_port=i2c_port,
+ write_register=write_register)
+
+ async def i2c_read_restart_transmission(self, address, register,
+ number_of_bytes,
+ callback, i2c_port=0,
+ write_register=True):
+ """
+ Read the specified number of bytes from the specified register for
+ the i2c device. This restarts the transmission after the read. It is
+ required for some i2c devices such as the MMA8452Q accelerometer.
+
+
+ :param address: i2c device address
+
+ :param register: i2c register (or None if no register
+ selection is needed)
+
+ :param number_of_bytes: number of bytes to be read
+
+ :param callback: Required callback function to report i2c data as a
+ result of read command
+
+ :param i2c_port: select the default port (0) or secondary port (1)
+
+ :param write_register: If True, the register is written
+ before read
+ Else, the write is suppressed
+
+ callback returns a data list:
+
+ [I2C_READ_REPORT, address, register, count of data bytes,
+ data bytes, time-stamp]
+
+ """
+ if not callback:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(
+ 'i2c_read_restart_transmission: A Callback must be specified')
+
+ await self._i2c_read_request(address, register, number_of_bytes,
+ stop_transmission=False,
+ callback=callback, i2c_port=i2c_port,
+ write_register=write_register)
+
+ async def _i2c_read_request(self, address, register, number_of_bytes,
+ stop_transmission=True, callback=None,
+ i2c_port=0, write_register=True):
+ """
+ This method requests the read of an i2c device. Results are retrieved
+ via callback.
+
+ :param address: i2c device address
+
+ :param register: register number (or None if no register selection is needed)
+
+ :param number_of_bytes: number of bytes expected to be returned
+
+ :param stop_transmission: stop transmission after read
+
+ :param callback: Required callback function to report i2c data as a
+ result of read command.
+
+ :param i2c_port: select the default port (0) or secondary port (1)
+
+ :param write_register: If True, the register is written
+ before read
+ Else, the write is suppressed
+
+ """
+ if not i2c_port:
+ if not self.i2c_1_active:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(
+ 'I2C Read: set_pin_mode i2c never called for i2c port 1.')
+
+ if i2c_port:
+ if not self.i2c_2_active:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(
+ 'I2C Read: set_pin_mode i2c never called for i2c port 2.')
+
+ if not callback:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError('I2C Read: A callback function must be specified.')
+
+ if not i2c_port:
+ self.i2c_callback = callback
+ else:
+ self.i2c_callback2 = callback
+
+ if not register:
+ register = 0
+
+ if write_register:
+ write_register = 1
+ else:
+ write_register = 0
+
+ # message contains:
+ # 1. address
+ # 2. register
+ # 3. number of bytes
+ # 4. restart_transmission - True or False
+ # 5. i2c port
+ # 6. suppress write flag
+
+ command = [PrivateConstants.I2C_READ, address, register, number_of_bytes,
+ stop_transmission, i2c_port, write_register]
+ await self._send_command(command)
+
+ async def i2c_write(self, address, args, i2c_port=0):
+ """
+ Write data to an i2c device.
+
+ :param address: i2c device address
+
+ :param i2c_port: 0= port 1, 1 = port 2
+
+ :param args: A variable number of bytes to be sent to the device
+ passed in as a list
+
+ """
+ if not i2c_port:
+ if not self.i2c_1_active:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(
+ 'I2C Write: set_pin_mode i2c never called for i2c port 1.')
+
+ if i2c_port:
+ if not self.i2c_2_active:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(
+ 'I2C Write: set_pin_mode i2c never called for i2c port 2.')
+
+ command = [PrivateConstants.I2C_WRITE, len(args), address, i2c_port]
+
+ for item in args:
+ command.append(item)
+
+ await self._send_command(command)
+
+ async def loop_back(self, start_character, callback):
+ """
+ This is a debugging method to send a character to the
+ Arduino device, and have the device loop it back.
+
+ :param start_character: The character to loop back. It should be
+ an integer.
+
+ :param callback: Looped back character will appear in the callback method
+
+ """
+
+ if not callback:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError('loop_back: A callback function must be specified.')
+ command = [PrivateConstants.LOOP_COMMAND, ord(start_character)]
+ self.loop_back_callback = callback
+ await self._send_command(command)
+
+ async def set_analog_scan_interval(self, interval):
+ """
+ Set the analog scanning interval.
+
+ :param interval: value of 0 - 255 - milliseconds
+ """
+
+ if 0 <= interval <= 255:
+ command = [PrivateConstants.SET_ANALOG_SCANNING_INTERVAL, interval]
+ await self._send_command(command)
+ else:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError('Analog interval must be between 0 and 255')
+
+ async def set_pin_mode_analog_input(self, pin_number, differential=0, callback=None):
+ """
+ Set a pin as an analog input.
+
+ :param pin_number: arduino pin number
+
+ :param callback: async callback function
+
+ :param differential: difference in previous to current value before
+ report will be generated
+
+ callback returns a data list:
+
+ [pin_type, pin_number, pin_value, raw_time_stamp]
+
+ The pin_type for analog input pins = 3
+
+ """
+
+ if not callback:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(
+ 'set_pin_mode_analog_input: A callback function must be specified.')
+
+ await self._set_pin_mode(pin_number, PrivateConstants.AT_ANALOG,
+ differential, callback=callback)
+
+ async def set_pin_mode_analog_output(self, pin_number):
+ """
+
+ Set a pin as a pwm (analog output) pin.
+
+ :param pin_number:arduino pin number
+
+ """
+
+ await self._set_pin_mode(pin_number, PrivateConstants.AT_OUTPUT, differential=0,
+ callback=None)
+
+ async def set_pin_mode_digital_input(self, pin_number, callback):
+ """
+ Set a pin as a digital input.
+
+ :param pin_number: arduino pin number
+
+ :param callback: async callback function
+
+ callback returns a data list:
+
+ [pin_type, pin_number, pin_value, raw_time_stamp]
+
+ The pin_type for all digital input pins = 2
+
+ """
+ await self._set_pin_mode(pin_number, PrivateConstants.AT_INPUT, differential=0,
+ callback=callback)
+
+ async def set_pin_mode_digital_input_pullup(self, pin_number, callback):
+ """
+ Set a pin as a digital input with pullup enabled.
+
+ :param pin_number: arduino pin number
+
+ :param callback: async callback function
+
+ callback returns a data list:
+
+ [pin_type, pin_number, pin_value, raw_time_stamp]
+
+ The pin_type for all digital input pins = 2
+
+ """
+ if not callback:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(
+ 'set_pin_mode_digital_input_pullup: A callback function must be specified.')
+
+ await self._set_pin_mode(pin_number, PrivateConstants.AT_INPUT_PULLUP,
+ differential=0, callback=callback)
+
+ async def set_pin_mode_digital_output(self, pin_number):
+ """
+ Set a pin as a digital output pin.
+
+ :param pin_number: arduino pin number
+ """
+
+ await self._set_pin_mode(pin_number, PrivateConstants.AT_OUTPUT, differential=0,
+ callback=None)
+
+ # noinspection PyIncorrectDocstring
+ async def set_pin_mode_i2c(self, i2c_port=0):
+ """
+ Establish the standard Arduino i2c pins for i2c utilization.
+
+ :param i2c_port: 0 = i2c1, 1 = i2c2
+
+ NOTES: 1. THIS METHOD MUST BE CALLED BEFORE ANY I2C REQUEST IS MADE
+ 2. Callbacks are set within the individual i2c read methods of this
+ API.
+
+ See i2c_read, or i2c_read_restart_transmission.
+
+ """
+ # test for i2c port 2
+ if i2c_port:
+ # if not previously activated set it to activated
+ # and the send a begin message for this port
+ if not self.i2c_2_active:
+ self.i2c_2_active = True
+ else:
+ return
+ # port 1
+ else:
+ if not self.i2c_1_active:
+ self.i2c_1_active = True
+ else:
+ return
+
+ command = [PrivateConstants.I2C_BEGIN, i2c_port]
+ await self._send_command(command)
+
+ async def set_pin_mode_dht(self, pin, callback=None, dht_type=22):
+ """
+
+ :param pin: connection pin
+
+ :param callback: callback function
+
+ :param dht_type: either 22 for DHT22 or 11 for DHT11
+
+ Error Callback: [DHT REPORT Type, DHT_ERROR_NUMBER, PIN, DHT_TYPE, Time]
+
+ Valid Data Callback: DHT REPORT Type, DHT_DATA=, PIN, DHT_TYPE, Humidity,
+ Temperature,
+ Time]
+
+ """
+ if self.reported_features & PrivateConstants.DHT_FEATURE:
+
+ if not callback:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError('set_pin_mode_dht: A Callback must be specified')
+
+ if self.dht_count < PrivateConstants.MAX_DHTS - 1:
+ self.dht_callbacks[pin] = callback
+ self.dht_count += 1
+
+ if dht_type != 22 and dht_type != 11:
+ dht_type = 22
+
+ command = [PrivateConstants.DHT_NEW, pin, dht_type]
+ await self._send_command(command)
+ else:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(
+ f'Maximum Number Of DHTs Exceeded - set_pin_mode_dht fails for pin {pin}')
+
+ else:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(f'The DHT feature is disabled in the server.')
+
+ async def set_pin_mode_servo(self, pin_number, min_pulse=544, max_pulse=2400):
+ """
+
+ Attach a pin to a servo motor
+
+ :param pin_number: pin
+
+ :param min_pulse: minimum pulse width
+
+ :param max_pulse: maximum pulse width
+
+ """
+ if self.reported_features & PrivateConstants.SERVO_FEATURE:
+
+ minv = (min_pulse).to_bytes(2, byteorder="big")
+ maxv = (max_pulse).to_bytes(2, byteorder="big")
+
+ command = [PrivateConstants.SERVO_ATTACH, pin_number,
+ minv[0], minv[1], maxv[0], maxv[1]]
+ await self._send_command(command)
+ else:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(f'The SERVO feature is disabled in the server.')
+
+ async def set_pin_mode_sonar(self, trigger_pin, echo_pin,
+ callback):
+ """
+
+ :param trigger_pin:
+
+ :param echo_pin:
+
+ :param callback: callback
+
+ """
+ if self.reported_features & PrivateConstants.SONAR_FEATURE:
+
+ if not callback:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError('set_pin_mode_sonar: A Callback must be specified')
+
+ if self.sonar_count < PrivateConstants.MAX_SONARS - 1:
+ self.sonar_callbacks[trigger_pin] = callback
+ self.sonar_count += 1
+
+ command = [PrivateConstants.SONAR_NEW, trigger_pin, echo_pin]
+ await self._send_command(command)
+ else:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(
+ f'Maximum Number Of Sonars Exceeded - set_pin_mode_sonar fails for pin {trigger_pin}')
+ else:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(f'The SONAR feature is disabled in the server.')
+
+ async def set_pin_mode_spi(self, chip_select_list=None):
+ """
+ Specify the list of chip select pins.
+
+ Standard Arduino MISO, MOSI and CLK pins are used for the board in use.
+
+ Chip Select is any digital output capable pin.
+
+ :param chip_select_list: this is a list of pins to be used for chip select.
+ The pins will be configured as output, and set to high
+ ready to be used for chip select.
+ NOTE: You must specify the chips select pins here!
+
+
+ command message: [command, number of cs pins, [cs pins...]]
+ """
+ if self.reported_features & PrivateConstants.SPI_FEATURE:
+
+ if type(chip_select_list) != list:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError('chip_select_list must be in the form of a list')
+ if not chip_select_list:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError('Chip select pins were not specified')
+
+ self.spi_enabled = True
+
+ command = [PrivateConstants.SPI_INIT, len(chip_select_list)]
+
+ for pin in chip_select_list:
+ command.append(pin)
+ self.cs_pins_enabled.append(pin)
+ await self._send_command(command)
+ else:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(f'The SPI feature is disabled in the server.')
+
+ # async def set_pin_mode_stepper(self, interface=1, pin1=2, pin2=3, pin3=4,
+ # pin4=5, enable=True):
+ # """
+ # Stepper motor support is implemented as a proxy for the
+ # the AccelStepper library for the Arduino.
+ #
+ # This feature is compatible with the TB6600 Motor Driver
+ #
+ # Note: It may not work for other driver types!
+ #
+ # https://github.com/waspinator/AccelStepper
+ #
+ # Instantiate a stepper motor.
+ #
+ # Initialize the interface and pins for a stepper motor.
+ #
+ # :param interface: Motor Interface Type:
+ #
+ # 1 = Stepper Driver, 2 driver pins required
+ #
+ # 2 = FULL2WIRE 2 wire stepper, 2 motor pins required
+ #
+ # 3 = FULL3WIRE 3 wire stepper, such as HDD spindle,
+ # 3 motor pins required
+ #
+ # 4 = FULL4WIRE, 4 wire full stepper, 4 motor pins
+ # required
+ #
+ # 6 = HALF3WIRE, 3 wire half stepper, such as HDD spindle,
+ # 3 motor pins required
+ #
+ # 8 = HALF4WIRE, 4 wire half stepper, 4 motor pins required
+ #
+ # :param pin1: Arduino digital pin number for motor pin 1
+ #
+ # :param pin2: Arduino digital pin number for motor pin 2
+ #
+ # :param pin3: Arduino digital pin number for motor pin 3
+ #
+ # :param pin4: Arduino digital pin number for motor pin 4
+ #
+ # :param enable: If this is true, the output pins at construction time.
+ #
+ # :return: Motor Reference number
+ # """
+ # if self.reported_features & PrivateConstants.STEPPERS_FEATURE:
+ #
+ # if self.number_of_steppers == self.max_number_of_steppers:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('Maximum number of steppers has already been assigned')
+ #
+ # if interface not in self.valid_stepper_interfaces:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('Invalid stepper interface')
+ #
+ # self.number_of_steppers += 1
+ #
+ # motor_id = self.next_stepper_assigned
+ # self.next_stepper_assigned += 1
+ # self.stepper_info_list[motor_id]['instance'] = True
+ #
+ # # build message and send message to server
+ # command = [PrivateConstants.SET_PIN_MODE_STEPPER, motor_id, interface, pin1,
+ # pin2, pin3, pin4, enable]
+ # await self._send_command(command)
+ #
+ # # return motor id
+ # return motor_id
+ # else:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError(f'The Stepper feature is disabled in the server.')
+
+ async def sonar_disable(self):
+ """
+ Disable sonar scanning for all sonar sensors
+ """
+ command = [PrivateConstants.SONAR_DISABLE]
+ await self._send_command(command)
+
+ async def sonar_enable(self):
+ """
+ Enable sonar scanning for all sonar sensors
+ """
+ command = [PrivateConstants.SONAR_ENABLE]
+ await self._send_command(command)
+
+ async def spi_cs_control(self, chip_select_pin, select):
+ """
+ Control an SPI chip select line
+ :param chip_select_pin: pin connected to CS
+
+ :param select: 0=select, 1=deselect
+ """
+ if not self.spi_enabled:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(f'spi_cs_control: SPI interface is not enabled.')
+
+ if chip_select_pin not in self.cs_pins_enabled:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(f'spi_cs_control: chip select pin never enabled.')
+ command = [PrivateConstants.SPI_CS_CONTROL, chip_select_pin, select]
+ await self._send_command(command)
+
+ async def spi_read_blocking(self, chip_select, register_selection,
+ number_of_bytes_to_read,
+ call_back=None):
+ """
+ Read the specified number of bytes from the specified SPI port and
+ call the callback function with the reported data.
+
+ :param chip_select: chip select pin
+
+ :param register_selection: Register to be selected for read.
+
+ :param number_of_bytes_to_read: Number of bytes to read
+
+ :param call_back: Required callback function to report spi data as a
+ result of read command
+
+
+ callback returns a data list:
+ [SPI_READ_REPORT, chip select pin, SPI Register, count of data bytes read,
+ data bytes, time-stamp]
+ SPI_READ_REPORT = 13
+
+ """
+
+ if not self.spi_enabled:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(f'spi_read_blocking: SPI interface is not enabled.')
+
+ if not call_back:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError('spi_read_blocking: A Callback must be specified')
+
+ self.spi_callback = call_back
+
+ command = [PrivateConstants.SPI_READ_BLOCKING, chip_select,
+ number_of_bytes_to_read,
+ register_selection]
+
+ await self._send_command(command)
+
+ async def spi_set_format(self, clock_divisor, bit_order, data_mode):
+ """
+ Configure how the SPI serializes and de-serializes data on the wire.
+
+ See Arduino SPI reference materials for details.
+
+ :param clock_divisor: 1 - 255
+
+ :param bit_order:
+
+ LSBFIRST = 0
+
+ MSBFIRST = 1 (default)
+
+ :param data_mode:
+
+ SPI_MODE0 = 0x00 (default)
+
+ SPI_MODE1 = 1
+
+ SPI_MODE2 = 2
+
+ SPI_MODE3 = 3
+
+ """
+
+ if not self.spi_enabled:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(f'spi_set_format: SPI interface is not enabled.')
+
+ if not 0 < clock_divisor <= 255:
+ raise RuntimeError(f'spi_set_format: illegal clock divisor selected.')
+ if bit_order not in [0, 1]:
+ raise RuntimeError(f'spi_set_format: illegal bit_order selected.')
+ if data_mode not in [0, 1, 2, 3]:
+ raise RuntimeError(f'spi_set_format: illegal data_order selected.')
+
+ command = [PrivateConstants.SPI_SET_FORMAT, clock_divisor, bit_order,
+ data_mode]
+ await self._send_command(command)
+
+ async def spi_write_blocking(self, chip_select, bytes_to_write):
+ """
+ Write a list of bytes to the SPI device.
+
+ :param chip_select: chip select pin
+
+ :param bytes_to_write: A list of bytes to write. This must
+ be in the form of a list.
+
+ """
+
+ if not self.spi_enabled:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(f'spi_write_blocking: SPI interface is not enabled.')
+
+ if type(bytes_to_write) is not list:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError('spi_write_blocking: bytes_to_write must be a list.')
+
+ command = [PrivateConstants.SPI_WRITE_BLOCKING, chip_select, len(bytes_to_write)]
+
+ for data in bytes_to_write:
+ command.append(data)
+
+ await self._send_command(command)
+
+ # async def set_pin_mode_one_wire(self, pin):
+ # """
+ # Initialize the one wire serial bus.
+ #
+ # :param pin: Data pin connected to the OneWire device
+ # """
+ # self.onewire_enabled = True
+ # command = [PrivateConstants.ONE_WIRE_INIT, pin]
+ # await self._send_command(command)
+ #
+ # async def onewire_reset(self, callback=None):
+ # """
+ # Reset the onewire device
+ #
+ # :param callback: required function to report reset result
+ #
+ # callback returns a list:
+ # [ReportType = 14, Report Subtype = 25, reset result byte,
+ # timestamp]
+ # """
+ # if not self.onewire_enabled:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError(f'onewire_reset: OneWire interface is not enabled.')
+ # if not callback:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('onewire_reset: A Callback must be specified')
+ #
+ # self.onewire_callback = callback
+ #
+ # command = [PrivateConstants.ONE_WIRE_RESET]
+ # await self._send_command(command)
+ #
+ # async def onewire_select(self, device_address):
+ # """
+ # Select a device based on its address
+ # :param device_address: A bytearray of 8 bytes
+ # """
+ # if not self.onewire_enabled:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError(f'onewire_select: OneWire interface is not enabled.')
+ #
+ # if type(device_address) is not list:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('onewire_select: device address must be an array of 8 '
+ # 'bytes.')
+ #
+ # if len(device_address) != 8:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('onewire_select: device address must be an array of 8 '
+ # 'bytes.')
+ # command = [PrivateConstants.ONE_WIRE_SELECT]
+ # for data in device_address:
+ # command.append(data)
+ # await self._send_command(command)
+ #
+ # async def onewire_skip(self):
+ # """
+ # Skip the device selection. This only works if you have a
+ # single device, but you can avoid searching and use this to
+ # immediately access your device.
+ # """
+ # if not self.onewire_enabled:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError(f'onewire_skip: OneWire interface is not enabled.')
+ #
+ # command = [PrivateConstants.ONE_WIRE_SKIP]
+ # await self._send_command(command)
+ #
+ # async def onewire_write(self, data, power=0):
+ # """
+ # Write a byte to the onewire device. If 'power' is one
+ # then the wire is held high at the end for
+ # parasitically powered devices. You
+ # are responsible for eventually de-powering it by calling
+ # another read or write.
+ #
+ # :param data: byte to write.
+ # :param power: power control (see above)
+ # """
+ # if not self.onewire_enabled:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError(f'onewire_write: OneWire interface is not enabled.')
+ # if 0 < data < 255:
+ # command = [PrivateConstants.ONE_WIRE_WRITE, data, power]
+ # await self._send_command(command)
+ # else:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('onewire_write: Data must be no larger than 255')
+ #
+ # async def onewire_read(self, callback=None):
+ # """
+ # Read a byte from the onewire device
+ # :param callback: required function to report onewire data as a
+ # result of read command
+ #
+ #
+ # callback returns a data list:
+ # [ONEWIRE_REPORT, ONEWIRE_READ=29, data byte, time-stamp]
+ #
+ # ONEWIRE_REPORT = 14
+ # """
+ # if not self.onewire_enabled:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError(f'onewire_read: OneWire interface is not enabled.')
+ #
+ # if not callback:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('onewire_read A Callback must be specified')
+ #
+ # self.onewire_callback = callback
+ #
+ # command = [PrivateConstants.ONE_WIRE_READ]
+ # await self._send_command(command)
+ # await asynio.sleep(.2)
+ #
+ # async def onewire_reset_search(self):
+ # """
+ # Begin a new search. The next use of search will begin at the first device
+ # """
+ #
+ # if not self.onewire_enabled:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError(f'onewire_reset_search: OneWire interface is not '
+ # f'enabled.')
+ # else:
+ # command = [PrivateConstants.ONE_WIRE_RESET_SEARCH]
+ # await self._send_command(command)
+ #
+ # async def onewire_search(self, callback=None):
+ # """
+ # Search for the next device. The device address will returned in the callback.
+ # If a device is found, the 8 byte address is contained in the callback.
+ # If no more devices are found, the address returned contains all elements set
+ # to 0xff.
+ #
+ # :param callback: required function to report a onewire device address
+ #
+ # callback returns a data list:
+ # [ONEWIRE_REPORT, ONEWIRE_SEARCH=31, 8 byte address, time-stamp]
+ #
+ # ONEWIRE_REPORT = 14
+ # """
+ # if not self.onewire_enabled:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError(f'onewire_search: OneWire interface is not enabled.')
+ #
+ # if not callback:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('onewire_read A Callback must be specified')
+ #
+ # self.onewire_callback = callback
+ #
+ # command = [PrivateConstants.ONE_WIRE_SEARCH]
+ # await self._send_command(command)
+ #
+ # async def onewire_crc8(self, address_list, callback=None):
+ # """
+ # Compute a CRC check on an array of data.
+ # :param address_list:
+ #
+ # :param callback: required function to report a onewire device address
+ #
+ # callback returns a data list:
+ # [ONEWIRE_REPORT, ONEWIRE_CRC8=32, CRC, time-stamp]
+ #
+ # ONEWIRE_REPORT = 14
+ #
+ # """
+ #
+ # if not self.onewire_enabled:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError(f'onewire_crc8: OneWire interface is not enabled.')
+ #
+ # if not callback:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('onewire_crc8 A Callback must be specified')
+ #
+ # if type(address_list) is not list:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('onewire_crc8: address list must be a list.')
+ #
+ # self.onewire_callback = callback
+ #
+ # address_length = len(address_list)
+ #
+ # command = [PrivateConstants.ONE_WIRE_CRC8, address_length - 1]
+ #
+ # for data in address_list:
+ # command.append(data)
+ #
+ # await self._send_command(command)
+
+ async def _set_pin_mode(self, pin_number, pin_state, differential, callback):
+ """
+ A private method to set the various pin modes.
+
+ :param pin_number: arduino pin number
+
+ :param pin_state: INPUT/OUTPUT/ANALOG/PWM/PULLUP - for SERVO use
+ servo_config()
+ For DHT use: set_pin_mode_dht
+
+ :param differential: for analog inputs - threshold
+ value to be achieved for report to
+ be generated
+
+ :param callback: A reference to an async call back function to be
+ called when pin data value changes
+
+ """
+ if not callback and pin_state != PrivateConstants.AT_OUTPUT:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError('_set_pin_mode: A Callback must be specified')
+ else:
+ if pin_state == PrivateConstants.AT_INPUT:
+ command = [PrivateConstants.SET_PIN_MODE, pin_number,
+ PrivateConstants.AT_INPUT, 1]
+ self.digital_callbacks[pin_number] = callback
+ elif pin_state == PrivateConstants.AT_INPUT_PULLUP:
+ command = [PrivateConstants.SET_PIN_MODE, pin_number,
+ PrivateConstants.AT_INPUT_PULLUP, 1]
+ self.digital_callbacks[pin_number] = callback
+ elif pin_state == PrivateConstants.AT_ANALOG:
+ command = [PrivateConstants.SET_PIN_MODE, pin_number,
+ PrivateConstants.AT_ANALOG,
+ differential >> 8, differential & 0xff, 1]
+ self.analog_callbacks[pin_number] = callback
+ elif pin_state == PrivateConstants.AT_OUTPUT:
+ command = [PrivateConstants.SET_PIN_MODE, pin_number,
+ PrivateConstants.AT_OUTPUT, 1]
+ else:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError('Unknown pin state')
+
+ if command:
+ await self._send_command(command)
+
+ await asyncio.sleep(.05)
+
+ async def servo_detach(self, pin_number):
+ """
+ Detach a servo for reuse
+ :param pin_number: attached pin
+ """
+ command = [PrivateConstants.SERVO_DETACH, pin_number]
+ await self._send_command(command)
+
+ async def servo_write(self, pin_number, angle):
+ """
+
+ Set a servo attached to a pin to a given angle.
+
+ :param pin_number: pin
+
+ :param angle: angle (0-180)
+
+ """
+ command = [PrivateConstants.SERVO_WRITE, pin_number, angle]
+ await self._send_command(command)
+
+ # async def stepper_move_to(self, motor_id, position):
+ # """
+ # Set an absolution target position. If position is positive, the movement is
+ # clockwise, else it is counter-clockwise.
+ #
+ # The run() function (below) will try to move the motor (at most one step per call)
+ # from the current position to the target position set by the most
+ # recent call to this function. Caution: moveTo() also recalculates the
+ # speed for the next step.
+ # If you are trying to use constant speed movements, you should call setSpeed()
+ # after calling moveTo().
+ #
+ # :param motor_id: motor id: 0 - 3
+ #
+ # :param position: target position. Maximum value is 32 bits.
+ # """
+ #
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_move_to: Invalid motor_id.')
+ #
+ # if position < 0:
+ # polarity = 1
+ # else:
+ # polarity = 0
+ # position = abs(position)
+ #
+ # position_bytes = list(position.to_bytes(4, 'big', signed=True))
+ #
+ # command = [PrivateConstants.STEPPER_MOVE_TO, motor_id]
+ # for value in position_bytes:
+ # command.append(value)
+ # command.append(polarity)
+ #
+ # await self._send_command(command)
+ #
+ # async def stepper_move(self, motor_id, relative_position):
+ # """
+ # Set the target position relative to the current position.
+ #
+ # :param motor_id: motor id: 0 - 3
+ #
+ # :param relative_position: The desired position relative to the current
+ # position. Negative is anticlockwise from
+ # the current position. Maximum value is 32 bits.
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_move: Invalid motor_id.')
+ #
+ # if relative_position < 0:
+ # polarity = 1
+ # else:
+ # polarity = 0
+ # position = abs(relative_position)
+ #
+ # position_bytes = list(position.to_bytes(4, 'big', signed=True))
+ #
+ # command = [PrivateConstants.STEPPER_MOVE, motor_id]
+ # for value in position_bytes:
+ # command.append(value)
+ # command.append(polarity)
+ # await self._send_command(command)
+ #
+ # async def stepper_run(self, motor_id, completion_callback=None):
+ # """
+ # This method steps the selected motor based on the current speed.
+ #
+ # Once called, the server will continuously attempt to step the motor.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param completion_callback: call back function to receive motion complete
+ # notification
+ #
+ # callback returns a data list:
+ #
+ # [report_type, motor_id, raw_time_stamp]
+ #
+ # The report_type = 19
+ # """
+ # if not completion_callback:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_run: A motion complete callback must be '
+ # 'specified.')
+ #
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_run: Invalid motor_id.')
+ #
+ # self.stepper_info_list[motor_id]['motion_complete_callback'] = completion_callback
+ # command = [PrivateConstants.STEPPER_RUN, motor_id]
+ # await self._send_command(command)
+ #
+ # async def stepper_run_speed(self, motor_id):
+ # """
+ # This method steps the selected motor based at a constant speed as set by the most
+ # recent call to stepper_set_max_speed(). The motor will run continuously.
+ #
+ # Once called, the server will continuously attempt to step the motor.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_run_speed: Invalid motor_id.')
+ #
+ # command = [PrivateConstants.STEPPER_RUN_SPEED, motor_id]
+ # await self._send_command(command)
+ #
+ # async def stepper_set_max_speed(self, motor_id, max_speed):
+ # """
+ # Sets the maximum permitted speed. The stepper_run() function will accelerate
+ # up to the speed set by this function.
+ #
+ # Caution: the maximum speed achievable depends on your processor and clock speed.
+ # The default maxSpeed is 1 step per second.
+ #
+ # Caution: Speeds that exceed the maximum speed supported by the processor may
+ # result in non-linear accelerations and decelerations.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param max_speed: 1 - 1000
+ # """
+ #
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_set_max_speed: Invalid motor_id.')
+ #
+ # if not 1 < max_speed <= 1000:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_set_max_speed: Speed range is 1 - 1000.')
+ #
+ # self.stepper_info_list[motor_id]['max_speed'] = max_speed
+ # max_speed_msb = (max_speed & 0xff00) >> 8
+ # max_speed_lsb = max_speed & 0xff
+ #
+ # command = [PrivateConstants.STEPPER_SET_MAX_SPEED, motor_id, max_speed_msb,
+ # max_speed_lsb]
+ # await self._send_command(command)
+ #
+ # async def stepper_get_max_speed(self, motor_id):
+ # """
+ # Returns the maximum speed configured for this stepper
+ # that was previously set by stepper_set_max_speed()
+ #
+ # Value is stored in the client, so no callback is required.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :return: The currently configured maximum speed.
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_max_speed: Invalid motor_id.')
+ #
+ # return self.stepper_info_list[motor_id]['max_speed']
+ #
+ # async def stepper_set_acceleration(self, motor_id, acceleration):
+ # """
+ # Sets the acceleration/deceleration rate.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param acceleration: The desired acceleration in steps per second
+ # per second. Must be > 0.0. This is an
+ # expensive call since it requires a square
+ # root to be calculated on the server.
+ # Dont call more often than needed.
+ #
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_set_acceleration: Invalid motor_id.')
+ #
+ # if not 1 < acceleration <= 1000:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_set_acceleration: Acceleration range is 1 - '
+ # '1000.')
+ #
+ # self.stepper_info_list[motor_id]['acceleration'] = acceleration
+ #
+ # max_accel_msb = acceleration >> 8
+ # max_accel_lsb = acceleration & 0xff
+ #
+ # command = [PrivateConstants.STEPPER_SET_ACCELERATION, motor_id, max_accel_msb,
+ # max_accel_lsb]
+ # await self._send_command(command)
+ #
+ # async def stepper_set_speed(self, motor_id, speed):
+ # """
+ # Sets the desired constant speed for use with stepper_run_speed().
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param speed: 0 - 1000 The desired constant speed in steps per
+ # second. Positive is clockwise. Speeds of more than 1000 steps per
+ # second are unreliable. Speed accuracy depends on the Arduino
+ # crystal. Jitter depends on how frequently you call the
+ # stepper_run_speed() method.
+ # The speed will be limited by the current value of
+ # stepper_set_max_speed().
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_set_speed: Invalid motor_id.')
+ #
+ # if not 0 < speed <= 1000:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_set_speed: Speed range is 0 - '
+ # '1000.')
+ #
+ # self.stepper_info_list[motor_id]['speed'] = speed
+ #
+ # speed_msb = speed >> 8
+ # speed_lsb = speed & 0xff
+ #
+ # command = [PrivateConstants.STEPPER_SET_SPEED, motor_id, speed_msb, speed_lsb]
+ # await self._send_command(command)
+ #
+ # async def stepper_get_speed(self, motor_id):
+ # """
+ # Returns the most recently set speed.
+ # that was previously set by stepper_set_speed();
+ #
+ # Value is stored in the client, so no callback is required.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_get_speed: Invalid motor_id.')
+ #
+ # return self.stepper_info_list[motor_id]['speed']
+ #
+ # async def stepper_get_distance_to_go(self, motor_id, distance_to_go_callback):
+ # """
+ # Request the distance from the current position to the target position
+ # from the server.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param distance_to_go_callback: required callback function to receive report
+ #
+ # :return: The distance to go is returned via the callback as a list:
+ #
+ # [REPORT_TYPE=15, motor_id, distance in steps, time_stamp]
+ #
+ # A positive distance is clockwise from the current position.
+ #
+ # """
+ # if not distance_to_go_callback:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_get_distance_to_go Read: A callback function must be specified.')
+ #
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_get_distance_to_go: Invalid motor_id.')
+ # self.stepper_info_list[motor_id][
+ # 'distance_to_go_callback'] = distance_to_go_callback
+ # command = [PrivateConstants.STEPPER_GET_DISTANCE_TO_GO, motor_id]
+ # await self._send_command(command)
+ #
+ # async def stepper_get_target_position(self, motor_id, target_callback):
+ # """
+ # Request the most recently set target position from the server.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param target_callback: required callback function to receive report
+ #
+ # :return: The distance to go is returned via the callback as a list:
+ #
+ # [REPORT_TYPE=16, motor_id, target position in steps, time_stamp]
+ #
+ # Positive is clockwise from the 0 position.
+ #
+ # """
+ # if not target_callback:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError(
+ # 'stepper_get_target_position Read: A callback function must be specified.')
+ #
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_get_target_position: Invalid motor_id.')
+ #
+ # self.stepper_info_list[motor_id][
+ # 'target_position_callback'] = target_callback
+ #
+ # command = [PrivateConstants.STEPPER_GET_TARGET_POSITION, motor_id]
+ # await self._send_command(command)
+ #
+ # async def stepper_get_current_position(self, motor_id, current_position_callback):
+ # """
+ # Request the current motor position from the server.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param current_position_callback: required callback function to receive report
+ #
+ # :return: The current motor position returned via the callback as a list:
+ #
+ # [REPORT_TYPE=17, motor_id, current position in steps, time_stamp]
+ #
+ # Positive is clockwise from the 0 position.
+ # """
+ # if not current_position_callback:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError(
+ # 'stepper_get_current_position Read: A callback function must be specified.')
+ #
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_get_current_position: Invalid motor_id.')
+ #
+ # self.stepper_info_list[motor_id]['current_position_callback'] = current_position_callback
+ #
+ # command = [PrivateConstants.STEPPER_GET_CURRENT_POSITION, motor_id]
+ # await self._send_command(command)
+ #
+ # async def stepper_set_current_position(self, motor_id, position):
+ # """
+ # Resets the current position of the motor, so that wherever the motor
+ # happens to be right now is considered to be the new 0 position. Useful
+ # for setting a zero position on a stepper after an initial hardware
+ # positioning move.
+ #
+ # Has the side effect of setting the current motor speed to 0.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param position: Position in steps. This is a 32 bit value
+ # """
+ #
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_set_current_position: Invalid motor_id.')
+ # position_bytes = list(position.to_bytes(4, 'big', signed=True))
+ #
+ # command = [PrivateConstants.STEPPER_SET_CURRENT_POSITION, motor_id]
+ # for value in position_bytes:
+ # command.append(value)
+ # await self._send_command(command)
+ #
+ # async def stepper_run_speed_to_position(self, motor_id, completion_callback=None):
+ # """
+ # Runs the motor at the currently selected speed until the target position is
+ # reached.
+ #
+ # Does not implement accelerations.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param completion_callback: call back function to receive motion complete
+ # notification
+ #
+ # callback returns a data list:
+ #
+ # [report_type, motor_id, raw_time_stamp]
+ #
+ # The report_type = 19
+ # """
+ # if not completion_callback:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_run_speed_to_position: A motion complete '
+ # 'callback must be '
+ # 'specified.')
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_run_speed_to_position: Invalid motor_id.')
+ #
+ # self.stepper_info_list[motor_id]['motion_complete_callback'] = completion_callback
+ # command = [PrivateConstants.STEPPER_RUN_SPEED_TO_POSITION, motor_id]
+ # await self._send_command(command)
+ #
+ # async def stepper_stop(self, motor_id):
+ # """
+ # Sets a new target position that causes the stepper
+ # to stop as quickly as possible, using the current speed and
+ # acceleration parameters.
+ #
+ # :param motor_id: 0 - 3
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_stop: Invalid motor_id.')
+ #
+ # command = [PrivateConstants.STEPPER_STOP, motor_id]
+ # await self._send_command(command)
+ #
+ # async def stepper_disable_outputs(self, motor_id):
+ # """
+ # Disable motor pin outputs by setting them all LOW.
+ #
+ # Depending on the design of your electronics this may turn off
+ # the power to the motor coils, saving power.
+ #
+ # This is useful to support Arduino low power modes: disable the outputs
+ # during sleep and then re-enable with enableOutputs() before stepping
+ # again.
+ #
+ # If the enable Pin is defined, sets it to OUTPUT mode and clears
+ # the pin to disabled.
+ #
+ # :param motor_id: 0 - 3
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_disable_outputs: Invalid motor_id.')
+ #
+ # command = [PrivateConstants.STEPPER_DISABLE_OUTPUTS, motor_id]
+ # await self._send_command(command)
+ #
+ # async def stepper_enable_outputs(self, motor_id):
+ # """
+ # Enable motor pin outputs by setting the motor pins to OUTPUT
+ # mode.
+ #
+ # If the enable Pin is defined, sets it to OUTPUT mode and sets
+ # the pin to enabled.
+ #
+ # :param motor_id: 0 - 3
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_enable_outputs: Invalid motor_id.')
+ #
+ # command = [PrivateConstants.STEPPER_ENABLE_OUTPUTS, motor_id]
+ # await self._send_command(command)
+ #
+ # async def stepper_set_min_pulse_width(self, motor_id, minimum_width):
+ # """
+ # Sets the minimum pulse width allowed by the stepper driver.
+ #
+ # The minimum practical pulse width is approximately 20 microseconds.
+ #
+ # Times less than 20 microseconds will usually result in 20 microseconds or so.
+ #
+ # :param motor_id: 0 -3
+ #
+ # :param minimum_width: A 16 bit unsigned value expressed in microseconds.
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_set_min_pulse_width: Invalid motor_id.')
+ #
+ # if not 0 < minimum_width <= 0xff:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_set_min_pulse_width: Pulse width range = '
+ # '0-0xffff.')
+ #
+ # width_msb = minimum_width >> 8
+ # width_lsb = minimum_width & 0xff
+ #
+ # command = [PrivateConstants.STEPPER_SET_MINIMUM_PULSE_WIDTH, motor_id, width_msb,
+ # width_lsb]
+ # await self._send_command(command)
+ #
+ # async def stepper_set_enable_pin(self, motor_id, pin=0xff):
+ # """
+ # Sets the enable pin number for stepper drivers.
+ # 0xFF indicates unused (default).
+ #
+ # Otherwise, if a pin is set, the pin will be turned on when
+ # enableOutputs() is called and switched off when disableOutputs()
+ # is called.
+ #
+ # :param motor_id: 0 - 4
+ # :param pin: 0-0xff
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_set_enable_pin: Invalid motor_id.')
+ #
+ # if not 0 < pin <= 0xff:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_set_enable_pin: Pulse width range = '
+ # '0-0xff.')
+ # command = [PrivateConstants.STEPPER_SET_ENABLE_PIN, motor_id, pin]
+ #
+ # await self._send_command(command)
+ #
+ # async def stepper_set_3_pins_inverted(self, motor_id, direction=False, step=False,
+ # enable=False):
+ # """
+ # Sets the inversion for stepper driver pins.
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param direction: True=inverted or False
+ #
+ # :param step: True=inverted or False
+ #
+ # :param enable: True=inverted or False
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_set_3_pins_inverted: Invalid motor_id.')
+ #
+ # command = [PrivateConstants.STEPPER_SET_3_PINS_INVERTED, motor_id, direction,
+ # step, enable]
+ #
+ # await self._send_command(command)
+ #
+ # async def stepper_set_4_pins_inverted(self, motor_id, pin1_invert=False,
+ # pin2_invert=False,
+ # pin3_invert=False, pin4_invert=False, enable=False):
+ # """
+ # Sets the inversion for 2, 3 and 4 wire stepper pins
+ #
+ # :param motor_id: 0 - 3
+ #
+ # :param pin1_invert: True=inverted or False
+ #
+ # :param pin2_invert: True=inverted or False
+ #
+ # :param pin3_invert: True=inverted or False
+ #
+ # :param pin4_invert: True=inverted or False
+ #
+ # :param enable: True=inverted or False
+ # """
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_set_4_pins_inverted: Invalid motor_id.')
+ #
+ # command = [PrivateConstants.STEPPER_SET_4_PINS_INVERTED, motor_id, pin1_invert,
+ # pin2_invert, pin3_invert, pin4_invert, enable]
+ #
+ # await self._send_command(command)
+ #
+ # async def stepper_is_running(self, motor_id, callback):
+ # """
+ # Checks to see if the motor is currently running to a target.
+ #
+ # Callback return True if the speed is not zero or not at the target position.
+ #
+ # :param motor_id: 0-4
+ #
+ # :param callback: required callback function to receive report
+ #
+ # :return: The current running state returned via the callback as a list:
+ #
+ # [REPORT_TYPE=18, motor_id, True or False for running state, time_stamp]
+ # """
+ # if not callback:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError(
+ # 'stepper_is_running: A callback function must be specified.')
+ #
+ # if not self.stepper_info_list[motor_id]['instance']:
+ # if self.shutdown_on_exception:
+ # await self.shutdown()
+ # raise RuntimeError('stepper_is_running: Invalid motor_id.')
+ #
+ # self.stepper_info_list[motor_id]['is_running_callback'] = callback
+ #
+ # command = [PrivateConstants.STEPPER_IS_RUNNING, motor_id]
+ # await self._send_command(command)
+
+ async def shutdown(self):
+ """
+ This method attempts an orderly shutdown
+ If any exceptions are thrown, they are ignored.
+
+ """
+ self.shutdown_flag = True
+
+ if self.hard_reset_on_shutdown:
+ await self.r4_hard_reset()
+ # stop all reporting - both analog and digital
+ try:
+ if self.serial_port:
+ command = [PrivateConstants.STOP_ALL_REPORTS]
+ await self._send_command(command)
+
+ await asyncio.sleep(.5)
+
+ await self.serial_port.reset_input_buffer()
+ await self.serial_port.close()
+ if self.close_loop_on_shutdown:
+ self.loop.stop()
+ elif self.sock:
+ command = [PrivateConstants.STOP_ALL_REPORTS]
+ await self._send_command(command)
+ self.the_task.cancel()
+ await asyncio.sleep(.5)
+ if self.close_loop_on_shutdown:
+ self.loop.stop()
+ except (RuntimeError, SerialException):
+ pass
+
+ async def r4_hard_reset(self):
+ """
+ Place the r4 into hard reset
+ """
+ command = [PrivateConstants.BOARD_HARD_RESET, 1]
+ await self._send_command(command)
+
+ async def disable_all_reporting(self):
+ """
+ Disable reporting for all digital and analog input pins
+ """
+ command = [PrivateConstants.MODIFY_REPORTING,
+ PrivateConstants.REPORTING_DISABLE_ALL, 0]
+ await self._send_command(command)
+
+ async def disable_analog_reporting(self, pin):
+ """
+ Disables analog reporting for a single analog pin.
+
+ :param pin: Analog pin number. For example for A0, the number is 0.
+
+ """
+ command = [PrivateConstants.MODIFY_REPORTING,
+ PrivateConstants.REPORTING_ANALOG_DISABLE, pin]
+ await self._send_command(command)
+
+ async def disable_digital_reporting(self, pin):
+ """
+ Disables digital reporting for a single digital pin
+
+
+ :param pin: pin number
+
+ """
+ command = [PrivateConstants.MODIFY_REPORTING,
+ PrivateConstants.REPORTING_DIGITAL_DISABLE, pin]
+ await self._send_command(command)
+
+ async def enable_analog_reporting(self, pin):
+ """
+ Enables analog reporting for the specified pin.
+
+ :param pin: Analog pin number. For example for A0, the number is 0.
+
+
+ """
+ command = [PrivateConstants.MODIFY_REPORTING,
+ PrivateConstants.REPORTING_ANALOG_ENABLE, pin]
+ await self._send_command(command)
+
+ async def enable_digital_reporting(self, pin):
+ """
+ Enable reporting on the specified digital pin.
+
+ :param pin: Pin number.
+ """
+
+ command = [PrivateConstants.MODIFY_REPORTING,
+ PrivateConstants.REPORTING_DIGITAL_ENABLE, pin]
+ await self._send_command(command)
+
+ async def enable_scroll_message(self, message, scroll_speed=50):
+ """
+
+ :param message: Message with maximum length of 25
+ :param scroll_speed: in milliseconds (maximum of 255)
+ """
+ if len(message) > 25:
+ raise RuntimeError("Scroll message size is maximum of 25 characters.")
+
+ if scroll_speed > 255:
+ raise RuntimeError("Scroll speed maximum of 255 milliseconds.")
+
+ message = message.encode()
+ command = [PrivateConstants.SCROLL_MESSAGE_ON, len(message), scroll_speed]
+ for x in message:
+ command.append(x)
+ await self._send_command(command)
+
+ async def disable_scroll_message(self):
+ """
+ Turn off a scrolling message
+ """
+
+ command = [PrivateConstants.SCROLL_MESSAGE_OFF]
+ await self._send_command(command)
+
+ async def _arduino_report_dispatcher(self):
+ """
+ This is a private method.
+ It continually accepts and interprets data coming from Telemetrix4Arduino,and then
+ dispatches the correct handler to process the data.
+
+ It first receives the length of the packet, and then reads in the rest of the
+ packet. A packet consists of a length, report identifier and then the report data.
+ Using the report identifier, the report handler is fetched from report_dispatch.
+
+ :returns: This method never returns
+ """
+
+ while True:
+ if self.shutdown_flag:
+ break
+ try:
+ if not self.transport_address:
+ packet_length = await self.serial_port.read()
+ else:
+
+ packet_length = ord(await self.sock.read())
+ except TypeError:
+ continue
+
+ # get the rest of the packet
+ if not self.transport_address:
+ packet = await self.serial_port.read(packet_length)
+ else:
+ packet = list(await self.sock.read(packet_length))
+ if len(packet) != packet_length:
+ continue
+ # print(f'packet.len() {}')
+ # await asyncio.sleep(.1)
+
+ report = packet[0]
+ # print(report)
+ # handle all other messages by looking them up in the
+ # command dictionary
+
+ await self.report_dispatch[report](packet[1:])
+ await asyncio.sleep(self.sleep_tune)
+
+ async def _ble_report_dispatcher(self, sender=None, data=None):
+ """
+ This is a private method called by the incoming data notifier
+
+ Using the report identifier, the report handler is fetched from report_dispatch.
+
+ :param sender: BLE sender ID
+ :param data: data received over the ble link
+
+ """
+ self.the_sender = sender
+ data = list(data)
+ report = data[1]
+
+ if report == 5: # get firmware data reply
+ self.firmware_version = list(data)
+ print()
+ # noinspection PyArgumentList
+ else:
+ await self.report_dispatch[report](data[2:])
+
+ '''
+ Report message handlers
+ '''
+
+ async def _report_loop_data(self, data):
+ """
+ Print data that was looped back
+
+ :param data: byte of loop back data
+ """
+ if self.loop_back_callback:
+ await self.loop_back_callback(data)
+
+ async def _spi_report(self, report):
+ report = list(report)
+ cb_list = [PrivateConstants.SPI_REPORT, report[0]] + report[1:]
+
+ cb_list.append(time.time())
+
+ await self.spi_callback(cb_list)
+
+ async def _onewire_report(self, report):
+ report = list(report)
+
+ cb_list = [PrivateConstants.ONE_WIRE_REPORT, report[0]] + report[1:]
+ cb_list.append(time.time())
+ await self.onewire_callback(cb_list)
+
+ async def _report_debug_data(self, data):
+ """
+ Print debug data sent from Arduino
+
+ :param data: data[0] is a byte followed by 2
+ bytes that comprise an integer
+ """
+ value = (data[1] << 8) + data[2]
+ print(f'DEBUG ID: {data[0]} Value: {value}')
+
+ async def _analog_message(self, data):
+ """
+ This is a private message handler method.
+ It is a message handler for analog messages.
+
+ :param data: message data
+
+ """
+ pin = data[0]
+ value = (data[1] << 8) + data[2]
+
+ time_stamp = time.time()
+
+ # append pin number, pin value, and pin type to return value and return as a list
+ message = [PrivateConstants.AT_ANALOG, pin, value, time_stamp]
+
+ await self.analog_callbacks[pin](message)
+
+ async def _dht_report(self, data):
+ """
+ This is a private message handler for dht reports
+
+ :param data: data[0] = report error return
+ No Errors = 0
+
+ Checksum Error = 1
+
+ Timeout Error = 2
+
+ Invalid Value = 999
+
+ data[1] = pin number
+
+ data[2] = dht type 11 or 22
+
+ data[3] = humidity positivity flag
+
+ data[4] = temperature positivity value
+
+ data[5] = humidity integer
+
+ data[6] = humidity fractional value
+
+ data[7] = temperature integer
+
+ data[8] = temperature fractional value
+ """
+ data = list(data)
+ if data[0]: # DHT_ERROR
+ # error report
+ # data[0] = report sub type, data[1] = pin, data[2] = error message
+ if self.dht_callbacks[data[1]]:
+ # Callback 0=DHT REPORT, DHT_ERROR, PIN, Time
+ message = [PrivateConstants.DHT_REPORT, data[0], data[1], data[2],
+ time.time()]
+ await self.dht_callbacks[data[1]](message)
+ else:
+ # got valid data DHT_DATA
+ f_humidity = float(data[5] + data[6] / 100)
+ if data[3]:
+ f_humidity *= -1.0
+ f_temperature = float(data[7] + data[8] / 100)
+ if data[4]:
+ f_temperature *= -1.0
+ message = [PrivateConstants.DHT_REPORT, data[0], data[1], data[2],
+ f_humidity, f_temperature, time.time()]
+
+ await self.dht_callbacks[data[1]](message)
+
+ async def _digital_message(self, data):
+ """
+ This is a private message handler method.
+ It is a message handler for Digital Messages.
+
+ :param data: digital message
+
+ """
+ pin = data[0]
+ value = data[1]
+
+ time_stamp = time.time()
+ if self.digital_callbacks[pin]:
+ message = [PrivateConstants.DIGITAL_REPORT, pin, value, time_stamp]
+ await self.digital_callbacks[pin](message)
+
+ async def _servo_unavailable(self, report):
+ """
+ Message if no servos are available for use.
+
+ :param report: pin number
+ """
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(
+ f'Servo Attach For Pin {report[0]} Failed: No Available Servos')
+
+ async def _i2c_read_report(self, data):
+ """
+ Execute callback for i2c reads.
+
+ :param data: [I2C_READ_REPORT, i2c_port, number of bytes read, address, register, bytes read..., time-stamp]
+ """
+
+ # we receive [# data bytes, address, register, data bytes]
+ # number of bytes of data returned
+
+ # data[0] = number of bytes
+ # data[1] = i2c_port
+ # data[2] = number of bytes returned
+ # data[3] = address
+ # data[4] = register
+ # data[5] ... all the data bytes
+ data = list(data)
+ cb_list = [PrivateConstants.I2C_READ_REPORT, data[0], data[1]] + data[2:]
+ cb_list.append(time.time())
+
+ if cb_list[1]:
+ await self.i2c_callback2(cb_list)
+ else:
+ await self.i2c_callback(cb_list)
+
+ async def _i2c_too_few(self, data):
+ """
+ I2c reports too few bytes received
+
+ :param data: data[0] = device address
+ """
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(
+ f'i2c too few bytes received from i2c port {data[0]} i2c address {data[1]}')
+
+ async def _i2c_too_many(self, data):
+ """
+ I2c reports too few bytes received
+
+ :param data: data[0] = device address
+ """
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError(
+ f'i2c too many bytes received from i2c port {data[0]} i2c address {data[1]}')
+
+ async def _sonar_distance_report(self, report):
+ """
+
+ :param report: data[0] = trigger pin, data[1] and data[2] = distance
+
+ callback report format: [PrivateConstants.SONAR_DISTANCE, trigger_pin, distance_value, time_stamp]
+ """
+ report = list(report)
+ # get callback from pin number
+ cb = self.sonar_callbacks[report[0]]
+
+ # build report data
+ cb_list = [PrivateConstants.SONAR_DISTANCE, report[0],
+ ((report[1] << 8) + report[2]), time.time()]
+
+ await cb(cb_list)
+
+ async def _stepper_distance_to_go_report(self, report):
+ return # for now
+ # """
+ # Report stepper distance to go.
+ #
+ # :param report: data[0] = motor_id, data[1] = steps MSB, data[2] = steps byte 1,
+ # data[3] = steps bytes 2, data[4] = steps LSB
+ #
+ # callback report format: [PrivateConstants.STEPPER_DISTANCE_TO_GO, motor_id
+ # steps, time_stamp]
+ # """
+ # report = list(report)
+ # # get callback
+ # cb = self.stepper_info_list[report[0]]['distance_to_go_callback']
+ #
+ # # isolate the steps bytes and covert list to bytes
+ # steps = bytes(report[1:])
+ #
+ # # get value from steps
+ # num_steps = int.from_bytes(steps, byteorder='big', signed=True)
+ #
+ # cb_list = [PrivateConstants.STEPPER_DISTANCE_TO_GO, report[0], num_steps,
+ # time.time()]
+ #
+ # await cb(cb_list)
+ #
+
+ async def _stepper_target_position_report(self, report):
+ return # for now
+ # """
+ # Report stepper target position to go.
+ #
+ # :param report: data[0] = motor_id, data[1] = target position MSB,
+ # data[2] = target position byte MSB+1
+ # data[3] = target position byte MSB+2
+ # data[4] = target position LSB
+ #
+ # callback report format: [PrivateConstants.STEPPER_TARGET_POSITION, motor_id
+ # target_position, time_stamp]
+ # """
+ # report = list(report)
+ # # get callback
+ # cb = self.stepper_info_list[report[0]]['target_position_callback']
+ #
+ # # isolate the steps bytes and covert list to bytes
+ # target = bytes(report[1:])
+ #
+ # # get value from steps
+ # target_position = int.from_bytes(target, byteorder='big', signed=True)
+ #
+ # cb_list = [PrivateConstants.STEPPER_TARGET_POSITION, report[0], target_position,
+ # time.time()]
+ #
+ # await cb(cb_list)
+ #
+
+ async def _stepper_current_position_report(self, report):
+ return # for now
+ # """
+ # Report stepper current position.
+ #
+ # :param report: data[0] = motor_id, data[1] = current position MSB,
+ # data[2] = current position byte MSB+1
+ # data[3] = current position byte MSB+2
+ # data[4] = current position LSB
+ #
+ # callback report format: [PrivateConstants.STEPPER_CURRENT_POSITION, motor_id
+ # current_position, time_stamp]
+ # """
+ # report = list(report)
+
+ # # get callback
+ # cb = self.stepper_info_list[report[0]]['current_position_callback']
+ #
+ # # isolate the steps bytes and covert list to bytes
+ # position = bytes(report[1:])
+ #
+ # # get value from steps
+ # current_position = int.from_bytes(position, byteorder='big', signed=True)
+ #
+ # cb_list = [PrivateConstants.STEPPER_CURRENT_POSITION, report[0], current_position,
+ # time.time()]
+ #
+ # await cb(cb_list)
+ #
+
+ async def _stepper_is_running_report(self, report):
+ return # for now
+ # """
+ # Report if the motor is currently running
+ #
+ # :param report: data[0] = motor_id, True if motor is running or False if it is not.
+ #
+ # callback report format: [18, motor_id,
+ # running_state, time_stamp]
+ # """
+ # report = list(report)
+
+ # # get callback
+ # cb = self.stepper_info_list[report[0]]['is_running_callback']
+ #
+ # cb_list = [PrivateConstants.STEPPER_RUNNING_REPORT, report[0], time.time()]
+ #
+ # await cb(cb_list)
+ #
+
+ async def _stepper_run_complete_report(self, report):
+ return # for now
+ # """
+ # The motor completed it motion
+ #
+ # :param report: data[0] = motor_id
+ #
+ # callback report format: [PrivateConstants.STEPPER_RUN_COMPLETE_REPORT, motor_id,
+ # time_stamp]
+ # """
+ # report = list(report)
+ # # get callback
+ # cb = self.stepper_info_list[report[0]]['motion_complete_callback']
+ #
+ # cb_list = [PrivateConstants.STEPPER_RUN_COMPLETE_REPORT, report[0],
+ # time.time()]
+ #
+ # await cb(cb_list)
+
+ async def _features_report(self, report):
+ self.reported_features = report[0]
+
+ async def _send_command(self, command):
+ """
+ This is a private utility method.
+
+
+ :param command: command data in the form of a list
+
+ :returns: number of bytes sent
+ """
+ # the length of the list is added at the head
+ # the length of the list is added at the head
+ command.insert(0, len(command))
+ send_message = bytes(command)
+
+ if self.transport_type == 1:
+ try:
+ await self.serial_port.write(send_message)
+ except SerialException:
+ if self.shutdown_on_exception:
+ await self.shutdown()
+ raise RuntimeError('write fail in _send_command')
+ elif self.transport_type == 0:
+ await self.sock.write(send_message)
+ else:
+ await self.ble_instance.write(send_message)