MPU9250 – 9-Axis Sensor Module – Part 2

About this post

This is now the continuation of the first part of my article about the 9-axis sensor MPU9250. In the last part I discussed the technical features and started to introduce my library MPU9250_WE. You can download it directly from GitHub or install it via the Arduino IDE Library Manager. 

I had shown you how to retrieve data from the accelerometer, gyroscope, magnetometer and thermometer and how the calibration works.

This part deals with the following topics:

  • Measuring angles
  • Interrupts
  • Low-Power / Cycle Mode
  • FIFO Buffer

The MPU9250 connected to an Arduino UNO

I used the wiring shown below for the example sketches:

The MPU9250 connected to an Arduino UNO
The MPU9250 connected to an Arduino UNO

Introduction to the Library MPU9250_WE – Part 2

Angles

You can use the (earth) acceleration data to calculate tilt angles. You need to make sure that no additional acceleration is applied to the MPU9250. I have implemented two methods for this.

Example sketch 6: MPU9250_angles_and_orientation

In this sketch, I simply calculate the angles between the axes and the horizontal from the arc sine of the acceleration value.

\alpha=\arcsin(\text{g-Wert})

For small angles this works perfectly, at larger angles the error increases. Why this is the case, I explained in my article about the MMA7361. Up to 60° the deviation in my experiments was less than one degree.

For this sketch, I recommend the autoOffset() method. With this function, you start at an angle of 0° for the x- and y-axis. For the z-axis you start at 90°. However, you will only get reasonable values for the tilt angles if you position the MPU9250 flat in its x,y-plane for calibration.

Here’s the sketch.

#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 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 DLPF 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);  
}

void loop() {
  xyzFloat gValue = myMPU9250.getGValues();
  xyzFloat angle = myMPU9250.getAngles();
  
/* For g-values the corrected raws are used */
  Serial.print("g-x      = ");
  Serial.print(gValue.x);
  Serial.print("  |  g-y      = ");
  Serial.print(gValue.y);
  Serial.print("  |  g-z      = ");
  Serial.println(gValue.z);

/* Angles are also based on the corrected raws. Angles are simply calculated by
   angle = arcsin(g Value) */
  Serial.print("Angle x  = ");
  Serial.print(angle.x);
  Serial.print("  |  Angle y  = ");
  Serial.print(angle.y);
  Serial.print("  |  Angle z  = ");
  Serial.println(angle.z);

  Serial.print("Orientation of the module: ");
  Serial.println(myMPU9250.getOrientationAsString());

  Serial.println();
  
  delay(1000);  
}

I have explained almost all the functions used here in part 1 of the article. These are the new functions:

  • getAngle() returns the angle of the x, y, and z axes to the horizontal in degrees.
    • g-values above 1 are truncated to 1 because the arc sine function is not defined for larger values.
  • getOrientationAsString() indicates which axis has the largest positive angle.
    • Possible return values are: x up, x down, y up, y down, z up, z down.
    • An alternative is getOrientation(). The return value is an enum (MPU9250_orientation). For the definition, look at MPU9250_WE.h.
Output of MPU9250_angles_and_orientation.ino

For the following output I have rotated the module around the y-axis, i.e. I have erected the x-axis.

Example sketch MPU9250 - MPU9250_angles_and_orientation.ino
Output of MPU9250_angles_and_orientation.ino

The sum of the angles of x and z should always be 90° in this process. However, this is obviously not the case. As already mentioned, the large angles are not exact.

Example sketch 7: MPU9250_pitch_and_roll.ino

This method uses multiple axes to calculate the angles. As a result, the values are more accurate at large angles than with the simple arc-sine method. On the other hand, at small angles, the latter method is preferable. To delineate the method, I used the nomenclature of other libraries and referred to the x-axis tilt angle as “pitch” and the y-axis tilt angle as “roll angle”.

pitch\; angle= \arctan \left(\frac{g_x}{\sqrt{\left|g_x\cdot g_y +g_z^2\right|}}\right)
roll\;angle = \arctan\left( \frac{g_y}{g_z} \right)

For comparison, I use both methods in this sketch. Try it out and choose what suits you more.

#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    (default)
   *  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 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);  
}

void loop() {
  xyzFloat angles = myMPU9250.getAngles();
  
/* This method provides quite precise values for x/y 
   angles up 60°. */
  Serial.print("Angle x = ");
  Serial.print(angles.x);
  Serial.print("  |  Angle y = ");
  Serial.print(angles.y);
  Serial.print("  |  Angle z = ");
  Serial.println(angles.z);

/* Pitch and roll consider all axes for calculation. According to my experience
   it provides more reliable results at higher angles (>60°) */
  float pitch = myMPU9250.getPitch();
  float roll  = myMPU9250.getRoll();
  
  Serial.print("Pitch   = "); 
  Serial.print(pitch); 
  Serial.print("  |  Roll    = "); 
  Serial.println(roll); 
  
  Serial.println();
  
  delay(1000); 
}

 

Two new functions are used here:

  • getPitch() returns the pitch angle (x-axis).
  • getRoll() returns the roll angle (y-axis).

Use of interrupts of the MPU9250

The MPU9250 has the following interrupts:

  • Wake on Motion (“WOM”): is triggered when an acceleration value threshold is exceeded.
  • Data Ready: is triggered when new measured values are available.
  • FIFO Overflow: is triggered when the FIFO memory is full (512 bytes).
  • FSYNC: an interrupt is triggered when a HIGH or LOW is attached to FSYNC (but I have not implemented this feature).

Example sketch 8: MPU9250_wake_on_motion_interrupt.ino

The name “Wake on Motion” suggests that the MPU9250 could wake up from sleep mode using this interrupt. However, this is not the case. It is a simple acceleration interrupt.

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

const int intPin = 2;
volatile bool motion = false;

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   (default)
   *  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
   *   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_125);

   /*  Set the interrupt pin:
   *  MPU9250_ACT_LOW  = active-low
   *  MPU9250_ACT_HIGH = active-high (default) 
   */
  myMPU9250.setIntPinPolarity(MPU9250_ACT_HIGH); 

  /*  If latch is enabled the interrupt pin level is held until the interrupt status 
   *  is cleared. If latch is disabled the interrupt pulse is ~50µs (default).
   */
  myMPU9250.enableIntLatch(true);

   /*  The interrupt can be cleared by any read or it will only be cleared if the interrupt 
   *  status register is read (default).
   */
  myMPU9250.enableClearIntByAnyRead(false); 

  /*  Enable/disable interrupts:
   *  MPU9250_DATA_READY 
   *  MPU9250_FIFO_OVF   
   *  MPU9250_WOM_INT    
   *  
   *  You can enable all interrupts.
   */
  myMPU9250.enableInterrupt(MPU9250_WOM_INT); 
  //myMPU9250.disableInterrupt(MPU9250_FIFO_OVF);

  /*  Set the Wake On Motion Threshold 
   *  Choose 1 (= 4 mg) ..... 255 (= 1020 mg); 
   */
  myMPU9250.setWakeOnMotionThreshold(128);  // 128 = ~0.5 g
  
   /*  Enable/disable wake on motion (WOM) and  WOM mode:
   *  MPU9250_WOM_DISABLE
   *  MPU9250_WOM_ENABLE
   *  ***
   *  MPU9250_WOM_COMP_DISABLE   // reference is the starting value
   *  MPU9250_WOM_COMP_ENABLE    // reference is the last value
   */
  myMPU9250.enableWakeOnMotion(MPU9250_WOM_ENABLE, MPU9250_WOM_COMP_DISABLE);

  /* 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.enableAccAxes(MPU9250_ENABLE_XYZ);
  attachInterrupt(digitalPinToInterrupt(intPin), motionISR, RISING);
  Serial.println("Turn your MPU9250 and see what happens...");
  
}

void loop() {
  xyzFloat gValue;

  if((millis()%1000) == 0){
    gValue = myMPU9250.getGValues();
    
    Serial.print(gValue.x);
    Serial.print("   ");
    Serial.print(gValue.y);
    Serial.print("   ");
    Serial.println(gValue.z);
  }
  if(motion){
    byte source = myMPU9250.readAndClearInterrupts();
    Serial.println("Interrupt!");
    if(myMPU9250.checkInterrupt(source, MPU9250_WOM_INT)){
      Serial.println("Interrupt Type: Motion");
      delay(1000);
    }
    motion = false;
    // if additional interrupts have occured in the meantime:
    myMPU9250.readAndClearInterrupts(); 
  }

}

void motionISR() {
  motion = true;
}

 

Here I use a number of additional functions to control the interrupts:

  • setIntPinPolarity() sets whether the interrupt pin is active-high or active-low.
  • enableLatch() controls whether the interrupt is output as a short signal at the interrupt pin, or whether the signal remains permanently at interrupt level until the interrupt is cleared (latch = locking).
  • With enableClearIntByAnyRead(), you set whether the interrupt is cleared with any read operation or only by reading the interrupt status register. The function only makes sense in combination with enableLatch().
  • With enableInterrupt() you choose which interrupt type you want to activate. If you want to enable multiple interrupts, then call function multiple times.
  • disableInterrupt() deactivates an interrupt.
  • setWakeOnMotionThreshold(value) determines at which acceleration an interrupt is triggered. “value” is the difference to a comparative value and can be selected between 1 (= 4 mg) and 255 (1.02 g).  The interrupt is not axis-specific. If you want to exclude certain axes, you need to disable them.
  • enableWakeOnMotion() expects two arguments. Argument 1 enables the interrupt or pauses it. With the second argument, you determine the comparative value mentioned above. This is either the initial value when activating the interrupt or the last reading.
  • readAndClearInterrupts() reads the interrupt status register and clears the active interrupt (if “latch” is enabled). The function returns the contents of the status register.
  • checkInterrupt() can be used to check if a particular interrupt has been triggered. You can also directly evaluate the return value of readAndClearInterrupt(). However, you have to look at MPU9250_WE.h to see how it is defined.
Output of MPU9250_wake_on_motion_interrupt.ino

By slowly erecting the x-axis of the MPU9250, I got the following output.

Example Skech MPU9250 - Edition of MPU9250_wake_on_motion_interrupt.ino
Output of MPU9250_wake_on_motion_interrupt.ino

With this sketch many parameters and functions were introduced. I suggest to plays around with them to better understand the effect.  

Example sketch 9: MPU9250_data_ready_interrupt_and_cycle.ino

Here I kill two birds with one stone. I explain the Data Ready Interrupt and Cycle Mode together in one sketch.

The cycle mode is a low-power mode in which the MPU9250 sleeps most of the time, saving power. At fixed intervals, it wakes up, measures an acceleration value and “falls asleep” again.

Note: The cycle mode is incompatible with the low-pass filter (DLPF). Leave it off. Accordingly, the sample rate divider is also ineffective. Likewise, you cannot record gyroscope data during wake-up phases (not least because the gyroscope takes a long 35 milliseconds to wake up). 

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

const int intPin = 2;
volatile bool dataReady = false;

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   (default)
   *  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(false);

  /*  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_0_24);

  /*  Set the interrupt pin:
   *  MPU9250_ACT_LOW  = active-low
   *  MPU9250_ACT_HIGH = active-high (default) 
   */
  myMPU9250.setIntPinPolarity(MPU9250_ACT_HIGH); 

  /*  If latch is enabled the Interrupt Pin Level is held until the Interrupt Status 
   *  is cleared. If latch is disabled the Interrupt Puls is ~50µs (default).
   */
  myMPU9250.enableIntLatch(true);

  /*  The Interrupt can be cleared by any read. Otherwise the Interrupt will only be
   *  cleared if the Interrupt Status register is read (default).
   */
  myMPU9250.enableClearIntByAnyRead(false); 

  /*  Enable/disable interrupts:
   *  MPU9250_DATA_READY 
   *  MPU9250_FIFO_OVF   
   *  MPU9250_WOM_INT    
   *  
   *  You can enable all interrupts.
   */
  myMPU9250.enableInterrupt(MPU9250_DATA_READY); 
  //myMPU9250.disableInterrupt(MPU9250_FIFO_OVF);

  /* 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 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.enableAccAxes(MPU9250_ENABLE_XYZ);
  attachInterrupt(digitalPinToInterrupt(intPin), dataReadyISR, RISING);
  
}

void loop() {
  if(dataReady){
    byte source = myMPU9250.readAndClearInterrupts();
    Serial.println("Interrupt!");
    if(myMPU9250.checkInterrupt(source, MPU9250_DATA_READY)){
      Serial.println("Interrupt Type: Data Ready");
      printData();
    }
    dataReady = false;
    myMPU9250.readAndClearInterrupts(); // if additional interrupts have occured in the meantime
  }
}

void printData(){
  xyzFloat gValue;
  gValue = myMPU9250.getGValues();
    
  Serial.print(gValue.x);
  Serial.print("   ");
  Serial.print(gValue.y);
  Serial.print("   ");
  Serial.println(gValue.z);
}

void dataReadyISR() {
  dataReady = true;
}

 

The following new functions are used here:

  • setLowPowerAccDataRate() sets the wake-up / data rate. You can set values between 0.24 and 500 Hz.
  • enableCycle() enables or deactivates cycle mode.
Output of MPU9250_data_ready_and_cycle.ino

As I have set the data rate to 0.24 Hz, the MPU9250 wakes up about every four seconds and takes a measurement. The Data Ready Interrupt controls the measurement recording and output rate. You see that the main loop of the sketch does not contain any delay.

Example sketch MPU9250 - Output of MPU9250_dataready_interrupt_and_cycle.ino
Output of MPU9250_dataready_interrupt_and_cycle.ino

Using the FIFO memory of the MPU9250

The FIFO (first in, first out) memory is a kind of recorder for measurement data. The data that the MPU9250 writes first, it also reads first – hence the name. The FIFO is 512 bytes in size. You determine which data is written to the FIFO. This also determines how many records fit into the FIFO. An acceleration or gyroscope value requires two bytes. It is the raw data which is stored. An x,y,z-triple therefore requires 6 bytes. I have implemented the following options:

  • Acceleration values as triples – 85 complete data triples fit into the FIFO (512 / 6 = 85, rest 2).
  • Gyroscope values as triples – 85 complete triples can be recorded.
  • Acceleration and gyroscope values – 42 complete records can be written into the FIFO (512 / 12 = 42, rest 8).

Moreover, the MPU9250 can also record gyroscope data of individual axes, temperatures or data from external sensors. However, I did not implement this to keep it simple.

To read the FIFO register, the FIFO data is pushed sequentially into a single data register of one byte. From there, the bytes are read individually and “reassembled” into meaningful data. However, for the individual bytes it is not recognizable whether it is the MSB or LSB of an acceleration or a gyroscope value. So, you have to count and read the data in the right order. This can only be automated to a certain extent. And in order not to make it too complex and thus error-prone for the user, I did not implement all options.

Example sketch 10: MPU9250_FIFO_stop_when_full

In this first FIFO Sketch, the MPU9250 stops data recording when the FIFO is full. This means that you specify the start of the recording. The recording time depends on the data rate and the recorded data types. By the way, a combination with the cycle mode does not work.

When the FIFO is full, you can be informed about it by the FIFO overflow interrupt.

When recording acceleration and gyroscope data, the MPU9250 always writes the acceleration data to the FIFO first. Since it stops in this example when the FIFO is full, the FIFO data begins with an acceleration value. This is different in the next example. For proper reading this is important!

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

const int intPin = 2;
volatile bool fifoFull = false;

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);

  /*  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_5);

  /*  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);

  /*  MPU9250_ACC_RANGE_2G      2 g   (default)
   *  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 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_0);

  /*  Set the interrupt pin:
   *  MPU9250_ACT_LOW  = active-low
   *  MPU9250_ACT_HIGH = active-high (default) 
   */
  //myMPU9250.setIntPinPolarity(MPU9250_ACT_LOW); 

  /*  If latch is enabled the interrupt pin level is held until the interrupt status 
   *  is cleared. If latch is disabled the interrupt pulse is ~50µs (default).
   */
  myMPU9250.enableIntLatch(true);

  /*  The interrupt can be cleared by any read or it will only be cleared if the interrupt 
   *  status register is read (default).
   */
  //myMPU9250.enableClearIntByAnyRead(true); 

  /*  Enable/disable interrupts:
   *  MPU9250_DATA_READY 
   *  MPU9250_FIFO_OVF   
   *  MPU9250_WOM_INT    
   *  
   *  You can enable all interrupts.
   */
  myMPU9250.enableInterrupt(MPU9250_FIFO_OVF); 
  //myMPU9250.disableInterrupt(MPU9250_FIFO_OVF);

  /*  Set the wake on motion threshold (WOM threshold)
   *  Choose 1 (= 4 mg) ..... 255 (= 1020 mg); 
   */
  //myMPU9250.setWakeOnMotionThreshold(170);
  
  /*  Enable/disable wake on motion (WOM) and  WOM mode:
   *  MPU9250_WOM_DISABLE
   *  MPU9250_WOM_ENABLE
   *  ***
   *  MPU9250_WOM_COMP_DISABLE   // reference is the starting value
   *  MPU9250_WOM_COMP_ENABLE    // reference is tha last value
   */
  //myMPU9250.enableWakeOnMotion(MPU9250_WOM_ENABLE, MPU9250_WOM_COMP_DISABLE);

  /* There are two different FIFO modes:
   *  MPU9250_CONTINUOUS --> samples are continuously stored in FIFO. If FIFO is full 
   *  new data will replace the oldest.
   *  MPU9250_STOP_WHEN_FULL --> self-explaining
   */
  //myMPU9250.setFifoMode(MPU9250_STOP_WHEN_FULL); // used below, but explained here
  
  /* The argument of startFifo defines the data stored in the FIFO
   * MPU9250_FIFO_ACC --> Acceleration Data ist stored in FIFO  
   * MPU9250_FIFO_GYR --> Gyroscope data is stored in FIFO   
   * MPU9250_FIFO_ACC_GYR --> Acceleration and Gyroscope Data is stored in FIFO
   * The library does not (yet) support storing single gyroscope axes data, temperature 
   * or data from slaves. 
   */
  //myMPU9250.startFifo(MPU9250_FIFO_ACC); // used below, but explained here
  
  /* stopFifo(): 
   * - stops additional writes into Fifo 
   * - clears the data type written into Fifo (acceleration and/or gyroscope
   */
  //myMPU9250.stopFifo(); // used below, but explained here

  /* sets the Fifo counter to zero */ 
  //myMPU9250.resetFifo(); // used below, but explained here

  
  /* 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);

  /* 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.enableAccAxes(MPU9250_ENABLE_X0Z);
  //myMPU9250.enableGyrAxes(MPU9250_ENABLE_XY0);

  attachInterrupt(digitalPinToInterrupt(intPin), eventISR, RISING);
  myMPU9250.setFifoMode(MPU9250_STOP_WHEN_FULL);
  myMPU9250.enableFifo(true);
  delay(100); // in some cases a delay after enabling Fifo makes sense
}

void loop() {
  
  countDown();
  myMPU9250.readAndClearInterrupts();
  fifoFull = false;
  myMPU9250.startFifo(MPU9250_FIFO_ACC_GYR);

  while(!fifoFull){}
  myMPU9250.stopFifo();
  printFifo();
  myMPU9250.resetFifo();

  Serial.println("For another series of measurements, enter any key and send");
  
  while(!(Serial.available())){}
  Serial.read();
  Serial.println();
 }

void printFifo(){
  int count = myMPU9250.getFifoCount();
  int dataSets = myMPU9250.getNumberOfFifoDataSets(); 
  Serial.print("Bytes in Fifo: ");
  Serial.println(count);
  Serial.print("Data Sets: ");
  Serial.println(dataSets);

  for(int i=0; i<dataSets; i++){
    xyzFloat gValue = myMPU9250.getGValuesFromFifo();
    xyzFloat gyr = myMPU9250.getGyrValuesFromFifo();
    Serial.print("Data set ");
    Serial.print(i+1);
    Serial.println(":");
    
    Serial.print(gValue.x);
    Serial.print("   ");
    Serial.print(gValue.y);
    Serial.print("   ");
    Serial.println(gValue.z);

    Serial.print(gyr.x);
    Serial.print("   ");
    Serial.print(gyr.y);
    Serial.print("   ");
    Serial.println(gyr.z);
  }
}

void countDown(){
  Serial.println("Move/turn your MPU9250 to obtain interesting data");
  Serial.println();
  delay(1000);
  Serial.print("Fifo collection begins in 3, "); 
  delay(1000);
  Serial.print("2, "); 
  delay(1000);
  Serial.print("1, "); 
  delay(1000);
  Serial.println("Now!");
}

void eventISR() {
  fifoFull = true;
}

 

Here a countdown starts the FIFO. You can also select other triggers, such as a WOM interrupt.

When the FIFO is full, this is indicated by an interrupt. Then the data is read and output. After that, a new FIFO recording can be started by sending any character via the serial monitor.

New functions

 The following new functions are used:

  • With setFifoMode(), you determine whether you use the continuous or the “stop-when-full” mode.
  • enableFifo() activates or deactivates the FIFO.
  • You pass startFiFo() the data types you want to write to the FIFO. This function also starts the counter.
  • With stopFifo(), you prevent that further data is written to the FIFO. This process is stopped anyway when the FIFO is full, but only temporarily. When you start reading, new space is created again. And that would be filled immediately with new data if you don’t call stopFifo()
  • getFifoCount() provides the FIFO counter reading, i.e. the number of bytes. When the FIFO is full, that’s 512. But you could theoretically stop recording before the FIFO is full.
  • getNumberOfFifoDataSets() calculates the number of records in the FIFO. In this example, a data record consists of an x,y,z acceleration data triple and an x,y,z gyroscope data triple and that is 12 bytes. 
  • getGValuesFromFifo() reads an x,y,z acceleration data triple and converts it to a g-value triple. Since the FIFO contains raw data, you must not change the parameters (i.e. range or offsets) between recording and reading.
  • getGyrValuesFromFifo() reads an x,y,z gyroscope data triple (raw data) and converts it to rotation values (°/s).
  • resetFifo() sets the FIFO counter to zero.

I need to stress once again that you must follow the order when reading out. In this example, you’ll alternately read acceleration data and gyroscope data and start with the acceleration data.

Output of MPU9250_FIFO_stop_when_full.ino

Here is an excerpt from the output of the sketch. In this example the Sample Rate Divider was 99. This means that the FIFO saves a data record every 10 milliseconds. 42 complete data sets fit into the FIFO. This means filling the FIFO requires 4.2 s under these conditions.

Example sketch MPU9250 - Output of MPU9250_FIFO_stop_when_full.ino
Output of MPU9250_FIFO_stop_when_full.ino

Example sketch 11: MPU9250_FIFO_continuous.ino

With this method, the FIFO continuously records the data types you specify. So, here you do not determine the start, but the end. If the FIFO is full before you stop, it deletes the oldest data for storing the new data.

The MPU9250 always writes complete records to the FIFO. This means you have a defined end. The first data record, on the other hand, is incomplete. This is because the FIFO does delete complete data sets. At 512 bytes, the data is brutally truncated. Therefore, an additional function is needed in this case. It finds the beginning of the first complete data set when reading the FIFO.

The example sketch uses a WOM interrupt as a stop for the FIFO. Rotate the MPU9250 to trigger the interrupt. To get interesting data, don’t turn it too fast.

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

const int intPin = 2;
volatile bool womEvent = false;

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);

  /*  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_5);

  /*  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);

  /*  MPU9250_ACC_RANGE_2G      2 g   (default)
   *  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_0);

  /*  Set the interrupt pin:
   *  MPU9250_ACT_LOW  = active-low
   *  MPU9250_ACT_HIGH = active-high (default) 
   */
  //myMPU9250.setIntPinPolarity(MPU9250_ACT_LOW); 

  /*  If latch is enabled the interrupt pin level is held until the interrupt status 
   *  is cleared. If latch is disabled the interrupt pulse is ~50µs (default).
   */
  myMPU9250.enableIntLatch(true);

  /*  The interrupt can be cleared by any read or it will only be cleared if the interrupt 
   *  status register is read (default).
   */
  //myMPU9250.enableClearIntByAnyRead(true); 

  /*  Enable/disable interrupts:
   *  MPU9250_DATA_READY 
   *  MPU9250_FIFO_OVF   
   *  MPU9250_WOM_INT    
   *  
   *  You can enable all interrupts.
   */
  myMPU9250.enableInterrupt(MPU9250_WOM_INT); 
  //myMPU9250.disableInterrupt(MPU9250_FIFO_OVF);

  /*  Set the wake on motion threshold (WOM threshold)
   *  Choose 1 (= 4 mg) ..... 255 (= 1020 mg); 
   */
  myMPU9250.setWakeOnMotionThreshold(225); // Interrupt bei 0.9 g
  
  /*  Enable/disable wake on motion (WOM) and  WOM mode:
   *  MPU9250_WOM_DISABLE
   *  MPU9250_WOM_ENABLE
   *  ***
   *  MPU9250_WOM_COMP_DISABLE   // reference is the starting value
   *  MPU9250_WOM_COMP_ENABLE    // reference is tha last value
   */
  myMPU9250.enableWakeOnMotion(MPU9250_WOM_ENABLE, MPU9250_WOM_COMP_DISABLE);

  /* There are two different FIFO modes:
   *  MPU9250_CONTINUOUS --> samples are continuously stored in FIFO. If FIFO is full 
   *  new data will replace the oldest.
   *  MPU9250_STOP_WHEN_FULL --> self-explaining
   */
  //myMPU9250.setFifoMode(MPU9250_STOP_WHEN_FULL); // used below, but explained here
  
  /* The argument of startFifo defines the data stored in the FIFO
   * MPU9250_FIFO_ACC --> Acceleration Data ist stored in FIFO  
   * MPU9250_FIFO_GYR --> Gyroscope data is stored in FIFO   
   * MPU9250_FIFO_ACC_GYR --> Acceleration and Gyroscope Data is stored in FIFO
   * The library does not (yet) support storing single gyroscope axes data, temperature 
   * or data from slaves. 
   */
  //myMPU9250.startFifo(MPU9250_FIFO_ACC); // used below, but explained here
  
  /* stopFifo(): 
   * - stops additional writes into Fifo 
   * - clears the data type written into Fifo (acceleration and/or gyroscope
   */
  //myMPU9250.stopFifo(); // used below, but explained here

  /* sets the Fifo counter to zero */ 
  //myMPU9250.resetFifo(); // used below, but explained here

  
  /* 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);

  /* 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.enableAccAxes(MPU9250_ENABLE_X0Z);
  //myMPU9250.enableGyrAxes(MPU9250_ENABLE_XY0);

  attachInterrupt(digitalPinToInterrupt(intPin), eventISR, RISING);
  myMPU9250.setFifoMode(MPU9250_CONTINUOUS);
  myMPU9250.enableFifo(true);
  delay(100); // in some cases a delay after enabling Fifo makes sense
  myMPU9250.startFifo(MPU9250_FIFO_ACC_GYR);

  Serial.println("Turn your MPU9250 around the x or y axis.");
  Serial.println("Waiting for wake-on-motion event...");
}

void loop() {
  if(womEvent){
    myMPU9250.stopFifo();
    printFifo();
    myMPU9250.resetFifo();
    myMPU9250.startFifo(MPU9250_FIFO_ACC_GYR);
   
    Serial.println("For another series of measurements, enter any key and send");
    
    while(!(Serial.available())){}
    Serial.read();
    Serial.println();
    myMPU9250.readAndClearInterrupts();
    womEvent = false;
  }
}

void printFifo(){
  myMPU9250.findFifoBegin(); /* this is needed for continuous Fifo mode. The Fifo buffer ends with a 
  complete data set, but the start is within a data set. 512/6 or 512/12 */
  int count = myMPU9250.getFifoCount();
  int dataSets = myMPU9250.getNumberOfFifoDataSets(); 
  Serial.print("Bytes in Fifo: ");
  Serial.println(count);
  Serial.print("Data Sets: ");
  Serial.println(dataSets);

  for(int i=0; i<dataSets; i++){
    /* if you read acceleration (g) and gyroscope values you need to start with g values */
    xyzFloat gValue = myMPU9250.getGValuesFromFifo();
    xyzFloat gyr = myMPU9250.getGyrValuesFromFifo();
    Serial.print("Data set ");
    Serial.print(i+1);
    Serial.println(":");
    
    Serial.print(gValue.x);
    Serial.print("   ");
    Serial.print(gValue.y);
    Serial.print("   ");
    Serial.println(gValue.z);

    Serial.print(gyr.x);
    Serial.print("   ");
    Serial.print(gyr.y);
    Serial.print("   ");
    Serial.println(gyr.z);
  }
}

void eventISR() {
  womEvent = true;
}

 

The Sketch uses only one new function, which is findFifoBegin(). It calculates where the first complete record begins in the FIFO. Then it reads the data up to that point and discards it. Therefore, getFifoCount() does not return 512 anymore after callingfindFifoBegin(). Just try and call getFifoCount() before findFifoBegin() and see what happens.

Output of MPU9250_FIFO_continuous.ino

Here’s what the output might look like:

Example sketch MPU9250 - Output of MPU_9250_FIFO_continuous.ino
Excerpt of the output of MPU_9250_FIFO_continuous.ino

Homework for you: play around with the parameters for FIFO. For example, select a different sample rate or stop the FIFO before it’s full. For the latter, you will see that reading still works. Moreover, you can try to record only acceleration or gyroscope data.

Other

Example sketch 12: MPU9250_blank_allsettings.ino

The examplee sketch MPU9250_blank_all_settings.ino is just a template that contains all the setting functions. This could be a good basis for your own sketches.

3 thoughts on “MPU9250 – 9-Axis Sensor Module – Part 2

  1. Thanks. I ´m going to try your advice. I am using an unofficial arduino module, the connection is the same as yours.

  2. Hi! I have a Arduino Mega 2560, I use your code. But when I use Monitor Serie, I read MPU9250 does not respond
    Magnetometer does not respond. what is the problem??? I use pins 21 and 22 for SDA and SLC

    1. Hi Franco, I tried it with a MEGA 2560 and it works with pins 21 / 22 as I2C pins. Maybe you can check first if you have a I2C connection at all. For this you can use an I2C Scannersketch. You find one here:

      https://wolles-elektronikkiste.de/en/i2c-scanner?lang=en

      If the scanner does not find any I2C address (should be 0x68) you could try to attach additional pull-up resistors to the I2C lines. If it also does not work check the power supply for the modules and maybe replace the jumper cables or wahtever you use for connection.

      If the scanner finds the right address, but the device still does not respond then it becomes difficult. Which example sketch have you tried? Did you change anything? Do you use a module like the one I used in my article?

Leave a Reply

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