MPU9250 – 9-Axis Sensor Module – Part 1

About this post

In previous articles, I have introduced the accelerometers MMA7361 and ADXL345. I also reported on the MPU6050 gyroscope and accelerometer sensor module. This time I would like to dedicate myself to the 9-axis sensor module MPU9250. This combines four functions:

  • Gyroscope
  • Accelerometer
  • Magnetometer
  • Thermometer

During my research I was looking for a library that on the one hand implemented the many functions and setting parameters of the MPU9250, but on the other hand can be operated without having to go into the depths of the data sheet. Since I didn’t really find what I was looking for, I wrote a library myself, which I will present in this article.

Because of the size of the topic, I have decided to split the post. In this first part, I deal with:

  • Features / Technical specifications of the MPU9250
  • Wiring
  • Introduction to my library MPU9250:
    • Getting the basic data:
      • Acceleration
      • Gyroscope
      • Magnetometer
      • All basic data and temperature
    • Calibration

In Part 2, I will continue the introduction to my library:

  • Measuring angles
  • Set up interrupts
  • Low-power / cycle mode
  • Using the FIFO

Features and specifications of the MPU9250

How a gyroscope and an accelerometer works, I have already described in my article about the MPU6050. The magnetometer works based on the Hall effect. You find a good animation here.

The MPU9250 is actually a discontinued model. On the manufacturer’s pages it is marked as “EoL” (End of Life). But I assume that you can buy the modules for a few more years. On Amazon and eBay, the selection of stores that offer the MPU9250 is huge. The successor model is the ICM-20948, which I will report on soon. However, the modules are still relatively expensive (€15). You can get the MPU9250 for half the price.

Since the MPU9250 combines four sensors, a complete list of technical data would be beyond the scope. The essential key data is:

  • Power supply: 3.0 – 5.0 volts (module)
    • for the actual IC it is 2.4 – 3.6 volts
  • Communication via I2C, the addresses are:
    • AD0 unconnected or LOW: 0x68
    • AD0 to HIGH: 0x69
    • Magnetometer (AK8963): 0x0C
  • Communication via SPI: I have not implemented SPI
  • Gyroscope
    • Ranges: +/-250, +/-500, +/-1000, +/-2000 °/s
    • Data rate: 3.91 – 32000 Hz
    • Resolution: 16 bits
    • Power consumption: 3.2 mA (normal mode) / 8 µA (sleep mode)
  • Accelerometer:
    • Ranges: +/- 2, +/-4, +/-8, +/-16 g
    • Data rate: 0.24 Hz (in cycle mode) to 4000 Hz
    • Resolution: 16 bits
    • Power consumption: 450 µA (normal mode) / 8.4 µA in low-power mode (0.98 Hz cycle) / 19.8 µA in low-power mode (31.25 Hz cycle)
  • Magnetometer:
    • Measuring range: +/- 4800 µT
    • Data rate: 8 Hz or 100 Hz
    • Resolution: 14 or 16 bits (I only implemented 16 bit resolution)
    • Power consumption: 280 A at 8 Hz data rate
  • FIFO (First in, first out) data storage: 512 bytes = 256 single values = 85 x,y,z data triple
  • Interrupts: FIFO Overflow, data ready and wake-on-motion (acceleration) interrupt
  • Integrated thermometer

The MPU9250 allows you to control five additional sensors via I2C in a subnet, to store and read the data in the MPU9250, or store it in the FIFO. But I haven’t implemented that (at least yet). 

Further information can be found in the technical data sheet, in the register map and in the data sheet of the magnetic sensor AK8963.

Control of the MPU9250 with Arduino UNO & Co

Wiring

There is not much to say about the wiring. Pull-up resistors for the I2C lines are not needed. The interrupt pin is required for some example sketches.

But a few words about the unused pins:

  • EDA/ECL are the I2C connectors for external sensors – I have not (yet) implemented this option as mentioned
  • AD0: connected to HIGH, the I2C address is changed from 0x68 to 0x69
  • NCS: Chip select for SPI operation (not implemented in my library)
  • FSYNC (“frame synchronization”): can be used as an interrupt input for other sensors – I have not (yet) implemented this.
Connecting the MPU9250 to an Arduino UNO
Connecting the MPU9250 to an Arduino UNO

Libraries for the MPU9250

There are a number of libraries available for the MPU9250. Check out the Arduino Library Manager or GitHub for this. As I mentioned at the beginning, I wasn’t happy with the available libraries, but that’s a matter of taste.

You can download my library MPU9250_WE here from GitHub or you install it directly via the Arduino IDE Library Manager.

The problem with the MPU9250 is that it has an incredible amount of setting options due to its 4 sensors plus I2C subnet control. In such cases,  the question arises whether everything that is possible should be implemented. And although I didn’t, my library contains fifty-nine public features. To explain the use of these functions, I have attached eleven example sketches.

Introduction to the library MPU9250_WE

The basic data

To get to know the library and the MPU9250, I recommend trying out the sketches in the order used here. We start with the basic data for acceleration, gyroscope, magnetometer and thermometer. This is a pretty dry read! If I were you, I would get an MPU9250 and try out the example sketches in parallel in practice.

Example sketch 1: MPU9250_acceleration_data

This first sketch is the one I go into most intensively. Many functions are also used in other sketches.

First, a comment about the data format “xyzFloat”. This is a structure (struct) consisting of three float values. I use xyzFloat for all data that has an x, y, and z component.

#include <MPU9250_WE.h>
#include <Wire.h>

MPU9250_WE myMPU9250 = MPU9250_WE(0x68);

void setup() {
  Serial.begin(115200);
  Wire.begin();
  if(!myMPU9250.init()){
    Serial.println("MPU9250 does not respond");
  }
  else{
    Serial.println("MPU9250 is connected");
  }
  
  /* The slope of the curve of acceleration vs measured values fits quite well to the theoretical 
   * values, e.g. 16384 units/g in the +/- 2g range. But the starting point, if you position the 
   * MPU9250 flat, is not necessarily 0g/0g/1g for x/y/z. The autoOffset function measures offset 
   * values. It assumes your MPU9250 is positioned flat with its x,y-plane. The more you deviate 
   * from this, the less accurate will be your results.
   * The function also measures the offset of the gyroscope data. The gyroscope offset does not   
   * depend on the positioning.
   * This function needs to be called at the beginning since it can overwrite your settings!
   */
  Serial.println("Position you MPU9250 flat and don't move it - calibrating...");
  delay(1000);
  myMPU9250.autoOffsets();
  Serial.println("Done!");
  
  /*  This is a more accurate method for calibration. You have to determine the minimum and maximum 
   *  raw acceleration values of the axes determined in the range +/- 2 g. 
   *  You call the function as follows: setAccOffsets(xMin,xMax,yMin,yMax,zMin,zMax);
   *  Use either autoOffset or setAccOffsets, not both.
   */
  //myMPU9250.setAccOffsets(-14240.0, 18220.0, -17280.0, 15590.0, -20930.0, 12080.0);

  /*  Sample rate divider divides the output rate of the gyroscope and accelerometer.
   *  Sample rate = Internal sample rate / (1 + divider) 
   *  It can only be applied if the corresponding DLPF is enabled and 0<DLPF<7!
   *  Divider is a number 0...255
   */
  myMPU9250.setSampleRateDivider(5);
  
  /*  MPU9250_ACC_RANGE_2G      2 g   
   *  MPU9250_ACC_RANGE_4G      4 g
   *  MPU9250_ACC_RANGE_8G      8 g   
   *  MPU9250_ACC_RANGE_16G    16 g
   */
  myMPU9250.setAccRange(MPU9250_ACC_RANGE_2G);

  /*  Enable/disable the digital low pass filter for the accelerometer 
   *  If disabled the the bandwidth is 1.13 kHz, delay is 0.75 ms, output rate is 4 kHz
   */
  myMPU9250.enableAccDLPF(true);

  /*  Digital low pass filter (DLPF) for the accelerometer, if enabled 
   *  MPU9250_DPLF_0, MPU9250_DPLF_2, ...... MPU9250_DPLF_7 
   *   DLPF     Bandwidth [Hz]      Delay [ms]    Output rate [kHz]
   *     0           460               1.94           1
   *     1           184               5.80           1
   *     2            92               7.80           1
   *     3            41              11.80           1
   *     4            20              19.80           1
   *     5            10              35.70           1
   *     6             5              66.96           1
   *     7           460               1.94           1
   */
  myMPU9250.setAccDLPF(MPU9250_DLPF_6);

  /*  Set accelerometer output data rate in low power mode (cycle enabled)
   *   MPU9250_LP_ACC_ODR_0_24          0.24 Hz
   *   MPU9250_LP_ACC_ODR_0_49          0.49 Hz
   *   MPU9250_LP_ACC_ODR_0_98          0.98 Hz
   *   MPU9250_LP_ACC_ODR_1_95          1.95 Hz
   *   MPU9250_LP_ACC_ODR_3_91          3.91 Hz
   *   MPU9250_LP_ACC_ODR_7_81          7.81 Hz
   *   MPU9250_LP_ACC_ODR_15_63        15.63 Hz
   *   MPU9250_LP_ACC_ODR_31_25        31.25 Hz
   *   MPU9250_LP_ACC_ODR_62_5         62.5 Hz
   *   MPU9250_LP_ACC_ODR_125         125 Hz
   *   MPU9250_LP_ACC_ODR_250         250 Hz
   *   MPU9250_LP_ACC_ODR_500         500 Hz
   */
  //myMPU9250.setLowPowerAccDataRate(MPU9250_LP_ACC_ODR_500);

  /* sleep() sends the MPU9250 to sleep or wakes it up. 
   * Please note that the gyroscope needs 35 milliseconds to wake up.
   */
  //myMPU9250.sleep(true);

 /* If cycle is set, and standby or sleep are not set, the module will cycle between
   *  sleep and taking a sample at a rate determined by setLowPowerAccDataRate().
   */
  //myMPU9250.enableCycle(true);

  /* You can enable or disable the axes for gyrometer and/or accelerometer measurements.
   * By default all axes are enabled. Parameters are:  
   * MPU9250_ENABLE_XYZ  //all axes are enabled (default)
   * MPU9250_ENABLE_XY0  // X, Y enabled, Z disabled
   * MPU9250_ENABLE_X0Z   
   * MPU9250_ENABLE_X00
   * MPU9250_ENABLE_0YZ
   * MPU9250_ENABLE_0Y0
   * MPU9250_ENABLE_00Z
   * MPU9250_ENABLE_000  // all axes disabled
   */
  //myMPU9250.enableAccAxes(MPU9250_ENABLE_XYZ);
  
}

void loop() {
  xyzFloat accRaw = myMPU9250.getAccRawValues();
  xyzFloat accCorrRaw = myMPU9250.getCorrectedAccRawValues();
  xyzFloat gValue = myMPU9250.getGValues();
  float resultantG = myMPU9250.getResultantG(gValue);
  
  Serial.println("Raw acceleration values (x,y,z):");
  Serial.print(accRaw.x);
  Serial.print("   ");
  Serial.print(accRaw.y);
  Serial.print("   ");
  Serial.println(accRaw.z);

  Serial.println("Corrected ('calibrated') acceleration values (x,y,z):");
  Serial.print(accCorrRaw.x);
  Serial.print("   ");
  Serial.print(accCorrRaw.y);
  Serial.print("   ");
  Serial.println(accCorrRaw.z);

  Serial.println("g values (x,y,z):");
  Serial.print(gValue.x);
  Serial.print("   ");
  Serial.print(gValue.y);
  Serial.print("   ");
  Serial.println(gValue.z);

  Serial.print("Resultant g: ");
  Serial.println(resultantG); // should always be 1 g if only gravity acts on the sensor.
  Serial.println();
  
  delay(1000);
}

 

Initialization and offsets

The function init() first performs a reset of the MPU9250 and writes default values to some registers. init() returns false if the MPU9250 should not be responsive, otherwise it returns true.

If the MPU9250 is positioned flat, i.e. horizontal x,y-plane, only the acceleration due to gravity acts on it. Accordingly, the g-values for the x- and y-axis should be zero and for the z-axis it should be one. However, these values are more or less shifted. The function autoOffset() measures the offset values which are subtracted from future measured values. The slope, on the other hand, had not to be corrected for the modules I tried out. In the 2g range, the difference between the minimum and maximum raw acceleration values was quite close to the expected 2 x 16348 (i.e. 2x 1015). autoOffset() only works reliably if:

  • the module is positioned flat with its x,y-plane,
  • is not moved, and
  • the function is called as the first function in the setup (because it changes some settings)

Alternatively, you can use the setAccOffsets() function. It results in less good zero values, but is more accurate with larger angles, and you don’t have to position the module when the program starts.

Other settings

The function setAccRange() sets the range for the acceleration measurements.

To control the data rate, you use the function setSampleRateDivider(divider).

\text{data rate}=\frac{1}{0+divider}\;[\text{kHz}]

However, this only works if the digital low pass filter (DLPF) is activated and its mode is 1 to 6. The DLPF is (de-)activated done with enableAccDLPF(true/false). You choose the level with setAccDLPF(). The higher the level, the lower the noise. Only level 7 does not fit in this series in this respect. The disadvantage of high smoothing is reduced response time. This means that when the acceleration changes, it takes a while for the MPU9250 to output the correct value. Details of these delay times can be found in the sketch.

With sleep(), you put the accelerometer and the gyroscope to sleep. However, this does not apply to the magnetometer, as it has a certain life of its own (not sure if this translates well).

If you don’t want to activate all axes, you can use enableAccAxes() to change the default setting. The parameters are explained in the sketch.

I explain the functions setCycle() and setLowPowerAccDataRate() elsewhere.

The results

You can query the measurement results with the following functions:

  • getAccRawValues() returns the raw values of acceleration as they are in the MPU9250 data registers
  • getCorrectedAccRawValues() reads the raw values and corrects them by the offsets
  • getGValues() returns the acceleration values in g (based on the corrected raw data)
  • getResultantG(gValue) calculates the resulting acceleration from a g-value triple, i.e. the magnitude of the sum of the three vectors
\text{resultantG}=\sqrt{(gValue.x)^2+(gValue.y)^2+(gValue.z)^2} \;\;\text{[g]}

If only gravity affects the MPU9250, the resultant should always be 1. With the resultant function, you can easily measure accelerations without having to align the movement to an axis. Simply subtract 1 to exclude the earth’s acceleration.

The output of MPU9250_acceleration_data.ino

And this is the output on the serial monitor:

MPU9250 Example sketch: Output of MPU9250_acceleration_data.ino
Output of MPU9250_acceleration_data.ino

Example sketch 2: MPU9250_gyroscope_data

Since you already know some functions, the explanation of the gyroscope functions can be much shorter.

#include <MPU9250_WE.h>
#include <Wire.h>

MPU9250_WE myMPU9250 = MPU9250_WE(0x68);

void setup() {
  Serial.begin(115200);
  Wire.begin();
  if(!myMPU9250.init()){
    Serial.println("MPU9250 does not respond");
  }
  else{
    Serial.println("MPU9250 is connected");
  }

  /* The slope of the curve of acceleration vs measured values fits quite well to the theoretical 
   * values, e.g. 16384 units/g in the +/- 2g range. But the starting point, if you position the 
   * MPU9250 flat, is not necessarily 0g/0g/1g for x/y/z. The autoOffset function measures offset 
   * values. It assumes your MPU9250 is positioned flat with its x,y-plane. The more you deviate 
   * from this, the less accurate will be your results.
   * The function also measures the offset of the gyroscope data. The gyroscope offset does not   
   * depend on the positioning.
   * This function needs to be called at the beginning since it can overwrite your settings!
   */
  Serial.println("Position you MPU9250 flat and don't move it - calibrating...");
  delay(1000);
  myMPU9250.autoOffsets();
  Serial.println("Done!");
  
  /*  The gyroscope data is not zero, even if you don't move the MPU9250. 
   *  To start at zero, you can apply offset values. These are the gyroscope raw values you obtain
   *  using the +/- 250 degrees/s range. 
   *  Use either autoOffset or setGyrOffsets, not both.
   */
  //myMPU9250.setGyrOffsets(45.0, 145.0, -105.0);
  
 /*  You can enable or disable the digital low pass filter (DLPF). If you disable the DLPF, you 
   *  need to select the bandwidth, which can be either 8800 or 3600 Hz. 8800 Hz has a shorter delay,
   *  but higher noise level. If DLPF is disabled, the output rate is 32 kHz.
   *  MPU9250_BW_WO_DLPF_3600 
   *  MPU9250_BW_WO_DLPF_8800
   */
  myMPU9250.enableGyrDLPF();
  //myMPU9250.disableGyrDLPF(MPU9250_BW_WO_DLPF_8800); // bandwidth without DLPF
  
  /*  Digital Low Pass Filter for the gyroscope must be enabled to choose the level. 
   *  MPU9250_DPLF_0, MPU9250_DPLF_2, ...... MPU9250_DPLF_7 
   *  
   *  DLPF    Bandwidth [Hz]   Delay [ms]   Output Rate [kHz]
   *    0         250            0.97             8
   *    1         184            2.9              1
   *    2          92            3.9              1
   *    3          41            5.9              1
   *    4          20            9.9              1
   *    5          10           17.85             1
   *    6           5           33.48             1
   *    7        3600            0.17             8
   *    
   *    You achieve lowest noise using level 6  
   */
  myMPU9250.setGyrDLPF(MPU9250_DLPF_6);

  /*  Sample rate divider divides the output rate of the gyroscope and accelerometer.
   *  Sample rate = Internal sample rate / (1 + divider) 
   *  It can only be applied if the corresponding DLPF is enabled and 0<DLPF<7!
   *  Divider is a number 0...255
   */
  myMPU9250.setSampleRateDivider(99);


  /*  MPU9250_GYRO_RANGE_250       250 degrees per second (default)
   *  MPU9250_GYRO_RANGE_500       500 degrees per second
   *  MPU9250_GYRO_RANGE_1000     1000 degrees per second
   *  MPU9250_GYRO_RANGE_2000     2000 degrees per second
   */
  myMPU9250.setGyrRange(MPU9250_GYRO_RANGE_250);
  
  /* sleep() sends the MPU9250 to sleep or wakes it up. 
   * Please note that the gyroscope needs 35 milliseconds to wake up.
   */
  //myMPU9250.sleep(true);

  /* This is a low power standby mode for the gyro function, which allows quick enabling. 
   * (see data sheet for further information)
   */
  //myMPU9250.enableGyrStandby(true);

  
  /* You can enable or disable the axes for gyroscope and/or accelerometer measurements.
   * By default all axes are enabled. Parameters are:  
   * MPU9250_ENABLE_XYZ  //all axes are enabled (default)
   * MPU9250_ENABLE_XY0  // X, Y enabled, Z disabled
   * MPU9250_ENABLE_X0Z   
   * MPU9250_ENABLE_X00
   * MPU9250_ENABLE_0YZ
   * MPU9250_ENABLE_0Y0
   * MPU9250_ENABLE_00Z
   * MPU9250_ENABLE_000  // all axes disabled
   */
  //myMPU9250.enableGyrAxes(MPU9250_ENABLE_000);  
}

void loop() {
  xyzFloat gyrRaw = myMPU9250.getGyrRawValues();
  xyzFloat corrGyrRaw = myMPU9250.getCorrectedGyrRawValues();
  xyzFloat gyr = myMPU9250.getGyrValues();
  
  Serial.println("Gyroscope raw values (x,y,z):");
  Serial.print(gyrRaw.x);
  Serial.print("   ");
  Serial.print(gyrRaw.y);
  Serial.print("   ");
  Serial.println(gyrRaw.z);

  Serial.println("Corrected gyroscope raw values (x,y,z):");
  Serial.print(corrGyrRaw.x);
  Serial.print("   ");
  Serial.print(corrGyrRaw.y);
  Serial.print("   ");
  Serial.println(corrGyrRaw.z);

  Serial.println("Gyroscope Data in degrees/s (x,y,z):");
  Serial.print(gyr.x);
  Serial.print("   ");
  Serial.print(gyr.y);
  Serial.print("   ");
  Serial.println(gyr.z);

  Serial.println("*********************************");

  delay(1000);
}

 

The setting options for the gyroscope

Again, you can use the autoOffsets() function. The gyroscope should return zero for all axes in the non-moving state, or at least fluctuate around zero. However, you will notice some offset that is independent of the positioning (tilt). Alternatively, use the values determined in the +/-250 °/s measuring range for setGyrOffsets(). I’ll come back to that when we talk about the calibration sketch.

You activate the low-pass filter (DLPF) with enableGyrDLPF(). If you disable the DLPF, you can still choose between the two bandwidths 8800 and 3600 Hz. Therefore, you have to pass the corresponding parameters to the disableGyrDLPF() function. You choose the level of the low-pass filter with setGyrDLPF(). Here, too, a stronger filter leads to greater delays. Similarly, level 7 does not fit in the series. Level 6 provides the lowest noise.

Other features:

  • setSampleRateDivider() works like the accelerometer
  • setGyrRange() sets the range
  • enableGyrStandby() sends the gyroscope into a kind of half-sleep, which allows a faster wake-up than from sleep mode (unfortunately I didn’t find concrete data)
  • enableGyrAxes() activates or deactivates the axes
  • getGyrRawValues() provides the currently available raw data
  • getCorrectedGyrRawValues() subtracts the offsets from the raw data and provides the corrected data
  • getGyrValues() provides the gyroscope data in degrees/second, based on the corrected raw data
Output of MPU9250_gyroscope_data.ino

The first values in the output below were determined without moving the MPU9250. For the second set of values, I rotated the module around its x-axis.

MPU9250 Example sketch: Output of MPU9250_gyroscope_data.ino
Output of MPU9250_gyroscope_data.ino

Example sketch 3: MPU9250_magnetometer_data.ino

The magnetometer (AK8963) behaves like a separate component. It has its own I2C address (0x0C), own registers and must therefore also be initialized separately. If you connect the MPU9250 module to the power supply and use an I2C scanner, you will only detect the I2C address of the MPU9250. Run the magnetometer sketch and then switch back to the I2C scanner without disconnecting the MPU9250 from power supply. Now you can also see the address of the magnetometer. This is because the I2C line to the magnetometer must first be switched through in the MPU9250. To achieve this, initMagnetometer() activates a “pass through” bit in the MPU9250.

#include <MPU9250_WE.h>
#include <Wire.h>

MPU9250_WE myMPU9250 = MPU9250_WE(0x68);

void setup() {
  Serial.begin(115200);
  Wire.begin();
  if(!myMPU9250.init()){
    Serial.println("MPU9250 does not respond");
  }
  else{
    Serial.println("MPU9250 is connected");
  }
  if(!myMPU9250.initMagnetometer()){
    Serial.println("Magnetometer does not respond");
  }
  else{     Serial.println("Magnetometer is connected");
  }

  /* You can choose the following operational modes
   * AK8963_PWR_DOWN            power down (default)
   * AK8963_TRIGGER_MODE        single shot (manual start needed)
   * AK8963_CONT_MODE_8HZ       continuous at 8Hz sample rate
   * AK8963_CONT_MODE_100HZ     continuous at 100Hz sample rate 
   * 
   * In trigger mode the AK8963 goes into power down after the measurement
   */
  myMPU9250.setMagOpMode(AK8963_CONT_MODE_8HZ);
  
  /* In continuous mode you need to wait for the first data to be available (after 
   * max 9 ms). If you comment the while loop below you will probably obtain zero. 
   * For the trigger mode I have implemented that the AK8983 will always wait for 
   * available data. In continuous mode you will read the data which is there at 
   * the moment. It can be up to 10 ms "old" at 100 Hz or up to 125 ms at 8 Hz.
   */
  while(!myMPU9250.isMagDataReady()){}
}

