Magnetic Compass

In this project, you'll learn how to connect and control the GY-273 magnetic compass module which uses the HMC5883L sensor chip to detect low magnetic fields. The HMC5883L sensor is sensitive to the earth's magnetic fields in three axes. These axes are labeled X, Y, and Z. An output is provided for each of these axes that describes the position of these axes relative to the earth's magnetic field. As illustrated, twisting or turning the device will provide the corresponding outputs.

Magnetic compass

Parts needed: Magnetic compass

You may want to first read about the I2C protocol before you continue.

1 Making the connections
  • Connect the SCL pin on the compass to pin A5 on the Arduino.
  • Connect the SDA pin on the compass to pin A4 on the Arduino.
  • Connect Vcc to 5V.
  • Connect GND to GND.
2 Create a new program and type in this code.


To understand the register usage and values to read/write to the registers, you'll need to look at the datasheet for the HMC5883L sensor chip.
// Copyright 2017 Enoch Hwang
#include <Wire.h>

#define I2C_address 0x1E //I2C Address for The HMC5883L
float scale;

void setup(){  
  Serial.begin(9600);
  Wire.begin();
  
  Wire.beginTransmission(I2C_address);
  // set scale to +/- 1.3 gauss
  scale = 0.92;       // for 1.3 gauss
  Wire.write(0x01);   // select register address
  Wire.write(0x20);   // write data
  Wire.endTransmission();
  
  Wire.beginTransmission(I2C_address);
  // set continuous measurement mode
  Wire.write(0x02); // select register address
  Wire.write(0x00); // write data
  Wire.endTransmission();
}

void loop(){  
  int raw_x, raw_y, raw_z; //triple axis data
  float scale_x, scale_y, scale_z;
  
  //Tell the chip what register to start reading from
  Wire.beginTransmission(I2C_address);
  Wire.write(0x03); //start with register 3.
  Wire.endTransmission();  
 
 //Read the data. 2 bytes for each axis. 6 total bytes
  Wire.requestFrom(I2C_address, 6);
  if(Wire.available() == 6){
    raw_x = Wire.read()<<8; //MSB  x 
    raw_x |= Wire.read();   //LSB  x
    raw_z = Wire.read()<<8; //MSB  z
    raw_z |= Wire.read();   //LSB z
    raw_y = Wire.read()<<8; //MSB y
    raw_y |= Wire.read();   //LSB y
  }

  scale_x = raw_x * scale;
  scale_z = raw_z * scale;
  scale_y = raw_y * scale;
  
  // Show Values
  Serial.print("X Value: ");
  Serial.print(raw_x);
  Serial.print(", ");
  Serial.println(scale_x);
  
  Serial.print("Y Value: ");
  Serial.print(raw_y);
  Serial.print(", ");
  Serial.println(scale_y);
  Serial.print("Z Value: ");
  Serial.print(raw_z);
  Serial.print(", ");
  Serial.println(scale_z);
  Serial.println();
  
  delay(1000);
}
3 Run the program and open the Serial Monitor to see the X, Y, and Z values of the compass.
4 Add this code to change the X, Y, and Z values to the compass direction headings.

Place the compass so that the X and Y axis of the compass are level with the ground, and the Z axis is pointing up.

Adjust the compass reading with the declination angle for your location. If you do not do this then your compass reading will be slightly off. Find your declination angle here.

Convert the declination angle given in degrees minutes seconds to just degrees here.

Convert from degress to radians here.

Replace the declinationAngle constant in the code with your radians number.
  // The X and Y values are used to calculate the heading
  float heading = atan2(scale_y, scale_x);
  
  // Add your declination angle, which is the error of the magnetic field for your location.
  // The declination angle at LSU is 11 degrees 52 minutes (11* 52')
  //  = 11.86667*
  //  = 0.20711246275 radians
  // If you cannot find your declination angle, comment out the next two lines, 
  // but the compass will be slightly off.
  const float declinationAngle = 0.20711246275;
  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; 
  
  Serial.print("Heading is "); 
  Serial.print(headingDegrees);
  Serial.println(" degrees");