MPU6050 readings not correct

Hi!
I am using a GY-521 MPU6050 with arduino uno. I am using the sketch below (it was from Johnchi, but I slightly modified it)

// MPU-6050 Short Example Sketch
// By Arduino User JohnChi
// August 17, 2014
// Public Domain
#include<Wire.h>
const int MPU_addr=0x68;  // I2C address of the MPU-6050
int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ;
float FX ;
float FY ;
float FZ ;
void setup(){
  Wire.begin();
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x6B);  // PWR_MGMT_1 register
  Wire.write(0);     // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);
  Serial.begin(9600);
}
void loop(){
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU_addr,14,true);  // request a total of 14 registers
  AcX=Wire.read()<<8|Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)     
  AcY=Wire.read()<<8|Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  AcZ=Wire.read()<<8|Wire.read();  // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
  // Tmp=Wire.read()<<8|Wire.read();  // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L)
  GyX=Wire.read()<<8|Wire.read();  // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
  GyY=Wire.read()<<8|Wire.read();  // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
  GyZ=Wire.read()<<8|Wire.read();  // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)
  FX = AcX/16384;
  FY = AcY/16384;
  FZ = AcZ/16384;
  Serial.print("AcX = "); Serial.print(AcX/16384.0);
  Serial.print(" | AcY = "); Serial.print(AcY/16384.0);
  Serial.print(" | AcZ = "); Serial.print(AcZ/16384.0);
  // Serial.print(" | Tmp = "); Serial.print(Tmp/340.00+36.53);  //equation for temperature in degrees C from datasheet
  Serial.print(" | GyX = "); Serial.print(GyX/131.0);
  Serial.print(" | GyY = "); Serial.print(GyY/131.0);
  Serial.print(" | GyZ = "); Serial.println(GyZ/131.0);
  delay(10);
}

when I put the sensor on table horizontally and statically, in serial monitor, I can see the outputs like the attached picture

gyro has those values, even it is not moving or rotating or vibrating
(but when I rotate the sensor, gyro readings seem right)
z axis g force is only about 0.94, with any attitude (not moving)
x axis g force shows about 1.05 when x is vertical

I guess something is wrong with the code or the sensor?
How can I fix it?

Thank you!

const int MPU_addr=0x68;  // I2C address of the MPU-6050

Addresses are bytes. Get out of the habit of wasting memory.

  AcX=Wire.read()<<8|Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)    
  AcY=Wire.read()<<8|Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  AcZ=Wire.read()<<8|Wire.read();

Wire.read() returns a byte. What happens to a byte when you shift it 8 bits to the left?

Wire.read() returns a byte. What happens to a byte when you shift it 8 bits to the left?

Integer promotion when the combined value is typed as an int.

int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ;

Have you checked that gravity is working correctly at your location?

Wire.requestFrom(MPU_addr,14,true);  // request a total of 14 registers
  AcX=Wire.read()<<8|Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)     
  AcY=Wire.read()<<8|Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  AcZ=Wire.read()<<8|Wire.read();  // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
  // Tmp=Wire.read()<<8|Wire.read();  // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L)
  GyX=Wire.read()<<8|Wire.read();  // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
  GyY=Wire.read()<<8|Wire.read();  // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
  GyZ=Wire.read()<<8|Wire.read();  // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)

You request 14 bytes, and the date is sent from sequential registers. You can not comment out two readings in the middle and expect the GyX,GyY, and GyZ values to from the the correct registers. I also think the data is in a receive buffer, and the next time through the AcX,Acy, and AcZ will be wrong.

I deleted comment sigh in this line "// Tmp=Wire.read()<<8|Wire.read(); " and the corresponding part in serialprint, this does help! but the gyro readings are still not zero when it is at rest, x axis is around -3.0, y is around -1.2, z is around -1.2

And I am sorry that I actually didn't explain it correctly in the first post. I wanted to say : z axis g force is up to about 0.94, with any attitude (not moving) , and it drops to -1.14, when I flip it over.

I tested with a sensor app on ipad, z is -1 when it lies on table.

you may need to set the offsets for your MPU6050 to find them more stable. The attached calibration program generates these offsets.

These generated values are placed in the following memory locations in the mpu6050 and should stabalize and correct the values.:

#define MPU6050_RA_XG_OFFS_TC       0x00 //[7] PWR_MODE, [6:1] XG_OFFS_TC, [0] OTP_BNK_VLD
#define MPU6050_RA_YG_OFFS_TC       0x01 //[7] PWR_MODE, [6:1] YG_OFFS_TC, [0] OTP_BNK_VLD
#define MPU6050_RA_ZG_OFFS_TC       0x02 //[7] PWR_MODE, [6:1] ZG_OFFS_TC, [0] OTP_BNK_VLD
...
#define MPU6050_RA_XA_OFFS_H        0x06 //[15:0] XA_OFFS
#define MPU6050_RA_XA_OFFS_L_TC     0x07
#define MPU6050_RA_YA_OFFS_H        0x08 //[15:0] YA_OFFS
#define MPU6050_RA_YA_OFFS_L_TC     0x09
#define MPU6050_RA_ZA_OFFS_H        0x0A //[15:0] ZA_OFFS
#define MPU6050_RA_ZA_OFFS_L_TC     0x0B

setting the values:

// XG_OFFS_TC register

uint8_t MPU6050::getOTPBankValid() {
    I2Cdev::readBit(devAddr, MPU6050_RA_XG_OFFS_TC, MPU6050_TC_OTP_BNK_VLD_BIT, buffer);
    return buffer[0];
}
void MPU6050::setOTPBankValid(bool enabled) {
    I2Cdev::writeBit(devAddr, MPU6050_RA_XG_OFFS_TC, MPU6050_TC_OTP_BNK_VLD_BIT, enabled);
}
int8_t MPU6050::getXGyroOffsetTC() {
    I2Cdev::readBits(devAddr, MPU6050_RA_XG_OFFS_TC, MPU6050_TC_OFFSET_BIT, MPU6050_TC_OFFSET_LENGTH, buffer);
    return buffer[0];
}
void MPU6050::setXGyroOffsetTC(int8_t offset) {
    I2Cdev::writeBits(devAddr, MPU6050_RA_XG_OFFS_TC, MPU6050_TC_OFFSET_BIT, MPU6050_TC_OFFSET_LENGTH, offset);
}

// YG_OFFS_TC register

int8_t MPU6050::getYGyroOffsetTC() {
    I2Cdev::readBits(devAddr, MPU6050_RA_YG_OFFS_TC, MPU6050_TC_OFFSET_BIT, MPU6050_TC_OFFSET_LENGTH, buffer);
    return buffer[0];
}
void MPU6050::setYGyroOffsetTC(int8_t offset) {
    I2Cdev::writeBits(devAddr, MPU6050_RA_YG_OFFS_TC, MPU6050_TC_OFFSET_BIT, MPU6050_TC_OFFSET_LENGTH, offset);
}

// ZG_OFFS_TC register

int8_t MPU6050::getZGyroOffsetTC() {
    I2Cdev::readBits(devAddr, MPU6050_RA_ZG_OFFS_TC, MPU6050_TC_OFFSET_BIT, MPU6050_TC_OFFSET_LENGTH, buffer);
    return buffer[0];
}
void MPU6050::setZGyroOffsetTC(int8_t offset) {
    I2Cdev::writeBits(devAddr, MPU6050_RA_ZG_OFFS_TC, MPU6050_TC_OFFSET_BIT, MPU6050_TC_OFFSET_LENGTH, offset);
}

above code snagged from:
// I2Cdev library collection - MPU6050 I2C device class
// Based on InvenSense MPU-6050 register map document rev. 2.0, 5/19/2011 (RM-MPU-6000A-00)
// 8/24/2011 by Jeff Rowberg jeff@rowberg.net
// Updates should (hopefully) always be available at[ GitHub - jrowberg/i2cdevlib: I2C device library collection for AVR/Arduino or other C++-based MCUs](http://" GitHub - jrowberg/i2cdevlib: I2C device library collection for AVR/Arduino or other C++-based MCUs")

MPU6050_calibration.ino (7.64 KB)

A must read: How to calibrate accelerometers and magnetometers

Gyro drift depends on temperature and other factors, so the gyro offset described in reply #6 must be done frequently.

Integer promotion when the combined value is typed as an int.

The variable that the result is stored in has NOTHING to do with the types of variables on the right of the equal sign.

The variable that the result is stored in has NOTHING to do with the types of variables on the right of the equal sign.

Correct you are.

Wire.read() returns a byte. What happens to a byte when you shift it 8 bits to the left?

What answer did expect to your question. "Integer promotion" or "the data is shifted to oblivion"?

I thought you were implying that the bitshift << 8 lost the data. The shifted byte is promoted to an int.

The result was an int, so there was not problem with the op's code in that respect.

The shifted byte is promoted to an int.

Why?

Quote
The shifted byte is promoted to an int.
Why?

.

They be the rules, and the compiler knows the rules. So do you :slight_smile:

ISO International Standard ISO/IEC 14882:2014(E) – Programming Language C++

http://en.cppreference.com/w/cpp/language/operator_arithmetic#Conversions

Bitwise shift operators
The bitwise shift operator expressions have the form
lhs << rhs (1)
lhs >> rhs (2)

  1. left shift of lhs by rhs bits
  2. right shift of lhs by rhs bits
    For the built-in operators, lhs and rhs must both have integral or unscoped enumeration type. Integral promotions are performed on both operands.
    The return type is the type of the left operand after integral promotions.

The return type is the type of the left operand after integral promotions.

My reading of the above suggests that, if Wire.read() returns a byte, the result is a byte, further suggesting that the result of Wire.read()<<8 would always be zero.

That suspicion is supported by the result of the following little program, which prints "0":

byte shift8 (byte x) {return x<<8;}

void setup() {
  Serial.begin(9600);
  int y= shift8(1);
  Serial.println(y);
}
void loop(){
}

On the other hand, it appears that the AVR hardware library version of Wire.read() actually returns an int, so the discussion may be moot:

// must be called in:
// slave rx event callback
// or after requestFrom(address, numBytes)
int TwoWire::read(void)
{
  int value = -1;
  
  // get each successive byte on each call
  if(rxBufferIndex < rxBufferLength){
    value = rxBuffer[rxBufferIndex];
    ++rxBufferIndex;
  }

  return value;
}

I think your example does not represent the case under discussion.

The shift8 function return of the byte only returns the low byte the promoted int.

byte shift8 (byte x)
{
  Serial.println(x<<8);
  return x << 8;
}

void setup() {
  Serial.begin(9600);
  int y = shift8(1);
  Serial.println(y);
}
void loop() {
}

In the original posted code, the bit shifting is acting on the byte returned value, and the combination of two Wire.read() values into an int is like this

void setup() {
 Serial.begin(9600);
 byte x = 1;
 byte y = 1;
 int z = x<<8|y;
 Serial.print(z);
}
void loop() {}

The issue of type promotion during shifts has puzzled me for a while, so I have always used constructs like (unsigned int)Wire.read()<<8 and will continue to do so.

It's probably a good practice and the explicit casting is important when combining bytes into something larger than a 16 bit integer. I do not think it is required for the simple two byte combination into a 16 bit integer.

I modified my post -- Wire.read() returns an int, which is why it works.

Wire.read() returns an int, which is why it works.

Good find on the return value of Wire.read().

I still think that even if a byte were returned by Wire.read() the bitshift combination would work because of the arithmetic operator promotion with the bitshift <<8 on the byte.

byte return_x2(byte a)
{
  byte b = a * 2;
  return b;
}
void setup() {
  Serial.begin(9600);
  int c = return_x2(1) << 8 | return_x2(1);
  Serial.print(c);
}
void loop() {}

My example suggests otherwise.

My example suggests otherwise.

You are correct in that if the function returns a byte, the result is a byte. However, your function was returning the low byte (which was 0) of a two byte integer value. This integer value within the function was the result of integer promotion by the arithmetic operation within the function.

In my last example the bitshift <<8 operates on the returned byte, and that byte (after it has been returned by the function) is promoted to an int by the arithmetic operator.

Wire.read() returns an int, which is why it works.

This bitshifting <<8 of the first byte and and or_ing with the second received byte to create an int works with EEPROM.read() and Serial.read(). I don't think that they both return an int like Wire.read().

However, getting into the habit of the explict cast like you do, can not go wrong.

Unfortunately, this part of the discussion started with my reaction to @PaulS implication that the OPs code had a problem with the <<8.

I probably should have let it go by.