HM6343 Calibration

User Hard-Iron Calibration

Hi
I want to calibrate my HMC6343 tilt adjusted magnetometer. The instructions are quoted below

I would appreciate some assistance to clarify a few points

The unit has 3 axis.
The Z axis is like YAW and output the compass heading. This axis points up and down through the chassis
The Y axis is like pitch it runs from side to side of the chassis. A rotation along this axis would rotate the chassis back over front etc
The X axis is like roll. The axis runs longitudinal along the length of the chassis. A rotation along this axis would rotate the chassis left side over right side

Is this correct?

The calibration requires 2 rotations.
rotation 1) can be 360 around either the X or Y axis. Apparently it does not matter which
rotation 2) has to be around the Z axis. This makes sense as its is the heading that we are really trying to calibrate

Arduino calibration code from a previous post. I have cut this down to the bear minimum

I would appreciate some feed back on this prior to running as the compass is expensive and I do not
want to mess it up.

The reason for the calibration is that the compass in the W/E direction seems to be inaccurate. I am hoping the calibration will improve this…comments welcome

lastly I have written in my code a routine to average the output heading values. I have just noticed that this can be initiated from a command to the unit itself. Does anyone have any views on which would be most effective.

#include <Wire.h>

int compassAddress = 0x32 >> 1;  // From datasheet compass address is 0x32 for write operations, 
                                 // or 0x33 for read operations.
                                 // shift the address 1 bit right, the Wire library only needs the 7
                                 // most significant bits for the addr
                                 
//byte responseBytes[6];  // for holding the sensor response bytes// probably not required

void setup() 
{ 
  delay(500);  //Wait at least 500 milli-seconds for device initialization
  Wire.begin();        // join i2c bus (address optional for master) 
  Serial.begin(9600);  // start serial communication at 9600bps 
   
  calibration();
} 

void loop() 
{


}

   void calibration()
    {
     Serial.println("Entering Calibration mode...");     
     delay(10000);

     Wire.beginTransmission(compassAddress); 
     Wire.send(0x71);
     Wire.endTransmission();
    
     Serial.println("Rotate in the Y Axis. ie Back over front");    
     delay(10000);
    
     Serial.println("Rotate in the Z Axis. ie Rotate the Rover 360 around its central vertical axis ");    
     delay(10000); 

     Serial.println("Done.");
     

     Wire.beginTransmission(compassAddress); 
     Wire.send(0x7E);
     Wire.endTransmission();
     Serial.println("Exiting Calibration");
     delay(2000) ;
    }

Calibration instructions from data sheet

The HMC6343 provides a user calibration routine with the 0x71 command permitting entry into the calibration mode and the 0x7E command to exit the calibration mode.

After entering the calibration mode, rotate the device reasonably steady for 360 degrees about the Y (Left - Right) axis and then 360 degrees about Z (Up - Down) axis. During the first rotation, maintain the Y axis at Level as much as possible. Maintain the Z axis upright as much as possible during the second rotation and until the exit calibration command is issued. The first rotation can also be done by rotating 360 degrees about X (Fore -Aft) axis. Then exit calibration.

The calibration routine collects these readings to correct for hard-iron distortions of the magnetic field. These hard-iron effects are due to magnetized materials nearby the HMC6343 part that in a fixed position with respect to the end user platform. An example would be the magnetized chassis or engine block of a vehicle in which the compass is mounted onto. Upon exiting the calibration mode, the resulting magnetometer offsets are updated.

The built in calibration technique for the HMC6343 is very primitive and actually not very useful, unless the compass has completely latched up (which can happen if it is placed near a strong magnet).

You are much better off following the procedures outlined in this great blog post. In that procedure you apply the calibration results to the raw compass readings, before the tilt correction calculations. The result will be far more accurate.

Magnetometers are improving all the time and many new ones outperform the HMC6343, at lower cost. A summary of my application to the above technique, and some handy Arduino code, are given in this post on the Pololu forum, which also shows how effective the correction can be.

Dear J Remington
I really appreciated your response. It will take me a couple of days to get my head around the info that you have provided me...many thanks.

In the meantime can you recommend the "best" and "most reasonably priced" compass on the market. Obviously something better than the HMC 6343.

I am using RTK gps to control my rover with the HMC compass. The gps seems very accurate, however the HMC6343 has a distortion around 90 and 270 degrees. It seems ok around 0 and 180. Unless I can get rid of the distortion the compass will always mess things up. I can apply some correction in the code however calibrating them out would be better.

One question that I am researching is
Would it be better to use an accurate, well calibrated compass with my RTK gps or would an IMU with the gps give better results

many thanks

An IMU uses a magnetometer, so the results are only as good as the corrections you apply to the magnetometer in the IMU.

As for using an IMU, the yaw measurement relies entirely on the magnetometer so there is actually no need for the gyros. Their main function is to correct the accelerometer errors that arise when the craft is rotating.

I suggest to just use a good magnetometer (the ones Pololu sells are state of the art for hobby purposes) and calibrate it as carefully as you can, in the final rover environment. That is what I do for a boat autopilot, and it is accurate to about 1 degree, over the entire compass rose.

I am new to compasses so please forgive the dumb questions.

I think I would like the following in the compass if possible

5v connectivity to an Arduino, I2C connection, fast update rate more than 10Hz, tilt compensated. Easy to calibrate inplace. Accurate to 2-3 degrees. Good library. Good documentation

I looked at the Pololu sensors online as you suggested. They seem ether quite expensive $165 (UM7 orientation sensor) or really cheap ($4 to $20).Bearing in mind that my GPS gear is quite expensive (RTK) I am happy to spend the money on the UM7 if it provided a simple out of the box accurate solution. I thought that was what I was buying when I bought the $150 HMC6343.

Don't get me wrong the HMC6353 is a good compass however you say there are better on the market. My problem is that I do not know which ones are the better ones.

I already have a GY-273 HMC5883L, GY 801, GY 88, HMC6343 and a Sparkfun 9DOF Raza board from other projects. I have never really tested these against each other so do not know which magnetometer chips are better than the others.

What should I look for in the Pololu boards to get something better ?

I would be grateful for a specific recommendation

thanks

however the HMC6343 has a distortion around 90 and 270 degrees.

Perhaps you could elaborate, but this suggests that there is a serious problem with the HMC6343.

Unfortunately, it is a black box, and so you are limited to either using it as it is, with a substandard calibration procedure, or reading the raw data and doing all the distortion corrections and tilt corrections in the main program.

It is possible to get +/-1 degree accuracy, all around the compass rose, from the $8 LSM303D compass module, if properly calibrated by the procedures I linked earlier.

Pololu provides a simple program to implement a tilt compensated compass with that module, but the program does not include the more sophisticated corrections that I mentioned.

Either way, I would be happy to help you get that fully implemented, but I don't have an HMC6343 to test it with (I had its predecessor and discarded it).

I ended up buying the UM7 and a separate IMU from Pololu so I can get this sorted. Until they arrive I want to try to sort out my HMC6343. I struggle also teaches me more about I2C.

It seems from all the reading I have done that I can either use a sketch with its library and the specific methods in the library or utilize the “Wire” library to send specific commands to get data or read or write to particular registers. The second route gives more flexibility however also more risks for a I2C I2C newbie like me.

Using the first route and using the “advanced sketch” in sparkfun’s HMC6343 library the sketch returns the output below. Generally pitch and roll are outputting values near 65K. When I run the basic spark fun sketch I get reasonable heading values but pitch and roll are also maxing out near 65K. These outputs seems not correct. Maybe this is a 2’s complement issue on the MSB?

I then tried to write some code (not my strongest skill) utilizing the Wire library to determine the values stored in various registers of the HMC6343. When I ran the code it gave either 0 or 255.

I would really appreciate some guidance to modify the code reproduced below to be able to determine if this unit is working or not.

I have not started just yet with your recommended calibration technique because I want to learn as much as I could before doing something major to this break out board

regards
Max

Raw Tilt Data:
Pitch: 12
Roll: 65528
Temperature: 221

OP Mode 1: 10001
HMC6343 IC Serial Number: 85C

Raw Magnetometer Data:
X: 461
Y: 65043
Z: 64007

Raw Tilt Data:
Pitch: 12
Roll: 65528
Temperature: 221

OP Mode 1: 10001
HMC6343 IC Serial Number: 85C

Raw Magnetometer Data:
X: 457
Y: 65039
Z: 64007

Raw Tilt Data:
Pitch: 12
Roll: 65528
Temperature: 221

OP Mode 1: 10001
HMC6343 IC Serial Number: 85C

Raw Magnetometer Data:
X: 461
Y: 65052
Z: 64007

Raw Tilt Data:
Pitch: 12
Roll: 65528
Temperature: 221

OP Mode 1: 10001
HMC6343 IC Serial Number: 85C

Raw Magnetometer Data:
X: 461
Y: 65049
Z: 64007

#include <Wire.h>

int compassAddress = 0x32 >> 1; // From datasheet compass address is 0x32 for write operations,
// or 0x33 for read operations.
// shift the address 1 bit right, the Wire library only needs the 7
// most significant bits for the address

int heading = 0; // variable to hold the heading angle
int tilt = 0; // variable to hold the tilt angle
int roll = 0; // variable to hold the roll angle

byte responseBytes[6]; // for holding the sensor response bytes

void setup()
{
delay(500); //Wait at least 500 milli-seconds for device initialization
Wire.begin(); // join i2c bus (address optional for master)
Serial.begin(9600); // start serial communication at 9600bps

readEepromX();
readEepromY();
readEepromZ();
readDeviceSN();

delay(5000);
}
//*************************************************************
void loop()
{
// readSensor(); // read data from the HMC6343 sensor
// Note that heading, tilt and roll values are in tenths of a degree, for example
// if the value of heading is 1234 would mean 123.4 degrees, that’s why the result
// is divided by 10 when printing.
// Serial.print("Heading: “);
// Serial.print(heading / 10, DEC);
// Serial.print(” Tilt: “);
// Serial.print(tilt / 10, DEC);
// Serial.print(” Roll: ");
// Serial.println(roll / 10, DEC);
// delay(200); // wait for half a second
}
//******************************************************************
void readSensor()
{
// step 1: instruct sensor to read echoes
Wire.beginTransmission(compassAddress); // transmit to device
// the address specified in the datasheet is 66 (0x42)
// but i2c adressing uses the high 7 bits so it’s 33
Wire.write(0x50); // Send a “Post Heading Data” (0x50) command to the HMC6343
Wire.endTransmission(); // stop transmitting

// step 2: wait for readings to happen
delay(2); // datasheet suggests at least 1 ms

// step 3: request reading from sensor
Wire.requestFrom(compassAddress, 6); // request 6 bytes from slave device #33

// step 4: receive reading from sensor
if(6 <= Wire.available()) // if six bytes were received
{
for(int i = 0; i<6; i++) {
responseBytes = Wire.read();

  • }*

  • }*

  • heading = ((int)responseBytes[0]<<8) | ((int)responseBytes[1]); // heading MSB and LSB*

  • tilt = (((int)responseBytes[2]<<8) | ((int)responseBytes[3])); // tilt MSB and LSB*

  • roll = (((int)responseBytes[4]<<8) | ((int)responseBytes[5])); // roll MSB and LSB*
    }
    void readEepromX()

  • {*

  • byte MSByte;*

  • Serial.println(“Reading X Offset MSB”); *

  • Wire.beginTransmission(compassAddress);*

  • Wire.write(0xE1);*

  • Wire.write(0x0F);*

  • Wire.endTransmission();*

  • Wire.requestFrom(compassAddress,1);*

  • MSByte = Wire.read();*

  • Serial.println(MSByte); *

  • byte LSByte; *

  • Serial.println(“Reading X Offset LSB”); *

  • Wire.beginTransmission(compassAddress);*

  • Wire.write(0xE1);*

  • Wire.write(0x0E);*

  • Wire.endTransmission();*

  • Wire.requestFrom(compassAddress,1);*

  • MSByte = Wire.read();*

  • Serial.println(LSByte); *

  • }*
    void readEepromY()

  • {*

  • byte MSByte;*

  • Serial.println(“Reading Y Offset MSB”); *

  • Wire.beginTransmission(compassAddress);*

  • Wire.write(0xE1);*

  • Wire.write(0x11);*

  • Wire.endTransmission();*

  • Wire.requestFrom(compassAddress,1);*

  • MSByte = Wire.read();*

  • Serial.println(MSByte); *

  • byte LSByte; *

  • Serial.println(“Reading Y Offset LSB”); *

  • Wire.beginTransmission(compassAddress);*

  • Wire.write(0xE1);*

  • Wire.write(0x10);*

  • Wire.endTransmission();*

  • Wire.requestFrom(compassAddress,1);*

  • MSByte = Wire.read();*

  • Serial.println(LSByte); *

  • }*

  • void readEepromZ()*

  • {*

  • byte MSByte;*

  • Serial.println(“Reading Z Offset MSB”); *

  • Wire.beginTransmission(compassAddress);*

  • Wire.write(0xE1);*

  • Wire.write(0x13);*

  • Wire.endTransmission();*

  • Wire.requestFrom(compassAddress,1);*

  • MSByte = Wire.read();*

  • Serial.println(MSByte); *

  • byte LSByte; *

  • Serial.println(“Reading Z Offset LSB”); *

  • Wire.beginTransmission(compassAddress);*

  • Wire.write(0xE1);*

  • Wire.write(0x12);*

  • Wire.endTransmission();*

  • Wire.requestFrom(compassAddress,1);*

  • MSByte = Wire.read();*

  • Serial.println(LSByte); *

  • }*

  • void readDeviceSN()*

  • {*

  • byte MSByte;*

  • Serial.println(“Reading MSB of Device SN”); *

  • Wire.beginTransmission(compassAddress);*

  • Wire.write(0xE1);*

  • Wire.write(0x07);*

  • Wire.endTransmission();*

  • Wire.requestFrom(compassAddress,1);*

  • MSByte = Wire.read();*

  • Serial.println(MSByte); *

  • byte LSByte; *

  • Serial.println(“Reading LSB of Device SN”); *

  • Wire.beginTransmission(compassAddress);*

  • Wire.write(0xE1);*

  • Wire.write(0x06);*

  • Wire.endTransmission();*

  • Wire.requestFrom(compassAddress,1);*

  • MSByte = Wire.read();*

  • Serial.println(LSByte); *

  • }*

Hi Max,
did you find a way to calibrate the hmc6343? Have the same problem ...
Thx for a quick info:=)

I have not made any progress here

I did do a calibration. I used the sparkfun advanced example (changed 1 millisecond to 5000). Result was nada.
I am trying now to change to a different sensor.
Pls keep me posted

I am trying now to change to a different sensor.

This is what I advised the OP to do quite some time ago.

With proper calibration you can do much, much better than the HMC6343 at a tiny fraction of the cost, for example, Pololu’s LSM303D.

:=)
Now I am trying to work with a gyro+accelerometer+cheap compass. In detail L3G4200D, ADXL345 and HMC5883L, all mounted on a board called gy-801. Do you have any experience with that?