HMC5883L 3-Axis Compass Module Interfacing with Arduino

HMC5883L is a magnetometer module developed by Honeywell using anisotropic magnetoresistive technology. It is a multichip module that behaves as a digital compass IC to find the direction and measures the magnitude and direction of the magnetic field along the X, Y and Z-axis. HMC5883L module converts the magnetic field into differential voltage output on 3 axis pins.

Why we Need Magnetometer?

The unmanned machines like robots and drones cannot sense in which direction to move, unlike humans. Therefore, they need a sensor to identify directions. Therefore, we need magnetometers. They sense the magnetic field and based on this magnetic field; it tells the direction of a device in which this magnetometer is integrated. Hence, we can use HMC5883L to determine the direction of objects

HMC5883L IC Pinout Diagram

HMC5883L magnetometer module consists of XC6206P332MR IC along with other components. Now let’s first discuss the pin configuration of this IC and after that we will see pinout of the module.

HMC5883 IC pinout diagram

IC Pin Configuration

This section provides the details of all pins of the HMC5883L IC. Pin 3, 5, 6, 7 and 14 are NC pins. They do not have any function and that’s why left unconnected.

I2C Communication Pins

Pin1 and pin16 are communication pins for the I2C interface.

  • SCL – Pin1
  • SDA – Pin16

SCL is a master/slave clock input which receives clock signal from a master device. Magnetometer IC acts as a slave when we interface it with microcontroller. Hence, microcontroller provides clock signal to IC. SDA is a serial data pin.

Power Supply Pins

Pin2 is a power supply pin. It requires voltage in a range of 2.16V to 3.6V for the sensor to operate. It provides power for internal operations. Pin9 and Pin11 are ground pins.

  • Pin_4 is the S1 pin. Tie this pin to VDDIO.
  • Pin_8 is a set/reset strap positive input pin. It removes the past magnetism stored in the sensor.
  • Pin_10 is a pin for a reservoir capacitor. Connect a 4.7 µF reservoir capacitor at this pin.
  • Pin_12 SETC is a connection on the driver side of set/reset strap.
  • Pin_13 VDDIO supplies power to the IO interface. It should be 1.71V to VDD.
  • Pin_15 is a data Ready interrupt pin which indicates when data is ready.

HMC5883L Module Pinout

As you can see that XC6206P332MR IC has a lot of pins and we need to use external components to make it work. But fortunately, module is available in market with all required components. This figure shows the pinout of the magnetometer module.

HMC5883L pinout diagram

As you can see from this pinout diagram that this module is easy to use and has fewer pins. The function of all pins are the same. SCL and SDA pins are used to interface with microcontrollers to receive 3-axis data from the magnetometer.

Features

  • It operates with a DC Voltage ranging from 3V to 6V.
  • It supports  communication protocol.
  • The sensor has 3-Axis Magnetoresistive Sensors, ASIC, 12-bit ADC and strap drive circuits.
  • The HMC2883L measures strength of magnetic field in a range of -8 to +8 gauss with an accuracy of 1-2 degree.
  • The maximum data output rate is 160Hz.

Where to use it?

HMC5883L is a small sensor which is cheap and easily available. Its digital  digital interface allows it to use with other microcontrollers. You can even connect it with normal IC’s. These are the most reliable and sensitive sensors as compared to other magnetometers in the industry.

You can use this sensor in applications to measure the strength and magnitude of magnetic field. It can also measure the magnetization of a material also. If you are looking for a magnetometer that can perform above functions by giving precise measurements, then this sensor would serve best for this purpose.

How to use HMC5883L?

Figures below show two reference designs of the HMC5883L sensor module to understand its working. In single supply design, apply same voltage supply at both pins VDD and VDDIO. While in dual supply mode, we have applied separate power supplies at both pins. VDD provides power to internal operations of IC whereas VDDIO supplies power to  interface to enable communication. All other connections in both circuits are same. It has two modes of operation standard and fast mode. Therefore, we connect pull up resistors to support these modes.

