MPU-6050 Accelerometer/Gyro in MicroPython

From Squirrel's Lair
Jump to navigation Jump to search

  • Cargo:

  • Categories:
  • Default form

The MPU-6050 is a six-axis device (accelerometers in X/Y/Z and gyro in X/Y/Z) that also has a built-in temperature sensor. It talks on the I2C bus and is quite easy to talk to with MicroPython.

This example uses a GY-521 module that contains all the support electronics needed to use the MPU-6050, and an RP2040 (Raspberry Pi Pico) microcontroller. The connections are:

MPU-6050 VCC ---> Pico 3V3 (Pin 36)
MPU-6050 GND ---> Pico GND (any GND pin will do, I used Pin 38)
MPU-6050 SCL ---> Pico GP1 (Pin 2)
MPU-6050 SDA ---> Pico GP0 (Pin 1)
MPU-6050 XD ---> No connection
MPU-6050 XCL ---> No connection
MPU-6050 ADD ---> No connection
MPU-6050 INT ---> No connection

The program (tested in MicroPython 1.18) wakes up the MPU-6050 and then reads 14 bytes of data into an array that takes care of the twos compliment numbers. The accelerometer, temperature, and gyro readings are then extracted from the array, some minor calculations are done on them, and the results are printed.

There are two useful datasheets for the MPU-6050, the product spec:

and the register map and descriptions:

# MPU-6050

# 03: Further cleanup, simplification, fix comments

# 02: Clean up, expand to all axes and gyro.

# Attempt to read and make sense of the output of a MPU-6050 3-axis
# accelerometer and gyro using a GY-521 breakout board.


# MPU-6050 output registers are 2-byte, MSB first

# All registers default to all zeroes on power-up, except for:
# - PWR_MGMT_1 (0x6b): default is 0x40 (sleep mode on)
# - WHO_AM_I (0x75): default is 0x68 (based on I2C address)

# Uses I2C, using pins GP0 and 1

from machine import Pin, I2C
import time
import array

# Set up array for signed 2-byte values from MPU
# 3x accel, 1x temperature, 3x gyro
array_s16 = array.array('h', [0,0,0,0,0,0,0])

# Start up I2C and get the address of the first device on the bus
i2c = I2C(0, sda=Pin(0), scl=Pin(1), freq=100000)

i2c_addr = (i2c.scan())[0]

# Now, turn on the device using PWR_MGMT_1 register
# 0x6b: 7:RST, 6:SLEEP, 5:CYCLE, 4:N/U, 3:TEMP_DIS, 2-0:CLKSEL
# CLKSEL = 000 tells device to use internal 8MHz oscillator
# RST is active high, so send a 0
# setting SLEEP to 1 puts the device to sleep, so send 0 to wake it up
# **device starts out in sleep mode when powered up**
# setting CYCLE to 1 with SLEEP disabled makes the device cycle to take readings, set to 0
# TEMP_DIS: set to 0 to enable temperature sensor

# The MPU-6050 defaults to sleep mode at power-on.
# Clear bit 6 of PWR_MGMT_1 register to wake the device up.
# The PWR_MGMT_1 register (0x6b) consists of:
# RST(7), SLEEP(6), CYCLE(5), unused(4), TEMP_TIS(3), CLKSEL(2-0)
# RST defaults to 0 and is active high, so leave it at 0
# SLEEP defaults to 1 and is active high, so need to clear this bit
# CYCLE defaults to 0 and is active high (and we're not using this function at this point), so leave it at 0
# TEMP_DIS defaults to 0 and needs to be 0 to enable temperature sensing, so can leave it at 0
# CLKSEL defaults to 0, which is 8MHz internal oscillator, so leave it at 0
# So it's safe to send 0x00 to this register to wake it up since we need to clear bit 6 and the rest are already 0

i2c.writeto(i2c_addr, bytes([0x6b, 0x00]))

while True:

    # Read the axis/temp/gyro registers (total of 14, two bytes per signed value)
    for i in range(0, 7, 1):
        register_temp = 0x3b + (i*2)
        i2c.writeto(i2c_addr, bytes([register_temp])) # send read command to the device
        array_s16[i] = int.from_bytes(i2c.readfrom(i2c_addr, 2), 'big') # read two bytes into array
    # MPU-6050 accelerometers have the following full-scale range:
    # AFS_SEL=0: +/-2g (power-on default)
    # AFS_SEL=1: +/-4g
    # AFS_SEL=2: +/-8g
    # AFS_SEL=3: +/-16g
    # Full range is -16384 to 16384
    # Default power-on setting is to use +/-2g, so -2g would be -16384 and 2g would be 16384
    # So to calculate the accel at default power-on settings, it would be
    # (1g/16384)*result

    # X accel is in bytes 0 and 1 returned from the MPU, which were put into element 0 in the array
    accel_x = 1/16384*(array_s16[0])
    # Y accel is in bytes 2 and 3, which are in element 1 in the array
    accel_y = 1/16384*(array_s16[1])
    # Z accel is in bytes 4 and 5, which are in element 2 in the array
    accel_z = 1/16384*(array_s16[2])
    # Temperature is in bytes 6 and 7, which are in element 3 in the array
    # From datasheet, temperature (in Celsius) = (reading/340) + 36.53
    temperature = (array_s16[3]/340) + 36.53
    # **GYROS**
    # MPU-6050 gyros have the following full-scale range:
    # FS_SEL=0: +/-250deg/s (power-on default)
    # FS_SEL=1: +/-500deg/s
    # FS_SEL=2: +/-1000deg/s
    # FS_SEL=3: +/-2000deg/s
    # Full range is -16384 to 16384
    # Default power-on setting is to use +/-250deg/s, so -250deg/s would be -16384 and 250deg/s would be 16384
    # So to calculate the accel at default power-on settings, it would be
    # ((250deg/s)/16384)*result

    # X gyro is in bytes 8 and 9, which are in element 4 in the array
    gyro_x = 250/16384*(array_s16[4])
    # Y gyro is in bytes 10 and 11, which are in element 5 in the array
    gyro_y = 250/16384*(array_s16[5])
    # Z gyro is in bytes 12 and 13, which are in element 6 in the array
    gyro_z = 250/16384*(array_s16[6])
    # Now print the results
    #print("X:", accel_x,"g at", gyro_x, "deg/s")
    print("X: {accel_x}g, {gyro_x}deg/s".format(accel_x=accel_x, gyro_x=gyro_x))
    print("Y: {accel_y}g, {gyro_y}deg/s".format(accel_y=accel_y, gyro_y=gyro_y))
    print("Z: {accel_z}g, {gyro_z}deg/s".format(accel_z=accel_z, gyro_z=gyro_z))
    print("Temperature: {temperature}C".format(temperature=temperature))

If everything goes right, you should see something like this: