Good news!!! DMP from MPU6050 can be used without interrupt pin

I am really short of interrupt pins on my arduino cause I have a project that already uses a lot of arduino pins and to read DMP data from MPU6050 you always have to use an interrupt pin. I searched A LOT in the internet and many people says it's impossible to retrieve DMP data without interrupt pin. Some people also said the only way to do that would be if I could reverse engineer the MPU6050 code (which I dont know how to do).

But today, playing with MPU6050 I could finally get all of its DMP data without the interrupt pin, you just have to connect 4 pins (VCC, GND, SCL and SDA). Upload the code below to your arduino and see that it works!

#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
#include "Wire.h"
MPU6050 mpu;
uint16_t packetSize;
uint16_t fifoCount;
uint8_t fifoBuffer[64];
Quaternion q;
VectorFloat gravity;
float ypr[3];

void setup() {

    Wire.begin();
    TWBR = 24;
    mpu.initialize();
    mpu.dmpInitialize();
    mpu.setXAccelOffset(-1343);
    mpu.setYAccelOffset(-1155);
    mpu.setZAccelOffset(1033);
    mpu.setXGyroOffset(19);
    mpu.setYGyroOffset(-27);
    mpu.setZGyroOffset(16);
    mpu.setDMPEnabled(true);
    packetSize = mpu.dmpGetFIFOPacketSize();
    fifoCount = mpu.getFIFOCount();

    Serial.begin(115200);

}

void loop() {

    while (fifoCount < packetSize) {

        //insert here your code
        
        
     

        fifoCount = mpu.getFIFOCount();

    }

    if (fifoCount == 1024) {
    
        mpu.resetFIFO();
        Serial.println(F("FIFO overflow!"));
        
    }
    else{
    
      if (fifoCount % packetSize != 0) {
        
          mpu.resetFIFO();
            
    }
        else{
    
            while (fifoCount >= packetSize) {
            
                mpu.getFIFOBytes(fifoBuffer,packetSize);
                fifoCount -= packetSize;
                
            }    
        
            mpu.dmpGetQuaternion(&q,fifoBuffer);
            mpu.dmpGetGravity(&gravity,&q);
            mpu.dmpGetYawPitchRoll(ypr,&q,&gravity);          
            
            Serial.print("ypr\t");
            Serial.print(ypr[0]*180/PI);
            Serial.print("\t");
            Serial.print(ypr[1]*180/PI);
            Serial.print("\t");
            Serial.print(ypr[2]*180/PI);
            Serial.println();
            
    }
   
    }

}

There is only one problem: intermitently Arduino hangs in the getFIFOBytes function for no reason. I am pretty sure it's a bug cause when you call too many times getFIFOBytes for some reason arduno freezes. Anybody would like to help me improve this code above?

Great work, this is almost exactly what I have been looking for! Did you ever manage to solve the freezing issue?

UNarmed:
Great work, this is almost exactly what I have been looking for! Did you ever manage to solve the freezing issue?

I solved it(for me atleast),

You'll have to edit the "MPU6050_6Axis_MotionApps20.h" file,
go to line:272-274
and you"ll find

 #ifndef MPU6050_DMP_FIFO_RATE_DIVISOR  
 #define MPU6050_DMP_FIFO_RATE_DIVISOR 0x01 
 #endif

edit the 0x01 to 0x02 or 0x03
It will slow down the readings thus decreasing stress on your MCU.

Awesome, thank you so much for this!

Here is alternate code that works well with the MPU6050
note that if you don't retrieve every reading from the fifo buffer you will overflow and corrupt all data
I have had extreme success with this version of the DMP code
full code attached

you can trigger the loop to check for MPU data with this timer:

// ================================================================
// ===                          Loop                            ===
// ================================================================
void loop() {
  static unsigned long _ETimer;
  if ( millis() - _ETimer >= (10)) {
    _ETimer += (10);
    mpuInterrupt = true;
  }
  if (mpuInterrupt ) { // wait for MPU interrupt or extra packet(s) available
    GetDMP();
  }
}

The get data is non blocking and will quickly bail and reset if corrupted data is detected. this is where you are freezing up

// ================================================================
// ===                    MPU DMP Get Data                      ===
// ================================================================
void GetDMP() { // Best version I have made so far
  static unsigned long LastGoodPacketTime;
  mpuInterrupt = false;
  FifoAlive = 1;
  fifoCount = mpu.getFIFOCount();
  if ((!fifoCount) || (fifoCount % packetSize)) { // we have failed Reset and wait till next time!
    digitalWrite(LED_PIN, LOW); // lets turn off the blinking light so we can see we are failing.
    mpu.resetFIFO();// clear the buffer and start over
  } else {
    while (fifoCount  >= packetSize) { // Get the packets until we have the latest!
      mpu.getFIFOBytes(fifoBuffer, packetSize); // lets do the magic and get the data
      fifoCount -= packetSize;
    }
    LastGoodPacketTime = millis();
    MPUMath(); // <<<<<<<<<<<<<<<<<<<<<<<<<<<< On success MPUMath() <<<<<<<<<<<<<<<<<<<
    digitalWrite(LED_PIN, !digitalRead(LED_PIN)); // Blink the Light
  }
}

This is my debugging print functions replace with your own or ignore if they are confusing

// My Debug code for my MPUMath() function

#define DEBUG
#ifdef DEBUG
//#define DPRINT(args...)  Serial.print(args)             //OR use the following syntax:
#define DPRINTSTIMER(t)    for (static unsigned long SpamTimer; (unsigned long)(millis() - SpamTimer) >= (t); SpamTimer = millis())
#define  DPRINTSFN(StrSize,Name,...) {char S[StrSize];Serial.print("\t");Serial.print(Name);Serial.print(" "); Serial.print(dtostrf((float)__VA_ARGS__ ,S));}//StringSize,Name,Variable,Spaces,Percision
#define DPRINTLN(...)      Serial.println(__VA_ARGS__)
#else
#define DPRINTSTIMER(t)    if(false)
#define DPRINTSFN(...)     //blank line
#define DPRINTLN(...)      //blank line
#endif

upon successful retrieval of data lets process it with this code:

// ================================================================
// ===                        MPU Math                          ===
// ================================================================
float Yaw, Pitch, Roll;
void MPUMath() {
  mpu.dmpGetQuaternion(&q, fifoBuffer);
  mpu.dmpGetGravity(&gravity, &q);
  mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
  Yaw = (ypr[0] * 180.0 / M_PI);
  Pitch = (ypr[1] *  180.0 / M_PI);
  Roll = (ypr[2] *  180.0 / M_PI);
 
  // this is my debug code chang it to simpler Serial.print() funcitons
  DPRINTSTIMER(100) {// delays 100 miliseconds between printing
    DPRINTSFN(15, " W:", q.w, -6, 4);
    DPRINTSFN(15, " X:", q.x, -6, 4);
    DPRINTSFN(15, " Y:", q.y, -6, 4);
    DPRINTSFN(15, " Z:", q.z, -6, 4);

    DPRINTSFN(15, " Yaw:", Yaw, -6, 2);
    DPRINTSFN(15, " Pitch:", Pitch, -6, 2);
    DPRINTSFN(15, " Roll:", Roll, -6, 2);
    DPRINTSFN(15, " Yaw:", ypr[0], -6, 2);
    DPRINTSFN(15, " Pitch:", ypr[1], -6, 2);
    DPRINTSFN(15, " Roll:", ypr[2], -6, 2);
    DPRINTLN();
  }
}

Z

MPU6050_Latest_code.ino (7.95 KB)

zhomeslice, I tried to use your program, but it also freezes after few minutes. I changed MPU6050_6Axis_MotionApps20.h" file back to original (0x01), but still freezes. Did you noticed that? Do you have a new version?

Thanks,

I'm also having the same issue. Has anyone been able to get a proper understanding why this happens? On the official i2cdevlib forums, there is also confusion about this. Jeff Rowberg didn't address this issue/question, sadly. It seems like it's not possible to NOT use an interrupt for the MPU-6050 and still be able to use the DMP normally... I researched quite a bit and found many people asking about the same thing due to the limited interrupt pins on their Arduino board. It would be really cool if we could get a detailed explanation about why the interrupt pin is necessary for DMP and also, hopefully, someone will make a fixed library for this... MPU6050_DMPnoInterruptPin.h? :smiley:

Edit: I found this thread where Jeff Rowberg replied several times about the DMP but i'm not sure if it's helpful to understand this particular issue... Anyway, here it is: Bountysource

DryRun:
I'm also having the same issue. ... sadly. It seems like it's not possible to NOT use an interrupt for the MPU-6050 and still be able to use the DMP normally...

DMP triggers every 10ms. so you need to respond at that point.
My version of the MPU6050 library has 2 advantages over Jeff's code.
(Note: I've helped improve Jeffs code over the last few weeks and added as many features as possible into it)
1st) I have a feature that prevents overflow during the delay(10000000); function.
You can add the OverflowProtection() function inside any of your blocking code like While ( 1 ) { do something forever until false } to keep the overflow from occurring preserving the data inside the FIFO buffer.
2nd) When you trigger the request for data it clears the FIFO buffer and returns the latest reading.
In reality by doing it this way you will always retrieve values that are accurate within 10ms of the actual reading.
Link to my library: SimpleMPU6050 (My library is fully compatible with the MPU6050, MPU6500, MPU9150, MPU9250 and MPU9255)
If you have any blocking code other than delay() ( I handle the delay for you using yield() ) you must trigger this line of code as fast as possible:

mpu.OverflowProtection();

See Attached File for a working Example using no interrupts.
This uses my SimpleMPU6050 library and Jeffs I2Cdev library.
(Get the Latest Version of I2Cdev as I discovered an error with writeWrods function)
Z
I tested this code on the MPU6050, MPU9250 and MPU9255 Should also work with MPU6500 and MPU9150

Simple_MPU6050_Example_No_interrupt_pin_used.ino (7.24 KB)

That's very useful! Thanks a lot for making this library. :slight_smile:

I have some questions:

In your example code:

mpu.GetQuaternion(&q, quat);
mpu.GetGravity(&gravity, &q);
mpu.GetYawPitchRoll(ypr, &q, &gravity);
  1. Is it necessary to have all 3 lines, if i only need the ypr values? That is, can't i just have the 3rd line only, instead of wasting CPU time on finding the quaternion and gravity?

  2. If i use only the 3rd line, then will the data still be fused, meaning that to get the most accurate readings from the gyro, its data needs to be fused with the accelerometer, so will this still work or will i get drift in the gyro values? That's because there are the 3 arguments and the two others are pointers &q and &gravity.

  3. Could you please add an example that showcases in detail how to use this library without needing interrupt pin connected to the IMU? I'm not sure how to implement the need to retrieve data at exactly 10ms and is the mpu.OverflowProtection(); necessary in this case?

DryRun:
That's very useful! Thanks a lot for making this library. :slight_smile:

I have some questions:

In your example code:

mpu.GetQuaternion(&q, quat);

mpu.GetGravity(&gravity, &q);
mpu.GetYawPitchRoll(ypr, &q, &gravity);




1. Is it necessary to have all 3 lines, if i only need the ypr values? That is, can't i just have the 3rd line only, instead of wasting CPU time on finding the quaternion and gravity?

The first function performs the math necessary to use the quaternion values

	q -> w = (float)(qI[0] >> 16) / 16384.0f;
	q -> x = (float)(qI[1] >> 16) / 16384.0f;
	q -> y = (float)(qI[2] >> 16) / 16384.0f;
	q -> z = (float)(qI[3] >> 16) / 16384.0f;

basically turning an integer into a floating-point value
as gravity is needed to get standard gravity-based yaw, pitch and roll values instead of euler values we must calculate this.
Try eular values to avoid gravity calculations

 // Eular print code 
#define printfloatx(Name,Variable,Spaces,Precision,EndTxt) print(Name); {char S[(Spaces + Precision + 3)];Serial.print(F(" ")); Serial.print(dtostrf((float)Variable,Spaces,Precision ,S));}Serial.print(EndTxt);//Name,Variable,Spaces,Precision,EndTxt
  Quaternion q;
  float euler[3];         // [psi, theta, phi]    Euler angle container
  mpu.GetQuaternion(&q, quat);
  mpu.GetEuler(euler, &q);
  Serial.printfloatx(F("euler  ")  , euler[0] * 180 / M_PI, 9, 4, F(",   ")); //printfloatx is a Helper Macro that works with Serial.print that I created (See #define above)
  Serial.printfloatx(F("")       , euler[1] * 180 / M_PI, 9, 4, F(",   "));
  Serial.printfloatx(F("")       , euler[2] * 180 / M_PI, 9, 4, F("\n"));

Quaternion and VectorFloat are deffinitionsthat allow us to handle the calculations easily

  Quaternion q;
  VectorFloat gravity;

The "q" variable is an instance of Quaternion .
Similarly the "gravity" variable is more than just a single value. the gravity is an instance of VectorFloat the VectorFloat is defined as follows

class VectorFloat {
  public:
  float x;
  float y;
  float z;

  VectorFloat() {
    x = 0;
    y = 0;
    z = 0;
  }
  
  VectorFloat(float nx, float ny, float nz) {
    x = nx;
    y = ny;
    z = nz;
  }

  float getMagnitude() {
    return sqrt(x*x + y*y + z*z);
  }

  VectorFloat :: normalize() {
    float m = getMagnitude();
    x /= m;
    y /= m;
    z /= m;
    return this;
  }
  
  VectorFloat getNormalized() {
    VectorFloat r(x, y, z);
    r.normalize();
    return r;
  }
  
  VectorFloat :: rotate(Quaternion *q) {
    Quaternion p(0, x, y, z);

    // quaternion multiplication: q * p, stored back in p
    p = q -> getProduct(p);

    // quaternion multiplication: p * conj(q), stored back in p
    p = p.getProduct(q -> getConjugate());

    // p quaternion is now [0, x', y', z']
    x = p.x;
    y = p.y;
    z = p.z;
    return this;
  }

  VectorFloat getRotated(Quaternion *q) {
    VectorFloat r(x, y, z);
    r.rotate(q);
    return r;
  }
};

This handles gravity in any vector as you rotate the MPU to other angles.

  1. If i use only the 3rd line, then will the data still be fused, meaning that to get the most accurate readings from the gyro, its data needs to be fused with the accelerometer, so will this still work or will i get drift in the gyro values? That's because there are the 3 arguments and the two others are pointers &q and &gravity.

The DMP "fusion" happens in the MPU6050 over a 10ms duration. Many readings are merged to provide you with a clean shift in orientation. This orientation is returned as four Quaternion values and 3 Accelerometer values and 3 gyro values While the gyro and accelerometer seem simple they are actually many readings over the 10ms period merged together. Accurate use of gyro and accelerometer readings would require you to use every 10ms instance luckily we have them already used in the Quaternion calculations so we can ignore them for the most part.

  1. Could you please add an example that showcases in detail how to use this library without needing interrupt pin connected to the IMU? I'm not sure how to implement the need to retrieve data at exactly 10ms and is the mpu.OverflowProtection(); necessary in this case?

If you are diligent in preventing long delays in your code, the mpu.OverflowProtection(); is not necessary.
mpu.OverflowProtection(); basically empties the FIFO buffer down to a reasonable level leaving about 10 reading in it.
If you overflow your next attempt to get any values will be rejected and nothing will occur.
The overflow protection has 2 parts
The delay(); part:

#define ENABLE_MPU_OVERFLOW_PROTECTION(...) void yield(void){mpu.OverflowProtection();} // yield is called from within the delay() function 
which is basically a simple function for you to easily use
void yield(void){
  mpu.OverflowProtection();
} // yield is called from within the delay() function

When you call delay(); the function yield(); is called normally noting happens because we don't know about it.

void delay( uint32_t ms )
{
  if ( ms == 0 )
  {
    return ;
  }

  uint32_t start = _ulTickCount ;

  do
  {
    yield() ; //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 
  } while ( _ulTickCount - start <= (ms-1) ) ;
}

And, since we don't care what is happening during delay lets, do something important like preventing overflow from occurring.

now let's look at the OverflowProtection();

void Simple_MPU6050::OverflowProtection(void) {
	uint32_t Timestamp ;
	Timestamp = millis();
	static uint32_t spamtimer;
	if ((Timestamp - spamtimer) >= 30) { // Blink without delay timer 30ms or 3 readings are generated in the FIFO Buffer
		spamtimer = Timestamp;
		uint16_t fifo_count;
		int8_t Packets;
		FIFO_COUNTH_READ_FIFO_CNT(&fifo_count);
		Packets = (fifo_count / packet_length) - (_maxPackets - 10); // Leaves room for more readings
		if (Packets <= 0)return; // nothing is happening get out.
		uint8_t trash[packet_length + 1] ;
		while (0 < Packets--) {
			FIFO_READ(packet_length, trash); // Trash all older packets down to about 10 readings.
		}
	}
}

Added a few notes in the code.

So there you have it.

Because we are using the MPU6050's processor to do all the Calculations leaving us with 10ms to do anything we want before the next reading is ready if we don't need the data we can use delay() and the most recent reading is preserved allowing us to instantly get it without flushing the FIFO buffer and waiting up to 10ms for a good reading.

Z

The code is working great,
BUT (there is always a but :wink: )
i am totally lost... I used the library and basic MPU-6050 RAW script from jeff without using int pins at all to compare 4 different MPUs. obviously those are just RAW data sets, but it works right now...
(AD0 to high on all 4, set one to low, get data, store data, AD0 High, next... compare, move servos)

but here i am totally failing to understand how i can further use your yaw pitch roll data...

i admit, its my first project and i started working on it 2 weeks ago, so there is A LOT i dont know.
your void print_Values function and the macro printfloatx provide all the info i need, but i dont get it how to not print it but further work with the values.
(even with your explaining comments... )

you might be wondering why i want to change my project if its working with code i dont even understand... this is way more accurate and the calibration is nice to have :slight_smile:

and just to be sure that i dont screw this up, since the MPUs do all the calculations and are just providing the info at 10ms intervalls i could still use multiple MPUs with the AD0 method, as long as i either get the data every 10ms or use the overflowprotection() on the respective MPUs that are AD0 high, correct? (meaning i give my servos some time to move...)

sorry for asking probably "obvious" questions...

Arne511:
The code is working great,
BUT (there is always a but :wink: )
i am totally lost... I used the library and basic MPU-6050 RAW script from jeff without using int pins at all to compare 4 different MPUs. obviously those are just RAW data sets, but it works right now...
(AD0 to high on all 4, set one to low, get data, store data, AD0 High, next... compare, move servos)

You will need to configure all 4 also to use the DMP firmware

but here i am totally failing to understand how i can further use your yaw pitch roll data...

i admit, its my first project and i started working on it 2 weeks ago, so there is A LOT i dont know.
your void print_Values function and the macro printfloatx provide all the info i need, but i dont get it how to not print it but further work with the values.
(even with your explaining comments... )

#define printfloatx(Name,Variable,Spaces,Precision,EndTxt) print(Name); {char S[(Spaces + Precision + 3)];Serial.print(F(" ")); Serial.print(dtostrf((float)Variable,Spaces,Precision ,S));}Serial.print(EndTxt);//Name,Variable,Spaces,Precision,EndTxt

#define is a simple search and replace it basically replaces printfloatx with the following string of text

Serial.printfloatx("MyTEXT",FOO,10,2,"Degrees")

would be replaced with: (I added new lines here to make it easy to read)

Serial.print("MyText");
 {
    char S[(10+ 2+ 3)]; // NOTE char s[] is only a local variable array between { and } thereafter it is dealocated
    Serial.print(F(" ")); 
    Serial.print(dtostrf((float)FOO,10,2,S));
 }
Serial.print("Degrees");

Nothing fancy :o
FYI dtostrf is a cool function that turns a floating-point number into a char array of x characters with a terminating null so Serial.print can use it.

and just to be sure that I don't screw this up since the MPUs do all the calculations and are just providing the info at 10ms intervals I could still use multiple MPUs with the AD0 method, as long as I either get the data every 10ms or use the overflowprotection() on the respective MPUs that are AD0 high, correct? (meaning I give my servos some time to move...)

The DMP process the MPU6050 values using noise filters and such to provide you with accurate readings and places the values in the FIFO buffer every 10ms. I created a new overflow proof function to get the latest reading that is now available in Jeff's library. the new examples doesn't even require interrupts. But using the interrupt allows you to do other things while waiting. This function would work well with your code. I would wait 10ms (blink without delay) and go get all the latest readings using the new function to get each mpu6050 in turn.

sorry for asking probably "obvious" questions...

Your project sounds fun Good luck :slight_smile:
I've been there, done that, and asked the "obvious" questions before too. No worries :slight_smile:
Z

1 Like

thank you very much for the explenation and the quick answer!
didnt expect that :slight_smile:

i will get right to it :slight_smile:

i used 4 MPUs to cope for some interference with the RAW data. i would only need 2 as long as they dont have too many or too large "spikes" in the readings, which should be the case after calibration and using the DMP.

so 2 more free pins :slight_smile: whoohoo :slight_smile:

for the project, you have a moving object with an arm attatched to it, in this arm are 2 servos to move in pitch and yaw. and a free movable "controling" object with one MPU. the 2nd MPU is in the base of the arm.

aim is that the arm is always pointed towards the direction the controlling MPU is aiming.

meaning i can move the whole object where the controlling MPU and the arm are moved the same amount so no movement is necessary, or i can just move the controlling MPU and the servos need to move the arm according to the difference.

(in other words a shoulder mounted gun turret for a Predator Helloween outfit :smiley: but i think i will put it on my desk in the office first to get annoying co-workers out of there :wink: )

@ zhomeslice
confirm you changed the example code? i just updated my libs and the code is different right now and (at least for me right now) not working anymore.
still trying to figure out why...

generating offsets works, last line i get is "magnetometer not found" and after that nothing

Edit: ok got it... kind of at least...

edit 3: had to delete edit 2... i tried to implement parts of the other code, that you explained in here. it did work for a while, and than i broke it. no idea where or how. so i am back to the ode explained in here.
right now i only have trouble to get a 2nd mpu working. i can generate the offsets but after that i have trouble to address it and make the overflow protetion work on it. (at least i think that is the reason)

any suggestions how to fix it?
SIMPLE_MPU6050 mpu1(0x68);
SIMPLE_MPU6050 mpu2(0x69); // is also not working, can you tell me where my mistake is there?

sorry for asking so much... but with your explenations and a lot of reading about what i dont understand, i already got a lot further :slight_smile: but here i cant even say what my search entry would be

Arne511:
@ zhomeslice
confirm you changed the example code? i just updated my libs and the code is different right now and (at least for me right now) not working anymore.
still trying to figure out why...

generating offsets works, last line i get is "magnetometer not found" and after that nothing

Edit: ok got it... kind of at least...

edit 3: had to delete edit 2... i tried to implement parts of the other code, that you explained in here. it did work for a while, and than i broke it. no idea where or how. so i am back to the ode explained in here.
right now i only have trouble to get a 2nd mpu working. i can generate the offsets but after that i have trouble to address it and make the overflow protetion work on it. (at least i think that is the reason)

any suggestions how to fix it?
SIMPLE_MPU6050 mpu1(0x68);
SIMPLE_MPU6050 mpu2(0x69); // is also not working, can you tell me where my mistake is there?

sorry for asking so much... but with your explenations and a lot of reading about what i dont understand, i already got a lot further :slight_smile: but here i cant even say what my search entry would be

I decided it's time to try getting 2 MPU6050s working. I tried this myself with a quick sketch without success yet... I had to travel for work so further testing must wait till Friday.
Unfortunately. I got to pay the bills :slight_smile:

Z

:smiley: ok got it :wink:

i will keep trying, if i get any results, i will let you know.

what surprises me, is that i could trigger both initially and get the calibration, offsets and image load done on both.

only after that i have problems (which i blame on my newbie skills and not your code)

my approach to this was the following:

mpu1 AD0 connected to Pin 10,
mpu2 AD0 connected to Pin 11,

pin 10 and 11 to HIGH initially.

Pin10 LOW,
mpu2 overflowprotection() [i think here i have a mistake somewhere, i want to use the 0x69 to make it flexible ]

mpu1 retrieve data and store data of yaw and pitch in new variables,

pin10 HIGH
pin11 low, overflowprotection of mpu1 (again 0x69 since this mpu is AD0 HIGH now)
get data again from the "default address"
store,
pin11 high

i will try it after work today again and see if i can make it work. but as mentioned, i am working with your first version again, because i had even more trouble to understand your updated code :wink:
but my attempts are more like a sledgehammer method compared to yours :smiley: (but as long as it works its fine for me)