Digital Compass Completed Project

Digital Compass Completed Project

 

Compasses are an essential instrument for the human race. They have been used for navigation and orientation purposes since the 11th century. The magnetic compass has helped the human race achieve accomplishments such as early arctic expeditions, Amazon rainforest explorations and even the discovery of the Americas!

In this tutorial we will show you how to build a Digital Compass. This digital compass will consist of a magnetometer that will be programmed to emit a LED display of the direction North, South, East, or West as you move. Just as a real magnetic compass always points to the north, our digital compass will also include a micro servo that continuously points north.

Magneto...what ?

3 Axis Magnetometer

3 Axis Magnetometer

 

The heart of our project will be the magnetometer, this will be used to sense the earth's magnetic field. We will be using the 3 Axis Magnetometer Breakout - HMC5883L which is available on the Jaycon website. This breakout board uses an I2C interface with an input voltage range of 2.16 to 3.6 volts. It is perfect for our project as it can be used in conjunction with an external microcontroller to create the digital compass.

The magnetometer and external microcontroller will provide our compass with the data it needs to display the correct letter coordinate. The letter (N, S, E, or W) will show on the 16 segment LED display.

The magnetometer and display will enable us to read and record the coordinates as we are moving, but we aren’t done just yet! We want to display north in the same way that a traditional magnetic compass does, so we are going to add a servo to do that job. With the data from the magnetometer and the rotation from the servo, we will have a compass that rotates properly to magnetic north.

Ready to build your very own compass? Here’s what you’ll need!

 

Materials for Digital Compass Project

Materials for Digital Compass Project

 

Hardware

Arduino Mega 2560 (Or any variation you have as long as you have enough pins)

A to B USB cable

Jumper wires (around 26)

Bread board

1 single 16 segment LED Display

HMC5883L Magnetometer

Servo motor

Printed compass (optional)

Wiring and connections

Digital Compass Circuit Diagram

Digital Compass Circuit Diagram

 

First, we will be setting up the LED Display. You can use any LED display that you want but for this tutorial I used a Jaycon favorite, the 16-segment LED display. We picked this one in particular because it allows us to display the letter W (west) which would not be possible with a 7-segment display. Connect the LED display and be sure that all the wires are set up properly so that the ground of the board is grounded to the arduino.

If you are having trouble setting up your LED display, you can look at any of our LED Display tutorials

  1. Programming a Single 7 Segment Display

  2. How a 7 Segment Display Works

With the LED set we can move on to the main part of this project, the magnetometer. The magnetometer is a really simple device consisting of five pins, and you can locate the pinning order via the data sheet. Even better, if you buy a magnetometer from Jaycon’s site, it will have the layout printed on it. I will be using four of the pins starting from top to bottom, GND, VCC, SCL, SDA. The last pin DRDY is for data ready, so we won’t be using that today.

If you have a ground line set in your breadboard you can use that, otherwise you will need to connect the ground to the Arduino GNDC. This magnetometer works in a voltage range from 2.16 to 3.6 volts; be sure to connect your VCC pin to the 3.3 V port in the Arduino.

The SDA and SCL pins are simple on my Arduino Mega, they are located beside the digital pins. On an Arduino UNO the SDA/SCL are the Analog Pins A4 &A5. If you are not sure about the pinning structure of your board, you can check your board manufacturer’s website by searching for SDA/SCL I2C connections.

 

Digital Compass Magnetometer and LED Display Connections

Digital Compass Magnetometer and LED Display Connections

 

At this point we have a functioning compass that displays the direction through the LED display, but let’s add a simple little servo for fun!. You should use a 360 degree servo so that it can rotate in any direction. Servos usually operate using three pins: power, gnd and the PWM connection. In the MEGA, I have dedicated PWM pins, but if you are using an UNO, for example, the pins are located in the Digital Pin section. You can recognize them by the ~ line beside the “D3” print. This symbol identifies the pin as a PWN pin.  

Note: Since we will be coding the servo to continuously point north, we will need the base of the servo to be static, and it will need to line up with our magnetometer. This is important so that the servo and the magnetometer always face the same position. I used a little tape to attach the servo to the small breadboard.

Be aware that the servo may have a magnetic motor that could possibly interfere with the magnetometer readings if you place it too close, so try to leave a little distance between these two.

Compass Face in Servo

Compass Face in Servo

 

 

If you want to fancy up the servo a little bit you can search for an image of a compass face, print it, and paste it into your servo’s moving head.

Now that all the hardware and wiring is set, we can begin programming.

Programming

Connect the Arduino to the computer, using the A to B USB cable and open the Arduino IDE software.

Go to Tools > Serial Port and make sure you have selected the proper serial port.  (Ex. COM3)

Go to Tools > Board and make sure you have selected the Arduino Mega 2560 or any Arduino board you are using.

Now it's time to enter the code: You can copy the code I've developed below (feel free to play with and edit this code). If you prefer, go ahead and enter your own code. We acknowledge that there are several ways to achieve results with this project. If you have any suggestions or ideas please let us know in the comment section.

To explain what I did with the code let’s take a look to the following diagram.

Location Diagram for Magnetometer

Location Diagram for Magnetometer

 

The diagram above shows the various directions that the magnetometer will move. It will start at 0° clockwise.

Since we used only one 16-segment display we are only able to display one coordinate at a time. This is why we divided the the cartesian plane as shown in the diagram below. We will be displaying N , when the magnetometer heading is between 315° and 45°. The same happens with E, S and W inside their respective coordinates.

The servo works differently than the magnetometer in terms of location, the servo’s movement is counterclockwise starting from 0° East.

Location Diagram for Servo

Location Diagram for Servo

 

We could say that the servo is off by 90° but this is taken into account when composing the code.

//www.jayconsystems.com

//These are the libraries that we will be using in the code

#include  // used to communicate with the magnetometer due to SDA/SCL connections
#include  // library to use the magnetometer
#include  // generic library for servo usage

HMC5883L compass; // creating an object called compass using HMC5883L library
Servo myservo;   // creatomg an object called my servo
int pos;  // variable for the position used in the servo


//size 21 array to hold the LED segment pins
int pinArr[21] = {22,24,26,28,30,32,34,36,38,40,42,44,46,47,48,50,51,52,54,56,58};
int duration = 1000; //INTEVAL PER DIGIT

//declaring the high and low pins to create letters in the 16 segment LED

int letN[21] ={0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,1,0,0,0,0,0};
int letS[21] ={1,0,0,1,1,0,0,0,1,1,0,1,0,1,0,0,1,0,0,0,0};
int letE[21] ={1,0,0,1,1,0,0,1,1,1,0,0,0,1,0,0,1,0,0,0,0};
int letW[21] ={0,0,0,1,0,0,1,1,0,0,0,1,1,0,0,1,0,0,0,0,0};

void setup(){
  Serial.begin(9600); //Sets the data rate in bits per second for serial data transmission
  Wire.begin(); //Initiate the Wire library and join the I2C bus as a master or slave
  
  myservo.attach(13); //telling the servo that is connected to the PWM Pin 13
  
  compass = HMC5883L(); //new instance of HMC5883L library
  setupHMC5883L(); //setup the HMC5883L magnetometer
  
  int Pin =0; //declared the pin var
  for (int Pin = 22; Pin <= 58; Pin++) { //loop to declare all LED pins as outputs
    pinMode(Pin, OUTPUT);
  }
}

//this function contains a for loop will set the proper pins as high or low, getting the commands from both arrays declared 
void segmentPrint(int array[])
{ 
for(int i =0; i<21; i++ ) 
digitalWrite (pinArr[i], array[i]); 

}



void setupHMC5883L(){
  //Setup the HMC5883L, and check for errors
  int error;  
  error = compass.SetScale(1.3); //Set the scale of the compass.
  if(error != 0) Serial.println(compass.GetErrorText(error)); //check if there is an error, and print if so

  error = compass.SetMeasurementMode(Measurement_Continuous); // Set the measurement mode to Continuous
  if(error != 0) Serial.println(compass.GetErrorText(error)); //check if there is an error, and print if so
}

float getHeading(){
  //Get the reading from the HMC5883L and calculate the heading
  MagnetometerScaled scaled = compass.ReadScaledAxis(); //scaled values from compass.
  float heading = atan2(scaled.YAxis, scaled.XAxis);

  // Correct for when signs are reversed.
  if(heading < 0) heading += 2*PI;
  if(heading > 2*PI) heading -= 2*PI;

 // int angle = heading * RAD_TO_DEG
  return heading * RAD_TO_DEG; //radians to degrees
}

// Our main program loop.
void loop(){
  
  float heading = getHeading();
  Serial.println(heading);
  delay(100); //only here to slow down the serial print
  
  
  //if statement for the compass digital and analog commands 
  //values are given in degrees and were separated in quarters to code different areas
  
  if ((heading >= 315 && heading <360) || (heading >= 0 && heading <45))
 {
   //will pass the letN array to the function to print the proper letter 
 segmentPrint(letN);
 
             if (heading >= 0 && heading <45)
             {
               //calculate the new position compensating the movement 
              int pos = 90 + heading;
             myservo.write(pos); 
             Serial.println(pos);
             }
             //each position will be modified according to the neccesary compensation
              else if (heading >=315 && heading =45 && heading <135)
             {
 segmentPrint(letE);
             if (heading >=45 && heading <=90)
             {
              int pos = 90 + heading;
             myservo.write(pos); 
             Serial.println(pos);
             }
             
             else if (heading >90 && heading <135)
             {
              int pos = (90 + (heading));
             myservo.write(pos); 
             Serial.println(pos);
             }
             } 
 
  else if (heading >=135 && heading <225)
 {
segmentPrint(letS);

           if (heading >135 && heading <225)
                       {
                        int pos = (90 + (heading));
                       myservo.write(pos); 
                       Serial.println(pos);
                       }
 } 

  else if (heading >=225 && heading <315)
 {
segmentPrint(letW); 

            if (heading >=270 && heading <=315)
             {
             int pos = (90 - (360 - heading));
             myservo.write(pos);
             
             
             Serial.println(pos);
             
             } 
}
}


 

Notes about the project

This compass points to the magnetic north not the true north. Please take that into account if you plan on using it for outdoor expeditions. You can research this discrepancy by searching the terms: “Magnetic North vs True North”


If your measurements and navigations need to be really accurate, you will have to calibrate the magnetometer very precisely. Use HMC5883L datasheet for detailed instructions on how to do this.