HMC5883L - how to establish goog heading data

Hello colleagues!

I am trying to setup an arduino with a HMC5883L regarding to your website example:

https://www.sparkfun.com/products/10530?

I used a modified sketch for the HMC5883L but the only differenc to your sketch is that I also calculate the heading. This code is from Arduino Cookbook :slight_smile:

But I found out, that I will get different kind of values, if I hold the HMC5883L a little bit down or tilt it to a side.

What I need is an device or a sample Code, that gives me the security, to get the correct heading data independent to the position of the compass.

I only need to now the actual heading, but i must works in all positions, like the compass in an aeroplane.

What can I do? I dont know :frowning:

Do you now a good tutorial for my requirements.

BR

Dieter

/*
 Uses HMC5883L to get earths magnetic field in x,y and z axis 
 Displays direction as angle between 0 and 359 degrees
*/

#include <Wire.h> //I2C Arduino Library

const int hmc5883Address = 0x1E; //0011110b, I2C 7bit address of HMC5883
const byte hmc5883ModeRegister      = 0x02;
const byte hmcContinuousMode        = 0x00;
const byte hmcDataOutputXMSBAddress = 0x03;


void setup(){
  //Initialize Serial and I2C communications
  Serial.begin(9600);
  Wire.begin();
  
  //Put the HMC5883 IC into the correct operating mode
  Wire.beginTransmission(hmc5883Address); //open communication with HMC5883
  Wire.write(hmc5883ModeRegister); //select mode register
  Wire.write(hmcContinuousMode);   //continuous measurement mode
  Wire.endTransmission();
}

void loop(){
  
  int x,y,z; //triple axis data

  //Tell the HMC5883 where to begin reading data
  Wire.beginTransmission(hmc5883Address);
  Wire.write(hmcDataOutputXMSBAddress); //select register 3, X MSB register
  Wire.endTransmission();
  
 
 //Read data from each axis, 2 registers per axis
  Wire.requestFrom(hmc5883Address, 6);
  if(6<=Wire.available()){
    x = Wire.read()<<8; //X msb
    x |= Wire.read(); //X lsb
    z = Wire.read()<<8; //Z msb
    z |= Wire.read(); //Z lsb
    y = Wire.read()<<8; //Y msb
    y |= Wire.read(); //Y lsb
  }
  
  //Print out values of each axis
  Serial.print("x: ");
  Serial.print(x);
  Serial.print("  y: ");
  Serial.print(y);
  Serial.print("  z: ");
  Serial.print(z);
  
  int angle = atan2(-y , x) / M_PI * 180; // angle is atan(-y/x)
  if(angle < 0)
     angle = angle  + 360; // angle from 0 to 359 instead of plus/minus 180
  Serial.print(" Direction = ");
  Serial.println(angle);
  
  delay(250);
}

First let me say that people who want a tilt-compensated compass usually but one. They have a 3 accelerometer built in that can be used to measure the tilt of the compass and then use the vertical component of the magnetic field measured to produce a proper direction. Now I think if you can understand how those work, you can do something like that with your compass.

What you need to do is calibrate your compass on a level platform (table) and find out the relative horizontal (Y, X) and vertical (Z) magnetic field components for your location on the Earth. Then you can measure the tilt of your compass by comparing those components when the compass is in use. Then, just like a “normal” tilt-compensated compass, you can produce a good directional reading. The drawback to this is that it will only work over a limited distance from wherever it’s calibrated. How limited might that distance be ? Honestly I can guess a radius of perhaps 100 miles, maybe more. You can find out for yourself by plugging in your “home” location here …

http://www.ngdc.noaa.gov/geomagmodels/IGRFWMM.jsp

… and finding the inclination of the field. Plug in other away-from-home locations and when the inclination differs by more than … I’ll guess 2 deg … then that’s a good conservative limit.