├── .gitignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── LICENSE ├── LICENSE.txt ├── Makefile ├── README.md ├── README.rst ├── docs ├── _static │ └── favicon.ico ├── api.rst ├── conf.py ├── examples.rst └── index.rst ├── examples ├── imu.py ├── mpu9250_simpletest.py └── test.py ├── hybrid └── roboticsmasters_mpu9250.py ├── requirements.txt ├── robohat_mpu9250 ├── ak8963.py ├── mpu6500.py └── mpu9250.py ├── roboticsmasters_ak8963.py ├── roboticsmasters_mpu6500.py ├── roboticsmasters_mpu9250.py ├── setup.cfg ├── setup.py └── tests ├── code.py └── robohatimu.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Python internal machinery 2 | python_mpu9250.egg-info/ 3 | __pycache__/ 4 | dist/ 5 | 6 | # Visual Studio Code configuration 7 | .vscode/ 8 | 9 | # Vim 10 | *.swp 11 | 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file, in reverse chronological order by release. 4 | 5 | ## [0.3.0](https://github.com/tuupola/micropython-mpu9250/compare/0.2.1...master) - unreleased 6 | ### Added 7 | 8 | - Support for internal temperature sensor ([#1](https://github.com/tuupola/micropython-mpu9250/issues/1), [#9](https://github.com/tuupola/micropython-mpu9250/pull/9)) 9 | - Support for gyro calibration ([#5](https://github.com/tuupola/micropython-mpu9250/issues/5), [#10](https://github.com/tuupola/micropython-mpu9250/pull/10)) 10 | 11 | ## [0.2.1](https://github.com/tuupola/micropython-mpu9250/compare/0.2.0...0.2.1) - 2019-02-07 12 | ### Fixed 13 | - Gyro degrees to radians conversion ([#8](https://github.com/tuupola/micropython-mpu9250/pull/8)). 14 | 15 | ## [0.2.0](https://github.com/tuupola/micropython-mpu9250/compare/0.1.0...0.2.0)- 2018-04-08 16 | ### Added 17 | - Support for magnetometer factory sensitivity adjustement values `ASAX`, `ASAY` and `ASAZ`. 18 | - Support for setting magnetometer offset and scale calibration values. 19 | ``` 20 | ak8963 = AK8963( 21 | i2c, 22 | offset=(-136.8931640625, -160.482421875, 59.02880859375), 23 | scale=(1.18437220840483, 0.923895823933424, 0.931707933618979) 24 | ) 25 | ``` 26 | - Method for retrieving the magnetometer offset and scale calibration values. 27 | ``` 28 | ak8963 = AK8963(i2c) 29 | offset, scale = ak8963.calibrate(count=256, delay=200) 30 | ``` 31 | 32 | ## 0.1.0 - 2018-02-17 33 | 34 | Initial working release. -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Adafruit Community Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and leaders pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level or type of 9 | experience, education, socio-economic status, nationality, personal appearance, 10 | race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | We are committed to providing a friendly, safe and welcoming environment for 15 | all. 16 | 17 | Examples of behavior that contributes to creating a positive environment 18 | include: 19 | 20 | * Be kind and courteous to others 21 | * Using welcoming and inclusive language 22 | * Being respectful of differing viewpoints and experiences 23 | * Collaborating with other community members 24 | * Gracefully accepting constructive criticism 25 | * Focusing on what is best for the community 26 | * Showing empathy towards other community members 27 | 28 | Examples of unacceptable behavior by participants include: 29 | 30 | * The use of sexualized language or imagery and sexual attention or advances 31 | * The use of inappropriate images, including in a community member's avatar 32 | * The use of inappropriate language, including in a community member's nickname 33 | * Any spamming, flaming, baiting or other attention-stealing behavior 34 | * Excessive or unwelcome helping; answering outside the scope of the question 35 | asked 36 | * Trolling, insulting/derogatory comments, and personal or political attacks 37 | * Public or private harassment 38 | * Publishing others' private information, such as a physical or electronic 39 | address, without explicit permission 40 | * Other conduct which could reasonably be considered inappropriate 41 | 42 | The goal of the standards and moderation guidelines outlined here is to build 43 | and maintain a respectful community. We ask that you don’t just aim to be 44 | "technically unimpeachable", but rather try to be your best self. 45 | 46 | We value many things beyond technical expertise, including collaboration and 47 | supporting others within our community. Providing a positive experience for 48 | other community members can have a much more significant impact than simply 49 | providing the correct answer. 50 | 51 | ## Our Responsibilities 52 | 53 | Project leaders are responsible for clarifying the standards of acceptable 54 | behavior and are expected to take appropriate and fair corrective action in 55 | response to any instances of unacceptable behavior. 56 | 57 | Project leaders have the right and responsibility to remove, edit, or 58 | reject messages, comments, commits, code, issues, and other contributions 59 | that are not aligned to this Code of Conduct, or to ban temporarily or 60 | permanently any community member for other behaviors that they deem 61 | inappropriate, threatening, offensive, or harmful. 62 | 63 | ## Moderation 64 | 65 | Instances of behaviors that violate the Adafruit Community Code of Conduct 66 | may be reported by any member of the community. Community members are 67 | encouraged to report these situations, including situations they witness 68 | involving other community members. 69 | 70 | You may report in the following ways: 71 | 72 | In any situation, you may send an email to . 73 | 74 | On the Adafruit Discord, you may send an open message from any channel 75 | to all Community Helpers by tagging @community moderators. You may also send an 76 | open message from any channel, or a direct message to @kattni#1507, 77 | @tannewt#4653, @Dan Halbert#1614, @cater#2442, @sommersoft#0222, or 78 | @Andon#8175. 79 | 80 | Email and direct message reports will be kept confidential. 81 | 82 | In situations on Discord where the issue is particularly egregious, possibly 83 | illegal, requires immediate action, or violates the Discord terms of service, 84 | you should also report the message directly to Discord. 85 | 86 | These are the steps for upholding our community’s standards of conduct. 87 | 88 | 1. Any member of the community may report any situation that violates the 89 | Adafruit Community Code of Conduct. All reports will be reviewed and 90 | investigated. 91 | 2. If the behavior is an egregious violation, the community member who 92 | committed the violation may be banned immediately, without warning. 93 | 3. Otherwise, moderators will first respond to such behavior with a warning. 94 | 4. Moderators follow a soft "three strikes" policy - the community member may 95 | be given another chance, if they are receptive to the warning and change their 96 | behavior. 97 | 5. If the community member is unreceptive or unreasonable when warned by a 98 | moderator, or the warning goes unheeded, they may be banned for a first or 99 | second offense. Repeated offenses will result in the community member being 100 | banned. 101 | 102 | ## Scope 103 | 104 | This Code of Conduct and the enforcement policies listed above apply to all 105 | Adafruit Community venues. This includes but is not limited to any community 106 | spaces (both public and private), the entire Adafruit Discord server, and 107 | Adafruit GitHub repositories. Examples of Adafruit Community spaces include 108 | but are not limited to meet-ups, audio chats on the Adafruit Discord, or 109 | interaction at a conference. 110 | 111 | This Code of Conduct applies both within project spaces and in public spaces 112 | when an individual is representing the project or its community. As a community 113 | member, you are representing our community, and are expected to behave 114 | accordingly. 115 | 116 | ## Attribution 117 | 118 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 119 | version 1.4, available at 120 | , 121 | and the [Rust Code of Conduct](https://www.rust-lang.org/en-US/conduct.html). 122 | 123 | For other projects adopting the Adafruit Community Code of 124 | Conduct, please contact the maintainers of those projects for enforcement. 125 | If you wish to use this code of conduct for your own project, consider 126 | explicitly mentioning your moderation policy or making a copy with your 127 | own moderation policy so as to avoid confusion. 128 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Cian Byrne for Robotics Masters Limited 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 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018-2019 Mika Tuupola 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 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .DEFAULT_GOAL := help 2 | 3 | help: 4 | @echo "" 5 | @echo "Available tasks:" 6 | @echo " watch Upload changed *.py files to board automatically" 7 | @echo " shell Start an remote shell session" 8 | @echo " sync Upload all *.py files to board" 9 | @echo " reset Soft reboot the board" 10 | @echo " repl Start a repl session" 11 | @echo " deps Install dependencies with upip" 12 | @echo "" 13 | 14 | watch: 15 | find . -name "*.py" | entr -c sh -c 'make sync && make reset' 16 | 17 | sync: 18 | rshell --port /dev/tty.SLAB_USBtoUART --timing --buffer-size=32 cp --recursive *.py /flash 19 | 20 | shell: 21 | rshell --port /dev/tty.SLAB_USBtoUART --timing --buffer-size=32 22 | 23 | repl: 24 | screen /dev/tty.SLAB_USBtoUART 115200 25 | 26 | reset: 27 | rshell --port /dev/tty.SLAB_USBtoUART --timing --buffer-size=32 repl "~ import machine ~ machine.reset()~" 28 | 29 | dist: 30 | python3 setup.py sdist 31 | # twine upload dist/filename.tar.gz 32 | 33 | .PHONY: help watch shell repl reset sync dist 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Python MPU-9250 (MPU-6500 + AK8963) I2C driver 2 | 3 | MPU-9250 is a System in Package (SiP) which combines two chips: MPU-6500 which contains 3-axis gyroscope and 3-axis accelerometer and an AK8963 which is a 3-axis digital compass. 4 | 5 | This library communicates with these sensors over I2C. It is written for 6 | CircuitPython. 7 | 8 | ## Usage 9 | 10 | Simple test with never ending loop. 11 | 12 | ```python 13 | import time 14 | from mpu9250 import MPU9250 15 | 16 | sensor = MPU9250() 17 | 18 | print("MPU9250 id: " + hex(sensor.whoami)) 19 | 20 | while True: 21 | print(sensor.read_acceleration()) 22 | print(sensor.read_gyro()) 23 | print(sensor.read_magnetic()) 24 | 25 | time.sleep(1000) 26 | ``` 27 | 28 | The library returns a 3-tuple of X, Y, Z axis values for either acceleration, gyroscope and magnetometer ie compass. Default units are `m/s^2`, `rad/s` and `uT`. It is possible to also get acceleration values in `g` and gyro values `deg/s`. See the example below. 29 | 30 | Note that both the MPU6500 and the AK8963 drivers are available as separate classes. MPU9250 is actually a composite of those two. 31 | 32 | ## Magnetometer Calibration 33 | 34 | For real life applications you should almost always [calibrate the magnetometer](https://appelsiini.net/2018/calibrate-magnetometer/). The AK8963 driver supports both hard and soft iron correction. Calibration function takes two parameters: `count` is the number of samples to collect and `delay` is the delay in milliseconds between the samples. 35 | 36 | With the default values of `256` and `200` calibration takes approximately one minute. While calibration function is running the sensor should be rotated multiple times around each axis. 37 | 38 | ```python 39 | from mpu9250 import MPU9250 40 | from ak8963 import AK8963 41 | 42 | ak8963 = AK8963() 43 | offset, scale = ak8963.calibrate(count=256, delay=200) 44 | 45 | sensor = MPU9250(ak8963=ak8963) 46 | ``` 47 | 48 | After finishing calibration the `calibrate()` method also returns tuples for both hard iron `offset` and soft iron `scale`. To avoid calibrating after each startup it would make sense to store these values in NVRAM or config file and pass them to the AK8963 constructor. Below example only illustrates how to use the constructor. 49 | 50 | ```python 51 | from mpu9250 import MPU9250 52 | from ak8963 import AK8963 53 | 54 | ak8963 = AK8963( 55 | offset=(-136.8931640625, -160.482421875, 59.02880859375), 56 | scale=(1.18437220840483, 0.923895823933424, 0.931707933618979) 57 | ) 58 | sensor = MPU9250(i2c, ak8963=ak8963) 59 | ``` 60 | 61 | 62 | ## License 63 | 64 | The MIT License (MIT). Please see [License File](LICENSE.txt) for more information. 65 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Introduction 2 | ============ 3 | 4 | .. image:: https://readthedocs.org/projects/roboticsmasters-circuitpython-mpu9250/badge/?version=latest 5 | :target: https://circuitpython.readthedocs.io/projects/mpu9250/en/latest/ 6 | :alt: Documentation Status 7 | 8 | .. image:: https://img.shields.io/discord/327254708534116352.svg 9 | :target: https://discord.gg/nBQh6qu 10 | :alt: Discord 11 | 12 | .. image:: https://github.com/wallarug/Roboticsmasters_CircuitPython_MPU9250/workflows/Build%20CI/badge.svg 13 | :target: https://github.com/wallarug/Roboticsmasters_CircuitPython_MPU9250/actions 14 | :alt: Build Status 15 | 16 | CircuitPython helper library for MPU9250 9-axis IMU 17 | 18 | 19 | Dependencies 20 | ============= 21 | This driver depends on: 22 | 23 | * `Adafruit CircuitPython `_ 24 | * `Bus Device `_ 25 | * `Register `_ 26 | 27 | Please ensure all dependencies are available on the CircuitPython filesystem. 28 | This is easily achieved by downloading 29 | `the Adafruit library and driver bundle `_. 30 | 31 | Installing from PyPI 32 | ===================== 33 | .. note:: This library is not available on PyPI yet. Install documentation is included 34 | as a standard element. Stay tuned for PyPI availability! 35 | 36 | .. todo:: Remove the above note if PyPI version is/will be available at time of release. 37 | If the library is not planned for PyPI, remove the entire 'Installing from PyPI' section. 38 | 39 | On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally `from 40 | PyPI `_. To install for current user: 41 | 42 | .. code-block:: shell 43 | 44 | pip3 install adafruit-circuitpython-mpu9250 45 | 46 | To install system-wide (this may be required in some cases): 47 | 48 | .. code-block:: shell 49 | 50 | sudo pip3 install adafruit-circuitpython-mpu9250 51 | 52 | To install in a virtual environment in your current project: 53 | 54 | .. code-block:: shell 55 | 56 | mkdir project-name && cd project-name 57 | python3 -m venv .env 58 | source .env/bin/activate 59 | pip3 install adafruit-circuitpython-mpu9250 60 | 61 | Usage Example 62 | ============= 63 | 64 | .. todo:: Add a quick, simple example. It and other examples should live in the examples folder and be included in docs/examples.rst. 65 | 66 | Contributing 67 | ============ 68 | 69 | Contributions are welcome! Please read our `Code of Conduct 70 | `_ 71 | before contributing to help this project stay welcoming. 72 | 73 | Documentation 74 | ============= 75 | 76 | For information on building library documentation, please check out `this guide `_. 77 | -------------------------------------------------------------------------------- /docs/_static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wallarug/CircuitPython_MPU9250/dc67ceb3baf5d53580693d14987c4a2a021f2e02/docs/_static/favicon.ico -------------------------------------------------------------------------------- /docs/api.rst: -------------------------------------------------------------------------------- 1 | 2 | .. If you created a package, create one automodule per module in the package. 3 | 4 | .. If your library file(s) are nested in a directory (e.g. /adafruit_foo/foo.py) 5 | .. use this format as the module name: "adafruit_foo.foo" 6 | 7 | .. automodule:: roboticsmasters_mpu9250 8 | :members: 9 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os 4 | import sys 5 | sys.path.insert(0, os.path.abspath('..')) 6 | 7 | # -- General configuration ------------------------------------------------ 8 | 9 | # Add any Sphinx extension module names here, as strings. They can be 10 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 11 | # ones. 12 | extensions = [ 13 | 'sphinx.ext.autodoc', 14 | 'sphinx.ext.intersphinx', 15 | 'sphinx.ext.napoleon', 16 | 'sphinx.ext.todo', 17 | ] 18 | 19 | # TODO: Please Read! 20 | # Uncomment the below if you use native CircuitPython modules such as 21 | # digitalio, micropython and busio. List the modules you use. Without it, the 22 | # autodoc module docs will fail to generate with a warning. 23 | # autodoc_mock_imports = ["digitalio", "busio"] 24 | 25 | 26 | intersphinx_mapping = {'python': ('https://docs.python.org/3.4', None),'BusDevice': ('https://circuitpython.readthedocs.io/projects/busdevice/en/latest/', None),'Register': ('https://circuitpython.readthedocs.io/projects/register/en/latest/', None),'CircuitPython': ('https://circuitpython.readthedocs.io/en/latest/', None)} 27 | 28 | # Add any paths that contain templates here, relative to this directory. 29 | templates_path = ['_templates'] 30 | 31 | source_suffix = '.rst' 32 | 33 | # The master toctree document. 34 | master_doc = 'index' 35 | 36 | # General information about the project. 37 | project = u'Roboticsmasters MPU9250 Library' 38 | copyright = u'2020 Cian Byrne' 39 | author = u'Cian Byrne' 40 | 41 | # The version info for the project you're documenting, acts as replacement for 42 | # |version| and |release|, also used in various other places throughout the 43 | # built documents. 44 | # 45 | # The short X.Y version. 46 | version = u'1.0' 47 | # The full version, including alpha/beta/rc tags. 48 | release = u'1.0' 49 | 50 | # The language for content autogenerated by Sphinx. Refer to documentation 51 | # for a list of supported languages. 52 | # 53 | # This is also used if you do content translation via gettext catalogs. 54 | # Usually you set "language" from the command line for these cases. 55 | language = None 56 | 57 | # List of patterns, relative to source directory, that match files and 58 | # directories to ignore when looking for source files. 59 | # This patterns also effect to html_static_path and html_extra_path 60 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', '.env', 'CODE_OF_CONDUCT.md'] 61 | 62 | # The reST default role (used for this markup: `text`) to use for all 63 | # documents. 64 | # 65 | default_role = "any" 66 | 67 | # If true, '()' will be appended to :func: etc. cross-reference text. 68 | # 69 | add_function_parentheses = True 70 | 71 | # The name of the Pygments (syntax highlighting) style to use. 72 | pygments_style = 'sphinx' 73 | 74 | # If true, `todo` and `todoList` produce output, else they produce nothing. 75 | todo_include_todos = False 76 | 77 | # If this is True, todo emits a warning for each TODO entries. The default is False. 78 | todo_emit_warnings = True 79 | 80 | napoleon_numpy_docstring = False 81 | 82 | # -- Options for HTML output ---------------------------------------------- 83 | 84 | # The theme to use for HTML and HTML Help pages. See the documentation for 85 | # a list of builtin themes. 86 | # 87 | on_rtd = os.environ.get('READTHEDOCS', None) == 'True' 88 | 89 | if not on_rtd: # only import and set the theme if we're building docs locally 90 | try: 91 | import sphinx_rtd_theme 92 | html_theme = 'sphinx_rtd_theme' 93 | html_theme_path = [sphinx_rtd_theme.get_html_theme_path(), '.'] 94 | except: 95 | html_theme = 'default' 96 | html_theme_path = ['.'] 97 | else: 98 | html_theme_path = ['.'] 99 | 100 | # Add any paths that contain custom static files (such as style sheets) here, 101 | # relative to this directory. They are copied after the builtin static files, 102 | # so a file named "default.css" will overwrite the builtin "default.css". 103 | html_static_path = ['_static'] 104 | 105 | # The name of an image file (relative to this directory) to use as a favicon of 106 | # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 107 | # pixels large. 108 | # 109 | html_favicon = '_static/favicon.ico' 110 | 111 | # Output file base name for HTML help builder. 112 | htmlhelp_basename = 'RoboticsmastersMpu9250Librarydoc' 113 | 114 | # -- Options for LaTeX output --------------------------------------------- 115 | 116 | latex_elements = { 117 | # The paper size ('letterpaper' or 'a4paper'). 118 | # 119 | # 'papersize': 'letterpaper', 120 | 121 | # The font size ('10pt', '11pt' or '12pt'). 122 | # 123 | # 'pointsize': '10pt', 124 | 125 | # Additional stuff for the LaTeX preamble. 126 | # 127 | # 'preamble': '', 128 | 129 | # Latex figure (float) alignment 130 | # 131 | # 'figure_align': 'htbp', 132 | } 133 | 134 | # Grouping the document tree into LaTeX files. List of tuples 135 | # (source start file, target name, title, 136 | # author, documentclass [howto, manual, or own class]). 137 | latex_documents = [ 138 | (master_doc, 'RoboticsmastersMPU9250Library.tex', u'RoboticsmastersMPU9250 Library Documentation', 139 | author, 'manual'), 140 | ] 141 | 142 | # -- Options for manual page output --------------------------------------- 143 | 144 | # One entry per manual page. List of tuples 145 | # (source start file, name, description, authors, manual section). 146 | man_pages = [ 147 | (master_doc, 'RoboticsmastersMPU9250library', u'Roboticsmasters MPU9250 Library Documentation', 148 | [author], 1) 149 | ] 150 | 151 | # -- Options for Texinfo output ------------------------------------------- 152 | 153 | # Grouping the document tree into Texinfo files. List of tuples 154 | # (source start file, target name, title, author, 155 | # dir menu entry, description, category) 156 | texinfo_documents = [ 157 | (master_doc, 'RoboticsmastersMPU9250Library', u'Roboticsmasters MPU9250 Library Documentation', 158 | author, 'RoboticsmastersMPU9250Library', 'One line description of project.', 159 | 'Miscellaneous'), 160 | ] 161 | -------------------------------------------------------------------------------- /docs/examples.rst: -------------------------------------------------------------------------------- 1 | Simple test 2 | ------------ 3 | 4 | Ensure your device works with this simple test. 5 | 6 | .. literalinclude:: ../examples/mpu9250_simpletest.py 7 | :caption: examples/mpu9250_simpletest.py 8 | :linenos: 9 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../README.rst 2 | 3 | Table of Contents 4 | ================= 5 | 6 | .. toctree:: 7 | :maxdepth: 4 8 | :hidden: 9 | 10 | self 11 | 12 | .. toctree:: 13 | :caption: Examples 14 | 15 | examples 16 | 17 | .. toctree:: 18 | :caption: API Reference 19 | :maxdepth: 3 20 | 21 | api 22 | 23 | .. toctree:: 24 | :caption: Tutorials 25 | 26 | .. todo:: Add any Learn guide links here. If there are none, then simply delete this todo and leave 27 | the toctree above for use later. 28 | 29 | .. toctree:: 30 | :caption: Related Products 31 | 32 | .. todo:: Add any product links here. If there are none, then simply delete this todo and leave 33 | the toctree above for use later. 34 | 35 | .. toctree:: 36 | :caption: Other Links 37 | 38 | Download 39 | CircuitPython Reference Documentation 40 | CircuitPython Support Forum 41 | Discord Chat 42 | Adafruit Learning System 43 | Adafruit Blog 44 | Adafruit Store 45 | 46 | Indices and tables 47 | ================== 48 | 49 | * :ref:`genindex` 50 | * :ref:`modindex` 51 | * :ref:`search` 52 | -------------------------------------------------------------------------------- /examples/imu.py: -------------------------------------------------------------------------------- 1 | """ 2 | Example IMU Control 3 | Robo HAT MM1 4 | https://github.com/wallarug/circuitpython_mpu9250 5 | """ 6 | import board 7 | import busio 8 | from robohat_mpu9250.mpu9250 import MPU9250 9 | from robohat_mpu9250.mpu6500 import MPU6500 10 | from robohat_mpu9250.ak8963 import AK8963 11 | 12 | from time import sleep 13 | 14 | i2c = busio.I2C(board.SCL, board.SDA) 15 | 16 | mpu = MPU6500(i2c, address=0x69) 17 | ak = AK8963(i2c) 18 | 19 | sensor = MPU9250(mpu, ak) 20 | 21 | print("Reading in data from IMU.") 22 | print("MPU9250 id: " + hex(sensor.read_whoami())) 23 | 24 | while True: 25 | print('Acceleration (m/s^2): ({0:0.3f},{1:0.3f},{2:0.3f})'.format(*sensor.read_acceleration())) 26 | print('Magnetometer (gauss): ({0:0.3f},{1:0.3f},{2:0.3f})'.format(*sensor.read_magnetic())) 27 | print('Gyroscope (degrees/sec): ({0:0.3f},{1:0.3f},{2:0.3f})'.format(*sensor.read_gyro())) 28 | print('Temperature: {0:0.3f}C'.format(sensor.read_temperature())) 29 | sleep(2) 30 | -------------------------------------------------------------------------------- /examples/mpu9250_simpletest.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wallarug/CircuitPython_MPU9250/dc67ceb3baf5d53580693d14987c4a2a021f2e02/examples/mpu9250_simpletest.py -------------------------------------------------------------------------------- /examples/test.py: -------------------------------------------------------------------------------- 1 | """ 2 | Test data aquisition from the sensors in a loop. 3 | """ 4 | 5 | import time 6 | import mpu6500 7 | import ak8963 8 | 9 | interval = 1 / 20 # [s] 10 | n_iterations = 100 11 | 12 | imu = mpu6500.MPU6500() 13 | mag = ak8963.AK8963() 14 | 15 | t_start_loop = time.time() 16 | for _ in range(n_iterations): 17 | t_start = time.time() 18 | acc = imu.read_acceleration() 19 | rot = imu.read_gyro() 20 | temp = imu.read_temperature() 21 | t_imu_end = time.time() 22 | mfield = mag.read_magnetic() 23 | t_mag_end = t_end = time.time() 24 | 25 | print("Acceleration:", acc) 26 | print("Rotation :", rot) 27 | print("Temperature :", temp) 28 | print("Duration IMU:", t_imu_end - t_start) 29 | print("Magnetic :", mfield) 30 | print("Duration MAG:", t_mag_end - t_imu_end) 31 | print() 32 | 33 | t_end = time.time() 34 | remaining = max(0, interval - (t_end - t_start)) 35 | time.sleep(remaining) 36 | 37 | t_end_loop = time.time() 38 | t_total = t_end_loop - t_start_loop 39 | print("Total time :", t_total) 40 | print("Average loop time:", t_total / n_iterations) 41 | print("Loop frequency :", n_iterations / t_total) 42 | 43 | 44 | -------------------------------------------------------------------------------- /hybrid/roboticsmasters_mpu9250.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2020 Cian Byrne for Robotics Masters Limited 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 13 | # all 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 21 | # THE SOFTWARE. 22 | """ 23 | `roboticsmasters_mpu9250` 24 | ================================================================================ 25 | 26 | CircuitPython helper library for MPU9250 9-axis IMU 27 | 28 | 29 | * Author(s): Cian Byrne 30 | 31 | Implementation Notes 32 | -------------------- 33 | 34 | **Hardware:** 35 | 36 | .. todo:: Add links to any specific hardware product page(s), or category page(s). Use unordered list & hyperlink rST 37 | inline format: "* `Link Text `_" 38 | 39 | **Software and Dependencies:** 40 | 41 | * Adafruit CircuitPython firmware for the supported boards: 42 | https://github.com/adafruit/circuitpython/releases 43 | 44 | .. todo:: Uncomment or remove the Bus Device and/or the Register library dependencies based on the library's use of either. 45 | 46 | # * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice 47 | # * Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register 48 | """ 49 | 50 | __version__ = "0.0.0-auto.0" 51 | __repo__ = "https://github.com/wallarug/CircuitPython_MPU9250.git" 52 | 53 | import time 54 | try: 55 | import struct 56 | except ImportError: 57 | import ustruct as struct 58 | 59 | # not required 60 | from adafruit_register.i2c_bit import RWBit 61 | from adafruit_register.i2c_bits import RWBits 62 | from adafruit_register.i2c_struct import UnaryStruct, ROUnaryStruct 63 | from adafruit_register.i2c_struct_array import StructArray 64 | 65 | # required 66 | import adafruit_bus_device.i2c_device as i2c_device 67 | import adafruit_bus_device.spi_device as spi_device 68 | from micropython import const 69 | 70 | 71 | # Internal constants and register values: 72 | # pylint: disable=bad-whitespace 73 | _MPU9250_DEFAULT_ADDRESS = const(0x69) # MPU9250 default i2c address 74 | _MPU9250_DEVICE_ID = const(0x71) # MPU9250 WHO_AM_I value 75 | 76 | _MPU9250_INT_PIN_CFG = const(0x37) # I2C Bypass enable configuration 77 | _MPU9250_INT_ENABLE = const(0x38) # Interrupt Enable 78 | _MPU9250_INT_STATUS = const(0x3A) # Interrupt Status 79 | 80 | _MPU9250_I2C_MST_CTRL = const(0x24) # 81 | _MPU9250_WHO_AM_I = const(0x75) # Device ID register 82 | 83 | _MPU6500_DEFAULT_ADDRESS = const(0x69) # MPU6500 default i2c address 84 | _MPU6500_DEVICE_ID = const(0x71) # MPU9250 WHO_AM_I value 85 | 86 | _MPU6500_SELF_TEST_X = const(0x0D) # Self test factory calibrated values register 87 | _MPU6500_SELF_TEST_Y = const(0x0E) # Self test factory calibrated values register 88 | _MPU6500_SELF_TEST_Z = const(0x0F) # Self test factory calibrated values register 89 | _MPU6500_SELF_TEST_A = const(0x10) # Self test factory calibrated values register 90 | _MPU6500_SMPLRT_DIV = const(0x19) # sample rate divisor register 91 | _MPU6500_CONFIG = const(0x1A) # General configuration register 92 | _MPU6500_GYRO_CONFIG = const(0x1B) # Gyro specfic configuration register 93 | _MPU6500_ACCEL_CONFIG = const(0x1C) # Accelerometer specific configration register 94 | _MPU6500_ACCEL_CONFIG2 = const(0x1D) # Accelerometer config register 95 | _MPU6500_INT_PIN_CONFIG = const(0x37) # Interrupt pin configuration register 96 | _MPU6500_INT_PIN_ENABLE = const(0x38) # Interrupt pin enable register 97 | _MPU6500_ACCEL_OUT = const(0x3B) # base address for sensor data reads 98 | _MPU6500_TEMP_OUT = const(0x42) # Temperature data low byte register (low: 0x41) 99 | _MPU6500_GYRO_OUT = const(0x43) # base address for sensor data reads 100 | _MPU6500_SIG_PATH_RESET = const(0x68) # register to reset sensor signal paths 101 | _MPU6500_PWR_MGMT_1 = const(0x6B) # Primary power/sleep control register 102 | _MPU6500_PWR_MGMT_2 = const(0x6C) # Secondary power/sleep control register 103 | _MPU6500_WHO_AM_I = const(0x75) # Device ID register 104 | 105 | _MPU6500_USER_CTRL = const(0x6A) # FIFO and I2C Master control register 106 | _MPU6500_I2C_SLV4_CTRL = const(0x34) # 107 | _MPU6500_I2C_MST_CTRL = const(0x24) # 108 | _MPU6500_I2C_SLV0_ADDR = const(0x25) # 109 | _MPU6500_I2C_SLV0_REG = const(0x26) # 110 | _MPU6500_I2C_SLV0_CTRL = const(0x27) # 111 | _MPU6500_I2C_SLV0_DO = const(0x63) # 112 | _MPU6500_I2C_MST_DELAY_CTRL = const(0x67) # 113 | _MPU6500_EXT_SENS_DATA_00 = const(0x49) # 114 | 115 | 116 | _AK8963_DEFAULT_ADDRESS = const(0x0c) # AK8963 default i2c address 117 | _AK8963_DEVICE_ID = const(0x48) # MPU9250 WHO_AM_I value 118 | 119 | _AK8963_WIA = const(0x00) # Device ID register 120 | _AK8963_INFO = const(0x01) # Device Information register 121 | _AK8963_ST1 = const(0x02) # Status register 1 122 | _AK8963_MAG_OUT = const(0x03) # base address for sensor data reads 123 | _AK8963_HXL = const(0x03) # 124 | _AK8963_HXH = const(0x04) 125 | _AK8963_HYL = const(0x05) 126 | _AK8963_HYH = const(0x06) 127 | _AK8963_HZL = const(0x07) 128 | _AK8963_HZH = const(0x08) 129 | _AK8963_ST2 = const(0x09) 130 | _AK8963_CNTL1 = const(0x0A) # control register 1 131 | _AK8963_CNTL2 = const(0x0B) # control register 2 132 | _AK8963_ADJUST = const(0x10) # base address for sensor adjust reads 133 | _AK8963_ASAX = const(0x10) 134 | _AK8963_ASAY = const(0x11) 135 | _AK8963_ASAZ = const(0x12) 136 | 137 | _MAGTYPE = True 138 | _XGTYPE = False 139 | 140 | STANDARD_GRAVITY = 9.80665 141 | # pylint: enable=bad-whitespace 142 | 143 | 144 | def _twos_comp(val, bits): 145 | # Convert an unsigned integer in 2's compliment form of the specified bit 146 | # length to its signed integer value and return it. 147 | if val & (1 << (bits - 1)) != 0: 148 | return val - (1 << bits) 149 | return val 150 | 151 | class MPU9250: 152 | """Driver for the MPU9250 9-DoF IMU accelerometer, magnetometer, gyroscope.""" 153 | 154 | # Class-level buffer for reading and writing data with the sensor. 155 | # This reduces memory allocations but means the code is not re-entrant or 156 | # thread safe! 157 | _BUFFER = bytearray(6) 158 | 159 | def __init__(self): 160 | # defaults 161 | Ascale = AccelRange.RANGE_2_G 162 | Gscale = GyroRange.RANGE_500_DPS 163 | Mscale = MagSensitivity.16BIT 164 | Mmode = MagMode.MEASURE_100HZ 165 | sampleRate = 0x04 166 | 167 | self._filter_bandwidth = Bandwidth.BAND_260_HZ 168 | self._gyro_range = GyroRange.RANGE_500_DPS 169 | self._accel_range = AccelRange.RANGE_2_G 170 | 171 | # soft reset & reboot accel/gyro 172 | self._write_u8(_XGTYPE, _MPU6500_PWR_MGMT_1, 0x00) 173 | time.sleep(0.01) 174 | 175 | # Check ID registers. 176 | if self._read_u8(_XGTYPE, _MPU6500_WHO_AM_I) != _MPU6500_DEVICE_ID:# or \ 177 | #self._read_u8(_MAGTYPE, _AK8963_WIA) != _AK8963_DEVICE_ID: 178 | raise RuntimeError('Could not find MPU9250, check wiring!') 179 | 180 | #self._write_u8(_XGTYPE, _MPU6500_SIG_PATH_RESET, 0x07) 181 | #time.sleep(0.01) 182 | 183 | # set stable timesource 184 | # Auto select clock source to be PLL gyroscope reference if ready else 185 | self._write_u8(_XGTYPE, _MPU6500_PWR_MGMT_1, 0x01) 186 | time.sleep(0.01) 187 | 188 | # Configure Gyro and Thermometer 189 | self._write_u8(_XGTYPE, _MPU6500_CONFIG, 0x03) 190 | 191 | # Set sample rate = gyroscope output rate/(1 + SMPLRT_DIV) 192 | self._write_u8(_XGTYPE, _MPU6500_SMPLRT_DIV, sampleRate) 193 | 194 | # Set Gyro full-scale range 195 | c = self._read_u8(_XGTYPE, _MPU6500_GYRO_CONFIG) 196 | c = c & ~0x02 # Clear Fchoice bits [1:0] 197 | c = c & ~0x18 # Clear AFS bits [4:3] 198 | c = c | Gscale << 3 # Set full scale range for the gyro 199 | self._write_u8(_XGTYPE, _MPU6500_GYRO_CONFIG, c) 200 | 201 | # Set accelerometer full-scale range configuration 202 | c = self._read_u8(_XGTYPE, _MPU6500_ACCEL_CONFIG) 203 | c = c & ~0x18 # Clear AFS bits [4:3] 204 | c = c | Ascale << 3 # Set full scale range for the accelerometer 205 | self._write_u8(_XGTYPE, _MPU6500_ACCEL_CONFIG, c) 206 | 207 | # Set accelerometer sample rate configuration 208 | c = self._read_u8(_XGTYPE, _MPU6500_ACCEL_CONFIG2) 209 | c = c & ~0x0F # Clear accel_fchoice_b (bit 3) and A_DLPFG (bits [2:0]) 210 | c = c | 0x03 # Set accelerometer rate to 1 kHz and bandwidth to 41 Hz 211 | self._write_u8(_XGTYPE, _MPU6500_ACCEL_CONFIG2, c) 212 | 213 | 214 | # Magnetometer configuration values 215 | self._offset = (143.725, 6.00244, -21.6755) 216 | self._scale = (1.07464, 0.97619, 0.956875) 217 | self._adjustment = (0,0,0) 218 | 219 | # Configure Interrupts and Bypass Enable 220 | self.initAK8963() 221 | 222 | def read_temp_raw(self): 223 | """Read the raw temperature sensor value and return it as a 12-bit 224 | signed value. If you want the temperature in nice units you probably 225 | want to use the temperature property! 226 | """ 227 | # Read temp sensor - TODO: was low bit _MPU6500_TEMP_OUT 228 | self._read_bytes(_XGTYPE, 0x80 | _MPU6500_TEMP_OUT, 2, 229 | self._BUFFER) 230 | temp = ((self._BUFFER[1] << 8) | self._BUFFER[0]) >> 4 231 | return _twos_comp(temp, 12) 232 | 233 | @property 234 | def temperature(self): 235 | """The current temperature in º C""" 236 | raw_temperature = self.read_temp_raw() 237 | temp = (raw_temperature / 333.87) + 21.0 238 | return temp 239 | 240 | 241 | def read_accel_raw(self): 242 | """Read the raw accelerometer sensor values and return it as a 243 | 3-tuple of X, Y, Z axis values that are 16-bit unsigned values. If you 244 | want the acceleration in nice units you probably want to use the 245 | accelerometer property! 246 | """ 247 | # Read the accelerometer 248 | self._read_bytes(_XGTYPE, 0x80 | _MPU6500_ACCEL_OUT, 6, 249 | self._BUFFER) 250 | raw_x, raw_y, raw_z = struct.unpack_from('>hhh', self._BUFFER[0:6]) 251 | return (raw_x, raw_y, raw_z) 252 | 253 | @property 254 | def acceleration(self): 255 | """Acceleration X, Y, and Z axis data in m/s^2""" 256 | raw_data = self.read_accel_raw() 257 | raw_x = raw_data[0] 258 | raw_y = raw_data[1] 259 | raw_z = raw_data[2] 260 | 261 | accel_range = self._accel_range 262 | accel_scale = 1 263 | if accel_range == AccelRange.RANGE_16_G: 264 | accel_scale = 2048 265 | if accel_range == AccelRange.RANGE_8_G: 266 | accel_scale = 4096 267 | if accel_range == AccelRange.RANGE_4_G: 268 | accel_scale = 8192 269 | if accel_range == AccelRange.RANGE_2_G: 270 | accel_scale = 16384 271 | 272 | # setup range dependant scaling 273 | accel_x = (raw_x / accel_scale) * STANDARD_GRAVITY 274 | accel_y = (raw_y / accel_scale) * STANDARD_GRAVITY 275 | accel_z = (raw_z / accel_scale) * STANDARD_GRAVITY 276 | 277 | return (accel_x, accel_y, accel_z) 278 | 279 | def read_gyro_raw(self): 280 | """Read the raw gyroscope sensor values and return it as a 281 | 3-tuple of X, Y, Z axis values that are 16-bit unsigned values. If you 282 | want the gyroscope in nice units you probably want to use the 283 | gyroscope property! 284 | """ 285 | # Read the gyroscope 286 | self._read_bytes(_XGTYPE, 0x80 | _MPU6500_GYRO_OUT, 6, 287 | self._BUFFER) 288 | raw_x, raw_y, raw_z = struct.unpack_from('>hhh', self._BUFFER[0:6]) 289 | return (raw_x, raw_y, raw_z) 290 | 291 | @property 292 | def gyro(self): 293 | """Gyroscope X, Y, and Z axis data in º/s""" 294 | raw_data = self.read_gyro_raw() 295 | raw_x = raw_data[0] 296 | raw_y = raw_data[1] 297 | raw_z = raw_data[2] 298 | 299 | gyro_scale = 1 300 | gyro_range = self._gyro_range 301 | if gyro_range == GyroRange.RANGE_250_DPS: 302 | gyro_scale = 131 303 | if gyro_range == GyroRange.RANGE_500_DPS: 304 | gyro_scale = 62.5 305 | if gyro_range == GyroRange.RANGE_1000_DPS: 306 | gyro_scale = 32.8 307 | if gyro_range == GyroRange.RANGE_2000_DPS: 308 | gyro_scale = 16.4 309 | 310 | # setup range dependant scaling 311 | gyro_x = (raw_x / gyro_scale) 312 | gyro_y = (raw_y / gyro_scale) 313 | gyro_z = (raw_z / gyro_scale) 314 | 315 | return (gyro_x, gyro_y, gyro_z) 316 | 317 | @property 318 | def cycle(self): 319 | """Enable or disable perodic measurement at a rate set by `cycle_rate`. 320 | If the sensor was in sleep mode, it will be waken up to cycle""" 321 | return self._cycle 322 | 323 | @cycle.setter 324 | def cycle(self, value): 325 | self.sleep = not value 326 | self._cycle = value 327 | 328 | @property 329 | def gyro_range(self): 330 | """The measurement range of all gyroscope axes. Must be a `GyroRange`""" 331 | return self._gyro_range 332 | 333 | @gyro_range.setter 334 | def gyro_range(self, value): 335 | if (value < 0) or (value > 3): 336 | raise ValueError("gyro_range must be a GyroRange") 337 | self._gyro_range = value 338 | sleep(0.01) 339 | 340 | @property 341 | def accelerometer_range(self): 342 | """The measurement range of all accelerometer axes. Must be a `Range`""" 343 | return self._accel_range 344 | 345 | @accelerometer_range.setter 346 | def accelerometer_range(self, value): 347 | if (value < 0) or (value > 3): 348 | raise ValueError("accelerometer_range must be a Range") 349 | self._accel_range = value 350 | sleep(0.01) 351 | 352 | @property 353 | def filter_bandwidth(self): 354 | """The bandwidth of the gyroscope Digital Low Pass Filter. Must be a `GyroRange`""" 355 | return self._filter_bandwidth 356 | 357 | @filter_bandwidth.setter 358 | def filter_bandwidth(self, value): 359 | if (value < 0) or (value > 6): 360 | raise ValueError("filter_bandwidth must be a Bandwidth") 361 | self._filter_bandwidth = value 362 | sleep(0.01) 363 | 364 | @property 365 | def cycle_rate(self): 366 | """The rate that measurements are taken while in `cycle` mode. Must be a `Rate`""" 367 | return self._cycle_rate 368 | 369 | @cycle_rate.setter 370 | def cycle_rate(self, value): 371 | if (value < 0) or (value > 3): 372 | raise ValueError("cycle_rate must be a Rate") 373 | self._cycle_rate = value 374 | sleep(0.01) 375 | 376 | ## MAG 377 | def read_mag_raw(self): 378 | """Read the raw magnetometer sensor values and return it as a 379 | 3-tuple of X, Y, Z axis values that are 16-bit unsigned values. If you 380 | want the magnetometer in nice units you probably want to use the 381 | magnetometer property! 382 | """ 383 | # Read the magnetometer 384 | self._read_bytes(_MAGTYPE, 0x80 | _AK8963_MAG_OUT, 6, 385 | self._BUFFER) 386 | raw_x, raw_y, raw_z = struct.unpack_from('hh", buf)[0] 248 | return struct.unpack(">hhh", self._BUFFER) 249 | 250 | def _write_u8(self, address, val): 251 | device = self.i2c 252 | with device as i2c: 253 | self._BUFFER[0] = address & 0xFF 254 | self._BUFFER[1] = val & 0xFF 255 | i2c.write(self._BUFFER, end=2) 256 | 257 | def __enter__(self): 258 | return self 259 | 260 | def __exit__(self, exception_type, exception_value, traceback): 261 | pass 262 | 263 | -------------------------------------------------------------------------------- /robohat_mpu9250/mpu9250.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2018-2019 Mika Tuupola 2 | # Copyright (c) 2019 Eike Welk 3 | # Copyright (c) 2019 Cian Byrne (wallarug) for Robotics Masters Limited 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 7 | # deal in the Software without restriction, including without limitation the 8 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9 | # sell copied 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 13 | # all 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 | 23 | 24 | """ 25 | Python I2C driver for MPU9250 9-axis motion tracking device 26 | """ 27 | 28 | # pylint: disable=import-error 29 | from robohat_mpu9250.mpu6500 import MPU6500 30 | from robohat_mpu9250.ak8963 import AK8963 31 | # pylint: enable=import-error 32 | 33 | __version__ = "1.0.1" 34 | 35 | class MPU9250: 36 | """Class which provides interface to MPU9250 9-axis motion tracking device.""" 37 | def __init__(self, mpu6500 = None, ak8963 = None): 38 | if mpu6500 is None: 39 | self.mpu6500 = MPU6500() 40 | else: 41 | self.mpu6500 = mpu6500 42 | 43 | if ak8963 is None: 44 | self.ak8963 = AK8963() 45 | else: 46 | self.ak8963 = ak8963 47 | 48 | def read_acceleration(self): 49 | """ 50 | Acceleration measured by the sensor. By default will return a 51 | 3-tuple of X, Y, Z axis values in m/s^2 as floats. To get values in g 52 | pass `accel_fs=SF_G` parameter to the MPU6500 constructor. 53 | """ 54 | return self.mpu6500.read_acceleration() 55 | 56 | def read_gyro(self): 57 | """ 58 | Gyro measured by the sensor. By default will return a 3-tuple of 59 | X, Y, Z axis values in rad/s as floats. To get values in deg/s pass 60 | `gyro_sf=SF_DEG_S` parameter to the MPU6500 constructor. 61 | """ 62 | return self.mpu6500.read_gyro() 63 | 64 | def read_magnetic(self): 65 | """ 66 | X, Y, Z axis micro-Tesla (uT) as floats. 67 | """ 68 | return self.ak8963.read_magnetic() 69 | 70 | def read_temperature(self): 71 | """ 72 | Temperature measured by the sensor. 73 | """ 74 | return self.mpu6500.read_temperature() 75 | 76 | def read_whoami(self): 77 | return self.mpu6500.read_whoami() 78 | 79 | def __enter__(self): 80 | return self 81 | 82 | def __exit__(self, exception_type, exception_value, traceback): 83 | pass 84 | -------------------------------------------------------------------------------- /roboticsmasters_ak8963.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2020 Cian Byrne for Robotics Masters Limited 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 13 | # all 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 21 | # THE SOFTWARE. 22 | """ 23 | `roboticsmasters_mpu9250` 24 | ================================================================================ 25 | 26 | CircuitPython helper library for MPU9250 9-axis IMU 27 | 28 | 29 | * Author(s): Cian Byrne 30 | 31 | Implementation Notes 32 | -------------------- 33 | 34 | **Hardware:** 35 | 36 | .. todo:: Add links to any specific hardware product page(s), or category page(s). Use unordered list & hyperlink rST 37 | inline format: "* `Link Text `_" 38 | 39 | **Software and Dependencies:** 40 | 41 | * Adafruit CircuitPython firmware for the supported boards: 42 | https://github.com/adafruit/circuitpython/releases 43 | 44 | .. todo:: Uncomment or remove the Bus Device and/or the Register library dependencies based on the library's use of either. 45 | 46 | # * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice 47 | # * Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register 48 | """ 49 | 50 | from time import sleep 51 | from adafruit_register.i2c_struct import UnaryStruct, ROUnaryStruct 52 | from adafruit_register.i2c_struct_array import StructArray 53 | from adafruit_register.i2c_bit import RWBit 54 | from adafruit_register.i2c_bits import RWBits 55 | import adafruit_bus_device.i2c_device as i2c_device 56 | 57 | try: 58 | import struct 59 | except ImportError: 60 | import ustruct as struct 61 | from micropython import const 62 | 63 | __version__ = "0.0.0-auto.0" 64 | __repo__ = "https://github.com/wallarug/CircuitPython_MPU9250.git" 65 | 66 | # pylint: disable=bad-whitespace 67 | _AK8963_DEFAULT_ADDRESS = 0x0c # AK8963 default i2c address 68 | _AK8963_DEVICE_ID = 0x48 # MPU9250 WHO_AM_I value 69 | 70 | _AK8963_WIA = 0x00 # Device ID register 71 | _AK8963_INFO = 0x01 # Device Information register 72 | _AK8963_ST1 = 0x02 # Status register 1 73 | _AK8963_MAG_OUT = 0x03 # base address for sensor data reads 74 | _AK8963_HXL = 0x03 # 75 | _AK8963_HXH = 0x04 76 | _AK8963_HYL = 0x05 77 | _AK8963_HYH = 0x06 78 | _AK8963_HZL = 0x07 79 | _AK8963_HZH = 0x08 80 | _AK8963_ST2 = 0x09 81 | _AK8963_CNTL1 = 0x0A # control register 1 82 | _AK8963_CNTL2 = 0x0B # control register 2 83 | _AK8963_ADJUST = 0x10 # base address for sensor adjust reads 84 | _AK8963_ASAX = 0x10 85 | _AK8963_ASAY = 0x11 86 | _AK8963_ASAZ = 0x12 87 | 88 | 89 | class Sensitivity: 90 | """Allowed values for `range`. 91 | 92 | - ``Rate.CYCLE_1_25_HZ`` 93 | - ``Rate.CYCLE_5_HZ`` 94 | - ``Rate.CYCLE_20_HZ`` 95 | - ``Rate.CYCLE_40_HZ`` 96 | """ 97 | SENSE_14BIT = 0 98 | SENSE_16BIT = 1 99 | 100 | class Mode: 101 | """Allowed values for `mode` setting 102 | 103 | - ``Mode.MODE_POWERDOWN`` 104 | - ``Mode.MODE_SINGLE`` 105 | - ``Mode.MODE_CONT1`` 106 | - ``Mode.MODE_CONT2`` 107 | - ``Mode.MODE_EXT_TRIG`` 108 | - ``Mode.MODE_SELFTEST`` 109 | - ``Mode.MODE_FUSE`` 110 | 111 | """ 112 | POWERDOWN = 0 #0b0000 113 | MEASURE_SINGLE = 1 #0b0001 114 | MEASURE_8HZ = 2 #0b0010 # 8 Hz (mode 1) 115 | EXT_TRIG = 4 #0b0100 116 | MEASURE_100HZ = 5 #0b0110 # 100 Hz (mode 2) 117 | SELFTEST = 8 #0b1000 118 | FUSE = 15 #0b1111 119 | 120 | def _twos_comp(val, bits): 121 | # Convert an unsigned integer in 2's compliment form of the specified bit 122 | # length to its signed integer value and return it. 123 | if val & (1 << (bits - 1)) != 0: 124 | return val - (1 << bits) 125 | return val 126 | 127 | class AK8963: 128 | """Driver for the AK8963 magnetometer. 129 | :param ~busio.I2C i2c_bus: The I2C bus the AK8963 is connected to. 130 | :param address: The I2C slave address of the sensor 131 | """ 132 | def __init__(self, i2c_bus, address=_AK8963_DEFAULT_ADDRESS): 133 | self.i2c_device = i2c_device.I2CDevice(i2c_bus, address) 134 | 135 | if self._device_id != _AK8963_DEVICE_ID: 136 | raise RuntimeError("Failed to find AKM8963 - check your wiring!") 137 | 138 | self.reset() 139 | 140 | self._mode = Mode.FUSE 141 | raw_adjustment = self._raw_adjustment_data 142 | self. _mode = Mode.POWERDOWN 143 | sleep(0.100) 144 | 145 | asax = raw_adjustment[0][0] 146 | asay = raw_adjustment[1][0] 147 | asaz = raw_adjustment[2][0] 148 | 149 | print(asax, asay, asaz) 150 | 151 | self._offset = (143.725, 6.00244, -21.6755) 152 | self._scale = (1.07464, 0.97619, 0.956875) 153 | self._adjustment = ( 154 | ((asax - 128.0) / 256.0) + 1.0, 155 | ((asay - 128.0) / 256.0) + 1.0, 156 | ((asaz - 128.0) / 256.0) + 1.0 157 | ) 158 | 159 | print(self._adjustment) 160 | 161 | self._mag_range = Sensitivity.SENSE_16BIT 162 | self._mode = Mode.MEASURE_8HZ 163 | sleep(0.100) 164 | 165 | 166 | def reset(self): 167 | """Reinitialize the sensor""" 168 | self._reset = True 169 | while self._reset is True: 170 | sleep(0.001) 171 | sleep(0.100) 172 | 173 | def start(self): 174 | """Start Up and Initalise the sensor""" 175 | _mag_range = Sensitivity.SENSE_16BIT 176 | _mode = Mode.MEASURE_8HZ 177 | 178 | 179 | _device_id = ROUnaryStruct(_AK8963_WIA, ">B") 180 | _reset = RWBit(_AK8963_CNTL2, 0, 1) 181 | 182 | _raw_magnet_data = StructArray(_AK8963_MAG_OUT, "B") 189 | 190 | 191 | @property 192 | def magnetic(self): 193 | """The magnetometer X, Y, Z axis values as a 3-tuple of 194 | gauss values. 195 | """ 196 | raw_data = self._raw_magnet_data 197 | #raw_x = _twos_comp(raw_data[0][0], 16) 198 | #raw_y = _twos_comp(raw_data[1][0], 16) 199 | #raw_z = _twos_comp(raw_data[2][0], 16) 200 | raw_x = raw_data[0][0] 201 | raw_y = raw_data[1][0] 202 | raw_z = raw_data[2][0] 203 | 204 | print(raw_x, raw_y, raw_z) 205 | 206 | self._status # Enable updating readings again 207 | 208 | # Apply factory axial sensitivy adjustments 209 | #raw_x *= self._adjustment[0] 210 | #raw_y *= self._adjustment[1] 211 | #raw_z *= self._adjustment[2] 212 | 213 | # Apply output scale determined in constructor 214 | mag_range = self._mag_range 215 | mag_scale = 1 216 | if mag_range == Sensitivity.SENSE_16BIT: 217 | #mag_scale = 0.15 - for uT (micro-tesla) 218 | mag_scale = 1.499389499 # for mG (milliGauss) calc: 10.*4912./32760.0 219 | if mag_range == Sensitivity.SENSE_14BIT: 220 | #mag_scale = 0.6 - for uT (mico-tesla) 221 | mag_scale = 5.997557998 # for mG (millGauss) calc: 10.*4912./8190.0 222 | 223 | # setup range dependant scaling and offsets 224 | #mag_x = ((raw_x / mag_scale) - self._offset[0]) * self._scale[0] 225 | #mag_y = ((raw_y / mag_scale) - self._offset[1]) * self._scale[1] 226 | #mag_z = ((raw_z / mag_scale) - self._offset[2]) * self._scale[2] 227 | mag_x = (raw_x * mag_scale * self._scale[0]) - self._offset[0] 228 | mag_y = (raw_y * mag_scale * self._scale[1]) - self._offset[1] 229 | mag_z = (raw_z * mag_scale * self._scale[2]) - self._offset[1] 230 | 231 | return (mag_x, mag_y, mag_z) 232 | 233 | def calibrate(self, count=256, delay=0.200): 234 | """ 235 | Calibrate the magnetometer. 236 | 237 | The magnetometer needs to be turned in all possible directions 238 | during the callibration process. Ideally each axis would once 239 | line up with the magnetic field. 240 | 241 | count: int 242 | Number of magnetometer readings that are taken for the calibration. 243 | 244 | delay: float 245 | Delay between the magntometer readings in seconds. 246 | """ 247 | print("Starting Calibration.") 248 | print("The magnetometer needs to be turned in all possible directions \ 249 | during the callibration process. Ideally each axis would once \ 250 | line up with the magnetic field.") 251 | 252 | self._offset = (0, 0, 0) 253 | self._scale = (1, 1, 1) 254 | 255 | raw_data = self._raw_magnet_data 256 | raw_x = raw_data[0][0] 257 | raw_y = raw_data[1][0] 258 | raw_z = raw_data[2][0] 259 | self._status # Enable updating readings again 260 | 261 | minx = maxx = raw_x 262 | miny = maxy = raw_y 263 | minz = maxz = raw_z 264 | 265 | while count: 266 | sleep(delay) 267 | 268 | raw_data = self._raw_magnet_data 269 | print(raw_x, raw_y, raw_z) 270 | raw_x = raw_data[0][0] 271 | raw_y = raw_data[1][0] 272 | raw_z = raw_data[2][0] 273 | self._status # Enable updating readings again 274 | 275 | minx = min(minx, raw_x) 276 | maxx = max(maxx, raw_x) 277 | miny = min(miny, raw_y) 278 | maxy = max(maxy, raw_y) 279 | minz = min(minz, raw_z) 280 | maxz = max(maxz, raw_z) 281 | 282 | count -= 1 283 | 284 | # Hard iron correction 285 | offset_x = (maxx + minx) / 2 286 | offset_y = (maxy + miny) / 2 287 | offset_z = (maxz + minz) / 2 288 | 289 | self._offset = (offset_x, offset_y, offset_z) 290 | 291 | print("+++++++++++") 292 | print("Hard Iron Offset Values:") 293 | print(self._offset) 294 | 295 | # Soft iron correction 296 | avg_delta_x = (maxx - minx) / 2 297 | avg_delta_y = (maxy - miny) / 2 298 | avg_delta_z = (maxz - minz) / 2 299 | 300 | avg_delta = (avg_delta_x + avg_delta_y + avg_delta_z) / 3 301 | 302 | scale_x = avg_delta / avg_delta_x 303 | scale_y = avg_delta / avg_delta_y 304 | scale_z = avg_delta / avg_delta_z 305 | 306 | self._scale = (scale_x, scale_y, scale_z) 307 | 308 | print("Soft iron values") 309 | print(self._scale) 310 | 311 | -------------------------------------------------------------------------------- /roboticsmasters_mpu6500.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2020 Cian Byrne for Robotics Masters Limited 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 13 | # all 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 21 | # THE SOFTWARE. 22 | """ 23 | `roboticsmasters_mpu9250` 24 | ================================================================================ 25 | 26 | CircuitPython helper library for MPU9250 9-axis IMU 27 | 28 | 29 | * Author(s): Cian Byrne 30 | 31 | Implementation Notes 32 | -------------------- 33 | 34 | **Hardware:** 35 | 36 | .. todo:: Add links to any specific hardware product page(s), or category page(s). Use unordered list & hyperlink rST 37 | inline format: "* `Link Text `_" 38 | 39 | **Software and Dependencies:** 40 | 41 | * Adafruit CircuitPython firmware for the supported boards: 42 | https://github.com/adafruit/circuitpython/releases 43 | 44 | .. todo:: Uncomment or remove the Bus Device and/or the Register library dependencies based on the library's use of either. 45 | 46 | # * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice 47 | # * Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register 48 | """ 49 | 50 | from time import sleep 51 | from adafruit_register.i2c_struct import UnaryStruct, ROUnaryStruct 52 | from adafruit_register.i2c_struct_array import StructArray 53 | from adafruit_register.i2c_bit import RWBit 54 | from adafruit_register.i2c_bits import RWBits 55 | import adafruit_bus_device.i2c_device as i2c_device 56 | 57 | try: 58 | import struct 59 | except ImportError: 60 | import ustruct as struct 61 | from micropython import const 62 | 63 | __version__ = "0.0.0-auto.0" 64 | __repo__ = "https://github.com/wallarug/CircuitPython_MPU9250.git" 65 | 66 | # pylint: disable=bad-whitespace 67 | _MPU6500_DEFAULT_ADDRESS = 0x69 # MPU6500 default i2c address 68 | _MPU6500_DEVICE_ID = 0x71 # MPU9250 WHO_AM_I value 69 | 70 | _MPU6500_SELF_TEST_X = 0x0D # Self test factory calibrated values register 71 | _MPU6500_SELF_TEST_Y = 0x0E # Self test factory calibrated values register 72 | _MPU6500_SELF_TEST_Z = 0x0F # Self test factory calibrated values register 73 | _MPU6500_SELF_TEST_A = 0x10 # Self test factory calibrated values register 74 | _MPU6500_SMPLRT_DIV = 0x19 # sample rate divisor register 75 | _MPU6500_CONFIG = 0x1A # General configuration register 76 | _MPU6500_GYRO_CONFIG = 0x1B # Gyro specfic configuration register 77 | _MPU6500_ACCEL_CONFIG = 0x1C # Accelerometer specific configration register 78 | _MPU6500_INT_PIN_CONFIG = 0x37 # Interrupt pin configuration register 79 | _MPU6500_ACCEL_OUT = 0x3B # base address for sensor data reads 80 | _MPU6500_TEMP_OUT = 0x41 # Temperature data high byte register 81 | _MPU6500_GYRO_OUT = 0x43 # base address for sensor data reads 82 | _MPU6500_SIG_PATH_RESET = 0x68 # register to reset sensor signal paths 83 | _MPU6500_USER_CTRL = 0x6A # FIFO and I2C Master control register 84 | _MPU6500_PWR_MGMT_1 = 0x6B # Primary power/sleep control register 85 | _MPU6500_PWR_MGMT_2 = 0x6C # Secondary power/sleep control register 86 | _MPU6500_WHO_AM_I = 0x75 # Device ID register 87 | 88 | STANDARD_GRAVITY = 9.80665 89 | # pylint: enable=bad-whitespace 90 | 91 | class Range: # pylint: disable=too-few-public-methods 92 | """Allowed values for `accelerometer_range`. 93 | - ``Range.RANGE_2_G`` 94 | - ``Range.RANGE_4_G`` 95 | - ``Range.RANGE_8_G`` 96 | - ``Range.RANGE_16_G`` 97 | """ 98 | RANGE_2_G = 0 # +/- 2g (default value) 99 | RANGE_4_G = 1 # +/- 4g 100 | RANGE_8_G = 2 # +/- 8g 101 | RANGE_16_G = 3 # +/- 16g 102 | 103 | class GyroRange: # pylint: disable=too-few-public-methods 104 | """Allowed values for `gyro_range`. 105 | - ``GyroRange.RANGE_250_DPS`` 106 | - ``GyroRange.RANGE_500_DPS`` 107 | - ``GyroRange.RANGE_1000_DPS`` 108 | - ``GyroRange.RANGE_2000_DPS`` 109 | """ 110 | RANGE_250_DPS = 0 # +/- 250 deg/s (default value) 111 | RANGE_500_DPS = 1 # +/- 500 deg/s 112 | RANGE_1000_DPS = 2 # +/- 1000 deg/s 113 | RANGE_2000_DPS = 3 # +/- 2000 deg/s 114 | 115 | class Bandwidth: # pylint: disable=too-few-public-methods 116 | """Allowed values for `filter_bandwidth`. 117 | - ``Bandwidth.BAND_260_HZ`` 118 | - ``Bandwidth.BAND_184_HZ`` 119 | - ``Bandwidth.BAND_94_HZ`` 120 | - ``Bandwidth.BAND_44_HZ`` 121 | - ``Bandwidth.BAND_21_HZ`` 122 | - ``Bandwidth.BAND_10_HZ`` 123 | - ``Bandwidth.BAND_5_HZ`` 124 | """ 125 | BAND_260_HZ = 0 # Docs imply this disables the filter 126 | BAND_184_HZ = 1 # 184 Hz 127 | BAND_94_HZ = 2 # 94 Hz 128 | BAND_44_HZ = 3 # 44 Hz 129 | BAND_21_HZ = 4 # 21 Hz 130 | BAND_10_HZ = 5 # 10 Hz 131 | BAND_5_HZ = 6 # 5 Hz 132 | 133 | class Rate: # pylint: disable=too-few-public-methods 134 | """Allowed values for `cycle_rate`. 135 | - ``Rate.CYCLE_1_25_HZ`` 136 | - ``Rate.CYCLE_5_HZ`` 137 | - ``Rate.CYCLE_20_HZ`` 138 | - ``Rate.CYCLE_40_HZ`` 139 | """ 140 | CYCLE_1_25_HZ = 0 # 1.25 Hz 141 | CYCLE_5_HZ = 1 # 5 Hz 142 | CYCLE_20_HZ = 2 # 20 Hz 143 | CYCLE_40_HZ = 3 # 40 Hz 144 | 145 | class MPU6500: 146 | """Driver for the MPU6050 6-DoF accelerometer and gyroscope. 147 | :param ~busio.I2C i2c_bus: The I2C bus the MPU6050 is connected to. 148 | :param address: The I2C slave address of the sensor 149 | """ 150 | def __init__(self, i2c_bus, address=_MPU6500_DEFAULT_ADDRESS): 151 | self.i2c_device = i2c_device.I2CDevice(i2c_bus, address) 152 | 153 | if self._device_id != _MPU6500_DEVICE_ID: 154 | raise RuntimeError("Failed to find MPU6500 - check your wiring!") 155 | 156 | self.reset() 157 | 158 | self._sample_rate_divisor = 0 159 | self._filter_bandwidth = Bandwidth.BAND_260_HZ 160 | self._gyro_range = GyroRange.RANGE_500_DPS 161 | self._accel_range = Range.RANGE_2_G 162 | sleep(0.100) 163 | self._clock_source = 1 # set to use gyro x-axis as reference 164 | sleep(0.100) 165 | self.sleep = False 166 | sleep(0.010) 167 | 168 | 169 | def reset(self): 170 | """Reinitialize the sensor""" 171 | self._reset = True 172 | while self._reset is True: 173 | sleep(0.001) 174 | sleep(0.100) 175 | 176 | _signal_path_reset = 0b111 # reset all sensors 177 | sleep(0.100) 178 | 179 | _clock_source = RWBits(3, _MPU6500_PWR_MGMT_1, 0) 180 | _device_id = ROUnaryStruct(_MPU6500_WHO_AM_I, ">B") 181 | 182 | _reset = RWBit(_MPU6500_PWR_MGMT_1, 7, 1) 183 | _signal_path_reset = RWBits(3, _MPU6500_SIG_PATH_RESET, 3) 184 | 185 | _gyro_range = RWBits(2, _MPU6500_GYRO_CONFIG, 3) 186 | _accel_range = RWBits(2, _MPU6500_ACCEL_CONFIG, 3) 187 | 188 | _filter_bandwidth = RWBits(2, _MPU6500_CONFIG, 3) 189 | 190 | _raw_accel_data = StructArray(_MPU6500_ACCEL_OUT, ">h", 3) 191 | _raw_gyro_data = StructArray(_MPU6500_GYRO_OUT, ">h", 3) 192 | _raw_temp_data = ROUnaryStruct(_MPU6500_TEMP_OUT, ">h") 193 | 194 | _cycle = RWBit(_MPU6500_PWR_MGMT_1, 5) 195 | _cycle_rate = RWBits(2, _MPU6500_PWR_MGMT_2, 6, 1) 196 | 197 | sleep = RWBit(_MPU6500_PWR_MGMT_1, 6, 1) 198 | """Shuts down the accelerometers and gyroscopes, saving power. No new data will 199 | be recorded until the sensor is taken out of sleep by setting to `False`""" 200 | sample_rate_divisor = UnaryStruct(_MPU6500_SMPLRT_DIV, ">B") 201 | """The sample rate divisor. See the datasheet for additional detail""" 202 | 203 | @property 204 | def temperature(self): 205 | """The current temperature in º C""" 206 | raw_temperature = self._raw_temp_data 207 | #temp = (raw_temperature + 12412.0) / 340.0 208 | #temp = (raw_temperature / 340.0) + 36.53 209 | temp = (raw_temperature / 333.87) + 21.0 210 | return temp 211 | 212 | @property 213 | def acceleration(self): 214 | """Acceleration X, Y, and Z axis data in m/s^2""" 215 | raw_data = self._raw_accel_data 216 | raw_x = raw_data[0][0] 217 | raw_y = raw_data[1][0] 218 | raw_z = raw_data[2][0] 219 | 220 | accel_range = self._accel_range 221 | accel_scale = 1 222 | if accel_range == Range.RANGE_16_G: 223 | accel_scale = 2048 224 | if accel_range == Range.RANGE_8_G: 225 | accel_scale = 4096 226 | if accel_range == Range.RANGE_4_G: 227 | accel_scale = 8192 228 | if accel_range == Range.RANGE_2_G: 229 | accel_scale = 16384 230 | 231 | # setup range dependant scaling 232 | accel_x = (raw_x / accel_scale) * STANDARD_GRAVITY 233 | accel_y = (raw_y / accel_scale) * STANDARD_GRAVITY 234 | accel_z = (raw_z / accel_scale) * STANDARD_GRAVITY 235 | 236 | return (accel_x, accel_y, accel_z) 237 | 238 | @property 239 | def gyro(self): 240 | """Gyroscope X, Y, and Z axis data in º/s""" 241 | raw_data = self._raw_gyro_data 242 | raw_x = raw_data[0][0] 243 | raw_y = raw_data[1][0] 244 | raw_z = raw_data[2][0] 245 | 246 | gyro_scale = 1 247 | gyro_range = self._gyro_range 248 | if gyro_range == GyroRange.RANGE_250_DPS: 249 | gyro_scale = 131 250 | if gyro_range == GyroRange.RANGE_500_DPS: 251 | gyro_scale = 62.5 252 | if gyro_range == GyroRange.RANGE_1000_DPS: 253 | gyro_scale = 32.8 254 | if gyro_range == GyroRange.RANGE_2000_DPS: 255 | gyro_scale = 16.4 256 | 257 | # setup range dependant scaling 258 | gyro_x = (raw_x / gyro_scale) 259 | gyro_y = (raw_y / gyro_scale) 260 | gyro_z = (raw_z / gyro_scale) 261 | 262 | return (gyro_x, gyro_y, gyro_z) 263 | 264 | @property 265 | def cycle(self): 266 | """Enable or disable perodic measurement at a rate set by `cycle_rate`. 267 | If the sensor was in sleep mode, it will be waken up to cycle""" 268 | return self._cycle 269 | 270 | @cycle.setter 271 | def cycle(self, value): 272 | self.sleep = not value 273 | self._cycle = value 274 | 275 | @property 276 | def gyro_range(self): 277 | """The measurement range of all gyroscope axes. Must be a `GyroRange`""" 278 | return self._gyro_range 279 | 280 | @gyro_range.setter 281 | def gyro_range(self, value): 282 | if (value < 0) or (value > 3): 283 | raise ValueError("gyro_range must be a GyroRange") 284 | self._gyro_range = value 285 | sleep(0.01) 286 | 287 | @property 288 | def accelerometer_range(self): 289 | """The measurement range of all accelerometer axes. Must be a `Range`""" 290 | return self._accel_range 291 | 292 | @accelerometer_range.setter 293 | def accelerometer_range(self, value): 294 | if (value < 0) or (value > 3): 295 | raise ValueError("accelerometer_range must be a Range") 296 | self._accel_range = value 297 | sleep(0.01) 298 | 299 | @property 300 | def filter_bandwidth(self): 301 | """The bandwidth of the gyroscope Digital Low Pass Filter. Must be a `GyroRange`""" 302 | return self._filter_bandwidth 303 | 304 | @filter_bandwidth.setter 305 | def filter_bandwidth(self, value): 306 | if (value < 0) or (value > 6): 307 | raise ValueError("filter_bandwidth must be a Bandwidth") 308 | self._filter_bandwidth = value 309 | sleep(0.01) 310 | 311 | @property 312 | def cycle_rate(self): 313 | """The rate that measurements are taken while in `cycle` mode. Must be a `Rate`""" 314 | return self._cycle_rate 315 | 316 | @cycle_rate.setter 317 | def cycle_rate(self, value): 318 | if (value < 0) or (value > 3): 319 | raise ValueError("cycle_rate must be a Rate") 320 | self._cycle_rate = value 321 | sleep(0.01) 322 | -------------------------------------------------------------------------------- /roboticsmasters_mpu9250.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2020 Cian Byrne for Robotics Masters Limited 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 13 | # all 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 21 | # THE SOFTWARE. 22 | """ 23 | `roboticsmasters_mpu9250` 24 | ================================================================================ 25 | 26 | CircuitPython helper library for MPU9250 9-axis IMU 27 | 28 | 29 | * Author(s): Cian Byrne 30 | 31 | Implementation Notes 32 | -------------------- 33 | 34 | **Hardware:** 35 | 36 | .. todo:: Add links to any specific hardware product page(s), or category page(s). Use unordered list & hyperlink rST 37 | inline format: "* `Link Text `_" 38 | 39 | **Software and Dependencies:** 40 | 41 | * Adafruit CircuitPython firmware for the supported boards: 42 | https://github.com/adafruit/circuitpython/releases 43 | 44 | .. todo:: Uncomment or remove the Bus Device and/or the Register library dependencies based on the library's use of either. 45 | 46 | # * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice 47 | # * Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register 48 | """ 49 | 50 | from time import sleep 51 | import adafruit_bus_device.i2c_device as i2c_device 52 | from adafruit_register.i2c_bit import RWBit 53 | from adafruit_register.i2c_struct import ROUnaryStruct 54 | 55 | from roboticsmasters_mpu6500 import MPU6500 56 | from roboticsmasters_ak8963 import AK8963 57 | 58 | try: 59 | import struct 60 | except ImportError: 61 | import ustruct as struct 62 | from micropython import const 63 | 64 | 65 | __version__ = "0.0.0-auto.0" 66 | __repo__ = "https://github.com/wallarug/CircuitPython_MPU9250.git" 67 | 68 | # pylint: disable=bad-whitespace 69 | _MPU9250_DEFAULT_ADDRESS = 0x69 # MPU9250 default i2c address 70 | _MPU9250_DEVICE_ID = 0x71 # MPU9250 WHO_AM_I value 71 | 72 | _MPU6500_DEFAULT_ADDRESS = 0x69 # MPU6500 default i2c address 73 | _AK8963_DEFAULT_ADDRESS = 0x0c # AK8963 default i2c address 74 | 75 | 76 | _MPU9250_INT_PIN_CFG = 0x37 # I2C Bypass enable configuration 77 | _MPU9250_INT_ENABLE = 0x38 # Interrupt Enable 78 | _MPU9250_INT_STATUS = 0x3A # Interrupt Status 79 | 80 | _MPU9250_I2C_MST_CTRL = 0x24 # 81 | _MPU9250_WHO_AM_I = 0x75 # Device ID register 82 | 83 | # pylint: enable=bad-whitespace 84 | 85 | class MPU9250: 86 | """Driver for the MPU9250 9-DoF IMU. 87 | :param ~busio.I2C i2c_bus: The I2C bus the MPU9250 is connected to. 88 | :param address: The I2C slave address of the sensor 89 | """ 90 | 91 | def __init__(self, i2c_bus, 92 | mpu_addr=_MPU6500_DEFAULT_ADDRESS, 93 | akm_addr=_AK8963_DEFAULT_ADDRESS): 94 | self.i2c_device = i2c_device.I2CDevice(i2c_bus, mpu_addr) 95 | 96 | if self._device_id != _MPU9250_DEVICE_ID: 97 | raise RuntimeError("Failed to find MPU9250 - check your wiring!") 98 | 99 | self._mpu = MPU6500(i2c_bus, mpu_addr) 100 | 101 | self._bypass = 1 102 | self._ready = 1 103 | sleep(0.100) 104 | 105 | self._akm = AK8963(i2c_bus, akm_addr) 106 | 107 | def reset(self): 108 | """Reinitialize the sensor""" 109 | self._mpu.reset() 110 | self._akm.reset() 111 | 112 | _device_id = ROUnaryStruct(_MPU9250_WHO_AM_I, ">B") 113 | _bypass = RWBit(_MPU9250_INT_PIN_CFG, 1, 1) 114 | _ready = RWBit(_MPU9250_INT_ENABLE, 0, 1) 115 | 116 | @property 117 | def temperature(self): 118 | """The current temperature in º C""" 119 | return self._mpu.temperature 120 | 121 | @property 122 | def acceleration(self): 123 | """Acceleration X, Y, and Z axis data in m/s^2""" 124 | return self._mpu.acceleration 125 | 126 | @property 127 | def gyro(self): 128 | """Gyroscope X, Y, and Z axis data in º/s""" 129 | return self._mpu.gyro 130 | 131 | @property 132 | def magnetic(self): 133 | """Magnetometer X, Y and Z asix data in micro-Tesla (uT)""" 134 | return self._akm.magnetic 135 | 136 | def cal_mag(self): 137 | return self._akm.calibrate() 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.md 3 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | """A setuptools based setup module. 2 | 3 | See: 4 | https://packaging.python.org/en/latest/distributing.html 5 | https://github.com/pypa/sampleproject 6 | """ 7 | 8 | from setuptools import setup, find_packages 9 | # To use a consistent encoding 10 | from codecs import open 11 | from os import path 12 | 13 | here = path.abspath(path.dirname(__file__)) 14 | 15 | # Get the long description from the README file 16 | with open(path.join(here, 'README.rst'), encoding='utf-8') as f: 17 | long_description = f.read() 18 | 19 | setup( 20 | name='adafruit-circuitpython-mpu9250', 21 | 22 | use_scm_version=True, 23 | setup_requires=['setuptools_scm'], 24 | 25 | description='CircuitPython helper library for MPU9250 9-axis IMU', 26 | long_description=long_description, 27 | long_description_content_type='text/x-rst', 28 | 29 | # The project's main homepage. 30 | url='https://github.com/adafruit/Adafruit_CircuitPython_MPU9250', 31 | 32 | # Author details 33 | author='Adafruit Industries', 34 | author_email='circuitpython@adafruit.com', 35 | 36 | install_requires=[ 37 | 'Adafruit-Blinka', 38 | 'adafruit-circuitpython-busdevice', 39 | 'adafruit-circuitpython-register' 40 | ], 41 | 42 | # Choose your license 43 | license='MIT', 44 | 45 | # See https://pypi.python.org/pypi?%3Aaction=list_classifiers 46 | classifiers=[ 47 | 'Development Status :: 3 - Alpha', 48 | 'Intended Audience :: Developers', 49 | 'Topic :: Software Development :: Libraries', 50 | 'Topic :: System :: Hardware', 51 | 'License :: OSI Approved :: MIT License', 52 | 'Programming Language :: Python :: 3', 53 | 'Programming Language :: Python :: 3.4', 54 | 'Programming Language :: Python :: 3.5', 55 | ], 56 | 57 | # What does your project relate to? 58 | keywords='adafruit blinka circuitpython micropython mpu9250 imu movement', 59 | 60 | # You can just specify the packages manually here if your project is 61 | # simple. Or you can use find_packages(). 62 | # TODO: IF LIBRARY FILES ARE A PACKAGE FOLDER, 63 | # CHANGE `py_modules=['...']` TO `packages=['...']` 64 | py_modules=['roboticsmasters_mpu9250'], 65 | ) 66 | -------------------------------------------------------------------------------- /tests/code.py: -------------------------------------------------------------------------------- 1 | ### Comparison between drivers 2 | ## 3 | import time 4 | import board 5 | import busio 6 | 7 | ### Adafruit MPU6050 (similar chip) 8 | import adafruit_mpu6050 9 | 10 | ### RM Forked Driver 11 | from robohat_mpu9250.mpu9250 import MPU9250 as RM9250 12 | from robohat_mpu9250.mpu6500 import MPU6500 as RM6500 13 | from robohat_mpu9250.ak8963 import AK8963 as RM8963 14 | 15 | ### RM CircuitPython Driver 16 | import roboticsmasters_mpu6500 17 | import roboticsmasters_mpu9250 18 | 19 | ### i2c 20 | i2c = busio.I2C(board.SCL, board.SDA) 21 | 22 | ### adafruit driver 23 | mpu = adafruit_mpu6050.MPU6050(i2c, address=0x69) 24 | ## 25 | ### RM Forked 26 | #rm_mpu = RM6500(i2c, address=0x69) 27 | #rm_ak = RM8963(i2c) 28 | #sensor = RM9250(rm_mpu, rm_ak) 29 | 30 | ### New Driver 31 | #nmpu = roboticsmasters_mpu6500.MPU6500(i2c, address=0x69) 32 | npmu = roboticsmasters_mpu9250.MPU9250(i2c) 33 | 34 | time.sleep(1) 35 | 36 | ##while True: 37 | ## print("=============") 38 | ## print("Acceleration:") 39 | ## print("X:%.2f, Y: %.2f, Z: %.2f m/s^2"%(mpu.acceleration)) 40 | ## print("X:{0:0.2f}, Y: {1:0.2f}, Z: {2:0.2f} m/s^2".format(*sensor.read_acceleration())) 41 | ## print("X:%.2f, Y: %.2f, Z: %.2f m/s^2"%(nmpu.acceleration)) 42 | ## print("Gyro:") 43 | ## print("X:%.2f, Y: %.2f, Z: %.2f degrees/s"%(mpu.gyro)) 44 | ## print("X:{0:0.2f}, Y: {1:0.2f}, Z: {2:0.2f} degrees/s".format(*sensor.read_gyro())) 45 | ## print("X:%.2f, Y: %.2f, Z: %.2f degrees/s"%(nmpu.gyro)) 46 | ## print("Temperature:") 47 | ## print("%.2f C"%mpu.temperature) 48 | ## print("%.2f C"%sensor.read_temperature()) 49 | ## print("%.2f C"%nmpu.temperature) 50 | ## print("") 51 | ## time.sleep(2) 52 | 53 | 54 | while not i2c.try_lock(): 55 | pass 56 | 57 | while True: 58 | print("I2C addresses found:", [hex(device_address) 59 | for device_address in i2c.scan()]) 60 | time.sleep(2) 61 | -------------------------------------------------------------------------------- /tests/robohatimu.py: -------------------------------------------------------------------------------- 1 | """ 2 | Example IMU Control 3 | Robo HAT MM1 4 | https://github.com/wallarug/circuitpython_mpu9250 5 | """ 6 | import board 7 | import busio 8 | from robohat_mpu9250.mpu9250 import MPU9250 9 | from robohat_mpu9250.mpu6500 import MPU6500 10 | from robohat_mpu9250.ak8963 import AK8963 11 | 12 | from time import sleep 13 | 14 | i2c = busio.I2C(board.SCL, board.SDA) 15 | 16 | mpu = MPU6500(i2c, address=0x69) 17 | ak = AK8963(i2c) 18 | 19 | sensor = MPU9250(mpu, ak) 20 | 21 | print("Reading in data from IMU.") 22 | print("MPU9250 id: " + hex(sensor.read_whoami())) 23 | 24 | while True: 25 | print('Acceleration (m/s^2): ({0:0.3f},{1:0.3f},{2:0.3f})'.format(*sensor.read_acceleration())) 26 | print('Magnetometer (gauss): ({0:0.3f},{1:0.3f},{2:0.3f})'.format(*sensor.read_magnetic())) 27 | print('Gyroscope (degrees/sec): ({0:0.3f},{1:0.3f},{2:0.3f})'.format(*sensor.read_gyro())) 28 | print('Temperature: {0:0.3f}C'.format(sensor.read_temperature())) 29 | sleep(2) 30 | --------------------------------------------------------------------------------