Problems combining MPU6050 Accel/Gyro with SD card and On/Off Button: HELP!

Hi all,

I know this is long, but I've tried to be as transparent as I can so please read and give me your thoughts!

I'm pretty much a beginner to Arduino coding etc and I'm trying to combine the DMP functions of the MPU6050 accelerometer/gyroscope (GY-521 breakout board) with an SD shield bought from Adafruit (Model Adafruit Assembled Data Logging shield for Arduino : ID 1141 : $13.95 : Adafruit Industries, Unique & fun DIY electronics and kits) and a simple momentary push button in order to make a simple datalogging device. I am attempting this by combining the standard SD card libraries and code with Jeff Rowberg's DMP libraries and some of my own code using an ARDUINO MEGA 2560.

Those familiar with Jeff Rowberg's DMP code will recognise what I have posted below. I have tried to simplify it as much as possible by removing the compiler if statements and a lot of the comments. It is essentially the same code there, with the addition of some integers to perform the Yaw/Pitch/ Roll calculations before printing in case that helped stop slowing things up and causing problems.

What I want it to do:

  1. Push the button , the program creates a new file of name "SET_XX", where XX increments from 01 upwards and data is saved from the accelerometer to that file.

  2. Press the button again (or in this case a separate button) that stops the stream of data to the file and closes it.

  3. Repeat

How I've tried to do this:

  1. Create a sub-routine, where I have put most of Jeff Rowberg's code. The accel/gyro values are collected and saved to the dataFile here. This was done to clear things up visually.
  2. Use the buttons to switch between two loops, the "Start button loop", where the program looks for the start button to be pressed, and the "Stop button loop" where the program records values and looks for the stop button to be pressed.

It's simple in concept, but something hasn't been working correctly and it's been giving me a nightmare for the last few weeks. The problem is almost certainly with the incorporation of the SD card writing and closing with the MPU6050.

What the main problems are:

  1. In the code shown below, there are FIFO buffer "overload" statements being thrown out from time to time. There also seems to be a link between the FIFO overload and the additional debug LED staying on a little longer every once in a while as described in the notes at the bottom. I have tried to solve this by reducing the rate in which the DMP produces information by modifying a value in the "MPU6050_6Axis_MotionApps20.h" as Jeff Rowberg himself instructs. This doesn't seem to have any effect, despite saving the library and restarting the Arduino IDE.
    This problem of clogging etc could be linked to the time taken for the SD card shield to write the data to the SD card and the interrupt fired by the accelerometer. I have put 200ms delays after the dataFile.print() to attempt to give the SD card more time to write but it hasn't changed anything and I don't like the idea of it. In my mind the interrupt will fire at the same time regardless and the delaying of the SD card write will only overload the FIFO buffer more. I don't have much experience with this, so please feel free to comment. NOTE: If I change the dataFile.print() to the original Serial.print() then everything seems to work fine without clogging/ overloading etc.

  2. The button is pressed to stop the flow of data to the SD card by breaking from the "Stop loop". The serial monitor displays all of the statements meant for debugging to say it is out of the loop and that the file has been correctly closed, but when the "ON" button is pressed a second time to open a second file it jumps to the loop "error creating file". When inspecting the file on the SD card, the first file has been created but with 0mb contents. This suggests that data was written to the file, but the file was not properly closed, and as a result the File Allocation Table was not created, despite the positive statements from the serial monitor debugging. I have found that the "SD" LED of the shield sticks on; this is the indication that if I press the OPEN button an error will occur- this could show that something is happening or sticking at the SD card side of things.

Disclaimer: I have programs that have the exact code for pressing the button and creating new files without the addition of the accelerometer. This works fine and saves many incrementing filenames with a set few lines inside.

  1. After a while, the entire system freezes. No data is saved to the particular file and I have to close and reopen SerialMonitor to begin again. Simply pressing the reset button on the Arduino does not un-freeze it.

There are a number of problems here, I understand. They are probably not cause by one single error in the code but if anyone has any pointers or thoughts at all I'd be happy to hear them. I'm sure there are some inefficiencies in my code because I'm a beginner, but I won't take offence at anyone pointing these out.

Thanks in advance,

CR

A couple of notes before reading the code below:

  1. It may seem nonsensical to define the pins 10-13 as inputs. The SPI pins of the Adafruit datalogging shield are hardwired to use the SPI pins of an Arduino UNO. For MEGA boards, the 'hard' SPI pins are instead 50-53. Adafruit encourage you to replace the normal SD library with another called SD-master, which uses software to re-assign these pins. This works very well. Thinking it could potentially be causing a conflict somewhere, I defined the pins 10-13 as inputs and have physically connected the 'hard' SPI pins, 50-53 of the MEGA to pins 10-13. It turns out that there is no difference in performance, so I will change it back, but I have kept the code this way so that any UNO users can still use their original libraries if they were kind enough to copy and check my code at all.

  2. This code includes two buttons to prevent ambiguity in on/off signals. Originally a debounce routine was implemented here and one button was used. This worked fine, but to reduce potential sources of error I took it out and replaced it with a second button.

  3. There is also an LED connected to DI/O pin 6 that acts as a bit of a debugger to see when things are being clogged up. Normally it should flicker at the same rate as values are recorded. Every so often something clogs in the Yaw/Pitch/Roll calculation and it momentarily stays on for longer than usual.
    In the end I'd like to have all four sets of values streaming to SD at once, but for to keep things simple for the time being, I have commented all but YPR out.

