HMC5883L strange behavior


I bought this Module:
I am able to read all sensors via I2C/TWI. So far so good.

With the HMC5883L I have some trouble regarding the values.
I use the library from here (Arduino 1+ Version): which includes an example explained here:

I get readings but the behavior is a bit strange. I put the IMU flat on the table or floor. When I turn it 360 degrees, the values (or the compass I display via Processing) go 360 degrees too.
But: around 160 of these degrees are done by turning the IMU only about 45 degree. So the compass goes way to fast in 45 degrees and way too slow in the other 315 degrees.

I think I can rule out interference. I put it at different spots and as far away from every magnet or electronic component as possible. I always get the some behavior.
Does someone have an idea on what may be wrong?

Thanks Robert

Arduino Sketch:

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

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

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

  Wire.begin(); // Start the I2C interface.
  compass = HMC5883L(); // Construct a new HMC5883 compass.
  compass.SetScale(1.3); // Set the scale of the compass. ==> If return is read it returns an error. Stange as the value is correct, also regarding the library.
  compass.SetMeasurementMode(Measurement_Continuous); // Set the measurement mode to Continuous

void loop()
  // Retrived the scaled values from the compass (scaled to the configured scale).
  MagnetometerScaled scaled = compass.ReadScaledAxis();
  // 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:
  // 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.0457;
  //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; 

Processing Sketch:

import processing.serial.*;
import controlP5.*;

Serial myPort;  // Create object from Serial class
ControlP5 cp5;
float angle;

void setup()
  size(600, 600);
  myPort = new Serial(this, "COM15", 115200);

void draw()
 lineAngle(0, 300, 0, 600);
 lineAngle(300, 600, 90, 600);
 lineAngle(300, 300, -angle, 250);

void lineAngle(int x, int y, float angle, float length)
  line(x, y, x+cos(radians(angle))*length, y-sin(radians(angle))*length); //==> for Degrees
  //line(x, y, x+cos(angle)*length, y-sin(angle)*length); // ==> for Radians

 public void Comm(){
 while (myPort.available () > 0) {
    String myString = myPort.readStringUntil(10);
    if (myString != null) {
      print ("received: ");
      print (millis());
      print(" - ");

      angle = Float.parseFloat(myString);

I have this problem with all three of my HMC6883L devices.

Basically it is a calibration issue. I set up a conversion

fixed_x = k1x * ( raw_x - k2x )

for each of the dimensions, and then turn the device every which way and record the
vector components, and then use Matlab to calculate the 6 k parameters which give
the smallest error between all the vectors and a sphere instead of a football shape.

Thanks a lot for the reply. I understand why it is wrong. And I understanf in general that you record all the max values for the axis and add some offset for the readings later.
But so far I didn't get how exactly you do this.
Do you map the max readings to the max values they should have? Do you move the 0 point for the axis?

It would be nice if you could go a bit more in detail regarding this. Maybe you can give an example for one axis explaining the variables?

I am just starting with the topic .. so I need to lear a lot. :~


OK, found some explanation on calculating offset and scale. I will dig into it.
Thanks for the input. You got me pointed to the right direction.


If you have found the offset and scale, you are on the right track.

There is a nice video on youtube about this, but i can't find it now.

I download the data to a computer with matlab to process it, but that isn't essential.
Considering each magnetic axis, you need to set an offset to that the reading is the same
when you point one end of the device at the magnetic pole, compared to turning it around
180 degrees and pointing the other end at the magnetic pole.

You then need to define a scale factor for each axis, so that the scaled maximum and
minimum values are the same.

Thanks. Got it to work for a 360° rotation on X/Y axis.
The correction of offset and scale works for a pure X/Y calibration.

If I now rotate Z during the calibration too, I get different scaling and offset values for X and Y (not only in total, which would be clear because Z is included but also relative to eachother). With them I don't get a nice centered circle later when I only rotate X/Y.
How do I calibrate X/Y + Z correctly?


I started another thread with more calibration questions: