Arduino outputs only first magnetometer readings from MPU9250 chip and waits.

Hi I am a newbie working on 9DOF sensor. I am able to read Accel and Gyro values. But the code reads only the first magnetometer reading and waits endlessly. Here is my code.

#include <Wire.h>
 
#define    MPU9250_ADDRESS            0x68
#define    MAG_ADDRESS                0x0C
 
#define    GYRO_FULL_SCALE_250_DPS    0x00  
#define    GYRO_FULL_SCALE_500_DPS    0x08
#define    GYRO_FULL_SCALE_1000_DPS   0x10
#define    GYRO_FULL_SCALE_2000_DPS   0x18
 
#define    ACC_FULL_SCALE_2_G        0x00  
#define    ACC_FULL_SCALE_4_G        0x08
#define    ACC_FULL_SCALE_8_G        0x10
#define    ACC_FULL_SCALE_16_G       0x18
 
 
 
// This function read Nbytes bytes from I2C device at address Address. 
// Put read bytes starting at register Register in the Data array. 
void I2Cread(uint8_t Address, uint8_t Register, uint8_t Nbytes, uint8_t* Data)
{
  // Set register address
  Wire.beginTransmission(Address);
  Wire.write(Register);
  Wire.endTransmission();
 
  // Read Nbytes
  Wire.requestFrom(Address, Nbytes); 
  uint8_t index=0;
  while (Wire.available())
    Data[index++]=Wire.read();
}
 
 
// Write a byte (Data) in device (Address) at register (Register)
void I2CwriteByte(uint8_t Address, uint8_t Register, uint8_t Data)
{
  // Set register address
  Wire.beginTransmission(Address);
  Wire.write(Register);
  Wire.write(Data);
  Wire.endTransmission();
}
 
 
// Initializations
void setup()
{
  // Arduino initializations
  Wire.begin();
  Serial.begin(115200);
 
  // Configure gyroscope range
  I2CwriteByte(MPU9250_ADDRESS,27,GYRO_FULL_SCALE_2000_DPS);
  // Configure accelerometers range
  I2CwriteByte(MPU9250_ADDRESS,28,ACC_FULL_SCALE_16_G);
  // Set by pass mode for the magnetometers
  I2CwriteByte(MPU9250_ADDRESS,0x37,0x02);
 
  // Request first magnetometer single measurement
  I2CwriteByte(MAG_ADDRESS,0x0A,0x01);
 
 
}
 
 
long int cpt=0;
// Main loop, read and display data
void loop()
{
 
  // _______________
  // ::: Counter :::
 
  // Display data counter
  Serial.print (cpt++,DEC);
  Serial.print ("\t");
 
 
 
  // ____________________________________
  // :::  accelerometer and gyroscope ::: 
 
  // Read accelerometer and gyroscope
  uint8_t Buf[14];
  I2Cread(MPU9250_ADDRESS,0x3B,14,Buf);
 
 
  // Create 16 bits values from 8 bits data
 
  // Accelerometer
  int16_t ax=-(Buf[0]<<8 | Buf[1]);
  int16_t ay=-(Buf[2]<<8 | Buf[3]);
  int16_t az=Buf[4]<<8 | Buf[5];
 
  // Gyroscope
  int16_t gx=-(Buf[8]<<8 | Buf[9]);
  int16_t gy=-(Buf[10]<<8 | Buf[11]);
  int16_t gz=Buf[12]<<8 | Buf[13];
 
    // Display values
 
  // Accelerometer
  Serial.print (ax,DEC); 
  Serial.print ("\t");
  Serial.print (ay,DEC);
  Serial.print ("\t");
  Serial.print (az,DEC);  
  Serial.print ("\t");
 
  // Gyroscope
  Serial.print (gx,DEC); 
  Serial.print ("\t");
  Serial.print (gy,DEC);
  Serial.print ("\t");
  Serial.print (gz,DEC);  
  Serial.print ("\t");
 
 
  // _____________________
  // :::  Magnetometer ::: 
 
 
  // Read register Status 1 and wait for the DRDY: Data Ready
 delay(100);
  uint8_t ST1;
  do
  {
    I2Cread(MAG_ADDRESS,0x02,1,&ST1);
  }
  while (!(ST1&0x01));
  
  // Read magnetometer data  
  uint8_t Mag[7];  
  I2Cread(MAG_ADDRESS,0x03,7,Mag);
 
 
  // Create 16 bits values from 8 bits data
 
  // Magnetometer
  int16_t mx=-(Mag[3]<<8 | Mag[2]);
  int16_t my=-(Mag[1]<<8 | Mag[0]);
  int16_t mz=-(Mag[5]<<8 | Mag[4]);
 
 Serial.print ("MAG ");
 
  // Magnetometer
  Serial.print (mx+200,DEC); 
  Serial.print ("\t");
  Serial.print (my-70,DEC);
  Serial.print ("\t");
  Serial.print (mz-700,DEC);  
  Serial.print ("\t");
 
 
 
  // End of line
  Serial.println("");
//  delay(100);    
}

connections

MPU 92/65 → Arduino
VCC 5V
GND GND
SDA A4
SCL A5

I have attached the output format too. A tiny Help on that magnetometer part would be great.

  uint8_t Mag[7]; 
  I2Cread(MAG_ADDRESS,0x03,7,Mag);

Just curious: if you expect 6 bytes, why read 7?

