jrowberg / i2cdevlib

I2C device library collection for AVR/Arduino or other C++-based MCUs
http://www.i2cdevlib.com
3.95k stars 7.51k forks source link

MPU6050 getting stuck in FIFO Loop #543

Open tonyvr4 opened 4 years ago

tonyvr4 commented 4 years ago

I have been testing the use of the MPU6050 without interrupt in my GPS-Accelerometer Data logger. I had to add a line to reset the FIFO before reading the FIFO since it was giving corrupted data.

For some reason it gets stuck in this loop on occasion, I have not determined why. The code was from the DMP6 example from the Electronic Cats MPU6050 library. They were not able to assist and suggested you may be able to help.

To troubleshoot I printed the fifocount and packetsize, from inside this loop, to the serial monitor. The packetsize is always 42. The fifocount is usually 0 but does increment to 42 then it exits the loop as expected.

However, for a reason I do not understand sometimes it gets stuck in the loop because fifo count does not change from 0

I would be happy to upload the full code. I started using the MPU6050 in polled mode instead of interrupt driven. My project is a data logger for GPS and Acceleration to an SD card. The interrupt would not work with the tinygps library encoding.

Any help would be greatly appreciated

// wait for MPU extra packet(s) available while (fifoCount < packetSize) { if (fifoCount < packetSize) { // try to get out of the infinite loop Serial.println("In FIFO Loop"); //for troubleshooting fifoCount = mpu.getFIFOCount(); } }

vprusa commented 4 years ago

Hello, I think I was having the same issue and it had something to do with waiting too long for FIFO reset. That works for m is in link below. The (rest of the) code is smelly (and I am too lazy to refactor it). https://github.com/vprusa/gag/blob/BTGATTSerial/proto/gag/gag.ino#L193 https://github.com/vprusa/gag/blob/BTGATTSerial/proto/gag/gag.h#L479 Notes:

tonyvr4 commented 4 years ago

I am not that familiar with the code so I do not understand how those links will help. I am including the code as it is now It has some comments to remind me what is going on since it is still a work in progress

`// // ESP32-GPS-Accel_to_SD // // TONY // MPU6050 Init, Calibrate, Read Data // Display Max-G X,Y,Z on OLED // Display GPS, Vmax, Hmax, Sats on OLED // Write GPS and Accel to SD // DOES NOT USE INTERRUPT //

include "heltec.h"

include "SSD1306Ascii.h"

include "SSD1306AsciiWire.h"

include "MPU6050_6Axis_MotionApps20.h"

include "Wire.h"

include <TinyGPS++.h>

include

const int cs_sd=26;

int LoopCounter = 0;

int16_t rawTemp; float tempC; float tempF;

//GPS RX Pin-17 GREEN WIRE //GPS TX Pin-23 YELLOW WIRE

TinyGPSPlus gps;

int maxspeed = 0, speed1 = 0; int maxhigh = 0, high1 = 0; int maxsatelite = 0, satelite1 = 0;

SSD1306AsciiWire oled;

MPU6050 mpu;

bool dmpReady = false; // set true if DMP init was successful uint8_t devStatus; // return status after each device operation (0 = success, !0 = error) uint16_t packetSize; // expected DMP packet size (default is 42 bytes) uint16_t fifoCount; // count of all bytes currently in FIFO uint8_t fifoBuffer[64]; // FIFO storage buffer float MaxGx = 0, MaxGy = 0, MaxGz = 0;

// orientation motion variables Quaternion q; // [w, x, y, z] quaternion container //VectorInt16 aa; // [x, y, z] accel sensor measurements //VectorInt16 aaReal; // [x, y, z] gravity-free accel sensor measurements //VectorInt16 aaWorld; // [x, y, z] world-frame accel sensor measurements //VectorFloat gravity; // [x, y, z] gravity vector //float euler[3]; // [psi, theta, phi] Euler angle container //float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll container and gravity vector

// ================================================================ // === SETUP === // ================================================================

void setup() { Heltec.begin(true /DisplayEnable Enable/, false /LoRa Enable/, true /Serial Enable/); oled.begin(&Adafruit128x64, 0x3C, 16); oled.setFont(System5x7); oled.clear(); oled.setCursor(25,3); oled.println("GPS and G-force"); oled.setCursor(30,4); oled.println("Logger to SD"); delay(2000); oled.clear();

  Wire.begin();  //pin 21 and 22 for ESP32
  Wire.setClock(400000); // 400kHz I2C clock

  // GPS RX=17, GPS TX=23
  Serial1.begin(9600, SERIAL_8N1, 23, 17);

  if (!SD.begin(26, 14, 13, 27))
     {
     oled.clear();
     oled.print("    NO SD Card");
     delay(2000);
     oled.clear();
     } 
    else
      {oled.print("  SD Card OK");
       delay(2000);
      }

    oled.clear();

    //SD for GPS Data
    File data = SD.open("GPS-data.txt",FILE_WRITE);        //Open the file "GPS-data.txt"
    data.println("");
    data.println("Start Recording");    // Write to file
    data.close(); 

    //SD for Accel Data
    File data2 = SD.open("ACC-data.txt",FILE_WRITE);        //Open the file "ACC-data.txt"
    data2.println("");
    data2.println("Start Recording");    // Write to file
    data2.close(); 

  // initialize serial monitor
  Serial.begin(115200);

  // initialize I2C devices
  Serial.println(F("Initializing I2C devices..."));
  mpu.initialize();

  // verify I2C device connection
  Serial.println(F("Testing device connections..."));
  Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));

  // load and configure the DMP
  Serial.println(F("Initializing DMP..."));
  oled.setCursor(0,0);
  oled.println("Calibrate Acc/Gyro");
  devStatus = mpu.dmpInitialize();

  // supply your own gyro offsets here, scaled for min sensitivity
  mpu.setXGyroOffset(220);
  mpu.setYGyroOffset(76);
  mpu.setZGyroOffset(-85);
  mpu.setZAccelOffset(1788);

  // returns 0 if MPU6050 working
if (devStatus == 0)
    {
    // Generate offsets and calibrate MPU6050
    mpu.CalibrateAccel(6);
    mpu.CalibrateGyro(6);
    mpu.PrintActiveOffsets();
    // turn on the DMP, now that it's ready
    Serial.println(F("Enabling DMP..."));
    oled.print("Accel/Gyro Ready");
    delay(1000);
    oled.clear();
    mpu.setDMPEnabled(true);

    // set our DMP Ready flag so the main loop() function knows it's okay to use it
    Serial.println(F("DMP ready!"));
    dmpReady = true;

    // get expected DMP packet size for later comparison
    packetSize = mpu.dmpGetFIFOPacketSize();
    // get FIFO count
    fifoCount = mpu.getFIFOCount();

}
  else {
    // ERROR!
    // 1 = initial memory load failed
    // 2 = DMP configuration updates failed
    // (if it's going to break, usually the code will be 1)
    Serial.print(F("DMP Initialization failed (code "));
    Serial.print(devStatus);
    Serial.println(F(")"));
}

}

// ================================================================ // === MAIN PROGRAM LOOP === // ================================================================ void loop() {

//****CODE FOR GPS****
satelite1 = (abs(gps.satellites.value()));

oled.setCursor(0,0);
oled.print("Vmax     ");
oled.print("Hmax   ");
oled.print("SAT  ");

speed1 = (gps.speed.mph()); if ( speed1 > maxspeed) { maxspeed = speed1; }

oled.setCursor(0 , 1); oled.clearToEOL(); oled.setCursor(10 , 1); oled.print(maxspeed);

high1 = (gps.altitude.feet()); if ( high1 > maxhigh) { maxhigh = high1; }

oled.setCursor(60 , 1); oled.print(maxhigh);

oled.setCursor(100 , 1); oled.print(satelite1);

oled.println(" "); oled.println(" ");

//Display Lat & Long on OLED oled.setCursor(0 , 2); oled.print("LAT "); oled.println(gps.location.lat(),6); oled.print("LNG "); oled.println(gps.location.lng(),6);

//-4 is offset for eastern time zone String Temps=String(gps.time.hour()-4)+(":")+(gps.time.minute())+(":")+(gps.time.second()); String Date=String(gps.date.month())+("/")+(gps.date.day())+("/")+(gps.date.year());

// Writing GPS data to text file //if (satelite1 > 1) { File data=SD.open("GPS-data.txt",FILE_WRITE); data.println(Date + "," + Temps + "," + String(gps.location.lat(), 6) + "," + String(gps.location.lng(), 6) + "," + String(gps.altitude.feet(),0) + "," + String(gps.speed.mph(),0) + "," + String(satelite1)); data.close();
//}

//Get temperature from MPU6050
 rawTemp = mpu.getTemperature();
 tempC=(rawTemp/340)+36.53;
 tempF=(tempC * 9/5) + 32;

// Write ACCEL G-Force data to text file //if (satelite1 > 1) { File data2=SD.open("ACC-data.txt",FILE_WRITE); //Temps is string hour, minute, second data2.println(Date + "," + Temps + "," + String(q.x, 2) + "," + String(q.y, 2) + "," + String(q.z,2) + "," + String(tempF,1)); data2.close();
//}

//ORIG Code DelayGPS(500);
//Delay of 900 gives 1 second logging interval
DelayGPS(900);

//END GPS CODE

//BEGIN MPU6050 Code

//If MPU6050 programming failed, don't do anything
//if (!dmpReady) return;
if (!dmpReady)
 {
  oled.setCursor(0,7);
  oled.clearToEOL();
  oled.println("DMP Not Ready !");
  //9-6 removed to allow processing return;
  }

//added 8-29-2020
//mpu.resetFIFO();
//added 9-4-2020
//Get current FIFO count
//fifoCount = mpu.getFIFOCount();

//wait for correct available MPU6050 data length, should be a VERY short wait
//*****CODE GETTING STUCK IN THIS LOOP************

LoopCounter = 0;

while (fifoCount < packetSize && LoopCounter < 10)
  {
  fifoCount = mpu.getFIFOCount();
  Serial.println("FIFO Loop=" + String(fifoCount) + " PacketSize=" + String(packetSize));  //fifocount=0 and packetsize=42
  LoopCounter++;
  Serial.println("Loops=" + String(LoopCounter));
  }

if (LoopCounter = 10)
  {
   //mpu.resetFIFO();
   fifoCount = packetSize;
  }

Serial.println("END FIFO Loop");
Serial.println(" ");

 // check for FIFO overflow
 if(fifoCount < packetSize)
  {
    //DO NOTHING
  }
else if (fifoCount >= 1024)
  {
    //Reset so we can continue cleanly
    mpu.resetFIFO();
    fifoCount = mpu.getFIFOCount();  // will be zero after FIFO reset
    Serial.println(F("FIFO overflow!"));
    oled.setCursor(0,7);
    oled.clearToEOL();
    oled.println("FIFO Overflow !");
  } 

    //Read a packet from FIFO
    while(fifoCount >= packetSize)
      {
       mpu.getFIFOBytes(fifoBuffer, packetSize);
       //Track FIFO count in case there is > 1 packet available
       fifoCount -= packetSize;
       //Serial.println("Read FIFO Count=" + String(fifoCount) + " Packetsize=" + String(packetSize));
      }

        // display quaternion values x y z
        mpu.dmpGetQuaternion(&q, fifoBuffer);
        //USE THIS FOR LIVE ACCEL DATA DISPLAY
        Serial.print("G-Force\t");
        //Serial.print(q.w);
        //Serial.print("\t");
        Serial.print(q.x);
        Serial.print("\t");
        Serial.print(q.y);
        Serial.print("\t");
        Serial.println(q.z);

        //calculate Max Accel X,Y,Z
         if (abs(q.x) > abs(MaxGx))
          {
            MaxGx = q.x;
          }
         else
          {
            MaxGx = MaxGx;
          }

        if (abs(q.y) > abs(MaxGy))
          {
            MaxGy = q.y;
          }
         else
          {
            MaxGy = MaxGy;
          }

        if (abs(q.z) > abs(MaxGz))
          {
            MaxGz = q.z;
          }
           else
          {
            MaxGz = MaxGz;
          }

         //Display LIVE ACCEL G data on OLED
          //oled.setCursor(0,5);
          //oled.clearToEOL();
          //oled.println("ACCEL  X     Y     Z");
          //oled.setCursor(0,6);
          //oled.clearToEOL();
          //oled.setCursor(20,6);
          //oled.print(q.x,2);
          //oled.setCursor(60,6);
          //oled.print(q.y,2);
          //oled.setCursor(95,6);
          //oled.println(q.z,2);

          //DISPLAY MAX G on OLED
          oled.setCursor(0,5);
          oled.println("MaxG   X     Y     Z");
          oled.setCursor(0,6);
          oled.clearToEOL();
          oled.setCursor(20,6);
          oled.print(MaxGx);
          oled.setCursor(60,6);
          oled.print(MaxGy);
          oled.setCursor(95,6);
          oled.println(MaxGz);
          oled.setCursor(0,7);
          oled.println ("TEMP = " + String(tempF,1) + " F");

          //Added for stable MPU data
          mpu.resetFIFO();

}

//Delay function but continue to receive date from GPS static void DelayGPS(unsigned long ms) { unsigned long start = millis(); do { while (Serial1.available()) gps.encode(Serial1.read()); mpu.resetFIFO(); } while (millis() - start < ms); } `

