├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.md ├── README.rst ├── doc ├── Makefile ├── _static │ └── .notempty ├── conf.py ├── index.rst ├── intro.rst ├── make.bat └── modules.rst ├── examples ├── rcpy_adc.py ├── rcpy_bare_minimum.py ├── rcpy_bare_minimum_dmp.py ├── rcpy_bare_minimum_thread.py ├── rcpy_blink.py ├── rcpy_test_dmp.py ├── rcpy_test_encoders.py ├── rcpy_test_imu.py ├── rcpy_test_leds.py ├── rcpy_test_motors.py └── rcpy_test_servos.py ├── rcpy ├── __init__.py ├── adc.py ├── button.py ├── clock.py ├── encoder.py ├── gpio.py ├── led.py ├── motor.py ├── mpu9250.py └── servo.py ├── setup.cfg ├── setup.py ├── src ├── _adc.c ├── _encoder.c ├── _motor.c ├── _mpu9250.c ├── _rcpy.c └── _servo.c └── test ├── __init__.py ├── test_clock.py ├── test_encoder.py ├── test_led.py ├── test_motor.py ├── test_mpu9250.py └── test_rcpy.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask stuff: 57 | instance/ 58 | .webassets-cache 59 | 60 | # Scrapy stuff: 61 | .scrapy 62 | 63 | # Sphinx documentation 64 | doc/_build/ 65 | 66 | # PyBuilder 67 | target/ 68 | 69 | # IPython Notebook 70 | .ipynb_checkpoints 71 | 72 | # pyenv 73 | .python-version 74 | 75 | # celery beat schedule file 76 | celerybeat-schedule 77 | 78 | # dotenv 79 | .env 80 | 81 | # virtualenv 82 | .venv/ 83 | venv/ 84 | ENV/ 85 | 86 | # Spyder project settings 87 | .spyderproject 88 | 89 | # Rope project settings 90 | .ropeproject 91 | 92 | # OSC .DS_Store 93 | .DS_Store 94 | 95 | # Local settings 96 | aed/settings.py 97 | 98 | # Emacs backup 99 | *~ 100 | 101 | # Object files 102 | *.o 103 | 104 | python/source/mpu9150/test -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Mauricio de Oliveira 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | rcpy: Python 3 Interface for the Robotics Cape on the Beaglebone Black and the Beaglebone Blue 2 | ============================================================================================== 3 | 4 | **Release 0.5** 5 | 6 | This is a Python library with bindings for some of the functionality of the [Robotics Cape library](https://github.com/StrawsonDesign/Robotics_Cape_Installer). 7 | 8 | We only support Python 3, and are not interested in Python 2 at all. 9 | 10 | Not all functions are supported. Currently supported functions are: 11 | 12 | 1. MPU9250 13 | 2. PWM motors 14 | 3. Servos and ESCs 15 | 4. Encoders 16 | 5. LEDs 17 | 6. Buttons 18 | 7. GPIO inputs 19 | 8. ADCs 20 | 21 | You might also be interested in the `pyctrl` package ([github](https://github.com/mcdeoliveira/pyctrl), [PyPI](https://pypi.python.org/pypi?:action=display&name=pyctrl)). 22 | 23 | Preliminaries 24 | ------------- 25 | 26 | You will need the [Robotics Cape library](https://github.com/StrawsonDesign/Robotics_Cape_Installer) version 0.3.4 or higher. Depending on the image you have on your Beaglebone Black or Blue it might already be installed! You can check if it is installed and the current version by running 27 | 28 | rc_version 29 | 30 | on your Beaglebone. If the command `rc_version` is not found type 31 | 32 | sudo apt-get update 33 | sudo apt-get install roboticscape 34 | 35 | to install or 36 | 37 | sudo apt-get update 38 | sudo apt-get upgrade roboticscape 39 | 40 | to upgrade from an older version. For more details see these [instructions](http://strawsondesign.com/#!manual-install). 41 | 42 | You must also have python3 installed. If you have not installed python3 yet type 43 | 44 | sudo apt install python3 python3-pip 45 | 46 | to install python3 and pip3. 47 | 48 | Installation 49 | ------------ 50 | 51 | Starting with version 0.5, rcpy relies on [libgpiod](https://github.com/brgl/libgpiod) for its gpio access. As of now, you have to manually install this library as follows. 52 | 53 | ### Install some dependencies used by autoconf 54 | 55 | sudo apt install autoconf-archive 56 | 57 | ### Clone repo, build and make 58 | 59 | git clone https://github.com/brgl/libgpiod 60 | cd libgpiod 61 | ./autogen.sh --enable-tools=yes --enable-bindings-python --prefix=/usr/local 62 | make 63 | sudo make install 64 | 65 | ### Move packages to /usr/local/lib/python3.5/dist-packages 66 | 67 | sudo mv /usr/local/lib/python3.5/site-packages/* /usr/local/lib/python3.5/dist-packages/. 68 | 69 | ### Install from PyPI 70 | 71 | Starting with version 0.3, `rcpy` is available from [PyPI](https://pypi.python.org/pypi?:action=display&name=rcpy). Just type 72 | 73 | sudo pip3 install rcpy 74 | 75 | to download and complete installation. 76 | 77 | Documentation: 78 | -------------- 79 | 80 | - [HTML](http://guitar.ucsd.edu/rcpy/html/index.html) 81 | - [PDF](http://guitar.ucsd.edu/rcpy/rcpy.pdf) 82 | 83 | Author: 84 | ------- 85 | 86 | [Mauricio C. de Oliveira](http://control.ucsd.edu/mauricio) 87 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | rcpy: Python 3 Interface for the Robotics Cape on the Beaglebone Black and the Beaglebone Blue 2 | ============================================================================================== 3 | 4 | **Release 0.5** 5 | 6 | This is a Python library with bindings for some of the functionality of 7 | the `Robotics Cape 8 | library `__. 9 | 10 | We only support Python 3, and are not interested in Python 2 at all. 11 | 12 | Not all functions are supported. Currently supported functions are: 13 | 14 | 1. MPU9250 15 | 2. PWM motors 16 | 3. Servos and ESCs 17 | 4. Encoders 18 | 5. LEDs 19 | 6. Buttons 20 | 7. GPIO inputs 21 | 8. ADCs 22 | 23 | You might also be interested in the ``pyctrl`` package 24 | (`github `__, 25 | `PyPI `__). 26 | 27 | Preliminaries 28 | ------------- 29 | 30 | You will need the `Robotics Cape 31 | library `__ 32 | version 0.3.4 or higher. Depending on the image you have on your 33 | Beaglebone Black or Blue it might already be installed! You can check if 34 | it is installed and the current version by running 35 | 36 | :: 37 | 38 | rc_version 39 | 40 | on your Beaglebone. If the command ``rc_version`` is not found type 41 | 42 | :: 43 | 44 | sudo apt-get update 45 | sudo apt-get install roboticscape 46 | 47 | to install or 48 | 49 | :: 50 | 51 | sudo apt-get update 52 | sudo apt-get upgrade roboticscape 53 | 54 | to upgrade from an older version. For more details see these 55 | `instructions `__. 56 | 57 | You must also have python3 installed. If you have not installed python3 58 | yet type 59 | 60 | :: 61 | 62 | sudo apt install python3 python3-pip 63 | 64 | to install python3 and pip3. 65 | 66 | Installation 67 | ------------ 68 | 69 | Starting with version 0.5, `rcpy` relies on `libgpiod 70 | `__ for its gpio access. As of now, 71 | you have to manually install this library as follows. 72 | 73 | Install some dependencies used by autoconf 74 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 75 | 76 | :: 77 | 78 | sudo apt install autoconf-archive 79 | 80 | Clone repo, build and make 81 | ^^^^^^^^^^^^^^^^^^^^^^^^^^ 82 | 83 | :: 84 | 85 | git clone https://github.com/brgl/libgpiod 86 | cd libgpiod 87 | ./autogen.sh --enable-tools=yes --enable-bindings-python --prefix=/usr/local 88 | make 89 | sudo make install 90 | 91 | Move packages to /usr/local/lib/python3.5/dist-packages 92 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 93 | 94 | :: 95 | 96 | sudo mv /usr/local/lib/python3.5/site-packages/* /usr/local/lib/python3.5/dist-packages/. 97 | 98 | Install from PyPI 99 | ^^^^^^^^^^^^^^^^^ 100 | 101 | Starting with version 0.3, ``rcpy`` is available from 102 | `PyPI `__. Just 103 | type 104 | 105 | :: 106 | 107 | sudo pip3 install rcpy 108 | 109 | to download and complete installation. 110 | 111 | Documentation: 112 | -------------- 113 | 114 | - `HTML `__ 115 | 116 | - `PDF `__ 117 | 118 | Author: 119 | ------- 120 | 121 | `Mauricio C. de Oliveira `__ 122 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SPHINXPROJ = rcpy 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /doc/_static/.notempty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcdeoliveira/rcpy/2879b01390c6dcbd8fb299e3c5299848d6c7fb71/doc/_static/.notempty -------------------------------------------------------------------------------- /doc/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # rcpy documentation build configuration file, created by 5 | # sphinx-quickstart on Sat May 20 09:36:24 2017. 6 | # 7 | # This file is execfile()d with the current directory set to its 8 | # containing dir. 9 | # 10 | # Note that not all possible configuration values are present in this 11 | # autogenerated file. 12 | # 13 | # All configuration values have a default; values that are commented out 14 | # serve to show the default. 15 | 16 | # If extensions (or modules to document with autodoc) are in another directory, 17 | # add these directories to sys.path here. If the directory is relative to the 18 | # documentation root, use os.path.abspath to make it absolute, like shown here. 19 | # 20 | # import os 21 | # import sys 22 | # sys.path.insert(0, os.path.abspath('.')) 23 | 24 | 25 | # -- General configuration ------------------------------------------------ 26 | 27 | # If your documentation needs a minimal Sphinx version, state it here. 28 | # 29 | # needs_sphinx = '1.0' 30 | 31 | # Add any Sphinx extension module names here, as strings. They can be 32 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 33 | # ones. 34 | extensions = ['sphinx.ext.mathjax'] 35 | 36 | # Add any paths that contain templates here, relative to this directory. 37 | templates_path = ['_templates'] 38 | 39 | # The suffix(es) of source filenames. 40 | # You can specify multiple suffix as a list of string: 41 | # 42 | # source_suffix = ['.rst', '.md'] 43 | source_suffix = '.rst' 44 | 45 | # The master toctree document. 46 | master_doc = 'index' 47 | 48 | # General information about the project. 49 | project = 'rcpy' 50 | copyright = '2017, Mauricio C. de Oliveira' 51 | author = 'Mauricio C. de Oliveira' 52 | 53 | # The version info for the project you're documenting, acts as replacement for 54 | # |version| and |release|, also used in various other places throughout the 55 | # built documents. 56 | # 57 | # The short X.Y version. 58 | version = '0.5' 59 | # The full version, including alpha/beta/rc tags. 60 | release = '0.5' 61 | 62 | # The language for content autogenerated by Sphinx. Refer to documentation 63 | # for a list of supported languages. 64 | # 65 | # This is also used if you do content translation via gettext catalogs. 66 | # Usually you set "language" from the command line for these cases. 67 | language = None 68 | 69 | # List of patterns, relative to source directory, that match files and 70 | # directories to ignore when looking for source files. 71 | # This patterns also effect to html_static_path and html_extra_path 72 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 73 | 74 | # The name of the Pygments (syntax highlighting) style to use. 75 | pygments_style = 'sphinx' 76 | 77 | # If true, `todo` and `todoList` produce output, else they produce nothing. 78 | todo_include_todos = False 79 | 80 | 81 | # -- Options for HTML output ---------------------------------------------- 82 | 83 | # The theme to use for HTML and HTML Help pages. See the documentation for 84 | # a list of builtin themes. 85 | # 86 | #html_theme = 'alabaster' 87 | html_theme = 'sphinxdoc' 88 | 89 | # Theme options are theme-specific and customize the look and feel of a theme 90 | # further. For a list of options available for each theme, see the 91 | # documentation. 92 | # 93 | # html_theme_options = {} 94 | 95 | # Add any paths that contain custom static files (such as style sheets) here, 96 | # relative to this directory. They are copied after the builtin static files, 97 | # so a file named "default.css" will overwrite the builtin "default.css". 98 | html_static_path = ['_static'] 99 | 100 | 101 | # -- Options for HTMLHelp output ------------------------------------------ 102 | 103 | # Output file base name for HTML help builder. 104 | htmlhelp_basename = 'rcpydoc' 105 | 106 | 107 | # -- Options for LaTeX output --------------------------------------------- 108 | 109 | latex_elements = { 110 | # The paper size ('letterpaper' or 'a4paper'). 111 | # 112 | # 'papersize': 'letterpaper', 113 | 114 | # The font size ('10pt', '11pt' or '12pt'). 115 | # 116 | # 'pointsize': '10pt', 117 | 118 | # Additional stuff for the LaTeX preamble. 119 | # 120 | # 'preamble': '', 121 | 122 | # Latex figure (float) alignment 123 | # 124 | # 'figure_align': 'htbp', 125 | } 126 | 127 | # Grouping the document tree into LaTeX files. List of tuples 128 | # (source start file, target name, title, 129 | # author, documentclass [howto, manual, or own class]). 130 | latex_documents = [ 131 | (master_doc, 'rcpy.tex', 'Python 3 Interface for the Robotics Cape on the Beaglebone Black and the Beaglebone Blue', 132 | 'Mauricio C. de Oliveira', 'manual'), 133 | ] 134 | 135 | 136 | # -- Options for manual page output --------------------------------------- 137 | 138 | # One entry per manual page. List of tuples 139 | # (source start file, name, description, authors, manual section). 140 | man_pages = [ 141 | (master_doc, 'rcpy', 'rcpy Documentation', 142 | [author], 1) 143 | ] 144 | 145 | 146 | # -- Options for Texinfo output ------------------------------------------- 147 | 148 | # Grouping the document tree into Texinfo files. List of tuples 149 | # (source start file, target name, title, author, 150 | # dir menu entry, description, category) 151 | texinfo_documents = [ 152 | (master_doc, 'rcpy', 'rcpy Documentation', 153 | author, 'rcpy', 'One line description of project.', 154 | 'Miscellaneous'), 155 | ] 156 | -------------------------------------------------------------------------------- /doc/index.rst: -------------------------------------------------------------------------------- 1 | .. rcpy documentation master file, created by 2 | sphinx-quickstart on Sat May 20 09:36:24 2017. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | ==================================================================================== 7 | Python 3 Interface for the Robotics Cape on the Beaglebone Black and Beaglebone Blue 8 | ==================================================================================== 9 | 10 | .. toctree:: 11 | :maxdepth: 2 12 | :caption: Contents: 13 | 14 | intro 15 | modules 16 | 17 | Indices and tables 18 | ------------------ 19 | 20 | * :ref:`genindex` 21 | * :ref:`modindex` 22 | * :ref:`search` 23 | -------------------------------------------------------------------------------- /doc/intro.rst: -------------------------------------------------------------------------------- 1 | Introduction 2 | ============ 3 | 4 | This package supports the hardware on the `Robotics Cape 5 | `_ running on a `Beaglebone Black 6 | `_ or a `Beaglebone Blue 7 | `_. 8 | 9 | Installation 10 | ------------ 11 | 12 | See ``_ for installation instructions. 13 | -------------------------------------------------------------------------------- /doc/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | set SPHINXPROJ=rcpy 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 20 | echo.installed, then set the SPHINXBUILD environment variable to point 21 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 22 | echo.may add the Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 34 | 35 | :end 36 | popd 37 | -------------------------------------------------------------------------------- /doc/modules.rst: -------------------------------------------------------------------------------- 1 | Modules 2 | ======= 3 | 4 | .. _rcpy: 5 | 6 | Module `rcpy` 7 | ------------- 8 | 9 | .. py:module:: rcpy 10 | 11 | This module is responsible to loading and initializing all hardware 12 | devices associated with the Robotics Cape or Beagelbone Blue. Just type:: 13 | 14 | import rcpy 15 | 16 | to load the module. After loading the Robotics Cape is left at the 17 | state :py:data:`rcpy.PAUSED`. It will also automatically cleanup after 18 | you exit your program. You can add additional function to be called by 19 | the cleanup routine using :py:func:`rcpy.add_cleanup`. 20 | 21 | Constants 22 | ^^^^^^^^^ 23 | 24 | .. py:data:: IDLE 25 | 26 | .. py:data:: RUNNING 27 | 28 | .. py:data:: PAUSED 29 | 30 | .. py:data:: EXITING 31 | 32 | Low-level functions 33 | ^^^^^^^^^^^^^^^^^^^ 34 | 35 | .. py:function:: get_state() 36 | 37 | Get the current state, one of :py:data:`rcpy.IDLE`, :py:data:`rcpy.RUNNING`, :py:data:`rcpy.PAUSED`, :py:data:`rcpy.EXITING`. 38 | 39 | .. py:function:: set_state(state) 40 | 41 | Set the current state, `state` is one of :py:data:`rcpy.IDLE`, :py:data:`rcpy.RUNNING`, :py:data:`rcpy.PAUSED`, :py:data:`rcpy.EXITING`. 42 | 43 | .. py:function:: idle() 44 | 45 | Set state to :py:data:`rcpy.IDLE`. 46 | 47 | .. py:function:: run() 48 | 49 | Set state to :py:data:`rcpy.RUNNING`. 50 | 51 | .. py:function:: pause() 52 | 53 | Set state to :py:data:`rcpy.PAUSED`. 54 | 55 | .. py:function:: exit() 56 | 57 | Set state to :py:data:`rcpy.EXITING`. 58 | 59 | .. py:function:: add_cleanup(fun, pars) 60 | 61 | :param fun: function to call at cleanup 62 | :param pars: list of positional parameters to pass to function `fun` 63 | 64 | Add function `fun` and parameters `pars` to the list of cleanup functions. 65 | 66 | .. _rcpy_adc: 67 | 68 | Module `rcpy.adc` 69 | -------------------- 70 | 71 | .. py:module:: rcpy.adc 72 | 73 | This module provides an interface to the Analog-to-digital converters 74 | (ADCs) in the Robotics Cape. The BeagleBone's 12-bit ADC is used on 75 | the robotics cape for reading the LiPo battery voltage, the DC jack 76 | input voltage, and 4 auxiliary signals that the user can connect to 77 | the 6-pin JST SH header labelled ADC. The pinout of this header is as 78 | follows: 79 | 80 | 1. Ground 81 | 2. VDD_ADC (1.8V) 82 | 3. AIN0 83 | 4. AIN1 84 | 5. AIN2 85 | 6. AIN3 86 | 87 | The command:: 88 | 89 | import rcpy.adc as adc 90 | 91 | imports the module. The DC Jack and battery voltages can be read using:: 92 | 93 | adc.dc_jack.get_voltage() 94 | 95 | and:: 96 | 97 | adc.battery.get_voltage() 98 | 99 | which return a floating point representation of the battery and DC 100 | jack voltage. All 7 ADC channels on the Sitara can be read with:: 101 | 102 | rc_adc_raw(channel) 103 | 104 | which returns the raw integer output of the 12-bit ADC.:: 105 | 106 | rc_adc_volt(channel) 107 | 108 | additionally converts this raw value to a floating point voltage 109 | before returning. `channel` must be an integer from 0 to 6. 110 | 111 | This module was contributed by `Brendan Simon `_. 112 | 113 | Constants 114 | ^^^^^^^^^ 115 | 116 | .. py:data:: CHANNEL_COUNT = 7 117 | .. py:data:: CHANNEL_MIN = 0 118 | .. py:data:: CHANNEL__MAX = 6 119 | 120 | .. py:data:: adc0 121 | 122 | :py:class:`rcpy.adc.ADC` representing the Beaglebone *AIN 0*. 123 | 124 | .. py:data:: adc1 125 | 126 | :py:class:`rcpy.adc.ADC` representing the Beaglebone *AIN 1*. 127 | 128 | .. py:data:: adc2 129 | 130 | :py:class:`rcpy.adc.ADC` representing the Beaglebone *AIN 2*. 131 | 132 | .. py:data:: adc3 133 | 134 | :py:class:`rcpy.adc.ADC` representing the Beaglebone *AIN 3*. 135 | 136 | .. py:data:: adc4 137 | 138 | :py:class:`rcpy.adc.ADC` representing the Beaglebone *AIN 4*. 139 | 140 | .. py:data:: adc5 141 | 142 | :py:class:`rcpy.adc.ADC` representing the Beaglebone *AIN 5*. 143 | 144 | .. py:data:: adc6 145 | 146 | :py:class:`rcpy.adc.ADC` representing the Beaglebone *AIN 6*. 147 | 148 | .. py:data:: adc7 149 | 150 | :py:class:`rcpy.adc.ADC` representing the Beaglebone *AIN 7*. 151 | 152 | Classes 153 | ^^^^^^^ 154 | 155 | .. py:class:: ADC() 156 | 157 | :param int channel: ADC channel 158 | 159 | :py:class:`rcpy.adc.ADC` represents one of the ADC channels in the Beaglebone. 160 | 161 | .. py:method:: get_raw() 162 | 163 | :returns: the raw integer output of the 12-bit ADC 164 | 165 | .. py:method:: get_voltage() 166 | 167 | :returns: the corresponding floating point voltage 168 | 169 | .. py:class:: DC_Jack() 170 | 171 | :py:class:`rcpy.adc.DC_Jack` represents the voltage in the Robotics Cape or Beaglebone Blue jack. 172 | 173 | .. py:method:: get_voltage() 174 | 175 | :returns: the corresponding floating point voltage 176 | 177 | .. py:class:: Battery() 178 | 179 | :py:class:`rcpy.adc.Battery` represents the voltage of the battery connected to the Robotics Cape or Beaglebone Blue. 180 | 181 | .. py:method:: get_voltage() 182 | 183 | :returns: the corresponding floating point voltage 184 | 185 | .. _rcpy_button: 186 | 187 | Module `rcpy.button` 188 | -------------------- 189 | 190 | .. py:module:: rcpy.button 191 | 192 | This module provides an interface to the *PAUSE* and *MODE* buttons in 193 | the Robotics Cape. The command:: 194 | 195 | import rcpy.button as button 196 | 197 | imports the module. The :ref:`rcpy_button` provides objects 198 | corresponding to the *PAUSE* and *MODE* buttons on the Robotics 199 | Cape. Those are :py:data:`rcpy.button.pause` and 200 | :py:data:`rcpy.button.mode`. One can import those objects directly 201 | using:: 202 | 203 | from rcpy.button import mode, pause 204 | 205 | After importing these objects:: 206 | 207 | if mode.pressed(): 208 | print(' pressed!') 209 | 210 | waits forever until the *MODE* button on the Robotics Cape is pressed and:: 211 | 212 | if mode.released(): 213 | print(' released!') 214 | 215 | waits forever until the *MODE* button on the Robotics Cape is 216 | released. Note that nothing will print if you first have to press the 217 | button before releasing because :py:meth:`rcpy.button.Button.released` 218 | returns :samp:`False` after the first input event, which in this case 219 | would correspond to the GPIO line associated to the button going 220 | :py:data:`rcpy.button.PRESSED`. 221 | 222 | :py:meth:`rcpy.button.Button.pressed` and 223 | :py:meth:`rcpy.button.Button.released` also raise the exception 224 | :py:class:`rcpy.gpio.InputTimeout` if the argument `timeout` is used 225 | as in:: 226 | 227 | import rcpy.gpio as gpio 228 | try: 229 | if mode.pressed(timeout = 2000): 230 | print(' pressed!') 231 | except gpio.InputTimeout: 232 | print('Timed out!') 233 | 234 | which waits for at most 2000 ms, i.e. 2 s, before giving up. 235 | 236 | The methods :py:meth:`rcpy.button.Button.is_pressed` and 237 | :py:meth:`rcpy.button.Button.is_released` are *non-blocking* versions 238 | of the above methods. For example:: 239 | 240 | import time 241 | while True: 242 | if mode.is_pressed(): 243 | print(' pressed!') 244 | break 245 | time.sleep(1) 246 | 247 | checks the status of the button *MODE* every 1 s and breaks when 248 | *MODE* is pressed. As with :ref:`rcpy_gpio`, a much better way to 249 | handle such events is to use event handlers, in the case of 250 | buttons the class :py:class:`rcpy.button.ButtonEvent`. For example:: 251 | 252 | class MyButtonEvent(button.ButtonEvent): 253 | def action(self, event): 254 | print('Got !') 255 | 256 | defines a class that can be used to print :samp:`Got !` every 257 | time the *PAUSE* button is pressed. To instantiate and start the event 258 | handler use:: 259 | 260 | pause_event = MyButtonEvent(pause, button.ButtonEvent.PRESSED) 261 | pause_event.start() 262 | 263 | Note that the event :py:data:`rcpy.button.ButtonEvent.PRESSED` was 264 | used so that `MyButtonEvent.action` is called only when the *PAUSE* 265 | button is pressed. The event handler can be stopped by calling:: 266 | 267 | pause_event.stop() 268 | 269 | Alternatively one could have created an input event handler by passing 270 | a function to the argument `target` of 271 | :py:class:`rcpy.button.ButtonEvent` as in:: 272 | 273 | from rcpy.button import ButtonEvent 274 | 275 | def pause_action(input, event): 276 | if event == ButtonEvent.PRESSED: 277 | print(' pressed!') 278 | elif event == ButtonEvent.RELEASED: 279 | print(' released!') 280 | 281 | pause_event = ButtonEvent(pause, 282 | ButtonEvent.PRESSED | ButtonEvent.RELEASED, 283 | target = pause_action) 284 | 285 | This event handler should be started and stopped using 286 | :py:meth:`rcpy.button.ButtonEvent.start` and 287 | :py:meth:`rcpy.button.ButtonEvent.stop` as before. Additional 288 | positional or keyword arguments can be passed as in:: 289 | 290 | def pause_action_with_parameter(input, event, parameter): 291 | print('Got with {}!'.format(parameter)) 292 | 293 | pause_event = ButtonEvent(pause, ButtonEvent.PRESSED, 294 | target = pause_action_with_parameter, 295 | vargs = ('some parameter',)) 296 | 297 | The main difference between :ref:`rcpy_button` and :ref:`rcpy_gpio` is 298 | that :ref:`rcpy_button` defines the constants 299 | :py:data:`rcpy.button.PRESSED` and :py:data:`rcpy.button.RELEASED`, 300 | the events :py:data:`rcpy.button.ButtonEvent.PRESSED` and 301 | :py:data:`rcpy.button.ButtonEvent.RELEASED`, and its classes handle 302 | debouncing by default. 303 | 304 | Constants 305 | ^^^^^^^^^ 306 | 307 | .. py:data:: pause 308 | 309 | :py:class:`rcpy.button.Button` representing the Robotics Cape *PAUSE* button. 310 | 311 | .. py:data:: mode 312 | 313 | :py:class:`rcpy.button.Button` representing the Robotics Cape *MODE* button. 314 | 315 | .. py:data:: PRESSED 316 | 317 | State of a pressed button; equal to :py:data:`rcpy.gpio.LOW`. 318 | 319 | .. py:data:: RELEASED 320 | 321 | State of a released button; equal to :py:data:`rcpy.gpio.HIGH`. 322 | 323 | .. py:data:: DEBOUNCE 324 | 325 | Number of times to test for deboucing (Default 3) 326 | 327 | Classes 328 | ^^^^^^^ 329 | 330 | .. py:class:: Button() 331 | 332 | :bases: :py:class:`rcpy.gpio.Input` 333 | 334 | :py:class:`rcpy.button.Button` represents buttons in the Robotics Cape or Beaglebone Blue. 335 | 336 | .. py:method:: is_pressed() 337 | 338 | :returns: :samp:`True` if button state is equal to :py:data:`rcpy.button.PRESSED` and :samp:`False` if line is :py:data:`rcpy.button.RELEASED` 339 | 340 | .. py:method:: is_released() 341 | 342 | :returns: :samp:`True` if button state is equal to :py:data:`rcpy.button.RELEASED` and :samp:`False` if line is :py:data:`rcpy.button.PRESSED` 343 | 344 | .. py:method:: pressed_or_released(debounce = rcpy.button.DEBOUNCE, timeout = None) 345 | 346 | :param int debounce: number of times to read input for debouncing (default `rcpy.button.DEBOUNCE`) 347 | :param int timeout: timeout in milliseconds (default None) 348 | :raises rcpy.gpio.InputTimeout: if more than `timeout` ms have elapsed without the button state changing 349 | 350 | :returns: the new state as :py:data:`rcpy.button.PRESSED` or :py:data:`rcpy.button.RELEASED` 351 | 352 | Wait for button state to change. 353 | 354 | If `timeout` is not :samp:`None` wait at most `timeout` ms otherwise wait forever until the input changes. 355 | 356 | .. py:method:: pressed(debounce = rcpy.button.DEBOUNCE, timeout = None) 357 | 358 | :param int debounce: number of times to read input for debouncing (default `rcpy.button.DEBOUNCE`) 359 | :param int timeout: timeout in milliseconds (default None) 360 | :raises rcpy.gpio.InputTimeout: if more than `timeout` ms have elapsed without the button state changing. 361 | 362 | :returns: :samp:`True` if the new state is :py:data:`rcpy.button.PRESSED` and :samp:`False` if the new state is :py:data:`rcpy.button.RELEASED` 363 | 364 | Wait for button state to change. 365 | 366 | If `timeout` is not :samp:`None` wait at most `timeout` ms otherwise wait forever until the input changes. 367 | 368 | .. py:method:: released(debounce = rcpy.button.DEBOUNCE, timeout = None) 369 | 370 | :param int debounce: number of times to read input for debouncing (default `rcpy.button.DEBOUNCE`) 371 | :param int timeout: timeout in milliseconds (default None) 372 | :raises rcpy.gpio.InputTimeout: if more than `timeout` ms have elapsed without the button state changing. 373 | 374 | :returns: :samp:`True` if the new state is :py:data:`rcpy.button.RELEASED` and :samp:`False` if the new state is :py:data:`rcpy.button.PRESSED` 375 | 376 | Wait for button state to change. 377 | 378 | If `timeout` is not :samp:`None` wait at most `timeout` ms otherwise wait forever until the input changes. 379 | 380 | .. py:class:: ButtonEvent(input, event, debounce = rcpy.button.DEBOUNCE, timeout = None, target = None, vargs = (), kwargs = {}) 381 | 382 | :bases: :py:class:`rcpy.gpio.InputEvent` 383 | 384 | :param int input: instance of :py:class:`rcpy.gpio.Input` 385 | :param int event: either :py:data:`rcpy.button.ButtonEvent.PRESSED` or :py:data:`rcpy.button.ButtonEvent.RELEASED` 386 | :param int debounce: number of times to read input for debouncing (default `rcpy.button.DEBOUNCE`) 387 | :param int timeout: timeout in milliseconds (default `None`) 388 | :param int target: callback function to run in case input changes (default `None`) 389 | :param int vargs: positional arguments for function `target` (default `()`) 390 | :param int kwargs: keyword arguments for function `target` (default `{}`) 391 | 392 | :py:class:`rcpy.button.ButtonEvent` is an event handler for button events. 393 | 394 | .. py:data:: PRESSED 395 | 396 | Event representing pressing a button; equal to :py:data:`rcpy.gpio.InputEvent.LOW`. 397 | 398 | .. py:data:: RELEASED 399 | 400 | Event representing releasing a button; equal to :py:data:`rcpy.gpio.InputEvent.HIGH`. 401 | 402 | .. py:method:: action(event, *vargs, **kwargs) 403 | 404 | :param event: either :py:data:`rcpy.button.PRESSED` or :py:data:`rcpy.button.RELEASED` 405 | :param vargs: variable positional arguments 406 | :param kwargs: variable keyword arguments 407 | 408 | Action to perform when event is detected. 409 | 410 | .. py:method:: start() 411 | 412 | Start the input event handler thread. 413 | 414 | .. py:method:: stop() 415 | 416 | Attempt to stop the input event handler thread. Once it has stopped it cannot be restarted. 417 | 418 | .. _rcpy_clock: 419 | 420 | Module `rcpy.clock` 421 | ------------------- 422 | 423 | .. py:module:: rcpy.clock 424 | 425 | This module provides a class :py:class:`rcpy.clock.Clock` that can be 426 | used to run actions periodically. Actions must inherit from the class 427 | :py:class:`rcpy.clock.Action` and implement the method 428 | :py:meth:`rcpy.clock.Action.run`. Examples of objects that inherit 429 | from :py:class:`rcpy.clock.Action` are :py:class:`rcpy.led.LED` and 430 | :py:class:`rcpy.servo.Servo`. 431 | 432 | For example:: 433 | 434 | import rcpy.clock as clock 435 | from rcpy.led import red 436 | clk = clock.Clock(red) 437 | clk.start() 438 | 439 | will start a *thread* that will blink the *red* LED every second. The 440 | command:: 441 | 442 | clk.stop() 443 | 444 | will stop the LED blinking. The subclass :py:class:`rcpy.led.Blink` 445 | will in addition turn off the led after stopping. 446 | 447 | Classes 448 | ^^^^^^^ 449 | 450 | .. py:class:: Action() 451 | 452 | :py:class:`rcpy.clock.Action` represents and action. 453 | 454 | .. py:method:: run() 455 | 456 | Run the action. 457 | 458 | .. py:class:: Actions(*actions) 459 | 460 | :bases: :py:class:`rcpy.clock.Action` 461 | 462 | :py:class:`rcpy.clock.Actions` represents a sequence of actions. 463 | 464 | :param actions: comma separated list of actions. 465 | 466 | .. py:class:: Clock(action, period) 467 | 468 | :bases: threading.Thread 469 | 470 | :py:class:`rcpy.clock.Clock` executes actions periodically. 471 | 472 | :param Action action: the action to be executed 473 | :param int period: the period in seconds (default 1 second) 474 | 475 | .. py:method:: set_period(period) 476 | 477 | :param float period: period in seconds 478 | 479 | Set action period. 480 | 481 | .. py:method:: toggle() 482 | 483 | Toggle action on and off. Call toggle again to resume or stop action. 484 | 485 | .. py:method:: start() 486 | 487 | Start the action thread. 488 | 489 | .. py:method:: stop() 490 | 491 | Stop the action thread. Action cannot resume after calling :py:meth:`rcpy.led.Blink.stop`. Use :py:meth:`rcpy.clock.Clock.toggle` to temporarily interrupt an action. 492 | 493 | .. _rcpy_encoder: 494 | 495 | Module `rcpy.encoder` 496 | --------------------- 497 | 498 | .. py:module:: rcpy.encoder 499 | 500 | This module provides an interface to the four *encoder channels* in 501 | the Robotics Cape. The command:: 502 | 503 | import rcpy.encoder as encoder 504 | 505 | imports the module. The :ref:`rcpy_encoder` provides objects 506 | corresponding to the each of the encoder channels on the Robotics 507 | Cape, namely :py:data:`rcpy.encoder.encoder1`, 508 | :py:data:`rcpy.encoder.encoder2`, :py:data:`rcpy.encoder.encoder3`, 509 | and :py:data:`rcpy.encoder.encoder4`. It may be convenient to import 510 | one or all of these objects as in:: 511 | 512 | from rcpy.encoder import encoder2 513 | 514 | The current encoder count can be obtained using:: 515 | 516 | encoder2.get() 517 | 518 | One can also *reset* the count to zero using:: 519 | 520 | encoder2.reset() 521 | 522 | or to an arbitrary count using:: 523 | 524 | encoder2.set(1024) 525 | 526 | after which:: 527 | 528 | encoder2.get() 529 | 530 | will return 1024. 531 | 532 | Constants 533 | ^^^^^^^^^ 534 | 535 | .. py:data:: encoder1 536 | 537 | :py:class:`rcpy.encoder.Encoder` representing the Robotics Cape *Encoder 1*. 538 | 539 | .. py:data:: encoder2 540 | 541 | :py:class:`rcpy.encoder.Encoder` representing the Robotics Cape *Encoder 2*. 542 | 543 | .. py:data:: encoder3 544 | 545 | :py:class:`rcpy.encoder.Encoder` representing the Robotics Cape *Encoder 3*. 546 | 547 | .. py:data:: encoder4 548 | 549 | :py:class:`rcpy.encoder.Encoder` representing the Robotics Cape *Encoder 4*. 550 | 551 | Classes 552 | ^^^^^^^ 553 | 554 | .. py:class:: Encoder(channel, count = None) 555 | 556 | :param output: encoder channel (1 through 4) 557 | :param state: initial encoder count (Default None) 558 | 559 | :py:class:`rcpy.encoder.Encoder` represents encoders in the Robotics Cape or Beaglebone Blue. 560 | 561 | .. py:method:: get() 562 | 563 | :returns: current encoder count 564 | 565 | .. py:method:: set(count) 566 | 567 | Set current encoder count to `count`. 568 | 569 | .. py:method:: reset() 570 | 571 | Set current encoder count to `0`. 572 | 573 | Low-level functions 574 | ^^^^^^^^^^^^^^^^^^^ 575 | 576 | .. py:function:: get(channel) 577 | 578 | :param int channel: encoder channel number 579 | 580 | :returns: current encoder count 581 | 582 | This is a non-blocking call. 583 | 584 | .. py:function:: set(channel, count = 0) 585 | 586 | :param int channel: encoder channel number 587 | :param int count: desired encoder count 588 | 589 | Set encoder `channel` count to `value`. 590 | 591 | .. _rcpy_gpio: 592 | 593 | Module `rcpy.gpio` 594 | ------------------ 595 | 596 | .. py:module:: rcpy.gpio 597 | 598 | This module provides an interface to the GPIO pins used by the 599 | Robotics Cape. There are low level functions which closely mirror the 600 | ones available in the C library and also Classes that provide a higher 601 | level interface. 602 | 603 | For example:: 604 | 605 | import rcpy.gpio as gpio 606 | pause_button = gpio.Input(*gpio.PAUSE_BTN) 607 | 608 | imports the module and create an :py:class:`rcpy.gpio.Input` object 609 | corresponding to the *PAUSE* button on the Robotics Cape. The 610 | command:: 611 | 612 | if pause_button.low(): 613 | print('Got !') 614 | 615 | waits forever until the *PAUSE* button on the Robotics Cape becomes 616 | :py:data:`rcpy.gpio.LOW`, which happens when it is pressed. The 617 | following slightly modified version:: 618 | 619 | try: 620 | if pause_button.low(timeout = 2000): 621 | print('Got !') 622 | except gpio.InputTimeout: 623 | print('Timed out!') 624 | 625 | waits for at most 2000 ms, i.e. 2 s, before giving up, which one can 626 | detect by catching the exception :py:class:`rcpy.gpio.InputTimeout`. 627 | 628 | In the same vein, the method :py:meth:`rcpy.gpio.Input.high` would 629 | wait for the button *PAUSE* to become :py:data:`rcpy.gpio.HIGH`. For 630 | instance:: 631 | 632 | if pause_button.high(): 633 | print(' got high!') 634 | 635 | waits forever until the *PAUSE* button on the Robotics Cape is 636 | released. Note that nothing will print if you first have to press the 637 | button before releasing because :py:meth:`rcpy.gpio.Input.high` 638 | returns :samp:`False` after the first input event, which in this case 639 | would correspond to the GPIO pin going :py:data:`rcpy.gpio.LOW`. 640 | 641 | The methods :py:meth:`rcpy.gpio.Input.is_low` and 642 | :py:meth:`rcpy.gpio.Input.is_high` are *non-blocking* versions of the 643 | above methods. For example:: 644 | 645 | import time 646 | while True: 647 | if pause_button.is_low(): 648 | print('Got !') 649 | break 650 | time.sleep(1) 651 | 652 | checks the status of the button *PAUSE* every 1 s and breaks when 653 | *PAUSE* is pressed. A much better way to handle such events is to use 654 | the class :py:class:`rcpy.gpio.InputEvent`. For example:: 655 | 656 | class MyInputEvent(gpio.InputEvent): 657 | def action(self, event): 658 | print('Got !') 659 | 660 | defines a class that can be used to print :samp:`Got !` every 661 | time an input event happens. To connect this class with the particular 662 | event that the *PAUSE* button is pressed instantiate:: 663 | 664 | pause_event = MyInputEvent(pause_button, gpio.InputEvent.LOW) 665 | 666 | which will cause the method `action` of the `MyInputEvent` class be 667 | called every time the state of the `pause_button` becomes 668 | :py:data:`rcpy.gpio.LOW`. The event handler must be started by 669 | calling:: 670 | 671 | pause_event.start() 672 | 673 | and it can be stopped by:: 674 | 675 | pause_event.stop() 676 | 677 | The event handler automatically runs on a separate *thread*, which 678 | means that the methods :py:meth:`rcpy.gpio.InputEvent.start` and 679 | :py:meth:`rcpy.gpio.InputEvent.stop` are *non-blocking*, that is they 680 | return immediately. 681 | 682 | The class :py:class:`rcpy.gpio.InputEvent` defines two types of 683 | events: :py:data:`rcpy.gpio.InputEvent.HIGH` and 684 | :py:data:`rcpy.gpio.InputEvent.LOW`. Note that these are not the same 685 | as :py:data:`rcpy.gpio.HIGH` and :py:data:`rcpy.gpio.LOW`! It is often 686 | convenient to import the base class :py:data:`rcpy.gpio.InputEvent`:: 687 | 688 | from rcpy.gpio import InputEvent 689 | 690 | Alternatively one could have created an input event handler by passing 691 | a function to the argument `target` of 692 | :py:class:`rcpy.gpio.InputEvent` as in:: 693 | 694 | def pause_action(input, event): 695 | if event == InputEvent.LOW: 696 | print(' went LOW') 697 | elif event == InputEvent.HIGH: 698 | print(' went HIGH') 699 | 700 | pause_event = InputEvent(pause_button, 701 | InputEvent.LOW | InputEvent.HIGH, 702 | target = pause_action) 703 | 704 | The function `pause_action` will be called when `pause_button` 705 | generates either event :py:data:`rcpy.gpio.InputEvent.HIGH` or 706 | :py:data:`rcpy.gpio.InputEvent.LOW` because the events passed to the 707 | the constructor :py:class:`InputEvent` were combined using the 708 | *logical or* operator `|`, as in:: 709 | 710 | InputEvent.LOW | InputEvent.HIGH, 711 | 712 | The function `pause_action` decides on the type of event by checking 713 | the variable `event`. This event handler should be started and stopped 714 | using :py:meth:`rcpy.gpio.InputEvent.start` and 715 | :py:meth:`rcpy.gpio.InputEvent.stop` as before. 716 | 717 | Additional positional or keyword arguments can be passed as in:: 718 | 719 | def pause_action_with_parameter(input, event, parameter): 720 | print('Got with {}!'.format(parameter)) 721 | 722 | pause_event = InputEvent(pause_button, InputEvent.LOW, 723 | target = pause_action_with_parameter, 724 | vargs = ('some parameter',)) 725 | 726 | See also :py:class:`rcpy.button.Button` for a better interface for 727 | working with the Robotics Cape buttons. 728 | 729 | Constants 730 | ^^^^^^^^^ 731 | 732 | .. py:data:: HIGH 733 | 734 | Logic high level; equals `1`. 735 | 736 | .. py:data:: LOW 737 | 738 | Logic low level; equals `0`. 739 | 740 | .. py:data:: DEBOUNCE_INTERVAL 741 | 742 | Interval in ms to be used for debouncing (Default 0.5ms) 743 | 744 | Classes 745 | ^^^^^^^ 746 | 747 | .. py:class:: InputTimeout() 748 | 749 | Exception representing an input timeout event. 750 | 751 | .. py:class:: GPIO(chip, line) 752 | 753 | :param int chip: GPIO chip 754 | :param int line: GPIO line 755 | 756 | :py:class:`rcpy.gpio.GPIO` represents one of the GPIO input or output lines in the Robotics Cape or Beaglebone Blue. Users are not supposed to directly use this class. Use :py:class:`rcpy.gpio.Input` or :py:class:`rcpy.gpio.Output` instead. 757 | 758 | .. py:method:: request(type) 759 | 760 | :param int type: the type of line as defined in `libgpiod `_ 761 | 762 | Request GPIO chip and line. 763 | 764 | .. py:method:: release(type) 765 | 766 | Release GPIO chip and line. 767 | 768 | .. py:class:: Output(chip, line) 769 | 770 | :bases: :py:class:`rcpy.gpio.GPIO` 771 | 772 | :param int chip: GPIO chip 773 | :param int line: GPIO line 774 | 775 | :py:class:`rcpy.gpio.Output` represents one of the GPIO output lines in the Robotics Cape or Beaglebone Blue. 776 | 777 | .. py:method:: set(state) 778 | 779 | :param int state: set the state of the line, :py:data:`rcpy.gpio.HIGH` or :py:data:`rcpy.gpio.LOW` 780 | 781 | .. py:class:: Input(chip, line) 782 | 783 | :bases: :py:class:`rcpy.gpio.GPIO` 784 | 785 | :param int chip: GPIO chip 786 | :param int line: GPIO line 787 | 788 | :py:class:`rcpy.gpio.Input` represents one of the GPIO input lines in the Robotics Cape or Beaglebone Blue. 789 | 790 | .. py:method:: get() 791 | 792 | :returns: the state of the line, :py:data:`rcpy.gpio.HIGH` or :py:data:`rcpy.gpio.LOW` (non-blocking) 793 | 794 | .. py:method:: is_high() 795 | 796 | :returns: :samp:`True` if line is equal to :py:data:`rcpy.gpio.HIGH` and :samp:`False` if line is :py:data:`rcpy.gpio.LOW` (non-blocking) 797 | 798 | .. py:method:: is_low() 799 | 800 | :returns: :samp:`True` if line is equal to :py:data:`rcpy.gpio.LOW` and :samp:`False` if line is :py:data:`rcpy.gpio.HIGH` (non-blocking) 801 | 802 | .. py:method:: high_or_low(debounce = 0, timeout = None) 803 | 804 | :param int debounce: number of times to read input for debouncing (default 0) 805 | :param int timeout: timeout in milliseconds (default None) 806 | :raises rcpy.gpio.InputTimeout: if more than `timeout` ms have elapsed without the input changing 807 | 808 | :returns: the new state as :py:data:`rcpy.gpio.HIGH` or :py:data:`rcpy.gpio.LOW` 809 | 810 | Wait for line to change state. This is a blocking call. 811 | 812 | If `timeout` is not :samp:`None` wait at most `timeout` ms otherwise wait forever until the input changes. 813 | 814 | 815 | .. py:method:: high(debounce = 0, timeout = None) 816 | 817 | :param int debounce: number of times to read input for debouncing (default 0) 818 | :param int timeout: timeout in milliseconds (default None) 819 | :raises rcpy.gpio.InputTimeout: if more than `timeout` ms have elapsed without the input changing 820 | 821 | :returns: :samp:`True` if the new state is :py:data:`rcpy.gpio.HIGH` and :samp:`False` if the new state is :py:data:`rcpy.gpio.LOW` 822 | 823 | Wait for line to change state. This is a blocking call. 824 | 825 | If `timeout` is not :samp:`None` wait at most `timeout` ms otherwise wait forever until the input changes. 826 | 827 | .. py:method:: low(debounce = 0, timeout = None) 828 | 829 | :param int debounce: number of times to read input for debouncing (default 0) 830 | :param int timeout: timeout in milliseconds (default None) 831 | :raises rcpy.gpio.InputTimeout: if more than `timeout` ms have elapsed without the input changing 832 | 833 | :returns: :samp:`True` if the new state is :py:data:`rcpy.gpio.LOW` and :samp:`False` if the new state is :py:data:`rcpy.gpio.HIGH` 834 | 835 | Wait for line to change state. This is a blocking call. 836 | 837 | If `timeout` is not :samp:`None` wait at most `timeout` ms otherwise wait forever until the input changes. 838 | 839 | .. py:method:: read(line, timeout = None) 840 | 841 | :param int timeout: timeout in milliseconds (default None) 842 | :raises rcpy.gpio.error: if it cannot read from `line` 843 | :raises rcpy.gpio.InputTimeout: if more than `timeout` ms have elapsed without the input changing 844 | :returns: the new value of the GPIO `line` 845 | 846 | Wait for value of the GPIO `line` to change. This is a blocking call. 847 | 848 | .. py:class:: InputEvent(input, event, debounce = 0, timeout = None, target = None, vargs = (), kwargs = {}) 849 | 850 | :bases: threading.Thread 851 | 852 | :py:class:`rcpy.gpio.InputEvent` is an event handler for GPIO input events. 853 | 854 | :param int input: instance of :py:class:`rcpy.gpio.Input` 855 | :param int event: either :py:data:`rcpy.gpio.InputEvent.HIGH` or :py:data:`rcpy.gpio.InputEvent.LOW` 856 | :param int debounce: number of times to read input for debouncing (default 0) 857 | :param int timeout: timeout in milliseconds (default None) 858 | :param int target: callback function to run in case input changes (default None) 859 | :param int vargs: positional arguments for function `target` (default ()) 860 | :param int kwargs: keyword arguments for function `target` (default {}) 861 | 862 | .. py:data:: LOW 863 | 864 | Event representing change to a low logic level. 865 | 866 | .. py:data:: HIGH 867 | 868 | Event representing change to a high logic level. 869 | 870 | .. py:method:: action(event, *vargs, **kwargs) 871 | 872 | :param event: either :py:data:`rcpy.gpio.HIGH` or :py:data:`rcpy.gpio.LOW` 873 | :param vargs: variable positional arguments 874 | :param kwargs: variable keyword arguments 875 | 876 | Action to perform when event is detected. 877 | 878 | .. py:method:: start() 879 | 880 | Start the input event handler thread. 881 | 882 | .. py:method:: stop() 883 | 884 | Attempt to stop the input event handler thread. Once it has stopped it cannot be restarted. 885 | 886 | .. _rcpy_led: 887 | 888 | Module `rcpy.led` 889 | ----------------- 890 | 891 | .. py:module:: rcpy.led 892 | 893 | This module provides an interface to the *RED* and *GREEN* buttons in 894 | the Robotics Cape. The command:: 895 | 896 | import rcpy.led as led 897 | 898 | imports the module. The :ref:`rcpy_led` provides objects corresponding 899 | to the *RED* and *GREEN* buttons on the Robotics Cape, namely 900 | :py:data:`rcpy.led.red` and :py:data:`rcpy.led.green`. It may be 901 | convenient to import one or all of these objects as in:: 902 | 903 | from rcpy.led import red, green 904 | 905 | For example:: 906 | 907 | red.on() 908 | 909 | turns the *RED* LED on and:: 910 | 911 | green.off() 912 | 913 | turns the *GREEN* LED off. Likewise:: 914 | 915 | green.is_on() 916 | 917 | returns :samp:`True` if the *GREEN* LED is on and:: 918 | 919 | red.is_off() 920 | 921 | returns :samp:`True` if the *RED* LED is off. 922 | 923 | This module also provides the class :py:class:`rcpy.led.Blink` to 924 | handle LED blinking. It spawns a thread that will keep LEDs blinking 925 | with a given period. For example:: 926 | 927 | blink = led.Blink(red, .5) 928 | blink.start() 929 | 930 | starts blinking the *RED* LED every 0.5 seconds. One can stop or 931 | resume blinking by calling :py:meth:`rcpy.led.Blink.toggle` as in:: 932 | 933 | blink.toggle() 934 | 935 | or call:: 936 | 937 | blink.stop() 938 | 939 | to permanently stop the blinking thread. 940 | 941 | One can also instantiate an :py:class:`rcpy.led.Blink` object by calling 942 | :py:meth:`rcpy.led.LED.blink` as in:: 943 | 944 | blink = red.blink(.5) 945 | 946 | which returns an instance of 947 | :py:class:`rcpy.led.Blink`. :py:meth:`rcpy.led.LED.blink` 948 | automatically calls :py:meth:`rcpy.led.Blink.start`. 949 | 950 | Constants 951 | ^^^^^^^^^ 952 | 953 | .. py:data:: red 954 | 955 | :py:class:`rcpy.led.LED` representing the Robotics Cape *RED* LED. 956 | 957 | .. py:data:: green 958 | 959 | :py:class:`rcpy.led.LED` representing the Robotics Cape *GREEN* LED. 960 | 961 | .. py:data:: ON 962 | 963 | State of an on LED; equal to :py:data:`rcpy.gpio.HIGH`. 964 | 965 | .. py:data:: OFF 966 | 967 | State of an off led; equal to :py:data:`rcpy.gpio.LOW`. 968 | 969 | Classes 970 | ^^^^^^^ 971 | 972 | .. py:class:: LED(output, state = rcpy.led.OFF) 973 | 974 | :bases: :py:class:`rcpy.gpio.Output` 975 | 976 | :param output: GPIO line 977 | :param state: initial LED state 978 | 979 | :py:class:`rcpy.led.LED` represents LEDs in the Robotics Cape or Beaglebone Blue. 980 | 981 | .. py:method:: is_on() 982 | 983 | :returns: :samp:`True` if LED is on and :samp:`False` if LED is off 984 | 985 | .. py:method:: is_off() 986 | 987 | :returns: :samp:`True` if LED is off and :samp:`False` if LED is on 988 | 989 | .. py:method:: on() 990 | 991 | Change LED state to :py:data:`rcpy.led.ON`. 992 | 993 | .. py:method:: off() 994 | 995 | Change LED state to :py:data:`rcpy.led.OFF`. 996 | 997 | .. py:method:: toggle() 998 | 999 | Toggle current LED state. 1000 | 1001 | .. py:method:: blink(period) 1002 | 1003 | :param float period: period of blinking 1004 | :returns: an instance of :py:class:`rcpy.led.Blink`. 1005 | 1006 | Blinks LED with a period of `period` seconds. 1007 | 1008 | .. py:class:: Blink(led, period) 1009 | 1010 | :bases: :py:class:`rcpy.clock.Clock` 1011 | 1012 | :py:class:`rcpy.led.Blink` toggles led on and off periodically. 1013 | 1014 | :param LED led: the led to toggle 1015 | :param int period: the period in seconds 1016 | 1017 | .. _rcpy_motor: 1018 | 1019 | Module `rcpy.motor` 1020 | ------------------- 1021 | 1022 | .. py:module:: rcpy.motor 1023 | 1024 | This module provides an interface to the four *motor channels* in the 1025 | Robotics Cape. Those control a high power PWM (Pulse Width Modulation) 1026 | signal which is typically used to control *DC Motors*. The command:: 1027 | 1028 | import rcpy.motor as motor 1029 | 1030 | imports the module. The :ref:`rcpy_motor` provides objects 1031 | corresponding to the each of the motor channels on the Robotics Cape, 1032 | namely :py:data:`rcpy.motor.motor1`, :py:data:`rcpy.motor.motor2`, 1033 | :py:data:`rcpy.motor.motor3`, and :py:data:`rcpy.motor.motor4`. It may 1034 | be convenient to import one or all of these objects as in :: 1035 | 1036 | from rcpy.motor import motor2 1037 | 1038 | This is a convenience object which is equivalent to:: 1039 | 1040 | motor2 = Motor(2) 1041 | 1042 | The current average voltage applied to the motor can be set using:: 1043 | 1044 | duty = 1 1045 | motor2.set(duty) 1046 | 1047 | where `duty` is a number varying from -1 to 1 which controls the 1048 | percentage of the voltage available to the Robotics Cape that should 1049 | be applied on the motor. A motor can be turned off by:: 1050 | 1051 | motor2.set(0) 1052 | 1053 | or using one of the special methods 1054 | :py:meth:`rcpy.motor.Motor.free_spin` or 1055 | :py:meth:`rcpy.motor.Motor.brake`, which can be used to turn off the 1056 | motor and set it in a *free-spin* or *braking* configuration. For 1057 | example:: 1058 | 1059 | motor2.free_spin() 1060 | 1061 | puts :py:data:`motor2` in *free-spin* mode. In *free-spin mode* the 1062 | motor behaves as if there were no voltage applied to its terminal, 1063 | that is it is allowed to spin freely. In *brake mode* the terminals of 1064 | the motor are *short-circuited* and the motor winding will exert an 1065 | opposing force if the motor shaft is moved. *Brake mode* is 1066 | essentially the same as setting the duty cycle to zero. 1067 | 1068 | Constants 1069 | ^^^^^^^^^ 1070 | 1071 | .. py:data:: motor1 1072 | 1073 | :py:class:`rcpy.motor.Motor` representing the Robotics Cape *Motor 1*. 1074 | 1075 | .. py:data:: motor2 1076 | 1077 | :py:class:`rcpy.motor.Motor` representing the Robotics Cape *Motor 2*. 1078 | 1079 | .. py:data:: motor3 1080 | 1081 | :py:class:`rcpy.motor.Motor` representing the Robotics Cape *Motor 3*. 1082 | 1083 | .. py:data:: motor4 1084 | 1085 | :py:class:`rcpy.motor.Motor` representing the Robotics Cape *Motor 4*. 1086 | 1087 | Classes 1088 | ^^^^^^^ 1089 | 1090 | .. py:class:: Motor(channel, duty = None) 1091 | 1092 | :param output: motor channel (1 through 4) 1093 | :param state: initial motor duty cycle (Default None) 1094 | 1095 | :py:class:`rcpy.motor.Motor` represents motors in the Robotics Cape or Beaglebone Blue. 1096 | 1097 | .. py:method:: set(duty) 1098 | 1099 | Set current motor duty cycle to `duty`. `duty` is a number between -1 and 1. 1100 | 1101 | .. py:method:: free_spin() 1102 | 1103 | Stops the motor and puts it in *free-spin* mode. 1104 | 1105 | .. py:method:: brake() 1106 | 1107 | Stops the motor and puts it in *brake* mode. 1108 | 1109 | Low-level functions 1110 | ^^^^^^^^^^^^^^^^^^^ 1111 | 1112 | .. py:function:: set(channel, duty) 1113 | 1114 | :param int channel: motor channel number 1115 | :param int duty: desired motor duty cycle 1116 | 1117 | Sets the motor channel `channel` duty cycle to `duty`. 1118 | 1119 | This is a non-blocking call. 1120 | 1121 | .. py:function:: set_free_spin(channel) 1122 | 1123 | :param int channel: motor channel number 1124 | 1125 | Puts the motor channel `channel` in *free-spin mode*. 1126 | 1127 | This is a non-blocking call. 1128 | 1129 | .. py:function:: set_brake(channel) 1130 | 1131 | :param int channel: motor channel number 1132 | 1133 | Puts the motor channel `channel` in *brake mode*. 1134 | 1135 | This is a non-blocking call. 1136 | 1137 | .. _rcpy_mpu9250: 1138 | 1139 | Module `rcpy.mpu9250` 1140 | --------------------- 1141 | 1142 | .. py:module:: rcpy.mpu9250 1143 | 1144 | This module provides an interface to the on-board `MPU-9250 1145 | `_ 1146 | Nine-Axis (Gyro + Accelerometer + Compass) MEMS device. The command:: 1147 | 1148 | import rcpy.mpu9250 as mpu9250 1149 | 1150 | imports the module. 1151 | 1152 | **IMPORTANT:** Beware that due to the way the Robotics Cape is written 1153 | objects of the class :py:class:`rcpy.mpu9250.IMU` are singletons, that 1154 | is they all refer to the same instance. 1155 | 1156 | Setup can be done at creation, such as in :: 1157 | 1158 | imu = mpu9250.IMU(enable_dmp = True, dmp_sample_rate = 4, 1159 | enable_magnetometer = True) 1160 | 1161 | which starts and initializes the MPU-9250 to use its DMP (Dynamic 1162 | Motion Processor) to provide periodic readings at a rate of 4 Hz and 1163 | also its magnetometer. 1164 | 1165 | The data can be read using:: 1166 | 1167 | imu.read() 1168 | 1169 | which performs a blocking call and can be used to synchronize 1170 | execution with the DMP. For example:: 1171 | 1172 | while True: 1173 | data = imu.read() 1174 | print('heading = {}'.format(data['head'])) 1175 | 1176 | will print the current heading at a rate of 4 Hz. More details about 1177 | the configuration options and the format of the data can be obtained 1178 | in the help for the functions :py:func:`rcpy.mpu9250.initialize` and 1179 | :py:func:`rcpy.mpu9250.read`. 1180 | 1181 | Constants 1182 | ^^^^^^^^^ 1183 | 1184 | The following constants can be used to set the accelerometer full scale register: 1185 | 1186 | .. py:data:: ACCEL_FSR_2G 1187 | .. py:data:: ACCEL_FSR_4G 1188 | .. py:data:: ACCEL_FSR_8G 1189 | .. py:data:: ACCEL_FSR_16G 1190 | 1191 | The following constants can be used to set the gyroscope full scale register: 1192 | 1193 | .. py:data:: GYRO_FSR_250DPS 1194 | .. py:data:: GYRO_FSR_500DPS 1195 | .. py:data:: GYRO_FSR_1000DPS 1196 | .. py:data:: GYRO_FSR_2000DPS 1197 | 1198 | The following constants can be used to set the accelerometer low-pass filter: 1199 | 1200 | .. py:data:: ACCEL_DLPF_OFF 1201 | .. py:data:: ACCEL_DLPF_184 1202 | .. py:data:: ACCEL_DLPF_92 1203 | .. py:data:: ACCEL_DLPF_41 1204 | .. py:data:: ACCEL_DLPF_20 1205 | .. py:data:: ACCEL_DLPF_10 1206 | .. py:data:: ACCEL_DLPF_5 1207 | 1208 | The following constants can be used to set the gyroscope low-pass filter: 1209 | 1210 | .. py:data:: GYRO_DLPF_OFF 1211 | .. py:data:: GYRO_DLPF_184 1212 | .. py:data:: GYRO_DLPF_92 1213 | .. py:data:: GYRO_DLPF_41 1214 | .. py:data:: GYRO_DLPF_20 1215 | .. py:data:: GYRO_DLPF_10 1216 | .. py:data:: GYRO_DLPF_5 1217 | 1218 | The following constants can be used to set the imu orientation: 1219 | 1220 | .. py:data:: ORIENTATION_Z_UP 1221 | .. py:data:: ORIENTATION_Z_DOWN 1222 | .. py:data:: ORIENTATION_X_UP 1223 | .. py:data:: ORIENTATION_X_DOWN 1224 | .. py:data:: ORIENTATION_Y_UP 1225 | .. py:data:: ORIENTATION_Y_DOWN 1226 | .. py:data:: ORIENTATION_X_FORWARD 1227 | .. py:data:: ORIENTATION_X_BACK 1228 | 1229 | Classes 1230 | ^^^^^^^ 1231 | 1232 | .. py:class:: IMU(**kwargs) 1233 | 1234 | :param kwargs kwargs: keyword arguments 1235 | 1236 | :py:class:`rcpy.mpu9250.imu` represents the MPU-9250 in the Robotics Cape or Beaglebone Blue. 1237 | 1238 | Any keyword accepted by :py:func:`rcpy.mpu9250.initialize` can be given. 1239 | 1240 | .. py:method:: set(**kwargs) 1241 | 1242 | :param kwargs kwargs: keyword arguments 1243 | 1244 | Set current MPU-9250 parameters. 1245 | 1246 | Any keyword accepted by :py:func:`rcpy.mpu9250.initialize` can be given. 1247 | 1248 | .. py:method:: read() 1249 | 1250 | :returns: dictionary with current MPU-9250 measurements 1251 | 1252 | Dictionary is constructed as in :py:func:`rcpy.mpu9250.read`. 1253 | 1254 | Low-level functions 1255 | ^^^^^^^^^^^^^^^^^^^ 1256 | 1257 | .. py:function:: initialize(accel_fsr, gyro_fsr, accel_dlpf, gyro_dlpf, enable_magnetometer, orientation, compass_time_constant, dmp_interrupt_priority, dmp_sample_rate, show_warnings, enable_dmp, enable_fusion) 1258 | 1259 | :param int accel_fsr: accelerometer full scale 1260 | :param int gyro_fsr: gyroscope full scale 1261 | :param int accel_dlpf: accelerometer low-pass filter 1262 | :param int gyro_dlpf: gyroscope low-pass filter 1263 | :param bool enable_magnetometer: :py:data:`True` enables the magnetometer 1264 | :param int orientation: imu orientation 1265 | :param float compass_time_constant: compass time-constant 1266 | :param int dmp_interrupt_priority: DMP interrupt priority 1267 | :param int dmp_sample_rate: DMP sample rate 1268 | :param int show_warnings: :py:data:`True` shows warnings 1269 | :param bool enable_dmp: :py:data:`True` enables the DMP 1270 | :param bool enable_fusion: :py:data:`True` enables data fusion algorithm 1271 | 1272 | Configure and initialize the MPU-9250. 1273 | 1274 | All parameters are optional. Default values are obtained by calling 1275 | the :c:func:`rc_get_default_imu_config` from the Robotics Cape 1276 | library. 1277 | 1278 | .. py:function:: power_off() 1279 | 1280 | Powers off the MPU-9250 1281 | 1282 | .. py:function:: read_accel_data() 1283 | 1284 | :returns: list with three-axis acceleration in m/s :math:`\!^2` 1285 | 1286 | This function forces the MPU-9250 registers to be read. 1287 | 1288 | .. py:function:: read_gyro_data() 1289 | 1290 | :returns: list with three-axis angular velocities in deg/s 1291 | 1292 | This function forces the MPU-9250 registers to be read. 1293 | 1294 | .. py:function:: read_mag_data() 1295 | 1296 | :raises mup9250Error: if magnetometer is disabled 1297 | 1298 | :returns: list with 3D magnetic field vector in :math:`\mu\!` T 1299 | 1300 | This function forces the MPU-9250 registers to be read. 1301 | 1302 | .. py:function:: read_imu_temp() 1303 | 1304 | :returns: the imu temperature in deg C 1305 | 1306 | This function forces the MPU-9250 registers to be read. 1307 | 1308 | .. py:function:: read() 1309 | 1310 | :returns: dictionary with the imu data; the keys in the dictionary depend on the current configuration 1311 | 1312 | If the magnetometer is *enabled* the dictionary contains the 1313 | following keys: 1314 | 1315 | * **accel**: 3-axis accelerations (m/s :math:`\!^2`) 1316 | * **gyro**: 3-axis angular velocities (degree/s) 1317 | * **mag**: 3D magnetic field vector in (:math:`\mu\!` T) 1318 | * **quat**: orientation quaternion 1319 | * **tb**: pitch/roll/yaw X/Y/Z angles (radians) 1320 | * **head**: heading from magnetometer (radians) 1321 | 1322 | If the magnetometer is *not enabled* the keys **mag** and **head** 1323 | are not present. 1324 | 1325 | This function forces the MPU-9250 registers to be read only if the 1326 | DMP is disabled. Otherwise it returns the latest DMP data. It is a 1327 | blocking call. 1328 | 1329 | .. _rcpy_servo: 1330 | 1331 | Module `rcpy.servo` 1332 | ------------------- 1333 | 1334 | .. py:module:: rcpy.servo 1335 | 1336 | This module provides an interface to the eight *servo* and *ESC* 1337 | (Electronic Speed Control) *channels* in the Robotics Cape. The 1338 | 3-pin servo connectors are not polarized so make sure the black/brown 1339 | ground wire is the one closest to the Robotics Cape. The command:: 1340 | 1341 | import rcpy.servo as servo 1342 | 1343 | imports the module. The :ref:`rcpy_servo` provides objects 1344 | corresponding to the each of the servo and ESC channels on the 1345 | Robotics Cape, namely :py:data:`rcpy.servo.servo1` through 1346 | :py:data:`rcpy.servo.servo8`, and namely :py:data:`rcpy.servo.esc1` 1347 | through :py:data:`rcpy.servo.esc8`. It may be convenient to import one 1348 | or all of these objects as in :: 1349 | 1350 | from rcpy.servo import servo7 1351 | 1352 | This is a convenience object which is equivalent to:: 1353 | 1354 | motor7 = Servo(7) 1355 | 1356 | The position of the servo can be set using:: 1357 | 1358 | duty = 1.5 1359 | servo7.set(duty) 1360 | 1361 | where `duty` is a number varying from -1.5 to 1.5, which controls the 1362 | angle of the servo. This command does not send a pulse to the 1363 | servo. In fact, before you can drive servos using the Robotics Cape 1364 | you need to enable power to flow to servos using:: 1365 | 1366 | servo.enable() 1367 | 1368 | For safety the servo power rail is disabled by default. Do not enable 1369 | the servo power rail when using brushless ESCs as they can be 1370 | damaged. Typical brushless ESCs only use the ground and signal lines, 1371 | so if you need to control servos and ESC simultaneously simply cut or 1372 | disconnect the middle power wire from the ESC connector. Use the 1373 | command:: 1374 | 1375 | servo.disable() 1376 | 1377 | to turn off the servo power rail. 1378 | 1379 | Back to servos, you can set the duty *and* send a pulse to the servo 1380 | at the same time using the command:: 1381 | 1382 | duty = 1.5 1383 | servo7.pulse(duty) 1384 | 1385 | Alternatively, you can initiate a :py:class:`rcpy.clock.Clock` object 1386 | using:: 1387 | 1388 | period = 0.1 1389 | clk = servo7.start(period) 1390 | 1391 | which starts sending the current servor `duty` value to the servo 1392 | every 0.1s. The clock can be stopped by:: 1393 | 1394 | clk.stop() 1395 | 1396 | The above is equivalent to:: 1397 | 1398 | import rcpy.clock as clock 1399 | period = 0.1 1400 | clk = clock.Clock(servo7, period) 1401 | clk.start() 1402 | 1403 | Similar commands are available for ESC. For example:: 1404 | 1405 | from rcpy.servo import esc3 1406 | duty = 1.0 1407 | esc3.pulse(duty) 1408 | 1409 | sends a *full-throttle* pulse to the ESC in channel 3. 1410 | 1411 | Constants 1412 | ^^^^^^^^^ 1413 | 1414 | .. py:data:: servo1 1415 | 1416 | :py:class:`rcpy.servo.Servo` representing the Robotics Cape *Servo 1*. 1417 | 1418 | .. py:data:: servo2 1419 | 1420 | :py:class:`rcpy.servo.Servo` representing the Robotics Cape *Servo 2*. 1421 | 1422 | .. py:data:: servo3 1423 | 1424 | :py:class:`rcpy.servo.Servo` representing the Robotics Cape *Servo 3*. 1425 | 1426 | .. py:data:: servo4 1427 | 1428 | :py:class:`rcpy.servo.Servo` representing the Robotics Cape *Servo 4*. 1429 | 1430 | .. py:data:: servo5 1431 | 1432 | :py:class:`rcpy.servo.Servo` representing the Robotics Cape *Servo 5*. 1433 | 1434 | .. py:data:: servo6 1435 | 1436 | :py:class:`rcpy.servo.Servo` representing the Robotics Cape *Servo 6*. 1437 | 1438 | .. py:data:: servo7 1439 | 1440 | :py:class:`rcpy.servo.Servo` representing the Robotics Cape *Servo 7*. 1441 | 1442 | .. py:data:: servo8 1443 | 1444 | :py:class:`rcpy.servo.Servo` representing the Robotics Cape *Servo 8*. 1445 | 1446 | .. py:data:: esc1 1447 | 1448 | :py:class:`rcpy.servo.ESC` representing the Robotics Cape *ESC 1*. 1449 | 1450 | .. py:data:: esc2 1451 | 1452 | :py:class:`rcpy.servo.ESC` representing the Robotics Cape *ESC 2*. 1453 | 1454 | .. py:data:: esc3 1455 | 1456 | :py:class:`rcpy.servo.ESC` representing the Robotics Cape *ESC 3*. 1457 | 1458 | .. py:data:: esc4 1459 | 1460 | :py:class:`rcpy.servo.ESC` representing the Robotics Cape *ESC 4*. 1461 | 1462 | .. py:data:: esc5 1463 | 1464 | :py:class:`rcpy.servo.ESC` representing the Robotics Cape *ESC 5*. 1465 | 1466 | .. py:data:: esc6 1467 | 1468 | :py:class:`rcpy.servo.ESC` representing the Robotics Cape *ESC 6*. 1469 | 1470 | .. py:data:: esc7 1471 | 1472 | :py:class:`rcpy.servo.ESC` representing the Robotics Cape *ESC 7*. 1473 | 1474 | .. py:data:: esc8 1475 | 1476 | :py:class:`rcpy.servo.ESC` representing the Robotics Cape *ESC 8*. 1477 | 1478 | Classes 1479 | ^^^^^^^ 1480 | 1481 | .. py:class:: Servo(channel, duty = None) 1482 | 1483 | :param output: servo channel (1 through 4) 1484 | :param state: initial servo duty cycle (Default 0) 1485 | 1486 | :py:class:`rcpy.servo.Servo` represents servos in the Robotics Cape or Beaglebone Blue. 1487 | 1488 | .. py:method:: set(duty) 1489 | 1490 | Set current servo duty cycle to `duty`. `duty` is a number 1491 | between -1.5 and 1.5. This method does not pulse the servo. Use 1492 | :py:meth:`rcpy.servo.Servo.pulse` for setting *and* pulsing the 1493 | servo at the same time. Otherwise use a 1494 | :py:class:`rcpy.clock.Clock` or the method 1495 | :py:meth:`rcpy.servo.Servo.start` to create one. The value of 1496 | `duty` corresponds to the following pulse widths and servo 1497 | angles: 1498 | 1499 | .. tabularcolumns:: c|c|c|c 1500 | 1501 | ===== ====== ====== ================= 1502 | input width angle direction 1503 | ===== ====== ====== ================= 1504 | -1.5 600us 90 deg counter-clockwise 1505 | -1.0 900us 60 deg counter-clockwise 1506 | 0.0 1500us 0 deg neutral 1507 | +1.0 2100us 60 deg clockwise 1508 | +1.5 2400us 90 deg clockwise 1509 | ===== ====== ====== ================= 1510 | 1511 | .. py:method:: pulse(duty) 1512 | 1513 | Set current servo duty cycle to `duty` and send a single pulse 1514 | to the servo. 1515 | 1516 | .. py:method:: run() 1517 | 1518 | Send a single pulse to the servo using the current value of `duty`. 1519 | 1520 | .. py:method:: start(period) 1521 | 1522 | :param float period: period 1523 | :returns: an instance of :py:class:`rcpy.clock.Clock`. 1524 | 1525 | Pulses servo periodically every `period` seconds. 1526 | 1527 | .. py:class:: ESC(channel, duty = None) 1528 | 1529 | :param output: ESC channel (1 through 4) 1530 | :param state: initial ESC duty cycle (Default 0) 1531 | 1532 | :py:class:`rcpy.servo.ESC` represents ESCs in the Robotics Cape 1533 | or Beaglebone Blue. 1534 | 1535 | .. py:method:: set(duty) 1536 | 1537 | Set current ESC duty cycle to `duty`, in which `duty` is a 1538 | number between -0.1 and 1.0. This method does not pulse the 1539 | ESC. Use :py:meth:`rcpy.servo.ESC.pulse` for setting *and* 1540 | pulsing the ESC at the same time. Otherwise use a 1541 | :py:class:`rcpy.clock.Clock` or the method 1542 | :py:meth:`rcpy.servo.ESC.start` to create one. The value of 1543 | `duty` corresponds to the following pulse widths and servo 1544 | angles: 1545 | 1546 | .. tabularcolumns:: c|c|c|c 1547 | 1548 | ===== ====== ===== ============= 1549 | input width power status 1550 | ===== ====== ===== ============= 1551 | -0.1 900us armed idle 1552 | 0.0 1000us 0% off 1553 | +0.5 1500us 50% half-throttle 1554 | +1.0 2000us 100% full-throttle 1555 | ===== ====== ===== ============= 1556 | 1557 | .. py:method:: pulse(duty) 1558 | 1559 | Set current ESC duty cycle to `duty` and send a single pulse to the ESC. 1560 | 1561 | .. py:method:: run() 1562 | 1563 | Send a single pulses to the ESC using the current value of `duty`. 1564 | 1565 | .. py:method:: start(period) 1566 | 1567 | :param float period: period 1568 | :returns: an instance of :py:class:`rcpy.clock.Clock`. 1569 | 1570 | Pulses ESC periodically every `period` seconds. 1571 | 1572 | Low-level functions 1573 | ^^^^^^^^^^^^^^^^^^^ 1574 | 1575 | .. py:function:: enable() 1576 | 1577 | Enables the servo power rail. Be careful when connecting ESCs! 1578 | 1579 | .. py:function:: disable() 1580 | 1581 | Disables the servo power rail. 1582 | 1583 | .. py:function:: pulse(channel, duty) 1584 | 1585 | :param int channel: servo channel number 1586 | :param int duty: desired servo duty cycle 1587 | 1588 | Sends a "normalized" pulse to the servo channel `channel`, in 1589 | which `duty` is a float varying from `-1.5` to `1.5`. See 1590 | :py:meth:`rcpy.servo.Servo.set` for details. 1591 | 1592 | This is a non-blocking call. 1593 | 1594 | .. py:function:: pulse_all(channel, duty) 1595 | 1596 | :param int duty: desired servo duty cycle 1597 | 1598 | Sends a "normalized" pulse to all servo channels, in which `duty` 1599 | is a float varying from `-1.5` to `1.5`. See 1600 | :py:meth:`rcpy.servo.Servo.set` for details. 1601 | 1602 | This is a non-blocking call. 1603 | 1604 | .. py:function:: esc_pulse(channel, duty) 1605 | 1606 | :param int channel: servo channel number 1607 | :param int duty: desired ESC duty cycle 1608 | 1609 | Sends a "normalized" pulse to the ESC channel `channel`, in 1610 | which `duty` is a float varying from `-0.1` to `1.5`. See 1611 | :py:meth:`rcpy.servo.ESC.set` for details. 1612 | 1613 | This is a non-blocking call. 1614 | 1615 | .. py:function:: esc_pulse_all(channel, duty) 1616 | 1617 | :param int duty: desired ESC duty cycle 1618 | 1619 | Sends a "normalized" pulse to all ESC channels, in which `duty` 1620 | is a float varying from `-0.1` to `1.5`. See 1621 | :py:meth:`rcpy.servo.ESC.set` for details. 1622 | 1623 | This is a non-blocking call. 1624 | 1625 | .. py:function:: oneshot_pulse(channel, duty) 1626 | 1627 | :param int channel: ESC channel number 1628 | :param int duty: desired ESC duty cycle 1629 | 1630 | Sends a "normalized" *oneshot* pulse to the ESC channel 1631 | `channel`, in which `duty` is a float varying from `-0.1` to 1632 | `1.0`. 1633 | 1634 | This is a non-blocking call. 1635 | 1636 | .. py:function:: oneshot_pulse_all(channel, duty) 1637 | 1638 | :param int duty: desired ESC duty cycle 1639 | 1640 | Sends a "normalized" *oneshot* pulse to all ESC channels, in 1641 | which `duty` is a float varying from `-0.1` to `1.0`. 1642 | 1643 | This is a non-blocking call. 1644 | 1645 | .. py:function:: pulse_us(channel, us) 1646 | 1647 | :param int channel: servo channel number 1648 | :param int us: desired servo duty cycle is :math:`\mu` s 1649 | 1650 | Sends a pulse of duration `us` :math:`\mu` s to all servo channels. 1651 | This is a non-blocking call. 1652 | 1653 | This is a non-blocking call. 1654 | 1655 | .. py:function:: pulse_us_all(channel, us) 1656 | 1657 | :param int us: desired servo duty cycle is :math:`\mu` s 1658 | 1659 | Sends a pulse of duration `us` :math:`\mu` s to all servo channels. 1660 | This is a non-blocking call. 1661 | 1662 | -------------------------------------------------------------------------------- /examples/rcpy_adc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Contributed by BrendanSimon 4 | 5 | # import rcpy libraries 6 | import rcpy 7 | import rcpy.adc as adc 8 | 9 | def adc_test(): 10 | # Read ADC channels via function calls. 11 | for ch in range(adc.CHANNEL_COUNT): 12 | raw = adc.get_raw(ch) 13 | voltage = adc.get_voltage(ch) 14 | print("channel={} : raw={:4} voltage={:+6.2f}".format(ch, raw, voltage)) 15 | 16 | # Read ADC channels via class instances. 17 | for ch, a in enumerate(adc.adc): 18 | raw = a.get_raw() 19 | voltage = a.get_voltage() 20 | print("adc[{}] : raw={:4} voltage={:+6.2f}".format(ch, raw, voltage)) 21 | 22 | # Read DC Jack and Battery voltages via function calls. 23 | dc_jack_voltage = adc.get_dc_jack_voltage() 24 | battery_voltage = adc.get_battery_voltage() 25 | print("dc-jack : voltage={:+6.2f}".format(dc_jack_voltage)) 26 | print("battery : voltage={:+6.2f}".format(battery_voltage)) 27 | 28 | # Read DC Jack and Battery voltages via class instances. 29 | dc_jack_voltage = adc.dc_jack.get_voltage() 30 | battery_voltage = adc.battery.get_voltage() 31 | print("dc-jack : voltage={:+6.2f}".format(dc_jack_voltage)) 32 | print("battery : voltage={:+6.2f}".format(battery_voltage)) 33 | 34 | if __name__ == "__main__": 35 | adc_test() 36 | 37 | -------------------------------------------------------------------------------- /examples/rcpy_bare_minimum.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # import python libraries 3 | import time 4 | 5 | # import rcpy library 6 | # This automatically initizalizes the robotics cape 7 | import rcpy 8 | 9 | # welcome message 10 | print("Hello BeagleBone!") 11 | print("Press Ctrl-C to exit") 12 | 13 | # set state to rcpy.RUNNING 14 | rcpy.set_state(rcpy.RUNNING) 15 | 16 | try: 17 | 18 | # keep running forever 19 | while True: 20 | 21 | # running? 22 | if rcpy.get_state() == rcpy.RUNNING: 23 | # do things 24 | pass 25 | 26 | # paused? 27 | elif rcpy.get_state() == rcpy.PAUSED: 28 | # do other things 29 | pass 30 | 31 | # sleep some 32 | time.sleep(1) 33 | 34 | except KeyboardInterrupt: 35 | # Catch Ctrl-C 36 | pass 37 | 38 | finally: 39 | 40 | # say bye 41 | print("\nBye Beaglebone!") 42 | 43 | # exiting program will automatically clean up cape 44 | -------------------------------------------------------------------------------- /examples/rcpy_bare_minimum_dmp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # import python libraries 3 | import time 4 | 5 | # import rcpy library 6 | # This automatically initizalizes the robotics cape 7 | import rcpy 8 | import rcpy.mpu9250 as mpu9250 9 | 10 | # welcome message 11 | print("Hello BeagleBone!") 12 | print("Press Ctrl-C to exit") 13 | 14 | # enable dmp 15 | sample_rate = 4 16 | imu = mpu9250.IMU(enable_dmp = True, 17 | dmp_sample_rate = sample_rate) 18 | 19 | # set state to rcpy.RUNNING 20 | rcpy.set_state(rcpy.RUNNING) 21 | 22 | # spinning wheel 23 | i = 0 24 | spin = '-\|/' 25 | 26 | try: 27 | 28 | # keep running forever 29 | while True: 30 | 31 | # read to synchronize with imu 32 | data = imu.read() 33 | 34 | # running? 35 | if rcpy.get_state() == rcpy.RUNNING: 36 | 37 | # do things 38 | print('\r{}'.format(spin[i % len(spin)]), end='') 39 | i = i + 1 40 | 41 | # paused? 42 | elif rcpy.get_state() == rcpy.PAUSED: 43 | # do other things 44 | pass 45 | 46 | # there is no need to sleep 47 | 48 | except KeyboardInterrupt: 49 | # Catch Ctrl-C 50 | pass 51 | 52 | finally: 53 | 54 | # say bye 55 | print("\nBye Beaglebone!") 56 | 57 | # exiting program will automatically clean up cape 58 | -------------------------------------------------------------------------------- /examples/rcpy_bare_minimum_thread.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # import python libraries 3 | import time 4 | import threading 5 | 6 | # import rcpy library 7 | # This automatically initizalizes the robotics cape 8 | import rcpy 9 | import rcpy.mpu9250 as mpu9250 10 | 11 | # function to run on a thread 12 | def thread_function(imu, name): 13 | 14 | # make sure the thread will terminate 15 | while rcpy.get_state() != rcpy.EXITING: 16 | 17 | # read to synchronize with imu 18 | data = imu.read() 19 | 20 | # handle other states 21 | if rcpy.get_state() == rcpy.RUNNING: 22 | 23 | # do things 24 | print('Hi from thread {}!'.format(name)) 25 | 26 | # there is no need to sleep 27 | 28 | # welcome message 29 | print("Hello BeagleBone!") 30 | print("Press Ctrl-C to exit") 31 | 32 | # enable dmp 33 | sample_rate = 8 34 | imu = mpu9250.IMU(enable_dmp = True, 35 | dmp_sample_rate = sample_rate) 36 | 37 | # set state to rcpy.RUNNING 38 | rcpy.set_state(rcpy.RUNNING) 39 | 40 | # fire up threads 41 | threads = [] 42 | threads.append(threading.Thread(target = thread_function, args = (imu, "#1"))) 43 | threads.append(threading.Thread(target = thread_function, args = (imu, "#2"))) 44 | for t in threads: 45 | t.start() 46 | 47 | # go do other things 48 | try: 49 | 50 | # keep running forever 51 | while True: 52 | 53 | # print some 54 | print('Hi from main loop!') 55 | 56 | # sleep some 57 | time.sleep(1) 58 | 59 | except KeyboardInterrupt: 60 | # Catch Ctrl-C 61 | pass 62 | 63 | finally: 64 | 65 | # wait for threads to finish 66 | for t in threads: 67 | t.join() 68 | 69 | # say bye 70 | print("\nBye Beaglebone!") 71 | 72 | # exiting program will automatically clean up cape 73 | -------------------------------------------------------------------------------- /examples/rcpy_blink.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # import rcpy libraries 3 | import rcpy 4 | import rcpy.gpio as gpio 5 | import rcpy.button as button 6 | import rcpy.led as led 7 | 8 | # configure LEDs 9 | rates = (0.5, 0.25, 0.1) 10 | index = 0 11 | 12 | # blink leds 13 | led.red.on() 14 | led.green.off() 15 | blink_red = led.Blink(led.red, rates[index % len(rates)]) 16 | blink_green = led.Blink(led.green, rates[index % len(rates)]) 17 | blink_red.start() 18 | blink_green.start() 19 | 20 | # set state to rcpy.RUNNING 21 | rcpy.set_state(rcpy.RUNNING) 22 | 23 | # mode pressed? 24 | def mode_pressed(input, event, blink_red, blink_green, rates): 25 | 26 | # increment rate 27 | global index 28 | index += 1 29 | 30 | print(" pressed, stepping blinking rate to {} s".format(rates[index % len(rates)])) 31 | 32 | # change blink period 33 | blink_red.set_period(rates[index % len(rates)]) 34 | blink_green.set_period(rates[index % len(rates)]) 35 | 36 | # reinitialize leds 37 | led.red.on() 38 | led.green.off() 39 | 40 | # create and start ButtonEvent 41 | mode_event = button.ButtonEvent(button.mode, 42 | button.ButtonEvent.PRESSED, 43 | target = mode_pressed, 44 | vargs = (blink_red, blink_green, rates)) 45 | mode_event.start() 46 | 47 | # welcome message 48 | print("Green and red LEDs should be flashing") 49 | print("Press button to change the blink rate") 50 | print("Press button to stop or restart blinking") 51 | print("Hold button for 2 s to exit") 52 | 53 | try: 54 | 55 | while rcpy.get_state() != rcpy.EXITING: 56 | 57 | # this is a blocking call! 58 | if button.pause.pressed(): 59 | 60 | # pause pressed 61 | 62 | try: 63 | 64 | # this is a blocking call with a timeout! 65 | if button.pause.released(timeout = 2000): 66 | 67 | # released too soon! 68 | print(" pressed, toggle blinking") 69 | 70 | # toggle blinking 71 | blink_red.toggle() 72 | blink_green.toggle() 73 | 74 | except gpio.InputTimeout: 75 | 76 | # timeout 77 | print(" held, exiting...") 78 | 79 | # exit 80 | rcpy.set_state(rcpy.EXITING) 81 | 82 | except KeyboardInterrupt: 83 | # Catch Ctrl-C 84 | pass 85 | 86 | finally: 87 | 88 | print("Exiting...") 89 | mode_event.stop() 90 | blink_red.stop() 91 | blink_green.stop() 92 | 93 | # say bye 94 | print("\nBye Beaglebone!") 95 | 96 | -------------------------------------------------------------------------------- /examples/rcpy_test_dmp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # import python libraries 3 | import time 4 | import getopt, sys 5 | 6 | # import rcpy library 7 | # This automatically initizalizes the robotics cape 8 | import rcpy 9 | import rcpy.mpu9250 as mpu9250 10 | 11 | def usage(): 12 | print("""usage: python rcpy_test_dmp [options] ... 13 | Options are: 14 | -m enable magnetometer 15 | -s rate Set sample rate in HZ (default 100) 16 | Sample rate must be a divisor of 200 17 | -c Print compass angle 18 | -a Print accelerometer data 19 | -g Print gyro data 20 | -t Print Tait-Bryan angles 21 | -q Print quaternion vector 22 | -f Print fused data 23 | -n Newline (default = '\r') 24 | -o Show a menu to select IMU orientation 25 | -h print this help message""") 26 | 27 | def main(): 28 | 29 | # exit if no options 30 | if len(sys.argv) < 2: 31 | usage() 32 | sys.exit(2) 33 | 34 | # Parse command line 35 | try: 36 | opts, args = getopt.getopt(sys.argv[1:], "hmpcagtqfos:", ["help"]) 37 | 38 | except getopt.GetoptError as err: 39 | # print help information and exit: 40 | print('rcpy_test_dmp: illegal option {}'.format(sys.argv[1:])) 41 | usage() 42 | sys.exit(2) 43 | 44 | # defaults 45 | enable_magnetometer = False 46 | show_compass = False 47 | show_gyro = False 48 | show_accel = False 49 | show_quat = False 50 | show_tb = False 51 | sample_rate = 100 52 | enable_fusion = False 53 | show_period = False 54 | newline = '\r' 55 | 56 | for o, a in opts: 57 | if o in ("-h", "--help"): 58 | usage() 59 | sys.exit() 60 | elif o in "-s": 61 | sample_rate = int(a) 62 | elif o == "-m": 63 | enable_magnetometer = True 64 | elif o == "-c": 65 | show_compass = True 66 | elif o == "-a": 67 | show_accel = True 68 | elif o == "-g": 69 | show_gyro = True 70 | elif o == "-q": 71 | show_quat = True 72 | elif o == "-t": 73 | show_tb = True 74 | elif o == "-f": 75 | enable_fusion = True 76 | elif o == "-p": 77 | show_period = True 78 | elif o == "-n": 79 | newline = a 80 | else: 81 | assert False, "Unhandled option" 82 | 83 | if show_compass and not enable_magnetometer: 84 | print('rcpy_test_dmp: -c can only be used with -m ') 85 | usage() 86 | sys.exit(2) 87 | 88 | # set state to rcpy.RUNNING 89 | rcpy.set_state(rcpy.RUNNING) 90 | 91 | # magnetometer ? 92 | mpu9250.initialize(enable_dmp=True, 93 | dmp_sample_rate=sample_rate, 94 | enable_fusion=enable_fusion, 95 | enable_magnetometer=enable_magnetometer) 96 | 97 | # message 98 | print("Press Ctrl-C to exit") 99 | 100 | # header 101 | if show_accel: 102 | print(" Accel XYZ (m/s^2) |", end='') 103 | if show_gyro: 104 | print(" Gyro XYZ (deg/s) |", end='') 105 | if show_compass: 106 | print(" Mag Field XYZ (uT) |", end='') 107 | print("Head(rad)|", end='') 108 | if show_quat: 109 | print(" Quaternion |", end='') 110 | if show_tb: 111 | print(" Tait Bryan (rad) |", end='') 112 | if show_period: 113 | print(" Ts (ms)", end='') 114 | print() 115 | 116 | try: 117 | 118 | # keep running 119 | while True: 120 | 121 | # running 122 | if rcpy.get_state() == rcpy.RUNNING: 123 | 124 | t0 = time.perf_counter() 125 | data = mpu9250.read() 126 | t1 = time.perf_counter() 127 | dt = t1 - t0 128 | t0 = t1 129 | 130 | print(newline, end='') 131 | if show_accel: 132 | print('{0[0]:6.2f} {0[1]:6.2f} {0[2]:6.2f} |' 133 | .format(data['accel']), end='') 134 | if show_gyro: 135 | print('{0[0]:6.1f} {0[1]:6.1f} {0[2]:6.1f} |' 136 | .format(data['gyro']), end='') 137 | if show_compass: 138 | print('{0[0]:7.1f} {0[1]:7.1f} {0[2]:7.1f} |' 139 | .format(data['mag']), end='') 140 | print(' {:6.2f} |' 141 | .format(data['head']), end='') 142 | if show_quat: 143 | print('{0[0]:6.1f} {0[1]:6.1f} {0[2]:6.1f} {0[3]:6.1f} |' 144 | .format(data['quat']), end='') 145 | if show_tb: 146 | print('{0[0]:6.2f} {0[1]:6.2f} {0[2]:6.2f} |' 147 | .format(data['tb']), end='') 148 | if show_period: 149 | print(' {:7.2f}'.format(1000*dt), end='') 150 | 151 | # no need to sleep 152 | 153 | except KeyboardInterrupt: 154 | # Catch Ctrl-C 155 | pass 156 | 157 | finally: 158 | 159 | # say bye 160 | print("\nBye Beaglebone!") 161 | 162 | # exiting program will automatically clean up cape 163 | 164 | if __name__ == "__main__": 165 | main() 166 | -------------------------------------------------------------------------------- /examples/rcpy_test_encoders.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # import python libraries 3 | import time 4 | import getopt, sys 5 | 6 | # import rcpy library 7 | # This automatically initizalizes the robotics cape 8 | import rcpy 9 | import rcpy.encoder as encoder 10 | 11 | def usage(): 12 | print("""usage: python rcpy_test_encoders [options] ... 13 | Options are: 14 | -e encoder specify a single encoder from 1-4, 0 for all channels 15 | -h print this help message""") 16 | 17 | def main(): 18 | 19 | # exit if no options 20 | if len(sys.argv) < 2: 21 | usage() 22 | sys.exit(2) 23 | 24 | # Parse command line 25 | try: 26 | opts, args = getopt.getopt(sys.argv[1:], "he:", ["help"]) 27 | 28 | except getopt.GetoptError as err: 29 | # print help information and exit: 30 | print('rcpy_test_encoders: illegal option {}'.format(sys.argv[1:])) 31 | usage() 32 | sys.exit(2) 33 | 34 | # defaults 35 | channel = 0 36 | 37 | for o, a in opts: 38 | if o in ("-h", "--help"): 39 | usage() 40 | sys.exit() 41 | elif o in "-e": 42 | channel = int(a) 43 | else: 44 | assert False, "Unhandled option" 45 | 46 | # set state to rcpy.RUNNING 47 | rcpy.set_state(rcpy.RUNNING) 48 | 49 | # message 50 | print("Press Ctrl-C to exit") 51 | 52 | # header 53 | if channel == 0: 54 | print(' E1 | E2 | E3 | E4') 55 | else: 56 | print(' E{}'.format(channel)) 57 | 58 | try: 59 | 60 | # keep running 61 | while True: 62 | 63 | # running 64 | if rcpy.get_state() == rcpy.RUNNING: 65 | 66 | if channel == 0: 67 | 68 | # read all encoders 69 | e1 = encoder.get(1) 70 | e2 = encoder.get(2) 71 | e3 = encoder.get(3) 72 | e4 = encoder.get(4) 73 | 74 | print('\r {:+6d} | {:+6d} | {:+6d} | {:+6d}'.format(e1,e2,e3,e4), end='') 75 | 76 | else: 77 | 78 | # read one encoder 79 | e = encoder.get(channel) 80 | 81 | print('\r {:+6d}'.format(e), end='') 82 | 83 | # sleep some 84 | time.sleep(.5) 85 | 86 | except KeyboardInterrupt: 87 | # Catch Ctrl-C 88 | pass 89 | 90 | finally: 91 | 92 | # say bye 93 | print("\nBye Beaglebone!") 94 | 95 | # exiting program will automatically clean up cape 96 | 97 | if __name__ == "__main__": 98 | main() 99 | -------------------------------------------------------------------------------- /examples/rcpy_test_imu.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # import python libraries 3 | import time 4 | import getopt, sys 5 | 6 | # import rcpy library 7 | # This automatically initizalizes the robotics cape 8 | import rcpy 9 | import rcpy.mpu9250 as mpu9250 10 | 11 | def usage(): 12 | print("""usage: python rcpy_test_imu [options] ... 13 | Options are: 14 | -m enable magnetometer 15 | -h print this help message""") 16 | 17 | def main(): 18 | 19 | # Parse command line 20 | try: 21 | opts, args = getopt.getopt(sys.argv[1:], "hm", ["help"]) 22 | 23 | except getopt.GetoptError as err: 24 | # print help information and exit: 25 | print('rcpy_test_imu: illegal option {}'.format(sys.argv[1:])) 26 | usage() 27 | sys.exit(2) 28 | 29 | # defaults 30 | enable_magnetometer = False 31 | 32 | for o, a in opts: 33 | if o in ("-h", "--help"): 34 | usage() 35 | sys.exit() 36 | elif o == "-m": 37 | enable_magnetometer = True 38 | else: 39 | assert False, "Unhandled option" 40 | 41 | # set state to rcpy.RUNNING 42 | rcpy.set_state(rcpy.RUNNING) 43 | 44 | # no magnetometer 45 | mpu9250.initialize(enable_magnetometer = enable_magnetometer) 46 | 47 | # message 48 | print("try 'python rcpy_test_imu -h' to see other options") 49 | print("Press Ctrl-C to exit") 50 | 51 | # header 52 | print(" Accel XYZ (m/s^2) |" 53 | " Gyro XYZ (deg/s) |", end='') 54 | if enable_magnetometer: 55 | print(" Mag Field XYZ (uT) |", end='') 56 | print(' Temp (C)') 57 | 58 | try: 59 | 60 | # keep running 61 | while True: 62 | 63 | # running 64 | if rcpy.get_state() == rcpy.RUNNING: 65 | 66 | temp = mpu9250.read_imu_temp() 67 | data = mpu9250.read() 68 | 69 | if enable_magnetometer: 70 | print(('\r{0[0]:6.2f} {0[1]:6.2f} {0[2]:6.2f} |' 71 | '{1[0]:6.1f} {1[1]:6.1f} {1[2]:6.1f} |' 72 | '{2[0]:6.1f} {2[1]:6.1f} {2[2]:6.1f} |' 73 | ' {3:6.1f}').format(data['accel'], 74 | data['gyro'], 75 | data['mag'], 76 | temp), 77 | end='') 78 | else: 79 | print(('\r{0[0]:6.2f} {0[1]:6.2f} {0[2]:6.2f} |' 80 | '{1[0]:6.1f} {1[1]:6.1f} {1[2]:6.1f} |' 81 | ' {2:6.1f}').format(data['accel'], 82 | data['gyro'], 83 | temp), 84 | end='') 85 | 86 | # sleep some 87 | time.sleep(.5) 88 | 89 | except KeyboardInterrupt: 90 | # Catch Ctrl-C 91 | pass 92 | 93 | finally: 94 | 95 | # say bye 96 | print("\nBye Beaglebone!") 97 | 98 | # exiting program will automatically clean up cape 99 | 100 | if __name__ == "__main__": 101 | main() 102 | -------------------------------------------------------------------------------- /examples/rcpy_test_leds.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # import rcpy libraries 3 | import rcpy 4 | import rcpy.led as led 5 | import time 6 | 7 | ON = 1 8 | OFF = 0 9 | 10 | # blink leds 11 | green = led.LED(2,3,ON) 12 | red = led.LED(2,2,OFF) 13 | 14 | 15 | # set state to rcpy.RUNNING 16 | rcpy.set_state(rcpy.RUNNING) 17 | 18 | 19 | 20 | # welcome message 21 | print("Green and red LEDs should be flashing") 22 | 23 | 24 | try: 25 | 26 | # keep running 27 | while True: 28 | red.toggle() 29 | green.toggle() 30 | # sleep some 31 | time.sleep(.5) 32 | 33 | except KeyboardInterrupt: 34 | # Catch Ctrl-C 35 | pass 36 | 37 | 38 | finally: 39 | 40 | print("Exiting...") 41 | red.off() 42 | green.off() 43 | 44 | # say bye 45 | print("\nBye Beaglebone!") 46 | 47 | -------------------------------------------------------------------------------- /examples/rcpy_test_motors.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # import python libraries 3 | import time, math 4 | import getopt, sys 5 | 6 | # import rcpy library 7 | # This automatically initizalizes the robotics cape 8 | import rcpy 9 | import rcpy.motor as motor 10 | 11 | def usage(): 12 | print("""usage: python rcpy_test_motors [options] ... 13 | Options are: 14 | -d duty define a duty cycle from -1.0 to 1.0 15 | -m motor specify a single motor from 1-4, 0 for all motors 16 | -s sweep motors back and forward at duty cycle 17 | -b enable motor brake function 18 | -f enable free spin function 19 | -n number of steps per sweep (default = 20) 20 | -t period of sweep (default = 4s) 21 | -h print this help message""") 22 | 23 | def main(): 24 | 25 | # exit if no options 26 | if len(sys.argv) < 2: 27 | usage() 28 | sys.exit(2) 29 | 30 | # Parse command line 31 | try: 32 | opts, args = getopt.getopt(sys.argv[1:], "bfhsd:m:", ["help"]) 33 | 34 | except getopt.GetoptError as err: 35 | # print help information and exit: 36 | print('rcpy_test_motors: illegal option {}'.format(sys.argv[1:])) 37 | usage() 38 | sys.exit(2) 39 | 40 | # defaults 41 | duty = 0.0 42 | channel = 0 43 | sweep = False 44 | brk = False 45 | free = False 46 | steps = 20 47 | period = 4 48 | 49 | for o, a in opts: 50 | if o in ("-h", "--help"): 51 | usage() 52 | sys.exit() 53 | elif o in "-d": 54 | duty = float(a) 55 | elif o in "-m": 56 | channel = int(a) 57 | elif o == "-s": 58 | sweep = True 59 | elif o == "-b": 60 | brk = True 61 | elif o == "-f": 62 | free = True 63 | elif o == "-t": 64 | period = float(a) 65 | elif o == "-n": 66 | steps = int(a) 67 | else: 68 | assert False, "Unhandled option" 69 | 70 | # set state to rcpy.RUNNING 71 | rcpy.set_state(rcpy.RUNNING) 72 | 73 | # set motor duty (only one option at a time) 74 | if brk: 75 | print('Breaking motor {}'.format(channel)) 76 | motor.set_brake(channel) 77 | sweep = False 78 | elif free: 79 | print('Setting motor {} free'.format(channel)) 80 | motor.set_free_spin(channel) 81 | sweep = False 82 | elif duty != 0: 83 | if not sweep: 84 | print('Setting motor {} to {} duty'.format(channel, duty)) 85 | motor.set(channel, duty) 86 | else: 87 | print('Sweeping motor {} to {} duty'.format(channel, duty)) 88 | else: 89 | sweep = False 90 | 91 | # message 92 | print("Press Ctrl-C to exit") 93 | 94 | try: 95 | 96 | # sweep 97 | if sweep: 98 | 99 | d = 0 100 | direction = 1 101 | duty = math.fabs(duty) 102 | delta = duty/steps 103 | dt = period/steps/2 104 | 105 | # keep running 106 | while rcpy.get_state() != rcpy.EXITING: 107 | 108 | # running 109 | if rcpy.get_state() == rcpy.RUNNING: 110 | 111 | # increment duty 112 | d = d + direction * delta 113 | motor.set(channel, d) 114 | 115 | # end of range? 116 | if d > duty or d < -duty: 117 | direction = direction * -1 118 | 119 | elif rcpy.get_state() == rcpy.PAUSED: 120 | 121 | # set motors to free spin 122 | motor.set_free_spin(channel) 123 | d = 0 124 | 125 | # sleep some 126 | time.sleep(dt) 127 | 128 | # or do nothing 129 | else: 130 | 131 | # keep running 132 | while rcpy.get_state() != rcpy.EXITING: 133 | 134 | # sleep some 135 | time.sleep(1) 136 | 137 | except KeyboardInterrupt: 138 | # handle what to do when Ctrl-C was pressed 139 | pass 140 | 141 | finally: 142 | 143 | # say bye 144 | print("\nBye Beaglebone!") 145 | 146 | # exiting program will automatically clean up cape 147 | 148 | if __name__ == "__main__": 149 | main() 150 | -------------------------------------------------------------------------------- /examples/rcpy_test_servos.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # import python libraries 3 | import time, math 4 | import getopt, sys 5 | 6 | # import rcpy library 7 | # This automatically initizalizes the robotics cape 8 | import rcpy 9 | import rcpy.servo as servo 10 | import rcpy.clock as clock 11 | 12 | def usage(): 13 | print("""usage: python rcpy_test_servos [options] ... 14 | Options are: 15 | -d duty define a duty cycle from -1.5 to 1.5 16 | -c servo specify a single servo from 1-8, 0 for all servos 17 | -s sweep servos back and forward at duty cycle 18 | -t period period 19 | -h print this help message""") 20 | 21 | def main(): 22 | 23 | # exit if no options 24 | if len(sys.argv) < 2: 25 | usage() 26 | sys.exit(2) 27 | 28 | # Parse command line 29 | try: 30 | opts, args = getopt.getopt(sys.argv[1:], "hst:d:c:", ["help"]) 31 | 32 | except getopt.GetoptError as err: 33 | # print help information and exit: 34 | print('rcpy_test_servos: illegal option {}'.format(sys.argv[1:])) 35 | usage() 36 | sys.exit(2) 37 | 38 | # defaults 39 | duty = 1.5 40 | period = 0.02 41 | channel = 0 42 | sweep = False 43 | brk = False 44 | free = False 45 | 46 | for o, a in opts: 47 | if o in ("-h", "--help"): 48 | usage() 49 | sys.exit() 50 | elif o in "-d": 51 | duty = float(a) 52 | elif o in "-t": 53 | period = float(a) 54 | elif o in "-c": 55 | channel = int(a) 56 | elif o == "-s": 57 | sweep = True 58 | else: 59 | assert False, "Unhandled option" 60 | 61 | # set state to rcpy.RUNNING 62 | rcpy.set_state(rcpy.RUNNING) 63 | 64 | # set servo duty (only one option at a time) 65 | srvo = servo.Servo(channel) 66 | if duty != 0: 67 | if not sweep: 68 | print('Setting servo {} to {} duty'.format(channel, duty)) 69 | srvo.set(duty) 70 | else: 71 | print('Sweeping servo {} to {} duty'.format(channel, duty)) 72 | else: 73 | sweep = False 74 | 75 | # message 76 | print("Press Ctrl-C to exit") 77 | 78 | clck = clock.Clock(srvo, period) 79 | 80 | try: 81 | 82 | # enable servos 83 | servo.enable() 84 | 85 | # start clock 86 | clck.start() 87 | 88 | # sweep 89 | if sweep: 90 | 91 | d = 0 92 | direction = 1 93 | duty = math.fabs(duty) 94 | delta = duty/100 95 | 96 | # keep running 97 | while rcpy.get_state() != rcpy.EXITING: 98 | 99 | # increment duty 100 | d = d + direction * delta 101 | 102 | # end of range? 103 | if d > duty or d < -duty: 104 | direction = direction * -1 105 | if d > duty: 106 | d = duty 107 | else: 108 | d = -duty 109 | 110 | srvo.set(d) 111 | 112 | # sleep some 113 | time.sleep(.02) 114 | 115 | # or do nothing 116 | else: 117 | 118 | # keep running 119 | while rcpy.get_state() != rcpy.EXITING: 120 | 121 | # sleep some 122 | time.sleep(1) 123 | 124 | except KeyboardInterrupt: 125 | # handle what to do when Ctrl-C was pressed 126 | pass 127 | 128 | finally: 129 | 130 | # stop clock 131 | clck.stop() 132 | 133 | # disable servos 134 | servo.disable() 135 | 136 | # say bye 137 | print("\nBye Beaglebone!") 138 | 139 | # exiting program will automatically clean up cape 140 | 141 | if __name__ == "__main__": 142 | main() 143 | -------------------------------------------------------------------------------- /rcpy/__init__.py: -------------------------------------------------------------------------------- 1 | import warnings 2 | import signal 3 | import sys, os, time 4 | 5 | from rcpy._rcpy import get_state 6 | from rcpy._rcpy import set_state as _set_state 7 | 8 | #from hanging_threads import start_monitoring 9 | #monitoring_thread = start_monitoring() 10 | 11 | # constants 12 | IDLE = 0 13 | RUNNING = 1 14 | PAUSED = 2 15 | EXITING = 3 16 | 17 | # create pipes for communicating state 18 | _RC_STATE_PIPE_LIST = [] 19 | 20 | def _get_state_pipe_list(p = _RC_STATE_PIPE_LIST): 21 | return p 22 | 23 | # creates pipes for communication 24 | 25 | def create_pipe(): 26 | r_fd, w_fd = os.pipe() 27 | _get_state_pipe_list().append((r_fd, w_fd)) 28 | return (r_fd, w_fd) 29 | 30 | def destroy_pipe(pipe): 31 | _get_state_pipe_list().remove(pipe) 32 | (r_fd, w_fd) = pipe 33 | os.close(r_fd) 34 | os.close(w_fd) 35 | 36 | # set state 37 | def set_state(state): 38 | # write to open pipes 39 | for (r_fd, w_fd) in _get_state_pipe_list(): 40 | os.write(w_fd, bytes(str(state), 'UTF-8')) 41 | # call robotics cape set_state 42 | _set_state(state) 43 | 44 | # cleanup function 45 | _CLEANUP_FLAG = False 46 | _cleanup_functions = {} 47 | 48 | def add_cleanup(fun, pars): 49 | global _cleanup_functions 50 | _cleanup_functions[fun] = pars 51 | 52 | def cleanup(): 53 | global _CLEANUP_FLAG 54 | global _cleanup_functions 55 | # return to avoid multiple calls to cleanup 56 | if _CLEANUP_FLAG: 57 | return 58 | _CLEANUP_FLAG = True 59 | 60 | print('Initiating cleanup...') 61 | 62 | # call cleanup functions 63 | for fun, pars in _cleanup_functions.items(): 64 | fun(*pars) 65 | 66 | # get state pipes 67 | pipes = _get_state_pipe_list() 68 | if len(pipes): 69 | print('{} pipes open'.format(len(pipes))) 70 | 71 | # set state as exiting 72 | set_state(EXITING) 73 | 74 | if len(pipes): 75 | print('Closing pipes') 76 | # close open pipes left 77 | while len(pipes): 78 | destroy_pipe(pipes[0]) 79 | 80 | print('Done with cleanup') 81 | 82 | # idle function 83 | def idle(): 84 | set_state(IDLE) 85 | 86 | # run function 87 | def run(): 88 | set_state(RUNNING) 89 | 90 | # pause function 91 | def pause(): 92 | set_state(PAUSED) 93 | 94 | # exit function 95 | def exit(): 96 | set_state(EXITING) 97 | 98 | # cleanup handler 99 | def handler(signum, frame): 100 | 101 | # warn 102 | warnings.warn('Signal handler called with signal {}'.format(signum)) 103 | 104 | # call rcpy cleanup 105 | cleanup() 106 | 107 | # no need to cleanup later 108 | atexit.unregister(cleanup) 109 | 110 | warnings.warn('> Robotics cape exited cleanly') 111 | 112 | raise KeyboardInterrupt() 113 | 114 | # set initial state 115 | set_state(PAUSED) 116 | warnings.warn('> Robotics cape initialized') 117 | 118 | # make sure it is disabled when exiting cleanly 119 | import atexit; atexit.register(cleanup) 120 | 121 | if 'RCPY_NO_HANDLERS' in os.environ: 122 | warnings.warn('> RCPY_NO_HANDLERS is set. User is responsible for handling signals') 123 | 124 | else: 125 | 126 | # install handler 127 | warnings.warn('> Installing signal handlers') 128 | signal.signal(signal.SIGINT, handler) 129 | signal.signal(signal.SIGTERM, handler) 130 | -------------------------------------------------------------------------------- /rcpy/adc.py: -------------------------------------------------------------------------------- 1 | import rcpy 2 | from rcpy._adc import * 3 | 4 | CHANNEL_COUNT = 7 5 | CHANNEL_MIN = 0 6 | CHANNEL_MAX = 6 7 | 8 | class ADC: 9 | 10 | def __init__(self, channel): 11 | self.channel = channel 12 | 13 | def get_raw(self): 14 | return get_raw(self.channel) 15 | 16 | def get_voltage(self): 17 | return get_voltage(self.channel) 18 | 19 | class DC_Jack: 20 | 21 | def get_voltage(self): 22 | return get_dc_jack_voltage() 23 | 24 | class Battery: 25 | 26 | def get_voltage(self): 27 | return get_battery_voltage() 28 | 29 | # define adcs 30 | adc0 = ADC(0) 31 | adc1 = ADC(1) 32 | adc2 = ADC(2) 33 | adc3 = ADC(3) 34 | adc4 = ADC(4) 35 | adc5 = ADC(5) 36 | adc6 = ADC(6) 37 | 38 | # list of ADCs, indexed by channel number 39 | adc = [ adc0, adc1, adc2, adc3, adc4, adc5, adc6 ] 40 | 41 | dc_jack = DC_Jack() 42 | 43 | battery = Battery() 44 | 45 | -------------------------------------------------------------------------------- /rcpy/button.py: -------------------------------------------------------------------------------- 1 | import rcpy 2 | import rcpy.gpio as gpio 3 | 4 | import threading 5 | import time 6 | 7 | DEBOUNCE = 3 8 | PRESSED = gpio.LOW 9 | RELEASED = gpio.HIGH 10 | 11 | class ButtonEvent(gpio.InputEvent): 12 | 13 | PRESSED = gpio.InputEvent.LOW 14 | RELEASED = gpio.InputEvent.HIGH 15 | 16 | def __init__(self, input, event, debounce = DEBOUNCE, timeout = None, 17 | target = None, vargs = (), kwargs = {}): 18 | 19 | super().__init__(input, event, debounce, timeout, 20 | target, vargs, kwargs) 21 | 22 | class Button(gpio.Input): 23 | 24 | def is_pressed(self): 25 | return self.is_low() 26 | 27 | def is_released(self): 28 | return self.is_high() 29 | 30 | def pressed_or_released(self, debounce = DEBOUNCE, timeout = None): 31 | return self.high_or_low(debounce, timeout) 32 | 33 | def pressed(self, debounce = DEBOUNCE, timeout = None): 34 | return self.low(debounce, timeout) 35 | 36 | def released(self, debounce = DEBOUNCE, timeout = None): 37 | return self.high(debounce, timeout) 38 | 39 | # definitions 40 | 41 | # BUTTONs 42 | pause = Button(*gpio.PAUSE_BTN) 43 | mode = Button(*gpio.MODE_BTN) 44 | -------------------------------------------------------------------------------- /rcpy/clock.py: -------------------------------------------------------------------------------- 1 | import rcpy 2 | 3 | import threading, time, warnings 4 | 5 | class Action: 6 | 7 | def run(self): 8 | raise Exception("Method run has not been defined yet.") 9 | 10 | class Actions(Action): 11 | 12 | def __init__(self, *actions): 13 | self.actions = actions 14 | 15 | def run(self): 16 | for a in self.actions: 17 | a.run() 18 | 19 | class Clock(threading.Thread): 20 | 21 | def __init__(self, action, period = 1): 22 | 23 | # call super 24 | super().__init__() 25 | 26 | self.condition = threading.Condition() 27 | self.period = period 28 | 29 | if isinstance(action, Action): 30 | self.action = action 31 | else: 32 | raise Exception("action must be of class Action") 33 | 34 | self._suspend = False 35 | 36 | def set_period(self, period): 37 | self.period = period 38 | 39 | def toggle(self): 40 | self._suspend = not self._suspend 41 | 42 | def _run(self): 43 | 44 | # Acquire lock 45 | self.condition.acquire() 46 | 47 | # Toggle 48 | if not self._suspend: 49 | self.action.run() 50 | 51 | # Notify lock 52 | self.condition.notify_all() 53 | 54 | # Release lock 55 | self.condition.release() 56 | 57 | def run(self): 58 | 59 | self.run = True 60 | while rcpy.get_state() != rcpy.EXITING and self.run: 61 | 62 | # Acquire condition 63 | self.condition.acquire() 64 | 65 | # Setup timer 66 | self.timer = threading.Timer(self.period, self._run) 67 | self.timer.start() 68 | 69 | # Wait 70 | self.condition.wait() 71 | 72 | # and release 73 | self.condition.release() 74 | 75 | def stop(self): 76 | 77 | self.run = False; 78 | 79 | # Acquire lock 80 | self.condition.acquire() 81 | 82 | # Notify lock 83 | self.condition.notify_all() 84 | 85 | # Release lock 86 | self.condition.release() 87 | -------------------------------------------------------------------------------- /rcpy/encoder.py: -------------------------------------------------------------------------------- 1 | import rcpy 2 | from rcpy._encoder import * 3 | 4 | class Encoder: 5 | 6 | def __init__(self, channel, count = None): 7 | 8 | self.channel = channel 9 | if count is not None: 10 | self.set(count) 11 | 12 | def get(self): 13 | return get(self.channel) 14 | 15 | def set(self, count): 16 | set(self.channel, count) 17 | 18 | def reset(self): 19 | set(self.channel, 0) 20 | 21 | # define leds 22 | encoder1 = Encoder(1) 23 | encoder2 = Encoder(2) 24 | encoder3 = Encoder(3) 25 | encoder4 = Encoder(4) 26 | -------------------------------------------------------------------------------- /rcpy/gpio.py: -------------------------------------------------------------------------------- 1 | import rcpy 2 | 3 | import sys 4 | import gpiod 5 | 6 | # definitions 7 | HIGH = 1 8 | LOW = 0 9 | 10 | # direction 11 | IN = 0 12 | OUT = 1 13 | 14 | # edge 15 | EDGE_NONE = 0 16 | EDGE_RISING = 1 17 | EDGE_FALLING = 2 18 | EDGE_BOTH = 3 19 | 20 | # input pins 21 | PAUSE_BTN = (2, 5) # gpio2.5 P8.9 22 | MODE_BTN = (2, 4) # gpio2.4 P8.10 23 | IMU_INTERRUPT_PIN = (3, 21) # gpio3.21 P9.25 24 | 25 | # gpio output pins 26 | RED_LED = (2,2) # gpio2.2 P8.7 27 | GRN_LED = (2,3) # gpio2.3 P8.8 28 | MDIR1A = (1,28) # gpio1.28 P9.12 29 | MDIR1A_BLUE = (2,0) # gpio2.0 pin T13 30 | MDIR1B = (0,31) # gpio0.31 P9.13 31 | MDIR2A = (1,16) # gpio1.16 P9.15 32 | MDIR2B = (2,17) # gpio2.17 P8.34 33 | MDIR2B_BLUE = (0,10) # gpio0.10 P8_31 34 | MDIR4A = (2,6) # gpio2.6 P8.45 35 | MDIR4B = (2,7) # gpio2.7 P8.46 36 | MDIR3B = (2,8) # gpio2.8 P8.43 37 | MDIR3A = (2,9) # gpio2.9 P8.44 38 | MOT_STBY = (0,20) # gpio0.20 P9.41 39 | DSM_PIN = (0,30) # gpio0.30 P9.11 40 | SERVO_PWR = (2,16) # gpio2.16 P8.36 41 | 42 | SPI1_SS1_GPIO_PIN = (3,17) # gpio3.17 P9.28 43 | SPI1_SS2_GPIO_PIN = (1,17) # gpio1.17 P9.23 44 | 45 | # BB Blue GPIO OUT 46 | BLUE_GP0_PIN_4 = (1,17) # gpio1.17 P9.23 47 | 48 | # Battery Indicator LEDs 49 | BATT_LED_1 = 27 # P8.17 50 | BATT_LED_2 = 65 # P8.18 51 | BATT_LED_2_BLUE = 11 # different on BB Blue 52 | BATT_LED_3 = 61 # P8.26 53 | BATT_LED_4 = 26 # P8.14 54 | 55 | DEBOUNCE_INTERVAL = 0.5 56 | 57 | import io, threading, time, os 58 | import select 59 | 60 | class InputTimeout(Exception): 61 | pass 62 | 63 | class GPIO: 64 | 65 | def __init__(self, chip, line): 66 | self.chip = gpiod.Chip('gpiochip{}'.format(chip)) 67 | self.line = self.chip.get_line(line) 68 | 69 | def request(self, type): 70 | self.line.request(consumer=str(id(self)), type=type) 71 | 72 | def release(self): 73 | self.line.release() 74 | 75 | class Output(GPIO): 76 | 77 | def __init__(self, chip, line): 78 | 79 | # call super 80 | super().__init__(chip, line) 81 | self.request(type=gpiod.LINE_REQ_DIR_OUT) 82 | 83 | def set(self, state): 84 | return self.line.set_value(state) 85 | 86 | class Input(GPIO): 87 | 88 | def __init__(self, chip, line): 89 | 90 | # call super 91 | super().__init__(chip, line) 92 | self.request(type=gpiod.LINE_REQ_DIR_IN) 93 | 94 | def read(self, timeout = None, pipe = None): 95 | 96 | # create pipe if necessary 97 | destroy_pipe = False 98 | if pipe is None: 99 | pipe = rcpy.create_pipe() 100 | destroy_pipe = True 101 | 102 | # request both edges and get file descriptor 103 | self.release() 104 | self.request(type=gpiod.LINE_REQ_EV_BOTH_EDGES) 105 | fdescriptor = self.line.event_get_fd() 106 | 107 | try: 108 | 109 | with os.fdopen(fdescriptor, 'rb', buffering = 0) as f: 110 | 111 | # create poller 112 | poller = select.poll() 113 | poller.register(f, 114 | select.POLLPRI | select.POLLIN | 115 | select.POLLHUP | select.POLLERR) 116 | 117 | # listen to state change as well 118 | state_r_fd, state_w_fd = pipe 119 | poller.register(state_r_fd, 120 | select.POLLIN | 121 | select.POLLHUP | select.POLLERR) 122 | 123 | while rcpy.get_state() != rcpy.EXITING: 124 | 125 | # wait for events 126 | if timeout: 127 | # can fail if timeout is given 128 | events = poller.poll(timeout) 129 | if len(events) == 0: 130 | raise InputTimeout('Input did not change in more than {} ms'.format(timeout)) 131 | 132 | else: 133 | # timeout = None, never fails 134 | events = poller.poll() 135 | 136 | # check flags 137 | for fd, flag in events: 138 | 139 | # state change 140 | if fd is state_r_fd: 141 | # get state 142 | state = int(os.read(state_r_fd, 1)) 143 | if state == rcpy.EXITING: 144 | # exit! 145 | return 146 | 147 | # input event 148 | if fd == f.fileno(): 149 | 150 | # read event 151 | event = self.line.event_read() 152 | 153 | # Handle inputs 154 | if flag & (select.POLLIN | select.POLLPRI): 155 | # release line 156 | self.release() 157 | self.request(type=gpiod.LINE_REQ_DIR_IN) 158 | # return read value 159 | return self.get() 160 | 161 | elif flag & (select.POLLHUP | select.POLLERR): 162 | # raise exception 163 | raise Exception('Could not read input {}'.format(self)) 164 | 165 | finally: 166 | 167 | # release line 168 | self.release() 169 | self.request(type=gpiod.LINE_REQ_DIR_IN) 170 | 171 | # destroy pipe 172 | if destroy_pipe: 173 | rcpy.destroy_pipe(pipe) 174 | 175 | def get(self): 176 | return self.line.get_value() 177 | 178 | def is_high(self): 179 | return self.get() == HIGH 180 | 181 | def is_low(self): 182 | return self.get() == LOW 183 | 184 | def high_or_low(self, debounce = 0, timeout = 0, pipe = None): 185 | 186 | # repeat until event is detected 187 | while rcpy.get_state() != rcpy.EXITING: 188 | 189 | # read event 190 | value = self.read(timeout, pipe) 191 | 192 | # debounce 193 | k = 0 194 | current_value = value 195 | while k < debounce and value == current_value: 196 | time.sleep(DEBOUNCE_INTERVAL/1000) 197 | current_value = self.get() 198 | k += 1 199 | 200 | # check value 201 | if value == current_value: 202 | 203 | # return value 204 | return value 205 | 206 | def high(self, debounce = 0, timeout = 0, pipe = None): 207 | event = self.high_or_low(debounce, timeout, pipe) 208 | if event == HIGH: 209 | return True 210 | else: 211 | return False 212 | 213 | def low(self, debounce = 0, timeout = 0, pipe = None): 214 | event = self.high_or_low(debounce, timeout, pipe) 215 | if event == LOW: 216 | return True 217 | else: 218 | return False 219 | 220 | class InputEvent(threading.Thread): 221 | 222 | LOW = 1 223 | HIGH = 2 224 | 225 | class InputEventInterrupt(Exception): 226 | pass 227 | 228 | def __init__(self, input, event, debounce = 0, timeout = None, 229 | target = None, vargs = (), kwargs = {}): 230 | 231 | super().__init__() 232 | 233 | self.input = input 234 | self.event = event 235 | self.target = target 236 | self.vargs = vargs 237 | self.kwargs = kwargs 238 | self.timeout = timeout 239 | self.debounce = 0 240 | self.pipe = rcpy.create_pipe() 241 | 242 | def action(self, event): 243 | if self.target: 244 | # call target 245 | self.target(self.input, event, *self.vargs, **self.kwargs) 246 | else: 247 | # just check for valid event 248 | if event != InputEvent.HIGH and event != InputEvent.LOW: 249 | raise Exception('Unkown InputEvent {}'.format(event)) 250 | 251 | def run(self): 252 | self.run = True 253 | while rcpy.get_state() != rcpy.EXITING and self.run: 254 | 255 | try: 256 | evnt = self.input.high_or_low(self.debounce, 257 | self.timeout, 258 | self.pipe) 259 | if evnt is not None: 260 | evnt = 1 << evnt 261 | if evnt & self.event: 262 | # fire callback 263 | self.action(evnt) 264 | 265 | except InputTimeout: 266 | self.run = False 267 | 268 | def stop(self): 269 | self.run = False 270 | # write to pipe to abort 271 | os.write(self.pipe[1], bytes(str(rcpy.EXITING), 'UTF-8')) 272 | # sleep and destroy pipe 273 | time.sleep(1) 274 | rcpy.destroy_pipe(self.pipe) 275 | self.pipe = None 276 | 277 | -------------------------------------------------------------------------------- /rcpy/led.py: -------------------------------------------------------------------------------- 1 | import rcpy 2 | from . import clock 3 | from . import gpio 4 | 5 | import threading, time 6 | 7 | ON = 1 8 | OFF = 0 9 | 10 | class Blink(clock.Clock): 11 | 12 | def __init__(self, led, period): 13 | 14 | # call super 15 | super().__init__(led, period) 16 | 17 | def stop(self): 18 | 19 | # call super 20 | super().stop() 21 | 22 | # turn off 23 | time.sleep(2*self.period) 24 | self.action.off() 25 | 26 | class LED(clock.Action): 27 | 28 | def __init__(self, chip, line, state = OFF): 29 | 30 | self.output = gpio.Output(chip, line) 31 | if state == ON: 32 | self.on() 33 | else: 34 | self.off() 35 | 36 | def is_on(self): 37 | return self.state == ON 38 | 39 | def is_off(self): 40 | return self.state == OFF 41 | 42 | def on(self): 43 | self.state = ON 44 | self.output.set(ON) 45 | 46 | def off(self): 47 | self.state = OFF 48 | self.output.set(OFF) 49 | 50 | def toggle(self): 51 | if self.state == ON: 52 | self.off() 53 | else: 54 | self.on() 55 | 56 | run = toggle 57 | 58 | def blink(self, period): 59 | thread = blink(self, period) 60 | thread.start() 61 | return thread 62 | 63 | 64 | # define leds 65 | red = LED(*gpio.RED_LED) 66 | green = LED(*gpio.GRN_LED) 67 | -------------------------------------------------------------------------------- /rcpy/motor.py: -------------------------------------------------------------------------------- 1 | import rcpy 2 | from rcpy._motor import * 3 | 4 | class Motor: 5 | 6 | def __init__(self, channel, duty = None): 7 | self.channel = channel 8 | if duty is not None: 9 | self.set(duty) 10 | 11 | def set(self, duty): 12 | set(self.channel, duty) 13 | 14 | def free_spin(self): 15 | set_free_spin(self.channel) 16 | 17 | def brake(self): 18 | set_brake(self.channel) 19 | 20 | # define leds 21 | motor1 = Motor(1) 22 | motor2 = Motor(2) 23 | motor3 = Motor(3) 24 | motor4 = Motor(4) 25 | -------------------------------------------------------------------------------- /rcpy/mpu9250.py: -------------------------------------------------------------------------------- 1 | import rcpy 2 | from rcpy._mpu9250 import * 3 | 4 | ACCEL_FSR_2G = 0 5 | ACCEL_FSR_4G = 1 6 | ACCEL_FSR_8G = 2 7 | ACCEL_FSR_16G = 3 8 | 9 | GYRO_FSR_250DPS = 0 10 | GYRO_FSR_500DPS = 1 11 | GYRO_FSR_1000DPS = 2 12 | GYRO_FSR_2000DPS = 3 13 | 14 | ACCEL_DLPF_OFF = 0 15 | ACCEL_DLPF_184 = 1 16 | ACCEL_DLPF_92 = 2 17 | ACCEL_DLPF_41 = 3 18 | ACCEL_DLPF_20 = 4 19 | ACCEL_DLPF_10 = 5 20 | ACCEL_DLPF_5 = 6 21 | 22 | GYRO_DLPF_OFF = 0 23 | GYRO_DLPF_184 = 1 24 | GYRO_DLPF_92 = 2 25 | GYRO_DLPF_41 = 3 26 | GYRO_DLPF_20 = 4 27 | GYRO_DLPF_10 = 5 28 | GYRO_DLPF_5 = 6 29 | 30 | ORIENTATION_Z_UP = 136, 31 | ORIENTATION_Z_DOWN = 396, 32 | ORIENTATION_X_UP = 14, 33 | ORIENTATION_X_DOWN = 266, 34 | ORIENTATION_Y_UP = 112, 35 | ORIENTATION_Y_DOWN = 336, 36 | ORIENTATION_X_FORWARD = 133, 37 | ORIENTATION_X_BACK = 161 38 | 39 | 40 | # Uses Alex Martelli's Borg for making MPU9250 a singleton 41 | 42 | class IMU(): 43 | 44 | _shared_state = {} 45 | 46 | def __init__(self, **kwargs): 47 | 48 | # Makes sure clock is a singleton 49 | self.__dict__ = self._shared_state 50 | 51 | # Do not initialize if already initialized 52 | if not self.__dict__ == {}: 53 | return self.set(**kwargs) 54 | 55 | # get defaults 56 | defaults = get() 57 | 58 | # accel_fsr 59 | self.accel_fsr = kwargs.pop('accel_fsr', defaults['accel_fsr']) 60 | 61 | # gyro_fsr 62 | self.gyro_fsr = kwargs.pop('gyro_fsr', defaults['gyro_fsr']) 63 | 64 | # accel_dlpf 65 | self.accel_dlpf = kwargs.pop('accel_dlpf', defaults['accel_dlpf']) 66 | 67 | # gyro_dlpf 68 | self.gyro_dlpf = kwargs.pop('gyro_dlpf', defaults['gyro_dlpf']) 69 | 70 | # enable_magnetometer 71 | self.enable_magnetometer = kwargs.pop('enable_magnetometer', 72 | defaults['enable_magnetometer']) 73 | 74 | # orientation 75 | self.orientation = kwargs.pop('orientation', defaults['orientation']) 76 | 77 | # compass_time_constant 78 | self.compass_time_constant = kwargs.pop('compass_time_constant', 79 | defaults['compass_time_constant']) 80 | 81 | # dmp_interrupt_priority 82 | self.dmp_interrupt_priority = kwargs.pop('dmp_interrupt_priority', 83 | defaults['dmp_interrupt_priority']) 84 | 85 | # dmp_sample_rate 86 | self.dmp_sample_rate = kwargs.pop('dmp_sample_rate', 87 | defaults['dmp_sample_rate']) 88 | 89 | # dmp_fetch_accel_gyro 90 | self.dmp_fetch_accel_gyro = kwargs.pop('dmp_fetch_accel_gyro', 91 | defaults['dmp_fetch_accel_gyro']) 92 | 93 | # show_warnings 94 | self.show_warnings = kwargs.pop('show_warnings', 95 | defaults['show_warnings']) 96 | 97 | # enable_dmp 98 | self.enable_dmp = kwargs.pop('enable_dmp', 99 | True) 100 | 101 | # enable_fusion 102 | self.enable_fusion = kwargs.pop('enable_fusion', 103 | defaults['enable_fusion']) 104 | 105 | # call mpu9250 initialize 106 | initialize(accel_fsr=self.accel_fsr, 107 | gyro_fsr=self.gyro_fsr, 108 | accel_dlpf=self.accel_dlpf, 109 | gyro_dlpf=self.gyro_dlpf, 110 | enable_magnetometer=self.enable_magnetometer, 111 | orientation=self.orientation, 112 | compass_time_constant=self.compass_time_constant, 113 | dmp_interrupt_priority=self.dmp_interrupt_priority, 114 | dmp_sample_rate=self.dmp_sample_rate, 115 | dmp_fetch_accel_gyro=self.dmp_fetch_accel_gyro, 116 | show_warnings=self.show_warnings, 117 | enable_dmp=self.enable_dmp, 118 | enable_fusion=self.enable_fusion) 119 | 120 | # initialize data 121 | self.data = {} 122 | 123 | def set(self, **kwargs): 124 | 125 | # accel_fsr 126 | if 'accel_fsr' in kwargs: 127 | self.accel_fsr = kwargs.pop('accel_fsr') 128 | 129 | # gyro_fsr 130 | if 'gyro_fsr' in kwargs: 131 | self.gyro_fsr = kwargs.pop('gyro_fsr') 132 | 133 | # accel_dlpf 134 | if 'accel_dlpf' in kwargs: 135 | self.accel_dlpf = kwargs.pop('accel_dlpf') 136 | 137 | # gyro_dlpf 138 | if 'gyro_dlpf' in kwargs: 139 | self.gyro_dlpf = kwargs.pop('gyro_dlpf') 140 | 141 | # enable_magnetometer 142 | if 'enable_magnetometer' in kwargs: 143 | self.enable_magnetometer = kwargs.pop('enable_magnetometer') 144 | 145 | # orientation 146 | if 'orientation' in kwargs: 147 | self.orientation = kwargs.pop('orientation') 148 | 149 | # compass_time_constant 150 | if 'compass_time_constant' in kwargs: 151 | self.compass_time_constant = kwargs.pop('compass_time_constant') 152 | 153 | # dmp_interrupt_priority 154 | if 'dmp_interrupt_priority' in kwargs: 155 | self.dmp_interrupt_priority = kwargs.pop('dmp_interrupt_priority') 156 | 157 | # dmp_sample_rate 158 | if 'dmp_sample_rate' in kwargs: 159 | self.dmp_sample_rate = kwargs.pop('dmp_sample_rate') 160 | 161 | # dmp_fetch_accel_gyro 162 | if 'dmp_fetch_accel_gyro' in kwargs: 163 | self.dmp_fetch_accel_gyro = kwargs.pop('dmp_fetch_accel_gyro') 164 | 165 | # show_warnings 166 | if 'show_warnings' in kwargs: 167 | self.show_warnings = kwargs.pop('show_warnings') 168 | 169 | # enable_dmp 170 | if 'enable_dmp' in kwargs: 171 | self.enable_dmp = kwargs.pop('enable_dmp') 172 | 173 | # enable_fusion 174 | if 'enable_fusion' in kwargs: 175 | self.enable_fusion = kwargs.pop('enable_fusion') 176 | 177 | # call mpu9250 initialize 178 | initialize(accel_fsr=self.accel_fsr, 179 | gyro_fsr=self.gyro_fsr, 180 | accel_dlpf=self.accel_dlpf, 181 | gyro_dlpf=self.gyro_dlpf, 182 | enable_magnetometer=self.enable_magnetometer, 183 | orientation=self.orientation, 184 | compass_time_constant=self.compass_time_constant, 185 | dmp_interrupt_priority=self.dmp_interrupt_priority, 186 | dmp_sample_rate=self.dmp_sample_rate, 187 | dmp_fetch_accel_gyro=self.dmp_fetch_accel_gyro, 188 | show_warnings=self.show_warnings, 189 | enable_dmp=self.enable_dmp, 190 | enable_fusion=self.enable_fusion) 191 | 192 | def read(self): 193 | 194 | # read mpu9250 195 | return read() 196 | -------------------------------------------------------------------------------- /rcpy/servo.py: -------------------------------------------------------------------------------- 1 | import rcpy 2 | from rcpy._servo import * 3 | import rcpy.clock as clock 4 | 5 | import threading, time 6 | 7 | class Servo(clock.Action): 8 | 9 | def __init__(self, channel, duty = 0): 10 | self.channel = channel 11 | self.duty = duty 12 | 13 | def set(self, duty): 14 | self.duty = duty 15 | 16 | def pulse(self, duty): 17 | self.duty = duty 18 | pulse(self.channel, self.duty) 19 | 20 | def run(self): 21 | pulse(self.channel, self.duty) 22 | 23 | def start(self, period): 24 | thread = clock.Clock(self, period) 25 | thread.start() 26 | return thread 27 | 28 | class ESC(Servo): 29 | 30 | def pulse(self, duty): 31 | self.duty = duty 32 | esc_pulse(self.channel, self.duty) 33 | 34 | def run(self): 35 | esc_pulse(self.channel, self.duty) 36 | 37 | # define servos 38 | servo1 = Servo(1) 39 | servo2 = Servo(2) 40 | servo3 = Servo(3) 41 | servo4 = Servo(4) 42 | servo5 = Servo(5) 43 | servo6 = Servo(6) 44 | servo7 = Servo(7) 45 | servo8 = Servo(8) 46 | 47 | # define escs 48 | esc1 = ESC(1) 49 | esc2 = ESC(2) 50 | esc3 = ESC(3) 51 | esc4 = ESC(4) 52 | esc5 = ESC(5) 53 | esc6 = ESC(6) 54 | esc7 = ESC(7) 55 | esc8 = ESC(8) 56 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.md 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, Extension, find_packages 2 | import platform 3 | 4 | LIBS = ['robotcontrol'] 5 | if platform.system().lower() == 'linux': 6 | LIBS.append('rt') 7 | 8 | _rcpy = Extension("rcpy._rcpy", 9 | sources=["src/_rcpy.c"], 10 | libraries=LIBS) 11 | 12 | _adc = Extension("rcpy._adc", 13 | sources=["src/_adc.c"], 14 | libraries=LIBS) 15 | 16 | _mpu9250 = Extension("rcpy._mpu9250", 17 | sources=["src/_mpu9250.c"], 18 | libraries=LIBS) 19 | 20 | _encoder = Extension("rcpy._encoder", 21 | sources=["src/_encoder.c"], 22 | libraries=LIBS) 23 | 24 | _motor = Extension("rcpy._motor", 25 | sources=["src/_motor.c"], 26 | libraries=LIBS) 27 | 28 | _servo = Extension("rcpy._servo", 29 | sources=["src/_servo.c"], 30 | libraries=LIBS) 31 | 32 | 33 | def readme(): 34 | with open('README.rst') as f: 35 | return f.read() 36 | 37 | 38 | setup( 39 | 40 | name="rcpy", 41 | version="0.5.1", 42 | packages=find_packages(), 43 | python_requires='>=3.4', 44 | 45 | # extensions 46 | ext_modules=[_rcpy, 47 | _adc, 48 | _mpu9250, 49 | _encoder, 50 | _motor, 51 | _servo], 52 | 53 | # metadata 54 | author="Mauricio C. de Oliveira", 55 | author_email="mauricio@ucsd.edu", 56 | 57 | description="Python Library for Robotics Cape on Beaglebone Black and Beaglebone Blue", 58 | long_description=readme(), 59 | license="MIT", 60 | 61 | keywords=["Robotics Cape", "Beaglebone Black", "Beaglebone Blue"], 62 | 63 | url="https://github.com/mcdeoliveira/rcpy", 64 | download_url="https://github.com/mcdeoliveira/rcpy/archive/0.5.tar.gz", 65 | 66 | classifiers=[ 67 | 'Development Status :: 3 - Alpha', 68 | 'Intended Audience :: Other Audience', 69 | 'Topic :: Scientific/Engineering', 70 | 'License :: OSI Approved :: MIT License', 71 | 'Programming Language :: Python :: 3.4', 72 | ], 73 | 74 | ) 75 | 76 | -------------------------------------------------------------------------------- /src/_adc.c: -------------------------------------------------------------------------------- 1 | /* Contributed by BrendanSimon */ 2 | 3 | #include 4 | 5 | #include 6 | 7 | static char module_docstring[] = 8 | "This module provides an interface for the ADC (Analog Digital Converter)."; 9 | 10 | static PyObject *adcError; 11 | 12 | // adc functions 13 | static PyObject *adc_get_raw(PyObject *self, PyObject *args); 14 | static PyObject *adc_get_voltage(PyObject *self, PyObject *args); 15 | static PyObject *adc_get_dc_jack_voltage(PyObject *self, PyObject *args); 16 | static PyObject *adc_get_battery_voltage(PyObject *self, PyObject *args); 17 | 18 | static PyMethodDef module_methods[] = { 19 | {"get_raw", 20 | (PyCFunction)adc_get_raw, 21 | METH_VARARGS, 22 | "get adc raw value"} 23 | , 24 | {"get_voltage", 25 | (PyCFunction)adc_get_voltage, 26 | METH_VARARGS, 27 | "get adc voltage"} 28 | , 29 | {"get_dc_jack_voltage", 30 | (PyCFunction)adc_get_dc_jack_voltage, 31 | METH_VARARGS, 32 | "get DC jack voltage"} 33 | , 34 | {"get_battery_voltage", 35 | (PyCFunction)adc_get_battery_voltage, 36 | METH_VARARGS, 37 | "get battery voltage"} 38 | , 39 | {NULL, NULL, 0, NULL} 40 | }; 41 | 42 | static struct PyModuleDef module = { 43 | PyModuleDef_HEAD_INIT, 44 | "_adc", /* name of module */ 45 | module_docstring, /* module documentation, may be NULL */ 46 | -1, /* size of per-interpreter state of the module, 47 | or -1 if the module keeps state in global variables. */ 48 | module_methods 49 | }; 50 | 51 | /* python functions */ 52 | 53 | PyMODINIT_FUNC PyInit__adc(void) 54 | { 55 | PyObject *m; 56 | 57 | /* create module */ 58 | m = PyModule_Create(&module); 59 | if (m == NULL) 60 | return NULL; 61 | 62 | /* create exception */ 63 | adcError = PyErr_NewException("adc.error", NULL, NULL); 64 | Py_INCREF(adcError); 65 | PyModule_AddObject(m, "error", adcError); 66 | 67 | /* initialize cape */ 68 | if(rc_adc_init() != 0){ 69 | return NULL; 70 | } 71 | 72 | return m; 73 | } 74 | 75 | static 76 | PyObject *adc_get_raw(PyObject *self, 77 | PyObject *args) 78 | { 79 | /* parse arguments */ 80 | unsigned channel; 81 | if (!PyArg_ParseTuple(args, "I", &channel)) { 82 | PyErr_SetString(adcError, "Invalid arguments"); 83 | return NULL; 84 | } 85 | 86 | /* get adc value */ 87 | int value; 88 | if ((value = rc_adc_read_raw((int)channel)) < 0) { 89 | PyErr_SetString(adcError, "Failed to get adc raw value"); 90 | return NULL; 91 | } 92 | 93 | /* Build the output tuple */ 94 | PyObject *ret = Py_BuildValue("i", value); 95 | 96 | return ret; 97 | } 98 | 99 | static 100 | PyObject *adc_get_voltage(PyObject *self, 101 | PyObject *args) 102 | { 103 | /* parse arguments */ 104 | unsigned channel; 105 | if (!PyArg_ParseTuple(args, "I", &channel)) { 106 | PyErr_SetString(adcError, "Invalid arguments"); 107 | return NULL; 108 | } 109 | 110 | /* get adc voltage */ 111 | float voltage; 112 | if ((voltage = rc_adc_read_volt((int)channel)) < 0) { 113 | PyErr_SetString(adcError, "Failed to get adc voltage"); 114 | return NULL; 115 | } 116 | 117 | /* Build the output tuple */ 118 | PyObject *ret = Py_BuildValue("f", voltage); 119 | 120 | return ret; 121 | } 122 | 123 | static 124 | PyObject *adc_get_dc_jack_voltage(PyObject *self, 125 | PyObject *args) 126 | { 127 | /* parse arguments */ 128 | 129 | /* get dc jack voltage */ 130 | float voltage; 131 | if ((voltage = rc_adc_dc_jack()) < 0) { 132 | PyErr_SetString(adcError, "Failed to get DC jack voltage"); 133 | return NULL; 134 | } 135 | 136 | /* Build the output tuple */ 137 | PyObject *ret = Py_BuildValue("f", voltage); 138 | 139 | return ret; 140 | } 141 | 142 | static 143 | PyObject *adc_get_battery_voltage(PyObject *self, 144 | PyObject *args) 145 | { 146 | /* parse arguments */ 147 | 148 | /* get dc jack voltage */ 149 | float voltage; 150 | if ((voltage = rc_adc_batt()) < 0) { 151 | PyErr_SetString(adcError, "Failed to get battery voltage"); 152 | return NULL; 153 | } 154 | 155 | /* Build the output tuple */ 156 | PyObject *ret = Py_BuildValue("f", voltage); 157 | 158 | return ret; 159 | } 160 | 161 | -------------------------------------------------------------------------------- /src/_encoder.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | static char module_docstring[] = 6 | "This module provides an interface for quadrature encoders."; 7 | 8 | static PyObject *encoderError; 9 | 10 | // one-shot sampling mode functions 11 | static PyObject *encoder_read(PyObject *self, PyObject *args); 12 | static PyObject *encoder_set(PyObject *self, PyObject *args); 13 | 14 | static PyMethodDef module_methods[] = { 15 | {"get", 16 | (PyCFunction)encoder_read, 17 | METH_VARARGS, 18 | "read encoder"} 19 | , 20 | {"set", 21 | (PyCFunction)encoder_set, 22 | METH_VARARGS, 23 | "set encoder"} 24 | , 25 | {NULL, NULL, 0, NULL} 26 | }; 27 | 28 | static struct PyModuleDef module = { 29 | PyModuleDef_HEAD_INIT, 30 | "_encoder", /* name of module */ 31 | module_docstring, /* module documentation, may be NULL */ 32 | -1, /* size of per-interpreter state of the module, 33 | or -1 if the module keeps state in global variables. */ 34 | module_methods 35 | }; 36 | 37 | /* python functions */ 38 | 39 | PyMODINIT_FUNC PyInit__encoder(void) 40 | { 41 | PyObject *m; 42 | 43 | /* create module */ 44 | m = PyModule_Create(&module); 45 | if (m == NULL) 46 | return NULL; 47 | 48 | /* create exception */ 49 | encoderError = PyErr_NewException("encoder.error", NULL, NULL); 50 | Py_INCREF(encoderError); 51 | PyModule_AddObject(m, "error", encoderError); 52 | 53 | /* initialize encoders */ 54 | /* remember to call rc_encoder_eqep_cleanup() later */ 55 | if(rc_encoder_init()) 56 | return NULL; 57 | 58 | // remember to call rc_encoder_cleanup() later 59 | 60 | return m; 61 | } 62 | 63 | static 64 | PyObject *encoder_read(PyObject *self, 65 | PyObject *args) 66 | { 67 | 68 | /* parse arguments */ 69 | int channel; 70 | if (!PyArg_ParseTuple(args, "i", &channel)) { 71 | PyErr_SetString(encoderError, "Invalid arguments"); 72 | return NULL; 73 | } 74 | 75 | int count; 76 | 77 | /* read encoder */ 78 | count=rc_encoder_read(channel); 79 | 80 | /* Build the output tuple */ 81 | PyObject *ret = Py_BuildValue("i", count); 82 | 83 | return ret; 84 | } 85 | 86 | static 87 | PyObject *encoder_set(PyObject *self, 88 | PyObject *args) 89 | { 90 | 91 | /* parse arguments */ 92 | int channel, count = 0; 93 | if (!PyArg_ParseTuple(args, "i|i", &channel, &count)) { 94 | PyErr_SetString(encoderError, "Invalid arguments"); 95 | return NULL; 96 | } 97 | 98 | /* set encoder */ 99 | rc_encoder_write(channel,count); 100 | 101 | /* Build the output tuple */ 102 | PyObject *ret = Py_BuildValue(""); 103 | 104 | return ret; 105 | } 106 | -------------------------------------------------------------------------------- /src/_motor.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | static char module_docstring[] = 6 | "This module provides an interface for motor."; 7 | 8 | static PyObject *motorError; 9 | 10 | // one-shot sampling mode functions 11 | static PyObject *motor_enable(PyObject *self); 12 | static PyObject *motor_disable(PyObject *self); 13 | static PyObject *motor_set(PyObject *self, PyObject *args); 14 | static PyObject *motor_set_free_spin(PyObject *self, PyObject *args); 15 | static PyObject *motor_set_brake(PyObject *self, PyObject *args); 16 | 17 | static PyMethodDef module_methods[] = { 18 | {"enable", 19 | (PyCFunction)motor_enable, 20 | METH_NOARGS, 21 | "enables motors"} 22 | , 23 | {"disable", 24 | (PyCFunction)motor_disable, 25 | METH_NOARGS, 26 | "disable motors"} 27 | , 28 | {"set", 29 | (PyCFunction)motor_set, 30 | METH_VARARGS, 31 | "set motor"} 32 | , 33 | {"set_free_spin", 34 | (PyCFunction)motor_set_free_spin, 35 | METH_VARARGS, 36 | "set motor to spin freely"} 37 | , 38 | {"set_brake", 39 | (PyCFunction)motor_set_brake, 40 | METH_VARARGS, 41 | "set motor to break"} 42 | , 43 | {NULL, NULL, 0, NULL} 44 | }; 45 | 46 | static struct PyModuleDef module = { 47 | PyModuleDef_HEAD_INIT, 48 | "_motor", /* name of module */ 49 | module_docstring, /* module documentation, may be NULL */ 50 | -1, /* size of per-interpreter state of the module, 51 | or -1 if the module keeps state in global variables. */ 52 | module_methods 53 | }; 54 | 55 | /* python functions */ 56 | 57 | PyMODINIT_FUNC PyInit__motor(void) 58 | { 59 | PyObject *m; 60 | 61 | /* create module */ 62 | m = PyModule_Create(&module); 63 | if (m == NULL) 64 | return NULL; 65 | 66 | /* create exception */ 67 | motorError = PyErr_NewException("motor.error", NULL, NULL); 68 | Py_INCREF(motorError); 69 | PyModule_AddObject(m, "error", motorError); 70 | 71 | /* initialize cape */ 72 | if(rc_motor_init()!=0){ 73 | return NULL; 74 | } 75 | 76 | return m; 77 | } 78 | 79 | static 80 | PyObject *motor_enable(PyObject *self) 81 | { 82 | 83 | /* enable motor */ 84 | if (rc_motor_standby(0)<0) { 85 | PyErr_SetString(motorError, "Failed to enable motors"); 86 | return NULL; 87 | } 88 | 89 | /* Build the output tuple */ 90 | PyObject *ret = Py_BuildValue(""); 91 | 92 | return ret; 93 | } 94 | 95 | static 96 | PyObject *motor_disable(PyObject *self) 97 | { 98 | 99 | /* enable motor */ 100 | if (rc_motor_standby(1)<0) { 101 | PyErr_SetString(motorError, "Failed to disable motors"); 102 | return NULL; 103 | } 104 | 105 | /* Build the output tuple */ 106 | PyObject *ret = Py_BuildValue(""); 107 | 108 | return ret; 109 | } 110 | 111 | static 112 | PyObject *motor_set(PyObject *self, 113 | PyObject *args) 114 | { 115 | 116 | /* parse arguments */ 117 | int motor; 118 | float duty; 119 | if (!PyArg_ParseTuple(args, "if", &motor, &duty)) { 120 | PyErr_SetString(motorError, "Invalid arguments"); 121 | return NULL; 122 | } 123 | 124 | /* set motor */ 125 | if (rc_motor_set(motor, duty)<0) { 126 | PyErr_SetString(motorError, "Failed to set motor"); 127 | return NULL; 128 | } 129 | 130 | /* Build the output tuple */ 131 | PyObject *ret = Py_BuildValue(""); 132 | 133 | return ret; 134 | } 135 | 136 | static 137 | PyObject *motor_set_free_spin(PyObject *self, 138 | PyObject *args) 139 | { 140 | 141 | /* parse arguments */ 142 | int motor; 143 | if (!PyArg_ParseTuple(args, "i", &motor)) { 144 | PyErr_SetString(motorError, "Invalid arguments"); 145 | return NULL; 146 | } 147 | 148 | /* set motor */ 149 | if (rc_motor_free_spin(motor)<0) { 150 | PyErr_SetString(motorError, "Failed to set motor free spin"); 151 | return NULL; 152 | } 153 | 154 | /* Build the output tuple */ 155 | PyObject *ret = Py_BuildValue(""); 156 | 157 | return ret; 158 | } 159 | 160 | static 161 | PyObject *motor_set_brake(PyObject *self, 162 | PyObject *args) 163 | { 164 | 165 | /* parse arguments */ 166 | int motor; 167 | if (!PyArg_ParseTuple(args, "i", &motor)) { 168 | PyErr_SetString(motorError, "Invalid arguments"); 169 | return NULL; 170 | } 171 | 172 | /* set motor */ 173 | if (rc_motor_brake(motor)<0) { 174 | PyErr_SetString(motorError, "Failed to brake motor"); 175 | return NULL; 176 | } 177 | 178 | /* Build the output tuple */ 179 | PyObject *ret = Py_BuildValue(""); 180 | 181 | return ret; 182 | } 183 | -------------------------------------------------------------------------------- /src/_mpu9250.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | static char module_docstring[] = 6 | "This module provides an interface for mpu9250."; 7 | 8 | static PyObject *mpu9250Error; 9 | 10 | // initialization 11 | static PyObject *mpu9250_initialize(PyObject *self, 12 | PyObject *args, 13 | PyObject *kwargs); 14 | static PyObject *mpu9250_power_off(PyObject *self); 15 | static PyObject *mpu9250_get(PyObject *self); 16 | 17 | // one-shot sampling mode functions 18 | static PyObject *mpu9250_read_accel_data(PyObject *self); 19 | static PyObject *mpu9250_read_gyro_data(PyObject *self); 20 | static PyObject *mpu9250_read_mag_data(PyObject *self); 21 | static PyObject *mpu9250_read_imu_temp(PyObject *self); 22 | 23 | // read dmp 24 | static PyObject *mpu9250_read(PyObject *self); 25 | 26 | /* // other */ 27 | /* static PyObject *mpu9250_calibrate_gyro_routine(PyObject *self, PyObject *args); */ 28 | /* static PyObject *mpu9250_calibrate_mag_routine(PyObject *self, PyObject *args); */ 29 | /* static PyObject *mpu9250_is_gyro_calibrated(PyObject *self, PyObject *args); */ 30 | /* static PyObject *mpu9250_is_mag_calibrated(PyObject *self, PyObject *args); */ 31 | 32 | static PyMethodDef module_methods[] = { 33 | {"initialize", 34 | (PyCFunction)mpu9250_initialize, 35 | METH_VARARGS | METH_KEYWORDS, 36 | "initialize imu"} 37 | , 38 | {"power_off", 39 | (PyCFunction)mpu9250_power_off, 40 | METH_NOARGS, 41 | "power off imu"} 42 | , 43 | {"get", 44 | (PyCFunction)mpu9250_get, 45 | METH_NOARGS, 46 | "get imu conf"} 47 | , 48 | {"read_accel_data", 49 | (PyCFunction)mpu9250_read_accel_data, 50 | METH_NOARGS, 51 | "read accelerometer"} 52 | , 53 | {"read_gyro_data", 54 | (PyCFunction)mpu9250_read_gyro_data, 55 | METH_NOARGS, 56 | "read gyroscope"} 57 | , 58 | {"read_mag_data", 59 | (PyCFunction)mpu9250_read_mag_data, 60 | METH_NOARGS, 61 | "read magnetometer"} 62 | , 63 | {"read_imu_temp", 64 | (PyCFunction)mpu9250_read_imu_temp, 65 | METH_NOARGS, 66 | "read temperature"} 67 | , 68 | {"read", 69 | (PyCFunction)mpu9250_read, 70 | METH_NOARGS, 71 | "read imu"} 72 | , 73 | {NULL, NULL, 0, NULL} 74 | }; 75 | 76 | static struct PyModuleDef module = { 77 | PyModuleDef_HEAD_INIT, 78 | "_mpu9250", /* name of module */ 79 | module_docstring, /* module documentation, may be NULL */ 80 | -1, /* size of per-interpreter state of the module, 81 | or -1 if the module keeps state in global variables. */ 82 | module_methods 83 | }; 84 | 85 | /* auxiliary function */ 86 | 87 | static int imu_enable_dmp = 0; // enable dmp 88 | static int imu_enable_fusion = 0; // enable fusion 89 | static rc_mpu_config_t imu_conf; // imu configuration 90 | static rc_mpu_data_t imu_data; // imu data 91 | 92 | /* python functions */ 93 | 94 | PyMODINIT_FUNC PyInit__mpu9250(void) 95 | { 96 | PyObject *m; 97 | 98 | /* create module */ 99 | m = PyModule_Create(&module); 100 | if (m == NULL) 101 | return NULL; 102 | 103 | /* create exception */ 104 | mpu9250Error = PyErr_NewException("mpu9250.error", NULL, NULL); 105 | Py_INCREF(mpu9250Error); 106 | PyModule_AddObject(m, "error", mpu9250Error); 107 | 108 | /* set default parameters for imu */ 109 | imu_conf = rc_mpu_default_config(); 110 | 111 | /* override dmp_fetch_accel_gryo */ 112 | imu_conf.dmp_fetch_accel_gyro = 1; 113 | 114 | return m; 115 | } 116 | 117 | 118 | static 119 | PyObject *mpu9250_initialize(PyObject *self, 120 | PyObject *args, 121 | PyObject *kwargs) 122 | { 123 | 124 | static char *kwlist[] = { 125 | "accel_fsr", /* rc_accel_fsr_t (int) */ 126 | "gyro_fsr", /* rc_gyro_fsr_t (int) */ 127 | "accel_dlpf", /* rc_accel_dlpf_t (int) */ 128 | "gyro_dlpf", /* rc_gyro_dlpf_t (int) */ 129 | "enable_magnetometer", /* int */ 130 | "orientation", /* rc_imu_orientation_t (int) */ 131 | "compass_time_constant", /* float */ 132 | "dmp_interrupt_priority", /* int */ 133 | "dmp_sample_rate", /* int */ 134 | "dmp_fetch_accel_gyro", /* int */ 135 | "show_warnings", /* int */ 136 | "enable_dmp", /* int */ 137 | "enable_fusion", /* int */ 138 | NULL 139 | }; 140 | 141 | PyObject *ret; 142 | 143 | /* Parse parameters */ 144 | if (! PyArg_ParseTupleAndKeywords(args, kwargs, "|iiiiiifiiiiii", kwlist, 145 | &imu_conf.accel_fsr, /* rc_accel_fsr_t (int) */ 146 | &imu_conf.gyro_fsr, /* rc_gyro_fsr_t (int) */ 147 | &imu_conf.accel_dlpf, /* rc_accel_dlpf_t (int) */ 148 | &imu_conf.gyro_dlpf, /* rc_gyro_dlpf_t (int) */ 149 | &imu_conf.enable_magnetometer, /* int */ 150 | &imu_conf.orient, /* rc_imu_orientation_t (int) */ 151 | &imu_conf.compass_time_constant, /* float */ 152 | &imu_conf.dmp_interrupt_priority, /* int */ 153 | &imu_conf.dmp_sample_rate, /* int */ 154 | &imu_conf.dmp_fetch_accel_gyro, /* int */ 155 | &imu_conf.show_warnings, /* int */ 156 | &imu_enable_dmp, /* int */ 157 | &imu_enable_fusion /* int */ )) { 158 | PyErr_SetString(mpu9250Error, "Failed to initialize IMU"); 159 | return NULL; 160 | } 161 | 162 | /* Initialize imu */ 163 | 164 | // To dmp or not to dmp? 165 | if (imu_enable_dmp) { 166 | 167 | // initialize imu + dmp 168 | if(rc_mpu_initialize_dmp(&imu_data, imu_conf)){ 169 | PyErr_SetString(mpu9250Error, "Failed to initialize IMU"); 170 | return NULL; 171 | } 172 | 173 | } 174 | else { 175 | if(rc_mpu_initialize(&imu_data, imu_conf)){ 176 | PyErr_SetString(mpu9250Error, "Failed to initialize IMU"); 177 | return NULL; 178 | } 179 | } 180 | 181 | /* Build the output tuple */ 182 | ret = Py_BuildValue(""); 183 | 184 | return ret; 185 | } 186 | 187 | static 188 | PyObject *mpu9250_power_off(PyObject *self) 189 | { 190 | 191 | /* power off imu */ 192 | if (rc_mpu_power_off()) { 193 | PyErr_SetString(mpu9250Error, "Failed to power off IMU"); 194 | return NULL; 195 | } 196 | 197 | /* Build the output tuple */ 198 | PyObject *ret = 199 | Py_BuildValue(""); 200 | 201 | return ret; 202 | } 203 | 204 | static 205 | PyObject *mpu9250_get(PyObject *self) 206 | { 207 | 208 | /* Build the output tuple */ 209 | PyObject *ret = 210 | Py_BuildValue("{sisisisisisfsisisOsOsOsOsO}", 211 | "accel_fsr", 212 | imu_conf.accel_fsr, 213 | "gyro_fsr", 214 | imu_conf.gyro_fsr, 215 | "accel_dlpf", 216 | imu_conf.accel_dlpf, 217 | "gyro_dlpf", 218 | imu_conf.gyro_dlpf, 219 | "orientation", 220 | imu_conf.orient, 221 | "compass_time_constant", 222 | imu_conf.compass_time_constant, 223 | "dmp_interrupt_priority", 224 | imu_conf.dmp_interrupt_priority, 225 | "dmp_sample_rate", 226 | imu_conf.dmp_sample_rate, 227 | "dmp_fetch_accel_gyro", 228 | imu_conf.dmp_fetch_accel_gyro ? Py_True : Py_False, 229 | "enable_magnetometer", 230 | imu_conf.enable_magnetometer ? Py_True : Py_False, 231 | "show_warnings", 232 | imu_conf.show_warnings ? Py_True : Py_False, 233 | "enable_dmp", 234 | imu_enable_dmp ? Py_True : Py_False, 235 | "enable_fusion", 236 | imu_enable_fusion ? Py_True : Py_False); 237 | 238 | return ret; 239 | } 240 | 241 | static 242 | PyObject *mpu9250_read_accel_data(PyObject *self) 243 | { 244 | 245 | /* read data */ 246 | if (rc_mpu_read_accel(&imu_data)<0) { 247 | PyErr_SetString(mpu9250Error, "Failed to read IMU"); 248 | return NULL; 249 | } 250 | 251 | /* Build the output tuple */ 252 | PyObject *ret = 253 | Py_BuildValue("(fff)", 254 | imu_data.accel[0], 255 | imu_data.accel[1], 256 | imu_data.accel[2]); 257 | 258 | return ret; 259 | } 260 | 261 | static 262 | PyObject *mpu9250_read_gyro_data(PyObject *self) 263 | { 264 | 265 | /* read data */ 266 | if (rc_mpu_read_gyro(&imu_data)<0) { 267 | PyErr_SetString(mpu9250Error, "Failed to read IMU"); 268 | return NULL; 269 | } 270 | 271 | /* Build the output tuple */ 272 | PyObject *ret = 273 | Py_BuildValue("(fff)", 274 | imu_data.gyro[0], 275 | imu_data.gyro[1], 276 | imu_data.gyro[2]); 277 | 278 | return ret; 279 | } 280 | 281 | static 282 | PyObject *mpu9250_read_mag_data(PyObject *self) 283 | { 284 | 285 | /* enabled? */ 286 | if (!imu_conf.enable_magnetometer) { 287 | PyErr_SetString(mpu9250Error, "Magnetometer is disabled"); 288 | return NULL; 289 | } 290 | 291 | /* read data */ 292 | if (rc_mpu_read_mag(&imu_data)<0) { 293 | PyErr_SetString(mpu9250Error, "Failed to read magnetometer data"); 294 | return NULL; 295 | } 296 | 297 | /* Build the output tuple */ 298 | PyObject *ret = 299 | Py_BuildValue("(fff)", 300 | imu_data.mag[0], 301 | imu_data.mag[1], 302 | imu_data.mag[2]); 303 | 304 | return ret; 305 | } 306 | 307 | static 308 | PyObject *mpu9250_read_imu_temp(PyObject *self) 309 | { 310 | 311 | /* read data */ 312 | if (rc_mpu_read_temp(&imu_data)<0) { 313 | PyErr_SetString(mpu9250Error, "Failed to read IMU"); 314 | return NULL; 315 | } 316 | 317 | /* Build the output tuple */ 318 | PyObject *ret = 319 | Py_BuildValue("f", imu_data.temp); 320 | 321 | return ret; 322 | } 323 | 324 | static 325 | PyObject *mpu9250_read(PyObject *self) 326 | { 327 | 328 | /* read data */ 329 | if (imu_enable_dmp) { 330 | 331 | /* from dmp */ 332 | 333 | // waits until a measurement is available 334 | if (rc_mpu_block_until_dmp_data()!=0) { 335 | PyErr_SetString(mpu9250Error, "Failed to read IMU"); 336 | return NULL; 337 | } 338 | 339 | } else { 340 | 341 | /* or from registers */ 342 | 343 | if (rc_mpu_read_accel(&imu_data)<0) { 344 | PyErr_SetString(mpu9250Error, "Failed to read IMU"); 345 | return NULL; 346 | } 347 | 348 | if (rc_mpu_read_gyro(&imu_data)<0) { 349 | PyErr_SetString(mpu9250Error, "Failed to read IMU"); 350 | return NULL; 351 | } 352 | 353 | if (imu_conf.enable_magnetometer) { 354 | if (rc_mpu_read_mag(&imu_data)<0) { 355 | PyErr_SetString(mpu9250Error, "Failed to read magnetometer data"); 356 | return NULL; 357 | } 358 | } 359 | 360 | if (rc_mpu_read_temp(&imu_data)<0) { 361 | PyErr_SetString(mpu9250Error, "Failed to read IMU"); 362 | return NULL; 363 | } 364 | 365 | } 366 | 367 | /* Build the output tuple */ 368 | PyObject *ret; 369 | 370 | if (imu_conf.enable_magnetometer) { 371 | 372 | if (imu_enable_fusion) { 373 | 374 | ret = Py_BuildValue("{s(fff)s(fff)s(fff)s(ffff)s(fff)sf}", 375 | "accel", 376 | imu_data.accel[0], 377 | imu_data.accel[1], 378 | imu_data.accel[2], 379 | "gyro", 380 | imu_data.gyro[0], 381 | imu_data.gyro[1], 382 | imu_data.gyro[2], 383 | "mag", 384 | imu_data.mag[0], 385 | imu_data.mag[1], 386 | imu_data.mag[2], 387 | "quat", 388 | imu_data.fused_quat[0], 389 | imu_data.fused_quat[1], 390 | imu_data.fused_quat[2], 391 | imu_data.fused_quat[3], 392 | "tb", 393 | imu_data.fused_TaitBryan[TB_PITCH_X], 394 | imu_data.fused_TaitBryan[TB_ROLL_Y], 395 | imu_data.fused_TaitBryan[TB_YAW_Z], 396 | "head", 397 | imu_data.compass_heading); 398 | 399 | } else { 400 | 401 | ret = Py_BuildValue("{s(fff)s(fff)s(fff)s(ffff)s(fff)sf}", 402 | "accel", 403 | imu_data.accel[0], 404 | imu_data.accel[1], 405 | imu_data.accel[2], 406 | "gyro", 407 | imu_data.gyro[0], 408 | imu_data.gyro[1], 409 | imu_data.gyro[2], 410 | "mag", 411 | imu_data.mag[0], 412 | imu_data.mag[1], 413 | imu_data.mag[2], 414 | "quat", 415 | imu_data.dmp_quat[0], 416 | imu_data.dmp_quat[1], 417 | imu_data.dmp_quat[2], 418 | imu_data.dmp_quat[3], 419 | "tb", 420 | imu_data.dmp_TaitBryan[TB_PITCH_X], 421 | imu_data.dmp_TaitBryan[TB_ROLL_Y], 422 | imu_data.dmp_TaitBryan[TB_YAW_Z], 423 | "head", 424 | imu_data.compass_heading_raw); 425 | 426 | } 427 | 428 | } else { 429 | 430 | ret = Py_BuildValue("{s(fff)s(fff)s(ffff)s(fff)}", 431 | "accel", 432 | imu_data.accel[0], 433 | imu_data.accel[1], 434 | imu_data.accel[2], 435 | "gyro", 436 | imu_data.gyro[0], 437 | imu_data.gyro[1], 438 | imu_data.gyro[2], 439 | "quat", 440 | imu_data.dmp_quat[0], 441 | imu_data.dmp_quat[1], 442 | imu_data.dmp_quat[2], 443 | imu_data.dmp_quat[3], 444 | "tb", 445 | imu_data.dmp_TaitBryan[TB_PITCH_X], 446 | imu_data.dmp_TaitBryan[TB_ROLL_Y], 447 | imu_data.dmp_TaitBryan[TB_YAW_Z]); 448 | 449 | } 450 | 451 | return ret; 452 | } 453 | -------------------------------------------------------------------------------- /src/_rcpy.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | /* initialization */ 6 | static PyObject *rcpyError; 7 | static PyObject *rcpy_get_state(PyObject *self); 8 | static PyObject *rcpy_set_state(PyObject *self, PyObject *args); 9 | 10 | static PyMethodDef module_methods[] = { 11 | {"get_state", 12 | (PyCFunction)rcpy_get_state, 13 | METH_NOARGS, 14 | "Get robotics cape state"} 15 | , 16 | {"set_state", 17 | (PyCFunction)rcpy_set_state, 18 | METH_VARARGS, 19 | "Set robotics cape state"} 20 | , 21 | {NULL, NULL, 0, NULL} 22 | }; 23 | 24 | static struct PyModuleDef module = { 25 | PyModuleDef_HEAD_INIT, 26 | "_rcpy", 27 | "This module provides an interface to the robotics cape", 28 | -1, 29 | module_methods 30 | }; 31 | 32 | /* python functions */ 33 | 34 | PyMODINIT_FUNC PyInit__rcpy(void) 35 | { 36 | PyObject *m; 37 | 38 | /* create module */ 39 | m = PyModule_Create(&module); 40 | if (m == NULL) 41 | return NULL; 42 | 43 | /* create exception */ 44 | rcpyError = PyErr_NewException("rcpy.error", NULL, NULL); 45 | Py_INCREF(rcpyError); 46 | PyModule_AddObject(m, "error", rcpyError); 47 | 48 | return m; 49 | } 50 | 51 | static 52 | PyObject *rcpy_get_state(PyObject *self) 53 | { 54 | /* return cape state */ 55 | return Py_BuildValue("i", rc_get_state()); 56 | } 57 | 58 | static 59 | PyObject *rcpy_set_state(PyObject *self, PyObject *args) 60 | { 61 | 62 | /* parse arguments */ 63 | int state; 64 | if (!PyArg_ParseTuple(args, "i", &state)) { 65 | PyErr_SetString(rcpyError, "Invalid argument"); 66 | return NULL; 67 | } 68 | 69 | /* set up cape state */ 70 | rc_set_state(state); 71 | 72 | /* return None */ 73 | return Py_BuildValue(""); 74 | } 75 | -------------------------------------------------------------------------------- /src/_servo.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | static char module_docstring[] = 6 | "This module provides an interface for servo."; 7 | 8 | static PyObject *servoError; 9 | 10 | // one-shot sampling mode functions 11 | static PyObject *servo_enable(PyObject *self); 12 | static PyObject *servo_disable(PyObject *self); 13 | 14 | static PyObject *servo_pulse_us(PyObject *self, PyObject *args); 15 | static PyObject *servo_pulse_us_all(PyObject *self, PyObject *args); 16 | 17 | static PyObject *servo_pulse(PyObject *self, PyObject *args); 18 | static PyObject *servo_pulse_all(PyObject *self, PyObject *args); 19 | 20 | static PyObject *servo_esc_pulse(PyObject *self, PyObject *args); 21 | static PyObject *servo_esc_pulse_all(PyObject *self, PyObject *args); 22 | 23 | static PyObject *servo_oneshot_pulse(PyObject *self, PyObject *args); 24 | static PyObject *servo_oneshot_pulse_all(PyObject *self, PyObject *args); 25 | 26 | static PyMethodDef module_methods[] = { 27 | {"enable", 28 | (PyCFunction)servo_enable, 29 | METH_NOARGS, 30 | "enables servos"} 31 | , 32 | {"disable", 33 | (PyCFunction)servo_disable, 34 | METH_NOARGS, 35 | "disable servos"} 36 | , 37 | {"pulse_us", 38 | (PyCFunction)servo_pulse_us, 39 | METH_VARARGS, 40 | "send servo pulse in us"} 41 | , 42 | {"pulse_us_all", 43 | (PyCFunction)servo_pulse_us_all, 44 | METH_VARARGS, 45 | "send pulse in us to all servos"} 46 | , 47 | {"pulse", 48 | (PyCFunction)servo_pulse, 49 | METH_VARARGS, 50 | "send servo normalized pulse"} 51 | , 52 | {"pulse_all", 53 | (PyCFunction)servo_pulse_all, 54 | METH_VARARGS, 55 | "send normalized pulse to all servos"} 56 | , 57 | {"esc_pulse", 58 | (PyCFunction)servo_esc_pulse, 59 | METH_VARARGS, 60 | "send servo normalized pulse"} 61 | , 62 | {"esc_pulse_all", 63 | (PyCFunction)servo_esc_pulse_all, 64 | METH_VARARGS, 65 | "send normalized pulse to all servos"} 66 | , 67 | {"oneshot_pulse", 68 | (PyCFunction)servo_oneshot_pulse, 69 | METH_VARARGS, 70 | "send servo normalized pulse"} 71 | , 72 | {"oneshot_pulse_all", 73 | (PyCFunction)servo_oneshot_pulse_all, 74 | METH_VARARGS, 75 | "send normalized pulse to all servos"} 76 | , 77 | {NULL, NULL, 0, NULL} 78 | }; 79 | 80 | static struct PyModuleDef module = { 81 | PyModuleDef_HEAD_INIT, 82 | "_servo", /* name of module */ 83 | module_docstring, /* module documentation, may be NULL */ 84 | -1, /* size of per-interpreter state of the module, 85 | or -1 if the module keeps state in global variables. */ 86 | module_methods 87 | }; 88 | 89 | /* python functions */ 90 | 91 | PyMODINIT_FUNC PyInit__servo(void) 92 | { 93 | PyObject *m; 94 | 95 | /* create module */ 96 | m = PyModule_Create(&module); 97 | if (m == NULL) 98 | return NULL; 99 | 100 | /* create exception */ 101 | servoError = PyErr_NewException("servo.error", NULL, NULL); 102 | Py_INCREF(servoError); 103 | PyModule_AddObject(m, "error", servoError); 104 | 105 | /* initialize cape */ 106 | if (rc_servo_init() != 0){ 107 | return NULL; 108 | } 109 | 110 | return m; 111 | } 112 | 113 | static 114 | PyObject *servo_enable(PyObject *self) 115 | { 116 | 117 | /* enable servo */ 118 | if (rc_servo_power_rail_en(1)<0) { 119 | PyErr_SetString(servoError, "Failed to enable servos"); 120 | return NULL; 121 | } 122 | 123 | /* Build the output tuple */ 124 | PyObject *ret = Py_BuildValue(""); 125 | 126 | return ret; 127 | } 128 | 129 | static 130 | PyObject *servo_disable(PyObject *self) 131 | { 132 | 133 | /* enable servo */ 134 | if (rc_servo_power_rail_en(0)<0) { 135 | PyErr_SetString(servoError, "Failed to disable servos"); 136 | return NULL; 137 | } 138 | 139 | /* Build the output tuple */ 140 | PyObject *ret = Py_BuildValue(""); 141 | 142 | return ret; 143 | } 144 | 145 | static 146 | PyObject *servo_pulse_us(PyObject *self, 147 | PyObject *args) 148 | { 149 | 150 | /* parse arguments */ 151 | int servo; 152 | float us; 153 | if (!PyArg_ParseTuple(args, "ii", &servo, &us)) { 154 | PyErr_SetString(servoError, "Invalid arguments"); 155 | return NULL; 156 | } 157 | 158 | /* set servo */ 159 | if (rc_servo_send_pulse_us(servo, us)<0) { 160 | PyErr_SetString(servoError, "Failed to send pulse to servo"); 161 | return NULL; 162 | } 163 | 164 | /* Build the output tuple */ 165 | PyObject *ret = Py_BuildValue(""); 166 | 167 | return ret; 168 | } 169 | 170 | static 171 | PyObject *servo_pulse_us_all(PyObject *self, 172 | PyObject *args) 173 | { 174 | 175 | /* parse arguments */ 176 | float us; 177 | if (!PyArg_ParseTuple(args, "i", &us)) { 178 | PyErr_SetString(servoError, "Invalid arguments"); 179 | return NULL; 180 | } 181 | 182 | /* set servo */ 183 | if (rc_servo_send_pulse_us(0, us)<0) { 184 | PyErr_SetString(servoError, "Failed to send pulse to servo"); 185 | return NULL; 186 | } 187 | 188 | /* Build the output tuple */ 189 | PyObject *ret = Py_BuildValue(""); 190 | 191 | return ret; 192 | } 193 | 194 | static 195 | PyObject *servo_pulse(PyObject *self, 196 | PyObject *args) 197 | { 198 | 199 | /* parse arguments */ 200 | int servo; 201 | float duty; 202 | if (!PyArg_ParseTuple(args, "if", &servo, &duty)) { 203 | PyErr_SetString(servoError, "Invalid arguments"); 204 | return NULL; 205 | } 206 | 207 | /* set servo */ 208 | if (rc_servo_send_pulse_normalized(servo, duty)<0) { 209 | PyErr_SetString(servoError, "Failed to send normalized pulse to servo"); 210 | return NULL; 211 | } 212 | 213 | /* Build the output tuple */ 214 | PyObject *ret = Py_BuildValue(""); 215 | 216 | return ret; 217 | } 218 | 219 | static 220 | PyObject *servo_pulse_all(PyObject *self, 221 | PyObject *args) 222 | { 223 | 224 | /* parse arguments */ 225 | float duty; 226 | if (!PyArg_ParseTuple(args, "f", &duty)) { 227 | PyErr_SetString(servoError, "Invalid arguments"); 228 | return NULL; 229 | } 230 | 231 | /* set servo */ 232 | if (rc_servo_send_pulse_normalized(0,duty)<0) { 233 | PyErr_SetString(servoError, "Failed to send normalized pulse to servo"); 234 | return NULL; 235 | } 236 | 237 | /* Build the output tuple */ 238 | PyObject *ret = Py_BuildValue(""); 239 | 240 | return ret; 241 | } 242 | 243 | static 244 | PyObject *servo_esc_pulse(PyObject *self, 245 | PyObject *args) 246 | { 247 | 248 | /* parse arguments */ 249 | int servo; 250 | float duty; 251 | if (!PyArg_ParseTuple(args, "if", &servo, &duty)) { 252 | PyErr_SetString(servoError, "Invalid arguments"); 253 | return NULL; 254 | } 255 | 256 | /* set servo */ 257 | if (rc_servo_send_esc_pulse_normalized(servo, duty)<0) { 258 | PyErr_SetString(servoError, "Failed to send normalized pulse to servo"); 259 | return NULL; 260 | } 261 | 262 | /* Build the output tuple */ 263 | PyObject *ret = Py_BuildValue(""); 264 | 265 | return ret; 266 | } 267 | 268 | static 269 | PyObject *servo_esc_pulse_all(PyObject *self, 270 | PyObject *args) 271 | { 272 | 273 | /* parse arguments */ 274 | float duty; 275 | if (!PyArg_ParseTuple(args, "f", &duty)) { 276 | PyErr_SetString(servoError, "Invalid arguments"); 277 | return NULL; 278 | } 279 | 280 | /* set servo */ 281 | if (rc_servo_send_esc_pulse_normalized(0,duty)<0) { 282 | PyErr_SetString(servoError, "Failed to send normalized pulse to servo"); 283 | return NULL; 284 | } 285 | 286 | /* Build the output tuple */ 287 | PyObject *ret = Py_BuildValue(""); 288 | 289 | return ret; 290 | } 291 | 292 | static 293 | PyObject *servo_oneshot_pulse(PyObject *self, 294 | PyObject *args) 295 | { 296 | 297 | /* parse arguments */ 298 | int servo; 299 | float duty; 300 | if (!PyArg_ParseTuple(args, "if", &servo, &duty)) { 301 | PyErr_SetString(servoError, "Invalid arguments"); 302 | return NULL; 303 | } 304 | 305 | /* set servo */ 306 | if (rc_servo_send_oneshot_pulse_normalized(servo, duty)<0) { 307 | PyErr_SetString(servoError, "Failed to send normalized pulse to servo"); 308 | return NULL; 309 | } 310 | 311 | /* Build the output tuple */ 312 | PyObject *ret = Py_BuildValue(""); 313 | 314 | return ret; 315 | } 316 | 317 | static 318 | PyObject *servo_oneshot_pulse_all(PyObject *self, 319 | PyObject *args) 320 | { 321 | 322 | /* parse arguments */ 323 | float duty; 324 | if (!PyArg_ParseTuple(args, "f", &duty)) { 325 | PyErr_SetString(servoError, "Invalid arguments"); 326 | return NULL; 327 | } 328 | 329 | /* set servo */ 330 | if (rc_servo_send_oneshot_pulse_normalized(0,duty)<0) { 331 | PyErr_SetString(servoError, "Failed to send normalized pulse to servo"); 332 | return NULL; 333 | } 334 | 335 | /* Build the output tuple */ 336 | PyObject *ret = Py_BuildValue(""); 337 | 338 | return ret; 339 | } 340 | -------------------------------------------------------------------------------- /test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcdeoliveira/rcpy/2879b01390c6dcbd8fb299e3c5299848d6c7fb71/test/__init__.py -------------------------------------------------------------------------------- /test/test_clock.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import rcpy.clock as clock 4 | import time 5 | 6 | def test1(): 7 | 8 | class MyAction(clock.Action): 9 | 10 | def __init__(self): 11 | self.count = 0 12 | 13 | def run(self): 14 | self.count += 1 15 | 16 | action = MyAction() 17 | obj = clock.Clock(action) 18 | 19 | obj.start() 20 | assert action.count == 0 21 | 22 | time.sleep(1.1*obj.period) 23 | assert action.count == 1 24 | 25 | time.sleep(1.1*obj.period) 26 | assert action.count == 2 27 | 28 | obj.toggle() 29 | 30 | time.sleep(1.1*obj.period) 31 | assert action.count == 2 32 | 33 | obj.toggle() 34 | 35 | time.sleep(1.1*obj.period) 36 | assert action.count == 3 37 | 38 | obj.stop() 39 | 40 | if __name__ == '__main__': 41 | 42 | test1() 43 | -------------------------------------------------------------------------------- /test/test_encoder.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import time 4 | import rcpy.encoder as encoder 5 | 6 | def test1(): 7 | 8 | N = 5 9 | 10 | # set to 10 11 | 12 | encoder.set(1, 10) 13 | assert encoder.get(1) == 10 14 | 15 | encoder.set(2, 10) 16 | assert encoder.get(2) == 10 17 | 18 | encoder.set(3, 10) 19 | assert encoder.get(3) == 10 20 | 21 | encoder.set(4, 10) 22 | assert encoder.get(4) == 10 23 | 24 | # default to zero 25 | 26 | encoder.set(1) 27 | assert encoder.get(1) == 0 28 | 29 | encoder.set(2) 30 | assert encoder.get(2) == 0 31 | 32 | encoder.set(3) 33 | assert encoder.get(3) == 0 34 | 35 | encoder.set(4) 36 | assert encoder.get(4) == 0 37 | 38 | print('\n ENC #1 | ENC #2 | ENC #3 | ENC #4') 39 | 40 | for i in range(N): 41 | 42 | e1 = encoder.get(1) 43 | e2 = encoder.get(2) 44 | e3 = encoder.get(3) 45 | e4 = encoder.get(4) 46 | 47 | print('\r{:7d} | {:7d} | {:7d} | {:7d}'.format(e1,e2,e3,e4), 48 | end='') 49 | 50 | time.sleep(1) 51 | 52 | if __name__ == '__main__': 53 | 54 | test1() 55 | -------------------------------------------------------------------------------- /test/test_led.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import time 4 | import rcpy.gpio as gpio 5 | import rcpy.led as led 6 | 7 | def test1(): 8 | 9 | led.green.on() 10 | assert led.green.is_on() 11 | assert gpio.Input(led.green.pin).is_low() # pin is output 12 | 13 | led.green.off() 14 | assert led.green.is_off() 15 | assert gpio.Input(led.green.pin).is_low() # pin is output 16 | 17 | led.red.on() 18 | assert led.red.is_on() 19 | assert gpio.Input(led.red.pin).is_low() # pin is output 20 | 21 | led.red.off() 22 | assert led.red.is_off() 23 | assert gpio.Input(led.red.pin).is_low() # pin is output 24 | 25 | if __name__ == '__main__': 26 | 27 | test1() 28 | -------------------------------------------------------------------------------- /test/test_motor.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import time 4 | import rcpy.motor as motor 5 | 6 | def test1(): 7 | 8 | N = 5 9 | 10 | try: 11 | 12 | # enable 13 | motor.enable() 14 | time.sleep(.5) 15 | 16 | # disable 17 | motor.disable() 18 | time.sleep(.5) 19 | 20 | # enable again 21 | motor.enable() 22 | 23 | # spin motor 2 forward and back 24 | motor.set(2, 1) 25 | time.sleep(2) 26 | 27 | motor.set_free_spin(2) 28 | time.sleep(1) 29 | 30 | motor.set(2, -1) 31 | time.sleep(2) 32 | 33 | motor.set_free_spin(2) 34 | 35 | # spin motor 2 forward and back 36 | motor.set(3, 1) 37 | time.sleep(2) 38 | 39 | motor.set_free_spin(3) 40 | time.sleep(3) 41 | 42 | motor.set(3, -1) 43 | time.sleep(2) 44 | 45 | motor.set_free_spin(3) 46 | 47 | # disable motor 48 | motor.disable() 49 | 50 | except (KeyboardInterrupt, SystemExit): 51 | pass 52 | 53 | finally: 54 | 55 | motor.disable() 56 | 57 | 58 | if __name__ == '__main__': 59 | 60 | test1() 61 | -------------------------------------------------------------------------------- /test/test_mpu9250.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import time 4 | import rcpy.mpu9250 as mpu9250 5 | 6 | def test1(): 7 | 8 | N = 1 9 | 10 | try: 11 | 12 | # no magnetometer 13 | mpu9250.initialize(enable_magnetometer = False) 14 | 15 | conf = mpu9250.get() 16 | assert conf == {'orientation': 136, 'accel_dlpf': 2, 'gyro_dlpf': 2, 'compass_time_constant': 5.0, 'enable_fusion': False, 'enable_dmp': False, 'enable_magnetometer': False, 'accel_fsr': 1, 'dmp_sample_rate': 100, 'show_warnings': False, 'gyro_fsr': 2, 'dmp_interrupt_priority': 98} 17 | 18 | print('\n Accel XYZ(m/s^2) | Gyro XYZ (rad/s) | Temp (C)') 19 | 20 | for i in range(N): 21 | 22 | (ax,ay,az) = mpu9250.read_accel_data() 23 | (gx,gy,gz) = mpu9250.read_gyro_data() 24 | temp = mpu9250.read_imu_temp() 25 | 26 | print(('\r{:6.2f} {:6.2f} {:6.2f} |' + 27 | '{:6.1f} {:6.1f} {:6.1f} | {:6.1f}') 28 | .format(ax, ay, az, 29 | gx, gy, gz, temp), 30 | end='') 31 | 32 | time.sleep(1) 33 | 34 | with pytest.raises(mpu9250.error): 35 | mpu9250.read_mag_data() 36 | 37 | # consolidated read function 38 | 39 | for i in range(N): 40 | 41 | data = mpu9250.read() 42 | print(('\r{0[0]:6.2f} {0[1]:6.2f} {0[2]:6.2f} |' 43 | '{1[0]:6.1f} {1[1]:6.1f} {1[2]:6.1f} |') 44 | .format(data['accel'], 45 | data['gyro']), 46 | end='') 47 | 48 | time.sleep(1) 49 | 50 | # with magnetometer 51 | mpu9250.initialize(enable_magnetometer = True) 52 | 53 | conf = mpu9250.get() 54 | assert conf == {'orientation': 136, 'accel_dlpf': 2, 'gyro_dlpf': 2, 'compass_time_constant': 5.0, 'enable_fusion': False, 'enable_dmp': False, 'enable_magnetometer': True, 'accel_fsr': 1, 'dmp_sample_rate': 100, 'show_warnings': False, 'gyro_fsr': 2, 'dmp_interrupt_priority': 98} 55 | 56 | print('\n Accel XYZ(m/s^2) | Gyro XYZ (rad/s) | Mag Field XYZ(uT) | Temp (C)') 57 | 58 | for i in range(N): 59 | 60 | (ax,ay,az) = mpu9250.read_accel_data() 61 | (gx,gy,gz) = mpu9250.read_gyro_data() 62 | (mx,my,mz) = mpu9250.read_mag_data() 63 | temp = mpu9250.read_imu_temp() 64 | 65 | print(('\r{:6.2f} {:6.2f} {:6.2f} |' + 66 | '{:6.1f} {:6.1f} {:6.1f} |' 67 | '{:6.1f} {:6.1f} {:6.1f} | {:6.1f}') 68 | .format(ax, ay, az, 69 | gx, gy, gz, 70 | mx, my, mz, temp), 71 | end='') 72 | 73 | time.sleep(1) 74 | 75 | # consolidated read function 76 | 77 | for i in range(N): 78 | 79 | data = mpu9250.read() 80 | print(('\r{0[0]:6.2f} {0[1]:6.2f} {0[2]:6.2f} |' 81 | '{1[0]:6.1f} {1[1]:6.1f} {1[2]:6.1f} |' 82 | '{2[0]:6.1f} {2[1]:6.1f} {2[2]:6.1f} |') 83 | .format(data['accel'], 84 | data['gyro'], 85 | data['mag']), 86 | end='') 87 | 88 | time.sleep(1) 89 | 90 | except (KeyboardInterrupt, SystemExit): 91 | pass 92 | 93 | finally: 94 | mpu9250.power_off() 95 | 96 | def test2(): 97 | 98 | N = 200 99 | 100 | try: 101 | 102 | # with dmp, no magnetometer 103 | mpu9250.initialize(enable_magnetometer = False, enable_dmp = True) 104 | 105 | conf = mpu9250.get() 106 | assert conf == {'orientation': 136, 'accel_dlpf': 2, 'gyro_dlpf': 2, 'compass_time_constant': 5.0, 'enable_fusion': False, 'enable_dmp': True, 'enable_magnetometer': False, 'accel_fsr': 1, 'dmp_sample_rate': 100, 'show_warnings': False, 'gyro_fsr': 2, 'dmp_interrupt_priority': 98} 107 | 108 | print('\n Accel XYZ(m/s^2) | Gyro XYZ (rad/s) | Temp (C)') 109 | 110 | for i in range(N): 111 | 112 | data = mpu9250.read() 113 | print(('\r{0[0]:6.2f} {0[1]:6.2f} {0[2]:6.2f} |' 114 | '{1[0]:6.1f} {1[1]:6.1f} {1[2]:6.1f} |') 115 | .format(data['accel'], 116 | data['gyro']), 117 | end='') 118 | 119 | # with dmp, with magnetometer 120 | mpu9250.initialize(enable_magnetometer = True, enable_dmp = True) 121 | 122 | conf = mpu9250.get() 123 | assert conf == {'orientation': 136, 'accel_dlpf': 2, 'gyro_dlpf': 2, 'compass_time_constant': 5.0, 'enable_fusion': False, 'enable_dmp': True, 'enable_magnetometer': True, 'accel_fsr': 1, 'dmp_sample_rate': 100, 'show_warnings': False, 'gyro_fsr': 2, 'dmp_interrupt_priority': 98} 124 | 125 | print('\n Accel XYZ(m/s^2) | Gyro XYZ (rad/s) | Mag Field XYZ(uT) | Temp (C)') 126 | 127 | for i in range(N): 128 | 129 | data = mpu9250.read() 130 | print(('\r{0[0]:6.2f} {0[1]:6.2f} {0[2]:6.2f} |' 131 | '{1[0]:6.1f} {1[1]:6.1f} {1[2]:6.1f} |' 132 | '{2[0]:6.1f} {2[1]:6.1f} {2[2]:6.1f} |') 133 | .format(data['accel'], 134 | data['gyro'], 135 | data['mag']), 136 | end='') 137 | 138 | except (KeyboardInterrupt, SystemExit): 139 | pass 140 | 141 | finally: 142 | mpu9250.power_off() 143 | 144 | if __name__ == '__main__': 145 | 146 | test1() 147 | test2() 148 | -------------------------------------------------------------------------------- /test/test_rcpy.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import time 4 | import rcpy 5 | 6 | def test1(): 7 | 8 | # initialize 9 | rcpy.initialize() 10 | assert rcpy.get_state() == rcpy.IDLE 11 | 12 | # clean up 13 | rcpy.cleanup() 14 | assert rcpy.get_state() == rcpy.EXITING 15 | 16 | # initialize again 17 | rcpy.initialize() 18 | assert rcpy.get_state() == rcpy.IDLE 19 | 20 | # set state 21 | rcpy.set_state(rcpy.PAUSED) 22 | assert rcpy.get_state() == rcpy.PAUSED 23 | 24 | # set state 25 | rcpy.set_state(rcpy.RUNNING) 26 | assert rcpy.get_state() == rcpy.RUNNING 27 | 28 | if __name__ == '__main__': 29 | 30 | test1() 31 | --------------------------------------------------------------------------------