MPU-9250 Teensy Library

brtaylor

Well-known member
Hello all,

I created an Arduino library for the InvenSense MPU-9250 with support for all Teensy 3.x and LC devices:
https://github.com/bolderflight/MPU9250

This library supports I2C and SPI communication with the MPU-9250 sensor. It also enables easy setup of the MPU-9250 Digital Low Pass Filters (DLPF), output data rate, and interrupt settings. Data is collected using multi-byte reads, eliminating potential time skew in the results, and results are converted to SI units and rotated to a common axis system so they can be immediately used. A variety of functions are given to provide different combinations of the accelerometer, gyroscope, magnetometer and temperature data. I think the library provides access to the most used functionality of the MPU-9250, is easy to use, and has great performance. I hope it is of use to the community and I'd love any feedback or suggestions.

Best!
Brian
 
Nice work, Brian! I was just about to post a minimal MPU9250 library myself, for use with Hackflight TeensyCopter, so I was delighted to see your much more developed library posted here. Since you asked for suggestions, here are mine. As a non-expert Teensy hacker, I will be happy to be corrected on these if I am mistaken:

(1) For initializing gyro and accelerometer parameters, I would use enumerated constants instead of strings. That way the compiler can catch a misspelling, rather than uses experiencing a run-time failure.

(2) For the I^2C, I would find it useful to support (a) pins 16/17 as well as 18/19, on Teensy 3.1/3.2; (b) I2C_PULLUP_INT. By hand-editing those settings in your MPU9250.cpp, I was able to get your Basic_I2C example working with the MPU9250 shield from Pesky Products, and I am about to try it on my copter code.

I will take a stab at cloning / modifying your code to support these changes, then issue a pull request.

Again, thanks for the awesome work!
 
Sorry; I meant "rather than users experiencing a run-time failure".

Also:

(3) I would make the private, integer-based accessor methods (getAccelCounts, ...) public. Users like me, whose code works with the raw IMU values, would find this very valuable.
 
Prevous postings from Paul and Nox771 (creator of the alternative i2c library, i2c_t3.h) have said that the internal pull-ups for Teensy 3.x/LC are too weak to be usable in general. You really need to add external pull up resistors between each of the SDA and SCL pins and 3.3v. Typically 2.2K is used for 3.3v systems like the Teensy and 4.7K is used for 5v systems, but it really depends on the electrical characteristics of your i2c bus what the optimal resistor should be. If you use 4.7K resistors, it should work for a few i2c devices with short wires at the default 100Khz speed, but it is possible that the i2c bus will not work at higher speeds.
 
Michael,

Thank you for clearing this up. I vaguely recalled something about the need for external pullups, and your correction is just the sort of thing I was hoping to elicit. Hence it makes sense for brtaylor to restrict his library to using EXT initializations only.

For my own MPU9250 library I was going to leave the I^2C initialization out, allowing the user to do it with the standard Arduino Wire library, i2c_t3, or whatever, thereby supporting Arduino boards as well. Given what you've explained, I think it makes more sense for me to continue on that path. Once I have my own bare-bones MPU9250 library up on github, I will be sure to include a link to brtaylor's as the preferred library for Teensy.
 
Thanks simondlevy! Great suggestions. Give me an hour or two; I think I have a good solution. I'd really like to support the MPU9250 shield from Pesky Products.
 
Cool, and thanks to both of you for the speedy replies! I guess it's up to you whether you want to worry about the internal/external pullup issues that Michael pointed out. I have never found this to be a problem on pins 16/17, but as a relative novice I cannot say what is the right thing to do.
 
Note, in terms of the Pesky Products (onehorse on this forum) MPU9250's, according to https://www.tindie.com/products/onehorse/mpu9250-teensy-3x-add-on-shields/?pt=full_prod_search, the micro board already has 4.7K pull-ups installed, and the mini board has solder jumpers to connect the 4.7K resistors or not. So if you are using those two boards, you don't need to add additional resistors.

I'm not sure what the differences are, but another option is the PJRC prop shield with motion sensors (http://www.pjrc.com/store/prop_shield.html). The prop shield has 2.2k pull-up resistors, so you wouldn't need to add additional resistors.
 
Last edited:
@Mantiou, Yeah, Kris's sketches are what I adopted for my firmware. But as I am using my own simplified sensor-fusion code (adapted from Multiwii32), I like Brian's library, which provides a clean API for the basic MPU9250 functionality, without the sensor-fusion built-in.
 
@MichaelMeissner: Thanks, now I recall that feature of the Pesky shield. FWIW, I'm using the following to start I^2C in my firmware:

