MPU6050 freezing/crashing

Hi! I just saw your code. You wrote TWBR = 24. However, in the definition, TWBR = ((F_CPU / frequency) - 16) / 2. So if you are trying to create a 400000 Hz frequency, you need the value to be 12. Is this correct?

zhomeslice:
@KCHuang
I appreciate your efforts with this code.
I have reviewed it and discovered several lapses back to the original issues I faced during my troubleshooting.
Please review my changes to your code and consider the need to adjust your shared example sketch.

I have tested this with zero issues.

Changes:

I simplified and removed the blocking code in readMPUFIFOBuffer:

bool readMPUFIFOBuffer() {

fifoCount = mpu.getFIFOCount();
  mpuInterrupt = false; // We caught an interrupt reset for next time

/************************************
    Check for incorrect packet size any size that is not divisible by 42 (Note: this will only occur when we overflow )
    Check for overflow  (1024 bytes FiFo Buffer divided by 42 bytes Packet Size leaves a remainder of 16)
    If the fifoCount is > Zero we have data so get it
  ************************************/
  if ((!fifoCount) || (fifoCount % packetSize)) { // something’s wrong. reset and try again.
    Serial.print(F("Wrong packet size, or overflow! packetSize= "));
    Serial.println(packetSize);
    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
    return (false); //no or corrupt data return false

} else {

/************************************
      You will want to empty the fifo buffer because the last packet is the latest reading
      If more than one packet exists then the first packet is already at least 10 MS old!!!
    ************************************/
    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;
    }
    digitalWrite(LED_PIN, !digitalRead(LED_PIN)); // Blink the LED to indicate activity
    return (true);
  }
}




and

Removed blocking while() in your main loop:



void loop() {
  // ---- read from the FIFO buffer ---- //
  // The interrupt pin could have changed for some time already.
  // So set mpuInterrupt to false and wait for the next interrupt pin CHANGE signal.
  //  Wait until the next interrupt signal. This ensures the buffer is read right after the signal change.
  if (mpuInterrupt) {
    if (readMPUFIFOBuffer()) {
      // Calculate variables of interest using the acquired values from the FIFO buffer
      // getQuaternion();
      // getEuler();
      getYawPitchRoll();
      // getRealAccel();
      // getWorldAccel();

static unsigned long SpamTimer;
      if ((millis() - SpamTimer) >= (100)) { // prints only 10 per second rather than 100 (10ms) that the MPU6050 interrupt generates
        SpamTimer = millis();
        // Example printouts of parameters of interest
        // printQuaternion();
        // printEuler();
        printYawPitchRoll();
        // printRealAccel();
        // printWorldAccel();
      }
      // ==========  Your code goes here that uses the MPU6050 readings ========= //
    }
  }

// ==========  Your code goes here for everything else ========= //

}




Z

ps. Yes, my efforts are based on Jeff Rowberg's example code.

@zhomeslice
Could you tell me what were the issues you faced and which parts of the code I wrote were causing those issues?

I tested your modified code and it seems to be working great. However, I am afraid that by removing the while loop I designed in the main loop, the old problem of occasional freezing might return under certain certain circumstances. I also tested your original code again (which originally gave me freezing issues), and now it runs perfectly also. So, I’m not sure what changed since the last time I ran the tests.

KCHuang: @zhomeslice Could you tell me what were the issues you faced and which parts of the code I wrote were causing those issues?

I tested your modified code and it seems to be working great. However, I am afraid that by removing the while loop I designed in the main loop, the old problem of occasional freezing might return under certain certain circumstances. I also tested your original code again (which originally gave me freezing issues), and now it runs perfectly also. So, I'm not sure what changed since the last time I ran the tests.

Hello @KCHuang, I'm glad to hear your code is working now. I've gone a little crazy in the past couple of weeks and completely re-wrote the entire MPU6050 code. I Literally started from scratch! Well, I did keep i2cdev library as it is perfect for the MPU6050.

So here it is: https://github.com/ZHomeSlice/Simple_MPU6050

This is how simple it is for setup:

void setup() {
  uint8_t val;
  // initialize serial communication
  Serial.begin(115200);
  while (!Serial); // wait for Leonardo enumeration, others continue immediately
  //  Wire.begin();
  Serial.println("Start: ");
  // Lets test for connection on any address
  mpu.SetAddress(MPU6050_ADDRESS_AD0_LOW).TestConnection(1);
  mpu.load_DMP_Image(OFFSETS); // Does it all for you
  mpu.on_FIFO(print_Values);
  Serial.print(F("Setup Complete in "));
  Serial.print(millis());
  Serial.println(F(" Miliseconds"));
}

And the loop() looks like this:

void loop() {
  mpu.dmp_read_fifo();// Must be in loop
}

So how does it do anything? I trigger a callback just like attachInterrupts does. This line sets it up: mpu.on_FIFO ( print_Values ) ;

So that every time the FIFO buffer has something in it this code runs:

void print_Values (int16_t *gyro, int16_t *accel, int32_t *quat, uint32_t *timestamp) {
  uint8_t Spam_Delay = 100; // Built in Blink without delay timer preventing Serial.print SPAM
  PrintAllValues(gyro, accel, quat, Spam_Delay);
}

And PrintAllValues does what it says.

There's More! Also, I have a trick so that the dreaded delay() [u]has no effect on overflow!!![/u] How you ask, the hidden yield() function is triggered while the delay is active and I take advantage of it to trim down the FIFO buffer keeping it from overflowing.

But Wait I've also created a Calibration routine :o that fits in with everything!

  mpu.CalibrateGyro(CalibrationLoops);
  mpu.CalibrateAccel(CalibrationLoops);
  mpu.PrintActiveOffsets();

The Code Footprint is smaller also !!!

I've always struggled with the MPU6050 library as it has too many unknowns and it uses an old version of the DMP software. The 6.1.2 version has auto calibration built in. and my code takes advantage of it.

Please Please try it out :) Give me feedback and if you would like, help me make it even better. Thanks Z

Hi @zhomeslice,

I’ve been testing your library for a while, going through the code, outstanding work!

I have a question, sorry if it doesn’t belong here: how do I get quaternion values so I can write them to serial as binary?

I was thinking of using something like this:

uint8_t packet[8] = { '

Thank you!, ‘0x02’, q.w, q.x, q.y, q.z, ‘\r’,’\n’ };
Serial.write( packet, 8 );


Thank you!

Ron_Dominator: Hi @zhomeslice,

I've been testing your library for a while, going through the code, outstanding work!

I have a question, sorry if it doesn't belong here: how do I get quaternion values so I can write them to serial as binary?

I was thinking of using something like this:

uint8_t packet[8] = { '

Thank you!

q.w, q.z, q.y, q.z are of type "float" so they won't fit in the 8 bit "byte" I am not sure the best practice to send a floating-point number over the serial connection. I would post this question as a new topic. there are serial port experts out can share what the best practices are for this.

Thank you for testing the code :) I just now updated it with even better calibration routines. I've also contributed to Jeffs library (Today, 7/23/19), the pull request is pending at the moment: (https://github.com/jrowberg/i2cdevlib/pull/459) I hope to see post when he wakes tomorrow.

https://github.com/jrowberg/i2cdevlib/tree/master/Arduino/MPU6050 His MPU6050 library is slightly ahead of mine as I have a small bug I need to resolve tomorrow.

I've improved the calibration to a new level! (both libraries) It is now super fast as I have a way to skip to a more detailed tuning parameter as we get closer to the ideal offsets. this has shaved off time especially if we are already close with our initial offsets.

I'll be determining what glitch I created in my Simple_MPU6050 library as I reworked it. The pitch and roll values only go to 90 instead of 180. I think it is simple like I have the accelerometer scale factor off or something.

While my code and jeffs have similar results my macro instead of function approach helps me see what I'm programming. I hope to have the magnetometer data available soon and possibly part of the DMP 9x fusion if I can find some code that breaks down the process.

Z

, '0x02', q.w, q.x, q.y, q.z, '\r','\n' }; Serial.write( packet, 8 );




Thank you!

q.w, q.z, q.y, q.z are of type "float" so they won't fit in the 8 bit "byte" I am not sure the best practice to send a floating-point number over the serial connection. I would post this question as a new topic. there are serial port experts out can share what the best practices are for this.

Thank you for testing the code :) I just now updated it with even better calibration routines. I've also contributed to Jeffs library (Today, 7/23/19), the pull request is pending at the moment: (https://github.com/jrowberg/i2cdevlib/pull/459) I hope to see post when he wakes tomorrow.

