HMC5883L Digital Compass Help

Hello I found this great code online for the HMC5883L Digital compass. Basically I am just wanting to write 4 characters to the serial port when the compass faces in any of the desired directions.

Something like this:

CompasCharactersToSend by Michael Knight, on Flickr

I made a slight change to the Arduino code so I think I am kind of the right track with what I need but could use a little help from someone a lot more code savvy :wink:

// Reference the I2C Library
#include <Wire.h>
// Reference the HMC5883L Compass Library
#include <HMC5883L.h>

// Store our compass as a variable.
HMC5883L compass;
// Record any errors that may occur in the compass.
int error = 0;

//rounding angle
int RoundDegreeInt;

int PreviousDegree = 0;

// Out setup routine, here we will configure the microcontroller and compass.
void setup()
{
  // Initialize the serial port.
  Serial.begin(9600);

  Wire.begin(); // Start the I2C interface.

  compass = HMC5883L(); // Construct a new HMC5883 compass.
  
  error = compass.SetScale(1.3); // Set the scale of the compass.
  if(error != 0) // If there is an error, print it out.
    Serial.println(compass.GetErrorText(error));

  error = compass.SetMeasurementMode(Measurement_Continuous); // Set the measurement mode to Continuous
  if(error != 0) // If there is an error, print it out.
    Serial.println(compass.GetErrorText(error));
}

// Our main program loop.
void loop()
{
  // Retrive the raw values from the compass (not scaled).
  MagnetometerRaw raw = compass.ReadRawAxis();
  // Retrived the scaled values from the compass (scaled to the configured scale).
  MagnetometerScaled scaled = compass.ReadScaledAxis();

  // Values are accessed like so:
  int MilliGauss_OnThe_XAxis = scaled.XAxis;// (or YAxis, or ZAxis)

  // Calculate heading when the magnetometer is level, then correct for signs of axis.
  float heading = atan2(scaled.YAxis, scaled.XAxis);

  // Once you have your heading, you must then add your 'Declination Angle', which is the 'Error' of the magnetic field in your location.
  // Find yours here: http://www.magnetic-declination.com/
  // Mine is: 2� 37' W, which is 2.617 Degrees, or (which we need) 0.0456752665 radians, I will use 0.0457
  // If you cannot find your Declination, comment out these two lines, your compass will be slightly off.
  float declinationAngle = 0.009 ;
  heading += declinationAngle;

  // Correct for when signs are reversed.
  if(heading < 0)
    heading += 2*PI;
  
  // Check for wrap due to addition of declination.
  if(heading > 2*PI)
    heading -= 2*PI;
  
  // Convert radians to degrees for readability.
  float headingDegrees = heading * 180/M_PI;

  //correcting the angle issue
  if (headingDegrees >= 1 && headingDegrees < 240) 
  {
    headingDegrees = map(headingDegrees,0,239,0,179);
    Serial.println("N"); //My Edit
  }
  else if (headingDegrees >= 240)
  {
    headingDegrees =  map(headingDegrees,240,360,180,360);
    Serial.println("O"); //My Edit
  }

  //rounding the angle
  RoundDegreeInt =round(headingDegrees);

  //smoothing value
  if( RoundDegreeInt < (PreviousDegree + 3) && RoundDegreeInt > (PreviousDegree - 3) ) {
    RoundDegreeInt = PreviousDegree;
  }

  Output(RoundDegreeInt);

  PreviousDegree = RoundDegreeInt;

  // Normally we would delay the application by 66ms to allow the loop
  // to run at 15Hz (default bandwidth for the HMC5883L).
  // However since we have a long serial out (104ms at 9600) we will let
  // it run at its natural speed.
  // delay(66);
}

// Output the data down the serial port.
void Output(int RoundDegreeInt)
{
   //Serial.println();
   Serial.println(RoundDegreeInt);
  
   delay(150);
}

I'm also a little fuzzy on the part in the code about the "Magnetic Declination" for my locations which I found out is as follows:

Winnipeg Manitoba
Latitude: 49° 53' 3.8" N
Longitude: 97° 8' 49.3" W
Magnetic declination: +3° 7'
Declination is POSITIVE (EAST)
Inclination: 74° 50'
Magnetic field strength: 57079.8 nT

Magnetic declination

I can't make much sense of your code. If I correctly understand what you want to do, my approach would look something like this:

...
  float headingDegrees = heading * 180/M_PI;
  if (headingDegrees > 80.0 && headingDegrees < 100.0) Serial.println("E");
  if (headingDegrees > 170.0 && headingDegrees < 190.0) Serial.println("S");
etc.

Hi,

I made a slight change to the Arduino code so I think I am kind of the right track with what I need but could use a little help from someone a lot more code savvy ;)

Before you changed the sketch, did it work?

Did you use any code from the examples that came with the library to check that you are communicating with the 5883?

What should your sketch do and what does it do?
What model arduino and what version IDE are you using?

Tom.... :slight_smile:

TomGeorge:
Hi,Before you changed the sketch, did it work?

Did you use any code from the examples that came with the library to check that you are communicating with the 5883?

What should your sketch do and what does it do?
What model arduino and what version IDE are you using?

Tom.... :slight_smile:

Yes the sketch was printing out direction info to the Serial monitor as expected which is great.
I'm using an Arduino Uno R3 right now just to get the code working right, ultimately though it will be put onto the Arduino Mega I'm using for the test of my project.

So the compass seems to be working fine.
I did another experiment based on what jremington had suggested. Although I kind of widened the detection angle a little for each direction.

//My Edit based on Posters suguestion
  if (headingDegrees > 50.0 && headingDegrees < 130.0) Serial.println("E");
  if (headingDegrees > 140.0 && headingDegrees < 220.0) Serial.println("S");
  if (headingDegrees > 230.0 && headingDegrees < 310.0) Serial.println("W");
  if (headingDegrees > 320.0 && headingDegrees < 40.0) Serial.println("N");

I was confused by what to do with my location Magnetic Declination, I guess that's used to calibrate the Digital Compass based on global location?

I made a little demo of the first experiment here:
Video Demo Of Experiment One

EDIT:

Seems like if I rotate the compass by keeping it flat I can get "S" & "W" values to print out only to the Serial monitor, if I pick it up and rotate it in other odd angles then I get "E" but do not seem to be able to get "N" ???
So I have no idea what's going on there.

The if statement

if (headingDegrees > 320.0 && headingDegrees < 40.0) Serial.println("N");

can never be true.

In order to work properly, magnetometers MUST be calibrated and kept away from magnets or magnetic materials. Here is an overview of the process and here is one calibration procedure for your compass.

jremington:
The if statement

if (headingDegrees > 320.0 && headingDegrees < 40.0) Serial.println("N");

can never be true.

In order to work properly, magnetometers MUST be calibrated and kept away from magnets or magnetic materials. Here is an overview of the process and here is one calibration procedure for your compass.

Thanks jeremington,
I'm on a Mac and not very good with a lot of the code stuff, I tried downloading the header thing but it just gave me some errors.?

Is there a way to calibrate the sensor on a Mac?

Is there a way to calibrate the sensor on a Mac?

Perhaps. You need to be able to read the raw data and from that, calculate an offset and scale factor for each axis.

jremington:
Perhaps. You need to be able to read the raw data and from that, calculate an offset and scale factor for each axis.

Thanks jremington,
I threw in the example code and I get this in the serial monitor:

With the module mounted on the breadboard the markings indicate (According to a real Compass) that the:

Y axis is facing towards the East
X axis is facing towards the West

not sure if that helps any?

Basically all I want to do is send a character through the serial port for whichever direction the compass is facing.

I found this awesome tutorial that looks like it has exactly what I am looking for as it uses 4 LEDs to indicate the direction the compass is facing which is GREAT because I can use the LED outputs to send the needed character to the serial monitor for my on screen direction arrow..... great right?

Wrong! because the code gives me errors which I don't understand.

The tutorial can be found here:
4 LED Compass Tutorial

Here is the code:

#include <Wire.h>
#include <HMC5883L.h>

HMC5883L compass;
int error = 0;

void setup()
{
Serial.begin(9600);

Serial.println(“Starting the I2C interface.”);
Wire.begin();

Serial.println(“Constructing new HMC5883L”);
compass = HMC5883L();

Serial.println(“Setting scale to +/- 1.3 Ga”);
error = compass.SetScale(1.3); // Set the scale of the compass.
if(error != 0) // If there is an error, print it out.
Serial.println(compass.GetErrorText(error));

Serial.println(“Setting measurement mode to continous.”);
error = compass.SetMeasurementMode(Measurement_Continuous); // Set the measurement mode to Continuous
if(error != 0) // If there is an error, print it out.
Serial.println(compass.GetErrorText(error));

pinMode(13, OUTPUT);
pinMode(12, OUTPUT);
pinMode(11, OUTPUT);
pinMode(10, OUTPUT);
}

void loop()
{
// Retrive the raw values from the compass (not scaled).
MagnetometerRaw raw = compass.ReadRawAxis();
// Retrived the scaled values from the compass (scaled to the configured scale).
MagnetometerScaled scaled = compass.ReadScaledAxis();

// Values are accessed like so:
int MilliGauss_OnThe_XAxis = scaled.XAxis;// (or YAxis, or ZAxis)

// Calculate heading when the magnetometer is level, then correct for signs of axis.
float heading = atan2(scaled.YAxis, scaled.XAxis);

// Once you have your heading, you must then add your ‘Declination Angle’, which is the ‘Error’ of the magnetic field in your location.
// Find yours here: http://www.magnetic-declination.com/
float declinationAngle = -0.1742;
heading += declinationAngle;

// Correct for when signs are reversed.
if(heading < 0)
heading += 2*PI;

// Check for wrap due to addition of declination.
if(heading > 2*PI)
heading -= 2*PI;

// Convert radians to degrees for readability.
float headingDegrees = heading * 180/M_PI;

//Light North LED
if(headingDegrees > 325 or headingDegrees < 45)
{
digitalWrite(13, HIGH);
digitalWrite(12, LOW);
digitalWrite(11, LOW);
digitalWrite(10, LOW);
}

//Light South LED
if(headingDegrees > 135 and headingDegrees < 225)
{
digitalWrite(13, LOW);
digitalWrite(12, LOW);
digitalWrite(11, LOW);
digitalWrite(10, HIGH);
}

//Light West LED
if(headingDegrees > 225 and headingDegrees < 325)
{
digitalWrite(13, LOW);
digitalWrite(12, HIGH);
digitalWrite(11, LOW);
digitalWrite(10, LOW);
}

//Light East LED
if (headingDegrees > 45 and headingDegrees < 135)
{
digitalWrite(13, LOW);
digitalWrite(12, LOW);
digitalWrite(11, HIGH);
digitalWrite(10, LOW);
}

Output(raw, scaled, heading, headingDegrees);
}

void Output(MagnetometerRaw raw, MagnetometerScaled scaled, float heading, float headingDegrees)
{
Serial.print(headingDegrees);
Serial.println(” Degrees \t”);
}

I get the following errors though:

Arduino: 1.6.5 (Mac OS X), Board: "Arduino/Genuino Uno"

ArduinoLEDcompass:11: error: stray '' in program
ArduinoLEDcompass:11: error: stray '' in program
ArduinoLEDcompass:14: error: stray '' in program
ArduinoLEDcompass:14: error: stray '' in program
ArduinoLEDcompass:17: error: stray '' in program
ArduinoLEDcompass:17: error: stray '' in program
ArduinoLEDcompass:22: error: stray '' in program
ArduinoLEDcompass:22: error: stray '' in program
ArduinoLEDcompass.ino: In function 'void setup()':
ArduinoLEDcompass:11: error: 'u201cStarting' was not declared in this scope
ArduinoLEDcompass:14: error: 'u201cConstructing' was not declared in this scope
ArduinoLEDcompass:17: error: 'u201cSetting' was not declared in this scope
ArduinoLEDcompass:22: error: expected ')' before 'measurement'
stray '' in program

This report would have more information with
"Show verbose output during compilation"
enabled in File > Preferences.

I'm not sure how crucial these parts were to the code's performance or accuracy but I changed the code to this by commenting out the parts that were giving me errors:

#include <Wire.h>
#include <HMC5883L.h>

HMC5883L compass;
int error = 0;

void setup()
{
Serial.begin(115200);

//Serial.println(“Starting the I2C interface.”);
Wire.begin();

//Serial.println(“Constructing new HMC5883L”);
compass = HMC5883L();

//Serial.println(“Setting scale to +/- 1.3 Ga”);
error = compass.SetScale(1.3); // Set the scale of the compass.
if(error != 0) // If there is an error, print it out.
Serial.println(compass.GetErrorText(error));

//Serial.println(“Setting measurement mode to continous.”);
error = compass.SetMeasurementMode(Measurement_Continuous); // Set the measurement mode to Continuous
if(error != 0) // If there is an error, print it out.
Serial.println(compass.GetErrorText(error));

pinMode(13, OUTPUT);
pinMode(12, OUTPUT);
pinMode(11, OUTPUT);
pinMode(10, OUTPUT);
}

void loop()
{
// Retrive the raw values from the compass (not scaled).
MagnetometerRaw raw = compass.ReadRawAxis();
// Retrived the scaled values from the compass (scaled to the configured scale).
MagnetometerScaled scaled = compass.ReadScaledAxis();

// Values are accessed like so:
int MilliGauss_OnThe_XAxis = scaled.XAxis;// (or YAxis, or ZAxis)

// Calculate heading when the magnetometer is level, then correct for signs of axis.
float heading = atan2(scaled.YAxis, scaled.XAxis);

// Once you have your heading, you must then add your ‘Declination Angle’, which is the ‘Error’ of the magnetic field in your location.
// Find yours here: http://www.magnetic-declination.com/
float declinationAngle = -0.1742;
heading += declinationAngle;

// Correct for when signs are reversed.
if(heading < 0)
heading += 2*PI;

// Check for wrap due to addition of declination.
if(heading > 2*PI)
heading -= 2*PI;

// Convert radians to degrees for readability.
float headingDegrees = heading * 180/M_PI;

//Light North LED
if(headingDegrees > 325 or headingDegrees < 45)
{
digitalWrite(13, HIGH);
digitalWrite(12, LOW);
digitalWrite(11, LOW);
digitalWrite(10, LOW);
Serial.println("N");
}

//Light South LED
if(headingDegrees > 135 and headingDegrees < 225)
{
digitalWrite(13, LOW);
digitalWrite(12, LOW);
digitalWrite(11, LOW);
digitalWrite(10, HIGH);
Serial.println("S");
}

//Light West LED
if(headingDegrees > 225 and headingDegrees < 325)
{
digitalWrite(13, LOW);
digitalWrite(12, HIGH);
digitalWrite(11, LOW);
digitalWrite(10, LOW);
Serial.println("W");
}

//Light East LED
if (headingDegrees > 45 and headingDegrees < 135)
{
digitalWrite(13, LOW);
digitalWrite(12, LOW);
digitalWrite(11, HIGH);
digitalWrite(10, LOW);
Serial.println("E");
}

Output(raw, scaled, heading, headingDegrees);
}

void Output(MagnetometerRaw raw, MagnetometerScaled scaled, float heading, float headingDegrees)
{
Serial.print(headingDegrees);
//Serial.println(” Degrees \t”);

delay(500);
}

With this I should at least be able to match up the LEDs with the Real physical compass and that should do the trick.

Unless someone knows a more appropriate way? :smiley:

The best way to check the performance of the compass is to compare its output in degrees with readings from a good quality, old fashioned magnetic needle compass.

jremington:
The best way to check the performance of the compass is to compare its output in degrees with readings from a good quality, old fashioned magnetic needle compass.

Yeah I think I have to change the code so that my character are sent out with the right directions because currently the ones in the code that say N W S E are incorrect.

Does anyone know if there is a way to properly calibrate the sensor on a Mac? All the apps I see are PC or the codes they say to run are only PC.

Can some one please explain if I am doing this part correctly?

// Once you have your heading, you must then add your ‘Declination Angle’, which is the ‘Error’ of the magnetic field in your location.
// Find yours here: http://www.magnetic-declination.com/
float declinationAngle = +3.70; //-0.1742;
heading += declinationAngle;

According to the Link: http://www.magnetic-declination.com/

my information is as follows:

Winnipeg Manitoba
Latitude: 49° 53' 3.8" N
Longitude: 97° 8' 49.3" W
Magnetic declination: +3° 7'
Declination is POSITIVE (EAST)

Inclination: 74° 50'
Magnetic field strength: 57079.2 nT

But I'm not sure I am using the values correctly:
float declinationAngle = +3.70;

Also which direction should the board be facing to point North? I keep thinking the "Y" marked on the board indicates the forward facing direction of the board??
2016-03-25_10-30-03 by Michael Knight, on Flickr

If you don't do anything, a compass needle points to magnetic North. If you want the compass itself to point due North, you have to subtract the magnetic declination for your locality.

Also which direction should the board be facing to point North? I keep thinking the "Y" marked on the board indicates the forward facing direction of the board??

A little common sense goes a long way.

Figure out where North is from where you are standing and see how the compass has to be oriented in order to report that it is pointing North. Or, you could consult the data sheet and compare that with the code. This assumes that the compass is properly calibrated, so that it actually works.

The same common sense approach works for the proper application of the magnetic declination. After making the correction, does the compass point to true North?

jremington:
If you don't do anything, a compass needle points to magnetic North. If you want the compass itself to point due North, you have to subtract the magnetic declination for your locality.

A little common sense goes a long way.

Figure out where North is from where you are standing and see how the compass has to be oriented in order to report that it is pointing North. Or, you could consult the data sheet and compare that with the code. This assumes that the compass is properly calibrated, so that it actually works.

The same common sense approach works for the proper application of the magnetic declination. After making the correction, does the compass point to true North?

Well it's kind of hard to say because I have two things I keep trying to get a straight answer on.

  1. which way should the board be facing to indicate it's reading.

  2. am I doing this part of the code correctly.... this 2nd one is the reason I "Cross Posted" as I felt that was more programming related? Was I incorrect in that assessment?

// Once you have your heading, you must then add your ‘Declination Angle’, which is the ‘Error’ of the magnetic field in your location.
// (+) Positive or (-) for negative
// For Bytom / Poland declination angle is 4'26E (positive)
// Formula: (deg + (min / 60.0)) / (180 / M_PI);
// Find yours here: http://www.magnetic-declination.com/
float declinationAngle = (3.0 + (8.0 / 60.0)) / (180 / M_PI);//+0.1742;
heading += declinationAngle;

I'm using this code as (And I'm just taking a guess here that the ----->Y marking on the board signifies the forward or front of the board that would correspond to the LED's that light up when the board faces in that direction)

#include <Wire.h>
#include <HMC5883L.h>

HMC5883L compass;
int error = 0;

void setup()
{
Serial.begin(115200);

//Serial.println(“Starting the I2C interface.”);
Wire.begin();

//Serial.println(“Constructing new HMC5883L”);
compass = HMC5883L();

//Serial.println(“Setting scale to +/- 1.3 Ga”);
error = compass.SetScale(1.3); // Set the scale of the compass.
if(error != 0) // If there is an error, print it out.
Serial.println(compass.GetErrorText(error));

//Serial.println(“Setting measurement mode to continous.”);
error = compass.SetMeasurementMode(Measurement_Continuous); // Set the measurement mode to Continuous
if(error != 0) // If there is an error, print it out.
Serial.println(compass.GetErrorText(error));

//Defines pins Our LEDs are On
pinMode(13, OUTPUT);
pinMode(12, OUTPUT);
pinMode(11, OUTPUT);
pinMode(10, OUTPUT);
}

void loop()
{
// Retrive the raw values from the compass (not scaled).
MagnetometerRaw raw = compass.ReadRawAxis();
// Retrived the scaled values from the compass (scaled to the configured scale).
MagnetometerScaled scaled = compass.ReadScaledAxis();

// Values are accessed like so:
int MilliGauss_OnThe_XAxis = scaled.XAxis;// (or YAxis, or ZAxis)

// Calculate heading when the magnetometer is level, then correct for signs of axis.
float heading = atan2(scaled.YAxis, scaled.XAxis);

// Once you have your heading, you must then add your ‘Declination Angle’, which is the ‘Error’ of the magnetic field in your location.
// (+) Positive or (-) for negative
// For Bytom / Poland declination angle is 4'26E (positive)
// Formula: (deg + (min / 60.0)) / (180 / M_PI);
// Find yours here: http://www.magnetic-declination.com/
float declinationAngle = (3.0 + (8.0 / 60.0)) / (180 / M_PI);//+0.1742;
heading += declinationAngle;

// Correct for when signs are reversed.
if(heading < 0)
heading += 2*PI;

// Check for wrap due to addition of declination.
if(heading > 2*PI)
heading -= 2*PI;

// Convert radians to degrees for readability.
float headingDegrees = heading * 180/M_PI;

//Light North LED
if(headingDegrees > 325 or headingDegrees < 45)
{
digitalWrite(13, HIGH);
digitalWrite(12, LOW);
digitalWrite(11, LOW);
digitalWrite(10, LOW);
Serial.println("N");
}

//Light South LED
if(headingDegrees > 135 and headingDegrees < 225)
{
digitalWrite(13, LOW);
digitalWrite(12, LOW);
digitalWrite(11, LOW);
digitalWrite(10, HIGH);
Serial.println("S");
}

//Light West LED
if(headingDegrees > 225 and headingDegrees < 325)
{
digitalWrite(13, LOW);
digitalWrite(12, HIGH);
digitalWrite(11, LOW);
digitalWrite(10, LOW);
Serial.println("W");
}

//Light East LED
if (headingDegrees > 45 and headingDegrees < 135)
{
digitalWrite(13, LOW);
digitalWrite(12, LOW);
digitalWrite(11, HIGH);
digitalWrite(10, LOW);
Serial.println("E");
}

Output(raw, scaled, heading, headingDegrees);
}

void Output(MagnetometerRaw raw, MagnetometerScaled scaled, float heading, float headingDegrees)
{
Serial.print(headingDegrees);
//Serial.println(” Degrees \t”);

delay(500);
}

20160325_122157 by Michael Knight, on Flickr

So it's partially working although when I rotate it in other directions the readings are a little off which I can only assume are because I have this wrong:

// Once you have your heading, you must then add your ‘Declination Angle’, which is the ‘Error’ of the magnetic field in your location.
// (+) Positive or (-) for negative
// For Bytom / Poland declination angle is 4'26E (positive)
// Formula: (deg + (min / 60.0)) / (180 / M_PI);
// Find yours here: http://www.magnetic-declination.com/
float declinationAngle = (3.0 + (8.0 / 60.0)) / (180 / M_PI);//+0.1742;
heading += declinationAngle;

which way should the board be facing to indicate it's reading.

I don't have the same board. This is something you need to figure out and understand. Read the data sheet for the compass, look at the code and experiment.

// Once you have your heading, you must then add your 'Declination Angle', which is the 'Error' of the magnetic field in your location.
// (+) Positive or (-) for negative

The above is to correct the heading, since the chosen compass axis points to magnetic North.

If you want the compass to point true North when it reads 0 degrees, you have to subtract the magnetic declination from the readings.

May I recommend a bit of study of basic navigation?

jremington:
I don't have the same board. This is something you need to figure out and understand. Read the data sheet for the compass, look at the code and experiment.

// Once you have your heading, you must then add your 'Declination Angle', which is the 'Error' of the magnetic field in your location.

// (+) Positive or (-) for negative




The above is to correct the [u]heading[/u], since the chosen compass axis points to [u]magnetic North[/u].

If you want the compass to [u]point true North[/u] when it reads 0 degrees, you have to [u]subtract[/u] the magnetic declination from the readings.

May I recommend a bit of study of basic navigation?

Thanks jremington,
I made a little bit of a demo video to try and better illustrate. I think the calibration is close but like I say I think I have that part of the code a little off based on my geographical location.... I'm not sure if I have the correct values in there for "Winnipeg, Manitoba CANADA"
Although I'm pretty sure according to the link that my "Declination Angle is Positive East
// For Winnipeg, Manitoba CANADA declination angle is +3 Degrees 8' E (positive)
so I would make it:
"heading += declinationAngle;"

Demo Video Link

Try remove your 5883L chip away from the laptop, and at least 50cm away from any metal.

You should build a calibration stand like this (takes 2 min)

Imgur

Then turn it clockwise in calibration mode.

The code could be a simple one like this :

//*************************************************************************
// Version: Arduino IDE 1.6.9
// Compass.h Author: helscream (Omer Ikram ul Haq)
// http://hobbylogs.me.pn/?p=17
// https://github.com/helscream/HMC5883L_Header_Arduino_Auto_calibration
//
// Code Modified by: = Tvixen
// Start Date: 02.07.2016
// Update: 03-07-2016
// Added Text when bearing is within the desired angle
// Update 03/07-2016
// Added Text when bearing due south

#include <Wire.h>
#include "compass.h"


//************************************
//Initialize and setup
//No serialprint in setup procedure
//************************************
void setup()
{
  Serial.begin(9600);
  Wire.begin();
  compass_x_offset = 122.17;
  compass_y_offset = 230.08;
  compass_z_offset = 389.85;
  compass_x_gainError = 1.12;
  compass_y_gainError = 1.13;
  compass_z_gainError = 1.03;
  
  /*Run Normal, without debug information  */
  compass_init(2);
  /*Run with debug information*/
  //  compass_debug = 1;
  /*Run with calibration*/
  //  compass_offset_calibration(3); // On a plane surface. Turn the compass print around the clock 360 degrees, with 1 sek interval. Start from North and go to east, then south, then west, then north.
}
//**********************************
// The loop where everything happens
//**********************************
void loop()
{
  compass_scalled_reading();
  
  Serial.print("x = ");
  Serial.println(compass_x_scalled);
  Serial.print("y = ");
  Serial.println(compass_y_scalled);
  Serial.print("z = ");
  Serial.println(compass_z_scalled);
  

  compass_heading();
  Serial.print ("Heading angle = ");
  Serial.print (bearing);
  Serial.println(" Degree");

  if (bearing > 0 && bearing < 45)
   {
    Serial.println("North");
   }

  if (bearing > 325 && bearing < 360)
   {
    Serial.println("North");
   }

  if (bearing > 45 && bearing < 135)
   {
    Serial.println("East");
   }

  if (bearing > 135 && bearing < 225)
   {
    Serial.println("South");
   }

  if (bearing >= 178 && bearing <= 182)
   {
    Serial.println("DUE SOUTH");
   }

  if (bearing > 225 && bearing < 325)
  {
   Serial.println("West");
  }
  Serial.println();
  Serial.println();
  delay(1000);
}

Remember to get the compass.h and add it to your Library.

You can get the scource code from github here: GitHub - helscream/HMC5883L_Header_Arduino_Auto_calibration: HMC5883L on Arduino with auto calibration capability

This was working for me the first time i used it.
The code is small and simple, and it's easy to implement it in your own code.