Wire.begin(I2C_MASTER, 0x00, I2C_PINS_16_17, I2C_PULLUP_INT, I2C_RATE_400);

When I switched from I2C_PULLUP_INT to I2C_PULLUP_EXT, I could no longer read from the MPU9250. I have the mini board and have not soldered the jumpers. As I do not really understand Pesky's instructions, I'll just go ahead and ask here: should I solder the jumpers and switch to I2C_PULLUP_EXT, or things as is (no solder, I2C_PULLUP_INT) ? Again, it works perfectly as-is for my test case.
 
Hi simondlevy, appreciate the feedback and updated the library. The MPU9250 declaration is overloaded so you can optionally specify the alternative I2C pins and pullups. See the README for more details.
https://github.com/bolderflight/MPU9250

Additionally, the enums were a great idea! I like having useful errors, so the accelerometer range, gyroscope range, and DLPF bandwidth settings were changed to enums.

Finally, the data collection functions returning counts, rather than engineering units, were made public. As you mention, this would be useful for users that have other libraries or pieces of code expecting the IMU data in counts. I still retain the coordinate axis transformation before returning the count data, so at least all of the data returned from my MPU-9250 library will be consistent with respect to the axis system.
 
Neat project you're working on! With the University of Minnesota, we're developing a flight control system as well. It's targeted to be extremely low latency and high data rate with a distributed architecture. There's a central flight management unit and a distributed actuator and sensor node network. We're doing a lot of flutter suppression research currently, so require extremely low latency for our control laws. Also that particular research aircraft has a MASSIVE sensor and actuator suite, but many of our research programs use simpler UAV platforms, which drove the development of the distributed actuator and node network, since it would scale well to the platform.

Attached is a picture a prototype unit on my desk that I'm using to develop the software.
IMG_2287.jpg

Prototype units should be flying any day now and we already have plans for some upgrades based on what we've learned from testing so far. I hope to have the upgraded PCB designs done in the next few weeks. The major components: the central flight management unit and distributed actuator and sensor nodes are open-source and based around Teensy devices.
 
Thanks, Brian, this is awesome! But if I can make one more request: would it be possible to migrate the axis transformations from the counts methods back into the floating-point methods, so that the counts methods return the "raw" axis values? I note that this is the standard used for MPU drivers in Cleanflight, and also used by Kris Winer (Pesky Products) in the sample code for his MPU9250 board. My firmware worked as-is with both of those, but with the latest version of your library I have to do this:

void Board::imuRead(int16_t accADC[3], int16_t gyroADC[3])
{
imu.getMotion6Counts(&accADC[1], &accADC[0], &accADC[2], &gyroADC[1], &gyroADC[0], &gyroADC[2]);

accADC[2] = -accADC[2];
gyroADC[2] = -gyroADC[2];
}


whereas I should be able to do just this:
void Board::imuRead(int16_t accADC[3], int16_t gyroADC[3])
{
imu.getMotion6Counts(&accADC[0], &accADC[1], &accADC[2], &gyroADC[0], &gyroADC[1], &gyroADC[2]);
}


Thanks again,
Simon
 
Thanks Simon!

Let me think about the transformation some more. The problem is that, because the MPU-9250 is a System in Package (SiP), it's essentially a mashup of the MPU-6500 gyro/accel and the AK8963 magnetometer. The two chips that are part of the SiP are not aligned with each other, so with the raw data the gyro/accel and the magnetometer data cannot be used together. You can see the two different axis systems below.
Screenshot from 2016-10-10 21:49:23.png

What my transformation does is align the sensors so that all of the data comes out of the library referencing the same axis system (i.e. all of the x values are pointed the same direction; same with Y and Z). I feel like this is the "proper" way to have it handled by default. In other words, without needing to read a lot about the sensor, my intuition would be that the x direction for all three sensors is the same. I like that the library outputs data in a way that people would expect it to.
 
This library supports I2C and SPI communication with the MPU-9250 sensor.

Which MPU-9250 breakout board in the market do you use with SPI comm? The Sparkfun version is not setup for SPI. Do you have a recommendation? I prefer SPI for my IMU devices because SPI is significantly faster and less susceptible to EMI interference with 3 to 4 foot long cables.
 
Which MPU-9250 breakout board in the market do you use with SPI comm? The Sparkfun version is not setup for SPI. Do you have a recommendation? I prefer SPI for my IMU devices because SPI is significantly faster and less susceptible to EMI interference with 3 to 4 foot long cables.

Really? It looks like the Sparkfun breakout has the necessary pins.
https://www.sparkfun.com/products/13762