void loop() {
  //myMPU9250.startMagMeasurement(); // needed in trigger mode
  xyzFloat magValue = myMPU9250.getMagValues(); // returns magnetic flux density [µT] 

  Serial.println("Magnetometer Data in µTesla: ");
  Serial.print(magValue.x);
  Serial.print("   ");
  Serial.print(magValue.y);
  Serial.print("   ");
  Serial.println(magValue.z);

  delay(1000);
}

 

The settings for the magnetometer

The magnetometer has a power-down mode, a trigger mode and two continuous modes. The continuous modes differ in the data rate, namely 8 or 100 Hz. You’re setting the mode with setMagOpMode().

You can query magnetometer values with getMagValues(). The result is output in microtesla. The function returns the values that are currently in the data store. In 8 Hz mode the data can be up to 125 ms “old” and after initialization the first value can be zero because this is the default value after reset. To wait for the next magnetometer value, you can use isMagDataReady() in a while loop like I did in the example sketch.

In trigger mode, you must initialize a measurement with startMagMeasurement(). In the background, the function uses isMagDataReady() to wait for current measurement to be completed.

Output of MPU9250_magnetometer_data.ino
MPU9250 example sketch: Output of MPU9250_magnetometer_data.ino
Output of MPU9250_gyroscope_data.ino

The results were a little sobering at first. I expected to be able to use the MPU9250 as a compass without any problems.

In Central Europe, the magnetic flux density of the Earth’s magnetic field is about 20 microns in the horizontal and around 44 µT in the vertical (source: German Wikipedia). Accordingly, the difference between the maximum and minimum values when rotating the module horizontally and vertically should be 40 and 88 micrometers, respectively. You, like me, probably won’t measure that because:

  1. Again, there are offsets.
  2. You are probably measuring inside, and depending on the design of the house, the geomagnetic field is shielded.
  3. When measuring on the breadboard, the jumper cables and metal inside the breadboard can influence the measurements.

I had also played around with strong magnets and magnetized my breadboard. Only when connecting with jumper cables only from below and without breadboard the results made more sense.

I haven’t (yet) pursued using the magnetometer as a compass. According to what I have read in other articles for other magnetometers, this seems to be a science in itself. At least if you don’t just want to find north and south.

Example sketch 4: MPU9250_all_data.ino

Using this sketch, you can determine the acceleration, gyroscope values, magnetometer values and temperature. You query the temperature with getTemperature(). I have already explained all other functions. You can find the sketch in the examples.

The result of the temperature measurement was about 2 degrees too high for all modules I tried. There are options to calibrate the thermometer in terms of zero point and slope. But I haven’t (yet) implemented that.

Setting offsets permanently

Example sketch 5: MPU9250_calibration

This sketch is designed to help you determine the offsets for the accelerometer and gyroscope. For this purpose, the lowest measuring range and the maximum low-pass filter are set first. This ensures high resolution and low noise.

For the offset determination of the accelerometer, you turn the MPU9250 slowly (!) around its axes and note the maximum and minimum raw values. It is best to have your elbows on the table when doing this, as any shaking will result in additional acceleration. You then use this data for the setAccOffsets() function. Internally, the offsets are determined from this. When you change the measuring range, the offsets are automatically adjusted. With this method, you no longer need to position the MPU9250 flat when starting the program. However, I would still recommend the function autoOffsets() to determine precise small tilt angles precisely.

For the gyroscope offsets you don’t have to rotate the MPU9250 because the offset is independent of the inclination. You take the values for the x-, y- and z-axis as parameters in setGyrOffsets().

You can find the sketch in the examples. I don’t show it here.

Output of MPU9250_calibration.ino
Example sketch MPU9250:
Output of MPU9250_calibration.ino
Output of MPU9250_calibration.ino

Outlook

The second part of the article is about angles, interrupts, the low-power mode and the FIFO buffer. Of course, you don’t have to wait since you can try the example sketches now.

Acknowledgement

I found the MPU9250 as a Fritzing component here – thanks to Randy!

I found the parts for the post picture as usual on Pixabay. The compass, the space shuttle and the thermometer are taken from OpenClipart-Vectors. The gyro is from InspiredImages (Anthony).

Leave a Reply

Your email address will not be published. Required fields are marked *