Single Supply Design

HMC5883L single supply connection diagram with pic microcontroller

Dual Supply design

HMC5883L dual supply connection diagram with microcontroller

Interfacing of HMC5883L with Arduino Uno

The HMC5883L has a breakout board which consists of extra circuitry to make it compatible for use with other microcontrollers. You can directly connect this breakout board with Arduino Uno without the need of extra components. The connections are simple. Connect Vcc to +5V and ground to the ground pin of Arduino. Connect the communication pins SCL and SDA to analog pin 5 and 4 or Arduino.

HMC5883 Arduino Library

Programming of this HMC5883 module is very easy with James Sleeman’s HMC5883L library. First of all, go to this link and download the library.

After that go to Arduino library manager and add the HMC5883 library. Or you can extract the downloaded folder and paste this file inside the Arduino libraries folder.

Connection Diagram with Arduino

Make connection with magnetometer module and Arduino Uno according to this schematic diagram.

  • GY-273 Compass Module -> Arduino
  • VCC -> VCC (See Note Below)
  • GND -> GND
  • SCL -> A5/SCL, (Use Pin 21 on the Arduino Mega)
  • SDA -> A4/SDA, (Use Pin 20 on the Arduino Mega)
  • DRDY -> Not Connected (in this example)
HMC5883 interfacing with Arduino

Arduino Code

This code shows the functions of HMC5883L module Arduino Library.

#include <Arduino.h>

// PLEASE NOTE!
// The Arduino IDE is a bit braindead, even though we include Wire.h here, it does nothing
// you must include Wire.h in your main sketch, the Arduino IDE will not include Wire
// in the build process otherwise.
#include <Wire.h>
#include "HMC5883L_Simple.h"

HMC5883L_Simple::HMC5883L_Simple()
{
  declination_offset_radians = 0;
  mode = COMPASS_SINGLE | COMPASS_SCALE_130 | COMPASS_HORIZONTAL_X_NORTH;  
  i2c_address = COMPASS_I2C_ADDRESS;  // NB: The HMC5883L does not appear to be able to have any different address.
                                      //     so this is a bit moot.                                      
}

/** Set declination in degrees, minutes and direction (E/W)
 *   See http://www.magnetic-declination.com/
 */

void HMC5883L_Simple::SetDeclination( int declination_degs , int declination_mins, char declination_dir )
{    
  // Convert declination to decimal degrees
  switch(declination_dir)
  {
    // North and East are positive   
    case 'E': 
      declination_offset_radians = ( declination_degs + (1/60 * declination_mins)) * (M_PI / 180);
      break;
      
    // South and West are negative    
    case 'W':
      declination_offset_radians =  0 - (( declination_degs + (1/60 * declination_mins) ) * (M_PI / 180));
      break;
  } 
}

/** Set the sampling mode to one of COMPASS_CONTINUOUS or COMPASS_SINGLE
 */

void HMC5883L_Simple::SetSamplingMode( uint16_t sampling_mode )
{  
  // Mode is the bits marked M in mode
  //    xxxxxxxxxxxSSSMM
  mode = (mode & ~0x03) | (sampling_mode & 0x03);

  Write(COMPASS_MODE_REGISTER, mode & 0x03);  
}

/** Set the scale to one of COMPASS_SCALE_088 through COMPASS_SCALE_810
 * Higher scales are less sensitive and less noisy
 * Lower scales are more sensitive and more noisy
 */

void HMC5883L_Simple::SetScale( uint16_t scale )
{
  // Scale is the bits marked S in mode
  //    xxxxxxxxxxxSSSMM  
  mode = (mode & ~0x1C) | (scale & 0x1C);

  Write(COMPASS_CONFIG_REGISTER_B, (( mode >> 2 ) & 0x07) << 5);
}

/** Set the orientation to one of COMPASS_HORIZONTAL_X_NORTH 
 * through COMPASS_VERTICAL_Y_WEST
 *  
 */