I use the breakout by Embedded Masters.
https://store.invensense.com/Contro...etail/EMSENSRMPU9250-Embedded-Masters/552444/

To use with SPI, you need to desolder two 0 Ohm resistors, R2 and R4, which are the top left and bottom right resistors if you're looking at the board with "9 Axis" facing up. I really like SPI, the performance is outstanding.
 
Thanks, Brian -- now this all makes sense to me! As an indoor pilot, I've only ever used only the accel / gyro part of these IMUs (MPU 6050, 9250). So I was unaware of the magnetometer alignment issue with the 9250. I appreciate the extra work you already did to accommodate my requests, and I see no reason for you to complicate your code any further. Instead, in my own code making use of your getMotion6Counts() method, I will add a comment with a link to your explanation above.
 
On the MPU9250 Mini boards, you can solder the three-pin jumper closed to enable the on board 4K7 I2C pullup resistors. These boards are hard-wired for I2C. At 400 kHz this is plenty fast for any application I am aware of, and I have run these boards at 1 MHz using the i2c_t3.h library with no problem. Without a magnetometer, at best you can get pitch and roll. But almost any 6 DoF fusion scheme will do this well. The challenge for orientation estimation is always accurate heading.
 
Brian

Thanks for developing this library, it's probably just what I need.

Recently, before I got my teensy 3.6 & 3.6, I got my Sparkfun MPU9250 to work with Uno, Mega and Due using Kris Winer/Sparkfun's MPU9250 library. My sketch is based on his MPU9250BasicAHRS sketch just cleaned it up and simplified a bit.

I read somewhere that your library was derived from Kris Winer's?

