Wire.endTransmission freezes arduino

Hi,
For starters, I’m using:
-Seeeduino Mega
-Sparkfun MPU-6050 breakout boardhttps://www.sparkfun.com/products/11028
-Jeff Rowbergs library for the mpu-6050https://github.com/jrowberg/i2cdevlib/blob/master/Arduino/I2Cdev/I2Cdev.cpp

With the example code MPU6050_DMP6, the measurements are really good, but the duino periodically freezes. I tracked this down through the libraries and it invariably leads (through different ways) to a Wire.endTransmission command in the i2cdev library (line 295), in the call to I2Cdev::readBytes(). Here is the relevant code:

            int8_t count = 0;
            uint32_t t1 = millis();    
             Serial.println("ARDUINO > 100");
            // Arduino v1.0.1+, Wire library
            // Adds official support for repeated start condition, yay!

            // I2C/TWI subsystem uses internal buffer that breaks with large data requests
            // so if user requests more than BUFFER_LENGTH bytes, we have to do it in
            // smaller chunks instead of all at once
            for (uint8_t k = 0; k < length; k += min(length, BUFFER_LENGTH)) {
                Wire.beginTransmission(devAddr);
                Wire.write(regAddr);
                Wire.endTransmission();
                Wire.beginTransmission(devAddr);
                Wire.requestFrom(devAddr, (uint8_t)min(length - k, BUFFER_LENGTH));
        
                for (; Wire.available() && (timeout == 0 || millis() - t1 < timeout); count++) {
                    data[count] = Wire.read();
                    #ifdef I2CDEV_SERIAL_DEBUG
                        Serial.print(data[count], HEX);
                        if (count + 1 < length) Serial.print(" ");
                    #endif
                }
        	Serial.println("ending transmission");
                Wire.endTransmission();
                Serial.println("ended Transmission");
            }
        #endif

I found this: http://letsmakerobots.com/node/28941
Where a suggestion is that the hardware buffer is overflowing, but as far as I can tell Jeff’s code is handling for that. Coming from a mech background, I’m a bit stumped on how to solve this…

Anybody have this, or a similar problem (or better yet solution)?

 Wire.beginTransmission(devAddr);
 Wire.requestFrom(devAddr, (uint8_t)min(length - k, BUFFER_LENGTH));

You don't do a beginTransmission before a requestFrom. Fix that first and then get back to us.

http://www.gammon.com.au/i2c

Ok, I commented out the last Wire.beginTransmission and Wire.endTransmission, like so:

            // Arduino v1.0.1+, Wire library
            // Adds official support for repeated start condition, yay!

            // I2C/TWI subsystem uses internal buffer that breaks with large data requests
            // so if user requests more than BUFFER_LENGTH bytes, we have to do it in
            // smaller chunks instead of all at once
            for (uint8_t k = 0; k < length; k += min(length, BUFFER_LENGTH)) {
                Wire.beginTransmission(devAddr);
                Wire.write(regAddr); Serial.println("wrote");
                Wire.endTransmission(); Serial.println("ended");
//                Wire.beginTransmission(devAddr);                        //THIS OUT 
                Wire.requestFrom(devAddr, (uint8_t)min(length - k, BUFFER_LENGTH)); Serial.println("requested");
        
                for (; Wire.available() && (timeout == 0 || millis() - t1 < timeout); count++) {
                    data[count] = Wire.read();
                    #ifdef I2CDEV_SERIAL_DEBUG
                        Serial.print(data[count], HEX);
                        if (count + 1 < length) Serial.print(" ");
                    #endif
                }
        
//                Wire.endTransmission();                  //THIS OUT
            }

From what I got from your site, startTransmission is prepping to write, and requestFrom blocks it to read. Between the Wire.startTransmission and Wire.endTransmission there is no Wire.write, so as far as I can tell, it’s not doing anything at all and taking them out shouldn’t do anything.

Now however it freezes with the Wire.requestFrom() call.
(note, it will running for a while without problems before freezing, but it always freezes there).

                for (; Wire.available() && (timeout == 0 || millis() - t1 < timeout); count++) {

I’m not sure what this does. Wire.requestFrom returns the number of bytes it received. All this stuff about timeouts doesn’t change anything.

Ok, from what I can tell, that loop is to avoid an infinite loop.
By default timeout is set to 1000 ms. t1 is the time taken with millis() at the beginning of the method. count is initialized as 0 at the beginning of the method.

I tried this:

                uint8_t numBytes = Wire.requestFrom(devAddr, (uint8_t)min(length - k, BUFFER_LENGTH)); 
		for(int i=0;i<numBytes;i++)  data[count] = Wire.read();

instead of that for loop. That’s what you meant right?

However this way doesn’t work: the sensor doesn’t even initialize. Sorry, not too familiar with this… I’ll update if I get something working in the meantime.

                uint8_t numBytes = Wire.requestFrom(devAddr, (uint8_t)min(length - k, BUFFER_LENGTH));

I don't understand this. You want some data from the thing you are talking to. That thing should have some way of determining how much data it will return per request. You seem to be trying to get just enough not to overflow some buffer. Why?

From the comments in the library:

// I2C/TWI subsystem uses internal buffer that breaks with large data requests // so if user requests more than BUFFER_LENGTH bytes, we have to do it in // smaller chunks instead of all at once

Well, what is BUFFER_LENGTH? Pity the whole sketch isn’t there. The I2C buffer is 32 bytes. It’s hard to imagine getting data from an accelerometer needing more than 32 bytes in one hit.

 uint8_t numBytes = Wire.requestFrom(devAddr, (uint8_t)min(length - k, BUFFER_LENGTH)); 
 for(int i=0;i<numBytes;i++)  data[count] = Wire.read();

Assuming for a moment BUFFER_LENGTH is the I2C buffer length (32), why are you requesting 32 bytes? You should be requesting the number of bytes you are expecting. Then requestFrom returns when they are in the buffer, or a NAK occurs in advance of that.

 for(int i=0;i<numBytes;i++)  data[count] = Wire.read();

That puts all the incoming bytes into one byte. Not very useful.

Hi,
Yup, I’m a bit out of my depth here. I didn’t post the entire code because it would include sketch+2 libraries, but their all in the link. Here they are:

-sketch: https://github.com/jrowberg/i2cdevlib/blob/master/Arduino/MPU6050/Examples/MPU6050_DMP6/MPU6050_DMP6.ino

-mpu6050 library: i2cdevlib/Arduino/MPU6050 at master · jrowberg/i2cdevlib · GitHub

-i2cdev library (where the posted code is from): i2cdevlib/Arduino/I2Cdev at master · jrowberg/i2cdevlib · GitHub

Aha, I think that is what the

for (; Wire.available() && (timeout == 0 || millis() - t1 < timeout); count++)  {
data[count] = Wire.read();
}

is doing: Every time it does Wire.read(), Wire.available will be reduced by one, until it returns 0 (everything has been read), and so it exits the loop.

Does that make sense?

I don't know why it needs to be this complex. How many bytes are you expecting from the device?

http://www.gammon.com.au/i2c

There is a link there to another library that doesn't hang under certain circumstances. That might be a better one if the hangs are hardware-related.