Cheers

#define SWITCH 3

#include <SPI.h> //For SD Card Datalogging
#include <SD.h>

#include "I2Cdev.h"

#include "MPU6050_6Axis_MotionApps20.h"

#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
    #include "Wire.h"
#endif

MPU6050 mpu(0x69); // <-- use for AD0 high

#define LED_PIN 13 // (Arduino is 13, Teensy is 11, Teensy++ is 6)
bool blinkState = false;

// MPU control/status vars
bool dmpReady = false;  // set true if DMP init was successful
uint8_t mpuIntStatus;   // holds actual interrupt status byte from MPU
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

// orientation/motion vars
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


//Debounce debouncer = Debounce( 40 , SWITCH ); 
int switch_state = 0;    //Variables to do with the switch and recording
int record_session = 0;

int session_num = 1; //Variables to do with saving a new file
File dataFile;
char NewFile[11];

//int euler1 = 0;
//int euler2 = 0;
//int euler3 = 0;

int yaw = 0;
int pitch = 0;
int roll = 0;

/*
int raccel_1 = 0;
int raccel_2 = 0;
int raccel_3 = 0;

int waccel_1 = 0;
int waccel_2 = 0;
int waccel_3 = 0;
*/


//INTERRUPT DETECTION ROUTINE


volatile bool mpuInterrupt = false;     // indicates whether MPU interrupt pin has gone high
void dmpDataReady() {
    mpuInterrupt = true;
}



//
//INITIAL SETUP


void setup() {
    // join I2C bus (I2Cdev library doesn't do this automatically)
    #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
        Wire.begin();
        TWBR = 24; // 400kHz I2C clock (200kHz if CPU is 8MHz)
    #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
        Fastwire::setup(400, true);
    #endif
    
    Serial.begin(115200);
    while (!Serial); 
  
  //INITIALISE THE SD CARD//  
         Serial.print("Initializing SD card...");
   pinMode(SS, OUTPUT);
   
pinMode(10,INPUT);
      pinMode(11, INPUT);
         pinMode(12, INPUT);
            pinMode(13, INPUT);
   
  if (!SD.begin()) {    //initialise SD card
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");  
    
//INITIALISE THE ACCELEROMETER//
    // initialize device
    Serial.println(F("Initializing I2C devices..."));
    mpu.initialize();

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

    // wait for ready
    Serial.println(F("\nSend any character to begin DMP programming and demo: "));
    while (Serial.available() && Serial.read()); // empty buffer
    while (!Serial.available());                 // wait for data
    while (Serial.available() && Serial.read()); // empty buffer again

    // load and configure the DMP
    Serial.println(F("Initializing DMP..."));
    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); // 1688 factory default for my test chip

    // make sure it worked (returns 0 if so)
    if (devStatus == 0) {
        // turn on the DMP, now that it's ready
        Serial.println(F("Enabling DMP..."));
        mpu.setDMPEnabled(true);

        // enable Arduino interrupt detection
        Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
        attachInterrupt(0, dmpDataReady, RISING);
        mpuIntStatus = mpu.getIntStatus();

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

        // get expected DMP packet size for later comparison
        packetSize = mpu.dmpGetFIFOPacketSize();
    } 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(")"));
    }

    // configure LED for output
    pinMode(LED_PIN, OUTPUT);
    pinMode(SWITCH,INPUT); // Defines the Switch pin as an input
    pinMode(4,INPUT);
    pinMode(6, OUTPUT);
}



// 
// MAIN PROGRAM LOOP                     

void loop()
{
    // if programming failed, don't try to do anything
    if (!dmpReady) return;
    
// Start Button Loop
 if (digitalRead(4) == HIGH)  
       {
       delay(1000);
       sprintf (NewFile, "SET_%02d.txt", session_num);
       dataFile = SD.open(NewFile, FILE_WRITE);
           
       if(dataFile)
             {
              Serial.println ("File Created, saving data"); 
              dataFile.println("Recording Values");
              dataFile.println("");
              Serial.println("recording");
             }
        session_num++;
           
        if (! dataFile)
          {
          Serial.println("error creating file");
         //    Wait forever since we cant write data
          while (1) ;
          }
        stop_button_loop();
       }

  
}

