Source code for circuitpython_mpu6050

# The MIT License (MIT)
#
# Copyright (c) 2015, 2016, 2017 MrTijn/Tijndagamer
# Copyright (c) 2019 Brendan Doherty
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""
=====================
circuitpython_mpu6050
=====================

The original code from `Tijndagamer's mpu6050 python module
<https://github.com/Tijndagamer/mpu6050.git>`_ was ported to use the
adafruit-circuitpython-busdevice library.

Authors: `Dr David Martin <https://github.com/davidmam>`_,
`Martijn <https://github.com/Tijndagamer>`_,
`Brendan Doherty <https://github.com/2bndy5>`_

modified by `Brendan Doherty <https://github.com/2bndy5>`_

"""
from struct import unpack_from
from adafruit_bus_device.i2c_device import I2CDevice

# Global Variables
_GRAVITY = 9.80665

# Scale Modifiers
ACCEL_SCALE_MODIFIER_2G = 16384.0
ACCEL_SCALE_MODIFIER_4G = 8192.0
ACCEL_SCALE_MODIFIER_8G = 4096.0
ACCEL_SCALE_MODIFIER_16G = 2048.0

GYRO_SCALE_MODIFIER_250DEG = 131.0
GYRO_SCALE_MODIFIER_500DEG = 65.5
GYRO_SCALE_MODIFIER_1000DEG = 32.8
GYRO_SCALE_MODIFIER_2000DEG = 16.4

# Pre-defined ranges
ACCEL_RANGE_2G = 0x00
ACCEL_RANGE_4G = 0x08
ACCEL_RANGE_8G = 0x10
ACCEL_RANGE_16G = 0x18

GYRO_RANGE_250DEG = 0x00
GYRO_RANGE_500DEG = 0x08
GYRO_RANGE_1000DEG = 0x10
GYRO_RANGE_2000DEG = 0x18

# MPU-6050 Registers
_PWR_MGMT_1 = 0x6B

_ACCEL_XOUT0 = 0x3B
_TEMP_OUT0 = 0x41
_GYRO_XOUT0 = 0x43

_ACCEL_CONFIG = 0x1C
_GYRO_CONFIG = 0x1B


def _twos_comp(val, bits):
    # Convert an unsigned integer in 2's compliment form of the specified bit
    # length to its signed integer value and return it.
    if val & (1 << (bits - 1)) != 0:
        return val - (1 << bits)
    return val


[docs]class MPU6050: """A driver class for the MPU6050 6 DoF (Degrees of Freedom) sensor. :param ~busio.I2C i2c: The I2C bus object connected to the MPU6050. .. note:: This object should be shared among other driver classes that use the same I2C bus (SDA & SCL pins) to connect to different I2C devices. :param int address: The MPU6050's I2C device address. In most cases this is the default of ``0x68``. If your scenario is different, you can specify an alternate address with this parameter. """ def __init__(self, i2c, address=0x68): self._i2c = I2CDevice(i2c, address) # Wake up the MPU-6050 since it starts in sleep mode self._write_byte(_PWR_MGMT_1, 0x00) self._accel_scale_modifier = None self.accel_range = ACCEL_RANGE_2G self._gyro_scale_modifier = None self.gyro_scale = GYRO_RANGE_250DEG def _write_byte(self, reg, value): """only writes 1 byte of data""" with self._i2c as i2c: # grab lock on bus i2c.write(bytes([reg, value])) def _read_bytes(self, reg, count=1): """count=1 means this function will only read and return 1 byte. Set count to the number of bytes you want to read.""" buf = bytearray([reg]) # first byte is the register address for _ in range(count): buf += b'\x00' # pad out buffer to length of desired bytes with self._i2c as i2c: i2c.write_then_readinto( buf, buf, out_end=1, in_start=1, in_end=count + 1) return buf[1:] # return only what was read @property def accel_range(self): """The range of the accelerometer to range.""" return self._read_bytes(_ACCEL_CONFIG) @accel_range.setter def accel_range(self, xl_range): if xl_range in (ACCEL_RANGE_2G, ACCEL_RANGE_4G, ACCEL_RANGE_8G, ACCEL_RANGE_16G): # Write the new range to the ACCEL_CONFIG register self._write_byte(_ACCEL_CONFIG, xl_range) if xl_range == ACCEL_RANGE_2G: self._accel_scale_modifier = ACCEL_SCALE_MODIFIER_2G elif xl_range == ACCEL_RANGE_4G: self._accel_scale_modifier = ACCEL_SCALE_MODIFIER_4G elif xl_range == ACCEL_RANGE_8G: self._accel_scale_modifier = ACCEL_SCALE_MODIFIER_8G elif xl_range == ACCEL_RANGE_16G: self._accel_scale_modifier = ACCEL_SCALE_MODIFIER_16G else: raise ValueError('specified accelerometer range is undefined') def _read_accel_raw(self): # Read the accelerometer raw_x, raw_y, raw_z = unpack_from( '>hhh', self._read_bytes(_ACCEL_XOUT0, 6)) return (_twos_comp(raw_x, 16), _twos_comp(raw_y, 16), _twos_comp(raw_z, 16)) @property def acceleration(self): """The accelerometer X, Y, Z axis values as a 3-tuple of m/s^2 values.""" return map(lambda data: data / self._accel_scale_modifier * _GRAVITY, self._read_accel_raw()) @property def gyro_scale(self): """The scale of the gyroscope.""" return self._read_bytes(_GYRO_CONFIG) @gyro_scale.setter def gyro_scale(self, g_range): if g_range in (GYRO_RANGE_250DEG, GYRO_RANGE_500DEG, GYRO_RANGE_1000DEG, GYRO_RANGE_2000DEG): # Write the new range to the ACCEL_CONFIG register self._write_byte(_GYRO_CONFIG, g_range) if g_range == GYRO_RANGE_250DEG: self._gyro_scale_modifier = GYRO_SCALE_MODIFIER_250DEG elif g_range == GYRO_RANGE_500DEG: self._gyro_scale_modifier = GYRO_SCALE_MODIFIER_500DEG elif g_range == GYRO_RANGE_1000DEG: self._gyro_scale_modifier = GYRO_SCALE_MODIFIER_1000DEG elif g_range == GYRO_RANGE_2000DEG: self._gyro_scale_modifier = GYRO_SCALE_MODIFIER_2000DEG else: raise ValueError('Specified Gyroscope scale is undefined') def _read_gyro_raw(self): raw_x, raw_y, raw_z = unpack_from( '>hhh', self._read_bytes(_GYRO_XOUT0, 6)) return (_twos_comp(raw_x, 16), _twos_comp(raw_y, 16), _twos_comp(raw_z, 16)) @property def gyro(self): """The gyroscope X, Y, Z axis values as a 3-tuple of degrees/second values.""" return map(lambda data: data / self._gyro_scale_modifier, self._read_gyro_raw()) def _read_temp_raw(self): raw = self._read_bytes(_TEMP_OUT0, 2) return _twos_comp((raw[0] << 8) | raw[1], 16) @property def temperature(self): """The temperature from the onboard temperature sensor of the MPU-6050 in degrees Celcius.""" # Get the actual temperature using the formule given in the # MPU-6050 Register Map and Descriptions revision 4.2, page 30 return (self._read_temp_raw() / 340.0) + 36.53