hmc5883l Anomalous readings - How to use reset/self test feature?

I recently bought hmc5883l module. When I started collecting readings using example magsensor sketch I immidietly saw there was something wrong.

The x axis and y axis barely reacted to changes in heading, while z axis remained fixed on value -418.

After studying the datasheet for the chip I figured out that the sensor must have been confused, or maybe damaged by either strong magnet or some local source of magnetism.

According to datasheet the chip does include degaussing/Self test feature. Which does seam ideal for this.

Since self test adds ~1.1 Gauss additional field to the existing field strength, using a reduced gain setting prevents sensor
from being saturated and data registers overflowed. For example, if the configuration register B is set to 0xA0 (Gain=5),
values around +452 LSb (1.16 Ga * 390 LSb/Ga) will be placed in the X and Y data output registers and around +421
(1.08 Ga * 390 LSb/Ga) will be placed in Z data output register. To leave the self test mode, change MS1 and MS0 bit of
the configuration register A back to 00 (Normal Measurement Mode). Acceptable limits of the self test values depend on
the gain setting. Limits for Gain=5 is provided in the specification table.
Below is an example of a “positive self test” process using continuous-measurement mode:

  1. Write CRA (00) – send 0x3C 0x00 0x71 (8-average, 15 Hz default, positive self test measurement)
  2. Write CRB (01) – send 0x3C 0x01 0xA0 (Gain=5)
  3. Write Mode (02) – send 0x3C 0x02 0x00 (Continuous-measurement mode)
  4. Wait 6 ms or monitor status register or DRDY hardware interrupt pin
  5. Loop
    Send 0x3D 0x06 (Read all 6 bytes. If gain is changed then this data set is using previous gain)
    Convert three 16-bit 2’s compliment hex values to decimal values and assign to X, Z, Y, respectively.
    Send 0x3C 0x03 (point to first data register 03)
    Wait about 67 ms (if 15 Hz rate) or monitor status register or DRDY hardware interrupt pin
  6. Check limits –
    If all 3 axes (X, Y, and Z) are within reasonable limits (243 to 575 for Gain=5, adjust these limits basing on the
    gain setting used. See an example below.) Then
    All 3 axes pass positive self test
    Write CRA (00) – send 0x3C 0x00 0x70 (Exit self test mode and this procedure)
    If Gain<7
    Write CRB (01) – send 0x3C 0x01 0x_0 (Increase gain setting and retry, skip the next data set)
    At least one axis did not pass positive self test
    Write CRA (00) – send 0x3C 0x00 0x70 (Exit self test mode and this procedure)
    End If

I decided to implement it in the sketch to the best of my abilities. Please note that this is basically my first experience with arduino. Before that I never touched microcontrollers and only coded stuff for PC or Android.

However I couldn’t make much sense of the pseudocode provided. First of all the loop didn’t seem to have any condition. Also there didn’t seem to be a way to add (substract) measured bias to/from the

Nevertheless I implemented the point 6 as a condition for the loop. Unfortunately even after exhausting all gain settings none of the axis passed the test. In fact none of the values changed significantly. I altered the loop so that it cycled trough the gain settings several times.

This did result in one, and sometimes two, axis passing the test with gain=5, but after switching to normal measurement the values returned to pre-test state.

Seeing that the code was ineffective I decided to implement my own approach:

void write8(byte address, byte reg, byte value)
  #if ARDUINO >= 100

void resetSensor(void){
	bool xPassed, yPassed, zPassed, testPassed;
	float X, Y, Z, adjustedX, adjustedY, adjustedZ;  	
	testPassed = false;
	  sensors_event_t event;		
		adjustedX = event.magnetic.x;
		adjustedY = event.magnetic.y;
		adjustedZ = event.magnetic.z;		
		X = event.magnetic.x;
		Y = event.magnetic.y;
		Z = event.magnetic.z;
		adjustedX = abs(X - adjustedX);
		adjustedY = abs(Y - adjustedY);
		adjustedZ = abs(Z - adjustedZ);
		if((adjustedX > 243) && (adjustedX < 575)){
			Serial.println("X axis passed the test");
			xPassed = true;
			Serial.println("X axis failed the test");
			xPassed = false;      
		if((adjustedY > 243) && (adjustedY < 575)){
			Serial.println("Y axis passed the test");
			yPassed = true;
			Serial.println("Y axis failed the test");
			yPassed = false;      
		if((adjustedZ > 243) && (adjustedZ < 575)){
			Serial.println("Z axis passed the test");
			zPassed = true;
			Serial.println("Z axis failed the test");      
			zPassed = false;
		if(xPassed && yPassed && zPassed){
			testPassed = true;

I lifted the Write8 method from the library so that I had it in the same file.
First I measure the values on the axis without enabled self-test, after that I perform another measurement with self test enabled. Finally I subtract the original values from self test values and compare the result with limits for gain=5 (again those are from the datasheet).
I repeat until all axis pass the test.

Now unfortunately this didn’t work either, so I’m kind of stuck.

The sensor is probably broken beyond repair, I do get some values from it. But it is completely useless as a compass. At best it might work as very shitty magnetometer.

However I really don’t want to throw it out. And so I want to ask if anyone has any idea what might be going on.

Is the sensor still salvageable? If so, what am I supposed to do? Does any library implement reset function?

The module may have been defective to begin with. Contact the seller for a replacement.

That is a possibility. However they can always say that it's my fault somehow.

If it is a functional copy of QMC5883L, then there is no bias coil.
Self test feature is not available in this type of clone HMC5883L.

In case others dig up this old post, I have successfully used the reset feature of the HMC5883L to fix a lockup problem caused by exposing the sensor to a strong magnet.

However, the HMC5883L is no longer in production, and many boards advertised to contain HMC5883L sensors actually contain one of two or more versions of the clone QMC5883, which does not have all the same functionalities as the HMC5883L (and has a different I2C address as well).

Did you use the code above?

I have two of the original chipped breakout board, they have been driving me crazy for the last 3 days. I want to use it as a 360 degree compass, but can’t get a decent result, although there is plenty of examples.
Calibrating it doesn’t give me consistent results either.