Continued... THANKS AGAIN FOLKS

void stop_button_loop()
{
    while(1)
      {
       accelerometer_data_capture(); // Loop the datacollect and save to SD
       
       if (digitalRead(3) == HIGH)  // If OFF button is pressed
          {
           delay(1000);
                   mpu.resetFIFO(); // Reset FIFO buffer to start clean again
           Serial.println("Stopped recording");
           
           delay(1000);
           Serial.println("Exiting Stop Loop");
           break;
          }
      }
     Serial.println("Out of Stop loop and closing file"); 
                dataFile.close(); //Close and 'save' file to FAT
      if (dataFile) //If dataFile happens to stil be open
          {
          Serial.println("Current File not closed");
            dataFile.close();
          }
       Serial.println("file closed");
}




void accelerometer_data_capture() //Simplified Jeff Rowberg DMP Code
{


    // wait for MPU interrupt or extra packet(s) available
    while (!mpuInterrupt && fifoCount < packetSize) {

    }

    // reset interrupt flag and get INT_STATUS byte
    mpuInterrupt = false;
    mpuIntStatus = mpu.getIntStatus();

    // get current FIFO count
    fifoCount = mpu.getFIFOCount();

    // check for overflow (this should never happen unless our code is too inefficient)
    if ((mpuIntStatus & 0x10) || fifoCount == 1024) 
      {
        // reset so we can continue cleanly
        mpu.resetFIFO();
        Serial.println(F("FIFO overflow!"));


      }
    
    else if (mpuIntStatus & 0x02) 
      {
        // wait for correct available data length, should be a VERY short wait
        while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();

        // read a packet from FIFO
        mpu.getFIFOBytes(fifoBuffer, packetSize);
        
        // track FIFO count here in case there is > 1 packet available
        // (this lets us immediately read more without waiting for an interrupt)
        fifoCount -= packetSize;

/*
            // display Euler angles in degrees
            mpu.dmpGetQuaternion(&q, fifoBuffer);
            mpu.dmpGetEuler(euler, &q);
            Serial.print("euler\t");
            Serial.print(euler[0] * 180/M_PI);
            Serial.print("\t");
            Serial.print(euler[1] * 180/M_PI);
            Serial.print("\t");
            Serial.println(euler[2] * 180/M_PI);

*/
            // display ypr
           digitalWrite(6, HIGH); // Turn on debug LED
            
            mpu.dmpGetQuaternion(&q, fifoBuffer);
            mpu.dmpGetGravity(&gravity, &q);
            mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
            
            yaw = (ypr[0] * 180/M_PI);
            pitch = (ypr[1] * 180/M_PI);
            roll = (ypr[2] * 180/M_PI);

           // Serial.println("Taking YPR");

            dataFile.print("ypr\t");
            dataFile.print(yaw);
            dataFile.print("\t");
            dataFile.print(pitch);
            dataFile.print("\t");
            dataFile.println(roll);
           digitalWrite(6, LOW); // Turn off debug LED


/*
            // display real acceleration, adjusted to remove gravity
            mpu.dmpGetQuaternion(&q, fifoBuffer);
            mpu.dmpGetAccel(&aa, fifoBuffer);
            mpu.dmpGetGravity(&gravity, &q);
            mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);
            Serial.print("areal\t");
            Serial.print(aaReal.x);
            Serial.print("\t");
            Serial.print(aaReal.y);
            Serial.print("\t");
            Serial.println(aaReal.z);


            // display initial world-frame acceleration, adjusted to remove gravity
            // and rotated based on known orientation from quaternion
            mpu.dmpGetQuaternion(&q, fifoBuffer);
            mpu.dmpGetAccel(&aa, fifoBuffer);
            mpu.dmpGetGravity(&gravity, &q);
            mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);
            mpu.dmpGetLinearAccelInWorld(&aaWorld, &aaReal, &q);
            Serial.print("aworld\t");
            Serial.print(aaWorld.x);
            Serial.print("\t");
            Serial.print(aaWorld.y);
            Serial.print("\t");
            Serial.println(aaWorld.z);
*/
        // blink LED to indicate activity
        blinkState = !blinkState;
        digitalWrite(LED_PIN, blinkState); // blink the SD LED while recording

      }  
  
 
  
}

Hi Crud9,
Were you able to resolve this issue? I am facing the same issue(FIFO overflow and then subsequent hang after 2-3 minutes) with GY-521 on Arduino UNO and I am stuck as to how to proceed?

-Pawan

Same here... struggling.

It seems that, from my experiments, this has something more to do with the serial buffer. If you print more than 55 characters in Serial.print (); using the MPU6050 and the SDCard, stack overflow errors occur. Limit the buffer to 55 using the snprintf(); function; instead of Serial.print(); directly. Or make the serial output divided by 55 if your serial output is more than 55 per line.