MPU6050 accel reading overflow int16_t to float

I’m having some trouble to identify why sometimes I get strange values and overflow from MPU6050 sensor. I have an ATM328P micro reading values from a DROTEK 10 DOF IMU sensor, one of these values is the acceleration one, and it sometimes become ovf or behaves in a weird way. This is the code I’m using for it:

#include<Wire.h>
const int MPU=0x69;
int16_t ax,ay,az;
float acc[3] = {0, 0, 0};
float S = 0.00006103515625; // scale factor g/LSB 4/65536

void setup(){
  Wire.begin();
  Wire.beginTransmission(MPU);
  Wire.write(0x6B);  // PWR_MGMT_1 register
  Wire.write(0);     // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);
  Serial.begin(115200);
}
void loop(){
  Wire.beginTransmission(MPU);
  Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU,6,true);  // request a total of 14 registers
  az=Wire.read()<<8|Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)    
  ay=Wire.read()<<8|Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  ax=Wire.read()<<8|Wire.read();  // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)

 acc[0] = ax*S;
 acc[1] = ay*S;
 acc[2] = az*S;
}

Why would these last acc values from the array overflow or behave in a weird way (not expected values) sometimes, and why when it starts behaving weird it doesn’t recover anymore? I’ve looked for problems regarding int16_t to float conversion but haven’t found any clue that could help me.

Does a anyone have any idea?

I’m starting to think that it may be hardware thing, as suggested in this post (http://forum.arduino.cc/index.php?topic=303600.0).

You could add some checks to see if the I2C transmission failed.

The Wire.endTransmission() returns an error.
The Wire.requestFrom() returns the number of read bytes. At least test this return value. If your requested 6 bytes, check if the return value is 6.

https://www.arduino.cc/en/Reference/WireEndTransmission
https://www.arduino.cc/en/Reference/WireRequestFrom

Sometimes a delay is needed between I2C transmissions. If that sketch is really your sketch, add a delay of 10ms at the end of the loop().

Some devices simply don't work with the Wire library, which does not perform the RepeatedStart condition on the bus. There is an alternative library to get around that problem. It also has timeouts built in that will give you a better idea of what might be wrong. Give it a try.

The Wire library has a repeated start since a few years, but the MPU-6050 does not need a repeated start. It will work with the Wire library.

Peter_n:
You could add some checks to see if the I2C transmission failed.

The Wire.endTransmission() returns an error.
The Wire.requestFrom() returns the number of read bytes. At least test this return value. If your requested 6 bytes, check if the return value is 6.

Arduino - WireEndTransmission
Arduino - WireRequestFrom

Sometimes a delay is needed between I2C transmissions. If that sketch is really your sketch, add a delay of 10ms at the end of the loop().

That’s not really my sketch, it’s an example about how I get those variables. I have been checking Wire.endTransmission() and Wire.requestFrom() and they seem to be ok. But from that clue I’ve compared this code lines with another ones that I use to get pressure and temperature values from the 10 DOF Drotek IMU that also has a MS561101BA barometer and I’ve found a difference:

These are my actual functions to read acceleration:

void readAcc()
{
 Wire.beginTransmission(MPU);
 Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
 Wire.endTransmission(false); // HERE IS THE MAIN DIFFERENCE
 delay(10);
 Wire.requestFrom(MPU,6);  // request a total of 6 registers
 debugRes = Wire.available();
 if (debugRes >= 6) // Also I added this condition since last code I posted***********
 {
 ax=Wire.read()<<8|Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
 ay=Wire.read()<<8|Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
 az=Wire.read()<<8|Wire.read();  // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
 }
 
        // float rawAcc[3] = {0, 0, 0} declared as global
 rawAcc[0] = ax*S; 
 rawAcc[1] = ay*S;
 rawAcc[2] = az*S; // float S = 0.00006103515625 declared as global // scale factor g/LSB 4/65536
}

and to read altitude:

float readAlt()
{
 D1 = getVal(ADDRESS, 0x48); // Pressure raw
 D2 = getVal(ADDRESS, 0x58);// Temperature raw

 dT   = D2 - ((uint32_t)C[5] << 8);
 OFF  = ((int64_t)C[2] << 16) + ((dT * C[4]) >> 7);
 SENS = ((int32_t)C[1] << 15) + ((dT * C[3]) >> 8);

 TEMP = (int64_t)dT * (int64_t)C[6] / 8388608 + 2000;

 if(TEMP < 2000) // if temperature lower than 20 Celsius
 {
 int32_t T1    = 0;
 int64_t OFF1  = 0;
 int64_t SENS1 = 0;

 T1    = pow(dT, 2) / 2147483648;
 OFF1  = 5 * pow((TEMP - 2000), 2) / 2;
 SENS1 = 5 * pow((TEMP - 2000), 2) / 4;
 
 if(TEMP < -1500) // if temperature lower than -15 Celsius
 {
 OFF1  = OFF1 + 7 * pow((TEMP + 1500), 2);
 SENS1 = SENS1 + 11 * pow((TEMP + 1500), 2) / 2;
 }
 
 TEMP -= T1;
 OFF -= OFF1;
 SENS -= SENS1;
 }
 
 float Temperature = (float)TEMP / 100;

 P  = ((int64_t)D1 * SENS / 2097152 - OFF) / 32768;

 float Pressure = (float)P / 100;
 
 return calcAltitude(Pressure, Temperature);
}


long getVal(int address, byte code)
{
 unsigned long ret = 0;
 Wire.beginTransmission(address);
 Wire.write(code);
 Wire.endTransmission();
 delay(10);
 // start read sequence
 Wire.beginTransmission(address);
 Wire.write((byte) 0x00);
 Wire.endTransmission();
 Wire.beginTransmission(address);
 Wire.requestFrom(address, (int)3);
 if (Wire.available() >= 3)
 {
 ret = Wire.read() * (unsigned long)65536 + Wire.read() * (unsigned long)256 + Wire.read();
 }
 else {
 ret = -1;
 }
 Wire.endTransmission();
 return ret;
}

float calcAltitude(float press, float temp)
{
 return ((pow((sea_press / press), 1/5.257) - 1.0) * (temp + 273.15)) / 0.0065;
}

Looking at the function getVal(), which deals with the Wire library for the barometer, the Wire.endTransmission() statement doesn’t contain a false, which from arduino reference I’ve seen that

If false, endTransmission() sends a restart message after transmission. The bus will not be released, which prevents another master device from transmitting between messages. This allows one master device to send multiple transmissions while in control.

I’ve reproduced a similar code in an Arduino Leonardo printing results over serial, acc and altitude readings. With false in Wire.EndTransmission() I’ve found that once in a while the accel readings were -1, and then normal again. Then, without False inside Wire.EndTransmission this is not happening anymore. What I don’t understand is why that -1 made my readings unstable and yet ovf, cause -1*S would be -0.00006103515625, which I believe its not beyond minimun float value.

So I’m testing right now the same code above in my ATM328P but without the False inside Wire.endTransmission() for the MPU6050, and it seems to be working right. Anyway, I don’t want to claim victory yet (Is this said in English: “Don’t count my chickens before they hatch”?), cause in other tests sometimes it’s been working for hours till it’s gone crazy.

Comparing the two codes, do you find any other improvement that I could try in MPU6050? Or even anywhere else?

jremington:
Some devices simply don’t work with the Wire library, which does not perform the RepeatedStart condition on the bus. There is an alternative library to get around that problem. It also has timeouts built in that will give you a better idea of what might be wrong. Give it a try.

I hope the Wire.EndTransmission thing solves the problem, but if not I will give this lib a try before trying new hardware.

Thank’s a lot for your help!

P.S.: Forgot to say that before trying this code I tried FreeImu library (contains I2Cdev from Jeff Rowberg) which also gave crazy results after a while running, then I read in Arduino reference that Drotek 10 DOF isn’t compatible with it because of this:

http://www.drotek.fr/shop/en/62-imu-10dof-mpu6050-hmc5883-ms5611.html
This sensor board contains three sensors. A schematic is not provided. The interrupt (‘INT’) of the MPU-6050 is not made available. Therefor the FIFO and the Jeff Rowberg library can not be used.

Too many confusing questions :o

This is your 10DOF module : http://www.drotek.fr/shop/en/home/62-imu-10dof-mpu6050-hmc5883-ms5611.html

I will try to explain, and hopefully you can make some sense out of it.

The parameter 'false' is used for a restart condition, and the Master does not release the I2C bus. The parameter 'true' is the same as no parameter. A restart can be used with most sensors while the Master is busy with it. It doesn't hurt, but often not needed. Some sensors are very picky and they require a restart.

In your first post, the use of 'true' and 'false' is okay. It should not go wrong with that. I would remove all those 'true' and 'false', since the MPU-6050 does not need it.

The timing can be a problem. A little delay of 100us between the transmissions is needed sometimes. The delay in the example code of 10ms is 100 times too much. Perhaps the restart has consequences for the I2C timing for the MPU-6050. It would not surprise me.

The code that you show after "and to read altitude:" is very bad :stuck_out_tongue_closed_eyes: . The function "getVal()" has bad use of the Wire library. Please forget that code.

I don't know about the float causing a bad number. If you have a dry test (without sensor), I can try it on an Arduino Uno.

The interrupt that is used by the i2cdevlib 'dmp' example has not to do with your code. I you want to use the i2cdevlib 'dmp' code someday, you have to buy another module.

Peter_n: Too many confusing questions :o

Sorry :sweat_smile:

Peter_n: This is your 10DOF module : http://www.drotek.fr/shop/en/home/62-imu-10dof-mpu6050-hmc5883-ms5611.html

Yes, it is.

Peter_n: I will try to explain, and hopefully you can make some sense out of it.

The parameter 'false' is used for a restart condition, and the Master does not release the I2C bus. The parameter 'true' is the same as no parameter. A restart can be used with most sensors while the Master is busy with it. It doesn't hurt, but often not needed. Some sensors are very picky and they require a restart.

I now understand, first misunderstood reading reference and thought that, in someway, accelerometer and barometer were disturbing each other so that accelerometer readings were sometime abnormal.

Peter_n: In your first post, the use of 'true' and 'false' is okay. It should not go wrong with that. I would remove all those 'true' and 'false', since the MPU-6050 does not need it.

The timing can be a problem. A little delay of 100us between the transmissions is needed sometimes. The delay in the example code of 10ms is 100 times too much. Perhaps the restart has consequences for the I2C timing for the MPU-6050. It would not surprise me.

About that 10 ms delay, I tried making it smaller inside the getVal() function for barometer, but I got bad readings so I left it like that, haven't tried on MPU-6050, but I'll do.

Peter_n: The code that you show after "and to read altitude:" is very bad :stuck_out_tongue_closed_eyes: . The function "getVal()" has bad use of the Wire library. Please forget that code.

I got that code from this topic, I was previously using freeIMU library, also tried MS561101 library but I had some unstable readings once in a while, with this code altitude readings have been ok and stable all the time. What could I improve? Why is it bad use?

Peter_n: I don't know about the float causing a bad number. If you have a dry test (without sensor), I can try it on an Arduino Uno.

The interrupt that is used by the i2cdevlib 'dmp' example has not to do with your code. I you want to use the i2cdevlib 'dmp' code someday, you have to buy another module.

I don't have a dry test, just use the sensor for trying but I may try to reproduce it and tell you. Anyway, since I wrote the previous post and made those changes I've had the ATMega328P running and I think it has improved in stability. I have an lcd connected to it showing rawAcc[2] and az, I'm not able to log them but I watch once in a while. rawAcc[2], has gone overflow and then inf for a little time, and after 2 minutes more or less it has gone to normal values again, this wouldn't happen in previous tests. The problem is that this overflow or infinite may have affected other variables that actuate in further code, I have leds that would have to react to accelerations and it's not doing so, even if the acceleration readings are ok as I see at the LCD.

I'll try to figure out why ovf or inf would spread and post an example. Thank's a lot for your help!! :D

The normal way to set a register and request data is like this:

  // Write the register inside the chip
  Wire.beginTransmission(address);
  Wire.write(register);
  Wire.endTransmission();

  // Optional delay, delayMicroseconds(100) or delay(1)
  // This delay is not needed for chips with hardware I2C inside.

  // request data
  int n = Wire.requestFrom(address, amount);
  if (n == amount)        // test if the received number of bytes is the same as the requested amount.
  {
    Wire.readBytes( buffer, n);
  }

It is of course okay to use Wire.available() and read bytes one by one with Wire.read().

The Wire.requestFrom() should not be encapsulated by Wire.beginTransmission() and Wire.endTransmission(), because they do things:

  • Wire.beginTransmission() clears the TX buffer of the Wire library.
  • Wire.write() writes a byte to the buffer of the Wire library.
  • Wire.endTransmission() transmits the data via the I2C bus.