Extracting a magnetic heading from GY-86

I have been having trouble with extracting a magnetic heading from my recently bought GY-86
Here is a link to the module: http://dx.com/p/148737 It has the chips: MPU6050 + HMC5883L + MS5611 on board.
I am able to use the “MPU6050” (GitHub - jrowberg/i2cdevlib: I2C device library collection for AVR/Arduino or other C++-based MCUs) library example but not the “HMC5883L” example.

When I use the HMC5883L example I always get the “HMC5883L connection failed”.
I suspect that this is some kind of an address problem, but I don’t know.

Does anyone here have some answers or hints for me?

HMC5883L_raw1.ino (3.3 KB)

It has been a while, when I studied the rowberg stuff. IIRC the magnetic sensor is on a seperate I2C bus, only talking to the MPU6050.

guttih: I suspect that this is some kind of an address problem, but I don’t know.

Use a I2CScanner to find the adress of the HMC5883L (and the MS5611). Link: http://playground.arduino.cc/Main/I2cScanner

According to schematic of GY-86 (http://www.aliexpress.com/store/product/Free-shipping-GY-86-10DOF-modules-MWC-flight-control-sensor-module-With-chips-MPU6050-HMC5883L-MS5611/915786_1044842262.html) magnetometer connected to the auxiliary master I2C bus of MPU5060. But MPU5060 has ability to connect auxiliary I2C bus directly to primary I2C (slave mode) bus via special bypass mux (see section 7.11 of MPU5060 product specification).

There are three steps required to activate the bypass mode of MPU 5060:

  1. set I2C Master enable bit (I2C_MST_EN, bit 5) in user control register (USER_CTRL , 0x6A) to 0 (on MPU5060 power up it is already equeal zero).
  2. set I2C Bypass enable bit (I2C_BYPASS_EN,bit 2) in INT Pin / Bypass Enable Configuration register (INT_PIN_CFG,0x37) to 1.
  3. Turn off sleep mode by reseting SLEEP bit (bit 6) of the power management register #1 (PWR_MGMT_1, 0x6B).

In short, do the folloving:

MPU6050_write_reg (0x6A, 0);
MPU6050_write_reg (0x37, 2);
MPU6050_write_reg (0x6B, 0);

After that you’ll be able to communicate to magnetometer by 0x1e address via the same I2C interface that MPU5060 connected.

Note:
MPU5060 can read magnetometer data by itself, (there are 5 sets of registers to control secondary I2C devices which is connected to auxiliary I2C bus (see sections 4.9-4.17 in MPU5060 Register map and description). But I haven’t manage yet to configure 5060 to read magnetometer by itself :~
In additional the HMC5883L required to be initialized to work in cycle mode (after power up it works in single measurement mode). MPU5060 can (probably) perform this initialization… but i’m not sure …
So the bypass mode is required (probably) at least for initialization of HMC5883L.

BTW: Barometer MS5611 connected to the external GY-86 (slave MPU5060) I2C bus and You can access it by 0x77 address without any manipulation with auxiliary I2C bus of MPU5060.

After spending the whole afternoon tempting to figure out how to connect the magnetometer to uc via the MPU6050, it finally worked and gave me readings of the magnetometer. :smiley: All credit to sly_tom_cat, for his suggestion is the most important thing in the whole procedure. Having been through all the tiring and frustration, :sweat_smile: despair emotions today, I can’t wait to share this to all, who may need it, but without having a satisfying answer up to this moment. Hope you’re gonna make it by the time you finish these steps. :wink:

  1. First, let import the 2 helpful libraries written by Jeff Rowberg from these sources to your library folder ( Simply save them by a code editor, and copy the whole folders containing those files into your Sketchbook location. Open the IDE > Files > Preferences, here you may find the link to that location)

MPU6050 library:

HMC5883L library:

  1. Open the source code MPU6050.cpp, you may find the 3 functions:
setI2CMasterModeEnabled(false);
setI2CBypassEnabled(true) ;
setSleepEnabled(false);

Those are to enable the MPU6050 I2c bypass mode. I list them in the same order as those 3 steps Sly_tom_cat mentioned above.

There are three steps required to activate the bypass mode of MPU 5060:

  1. set I2C Master enable bit (I2C_MST_EN, bit 5) in user control register (USER_CTRL , 0x6A) to 0 (on MPU5060 power up it is already equeal zero).
  2. set I2C Bypass enable bit (I2C_BYPASS_EN,bit 2) in INT Pin / Bypass Enable Configuration register (INT_PIN_CFG,0x37) to 1.
  3. Turn off sleep mode by reseting SLEEP bit (bit 6) of the power management register #1 (PWR_MGMT_1, 0x6B).

I included the arguments as “true” or “false”. Those are the same as “setting” or " clearing" a bit respectively. Briefly saying, those 3 lines work as the key to unlock the door in the MPU, which allows your micro controller to access the magnetometer. After calling those functions in your code, you have all the freedom to access the HMC5883 as I told. That means, any function from the HMC5883L library you just downloaded would probably work from now on!

  1. You don’t have to wait much longer, just a few small steps. :wink: After working so hard on this, I lost all of patience to write the testing code myself. Simply use the available examples provided within those 2 libraries. Let open the IDE: Files> Examples> MPU6050_raw and type all the above functions inside void setup(){ }.
    Note: they must be called after initiating I2C bus, below Wire.begin();
void setup() {
   
    Wire.begin();
    accelgyro.setI2CMasterModeEnabled(false);
    accelgyro.setI2CBypassEnabled(true) ;
    accelgyro.setSleepEnabled(false);

    Serial.begin(38400);

Then open Files>Examples>HMC5883_raw and copy some lines of code to your currently working MPU6050_raw sketch to acquire and display the mag readings. Combine them all and the final sketch’s gonna look like this:

#include "Wire.h"
#include "I2Cdev.h"
#include "MPU6050.h"
#include "HMC5883L.h"


MPU6050 accelgyro;
HMC5883L mag;

int16_t ax, ay, az;
int16_t gx, gy, gz;
int16_t mx, my, mz;

#define LED_PIN 13
bool blinkState = false;

void setup() {
   
    Wire.begin();
    accelgyro.setI2CMasterModeEnabled(false);
    accelgyro.setI2CBypassEnabled(true) ;
    accelgyro.setSleepEnabled(false);

    Serial.begin(38400);

    // initialize device
    Serial.println("Initializing I2C devices...");
    accelgyro.initialize();
    mag.initialize();
    Serial.println(mag.testConnection() ? "HMC5883L connection successful" : "HMC5883L connection failed");

    // verify connection
    Serial.println("Testing device connections...");
    Serial.println(accelgyro.testConnection() ? "MPU6050 connection successful" : "MPU6050 connection failed");

    // configure Arduino LED for
    pinMode(LED_PIN, OUTPUT);
}

void loop() {
    
    accelgyro.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
    mag.getHeading(&mx, &my, &mz);

    // display tab-separated accel/gyro x/y/z values
    Serial.print("a/g:\t");
    Serial.print(ax); Serial.print("\t");
    Serial.print(ay); Serial.print("\t");
    Serial.print(az); Serial.print("\t");
    Serial.print(gx); Serial.print("\t");
    Serial.print(gy); Serial.print("\t");
    Serial.print(gz);Serial.print("\t");
    
    Serial.print("mag:\t");
    Serial.print(mx); Serial.print("\t");
    Serial.print(my); Serial.print("\t");
    Serial.print(mz); Serial.print("\t");
    
// To calculate heading in degrees. 0 degree indicates North
    float heading = atan2(my, mx);
    if(heading < 0)
      heading += 2 * M_PI;
    Serial.print("heading:\t");
    Serial.println(heading * 180/M_PI);

    // blink LED to indicate activity
    blinkState = !blinkState;
    digitalWrite(LED_PIN, blinkState);
}

now, let compile, then upload and open your serial monitor to get your mag readings :smiley:

I use your code? but i have zero in mx, my, mz What do you think, what's the problem?