I2C data collect and use (from mpu6050)

Quick background, me: Ive been through a few arduino examples, had a lot of fun learning, adjusting and experimenting with them. This will be my first project. Previously only programming experience with some basic/intermediate Python.

Background project. Camera gimbal. Very early stage. Using mpu6050(GY-521 board) imu with Uno. This is my first experience with I2C. Read up on it, I think I fully understand it. The problem maybe with my understanding of the c++ data types. Ive run the examples from here and here. They both work.

Heres my code:

#include <Wire.h>

#define MPU6050_I2C_ADDRESS 0x68 //Default MPU-6050 address

#define MPU6050_TEMP_OUT_H 0x41 // R Temperature high address
#define MPU6050_TEMP_OUT_L 0x42 // R Temperature low address

void setup()
{
  Wire.begin(); //Initiate wire library
  Serial.begin(9600); //Initiate serial port 
 
  Wire.beginTransmission(MPU6050_I2C_ADDRESS); // Wake up MPU6050
  Wire.write(0); 
  Wire.endTransmission();
}

void loop()
{
  Wire.beginTransmission(MPU6050_I2C_ADDRESS);
  Wire.write(MPU6050_TEMP_OUT_H);  // Set address at Temperature high
  Wire.endTransmission();
  
  int datasize = 2;
  
  Wire.requestFrom(MPU6050_I2C_ADDRESS, datasize); // Request temperature high and low
  
  uint8_t tempdata[2]; // Array for temperature data
  int tdata = 0;
  
  if(datasize <= Wire.available()){
    tdata = Wire.read();
    tdata = tdata << 8;
    tdata |= Wire.read();
    Serial.println(tdata);
  }
  
  Serial.println(tdata);
  
    
  delay(5000);
  
}

This way doesnt work at all, I get 2 prints of “0”.

Attempt 2. Trying a different way of storing and combinding

#include <Wire.h>

#define MPU6050_I2C_ADDRESS 0x68 //Default MPU-6050 address

#define MPU6050_TEMP_OUT_H 0x41 // R Temperature high address
#define MPU6050_TEMP_OUT_L 0x42 // R Temperature low address

void setup()
{
  Wire.begin(); //Initiate wire library
  Serial.begin(9600); //Initiate serial port 
 
  Wire.beginTransmission(MPU6050_I2C_ADDRESS); // Wake up MPU6050
  Wire.write(0); 
  Wire.endTransmission();
}

void loop()
{
  Wire.beginTransmission(MPU6050_I2C_ADDRESS);
  Wire.write(MPU6050_TEMP_OUT_H);  // Set address at Temperature high
  Wire.endTransmission();
  
  int datasize = 2;
  
  Wire.requestFrom(MPU6050_I2C_ADDRESS, datasize); // Request temperature high and low
  
  uint8_t tempdata[2]; // Array for temperature data
   
  int i = 0;
  while(Wire.available())
  {  
    tempdata[i++] = Wire.read();
    Serial.println(tempdata[i]); // Print value of each
  }
  
  uint16_t combinddata = (tempdata[0]*256)+tempdata[1]; // combind the high and low byte
  Serial.println(combinddata);
  
  Serial.println(tempdata[0]); // test - print again
  Serial.println(tempdata[1]);
    
  delay(5000);
  
}

This way gets prints 2 different values from the while loop that seem to be working. But when I try to join the high and low bytes and print, the value is 0. I try print the bytes separately again and now the both 0.

Ive re-read through the basic example, googled and read about c++ data types. Im kinda stumped what Im doing wrong ?

Quick question, slightly off topic.Am I wasting my time trying to learn the basics and write all my own code or should I just learn to use available libraries(like from here) ?

Thanks for help and advice.

  uint16_t combinddata = (tempdata[0]*256)+tempdata[1]; // combind the high and low byte

What happens to your byte when you multiple it by 256? What happens if you declare tempdata to be int or unsigned int, instead of uint8_t?

They way I understand it. An int uses 2 bytes to contain the value. I2C only sends 1 byte at a time. So you have to join the high and low bytes.

(tempdata[0]*256)+tempdata[1]

tempdata[0] contains the high byte, multiplying by 256 basicly moves it up 1 byte, then add the low byte from tempdata[1]

Ive tried using int, and byte for the array, same results.

But,

while(Wire.available())
  {  
    tempdata[i++] = Wire.read();
    Serial.println(tempdata[i]); // Print value of each
  }

this loop gets the data, that serial.print works and prints 2 different values. After the loop ends and I check the values again. They are both 0. Im not sure why.

tempdata[0] contains the high byte, multiplying by 256 basicly moves it up 1 byte

Yes, but what happens to the 7th bit (of the 8) when you multiply by 2? What happens to the 6th bit when you multiply by 4? What happens to the 8 bits when you multiply by 256?

You need to use a data type that is large enough for all the manipulations you want to perform on the value.

Ive tried using int, and byte for the array, same results.

OK. So, then, you really need to print out the individual bytes. If they are both 0, then you are not reading data correctly, and no amount of diddling with what you read will make magic happen.

You are probably not actually acheiving any communication at all.

Getting a reading of zero might mean the device is sending you a zero. It more probably means that the device is not actually talking to you at all.

Suggestions:

  1. try the "i2c_scanner" sketch, and see if you get a response from the device.

  2. try reading some other "register" from the device, for example, the "who am I" code, and check that what you get matches what the device datasheet says.

  3. You have to set a couple of control bytes on this device, to put it into active mode. You didn't do that.

After a lot of confusion. Ive done it.