ZHomeSlice commented 4 years ago

The code your referencing is flawed

This attached version gets the latest FIFO packed in the most efficient way possible. This can be spammed to get the packed when it becomes available so you can ignore interrupts (if you don't need the extra processing time). it also handles overflow etc.

Copied from: MPU6050.cpp line 2744 ~2782

/** Get latest byte from FIFO buffer no matter how much time has passed.
 * ===                  GetCurrentFIFOPacket                    ===
 * ================================================================
 * Returns 1) when nothing special was done
 *         2) when recovering from overflow
 *         0) when no valid data is available
 * ================================================================ */
 int8_t MPU6050::GetCurrentFIFOPacket(uint8_t *data, uint8_t length) { // overflow proof
     int16_t fifoC;
     // This section of code is for when we allowed more than 1 packet to be acquired
     uint32_t BreakTimer = micros();
     do {
         if ((fifoC = getFIFOCount())  > length) {

             if (fifoC > 200) { // if you waited to get the FIFO buffer to > 200 bytes it will take longer to get the last packet in the FIFO Buffer than it will take to  reset the buffer and wait for the next to arrive
                 resetFIFO(); // Fixes any overflow corruption
                 fifoC = 0;
                 while (!(fifoC = getFIFOCount()) && ((micros() - BreakTimer) <= (11000))); // Get Next New Packet
                 } else { //We have more than 1 packet but less than 200 bytes of data in the FIFO Buffer
                 uint8_t Trash[BUFFER_LENGTH];
                 while ((fifoC = getFIFOCount()) > length) {  // Test each time just in case the MPU is writing to the FIFO Buffer
                     fifoC = fifoC - length; // Save the last packet
                     uint16_t  RemoveBytes;
                     while (fifoC) { // fifo count will reach zero so this is safe
                         RemoveBytes = min((int)fifoC, BUFFER_LENGTH); // Buffer Length is different than the packet length this will efficiently clear the buffer
                         getFIFOBytes(Trash, (uint8_t)RemoveBytes);
                         fifoC -= RemoveBytes;
                     }
                 }
             }
         }
         if (!fifoC) return 0; // Called too early no data or we timed out after FIFO Reset
         // We have 1 packet
         if ((micros() - BreakTimer) > (11000)) return 0;
     } while (fifoC != length);
     getFIFOBytes(data, length); //Get 1 packet
     return 1;
} 

Z

tonyvr4 commented 4 years ago

I figured that but I did not know how to fix it. But your post will help a lot.

I am a beginner with the MPU6050 and tried to paste your code into my sketch. I got so many errors from the first line and many of these errors I have never seen before and do not know how to fix them.

I am not using an interrupt but am confused when you mentioned "This can be spammed to get the packed when it becomes available"

Thanks for the help

tonyvr4 commented 4 years ago

Any suggestion on how to fix this?

ZHomeSlice commented 4 years ago

@tonyvr4 "I am not using an interrupt but am confused when you mentioned "This can be spammed to get the packed when it becomes available"" without interrupts, you will need to manage overflow, and depending upon your sequence manage no data.

This is a simplified summery of what is going on:

  do {
         if ((fifoC = getFIFOCount())  > length) { // this gets the fifo count and checks to see if we have more than 1 packet
... clean up the extra data and or fix the overflow coruption
         }
         if (!fifoC) return 0; // Called too early no data or we timed out after FIFO Reset
         // We (should) have 1 packet
     } while (fifoC != length); // proof our assumption
     getFIFOBytes(data, length); //Get 1 packet
     return 1;

because we check for a packet here if (!fifoC) return 0; // Called too early no data or we timed out after FIFO Reset we return 0 (zero) allowing your code to do something else if you get zero from this function you can just ask it again and again until you get a 1(one) as a return and now you have 1 packet of data loaded ready to process.

I believe you will find this simple. This function "GetCurrentFIFOPacket" is located in the MPU6050.cpp library you are referencing here. The latest example code uses it. if you haven't downloaded all the updates and fixed that have occurred on this library you might want to do that first to avoid errors.

Added function to your code you provided above

//BEGIN MPU6050 Code
// Replace with this until you arrive at your code below.
if(mpu.GetCurrentFIFOPacket(fifoBuffer, packetSize) ){// gets the latest packet if available and runs your code else this will skips your code because there is nothing new to process.

// YOUR CODE
     // display quaternion values x y z
        mpu.dmpGetQuaternion(&q, fifoBuffer);
        //USE THIS FOR LIVE ACCEL DATA DISPLAY
        Serial.print("G-Force\t");
        //Serial.print(q.w);
        //Serial.print("\t");
        Serial.print(q.x);
        Serial.print("\t");
        Serial.print(q.y);
        Serial.print("\t");
        Serial.println(q.z);

        //calculate Max Accel X,Y,Z
         if (abs(q.x) > abs(MaxGx))
          {
            MaxGx = q.x;
          }
         else
          {
            MaxGx = MaxGx;
          }

        if (abs(q.y) > abs(MaxGy))
          {
            MaxGy = q.y;
          }
         else
          {
            MaxGy = MaxGy;
          }

        if (abs(q.z) > abs(MaxGz))
          {
            MaxGz = q.z;
          }
           else
          {
            MaxGz = MaxGz;
          }

         //Display LIVE ACCEL G data on OLED
          //oled.setCursor(0,5);
          //oled.clearToEOL();
          //oled.println("ACCEL  X     Y     Z");
          //oled.setCursor(0,6);
          //oled.clearToEOL();
          //oled.setCursor(20,6);
          //oled.print(q.x,2);
          //oled.setCursor(60,6);
          //oled.print(q.y,2);
          //oled.setCursor(95,6);
          //oled.println(q.z,2);

          //DISPLAY MAX G on OLED
          oled.setCursor(0,5);
          oled.println("MaxG   X     Y     Z");
          oled.setCursor(0,6);
          oled.clearToEOL();
          oled.setCursor(20,6);
          oled.print(MaxGx);
          oled.setCursor(60,6);
          oled.print(MaxGy);
          oled.setCursor(95,6);
          oled.println(MaxGz);
          oled.setCursor(0,7);
          oled.println ("TEMP = " + String(tempF,1) + " F");
}

Your request was simpler than most so I provided an example most of the time this may not be possible and I would suggest posting on arduino.cc forum for help. tag me here in when you do and often I will jump in to help.

Z

tonyvr4 commented 4 years ago

This is a great step forward! However, the library I am using is from Electronic Cats, which is based on the library by jrowberg. Their function is a little different. The function to read the FIFO is mpu.getFIFOBytes(fifoBuffer, packetSize);

The reason I started with their library is because it is compatible with the ESP32 I would have no problem recoding if this library is compatible with the ESP32

Also, I did post on the arduino.cc a while back and got nothing by sarcastic and useless responses. In the months I was on there I did not receive one productive response

ZHomeSlice commented 4 years ago

This function should work after being added to his mpu6050 code if it is an adaptation of jeffs

ask to see if someone there can add this function. I may find time to compare the changes to there code but I don't have an ESP to test it on.

This is the function that they should add.

/** Get latest byte from FIFO buffer no matter how much time has passed.
 * ===                  GetCurrentFIFOPacket                    ===
 * ================================================================
 * Returns 1) when nothing special was done
 *         2) when recovering from overflow
 *         0) when no valid data is available
 * ================================================================ */
 int8_t MPU6050::GetCurrentFIFOPacket(uint8_t *data, uint8_t length) { // overflow proof
     int16_t fifoC;
     // This section of code is for when we allowed more than 1 packet to be acquired
     uint32_t BreakTimer = micros();
     do {
         if ((fifoC = getFIFOCount())  > length) {

             if (fifoC > 200) { // if you waited to get the FIFO buffer to > 200 bytes it will take longer to get the last packet in the FIFO Buffer than it will take to  reset the buffer and wait for the next to arrive
                 resetFIFO(); // Fixes any overflow corruption
                 fifoC = 0;
                 while (!(fifoC = getFIFOCount()) && ((micros() - BreakTimer) <= (11000))); // Get Next New Packet
                 } else { //We have more than 1 packet but less than 200 bytes of data in the FIFO Buffer
                 uint8_t Trash[BUFFER_LENGTH];
                 while ((fifoC = getFIFOCount()) > length) {  // Test each time just in case the MPU is writing to the FIFO Buffer
                     fifoC = fifoC - length; // Save the last packet
                     uint16_t  RemoveBytes;
                     while (fifoC) { // fifo count will reach zero so this is safe
                         RemoveBytes = min((int)fifoC, BUFFER_LENGTH); // Buffer Length is different than the packet length this will efficiently clear the buffer
                         getFIFOBytes(Trash, (uint8_t)RemoveBytes);
                         fifoC -= RemoveBytes;
                     }
                 }
             }
         }
         if (!fifoC) return 0; // Called too early no data or we timed out after FIFO Reset
         // We have 1 packet
         if ((micros() - BreakTimer) > (11000)) return 0;
     } while (fifoC != length);
     getFIFOBytes(data, length); //Get 1 packet
     return 1;
} 

your alternative is to make this function a local function in your code. this version is not part of the MPU6050 class NOTE: this has not been tested and may contain errors!!!

/** Get the latest byte from FIFO buffer no matter how much time has passed.
 * ===                  GetCurrentFIFOPacket                    ===
 * ================================================================
 * Returns 1) when nothing special was done
 *         2) when recovering from overflow
 *         0) when no valid data is available
 * ======================
 int8_t GetCurrentFIFOPacket(uint8_t *data, uint8_t length) { // overflow proof
     int16_t fifoC;
     // This section of code is for when we allowed more than 1 packet to be acquired
     uint32_t BreakTimer = micros();
     do {
         if ((fifoC = mpu.getFIFOCount())  > length) {

             if (fifoC > 200) { // if you waited to get the FIFO buffer to > 200 bytes it will take longer to get the last packet in the FIFO Buffer than it will take to  reset the buffer and wait for the next to arrive
                 mpu.resetFIFO(); // Fixes any overflow corruption
                 fifoC = 0;
                 while (!(fifoC = mpugetFIFOCount()) && ((micros() - BreakTimer) <= (11000))); // Get Next New Packet
                 } else { //We have more than 1 packet but less than 200 bytes of data in the FIFO Buffer
                 uint8_t Trash[BUFFER_LENGTH];
                 while ((fifoC = mpu.getFIFOCount()) > length) {  // Test each time just in case the MPU is writing to the FIFO Buffer
                     fifoC = fifoC - length; // Save the last packet
                     uint16_t  RemoveBytes;
                     while (fifoC) { // fifo count will reach zero so this is safe
                         RemoveBytes = min((int)fifoC, BUFFER_LENGTH); // Buffer Length is different than the packet length this will efficiently clear the buffer
                         mpu.getFIFOBytes(Trash, (uint8_t)RemoveBytes);
                         fifoC -= RemoveBytes;
                     }
                 }
             }
         }
         if (!fifoC) return 0; // Called too early no data or we timed out after FIFO Reset
         // We have 1 packet
         if ((micros() - BreakTimer) > (11000)) return 0;
     } while (fifoC != length);
     mpu.getFIFOBytes(data, length); //Get 1 packet
     return 1;
} 

Z

tonyvr4 commented 4 years ago

I posted the suggestion and the new code in the issue section of the Electronic Cats MPU6050 github. Hopefully someone will reply and add the function

ZHomeSlice commented 4 years ago

@tonyvr4 Did the other function work?

/** Get the latest byte from FIFO buffer no matter how much time has passed.
 * ===                  GetCurrentFIFOPacket                    ===
 * ================================================================
 * Returns 1) when Data loaded
 *         0) when no valid data is available
 * ======================
 int8_t GetCurrentFIFOPacket(uint8_t *data, uint8_t length) { // overflow proof
     int16_t fifoC;
     // This section of code is for when we allowed more than 1 packet to be acquired
     uint32_t BreakTimer = micros();
     do {
         if ((fifoC = mpu.getFIFOCount())  > length) {

             if (fifoC > 200) { // if you waited to get the FIFO buffer to > 200 bytes it will take longer to get the last packet in the FIFO Buffer than it will take to  reset the buffer and wait for the next to arrive
                 mpu.resetFIFO(); // Fixes any overflow corruption
                 fifoC = 0;
                 while (!(fifoC = mpu.getFIFOCount()) && ((micros() - BreakTimer) <= (11000))); // Get Next New Packet
                 } else { //We have more than 1 packet but less than 200 bytes of data in the FIFO Buffer
                 uint8_t Trash[BUFFER_LENGTH];
                 while ((fifoC = mpu.getFIFOCount()) > length) {  // Test each time just in case the MPU is writing to the FIFO Buffer
                     fifoC = fifoC - length; // Save the last packet
                     uint16_t  RemoveBytes;
                     while (fifoC) { // fifo count will reach zero so this is safe
                         RemoveBytes = min((int)fifoC, BUFFER_LENGTH); // Buffer Length is different than the packet length this will efficiently clear the buffer
                         mpu.getFIFOBytes(Trash, (uint8_t)RemoveBytes);
                         fifoC -= RemoveBytes;
                     }
                 }
             }
         }
         if (!fifoC) return 0; // Called too early no data or we timed out after FIFO Reset
         // We have 1 packet
         if ((micros() - BreakTimer) > (11000)) return 0;
     } while (fifoC != length);
     mpu.getFIFOBytes(data, length); //Get 1 packet
     return 1;
} 

minor change here also

//BEGIN MPU6050 Code
// Replace with this until you arrive at your code below.
if(GetCurrentFIFOPacket(fifoBuffer, packetSize) ){// gets the latest packet if available and runs your code else this will skip your code because there is nothing new to process.

// YOUR CODE

Z

tonyvr4 commented 4 years ago

Do you mean the function you provided? I can't tell if it worked as I do not know how to edit a library file.

ZHomeSlice commented 4 years ago

The above post "Did the other function work?" is for your sketch. I removed all need to add this to the library. Just place this function in your sketch and modify the line in your loop. This should work. compare the two to see what I changed to remove it from requiring to be in a library.

Z

tonyvr4 commented 4 years ago

The above post "Did the other function work?" is for your sketch. I removed all need to add this to the library. Just place this function in your sketch and modify the line in your loop. This should work. compare the two to see what I changed to remove it from requiring to be in a library.

Z

I am so sorry for the misunderstanding. I will try that code tomorrow and let you know Thanks you are awesome!

tonyvr4 commented 4 years ago

I tried pasting the code into my sketch and got a bunch of compile errors.

Procedure 1) Paste in function 2) Remove existing code in mail loop for MPU6050 3) Paste in new call to function if(GetCurrentFIFOPacket(fifoBuffer, packetSize) ) { // gets the latest packet if available and runs your code else this will skip your code because there is // nothing new to process. }``

The compile error is below

C:\NEW-ESP32-GPS-Accel_to_SD: In function 'int8_t GetCurrentFIFOPacket(uint8_t*, uint8_t)':

NEW-ESP32-GPS-Accel_to_SD:351:32: error: 'BUFFER_LENGTH' was not declared in this scope

              uint8_t Trash[BUFFER_LENGTH];

                            ^

NEW-ESP32-GPS-Accel_to_SD:357:43: error: 'Trash' was not declared in this scope

                      mpu.getFIFOBytes(Trash, (uint8_t)RemoveBytes);

                                       ^

exit status 1

'BUFFER_LENGTH' was not declared in this scope

tonyvr4 commented 4 years ago

If I set BUFFER_LENGTH to any number the compile error goes away. However the code never returns data from the MPU. It always displays 0

ZHomeSlice commented 4 years ago
// Place at or near the top of your code
#define BUFFER_LENGTH 32 

I'm not sure why your code isn't catching this definition. See if that fixes the problem.

Z

tonyvr4 commented 4 years ago

There is no NBWIRE_BUFFER_LENGTH in the function you provided. There is a BUFFER_LENGTH Is that what I should define as 32 ?

ZHomeSlice commented 4 years ago

sorry cut and paste try

define BUFFER_LENGTH 32

tonyvr4 commented 4 years ago

That gets the ball rolling. There is valid data being read from the MPU now. But for some reason after a few seconds, 30 sec or so, the x,y,z values from the MPU freeze and stop changing even if there is motion.

This lockup interval is not consistent. I ran it a few times and one time the data locked up after a minute or so. In other tests ir could be 10 seconds and yet another ran for 30 seconds before the data froze.

I have a line in the MPU section that prints the live x,y,x values to the serial monitor so I can test and see what is going on. This is the data that freezes and stops changing

Is there a way I can see the returned value from the function?

tonyvr4 commented 4 years ago

Any suggestions on how to fix the data freezing issue?

tonyvr4 commented 4 years ago

I am forced to put this project on hold until a solution can be found.

tonyvr4 commented 4 years ago

I can't figure out how to create a pull request from this issue. Can you assist?

ZHomeSlice commented 4 years ago

@tonyvr4, Have you discovered a fix for your freezing issue?

FYI In the past, after discovering this routine, MPU 6050 freezing issues have been isolated and determined to be related to hardware and i2c bus noise. The MPU6050 would freeze completely and not provide any new data.

so you might want to review what we went through to come up with this function GetCurrentFifoPacket() somewhere around December 9, 2019, we discovered the current routine. This was further refined to what you see today. The discussion can be found here: https://github.com/jrowberg/i2cdevlib/issues/479

@paynterf was incredibly helpful and he was experiencing similar issues as you are. You might find items within his comments to further resolve your struggles.

Z

kkowalk commented 4 years ago

Hi, I also had freeze issue. Checked few options described above but on the end the solution, at least for Arduino Pro Nano, is to slow down the readings.

MPU6050_6Axis_MotionApps_V6_12.h :

I2Cdev::writeBytes(devAddr,0x19, 1, &(val = 0x08)); // 0000 1000 SMPLRT_DIV: Divides the internal sample rate 400Hz ( Sample Rate = Gyroscope Output Rate / (1 + SMPLRT_DIV))

Adjusted above settings and now looks fine. Still - havent tested for really long time (used to hang within few minutes, now working for 2 hours) plus I need to make sure there are no other consequences.

tonyvr4 commented 4 years ago

Hi, I also had freeze issue. Checked few options described above but on the end the solution, at least for Arduino Pro Nano, is to slow down the readings.

MPU6050_6Axis_MotionApps_V6_12.h :

I2Cdev::writeBytes(devAddr,0x19, 1, &(val = 0x08)); // 0000 1000 SMPLRT_DIV: Divides the internal sample rate 400Hz ( Sample Rate = Gyroscope Output Rate / (1 + SMPLRT_DIV))

Adjusted above settings and now looks fine. Still - havent tested for really long time (used to hang within few minutes, now working for 2 hours) plus I need to make sure there are no other consequences.

Do you mean modify the library?

First, I do not know how to do that. But I could learn.

However, this function provided by Homer Creutz at the gitbhub jrowberg/i2cdevlib has the same issue I am not sure if I posted the function. I apologize if I forgot.

Here it is (comments are part of the provided code) //BEGIN FUNCTION GetCurrentFIFOPacket // Get the latest byte from FIFO buffer no matter how much time has passed. // === GetCurrentFIFOPacket === // ================================================================ // Returns 1) when nothing special was done // 2) when recovering from overflow // 0) when no valid data is available // ====================== // int8_t GetCurrentFIFOPacket(uint8_t *data, uint8_t length) { // overflow proof LoopCounter++;

 int16_t fifoC;
 // This section of code is for when we allowed more than 1 packet to be acquired
 uint32_t BreakTimer = micros();

 Serial.println("Function  Loops=" + String(LoopCounter));

 do {
     if ((fifoC = mpu.getFIFOCount())  > length) {

         if (fifoC > 200) { // if you waited to get the FIFO buffer to > 200 bytes it will take longer to get the last packet in the FIFO Buffer than it will take to  reset the buffer and wait for the next to arrive
             mpu.resetFIFO(); // Fixes any overflow corruption
             fifoC = 0;
             while (!(fifoC = mpu.getFIFOCount()) && ((micros() - BreakTimer) <= (11000))); // Get Next New Packet
             } else { //We have more than 1 packet but less than 200 bytes of data in the FIFO Buffer
             uint8_t Trash[BUFFER_LENGTH];
             while ((fifoC = mpu.getFIFOCount()) > length) {  // Test each time just in case the MPU is writing to the FIFO Buffer
                 fifoC = fifoC - length; // Save the last packet
                 uint16_t  RemoveBytes;
                 while (fifoC) { // fifo count will reach zero so this is safe
                     RemoveBytes = min((int)fifoC, BUFFER_LENGTH); // Buffer Length is different than the packet length this will efficiently clear the buffer
                     mpu.getFIFOBytes(Trash, (uint8_t)RemoveBytes);
                     fifoC -= RemoveBytes;
                 }
             }
         }
     }
     if (!fifoC) return 0; // Called too early no data or we timed out after FIFO Reset
     // We have 1 packet
     if ((micros() - BreakTimer) > (11000)) return 0;
 } while (fifoC != length);
 //ORIG LINE mpu.getFIFOBytes(data, length); //Get 1 packet
 mpu.getFIFOBytes(fifoBuffer, packetSize);
 return 1;

}

kkowalk commented 4 years ago

Hello,

Do you mean modify the library?

Yes, I have modified the file MPU6050_6Axis_MotionApps_V6_12.h . Just change the value in this line (originally divider is 0x04):

I2Cdev::writeBytes(devAddr,0x19, 1, &(val = 0x08)); // 0000 1000 SMPLRT_DIV: Divides the internal sample rate 400Hz ( Sample Rate = Gyroscope Output Rate / (1 + SMPLRT_DIV))

You can try with higher dividers, thus, if I understand that correctly, lower performance - actually I am quite sure this has expected effect because if set even higher than 0x08 it tends to "skip" some readings (fast movements).

However, what I can add right now - the situation is much better but after a night I found the device hanged again. It lasted few hours though so there is big improvement. But it may also prove that there is still some memory leak which we actually did not solve. So changing the divider may on the end not be final solution. I will keep looking but Im afraid it will take more time than I expected as I need to dig into documentation (at least that available part).

tonyvr4 commented 4 years ago

Hello,

Do you mean modify the library?

Yes, I have modified the file MPU6050_6Axis_MotionApps_V6_12.h . Just change the value in this line (originally divider is 0x04):

I2Cdev::writeBytes(devAddr,0x19, 1, &(val = 0x08)); // 0000 1000 SMPLRT_DIV: Divides the internal sample rate 400Hz ( Sample Rate = Gyroscope Output Rate / (1 + SMPLRT_DIV))

You can try with higher dividers, thus, if I understand that correctly, lower performance - actually I am quite sure this has expected effect because if set even higher than 0x08 it tends to "skip" some readings (fast movements).

However, what I can add right now - the situation is much better but after a night I found the device hanged again. It lasted few hours though so there is big improvement. But it may also prove that there is still some memory leak which we actually did not solve. So changing the divider may on the end not be final solution. I will keep looking but Im afraid it will take more time than I expected as I need to dig into documentation (at least that available part).

That is awesome. Although I am not sure how to apply it. The library I am using is the MPU6050 library by Electronic Cats

I checked today and there is no update available

ZHomeSlice commented 4 years ago

@tonyvr4 from the manual:

4.2 Register 25 – Sample Rate Divider SMPRT_DIV Type: Read/Write

This register specifies the divider from the gyroscope output rate used to generate the Sample Rate for the MPU-60X0. The sensor register output, FIFO output, and DMP sampling are all based on the Sample Rate. The Sample Rate is generated by dividing the gyroscope output rate by SMPLRT_DIV: Sample Rate = Gyroscope Output Rate / (1 + SMPLRT_DIV) where Gyroscope Output Rate = 8kHz when the DLPF is disabled (DLPF_CFG = 0 or 7), and 1kHz when the DLPF is enabled (see Register 26). Note: The accelerometer output rate is 1kHz. This means that for a Sample Rate greater than 1kHz, the same accelerometer sample may be output to the FIFO, DMP, and sensor registers more than once. For a diagram of the gyroscope and accelerometer signal paths, see Section 8 of the MPU6000/MPU-6050 Product Specification document. Parameters: SMPLRT_DIV 8-bit unsigned value. The Sample Rate is determined by dividing the gyroscope output rate by this value.

I don't have time to determine how the DMP is affected by the change in sample rate.

Jeff's attempts were to use the InvenSense example program they provide and use the values that InvenSense deems appropriate to run the chip. The reason we didn't use the InvenSense code out of the box is the end result was bloated and would bearly fit into an atmega328p or similar processors so the refining began and this is the resulting code. In Jeff's code, the embedded DMP BLOB is pre-configured for the resulting output and rates before uploading it to the mpu6050 saving valuable memory for our projects. There are several examples that use the InvenSense code which could work better with larger memory processors. In addition, none of this code uses the 9 deg of freedom library provided by InvenSense. This library runs in the processor instead of the mpuXXXX chips to provide compass integration. The library is a pre-compiled .lib file.

Z aka (Homer Creutz)

kkowalk commented 4 years ago

ZHomeSlice - exactly, my idea was to lower the "loading". Thus I changed the divider. Yet, I am now quite convinced its workaround, not solving the problem. You also mentioned different libraries. I have checked few but with not much success (different problems with readings etc.). Do you maybe have any suggestions for board like Arduino Pro Nano? Or any successful implementation on some more powerful chipsets?

Best Regards,

ZHomeSlice commented 4 years ago

While I love messing with the MPU6050 the main reason I pursued it was the price and it worked with the atmega328p. If you are looking for a solution with a more powerful chipset and say $80 US isn't an issue, take a look at this all in one Bluetooth option (it has everything) https://invensense.tdk.com/smartbug/ also a $20US 9 deg of freedom option: ICM-20948 9-axis MotionTracking device breakout

You may want to register with Invensense as a developer and take a look at their download section https://invensense.tdk.com/developers/software-downloads/

While I enjoy working with the mpu6050 and have made several projects with it. it is a discontinued item and much better solutions are available now for more powerful chipsets.

As for your crashing issues. I'm assuming you are using an MPU6050 breakout board? If so, are you powering it with 3.3V or 5V? Why I ask is that you should be powering the MPU6050 Breakout boards with 5V as they have a built-in Voltage regulator for 3.3V. you are probably underpowering the mpu6050. a solution is to bypass the voltage regulator when powering with 3.3v. The mpu6050 breakout also has pull-up resistors tied to the 3.3V side of the voltage regulator. MPU6050 Breakout Schematic

Z

tonyvr4 commented 4 years ago

There is no way I could afford an $80 product. But this could explain why it was so cheap. I have powered the board with 3.3v and 5v and it did not have any effect on the problem.

I would hate to rewrite the entire sketch for another product. But at this point it sounds like the only option.""

I need it to be as small as possible because it will be used to log GPS data and acceleration for my RC vehicles. What would you recommend?

paynterf commented 4 years ago

Tonyvr4,

Don't give up on the cheap, ubiquitous MPU6050 IMU modules. I have been using them in my robot projects for years now, and they seem to be completely bullet-proof when used and driven properly. Take a look at this post on my 'Paynters Palace' blog site. You don't need to understand it in detail, but you should come away with the realization that if you use Homer Creutz's GetCurrentFIFOPacket() routine that is embedded in Jeff Rowberg's library, you can rest assured that you will get reliable, valid data from the MPU6050 whether you use the interrupt method or the polling method.

It sounds to me like you have run up against the classic I2C bus 'freeze' problem, and if so, it has nothing at all to do with the MPU6060. I2C bus freezes are a known problem with Arduino, and it has only been recently addressed. See this post for all the gory details. The good news is, if you use the latest Wire library code and add the timeout setting line to your setup() function like this

Wire.setWireTimeout(3000, true); //timeout value in uSec

then the I2C freeze problem should go away. Note that if you aren't using the latest Wire library, the above code line won't compile, so it's easy to tell one way or the other.

Hope this helps,

Frank

tonyvr4 commented 4 years ago

You hit on the reason I looked at the MPU6050, it was inexpensive and it did what I needed.

I added that line to my code and got this error: 'class TwoWire' has no member named 'setWireTimeout'

I checked the library manager and there are no updates to any of my libraries

paynterf commented 4 years ago

The Wire library isn't a downloadable auxiliary library - it's part of the Arduino core, and you have to update Wire.h & Wire.cpp for the particular hardware you are using. In my case, since I was using an Arduino Mega, I cloned the entire Arduino-core library from GitHub to my local Arduino folder, and then replaced Wire.cpp & Wire.h in the Arduino core files on my PC at C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\Wire\src. If you are using a different MCU, then Wire.cpp & Wire.h will be in a different folder. Note that the Wire.cpp & Wire.h for some hardware (notably the Teensy line) already includes timeouts, so one alternative would be to just get yourself a Teensy or two and use them.

Another alternative (and the one I used for years) is to use the SBWire library, which fixed the I2C hangup problem long ago. If you are using Jeff Rowberg's library for the MPU6050, there are #defines you can enable that switches out the Arduino core Wire library for the SBWire versions that do have timeouts. See this post for the details.

I'm pretty sure that digging around in the core Arduino files wasn't really what you had in mind when you started using Arduino and MPU6050, but if you want to use I2C you'll just have to deal with it until the modified files are incorporated into an Arduino IDE update, assuming the Arduino folks ever get around to it. YMMV

Frank

kkowalk commented 4 years ago

It sounds to me like you have run up against the classic I2C bus 'freeze' problem, and if so, it has nothing at all to do with the MPU6060. I2C bus freezes are a known problem with Arduino, and it has only been recently addressed. See this post for all the gory details. The good news is, if you use the latest Wire library code and add the timeout setting line to your setup() function like this

Ha, finally. I dont know why I never had this issue before with older driver, even with much more devices on the line. Anyway, tried this first for an hour - looked promising. Now I restored divider value for accelerometer - its running for 2h while before it was hanging within 10-30min. So - I will leave it as is for a while and see. Still, looks to be it, thanks!

paynterf commented 4 years ago

Glad to hear you are making progress. Which route did you take - replacing Wire.cpp/h, or SBWire, or something else?

Frank

kkowalk commented 4 years ago

Actually I just finally updated Arduino SDK. In libraries manager there is also a package for boards, normally a part of SDK. I have updated to newest one, and then added the delay as suggested above.

tonyvr4 commented 4 years ago

It seems that this issue is more involved than I thought. I do not want to abandon the project so I am looking at alternatives.

Can I get acceleration from GPS somehow?

If not then are there other accelerometer modules that will work ?

tonyvr4 commented 3 years ago

Any updates on this issue? I was ready to wire up a usable version of this project but can not continue due to the I2C freeze issue

uDude commented 3 years ago

At the current state for me, The OScope show significant noise at peaks down to 2V, that are a High was bouncing 2-3V all the time with occassionally more. The problem is that the board produceD (in my case) not follow manufacturer guidelines in trace placement and size among other things. I use a pF cap to kill some noise on both clock and sda and everything is more more stable.

Note, problem not i2cdevlib. It are a BAD board layout. You can put in a simple HF filter (LB-pass) and get by. Also some folks clain the 6050 is 5V tolerant though the specification do not so state. This give less %noise if correct.

Final note if just bad data you use another gpio with n-channel MOSFET to reboot mcu when you detect needed it. High = on, low = turn off.

Oh my board NOT HAVE switching regulator. Use LDO to 3.3V. Not checked to see if LDO is under capable and should replace with ams1117 (support 1A, many crap ones support 250-500 mA only). Maybe layout good board and give free to ALI vendors,but board not recommend new dev. Brain dead, by

k0xxx commented 3 years ago

To resolve this issue you need to increase the delay between mpu creation task and readings from mpu vTaskDelay(2000 / portTICK_PERIOD_MS); Maybe it was related to some delay while MPU setting up all the things under the hood

chanov commented 3 years ago

To resolve this issue you need to increase the delay between mpu creation task and readings from mpu vTaskDelay(2000 / portTICK_PERIOD_MS); Maybe it was related to some delay while MPU setting up all the things under the hood

Please show example code of this.