https://github.com/jrowberg/i2cdevlib/tree/master/Arduino/MPU6050 His MPU6050 library is slightly ahead of mine as I have a small bug I need to resolve tomorrow.

I've improved the calibration to a new level! (both libraries) It is now super fast as I have a way to skip to a more detailed tuning parameter as we get closer to the ideal offsets. this has shaved off time especially if we are already close with our initial offsets.

I'll be determining what glitch I created in my Simple_MPU6050 library as I reworked it. The pitch and roll values only go to 90 instead of 180. I think it is simple like I have the accelerometer scale factor off or something.

While my code and jeffs have similar results my macro instead of function approach helps me see what I'm programming. I hope to have the magnetometer data available soon and possibly part of the DMP 9x fusion if I can find some code that breaks down the process.

Z

Hi @zhomeslice,

Thank you for the response, I ended up doing as follows(do you see any mistakes btw? any comments regarding the code highly appreciated).

I’ve tested Jeffs MPU6050 and it freezes almost every time after 8 seconds.
I’ve switched to yours and left device running for 12h, yaw drifted a bit, but overall very stable.

#include "Simple_MPU6050.h"

#define MPU6050_ADDRESS_AD0_LOW     0x68
#define MPU6050_ADDRESS_AD0_HIGH    0x69
#define MPU6050_DEFAULT_ADDRESS     MPU6050_ADDRESS_AD0_LOW
#define spamtimer(t) for (static uint32_t SpamTimer; (uint32_t)(millis() - SpamTimer) >= (t); SpamTimer = millis())

Simple_MPU6050 mpu;
ENABLE_MPU_OVERFLOW_PROTECTION();

typedef union {
  int16_t value;
  byte binary[2];
} bval;

int writeQuaternion(int32_t *quat, uint16_t SpamDelay = 100) {
  spamtimer(SpamDelay) {
    bval qw;
    qw.value = quat[0] >> 16;

    bval qx;
    qx.value = quat[1] >> 16;

    bval qy;
    qy.value = quat[2] >> 16;

    bval qz;
    qz.value = quat[3] >> 16;

    // data packet
    byte packet[14] = { '

, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x00, ‘\r’, ‘\n’ };

packet[2] = qw.binary[1];
    packet[3] = qw.binary[0];
    packet[4] = qx.binary[1];
    packet[5] = qx.binary[0];
    packet[6] = qy.binary[1];
    packet[7] = qy.binary[0];
    packet[8] = qz.binary[1];
    packet[9] = qz.binary[0];

// collect on receiving end as float qw = ((qw.binary[0] << 8) | qw.binary[1]) / 16384.0f;
    Serial.write(packet, 14);
  }
}

void writeValues (int16_t *gyro, int16_t *accel, int32_t *quat, uint32_t *timestamp) {
  writeQuaternion(quat, 20);
}

void setup() {
  Wire.begin();
  Wire.setClock(400000);
 
  Serial.begin(115200);

mpu.SetAddress(MPU6050_ADDRESS_AD0_LOW).CalibrateMPU().load_DMP_Image();
  mpu.on_FIFO(writeValues);
}

void loop() {
  mpu.dmp_read_fifo();
}

Ron_Dominator:
Hi @zhomeslice,

Thank you for the response, I ended up doing as follows(do you see any mistakes btw? any comments regarding the code highly appreciated).

I’ve tested Jeffs MPU6050 and it freezes almost every time after 8 seconds.
I’ve switched to yours and left device running for 12h, yaw drifted a bit, but overall very stable.

#include "Simple_MPU6050.h"

#define MPU6050_ADDRESS_AD0_LOW     0x68
#define MPU6050_ADDRESS_AD0_HIGH    0x69
#define MPU6050_DEFAULT_ADDRESS     MPU6050_ADDRESS_AD0_LOW
#define spamtimer(t) for (static uint32_t SpamTimer; (uint32_t)(millis() - SpamTimer) >= (t); SpamTimer = millis())

Simple_MPU6050 mpu;
ENABLE_MPU_OVERFLOW_PROTECTION();

typedef union {
 int16_t value;
 byte binary[2];
} bval;