For now,it doesnt matter. the program hangs at the loop itself :( that lies before the part you mention

How do you know that? To see where the program stops, put print statements in strategic places, like ("about to read magnetometer") just before the 7-byte request to read 6 bytes.

Hi i started reading continuos values successfully it seems that i had to change the mode after referring ak8973 magnetometer datasheet...But i receive 8bit like values..Even though the code aims to read 16 bit ones.. Kind of like MSB part is missing..

Could you elaborate on your fix for this problem. I am having the same exact problem with my MPU9255 as you had with the same program.

Hello,

I’ve encountered the problem and seen that depending of the generation of the MPU9250 & coupled magnetometer, the continuous read mode could not work and you had to request a single measurement each time before trying to read the magnetometer value.

In the previous code, this means adding

 I2CwriteByte(MAG_ADDRESS,0x0A,0x01);

Before

  uint8_t ST1;
  do
  {      
    I2Cread(MAG_ADDRESS,0x02,1,&ST1);
  }
  while (!(ST1&0x01));

Works for me on my Waveshare 10DOF IMU Sensor http://www.waveshare.com/wiki/10_DOF_IMU_Sensor_(B)

Thanks essorrac, adding the line "I2CwriteByte(MAG_ADDRESS,0x0A,0x01);" where you described fixed my problem with MPU 92/65 hanging on magnetometer read.

Rinzler23:
For now,it doesnt matter. the program hangs at the loop itself :frowning: that lies before the part you mention

jremington:
How do you know that?
To see where the program stops, put print statements in strategic places, like (“about to read magnetometer”) just before the 7-byte request to read 6 bytes.

In the AK8963’s datasheet it states you have to read the “data status 2”-byte to end the measurement reading. This is the next in the register after the measurement data, and thus the 7 byte reading.

It’s a little late, but i hope you still find it interesting.

For those interested, there is also described how to read sensor adjustment values. This can be done with this code…

void readMagSensAdjustVal() {

  int16_t mxAdjustVal, myAdjustVal, mzAdjustVal;

  // Sets magnetometer in fuse ROM mode, to access sensitivity adjustment values
  I2CwriteByte(MAG_ADDRESS,0x0A,0x00);
  I2CwriteByte(MAG_ADDRESS,0x0A,0x0F);

  //Reads sensitivity adjustment values
  uint8_t Buf[3];
  I2Cread(MAG_ADDRESS,0x10,3,Buf);

  //Stores sensitivity adjustment values
  mxAdjustVal = Buf[0];
  myAdjustVal = Buf[1];
  mzAdjustVal = Buf[2];

  //Stores sensitivity adjustment values in global variables for use in rest of the program
  magSensAdjustX = mxAdjustVal;
  magSensAdjustY = myAdjustVal;
  magSensAdjustZ = mzAdjustVal;

  // Set magnetometer back to 16-bit continuous measurement
  I2CwriteByte(MAG_ADDRESS,0x0A,0x00);
  I2CwriteByte(MAG_ADDRESS,0x0A,0x02);
}

There is also described a self test procedure, I like to run this in the setup of the program. It uses an internal magnet as reference and the output has to be inside a set of values.
Here is the code for that…

PS: Be sure to run the adjustment value code befor this one, as it uses those values in the code.

int MagSelfTest() {

  int16_t mxr, myr, mzr;
  uint8_t ST1;
  int c = 0;
  int returnVal = 0;

  // Set magnetometer to power-down mode
  I2CwriteByte(MAG_ADDRESS,0x0A,0x00);
  // Power internal magnets for self test
  I2CwriteByte(MAG_ADDRESS,0x0C,0x40);
  // Set magnetometer to self test mode
  I2CwriteByte(MAG_ADDRESS,0x0A,0x08);

  do {
  I2Cread(MAG_ADDRESS,0x02,1,&ST1);
  c++;
  delay(10);
  if(c > 25){
    returnVal = 1;
    break;
   }
  }
  while (!(ST1&0x01));

  if(returnVal != 1) {
    
    uint8_t Buf[6];
    I2Cread(MAG_ADDRESS,0x03,6,Buf);
  
      // Magnetometer
    mxr = Buf[1]<<8 | Buf[0];
    myr = Buf[3]<<8 | Buf[2];
    mzr = Buf[5]<<8 | Buf[4];
  
    mxr = mxr*((((magSensAdjustX-128)*0.5)/128)+1);
    myr = myr*((((magSensAdjustY-128)*0.5)/128)+1);
    mzr = mzr*((((magSensAdjustZ-128)*0.5)/128)+1);

    
    if(mxr <= -50 || mxr >= 50) {
      returnVal = 3;
    }
    if(myr <= -50 || myr >= 50) {
      returnVal = 3;
    }
    if(mzr <= -800 || mzr >= -200) {
      returnVal = 3;
    }
    else {returnVal = 2;}
    
  }

   // Power down internal magnets for self test
  I2CwriteByte(MAG_ADDRESS,0x0C,0x00);
  // Set magnetometer to power down mode
  I2CwriteByte(MAG_ADDRESS,0x0A,0x00);
  // Request magnetometer 14-bit continuous measurement
  I2CwriteByte(MAG_ADDRESS,0x0A,0x02);


  return returnVal;
}

The code will return 1 if the test was not made, 2 if it passed the test, and 3 if it did not pass.

I hope this comes in handy

And I have yet to figure out how to turn the raw data into usable values… :confused: