Saturday, September 26, 2015

How to Setup the MPU-9150 9-Axis Accelerometer, Gyro, & Compass with an Arduino

The MPU-9150 9-Axis Accelerometer, Gyro, & Compass (hereafter referred to as just "The MPU") gives you a lot of info that can be used in some very creative ways. But for my purposes, I'm only looking for a few key points of data; namely, the magnetic heading of the sensor, its roll/pitch/yaw, and the rate at which it is rolling/pitching/yawing. So I'm going to explain how to set this nifty little chip up, calibrate it (which is absolutely necessary!), and declare floating-point variables for use later on. As with the spirit of King Tide Sailing, this is written for the person who has no clue what they're doing--so don't worry if you're like me and aren't a programmer during the day.

Recommended Gear

Setting Up the Arduino

This part is pretty simple. Head over to the Arduino Getting Started page and get familiar with the Arduino. In a nutshell, you download their IDE program, write your code in said program, upload it to the Arduino, and then use the program's Serial Monitor to make sure it's working correctly. But since we wan't to do more than just make the little LED light blink (which is a good starter program, I think), we have to include Libraries that tell the Arduino how to interface with other devices... such as the MPU.

Problem is, it's really difficult to find a good Library for the MPU that spits out the information I need, but doesn't require a separate computer program to decode in the information. It all has to be contained in the Arduino, since this is going on my sailboat and it won't be hooked up to my laptop. The reason why this is a particularly important issue to resolve is because the MPU is pretty worthless without calibration, and the calibration data needs to be saved on the Arduino itself. Most Libraries and calibrators save the data on your end machine to filter the output before you do anything. Like I said before, that's not going to work for our application.

Fortunately, there is a library out there that does exactly that. It's Richard Tech's RTIMULib-Arduino, specifically for the Arduino, available at GitHub. This is a pretty simple library that saves the compass calibration data to the Arduino's EEPROM, or it's "read-only but writable memory." But if you go to the GitHub page, you'll see that in big bold letters it says, "Please note that this library is no longer supported." Don't panic. That's totally fine. And just in case something happens over there, I've uploaded the library here, which is the exact copy I'm using. Please note, however, that you should always go to the GitHub page for the most recent software and that there's no guarantee mine will work with updates.

So go over to the RTIMULib-Arduino GitHub page and click the "Download Zip" button on the right side (or just download the zip above). Unzip and copy its contents over to your Arduino folder such that it looks like the screenshot here. Next, open up the Arduino IDE, open up the "sketch" (which is the fancy term for program) titled "ArduinoMagCal.ino" which is in the ArduinoMagCal folder. Ensure your Arduino is connected to your laptop (and you should have already made sure you can upload code when you went through the starter guide at the Arduino's website), and click the right arrow at the top to upload the sketch to the Arduino.

Great, now the code is loaded onto the Arduino--we just have to connect the MPU itself for it to work.

Connecting the MPU-9150 to the Arduino

Plugin the soldering iron, we're about to get down and dirty. You are basically looking at only four wires: power, ground, SDA, and SCL. Ideally, you should already have this mounted in your box or wherever it will be for use, so that it is calibrated in exactly the same spot it will be used. But for this guide, I'm just going to do it at my dining room table. Here's the wiring guide:

Arduino MPU-9150
3.3V VCC
Ground Ground

Pretty simple, right? Using the male end of a jumper cable, plug each wire into it's respective pin on the Arduino. Now, you have to be very careful with this next step so you don't cross solder the pin headers. I strongly recommend you break off four header such that they stay connected to each other, and then solder them into the four pins on the MPU. They're all right next to each other, so that part works out just fine. But now you have to plan ahead a little bit--my MPU is actually mounted on the underside of the lid of my box. The MPU's orientation is printed on the board itself, so make sure it's oriented such that the Mag X is pointed to the bow of the boat (please note that it is different than the gyro and accelerometer orientation). Anyways, what this means is that I actually had to solder the pins such that the male prong sticks out the bottom of the chip (since it will be hanging that way when the lid of the box is closed). Confused? Just take a look at the pics to see my setup (ignore the Raspberry Pi and other gadgets).

How do you solder such small connections without spilling solder over to other pins? Very carefully. Also, use the little stand that came with the iron to hold it steady! After you solder the pin headers, connect the female end of the jumper cables to their respective pins. And now it's ready to go!

Calibrating the MPU-9150

We've already loaded the code, the MPU is now connected--so all it's waiting for is to be initiated. To do this, open the Serial Monitor in the Arduino IDE (it's under Tools), and you should see something like the screen shot here. If you don't, here's a few pointers:
  • Ensure your baud rate is correct. The ArduinoMagCal sketch defines the baud rate as 115200, so make sure this matches what the Serial Monitor is looking for using the drop-down menu on the bottom right.
  • Close the Serial Monitor, and open it back up. This "re-initiates" the Arduino.
  • Unplug the Arduino, wait a few seconds for it to fully power down, then plug it back in and repeat.
Great. Now it's spitting out its min and max values for each compass. To calibrate it, simply spin the MPU along each axis, one at a time. So, pitch it completely forward so that it spins back over front 360 degrees. Then, roll it left to right all the way through another 360 degrees. Finally, rotate it through every magnetic heading--all 360 of them. The whole time, it'll be logging the min/max values, which it uses for correction. When you've cycled through each axis, type in the letter "s" in the text box at the top, click Send, and it will tell you "Mag cal data saved for device MPU-9150." And that's it! Your MPU-9150 is now calibrated!

Using the MPU-9150 with Arduino

Now we're ready to start using the MPU. Here is a sample sketch I made using the RTIMULib-Arduino sketch, but modified so as to declare variables for each parameter. You can download it right here, or just copy and paste it from below (it's also included in the zip file above).

 // This file is part of RTIMULib-Arduino  
 // Copyright (c) 2014-2015, richards-tech  
 // Permission is hereby granted, free of charge, to any person obtaining a copy of   
 // this software and associated documentation files (the "Software"), to deal in   
 // the Software without restriction, including without limitation the rights to use,   
 // copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the   
 // Software, and to permit persons to whom the Software is furnished to do so,   
 // subject to the following conditions:  
 // The above copyright notice and this permission notice shall be included in all   
 // copies or substantial portions of the Software.  
 // Further modified by King Tide Sailing ( 
 #include "Wire.h"  
 #include "I2Cdev.h"  
 #include "RTIMUSettings.h"  
 #include "RTIMU.h"  
 #include "RTFusionRTQF.h"   
 #include "CalLib.h"  
 #include "EEPROM.h"  
 RTIMU *imu;                      // the IMU object  
 RTFusionRTQF fusion;                 // the fusion object  
 RTIMUSettings settings;                // the settings object  
 // DISPLAY_INTERVAL sets the rate at which results are displayed  
 #define DISPLAY_INTERVAL 1000             // interval between pose displays  
 // SERIAL_PORT_SPEED defines the speed to use for the debug serial port  
 #define SERIAL_PORT_SPEED 4800  
 unsigned long lastDisplay;  
 unsigned long lastRate;  
 int sampleCount;  
 void setup()  
   int errcode;  
   imu = RTIMU::createIMU(&settings);            // create the imu object  
   Serial.print("ArduinoIMU starting using device "); Serial.println(imu->IMUName());  
   if ((errcode = imu->IMUInit()) < 0) {  
     Serial.print("Failed to init IMU: "); Serial.println(errcode);  
   if (imu->getCalibrationValid())  
     Serial.println("Using compass calibration");  
     Serial.println("No valid compass calibration data");  
   lastDisplay = lastRate = millis();  
   sampleCount = 0;  
   // Slerp power controls the fusion and can be between 0 and 1  
   // 0 means that only gyros are used, 1 means that only accels/compass are used  
   // In-between gives the fusion mix.  
   // use of sensors in the fusion algorithm can be controlled here  
   // change any of these to false to disable that sensor  
   Serial.println("Pose: Roll, Pitch, Magnetic Heading (measured in degrees)");  
   Serial.println("Gyro: Rate of Roll, Pitch, and Yaw (measured in degrees per second)");  
   Serial.println("Accl: Acceleration in X, Y, and Z (measured in g's, with 1 g equal to 9.8 meters per second squared)");  
   Serial.println("Time: Timestamp (measured in milliseconds from when the MPU-9150 was initiated)");  
 void loop()  
   unsigned long now = millis();  
   unsigned long delta;  
   int loopCount = 1;  
   while (imu->IMURead()) {                // get the latest data if ready yet  
     // this flushes remaining data in case we are falling behind  
     if (++loopCount >= 10)  
     fusion.newIMUData(imu->getGyro(), imu->getAccel(), imu->getCompass(), imu->getTimestamp());  
     if ((delta = now - lastRate) >= 1000) {  
       sampleCount = 0;  
       lastRate = now;  
     if ((now - lastDisplay) >= DISPLAY_INTERVAL) {  
       lastDisplay = now;  
       RTVector3 pose = fusion.getFusionPose();  
       RTVector3 gyro = imu->getGyro();  
       RTVector3 accel = imu->getAccel();  
       float r = M_PI/180.0f;      // degrees to radians  
       float d = 180.0f/M_PI;      // radians to degrees  
       float roll = pose.y()*d*-1;    // left roll is negative  
       float pitch = pose.x()*d;     // nose down is negative  
       float yaw = pose.z()*d;      // 0 Yaw = 270 magnetic, this gives left or right up to 180 degrees  
       float hdm = yaw-90;        // converts yaw to heading magnetic  
       if (yaw < 90 && yaw >= -179.99) {  
        hdm = yaw+270;  
       float ror = gyro.y()*-1*d;    // rate of roll  
       float rop = gyro.x()*d;      // rate of pitch  
       float rot = gyro.z()*d;      // rate of turn  
       float ax = accel.x();       // acceleration on the x-axis  
       float ay = accel.y();       // acceleration on the y-axis  
       float az = accel.z();       // acceleration on the z-axis  
       int ts = imu->getTimestamp();   // the timestamp  
       Serial.print("Pose: ");  
       Serial.print(" | ");  
       Serial.print(" | ");  
       Serial.print("Gyro: ");  
       Serial.print(" | ");  
       Serial.print(" | ");  
       Serial.print("Accl: ");  
       Serial.print(" | ");  
       Serial.print(" | ");  
       Serial.print("Time: ");  

You should now see something like this screenshot here. If not, make sure you are using 4800 baud (the standard for NMEA 0183, which is what we'll be using in the near future). Also, this has a refresh rate of 1000 milliseconds, which is fine for this--but you can change that to whatever you want by editing the DISPLAY_INTERVAL in the top of the code.

The raw output is kind of screwy, but fortunately it's quite easy to make it less screwy. For instance, roll is measured about the y-axis (hence the declaration of roll coming from the y axis data). Negative roll is to the left, negative pitch is nose down, and negative yaw is degrees to the left of 270 magnetic heading. There's a quick function to convert yaw into magnetic degrees, so you actually don't see the raw yaw value.

The raw output is also measured in radians or radians per second. That's useful if you're going to be using trigonometric functions later on (which I will be, in which case just convert it back to radians), but for reading this data, the degree is the ideal unit. So I included two variables, r and d, for converting to radians and to degrees respectively.

Also note that since this sensor was resting flat on the table, there is ~1 g in the Z direction for acceleration. If I didn't see that, I wouldn't care because gravity will have ceased to exist. So all this means is that the positive Z direction is pointed downward through the sensor--when in doubt, just check the schematic printed on the sensor itself. If you need to flip the directions, just multiply the variable by -1, as I did for a few.

9 Degrees of Freedom!

Now it's all set! You can edit the code as necessary to fit your own application, using the simplified version that I have supplied here to manipulate the variables. Next up, I'll show you how to make a maritime NMEA-0183 device that calculates magnetic heading, true heading, roll, pitch, yaw, and all their rates.


  1. Hello,
    Hope you are doing well there.
    Luckily, I found your work here.
    Have you ever tried the MPU-9250 for the same RTIMULib?
    In fact, I cannot let it work.
    If any suggestion, I am very appreciate.
    Good day.

  2. I have not. Have you checked the RTIMULib's library page? It says it supports it there:

    1. Hello,

      I am actually trying to establish a calibration with the MPU9250, but for some reason the model number is not detected properly: The serial monitor prints out :" ...calibrating device MPU-9150", and for some reason no measurements are printed out. I have no clue whats going on. There seems to be a problem with the IC2 connection. But why should it effect the false model detection?

    2. I would try new jumper cables and double check connections (SDA vs SCL, since they are in opposite order sometimes). Also, another commenter found that his jumper cable was internally broken and replacing it fixed the issue.

      As for me, I actually had to replace the MPU9150 because it was causing all sorts of errors, so I got an MPU-9250 instead and no problems there :). So it is possible that your specific sensor is bad. If all else fails, perhaps it would be useful to buy another one from a different manufacturer.

      Good luck.

  3. Hi,

    I find your blog interesting, and it really amazed me. Have you ever tried connecting two MPUs into one arduino to get your desired output with different results between them. That is what I'm trying to do right now, looking your blog as a reference to my project.

    Please guide me on my program. I hope you can help me on this.
    Thank you very much.


    1. I have not, but I don't think that would be a problem... so long as you change the I2C address of one of the mpu's, I don't suspect there would be an issue. With respect to a boat, it might be interesting to have two in different locations and average out the results. But that's pretty far into it as far as I'm concerned.

    2. Hmm.. That sounds great! Btw, I still have another question, how will I get the values for magnetometer/compass (x,y and z)?

    3. I'm not too sure about that part. The variable yaw declared in the code takes into account the magnometer values for x, y, and z, and gives you the heading of the MPU-9150. I would have to direct you to the RTIMULib's github page for more info (, or, as a last resort, do a google search for something like "raw mpu-9150 x y z magnometer. Or check out the Sparkfun's tutorial page (, but keep in mind if you use their library, I'm not sure if the calibration data is saved on the EEPROM of the Arduino, and thus calibration may be kind of finicky.

    4. Thanks, Connor. But how will I set the sensitivity of the accelerometer, and gyroscope? From what part of the program will I do the editing to set the sensitivity of them?

    5. And what program should I add? Please guide me. Thanks.

    6. What exactly is your end product goal? Do you want heading or attitude information, or something else?

      I *believe* what you'll probably want to do is change the fusion.setSlerpPower(0.02) to something other than 0.02, though what that actually affects is beyond me. It seems like that is the only user editable variable that is related to sensitivity.

    7. What I want is to show the raw value of the Accelerometer,Gyroscope and Magnetometer. Magnetometer is my problem I dont know how to get it. If i will not get the raw value is it ok if i will use the compass ? What do you think ?

      About the sensitivity Im just curious what will be the value if i will change the sensitivity .

    8. Phew, I'm not sure how to display the raw values of the magnetometer. If that's all you want, then I'd check out the Sparkfun Library I linked to above, since calibration isn't as important for that. Keep in mind I think the raw values are something like -10,000 to +10,000 for each axis, but I could be totally wrong about that. The sketch I have above integrates all three fields and spits out a heading, which I think is totally fine to use (the compass, that is).

    9. Do you mean that the compass is the magnetometer ?


    10. Yes. The MPU-9150 is a 9 Degree of Freedom sensor, meaning it has 3 gyroscopes (measuring roll, pitch, and yaw, relative to Earth's center, 1 for each axis: X,Y,Z, as depicted on the chip itself (see the picture in the blog post)), 3 accelerometers (against, 1 for each axis as in the picture), and 3 magnetometers (you guessed it: 1 for each axis). The magnetometers simply measure Earth's magnetic field, in each axis, relative to the Earth's surface.

      Think of each magnetometer as a regular compass. But just like with a regular compass, if you tilt it only a little bit, it stops working. But the MPU-9150 has three compasses (magnetometer), one for each axis. If you place the MPU-9150 flat on a table, it should theoretically only have one magnetometer giving a reading--the other two axis are there for when you tilt the sensor.

      The RTIMU-Lib takes the readings from all three magnetometers/compasses, and tells you which magnetic heading the sensor is pointed (reference the picture to determine which edge is the forward edge. Keep in mind, the RTIMU-Lib has 0 degrees = 270 magnetic, so I wrote a quick routine to convert the RTIMU-Lib output into a regular magnetic heading.

      I hope this helps.

    11. I edited the code and get the x, y and z of the compass. Are the x, y and z outputs calibrated? i compared the outputs with magnetometer app installed on my phone and the outputs and orientation (sign) are different. what do you suggest i should do?

      i tried the calibration you posted above. does it mean that once it is saved it is calibrated even though you disconnect it and back. or should i always do the above instruction every time i connect it and run?

    12. Connor OldsFebruary 12, 2016 at 11:01 AM
      The calibration data is saved on the arduino, so no, you don't have to run it over and over again. BUT I do believe the library applies the calibration TO the raw values, so if you figured out how to get the magnetometer values, I would suspect that those are not calibrated.

      What can give it wrong readings? A lot of things. Ferrous metal nearby will throw it off (within a foot or two), such as steel screws or nails. In my boat, my radar screen is an old CRT type with a big magnet in it, so I have to keep the mpu plenty far away from that. But the biggest culprit is probably the magnetic fields induced by electricity flowing through the wires. It generates a small magnetic field, but guess what--the mpu is designed to detect small magnetic fields, such as Earths core.

      So my advice is to keep the sensor as far away from any metal and wires as possible.

  4. The calibration data is saved on the arduino, so no, you don't have to run it over and over again. BUT I do believe the library applies the calibration TO the raw values, so if you figured out how to get the magnetometer values, I would suspect that those are not calibrated.

    What can give it wrong readings? A lot of things. Ferrous metal nearby will throw it off (within a foot or two), such as steel screws or nails. In my boat, my radar screen is an old CRT type with a big magnet in it, so I have to keep the mpu plenty far away from that. But the biggest culprit is probably the magnetic fields induced by electricity flowing through the wires. It generates a small magnetic field, but guess what--the mpu is designed to detect small magnetic fields, such as Earths core.

    So my advice is to keep the sensor as far away from any metal and wires as possible.

  5. This comment has been removed by the author.

  6. Thank you so much ! :)

    I also found out that even the two mobile has different magnetometer output. How did you calibrate your magnetometer ? Do you have suggestion on how can calibrate the magnetometer in the program? thanks :)

    1. I calibrated it as described in the blog post above (run the cal program, rotate it about each axis, send the letter "s", and that's it).

    2. is the saved calibration good for long term?
      or do we need to repeat it every once in a while?

    3. It should be good for the long term, but I'll be honest--my MPU-9150 just took a shit this weekend when I was testing it. The compass information was good, but every 25 seconds or so, it would fluctuate and output random numbers. I've tried and tried and tried, but I finally caved in and just ordered a new MPU-9250, and I'm going to use that with the Raspberry Pi instead of an Arduino, which has better calibration info (I'm using OpenPlotter).

    4. Thank you so much for the info :)
      Please update me about the result for the MPU9250.

    5. hi Connor,

      I have question again, what if I want the output for accelerometer, gyroscope and magnetometer in Quaternion.

      I try this code

      RTQuaterniongyro = imu->getGyro();
      RTQuaternion accel = imu->getAccel();
      RTQuaternion mag =imu->getCompass();

      But is not working ? Any suggestion ? Thank you :)

    6. I do not have any suggestions about that... I'm not actually sure what Quaternion means, other than I know it's some sort of coordinate system.

      As far as the 9250 goes, here's what I've discovered. The magnetometer is super sensitive to everything. I waved a stainless steel screw over it, and the heading changed by over 30 degrees. Inside my apartment, the heading only goes through about 270 degrees (even with calibration! I don't know how that's possible). Outside, it works normally. Maybe my wood table is full of lead? I don't know.

      But I do know that it's just super sensitive to everything, so wherever you place it to take readings, make sure it's completely isolated in a good environment!

  7. Hi.

    Thanks for the comprehensive blog post.

    How do you get around that the I2C signal level being (i think) 5V when running from a 5V atmega? The MPU9150 expects 3.3V, unless I've misunderstood something? See for details.

    Have you seen any ill effects from connecting it without a level converter?


    1. I had my MPU plugged into 3.3V, and then the SDA/SCL plugged directly into their respective ports on the Arduino. There was no 5V involved in just the MPU setup.

      Hope that helps.

    2. Thanks for responding.

      I was thinking about the voltage level used on the SDA and SCL pins. The atmega will indicate 0 and 1 using "0V" and "5V", while the mpu uses 0V low and 3.3V for high. There are voltage tolerances built in on both sides.

      See for example for an example of the same issue for i2c between a pi and an arduino.

      Workaround is using a 3.3V arduino ("pro") or adding a level converter on the i2c pins.

      If you haven't run into this, I suspect it works fine? Have you experienced that any of the mpu chips just stopped working?

    3. I did run into two weird glitches. The first was when I also incorrectly connected the interrupt pin (or something like that... I had five wires connected, which would make it freeze after five seconds. Then I discovered you only need the four listed above).

      The second time was probably because I simply fried it or had bad soldering. I had to resolder it many times because of changing installation requirements and orientations. Eventually some silver got stuck in the pin holes and I couldn't correctly solder in the pins, causing one of them to wiggle freely. I simply bought a new one to resolve that issue.

      But other than those two, it has worked flawlessly the way described above.

  8. Is there a filter on it like a kalman filter? is the filter the RTFusionRTQF.h?

    Br Johnny Mogensen

    1. Yes, the RTQF is a "simplified" Kalman filter. For more information on that, visit Richard's Tech (the author of the library) page here and search for "Kalman"

  9. Im getting an error for the library after following the instructions. And Im using an LSM303 so not sure if anything changes there but I haven't even gotten that far yet because the verification gave me that error. Any ideas why?

    1. didn't show up in comment above. the I2cdev.h

    2. I do not believe the LSM303 is supported by this library. See here for more information:

  10. This comment has been removed by the author.

  11. Is there room on the Atmega 328p to load both the calibration and regular compass, as well as enough code to allow a physical switch on a GPIO to activate calibration and saving the data? In-situ, standalone operation would make this a viable replacement to a full-fledged electronic heading sensor.

    1. I do not believe there is enough room to store the full up calibration data, but I may not be correct. I believe the author of this library switched to using a Raspberry Pi for this reason.

      Yes you could add code for a switch, there's plenty of room for that. Now, how you'd implement the switch activated calibration etc... is beyond my abilities.

    2. Here is what I will be using for attaching a digital pin to an interrupt.
      When the interrupt is thrown, the arduino will go into the calibration routine. I will then have to rotate the compass about its axes. The next press of the button will write the calibration data to the eeprom, blink a pattern on the LED, then go back to the regular NMEA compass routine.

  12. hi ,

    Awesome Job!!!

    Did you use the accelerometer, gyroscope and compass for the output of pitch, roll and yaw?


    1. I'm not entirely sure what you're asking... but I think the answer is yes, I got pitch and roll from the gyroscope, and yaw from the compass.

  13. Hi, I noticed that you had the BNO055 in your setup list for calibration. Have you done a calibration and setup for this board? If so, can you give me any insight. I tried to follow the calibration from your notes to set to either BNO055_28 or BNO055_29. All good until compile error on "RTIMUSettings settings;". This board has been a nightmare to get working as a compass. Can get it to do the "Bunny" thing from Adafruit's tutorial, as well as rawdata. I need to first get the calibration sent in the device and then get it to talk NMEA sentences. Any suggestions? Or should I get MPU2150? Thanks . . .