My question: If I update my Uno/Mega/Due sketch to use the i2c_t3 library built into teensyduino and your MPU9250 syntax, would you expect it to work without any other mods on T3.5/3.6 using your library?
(If not, I will probably try using Kris Winer's library as-is + i2c_t3 ... nothing to lose!)

Essentially I'm asking how similar the 2 libraries are ... (basically I'm a lazy sod and don't want to rewrite much of my sketch, if possible :) )

Happy to provide my sketch if need be, but I thought you may know "in principle" answer anyway.

TIA

PS: suggestion: if your library is teensy-specific perhaps it should be renamed to MPU9250_t3 or similar, so users like me can easily install both a non-teensy library like Kris Winer's and yours for teensy in Arduino IDE ...
 
Last edited:
I wouldn't go so far as to say based on, but there's only so many ways to talk with the same device. Certain parts of my library's code date back almost a couple years to when the MPU-9250 first came out. I only recently released it after figuring out licensing (the easy part) and adding the magnetometer functionality (by far the hardest part of working with the MPU-9250).

My assumption is that you would like to use my library to talk with the MPU-9250 and Kris' code to run the AHRS? I've done this and it works. You just have to be careful regarding the axis orientation and the units. I use SI units (m/s/s for accelerometers, rad/s for gyrscopes, micro-Tesla for magnetometers).

My library uses i2c_t3 internally for the I2C communication, no need to call it out specifically outside of the library. It supports and has been tested on all Teensy 3.x and LC devices, including the Teensy 3.5 and 3.6.

Thanks for the suggestion. Yes, my library is Teensy specific, mostly because I'm using i2c_t3 for my I2C communication and I don't have any non-teensy devices to test against.

Please let me know if you run into any trouble and I'd be happy to look at it.

Brian
 
Brian

Yes my end goal is accurate yaw/pitch/roll + altitude readings ... I guess that's what AHRS (Aircraft Heading & Reference System) means?

So I reckon I will need to combine Kris Winer's quaternion filter code with your library?

After looking more closely at your library and examples I've realised it is different enough so I'm spending the time getting familiar first before converting my most mature sketch.

I've started with your BasicI2C example and it's working fine. But quite often when I start the sketch on teensy it returns a beginStatus of -1 and grinds to an early halt. I can always fix this by unplugging (powering off) the teensy and then it starts perfectly, every time. Perhaps there is way to issue a reset command to the MPU9250 at start of the sketch?

Chris

PS: I like that you chose SI units, and aligned all the axes ... simple and logical
 
Last edited:
Hi Chris, thanks!

Yes my end goal is accurate yaw/pitch/roll + altitude readings ... I guess that's what AHRS (Aircraft Heading & Reference System) means?

So I reckon I will need to combine Kris Winer's quaternion filter code with your library?

AHRS is an acronym for Attitude and Heading Reference System. Madgwick and Mahony are two types of AHRS, of many. They'll give very good orientation results for low-dynamic environments. You'll need an AHRS with either a static pressure measurement or GPS to get the altitude.

Just for edification, this is because getting position, in an X-Y sense or altitude sense, would mean a double integration of the IMU accelerometer. Problem being that any error in the accelerometer measurement leads to a velocity error and a much larger position error. Integrating the IMU sensor works well for short periods of time, but leads to large errors in position relatively quickly. We can use other sensors to bound this error. One method that works very well is an extended kalman filter, especially where you combine measurements from the IMU with a GPS or static pressure sensor. In these cases the IMU can be integrated at a high rate (i.e. hundreds of Hz) and the position error kept in check with the lower rate GPS (1-10 Hz) or static pressure measurement. As an added bonus, you get a good estimate of the IMU bias and scale factor as well, so the high rate integration error is lowered. These extended kalman filters tend to work better in high dynamic environments than AHRS do, but are more computationally intensive and complex to code.

I hope to put together a library with a few different AHRS and EKF functions, including a 15 state quaternion EKF for a loosely coupled IMU and GPS that we use at the University of Minnesota, but it could take a while. In the meantime, your approach seems great.

I've started with your BasicI2C example and it's working fine. But quite often when I start the sketch on teensy it returns a beginStatus of -1 and grinds to an early halt. I can always fix this by unplugging (powering off) the teensy and then it starts perfectly, every time. Perhaps there is way to issue a reset command to the MPU9250 at start of the sketch?

This has been a bit of a known issue. The initialization was working great on a clean power up, but not always working after a code upload. I investigated some a while ago and discovered that the AK8963 magnetometer was hanging. It was so bad that I could put the MPU-9250 in I2C pass through mode to interact directly with the AK8963, but the AK8963 would not even Ack its I2C address. I tried I2C bus resets, but nothing would get the AK8963 to unhang. And unfortunately, the way the sensor is packaged, I can't command an AK8963 reset if it's not responding to I2C communication.

When I was writing a response to your post earlier today, it got me thinking about the root cause more, and I think I've solved it. The problem was that during initialization I restart the MPU-9250. Now on a code upload, the AK8963 was likely in continuous sampling mode and sending data to the MPU-9250. It occurred to me that the MPU-9250 restart may be occurring while the AK8963 was trying to communicate with it, causing the AK8963 to hang. So I added some code today that puts the AK8963 into a power-down / standby mode before the MPU-9250 is restarted and it appears to work well at preventing the AK8963 from hanging.

TLDR, the issue with the initialization not being successful after a code upload should now be solved after re-working the initialization sequence. The library has been updated on github.

Brian
 
I'm upgrading my fairly mature Mega based design to T3.5 or 3.6. It has the Bosch BMP-180 pressure sensor and Adafruit GPS already, and lots of logging to an Adafruit microSD. So my present game is to integrate MPU9250 with all that, but on teensy

I'm reasonably comfortable with mathematics (up to a point!) and understand the principles in your paragraph on integration, filters, errors etc. although converting principles to actual code is occasionally beyond my new-found coding skills. A good example is Kris Winer's quaternion filter code, I can understand and use it, but I'm sure glad I didn't have to write it. And I've never had my head fully around kalman filter maths, but I like what they offer!

I was partly attracted to the teensy 3.5/6 by their raw power and the FPU, because I've got a strong hunch I'm heading towards fairly serious data manipulation and fusion algorithms from all the various sensors - simply to get the best possible / usable accuracy for attitude, direction and altitude.

I've downloaded your updated library - nice work. As an aside: this time I renamed the folder, cpp and h files to MPU9250_t3, and edited #include "MPU9250_t3.h" into the cpp and my sketch, and it works fine. So now I expect I can bring the Spakrfun MPU9250 library back into my libraries folder without conflicting (for when I need to work on the Mega design).

Meanwhile however I've been playing with your SPI versions. Once I realised I had to swap a solder junction around to setup my Sparkfun MPU9250 for SPI (SJ2 to the AD0/SDO pin) it started working. I fitted an external LED, since SCK0 is also on D13. Then I jumped to your interrupt_SPI example, made my slightly modified version and got it going no problem. Even before I downloaded the updated library I found the SPI version was more stable than I2C mode.

I was attracted to SPI after reading the Perfomance section at end of your MPU9250 page on Github. I'm wondering if this may create a conflict with SPI for the microSD card on T3.5/6 ? But even if it is, the Kickstarter lists "3 SPI ports (one with FIFO)" so presumably there is a way to use the other SPI ports (or will be :) ).

Off to play a bit more ....
 
Last edited:
Back
Top