I think it was just a few little things I misunderstood how to use in the language. Never knew how easy I had it in python :wink:
Mainly how I used and retrieved data from arrays. Was causing confusion, getting 0 when I expected data, and getting fixed numbers when I expected variables.
Also I was so focused on the sleep mode and finding the expected bit to be 1(On) and didnt. I thought I was getting incorrect data. It should be on by default according to data sheet, but its actually off. In the example here I found a comment about it being 0 too.

Anyway, I rewrote the code a little neater, tried to comment everything. While googling about this I found others having a hard time with this. So here is a simple and easy to understand(I hope) way to retrieve the temperature on the MPU6050:

#include <Wire.h>

#define MPU6050_I2C_ADDRESS 0x68   // Default MPU-6050 address
#define MPU6050_PWR_MGMT_1  0x6B   // Bit 6 for sleep mode
#define MPU6050_WHO_AM_I 0x75      // R Who am I address
#define MPU6050_TEMP_OUT_H 0x41    // R Temperature high address

void setup()
{
  Wire.begin();        // Initiate wire library
  Serial.begin(9600);  // Initiate serial port 
  WhoAmI();            // Verifies identity of device
  Awaken();            // Check for sleep mode, awaken
}

void loop()
{
  PrintTemperature();  // Print temperature
  delay(2000);         // delay
}

void Awaken(){
  uint8_t awakeByte;                                 // Data will go here
  MPU6050_Read(MPU6050_PWR_MGMT_1, &awakeByte);      // Get data
  boolean sleepModeOn = bitRead(awakeByte,6);        // Check if sleep mode on
  while(sleepModeOn){                                // If on...
    Serial.println(F("Sleep mode is 'On'"));         // Report its On
    delay(500);                                      // delay
    bitClear(awakeByte,6);                           // Clear bit of sleep
    MPU6050_Write(MPU6050_PWR_MGMT_1, &awakeByte);   // Send data back
    Serial.println(F("Setting to 'Off'"));           // Report data sending
    delay(500);                                      // delay
    MPU6050_Read(MPU6050_PWR_MGMT_1, &awakeByte);    // Get data again
    sleepModeOn = bitRead(awakeByte,6);              // If sleep mode off, exit loop
  }                                                  //
  delay(500);                                        // delay
  Serial.println(F("Sleep mode is 'Off'"));          // Report sleep mode Off
}

void WhoAmI(){
  uint8_t waiByte;                                    // Data will go here
  MPU6050_Read(MPU6050_WHO_AM_I, &waiByte);           // Get data
  Serial.print(F("Device WhoAmI reports as: 0x"));    // 
  Serial.println(waiByte,HEX);                        // Report WhoAmI data
  delay(500);                                         // delay
}

void PrintTemperature(){
  typedef union tempBytes_union{                           // Union for data
    struct                                                 //
    {                                                      //
      uint8_t temp_h;                                      // Temperature high byte
      uint8_t temp_l;                                      // Temperature low byte
    } reg;                                                 //
    struct                                                 //
    {                                                      //
      int16_t temp;                                        // Combind temperature value
    } value;                                               //
  };                                                       //
  tempBytes_union tempBytes;                               //
  MPU6050_Read(MPU6050_TEMP_OUT_H,(uint8_t *)&tempBytes);  // Get data
  uint8_t swap;                                            //
  #define SWAP(x,y) swap = x; x = y; y = swap              // Used for swapping high and low bytes
  SWAP(tempBytes.reg.temp_h,tempBytes.reg.temp_l);         // Swaps high and low bytes. The union would join them the wrong way around otherwise.
  double temp;                                             //
  temp =((double)tempBytes.value.temp + 12412.0) / 340.0;  // 340 per degrees Celsius, -512 at 35 degrees. At 0 degrees: -512 - (340 * 35) = -12412
  Serial.print(F("Temperature: "));                        //
  Serial.print(temp,3);                                    // Prints temperature    
  Serial.println(F(" degrees Celsius"));                   //
}  

void MPU6050_Read(int address,uint8_t *data){            // Read from MPU6050. Needs register address, data array
  int size = sizeof(*data);                              //
  Wire.beginTransmission(MPU6050_I2C_ADDRESS);           // Begin talking to MPU6050
  Wire.write(address);                                   // Set register address
  Wire.endTransmission(false);                           // Hold the I2C-bus
  Wire.requestFrom(MPU6050_I2C_ADDRESS, size, true);     // Request bytes, release I2C-bus after data read
  int i = 0;                                             //
  while(Wire.available()){                               //
    data[i++]=Wire.read();                               // Add data to array
  }
}

void MPU6050_Write(int address,uint8_t *dbytes){  // Write to MPU6050. Needs register address, data array
  int size = sizeof(*dbytes);                     // Set size of data
  Wire.beginTransmission(MPU6050_I2C_ADDRESS);    // Begin talking to MPU6050
  Wire.write(address);                            // Set register address
  Wire.write(dbytes,size);                        // Send data, size of data
  Wire.endTransmission();                         // Release I2C-bus
}

Heres the serial print out as I touch the chip, then release:

Device WhoAmI reports as: 0x68
Sleep mode is 'Off'
Temperature: 24.579 degrees Celsius
Temperature: 24.579 degrees Celsius
Temperature: 27.591 degrees Celsius
Temperature: 28.344 degrees Celsius
Temperature: 29.097 degrees Celsius
Temperature: 29.097 degrees Celsius
Temperature: 29.850 degrees Celsius
Temperature: 29.850 degrees Celsius
Temperature: 28.344 degrees Celsius
Temperature: 28.344 degrees Celsius
Temperature: 27.591 degrees Celsius
Temperature: 27.591 degrees Celsius

It works, but if you have and ideas to improve the code, big or small, I would like here them.

Thank you