Drivers for MPU9250 IMU Sensor

These drivers support the MPU9250 Inertial Measurement Unit.

Prerequisites

  • Linux device with an MPU9250 connected via I2C, such as any version of Forward Loop Zero with the optional inertial measurement unit.
  • (Optional) Docker on the Linux device. We recommend you check out floop, our simple, open-source tool for working with embedded Docker on multiple devices at the same time.

Install

In order to use the drivers with floop, you configure the drivers and your application on a host device then push the source code and build instructions to each target device using floop.

On your host, install floop using pip:

pip install --upgrade floopcli

Clone the driver repository:

git clone --recursive https://github.com/ForwardLoopLLC/mpu9250

The clone needs to be recursive in order to clone the appropriate I2C drivers.

Change directory into driver directory:

cd mlx90614

Configure floop to allow access to I2C devices on the target device. For example, if you have one target device available over SSH at the address 192.168.1.100, your floop.json in the mlx90614 directory could be:

Notice that privileged is true, so floop has access to target device hardware.

In order to check that installation succeeded, you can run the simple example included in the driver repository. You will need to tell the driver which bus holds the MPU9250.

Edit the file run.sh.linux:

#!/bin/bash
mkdir -p /drivers/i2c/
mkdir -p /drivers/mpu9250/
cp /floop/include/i2c/linux/*.h /drivers/i2c/
cp /floop/mpu9250/*.h /drivers/mpu9250/
g++ /floop/example/main.cpp -O3 -I/drivers -o /floop/mpu9250/mpu9250 && /floop/mpu9250/mpu9250

Make sure to change the value of BUS to match the bus to which your MPU9250 is connected. The value of BUS is the same as the integer that corresponds to the /dev/i2c-* entry for your device.

Now run the example:

floop run -v

If the example returns with no error, then the installation succeeded.

You can install the C++ header files for use in your own applications.

In order to use the drivers with Linux, you need to install some I2C dependencies:

sudo apt-get install -y python-smbus python-dev i2c-tools libi2c-dev

You need to install the MPU9250 and I2C headers, then include them during compilation of your application. Inside of your application folder, you can install both headers at the same time:

mkdir -p ./floop/i2c/ && \
mkdir -p ./floop/mpu9250/ && \
wget -O ./floop/i2c/i2c.h https://github.com/ForwardLoopLLC/i2c/blob/master/i2c/i2c.h && \
wget -O ./floop/mpu9250/mpu9250.h https://github.com/ForwardLoopLLC/mpu9250/blob/master/mpu9250/mpu9250.h && \
wget -O ./floop/mpu9250/mpu9250.h https://github.com/ForwardLoopLLC/mpu9250/blob/master/mpu9250/quaternion.h
# optional quaternion functionality

When you compile your application, make sure to include the path to the driver headers. For example, add -I./floop/ during compilation. You can then access the drivers by including the mpu9250header(s):

#include "mpu9250/mpu9250.h"
#include "mpu9250/quaternion.h" //optional

Note that you can also use these steps to install the drivers inside of a Docker container.

Example

The example shows the major functionality of the drivers.

#include "mpu9250/mpu9250.h" // drivers
#include "mpu9250/quaternion.h" //madgwickQuaternionUpdate
#include <sys/time.h>

double now() {
    struct timeval tp;
    gettimeofday(&tp, NULL);
    double ms = tp.tv_sec + tp.tv_usec/1000000.0;
    return ms;
}

int main() {
    MPU9250 mpu9250(0);
    if (mpu9250.error()) {
        printf("ERROR: MPU9250 did not initialize\n");
    }
    printf("MPU9250 initialized\n");
    if(!mpu9250.reset()) {
        printf("ERROR: reset failed\n");
    }
    if(!mpu9250.becomeSlave()) {
        printf("ERROR: failed to become slave device\n");
    }
    if(!mpu9250.disableSleepMode()) {
        printf("ERROR: failed to disable sleep mode\n");
    }
    if(!mpu9250.useBestClock()) {
        printf("ERROR: failed to use best clock\n");
    }
    if(!mpu9250.setGyroscopeLowPassFilterFrequency200Hz()) {
        printf("ERROR: failed to set gyroscope low pass filter frequency\n");
    }
    if(!mpu9250.setAccelerometerGyroscopeSampleFrequency10Hz()) {
        printf("ERROR: failed to set accelerometer and gyroscope sample frequency\n");
    }
    if(!mpu9250.setGyroscopeScale250DPS()) {
        printf("ERROR: failed to set gyroscope scale\n");
    }
    if(!mpu9250.setAccelerometerScale2G()) {
        printf("ERROR: failed to set accelerometer scale\n");
    }
    if(!mpu9250.setAccelerometerBandwidth45Hz()) {
        printf("ERROR: failed to set accelerometer bandwidth\n");
    }
    if(!mpu9250.enableMagnetometer()) {
        printf("ERROR: failed to enable magnetometer\n");
    }
    if(!mpu9250.enableDataReady()) {
        printf("ERROR: failed to enable data ready interrupt\n");
    }
    double magnetometerSensitivity[3];
    if(!mpu9250.magnetometerSensitivity(magnetometerSensitivity)) {
         printf("ERROR: failed to read magnetometer magnetometerSensitivity\n");
    }
    if(!mpu9250.setMagnetometerScale14Bits()) {
        printf("ERROR: failed to set magnetometer scale\n");
    }
    if(!mpu9250.setMagnetometerSampleRate100Hz()) {
        printf("ERROR: failed to set magnetometer rate\n");
    }
    double acceleration[3] = {0.0};
    double rotationRate[3] = {0.0};
    double magneticField[3] = {0.0};
    double quaternion[4] = {1.0, 0.0, 0.0, 0.0};
    double lastUpdate = 0.0;
    int sample = 0;
    while(true) {
        if(mpu9250.dataReady()) {
            // collect data samples and compute Madgwick quaternions while
            // waiting for new data to become available
            // (more iterations == more accurate orientation)
            // write and overwrite data to a file
            if(sample > 1) {
                FILE* fid = fopen("/floop/quaternion", "w");
                if (fid != NULL) {
                    fprintf(fid,
                            "%1.6f,%1.6f,%1.6f,%1.6f\n",
                            quaternion[0],
                            quaternion[1],
                            quaternion[2],
                            quaternion[3]);
                    fflush(fid);
                    fclose(fid);
                }
            }
            // read data
            mpu9250.readAccelerationAndRotationRate(acceleration, rotationRate);
            if(mpu9250.magnetometerReady()){
                mpu9250.readMagneticField(magneticField);
                for(int i=0; i<3; i++) {
                    magneticField[i] *= magnetometerSensitivity[i];
                }
            }
            sample++;
            if (sample > 100) {
                break;
            }
        }
        double deltat = now() - lastUpdate;
        lastUpdate = now();
        // calculate most accurate quaternion while waiting for new data
        if(sample > 1){
            // gets about ~2000 iterations per data read on a Forward Loop Zero
            if(!madgwickQuaternionUpdate(quaternion, acceleration, rotationRate, magneticField, deltat)) {
                printf("Madgwick failed!\n");
            }
        }
    }
    return 0;
}