void HMC5883L_Simple::SetOrientation( uint16_t orientation )
{
  // Orientation is the bits marked XXXYYYZZZ in mode
  //    xxXXXYYYZZZxxxxx
  mode = (mode & ~0x3FE0) | (orientation & 0x3FE0);    
}

/** Get the heading of the compass in degrees. */
float HMC5883L_Simple::GetHeadingDegrees()
{     
  // Obtain a sample of the magnetic axes
  MagnetometerSample sample = ReadAxes();
  
  float heading;    
  
  // Determine which of the Axes to use for North and West (when compass is "pointing" north)
  float mag_north, mag_west;
   
  // Z = bits 0-2
  switch((mode >> 5) & 0x07 )
  {
    case COMPASS_NORTH: mag_north = sample.Z; break;
    case COMPASS_SOUTH: mag_north = 0-sample.Z; break;
    case COMPASS_WEST:  mag_west  = sample.Z; break;
    case COMPASS_EAST:  mag_west  = 0-sample.Z; break;
      
    // Don't care
    case COMPASS_UP:
    case COMPASS_DOWN:
     break;
  }
  
  // Y = bits 3 - 5
  switch(((mode >> 5) >> 3) & 0x07 )
  {
    case COMPASS_NORTH: mag_north = sample.Y;  break;
    case COMPASS_SOUTH: mag_north = 0-sample.Y; ;  break;
    case COMPASS_WEST:  mag_west  = sample.Y;  break;
    case COMPASS_EAST:  mag_west  = 0-sample.Y;  break;
      
    // Don't care
    case COMPASS_UP:
    case COMPASS_DOWN:
     break;
  }
  
  // X = bits 6 - 8
  switch(((mode >> 5) >> 6) & 0x07 )
  {
    case COMPASS_NORTH: mag_north = sample.X; break;
    case COMPASS_SOUTH: mag_north = 0-sample.X; break;
    case COMPASS_WEST:  mag_west  = sample.X; break;
    case COMPASS_EAST:  mag_west  = 0-sample.X; break;
      
    // Don't care
    case COMPASS_UP:
    case COMPASS_DOWN:
     break;
  }
    
  // calculate heading from the north and west magnetic axes
  heading = atan2(mag_west, mag_north);
  
  // Adjust the heading by the declination
  heading += declination_offset_radians;
  
  // Correct for when signs are reversed.
  if(heading < 0)
    heading += 2*M_PI;
    
  // Check for wrap due to addition of declination.
  if(heading > 2*M_PI)
    heading -= 2*M_PI;
   
  // Convert radians to degrees for readability.
  return heading * 180/M_PI; 
}


/** Read the axes from the magnetometer.
 * In SINGLE mode we take a sample.  In CONTINUOUS mode we 
 * just grab the most recent result in the registers.
 */

HMC5883L_Simple::MagnetometerSample HMC5883L_Simple::ReadAxes()
{
  if(mode & COMPASS_SINGLE) 
  {    
    Write(COMPASS_MODE_REGISTER, (uint8_t)( mode & 0x03 ));  
    delay(66); // We could listen to the data ready pin instead of waiting.
  }
  
  uint8_t buffer[6];
  Read(COMPASS_DATA_REGISTER, buffer, 6);

  MagnetometerSample sample;
  
  // NOTE:
  // The registers are in the order X Z Y  (page 11 of datasheet)
  // the datasheet when it describes the registers details then in order X Y Z (page 15)
  // stupid datasheet writers
  sample.X = (buffer[0] << 8) | buffer[1];  
  sample.Z = (buffer[2] << 8) | buffer[3];
  sample.Y = (buffer[4] << 8) | buffer[5];
  
  return sample;
}

/** Write data to the compass by I2C */
void HMC5883L_Simple::Write(uint8_t register_address, uint8_t data)
{
  Wire.beginTransmission(i2c_address);
  Wire.write(register_address);
  Wire.write(data);
  Wire.endTransmission();
}

/** Read data from the compass by I2C  
 */
uint8_t HMC5883L_Simple::Read(uint8_t register_address, uint8_t buffer[], uint8_t length)
{
  // Write the register address that we will begin the read from, this
  // has the effect of "seeking" to that register
  Wire.beginTransmission(i2c_address);
  Wire.write(register_address);
  Wire.endTransmission();
  
  // Read the data starting at that register we seeked
  Wire.requestFrom(i2c_address, length);

  if(Wire.available() == length)
  {
    for(uint8_t i = 0; i < length; i++)
    {
      buffer[i] = Wire.read();
    }
    
    return length;
  }

  return 0;
}

Example Code

This example sketch shows a reading of a heading from an HMC5883L triple-axis magnetometer on the Arduino Serial monitor.

#include <Arduino.h>
#include <Wire.h>
#include <HMC5883L_Simple.h>

// Create a compass
HMC5883L_Simple Compass;

void setup()
{
  Serial.begin(9600);
  Wire.begin();
    
  // Magnetic Declination is the correction applied according to your present location
  // in order to get True North from Magnetic North, it varies from place to place.
  // 
  // The declination for your area can be obtained from http://www.magnetic-declination.com/
  // Take the "Magnetic Declination" line that it gives you in the information, 
  //
  // Examples:
  //   Christchurch, 23° 35' EAST
  //   Wellington  , 22° 14' EAST
  //   Dunedin     , 25° 8'  EAST
  //   Auckland    , 19° 30' EAST
  //    
  Compass.SetDeclination(23, 35, 'E');  
  
  // The device can operate in SINGLE (default) or CONTINUOUS mode
  //   SINGLE simply means that it takes a reading when you request one
  //   CONTINUOUS means that it is always taking readings
  // for most purposes, SINGLE is what you want.
  Compass.SetSamplingMode(COMPASS_SINGLE);
  
  // The scale can be adjusted to one of several levels, you can probably leave it at the default.
  // Essentially this controls how sensitive the device is.
  //   Options are 088, 130 (default), 190, 250, 400, 470, 560, 810
  // Specify the option as COMPASS_SCALE_xxx
  // Lower values are more sensitive, higher values are less sensitive.
  // The default is probably just fine, it works for me.  If it seems very noisy
  // (jumping around), incrase the scale to a higher one.
  Compass.SetScale(COMPASS_SCALE_130);
  
  // The compass has 3 axes, but two of them must be close to parallel to the earth's surface to read it, 
  // (we do not compensate for tilt, that's a complicated thing) - just like a real compass has a floating 
  // needle you can imagine the digital compass does too.
  //
  // To allow you to mount the compass in different ways you can specify the orientation:
  //   COMPASS_HORIZONTAL_X_NORTH (default), the compass is oriented horizontally, top-side up. when pointing North the X silkscreen arrow will point North
  //   COMPASS_HORIZONTAL_Y_NORTH, top-side up, Y is the needle,when pointing North the Y silkscreen arrow will point North
  //   COMPASS_VERTICAL_X_EAST,    vertically mounted (tall) looking at the top side, when facing North the X silkscreen arrow will point East
  //   COMPASS_VERTICAL_Y_WEST,    vertically mounted (wide) looking at the top side, when facing North the Y silkscreen arrow will point West  
  Compass.SetOrientation(COMPASS_HORIZONTAL_X_NORTH);
  
}

// Our main program loop.
void loop()
{
   float heading = Compass.GetHeadingDegrees();
   
   Serial.print("Heading: \t");
   Serial.println( heading );   
   delay(1000);
}

Applications

  • GPS based wireless phones
  • computer games
  • wireless pointers
  • sport watches
  • portable GPS receivers

Datasheet

1 thought on “HMC5883L 3-Axis Compass Module Interfacing with Arduino”

  1. Using the provided code I am receiving the same heading over and over while moving the board in different directions.
    Compile has no errors.
    The displayed heading is 339.08 and never changes.
    Any ideas?
    Pat Kirk

    Reply

Leave a Comment