int writeQuaternion(int32_t *quat, uint16_t SpamDelay = 100) {
 spamtimer(SpamDelay) {
   bval qw;
   qw.value = quat[0] >> 16;

bval qx;
   qx.value = quat[1] >> 16;

bval qy;
   qy.value = quat[2] >> 16;

bval qz;
   qz.value = quat[3] >> 16;

// data packet
   byte packet[14] = { ’

Your code looks good but I am not as experienced with serial as many others.
As for my (Simple_MPU6050.h) Library code, I would capture the offsets that the calibration routine generates. then use the load_DMP_Image function to set the pre-captured offsets:

//               X Accel  Y Accel  Z Accel   X Gyro   Y Gyro   Z Gyro
#define OFFSETS  -5260,    6596,    7866,     -45,       5,      -9  // My Last offsets. you will want to use your own as these are only for my specific MPU6050

// Later on in your code:
  mpu.SetAddress(MPU6050_ADDRESS_AD0_LOW).load_DMP_Image(OFFSETS); // Does it all for you
  mpu.on_FIFO(writeValues);
//...

The one your using calibrates every startup and the offsets should be very similar to each other every startup.
so using the #define OFFSETS will speed up startup and you won’t need to be level at startup for calibration.

If you still want to calibrate every time you could make calibration super fast by adding

//               X Accel  Y Accel  Z Accel   X Gyro   Y Gyro   Z Gyro
#define OFFSETS  -5260,    6596,    7866,     -45,       5,      -9  // My Last offsets. you will want to use your own as these are only for my specific MPU6050
// Later on in your code:
 mpu.SetAddress(MPU6050_ADDRESS_AD0_LOW).CalibrateMPU(OFFSETS).load_DMP_Image();
 mpu.on_FIFO(writeValues);
//...

by priming the CalibrateMPU with good offsets the calibration routine only has to do tiny adjustments and it can do this extremely fast. around 200 ~ 300 readings at about 1 millisecond each for the Gyro and Accelerometer
Z, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0x00, 0x00, ‘\r’, ‘\n’ };

packet[2] = qw.binary[1];
   packet[3] = qw.binary[0];
   packet[4] = qx.binary[1];
   packet[5] = qx.binary[0];
   packet[6] = qy.binary[1];
   packet[7] = qy.binary[0];
   packet[8] = qz.binary[1];
   packet[9] = qz.binary[0];

// collect on receiving end as float qw = ((qw.binary[0] << 8) | qw.binary[1]) / 16384.0f;
   Serial.write(packet, 14);
 }
}

void writeValues (int16_t *gyro, int16_t *accel, int32_t *quat, uint32_t *timestamp) {
 writeQuaternion(quat, 20);
}

void setup() {
 Wire.begin();
 Wire.setClock(400000);
 
 Serial.begin(115200);

mpu.SetAddress(MPU6050_ADDRESS_AD0_LOW).CalibrateMPU().load_DMP_Image();
 mpu.on_FIFO(writeValues);
}

void loop() {
 mpu.dmp_read_fifo();
}

Your code looks good but I am not as experienced with serial as many others.
As for my (Simple_MPU6050.h) Library code, I would capture the offsets that the calibration routine generates. then use the load_DMP_Image function to set the pre-captured offsets:

§_DISCOURSE_HOISTED_CODE_1_§

The one your using calibrates every startup and the offsets should be very similar to each other every startup.
so using the #define OFFSETS will speed up startup and you won’t need to be level at startup for calibration.

If you still want to calibrate every time you could make calibration super fast by adding

§_DISCOURSE_HOISTED_CODE_2_§

by priming the CalibrateMPU with good offsets the calibration routine only has to do tiny adjustments and it can do this extremely fast. around 200 ~ 300 readings at about 1 millisecond each for the Gyro and Accelerometer
Z

zhomeslice: Your code looks good but I am not as experienced with serial as many others. As for my (Simple_MPU6050.h) Library code, I would capture the offsets that the calibration routine generates. then use the load_DMP_Image function to set the pre-captured offsets:

//               X Accel  Y Accel  Z Accel   X Gyro   Y Gyro   Z Gyro
#define OFFSETS  -5260,    6596,    7866,     -45,       5,      -9  // My Last offsets. you will want to use your own as these are only for my specific MPU6050

// Later on in your code:   mpu.SetAddress(MPU6050_ADDRESS_AD0_LOW).load_DMP_Image(OFFSETS); // Does it all for you   mpu.on_FIFO(writeValues); //...



The one your using calibrates every startup and the offsets should be very similar to each other every startup. 
so using the #define OFFSETS will speed up startup and you won't need to be level at startup for calibration.

If you still want to calibrate every time you could make calibration super fast by adding


//              X Accel  Y Accel  Z Accel  X Gyro  Y Gyro  Z Gyro

define OFFSETS  -5260,    6596,    7866,    -45,      5,      -9  // My Last offsets. you will want to use your own as these are only for my specific MPU6050

// Later on in your code: mpu.SetAddress(MPU6050_ADDRESS_AD0_LOW).CalibrateMPU(OFFSETS).load_DMP_Image(); mpu.on_FIFO(writeValues); //... ```

by priming the CalibrateMPU with good offsets the calibration routine only has to do tiny adjustments and it can do this extremely fast. around 200 ~ 300 readings at about 1 millisecond each for the Gyro and Accelerometer Z

Thank you very much, I'm gonna try it!

zhomeslice:
Your code looks good but I am not as experienced with serial as many others.
As for my (Simple_MPU6050.h) Library code, I would capture the offsets that the calibration routine generates. then use the load_DMP_Image function to set the pre-captured offsets:

//               X Accel  Y Accel  Z Accel   X Gyro   Y Gyro   Z Gyro

#define OFFSETS  -5260,    6596,    7866,    -45,      5,      -9  // My Last offsets. you will want to use your own as these are only for my specific MPU6050

// Later on in your code:
  mpu.SetAddress(MPU6050_ADDRESS_AD0_LOW).load_DMP_Image(OFFSETS); // Does it all for you
  mpu.on_FIFO(writeValues);
//…



The one your using calibrates every startup and the offsets should be very similar to each other every startup. 
so using the #define OFFSETS will speed up startup and you won't need to be level at startup for calibration.

If you still want to calibrate every time you could make calibration super fast by adding


//              X Accel  Y Accel  Z Accel  X Gyro  Y Gyro  Z Gyro
#define OFFSETS  -5260,    6596,    7866,    -45,      5,      -9  // My Last offsets. you will want to use your own as these are only for my specific MPU6050
// Later on in your code:
mpu.SetAddress(MPU6050_ADDRESS_AD0_LOW).CalibrateMPU(OFFSETS).load_DMP_Image();
mpu.on_FIFO(writeValues);
//…




by priming the CalibrateMPU with good offsets the calibration routine only has to do tiny adjustments and it can do this extremely fast. around 200 ~ 300 readings at about 1 millisecond each for the Gyro and Accelerometer 
Z

Good day Sir,

I’ve been using your library so far and I’ve been getting great results, with no crashing of the data or any FIFO issues despite varying the delay! However, I am facing trouble trying to use 2 MPU6050s. May I know if you have had any success in implementing a system with 2 IMUs? (one using address 0x68 and the other using 0x69).

I have managed to compile the code after renaming some of the variables, but when I open the serial monitor, the values are not printed (shown in the image)

I am currently using an Bluno Beetle (Similar to Arduino Uno) and 2 GY-521s (MPU6050 Breakout board).

I have attached my modified code.

Thank you and sorry for the inconvenience

beetle_imu_test4.ino (5.88 KB)

zhomeslice: Discovered the same problem. I ended up re-coding and simplifying the example with success The void GetDMP() { function has the resolution to the lockup issues you are facing. note that I simplified the loop() and removed all the DMP code to functions. I documented it well and hope you and others can avoid my headaches.

Please if you find additional fixes or improvements please let me know this is a work in progress for me as well

p.s. about your delay(100) look at this alternative way to do that :) look for it in the example attached

  static long QTimer = millis();
  if ((long)( millis() - QTimer ) >= 100) {
    QTimer = millis();
    Serial.print(F("\t Yaw")); Serial.print(Yaw);
    Serial.print(F("\t Pitch ")); Serial.print(Pitch);
    Serial.print(F("\t Roll ")); Serial.print(Roll);
    Serial.println();
  }
}

What are the necessary changes to this code in order to add another MPU6050 so that the program could read the data from de two MPU6050 separately?