├── .gitignore ├── LICENSE ├── README.md ├── bno055.py ├── bno055_base.py ├── bno055_test.py └── package.json /.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 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # micropython-bno055 2 | 3 | A MicroPython driver for the Bosch BNO055 inertial measurement unit (IMU). This 4 | chip has the advantage of performing sensor fusion in hardware. The driver is 5 | based on the [Adafruit CircuitPython driver](https://github.com/adafruit/Adafruit_CircuitPython_BNO055.git). 6 | 7 | This driver has the following objectives: 8 | * It runs under official MicroPython. 9 | * It is cross-platform and designed to run on Pyboard 1.x, Pyboard D, ESPx. It 10 | should run on any hardware supporting the `machine` module and the I2C 11 | interface. 12 | * There is a minimal version with small memory footprint for ESP8266 (~9.7KB). 13 | * Supports changing the device mode. 14 | * Supports vehicle-relative coordinate transformation on-chip. 15 | * Supports changing the hardware configuration. 16 | * Supports access in interrupt service routines. 17 | * Uses the MicroPython approach to coding (avoids properties/descriptors). 18 | 19 | Testing was done with the [Adafruit BNO055 breakout](https://www.adafruit.com/product/2472). 20 | This chip and breakout come highly recommended. Calibration requires a little 21 | practice, but once done the fusion algorithm is remarkably immune to external 22 | magnetic fields. A field which displaced my hiker's compass by 90° caused at 23 | most 2° of heading change on this device. The raw magnetometer readings changed 24 | radically but heading remained essentially constant. 25 | 26 | # Contents 27 | 28 | 1. [Files and dependencies](./README.md#1-files-and-dependencies) 29 | 2. [Getting started](./README.md#2-getting-started) 30 | 2.1 [Pullups](./README.md#21-pullups) 31 | 2.2 [Clock stretching](./README.md#22-clock-stretching) 32 | 2.3 [Pico issue](./README.md#23-pico-issue) 33 | 2.4 [Basic usage](./README.md#23-basic-usage) 34 | 3. [The BNO055 class](./README.md#3-the-bno055-class) 35 | 3.1 [Constructor](./README.md#31-constructor) 36 | 3.2 [Read only methods](./README.md#32-read-only-methods) Read data from device. 37 | 3.3 [Changing the device configuration](./README.md#33-changing-the-device-configuration) 38 | 3.3.1 [Mode setting](./README.md#331-mode-setting) Modify device operating mode. 39 | 3.3.2 [Rate and range control](./README.md#332-rate-and-range-control) Further settings. 40 | 3.4 [Use in interrupt handlers](./README.md#34-use-in-interrupt-handlers) 41 | 3.5 [Other methods](./README.md#35-other-methods) 42 | 4. [Calibration](./README.md#4-calibration) 43 | 5. [Minimal version](./README.md#5-minimal-version) Minimise RAM usage. 44 | 6. [References](./README.md#6-references) 45 | 46 | # 1. Files and dependencies 47 | 48 | * `bno055_base.py` Base class for device driver. 49 | * `bno055.py` Device driver. 50 | * `bno055_test.py` Simple test program. Can run on Pyboard or (with pin 51 | changes) ESP8266: see code comments. 52 | 53 | The files may be installed on target hardware with `mpremote`: 54 | ```bash 55 | $ mpremote mip install github:micropython-IMU/micropython-bno055 56 | ``` 57 | The driver has no dependencies. It is designed to be imported using 58 | ```python 59 | from bno055 import * 60 | ``` 61 | In addition to the `BNO055` class this imports symbolic names for modes and 62 | data registers. On highly RAM-constrained targets the base class may be used 63 | alone with some loss of functionality, see 64 | [section 5](./README.md#5-minimal-version). 65 | 66 | # 2. Getting started 67 | 68 | The Adafruit breakout board has a voltage regulator and may be powered from a 69 | 5V or 3.3V supply. Note that other hardware may require a 3.3V source. 70 | 71 | The wiring below is for I2C(1) as used in the test program. 72 | 73 | | pyboard | bno055 | 74 | |:-------:|:------:| 75 | | VIN | VIN | 76 | | GND | GND | 77 | | SCL X9 | SCL | 78 | | SDA X10 | SDA | 79 | 80 | ## 2.1 Pullups 81 | 82 | Pullups (resistors connected to 3.3V) are required on SCL and SDA. The Pyboard 83 | has these on `I2C(1)` and `I2C(2)`, as does the Adafruit BNO055 breakout. 84 | ESP8266 boards have pullups on pins 0 and 2. Pyboard 1.1 pullups are 4.7KΩ, 85 | those on the Adafruit board are 10KΩ. The Raspberry Pico lacks pullups as do 86 | most ESP32 breakout boards. 87 | 88 | I encountered problems with the Pico and the Adafruit board: waveforms had slow 89 | risetimes and invalid data occurred at times. This was solved by adding 1KΩ 90 | resistors, with waveforms showing clean edges. 91 | 92 | As a general comment, my first port of call in the event of any problem would 93 | be to add 1KΩ resistors. 94 | 95 | ## 2.2 Clock stretching 96 | 97 | See [this issue](https://github.com/micropython-IMU/micropython-bno055/issues/4). 98 | 99 | The BNO055 hardware performs I2C clock stretching. I have found no 100 | documentation of this, but measurement suggests a maximum of about 500μs. The 101 | default timeout on Soft I2C is 255μs: it is therefore necessary to specify a 102 | `timeout` value in the SoftI2C constructor call - see below. 103 | 104 | ## 2.3 Pico Issue 105 | 106 | Firmware problems with I2C on the Pico have now been fixed. Please use a daily 107 | build or release build later than 1.18. Running I2C at the 400KHz default can 108 | be unreliable if the I2C pullup resistors are too high. Options are to reduce 109 | speed (Adafruit use 100KHz) or to add 1KΩ resistors from SDA and SCL to 3.3V. 110 | 111 | Refs: [this forum thread](https://forum.micropython.org/viewtopic.php?f=21&t=11745) 112 | and 113 | [this issue](https://github.com/micropython/micropython/issues/8167#issuecomment-1013696765). 114 | 115 | ## 2.4 Basic usage 116 | 117 | ```python 118 | import machine 119 | import time 120 | from bno055 import * 121 | # Pyboard hardware I2C 122 | i2c = machine.I2C(1) 123 | # ESP32 and ESP8266 soft I2C 124 | # i2c = machine.SoftI2C(scl=machine.Pin(2), sda=machine.Pin(0), timeout=100_000) 125 | imu = BNO055(i2c) 126 | calibrated = False 127 | while True: 128 | time.sleep(1) 129 | if not calibrated: 130 | calibrated = imu.calibrated() 131 | print('Calibration required: sys {} gyro {} accel {} mag {}'.format(*imu.cal_status())) 132 | print('Temperature {}°C'.format(imu.temperature())) 133 | print('Mag x {:5.0f} y {:5.0f} z {:5.0f}'.format(*imu.mag())) 134 | print('Gyro x {:5.0f} y {:5.0f} z {:5.0f}'.format(*imu.gyro())) 135 | print('Accel x {:5.1f} y {:5.1f} z {:5.1f}'.format(*imu.accel())) 136 | print('Lin acc. x {:5.1f} y {:5.1f} z {:5.1f}'.format(*imu.lin_acc())) 137 | print('Gravity x {:5.1f} y {:5.1f} z {:5.1f}'.format(*imu.gravity())) 138 | print('Heading {:4.0f} roll {:4.0f} pitch {:4.0f}'.format(*imu.euler())) 139 | ``` 140 | To calibrate the chip move the unit as per [section 4](./README.md#4-calibration) 141 | until calibration values for gyro, accel and mag are 3 and sys is >0. 142 | 143 | Note that if code is started automatically on power up (by a line in main.py) a 144 | delay of 500ms should be applied before instantiating the `BNO055`. This is to 145 | allow for the BNO055 chip startup time (400ms typical). 146 | 147 | ###### [Contents](./README.md#contents) 148 | 149 | # 3. The BNO055 class 150 | 151 | ## 3.1 Constructor 152 | 153 | This takes the following args 154 | 155 | * `i2c` An initialised I2C instance. 156 | * `address=0x28` Default device address. The Adafruit breakout allows this to 157 | be changed to 0x29 for the case where two devices are attached to one bus. 158 | * `crystal=True` If `True` use an external crystal for higher accuracy; the 159 | Adafruit board has a crystal. If the hardware does not have a crystal this 160 | should be `False`: the chip's internal oscillator will be used. 161 | 162 | The following optional args provide for vehicle relative coordinates. The 163 | default values assume that the IMU is mounted component side up and in the 164 | horizontal plane. Alternatives cater for cases where the IMU is rotated or 165 | mounted orthogonally relative to the horizontal plane of the vehicle. 166 | * `transpose=(0, 1, 2)` 167 | * `sign=(0, 0, 0)` 168 | 169 | Axes are numbered 0 for X, 1 for Y and 2 for Z, and the transpose tuple is 170 | `(x, y, z)`. Hence `(0, 1, 2)` implies no transformation. Passing `(1, 0, 2)` 171 | implies a rotation around the Z axis. 172 | 173 | Sign values must be 0 (normal) or 1 (inverted). Hence a board rotated around 174 | the Y axis and mounted upside down would have `sign=(1, 0, 1)` (X and Z axes 175 | inverted). This is further explained in the 176 | [Device datasheet](https://cdn-learn.adafruit.com/assets/assets/000/036/832/original/BST_BNO055_DS000_14.pdf) 177 | section 3.4. 178 | 179 | The constructor blocks for 700ms (1.2s if `crystal==True`). 180 | 181 | ## 3.2 Read only methods 182 | 183 | Return values (numbers are floats unless stated otherwise): 184 | * `mag()` Magnetometer vector `(x, y, z)` in μT (microtesla). 185 | * `accel()` Accelerometer vector `(x, y, z)` in m.s^-2 186 | * `lin_acc()` Acceleration vector `(x, y, z)` after removal of gravity 187 | component (m.s^-2).* 188 | * `gravity()` Gravity vector `(x, y, z)` in m.s^-2 after removal of 189 | acceleration data.* 190 | * `gyro()` Gyro vector `(x, y, z)` in deg.s^-1. 191 | * `euler()` Euler angles in degrees `(heading, roll, pitch)`.* 192 | * `quaternion()` Quaternion `(w, x, y, z)`.* 193 | * `temperature()` Chip temperature as an integer °C (Celcius). 194 | * `calibrated()` `True` if all elements of the device are calibrated. 195 | * `cal_status()` Returns bytearray `[sys, gyro, accel, mag]`. Each element 196 | has a value from 0 (uncalibrated) to 3 (fully calibrated). 197 | * `sensor_offsets()` Returns the current calibration offsets stored on the device as 198 | a `bytearray`. 199 | * `set_offsets()` Sets the current calibration offsets of the device. For example, 200 | this can be called with the results of `sensor_offsets()` after a reset. 201 | * `external_crystal()` `True` if using an external crystal. 202 | * `get_config(sensor)` See [Section 3.3.2](./README.md#332-rate-and-range-control). 203 | 204 | Some methods only produce valid data if the chip is in a fusion mode. If the 205 | mode is changed from the default to a non-fusion one, methods such as `euler` 206 | will return zeros. Such methods are marked with a * above. 207 | 208 | See [my notes on quaternions](https://github.com/peterhinch/micropython-samples/blob/master/README.md#412-quaternions) 209 | for code enabling them to be used to perform 3D rotation with minimal 210 | mathematics. They are easier to use for this purpose than Euler angles. 211 | 212 | ###### [Contents](./README.md#contents) 213 | 214 | ## 3.3 Changing the device configuration 215 | 216 | Many applications will use the default mode of the chip. This section describes 217 | ways of changing this for special purposes, for example where a high update 218 | rate is required. This can arise if readings are used in a feedback loop where 219 | latency can cause stability issues. 220 | 221 | ### 3.3.1 Mode setting 222 | 223 | * `mode(new_mode=None)` If a value is passed, change the mode of operation. 224 | Returns the mode as it was before any change was made. The mode is an integer. 225 | The `BNO055` module provides symbolic names as per the table below. 226 | 227 | The mode of operation defines which sensors are enabled, whether fusion occurs 228 | and if measurements are absolute or relative. The `bno055` module provides the 229 | mode values listed below as integers. 230 | 231 | | Mode | Accel | Compass | Gyro | Absolute | Fusion mode | 232 | |:----------------:|:-----:|:-------:|:----:|:--------:|:-----------:| 233 | | CONFIG_MODE | - | - | - | - | N | 234 | | ACCONLY_MODE | X | - | - | - | N | 235 | | MAGONLY_MODE | - | X | - | - | N | 236 | | GYRONLY_MODE | - | - | X | - | N | 237 | | ACCMAG_MODE | X | X | - | - | N | 238 | | ACCGYRO_MODE | X | - | X | - | N | 239 | | MAGGYRO_MODE | - | X | X | - | N | 240 | | AMG_MODE | X | X | X | - | N | 241 | | IMUPLUS_MODE | X | - | X | - | Y | 242 | | COMPASS_MODE | X | X | - | X | Y | 243 | | M4G_MODE | X | X | - | - | Y | 244 | | NDOF_FMC_OFF_MODE| X | X | X | X | Y | 245 | | NDOF_MODE | X | X | X | X | Y | 246 | 247 | The default mode is `NDOF_MODE` which supports fusion with absolute heading. 248 | This example illustrates restoration of the prior mode (`imu` is a `BNO055` 249 | instance): 250 | ```python 251 | from bno055 import * 252 | # code omitted to instantiate imu 253 | old_mode = imu.mode(ACCGYRO_MODE) 254 | # code omitted 255 | imu.mode(old_mode) 256 | ``` 257 | The purpose of the various modes is covered in the 258 | [Device datasheet](https://cdn-learn.adafruit.com/assets/assets/000/036/832/original/BST_BNO055_DS000_14.pdf) 259 | section 3.3. 260 | 261 | ###### [Contents](./README.md#contents) 262 | 263 | ### 3.3.2 Rate and range control 264 | 265 | In non-fusion modes the chip allows control of the update rate of each sensor 266 | and the range of the accelerometer and gyro. In fusion modes rates are fixed: 267 | the only available change is to the accelerometer range. The folowing shows the 268 | default setings after initialisation (mode is `NDOF`). The update rate of 269 | fusion values is 100Hz. 270 | 271 | | Device | Full scale | Update rate | 272 | |:------:|:-----------:|:-----------:| 273 | | Accel | +-4G | 100Hz | 274 | | Gyro | 2000°/s | 100Hz | 275 | | Mag | - | 20Hz | 276 | 277 | The magnetometer has a single range: units are Micro Tesla (μT). 278 | 279 | In modes which permit it, sensors may be controlled with the following method. 280 | 281 | * `config(dev, value=None)` `dev` is the device: must be `ACC`, `GYRO` or 282 | `MAG` (names defined in `bno055.py`). The `value` arg is a tuple holding the 283 | requested configuration. See below for details specific to each sensor. 284 | 285 | The default value of `None` causes no change to be made. In each case the 286 | method returns the config tuple as it was before any change was made. In 287 | certain circumstances the chip can return an unknown value. This was observed 288 | in the case of the initial value from the magnetometer. In such cases the 289 | result will be `False`. Returning the prior value allows old values to be 290 | restored, e.g. 291 | ```python 292 | old_config = imu.config(ACC, (2, 250)) 293 | # ... 294 | if old_config: # Valid config returned 295 | imu.config(ACC, old_config) # Restore old config 296 | ``` 297 | Note that the hardware will only allow configuration changes in appropriate 298 | modes. For example to change gyro settings the chip must be in a non-fusion 299 | mode which enables the gyro. If the mode is such that changes are not allowed, 300 | failure will be silent. If in doubt check the result by reading back the 301 | resultant config: 302 | ```python 303 | imu.mode(ACCGYRO_MODE) # Allows config change to ACC or GYRO 304 | cfg = (2, 250) # Intended config 305 | imu.config(ACC, cfg) 306 | if imu.config(ACC) == cfg: 307 | print('Success') 308 | ``` 309 | 310 | #### Accelerometer (dev == ACC) 311 | 312 | `value` is a 2-tuple comprising `(range, bandwidth)` 313 | Allowable values: 314 | Range: 2, 4, 8, 16 (G). 315 | Bandwidth: 8, 16, 31, 62, 125, 250, 500, 1000 (Hz). 316 | The outcome of a change may be shown by means of the `.config(ACC)` method. 317 | ```python 318 | from bno055 import * 319 | # code omitted 320 | imu.mode(ACCONLY_MODE) # non-fusion mode 321 | cfg = (2, 1000) # Intended config 322 | imu.config(ACC, cfg) # Update. 323 | if imu.config(ACC) == cfg: 324 | print('Success') 325 | ``` 326 | 327 | #### Gyro (dev == GYRO) 328 | 329 | `value` is a 2-tuple comprising `(range, bandwidth)` 330 | Allowable values: 331 | Range: 125, 250, 500, 1000, 2000 (dps) 332 | Bandwidth: 12, 23, 32, 47, 64, 116, 230, 523 (Hz). 333 | The outcome of a change may be shown by means of the `.config(GYRO)` method. 334 | 335 | #### Magnetometer (dev == MAG) 336 | 337 | `value` is a 1-tuple comprising `(rate,)` being the update rate in Hz. 338 | Allowable values: 339 | Rate: 2, 6, 8, 10, 15, 20, 25, 30 (Hz) 340 | The outcome of a change may be shown by means of the `.config(MAG)` method. 341 | Note that on first call the prior config may be unknown and the method will 342 | return `False`. This is a chip behaviour. 343 | 344 | ###### [Contents](./README.md#contents) 345 | 346 | ## 3.4 Use in interrupt handlers 347 | 348 | The `BNO055` class supports access in interrupt service routines (ISR's) by 349 | means of the `iget` method and `w`, `x`, `y`, and `z` bound variables. The ISR 350 | calls `iget` with the name of the value to be accessed. On return the bound 351 | variables are updated with the raw data from the device. Each value is a signed 352 | integer that requires scaling to be converted to standard units of measurement. 353 | 354 | | Name | Scaling | Units | 355 | |:------------:|:-----------:|:--------:| 356 | | ACC_DATA | 1/100 | m.s^-2 | 357 | | MAG_DATA | 1/16 | μT | 358 | | GYRO_DATA | 1/16 | °.s^-1 | 359 | | GRAV_DATA | 1/100 | m.s^-2 | 360 | | LIN_ACC_DATA | 1/100 | m.s^-2 | 361 | | EULER_DATA | 1/16 | ° | 362 | | QUAT_DATA | 1/(1 << 14) | unitless | 363 | 364 | In each case the integer values must be multiplied by the scaling to give the 365 | units specified. In all cases other than quaternion (`QUAT_DATA`) the `iget` 366 | method sets `.w` to zero. Example usage: 367 | 368 | ```python 369 | def cb(t): 370 | imu.iget(ACC_DATA) 371 | print(imu.w, imu.x, imu.y, imu.z) 372 | 373 | t = pyb.Timer(1, period=200, callback=cb) 374 | ``` 375 | 376 | ## 3.5 Other methods 377 | 378 | * `reset` No args. Equivalent to pulsing the chip's reset line: restores all 379 | power on defaults and resets the calibration status. Blocks for 700ms (1.2s if 380 | the constructor was called with `crystal==True`). Reinstates vehicle relative 381 | transformations specified to the constructor. 382 | 383 | ###### [Contents](./README.md#contents) 384 | 385 | # 4. Calibration 386 | 387 | Calibration requires only movement of the device while running: the process is 388 | internal to the chip and its nature is opaque. The calibration status may be 389 | read by methods described in [section 3.2](./README.md#32-read-only-methods). 390 | 391 | Until the device is calibrated its orientation will be relative to that when it 392 | was powered on. When system calibration status becomes 1 or higher the device 393 | has found magnetic north and orientation values become absolute. 394 | ([Source](https://learn.adafruit.com/adafruit-bno055-absolute-orientation-sensor/device-calibration)). 395 | The status returned by the chip, and hence the return values of `.calibrated` 396 | and `.cal_status` methods can regress after successful calibration. The meaning 397 | of this is unclear. It seems reasonable to assume that once the chip returns a 398 | good status it can be assumed to be OK; the demo scripts make that assumption. 399 | 400 | The following text is adapted from the chip datasheet; it could be clearer. I 401 | recommend watching [this Bosch video](https://youtu.be/Bw0WuAyGsnY) for a good 402 | exposition. 403 | 404 | Though the sensor fusion software runs the calibration algorithm of all the 405 | three sensors (accelerometer, gyroscope and magnetometer) in the background to 406 | remove the offsets, some preliminary steps should be ensured for this automatic 407 | calibration to take place. 408 | 409 | The accelerometer and the gyroscope are relatively less susceptible to external 410 | disturbances, as a result of which the offset is negligible. Whereas the 411 | magnetometer is susceptible to external magnetic field and therefore to ensure 412 | proper heading accuracy, the calibration steps described below have to be 413 | taken. 414 | 415 | Depending on the sensors selected, the following simple steps should be taken 416 | after every ‘Power on Reset’ for proper calibration of the device. 417 | 418 | ### Accelerometer Calibration 419 | 420 | * Place the device in 6 different stable positions for a period of few seconds 421 | to allow the accelerometer to calibrate. 422 | * Make sure that there is slow movement between 2 stable positions. 423 | * The 6 stable positions could be in any direction, but make sure that the 424 | device is lying at least once perpendicular to the x, y and z axis. 425 | * The `cal_status()` method may be used to see the calibration status of the 426 | accelerometer. 427 | 428 | ### Gyroscope Calibration 429 | 430 | * Place the device in a single stable position for a period of few seconds to 431 | allow the gyroscope to calibrate 432 | * The `cal_status()` method may be used to see the calibration status of the 433 | gyroscope. 434 | 435 | ### Magnetometer Calibration 436 | 437 | Magnetometers in general are susceptible to both hard-iron and soft-iron 438 | distortions, but the majority of the cases are rather due to the former. The 439 | steps mentioned below are to calibrate the magnetometer for hard-iron 440 | distortions. 441 | 442 | * Make some random movements (for example: writing the number ‘8’ on air) 443 | until the `cal_status()` method indicates fully calibrated. 444 | * It takes more calibration movements to get the magnetometer calibrated than 445 | in the NDOF mode. 446 | 447 | NDOF: 448 | 449 | * The same random movements have to be made to calibrate the sensor as in the 450 | FMC_OFF mode, but here it takes relatively less calibration movements (and 451 | slightly higher current consumption) to get the magnetometer calibrated. 452 | * The `cal_status()` method can be used to see the calibration status of the 453 | magnetometer. 454 | 455 | 456 | ### Calibration Restoration 457 | 458 | Restoring previous calibration offsets after a reset is also supported, via the 459 | `sensor_offsets()` and `set_offsets()` methods. 460 | 461 | After a successful calibration, `sensor_offsets()` can be called to retrieve a 462 | `bytearray`. This can then, for example, be written to disk for loading after a 463 | reset of the device. 464 | 465 | The corresponding `set_offsets()` method allows for restoring calibration offsets 466 | stored in said `bytearray`. Please be aware that the magnetometer's calibration 467 | status will remain as 0, as per the `cal_status()` method, even after restoring 468 | its offsets. 469 | 470 | ###### [Contents](./README.md#contents) 471 | 472 | # 5. Minimal Version 473 | 474 | This is intended for devices such as ESP8266 where RAM is limited. Note that 475 | the full version will run on ESP8266 using ~14K of RAM. The minimal version 476 | reduces this to just over 9K. 477 | 478 | The minimal version does not support vehicle-relative coordinates, ISR usage 479 | or configuration changes. Mode changes can be done, but symbolic names of modes 480 | are not supplied. The version is primarily intended for use in the default 481 | `NDOF` mode. 482 | 483 | In use the `bno055_base` module is imported and the base class is used. Example 484 | tested on an ESP8266: 485 | ```python 486 | import machine 487 | import time 488 | from bno055_base import BNO055_BASE 489 | 490 | i2c = machine.I2C(-1, scl=machine.Pin(2), sda=machine.Pin(0)) 491 | imu = BNO055_BASE(i2c) 492 | calibrated = False 493 | while True: 494 | time.sleep(1) 495 | if not calibrated: 496 | calibrated = imu.calibrated() 497 | print('Calibration required: sys {} gyro {} accel {} mag {}'.format(*imu.cal_status())) 498 | print('Temperature {}°C'.format(imu.temperature())) 499 | print('Mag x {:5.0f} y {:5.0f} z {:5.0f}'.format(*imu.mag())) 500 | print('Gyro x {:5.0f} y {:5.0f} z {:5.0f}'.format(*imu.gyro())) 501 | print('Accel x {:5.1f} y {:5.1f} z {:5.1f}'.format(*imu.accel())) 502 | print('Lin acc. x {:5.1f} y {:5.1f} z {:5.1f}'.format(*imu.lin_acc())) 503 | print('Gravity x {:5.1f} y {:5.1f} z {:5.1f}'.format(*imu.gravity())) 504 | print('Heading {:4.0f} roll {:4.0f} pitch {:4.0f}'.format(*imu.euler())) 505 | ``` 506 | 507 | # 6. References 508 | 509 | [Adafruit BNO055 breakout](https://www.adafruit.com/product/2472) 510 | [Adafruit CircuitPython driver](https://github.com/adafruit/Adafruit_CircuitPython_BNO055.git). 511 | [Device datasheet](https://cdn-learn.adafruit.com/assets/assets/000/036/832/original/BST_BNO055_DS000_14.pdf) 512 | -------------------------------------------------------------------------------- /bno055.py: -------------------------------------------------------------------------------- 1 | # bno055.py MicroPython driver for Bosch cls nine degree of freedom inertial 2 | # measurement unit module with sensor fusion. 3 | 4 | # The MIT License (MIT) 5 | # 6 | # Copyright (c) 2017 Radomir Dopieralski for Adafruit Industries. 7 | # 8 | # Permission is hereby granted, free of charge, to any person obtaining a copy 9 | # of this software and associated documentation files (the "Software"), to deal 10 | # in the Software without restriction, including without limitation the rights 11 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | # copies of the Software, and to permit persons to whom the Software is 13 | # furnished to do so, subject to the following conditions: 14 | # 15 | # The above copyright notice and this permission notice shall be included in 16 | # all copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | # THE SOFTWARE. 25 | 26 | # This is a port of the Adafruit CircuitPython driver to MicroPython, with 27 | # modified/enhanced functionality. 28 | 29 | # Original Author: Radomir Dopieralski 30 | # Ported to MicroPython and extended by Peter Hinch 31 | # This port copyright (c) Peter Hinch 2019 32 | 33 | from micropython import const 34 | from bno055_base import BNO055_BASE 35 | 36 | 37 | CONFIG_MODE = 0x00 38 | ACCONLY_MODE = 0x01 39 | MAGONLY_MODE = 0x02 40 | GYRONLY_MODE = 0x03 41 | ACCMAG_MODE = 0x04 42 | ACCGYRO_MODE = 0x05 43 | MAGGYRO_MODE = 0x06 44 | AMG_MODE = 0x07 45 | IMUPLUS_MODE = 0x08 46 | COMPASS_MODE = 0x09 47 | M4G_MODE = 0x0a 48 | NDOF_FMC_OFF_MODE = 0x0b 49 | NDOF_MODE = 0x0c 50 | 51 | ACC = 0x08 # Registers for configuration (page 1) 52 | MAG = 0x09 53 | GYRO = 0x0a 54 | 55 | ACC_DATA = 0x08 # Data regsiters (page 0) 56 | MAG_DATA = 0x0e 57 | GYRO_DATA = 0x14 58 | GRAV_DATA = 0x2e 59 | LIN_ACC_DATA = 0x28 60 | EULER_DATA = 0x1a 61 | QUAT_DATA = 0x20 62 | 63 | _PAGE_REGISTER = const(0x07) 64 | _AXIS_MAP_SIGN = const(0x42) 65 | _AXIS_MAP_CONFIG = const(0x41) 66 | 67 | class BNO055(BNO055_BASE): 68 | 69 | acc_range = (2, 4, 8, 16) # G 70 | acc_bw = (8, 16, 31, 62, 125, 250, 500, 1000) 71 | gyro_range = (2000, 1000, 500, 250, 125) # dps 72 | gyro_bw = (523, 230, 116, 47, 23, 12, 64, 32) # bandwidth (Hz) 73 | mag_rate = (2, 6, 8, 10, 15, 20, 25, 30) # rate (Hz) 74 | 75 | @classmethod 76 | def _tuple_to_int(cls, dev, v): # Convert (range, bw) to register value 77 | try: 78 | if dev == ACC: 79 | msg = 'Illegal accel range {} or bandwidth {}' 80 | return cls.acc_range.index(v[0]) | (cls.acc_bw.index(v[1]) << 2) 81 | elif dev == GYRO: 82 | msg = 'Illegal gyro range {} or bandwidth {}' 83 | return cls.gyro_range.index(v[0]) | (cls.gyro_bw.index(v[1]) << 3) 84 | elif dev == MAG: 85 | msg = 'Illegal magnetometer rate {}' 86 | return cls.mag_rate.index(v[0]) 87 | except ValueError: 88 | raise ValueError(msg.format(*v)) 89 | 90 | # Return the current config in human readable form 91 | @classmethod 92 | def _int_to_tuple(cls, dev, v): 93 | try: 94 | if dev == ACC: 95 | return (cls.acc_range[v & 3], cls.acc_bw[v >> 2]) 96 | elif dev == GYRO: 97 | return (cls.gyro_range[v & 7], cls.gyro_bw[v >> 3]) 98 | elif dev == MAG: 99 | return (cls.mag_rate[v],) 100 | except IndexError: 101 | return False # Can occur e.g. initial config of magnetometer 102 | raise ValueError('Unknown device.', dev) 103 | 104 | # Convert two bytes to signed integer (little endian) Can be used in an interrupt handler 105 | @staticmethod 106 | def _bytes_toint(lsb, msb): 107 | if not msb & 0x80: 108 | return msb << 8 | lsb # +ve 109 | return - (((msb ^ 255) << 8) | (lsb ^ 255) + 1) 110 | 111 | @staticmethod 112 | def _argcheck(arg, name): 113 | if len(arg) != 3 or not (isinstance(arg, list) or isinstance(arg, tuple)): 114 | raise ValueError(name + ' must be a 3 element list or tuple') 115 | 116 | # Transposition (x, y, z) 0 == x 1 == y 2 == z hence (0, 1, 2) is no change 117 | # Scaling (x, y, z) 0 == normal 1 == invert 118 | def __init__(self, i2c, address=0x28, crystal=True, transpose=(0, 1, 2), sign=(0, 0, 0)): 119 | self._argcheck(sign, 'Sign') 120 | if [x for x in sign if x not in (0, 1)]: 121 | raise ValueError('Sign values must be 0 or 1') 122 | self.sign = sign 123 | self._argcheck(transpose, 'Transpose') 124 | if set(transpose) != {0, 1, 2}: 125 | raise ValueError('Transpose indices must be unique and in range 0-2') 126 | self.transpose = transpose 127 | super().__init__(i2c, address, crystal) 128 | self.buf6 = bytearray(6) 129 | self.buf8 = bytearray(8) 130 | self.w = 0 131 | self.x = 0 132 | self.y = 0 133 | self.z = 0 134 | 135 | def orient(self): 136 | if self.transpose != (0, 1, 2): 137 | a = self.transpose 138 | self._write(_AXIS_MAP_CONFIG, (a[2] << 4) + (a[1] << 2) + a[0]) 139 | if self.sign != (0, 0, 0): 140 | a = self.sign 141 | self._write(_AXIS_MAP_SIGN, a[2] + (a[1] << 1) + (a[0] << 2)) 142 | 143 | # Configuration: if a tuple is passed, convert to int using function from bno055_help.py 144 | def config(self, dev, value=None): 145 | if dev not in (ACC, MAG, GYRO): 146 | raise ValueError('Unknown device:', dev) 147 | if isinstance(value, tuple): 148 | value = self._tuple_to_int(dev, value) # Convert tuple to register value 149 | elif value is not None: 150 | raise ValueError('value must be a tuple or None.') 151 | last_mode = self.mode(CONFIG_MODE) 152 | self._write(_PAGE_REGISTER, 1) 153 | old_val = self._read(dev) 154 | if value is not None: 155 | self._write(dev, value) 156 | self._write(_PAGE_REGISTER, 0) 157 | self.mode(last_mode) 158 | return self._int_to_tuple(dev, old_val) 159 | 160 | # For use in ISR 161 | def iget(self, reg): 162 | if reg == 0x20: 163 | n = 4 164 | buf = self.buf8 165 | else: 166 | n = 3 167 | buf = self.buf6 168 | self._i2c.readfrom_mem_into(self.address, reg, buf) 169 | if n == 4: 170 | self.w = self._bytes_toint(buf[0], buf[1]) 171 | i = 2 172 | else: 173 | self.w = 0 174 | i = 0 175 | self.x = self._bytes_toint(buf[i], buf[i+1]) 176 | self.y = self._bytes_toint(buf[i+2], buf[i+3]) 177 | self.z = self._bytes_toint(buf[i+4], buf[i+5]) 178 | -------------------------------------------------------------------------------- /bno055_base.py: -------------------------------------------------------------------------------- 1 | # bno055_base.py Minimal MicroPython driver for Bosch BNO055 nine degree of 2 | # freedom inertial measurement unit module with sensor fusion. 3 | 4 | # The MIT License (MIT) 5 | # 6 | # Copyright (c) 2017 Radomir Dopieralski for Adafruit Industries. 7 | # 8 | # Permission is hereby granted, free of charge, to any person obtaining a copy 9 | # of this software and associated documentation files (the "Software"), to deal 10 | # in the Software without restriction, including without limitation the rights 11 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | # copies of the Software, and to permit persons to whom the Software is 13 | # furnished to do so, subject to the following conditions: 14 | # 15 | # The above copyright notice and this permission notice shall be included in 16 | # all copies or substantial portions of the Software. 17 | # 18 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | # THE SOFTWARE. 25 | 26 | # This is a port of the Adafruit CircuitPython driver to MicroPython, with 27 | # modified/enhanced functionality. 28 | 29 | # Original Author: Radomir Dopieralski 30 | # Ported to MicroPython and extended by Peter Hinch 31 | # This port copyright (c) Peter Hinch 2019 32 | 33 | import utime as time 34 | import ustruct 35 | from micropython import const 36 | 37 | _CHIP_ID = const(0xA0) 38 | 39 | _CONFIG_MODE = const(0) 40 | _NDOF_MODE = const(0x0C) 41 | 42 | _POWER_NORMAL = const(0x00) 43 | _POWER_LOW = const(0x01) 44 | _POWER_SUSPEND = const(0x02) 45 | 46 | _MODE_REGISTER = const(0x3D) 47 | _PAGE_REGISTER = const(0x07) 48 | _CALIBRATION_REGISTER = const(0x35) 49 | _TRIGGER_REGISTER = const(0x3F) 50 | _POWER_REGISTER = const(0x3E) 51 | _ID_REGISTER = const(0x00) 52 | 53 | ACCEL_OFFSET_X_LSB_ADDR = const(0x55) 54 | ACCEL_OFFSET_X_MSB_ADDR = const(0x56) 55 | ACCEL_OFFSET_Y_LSB_ADDR = const(0x57) 56 | ACCEL_OFFSET_Y_MSB_ADDR = const(0x58) 57 | ACCEL_OFFSET_Z_LSB_ADDR = const(0x59) 58 | ACCEL_OFFSET_Z_MSB_ADDR = const(0x5A) 59 | 60 | MAG_OFFSET_X_LSB_ADDR = const(0x5B) 61 | MAG_OFFSET_X_MSB_ADDR = const(0x5C) 62 | MAG_OFFSET_Y_LSB_ADDR = const(0x5D) 63 | MAG_OFFSET_Y_MSB_ADDR = const(0x5E) 64 | MAG_OFFSET_Z_LSB_ADDR = const(0x5F) 65 | MAG_OFFSET_Z_MSB_ADDR = const(0x60) 66 | 67 | GYRO_OFFSET_X_LSB_ADDR = const(0x61) 68 | GYRO_OFFSET_X_MSB_ADDR = const(0x62) 69 | GYRO_OFFSET_Y_LSB_ADDR = const(0x63) 70 | GYRO_OFFSET_Y_MSB_ADDR = const(0x64) 71 | GYRO_OFFSET_Z_LSB_ADDR = const(0x65) 72 | GYRO_OFFSET_Z_MSB_ADDR = const(0x66) 73 | 74 | ACCEL_RADIUS_LSB_ADDR = const(0x67) 75 | ACCEL_RADIUS_MSB_ADDR = const(0x68) 76 | MAG_RADIUS_LSB_ADDR = const(0x69) 77 | MAG_RADIUS_MSB_ADDR = const(0x6A) 78 | 79 | 80 | class BNO055_BASE: 81 | def __init__(self, i2c, address=0x28, crystal=True, transpose=(0, 1, 2), sign=(0, 0, 0)): 82 | self._i2c = i2c 83 | self.address = address 84 | self.crystal = crystal 85 | self.mag = lambda: self.scaled_tuple(0x0E, 1 / 16) # microteslas (x, y, z) 86 | self.accel = lambda: self.scaled_tuple(0x08, 1 / 100) # m.s^-2 87 | self.lin_acc = lambda: self.scaled_tuple(0x28, 1 / 100) # m.s^-2 88 | self.gravity = lambda: self.scaled_tuple(0x2E, 1 / 100) # m.s^-2 89 | self.gyro = lambda: self.scaled_tuple(0x14, 1 / 16) # deg.s^-1 90 | self.euler = lambda: self.scaled_tuple(0x1A, 1 / 16) # degrees (heading, roll, pitch) 91 | self.quaternion = lambda: self.scaled_tuple( 92 | 0x20, 1 / (1 << 14), bytearray(8), "> 6) & 0x03 # sys 130 | s[1] = (cdata >> 4) & 0x03 # gyro 131 | s[2] = (cdata >> 2) & 0x03 # accel 132 | s[3] = cdata & 0x03 # mag 133 | return s 134 | 135 | def calibrated(self): 136 | s = self.cal_status() 137 | # https://learn.adafruit.com/adafruit-bno055-absolute-orientation-sensor/device-calibration 138 | return min(s[1:]) == 3 and s[0] > 0 139 | 140 | def sensor_offsets(self): 141 | lastMode = self._mode 142 | 143 | self.mode(_CONFIG_MODE) 144 | offsets = self._readn(bytearray(22), ACCEL_OFFSET_X_LSB_ADDR) 145 | self.mode(lastMode) 146 | 147 | return offsets 148 | 149 | def set_offsets(self, buf): 150 | lastMode = self._mode 151 | self.mode(_CONFIG_MODE) 152 | 153 | time.sleep_ms(25) 154 | 155 | """Note: Configuration will take place only when user writes to the last 156 | byte of each config data pair (ex. ACCEL_OFFSET_Z_MSB_ADDR, etc.). 157 | Therefore the last byte must be written whenever the user wants to 158 | changes the configuration.""" 159 | 160 | self._write(ACCEL_OFFSET_X_LSB_ADDR, buf[0]) 161 | self._write(ACCEL_OFFSET_X_MSB_ADDR, buf[1]) 162 | self._write(ACCEL_OFFSET_Y_LSB_ADDR, buf[2]) 163 | self._write(ACCEL_OFFSET_Y_MSB_ADDR, buf[3]) 164 | self._write(ACCEL_OFFSET_Z_LSB_ADDR, buf[4]) 165 | self._write(ACCEL_OFFSET_Z_MSB_ADDR, buf[5]) 166 | 167 | self._write(MAG_OFFSET_X_LSB_ADDR, buf[6]) 168 | self._write(MAG_OFFSET_X_MSB_ADDR, buf[7]) 169 | self._write(MAG_OFFSET_Y_LSB_ADDR, buf[8]) 170 | self._write(MAG_OFFSET_Y_MSB_ADDR, buf[9]) 171 | self._write(MAG_OFFSET_Z_LSB_ADDR, buf[10]) 172 | self._write(MAG_OFFSET_Z_MSB_ADDR, buf[11]) 173 | 174 | self._write(GYRO_OFFSET_X_LSB_ADDR, buf[12]) 175 | self._write(GYRO_OFFSET_X_MSB_ADDR, buf[13]) 176 | self._write(GYRO_OFFSET_Y_LSB_ADDR, buf[14]) 177 | self._write(GYRO_OFFSET_Y_MSB_ADDR, buf[15]) 178 | self._write(GYRO_OFFSET_Z_LSB_ADDR, buf[16]) 179 | self._write(GYRO_OFFSET_Z_MSB_ADDR, buf[17]) 180 | 181 | self._write(ACCEL_RADIUS_LSB_ADDR, buf[18]) 182 | self._write(ACCEL_RADIUS_MSB_ADDR, buf[19]) 183 | 184 | self._write(MAG_RADIUS_LSB_ADDR, buf[20]) 185 | self._write(MAG_RADIUS_MSB_ADDR, buf[21]) 186 | 187 | self.mode(lastMode) 188 | 189 | # read byte from register, return int 190 | def _read(self, memaddr, buf=bytearray(1)): # memaddr = memory location within the I2C device 191 | self._i2c.readfrom_mem_into(self.address, memaddr, buf) 192 | return buf[0] 193 | 194 | # write byte to register 195 | def _write(self, memaddr, data, buf=bytearray(1)): 196 | buf[0] = data 197 | self._i2c.writeto_mem(self.address, memaddr, buf) 198 | 199 | # read n bytes, return buffer 200 | def _readn(self, buf, memaddr): # memaddr = memory location within the I2C device 201 | self._i2c.readfrom_mem_into(self.address, memaddr, buf) 202 | return buf 203 | 204 | def mode(self, new_mode=None): 205 | old_mode = self._read(_MODE_REGISTER) 206 | if new_mode is not None: 207 | self._write( 208 | _MODE_REGISTER, _CONFIG_MODE 209 | ) # This is empirically necessary if the mode is to be changed 210 | time.sleep_ms(20) # Datasheet table 3.6 211 | if new_mode != _CONFIG_MODE: 212 | self._write(_MODE_REGISTER, new_mode) 213 | time.sleep_ms(10) # Table 3.6 214 | 215 | self._mode = old_mode if new_mode is None else new_mode 216 | return old_mode 217 | 218 | def external_crystal(self): 219 | return bool(self._read(_TRIGGER_REGISTER) & 0x80) 220 | -------------------------------------------------------------------------------- /bno055_test.py: -------------------------------------------------------------------------------- 1 | # bno055_test.py Simple test program for MicroPython bno055 driver 2 | 3 | # Copyright (c) Peter Hinch 2019 4 | # Released under the MIT licence. 5 | 6 | import machine 7 | import time 8 | from bno055 import * 9 | # Tested configurations 10 | # Pyboard hardware I2C 11 | # i2c = machine.I2C(1) 12 | 13 | # Pico: hard I2C doesn't work without this patch 14 | # https://github.com/micropython/micropython/issues/8167#issuecomment-1013696765 15 | i2c = machine.I2C(0, sda=machine.Pin(16), scl=machine.Pin(17)) # EIO error almost immediately 16 | 17 | # All platforms: soft I2C requires timeout >= 1000μs 18 | # i2c = machine.SoftI2C(sda=machine.Pin(16), scl=machine.Pin(17), timeout=1_000) 19 | # ESP8266 soft I2C 20 | # i2c = machine.SoftI2C(scl=machine.Pin(2), sda=machine.Pin(0), timeout=100_000) 21 | # ESP32 hard I2C 22 | # i2c = machine.I2C(1, scl=machine.Pin(21), sda=machine.Pin(23)) 23 | imu = BNO055(i2c) 24 | calibrated = False 25 | while True: 26 | time.sleep(1) 27 | if not calibrated: 28 | calibrated = imu.calibrated() 29 | print('Calibration required: sys {} gyro {} accel {} mag {}'.format(*imu.cal_status())) 30 | print('Temperature {}°C'.format(imu.temperature())) 31 | print('Mag x {:5.0f} y {:5.0f} z {:5.0f}'.format(*imu.mag())) 32 | print('Gyro x {:5.0f} y {:5.0f} z {:5.0f}'.format(*imu.gyro())) 33 | print('Accel x {:5.1f} y {:5.1f} z {:5.1f}'.format(*imu.accel())) 34 | print('Lin acc. x {:5.1f} y {:5.1f} z {:5.1f}'.format(*imu.lin_acc())) 35 | print('Gravity x {:5.1f} y {:5.1f} z {:5.1f}'.format(*imu.gravity())) 36 | print('Heading {:4.0f} roll {:4.0f} pitch {:4.0f}'.format(*imu.euler())) 37 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "urls": [ 3 | ["bno055_base.py", "https://raw.githubusercontent.com/micropython-IMU/micropython-bno055/HEAD/bno055_base.py"], 4 | ["bno055_test.py", "https://raw.githubusercontent.com/micropython-IMU/micropython-bno055/HEAD/bno055_test.py"], 5 | ["bno055.py", "https://raw.githubusercontent.com/micropython-IMU/micropython-bno055/HEAD/bno055.py"] 6 | ], 7 | "version": "0.1" 8 | } 9 | --------------------------------------------------------------------------------