|
1 | 1 | # The MIT License (MIT)
|
2 | 2 | #
|
3 |
| -# Copyright (c) 2017 Radomir Dopieralski for Adafruit Industries |
| 3 | +# Copyright (c) 2017 Radomir Dopieralski for Adafruit Industries. |
4 | 4 | #
|
5 | 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy
|
6 | 6 | # of this software and associated documentation files (the "Software"), to deal
|
|
19 | 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20 | 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21 | 21 | # THE SOFTWARE.
|
| 22 | + |
| 23 | + |
22 | 24 | """
|
23 |
| -`adafruit_bno055` |
24 |
| -==================================================== |
| 25 | +``adafruit_bno055`` |
| 26 | +=================== |
25 | 27 |
|
26 |
| -TODO(description) |
| 28 | +This is a CircuitPython driver for the Bosch BNO055 nine degree of freedom |
| 29 | +inertial measurement unit module with sensor fusion. |
27 | 30 |
|
28 | 31 | * Author(s): Radomir Dopieralski
|
29 | 32 | """
|
| 33 | + |
| 34 | +from micropython import const |
| 35 | +from adafruit_bus_device.i2c_device import I2CDevice |
| 36 | +from adafruit_register.i2c_struct import Struct, UnaryStruct |
| 37 | +import time |
| 38 | + |
| 39 | + |
| 40 | +_CHIP_ID = const(0xa0) |
| 41 | + |
| 42 | +CONFIG_MODE = const(0x00) |
| 43 | +ACCONLY_MODE = const(0x01) |
| 44 | +MAGONLY_MODE = const(0x02) |
| 45 | +GYRONLY_MODE = const(0x03) |
| 46 | +ACCMAG_MODE = const(0x04) |
| 47 | +ACCGYRO_MODE = const(0x05) |
| 48 | +MAGGYRO_MODE = const(0x06) |
| 49 | +AMG_MODE = const(0x07) |
| 50 | +IMUPLUS_MODE = const(0x08) |
| 51 | +COMPASS_MODE = const(0x09) |
| 52 | +M4G_MODE = const(0x0a) |
| 53 | +NDOF_FMC_OFF_MODE = const(0x0b) |
| 54 | +NDOF_MODE = const(0x0c) |
| 55 | + |
| 56 | +_POWER_NORMAL = const(0x00) |
| 57 | +_POWER_LOW = const(0x01) |
| 58 | +_POWER_SUSPEND = const(0x02) |
| 59 | + |
| 60 | +_MODE_REGISTER = const(0x3d) |
| 61 | +_PAGE_REGISTER = const(0x07) |
| 62 | +_TRIGGER_REGISTER = const(0x3f) |
| 63 | +_POWER_REGISTER = const(0x3e) |
| 64 | +_ID_REGISTER = const(0x00) |
| 65 | + |
| 66 | + |
| 67 | +class _ScaledReadOnlyStruct(Struct): |
| 68 | + def __init__(self, register_address, struct_format, scale): |
| 69 | + super(_ScaledReadOnlyStruct, self).__init__( |
| 70 | + register_address, struct_format) |
| 71 | + self.scale = scale |
| 72 | + |
| 73 | + def __get__(self, obj, objtype=None): |
| 74 | + result = super(_ScaledReadOnlyStruct, self).__get__(obj, objtype) |
| 75 | + return tuple(self.scale * v for v in result) |
| 76 | + |
| 77 | + def __set__(self, obj, value): |
| 78 | + raise NotImplementedError() |
| 79 | + |
| 80 | + |
| 81 | +class _ReadOnlyUnaryStruct(UnaryStruct): |
| 82 | + def __set__(self, obj, value): |
| 83 | + raise NotImplementedError() |
| 84 | + |
| 85 | + |
| 86 | +class BNO055: |
| 87 | + """ |
| 88 | + Driver for the BNO055 9DOF IMU sensor. |
| 89 | + """ |
| 90 | + |
| 91 | + temperature = _ReadOnlyUnaryStruct(0x34, 'B') |
| 92 | + """Measures the temperature of the chip in degrees Celsius.""" |
| 93 | + accelerometer = _ScaledReadOnlyStruct(0x08, '<hhh', 1/100) |
| 94 | + """Gives the raw accelerometer readings, in m/s.""" |
| 95 | + magnetometer = _ScaledReadOnlyStruct(0x0e, '<hhh', 1/16) |
| 96 | + """Gives the raw magnetometer readings in microteslas.""" |
| 97 | + gyroscope = _ScaledReadOnlyStruct(0x14, '<hhh', 1/900) |
| 98 | + """Gives the raw gyroscope reading in degrees per second.""" |
| 99 | + euler = _ScaledReadOnlyStruct(0x1a, '<hhh', 1/16) |
| 100 | + """Gives the calculated orientation angles, in degrees.""" |
| 101 | + quaternion = _ScaledReadOnlyStruct(0x20, '<hhhh', 1/(1<<14)) |
| 102 | + """Gives the calculated orientation as a quaternion.""" |
| 103 | + linear_acceleration = _ScaledReadOnlyStruct(0x28, '<hhh', 1/100) |
| 104 | + """Returns the linear acceleration, without gravity, in m/s.""" |
| 105 | + gravity = _ScaledReadOnlyStruct(0x2e, '<hhh', 1/100) |
| 106 | + """Returns the gravity vector, without acceleration in m/s.""" |
| 107 | + |
| 108 | + def __init__(self, i2c, address=0x28): |
| 109 | + self.i2c_device = I2CDevice(i2c, address) |
| 110 | + self.buffer = bytearray(2) |
| 111 | + self.init() |
| 112 | + |
| 113 | + def _write_register(self, register, value): |
| 114 | + self.buffer[0] = register |
| 115 | + self.buffer[1] = value |
| 116 | + with self.i2c_device as i2c: |
| 117 | + i2c.writeto(self.buffer) |
| 118 | + |
| 119 | + def _read_register(self, register): |
| 120 | + self.buffer[0] = register |
| 121 | + with self.i2c_device as i2c: |
| 122 | + i2c.writeto(self.buffer, end=1, stop=False) |
| 123 | + i2c.readfrom_into(self.buffer, start=1) |
| 124 | + return self.buffer[1] |
| 125 | + |
| 126 | + def switch_mode(self, mode): |
| 127 | + """ |
| 128 | + Switch the mode of operation and return the previous mode. |
| 129 | +
|
| 130 | + Mode of operation defines which sensors are enabled and whether the |
| 131 | + measurements are absolute or relative: |
| 132 | +
|
| 133 | + +------------------+-------+---------+------+----------+ |
| 134 | + | Mode | Accel | Compass | Gyro | Absolute | |
| 135 | + +==================+=======+=========+======+==========+ |
| 136 | + | CONFIG_MODE | - | - | - | - | |
| 137 | + +------------------+-------+---------+------+----------+ |
| 138 | + | ACCONLY_MODE | X | - | - | - | |
| 139 | + +------------------+-------+---------+------+----------+ |
| 140 | + | MAGONLY_MODE | - | X | - | - | |
| 141 | + +------------------+-------+---------+------+----------+ |
| 142 | + | GYRONLY_MODE | - | - | X | - | |
| 143 | + +------------------+-------+---------+------+----------+ |
| 144 | + | ACCMAG_MODE | X | X | - | - | |
| 145 | + +------------------+-------+---------+------+----------+ |
| 146 | + | ACCGYRO_MODE | X | - | X | - | |
| 147 | + +------------------+-------+---------+------+----------+ |
| 148 | + | MAGGYRO_MODE | - | X | X | - | |
| 149 | + +------------------+-------+---------+------+----------+ |
| 150 | + | AMG_MODE | X | X | X | - | |
| 151 | + +------------------+-------+---------+------+----------+ |
| 152 | + | IMUPLUS_MODE | X | - | X | - | |
| 153 | + +------------------+-------+---------+------+----------+ |
| 154 | + | COMPASS_MODE | X | X | - | X | |
| 155 | + +------------------+-------+---------+------+----------+ |
| 156 | + | M4G_MODE | X | X | - | - | |
| 157 | + +------------------+-------+---------+------+----------+ |
| 158 | + | NDOF_FMC_OFF_MODE| X | X | X | X | |
| 159 | + +------------------+-------+---------+------+----------+ |
| 160 | + | NDOF_MODE | X | X | X | X | |
| 161 | + +------------------+-------+---------+------+----------+ |
| 162 | +
|
| 163 | + The default mode is ``NDOF_MODE``. |
| 164 | + """ |
| 165 | + last_mode = self._read_register(_MODE_REGISTER) |
| 166 | + self._write_register(_MODE_REGISTER, mode) |
| 167 | + return last_mode |
| 168 | + |
| 169 | + def init(self, mode=NDOF_MODE): |
| 170 | + chip_id = self._read_register(_ID_REGISTER) |
| 171 | + if chip_id != _CHIP_ID: |
| 172 | + raise RuntimeError("bad chip id (%x != %x)" % (chip_id, _CHIP_ID)) |
| 173 | + self.reset() |
| 174 | + self._write_register(_POWER_REGISTER, _POWER_NORMAL) |
| 175 | + self._write_register(_PAGE_REGISTER, 0x00) |
| 176 | + self._write_register(_TRIGGER_REGISTER, 0x00) |
| 177 | + time.sleep(0.01) |
| 178 | + self.switch_mode(mode) |
| 179 | + time.sleep(0.01) |
| 180 | + |
| 181 | + def reset(self): |
| 182 | + """Resets the sensor to default settings.""" |
| 183 | + self.switch_mode(CONFIG_MODE) |
| 184 | + try: |
| 185 | + self._write_register(_TRIGGER_REGISTER, 0x20) |
| 186 | + except OSError: # error due to the chip resetting |
| 187 | + pass |
| 188 | + while True: # wait for the chip to reset |
| 189 | + time.sleep(0.01) |
| 190 | + try: |
| 191 | + chip_id = self._read_register(_ID_REGISTER) |
| 192 | + except OSError: |
| 193 | + chip_id = 0 |
| 194 | + if chip_id == _CHIP_ID: |
| 195 | + break |
| 196 | + |
| 197 | + def use_external_crystal(self, value): |
| 198 | + """Switches the use of external crystal on or off.""" |
| 199 | + last_mode = self.switch_mode(CONFIG_MODE) |
| 200 | + self._write_register(_PAGE_REGISTER, 0x00) |
| 201 | + self._write_register(_TRIGGER_REGISTER, 0x80 if value else 0x00) |
| 202 | + self.switch_mode(last_mode) |
| 203 | + time.sleep(0.01) |
0 commit comments