Hello! In this instructable you will see how you can make a Digital Compass using an Arduino and the Processing IDE. This is quite simple but interesting and cool looking Arduino Project.

You can watch the demo example of this tutorial on the video above. You can always find more interesting videos like this on my YouTube channel as well as find a lot of electronics projects and tutorials on my website, HowToMechatronics.com

Step 1: Parts Required

For this project you will need just an Arduino Board and a MEMS Magnetometer, for measuring the earth magnetic field. I will use the GY – 80 breakout board which contains the MC5883L 3 – Axis Magnetometer.

Before we continue with the source code for the project If you need more details how the MEMS magnetometer works as well as how to connect and use the GY - 80 breakout board via the I2C Communication you can check my particular tutorials for that.

Step 2: Arduino Source Code

What we need to do first is to upload a sketch to the Arduino Board that will read the data from the magnetometer and it will send it to the Processing IDE. Here's the Arduino Source Code:

<p>/*   Arduino Compass <br> *      
 *  by Dejan Nedelkovski, 
 *   www.HowToMechatronics.com
* */</p><p>#include <wire.h> //I2C Arduino Library</wire.h></p><p>#define Magnetometer_mX0 0x03 #define Magnetometer_mX1 0x04 #define Magnetometer_mZ0 0x05 #define Magnetometer_mZ1 0x06 #define Magnetometer_mY0 0x07 #define Magnetometer_mY1 0x08 </p><p>int mX0, mX1, mX_out; int mY0, mY1, mY_out; int mZ0, mZ1, mZ_out;</p><p>float heading, headingDegrees, headingFiltered, declination;</p><p>float Xm,Ym,Zm;</p><p>#define Magnetometer 0x1E //I2C 7bit address of HMC5883</p><p>void setup(){ //Initialize Serial and I2C communications Serial.begin(115200); Wire.begin(); delay(100); Wire.beginTransmission(Magnetometer); Wire.write(0x02); // Select mode register Wire.write(0x00); // Continuous measurement mode Wire.endTransmission(); }</p><p>void loop(){ //---- X-Axis Wire.beginTransmission(Magnetometer); // transmit to device Wire.write(Magnetometer_mX1); Wire.endTransmission(); Wire.requestFrom(Magnetometer,1); if(Wire.available()<=1) { mX0 = Wire.read(); } Wire.beginTransmission(Magnetometer); // transmit to device Wire.write(Magnetometer_mX0); Wire.endTransmission(); Wire.requestFrom(Magnetometer,1); if(Wire.available()<=1) { mX1 = Wire.read(); }</p><p> //---- Y-Axis Wire.beginTransmission(Magnetometer); // transmit to device Wire.write(Magnetometer_mY1); Wire.endTransmission(); Wire.requestFrom(Magnetometer,1); if(Wire.available()<=1) { mY0 = Wire.read(); } Wire.beginTransmission(Magnetometer); // transmit to device Wire.write(Magnetometer_mY0); Wire.endTransmission(); Wire.requestFrom(Magnetometer,1); if(Wire.available()<=1) { mY1 = Wire.read(); } //---- Z-Axis Wire.beginTransmission(Magnetometer); // transmit to device Wire.write(Magnetometer_mZ1); Wire.endTransmission(); Wire.requestFrom(Magnetometer,1); if(Wire.available()<=1) { mZ0 = Wire.read(); } Wire.beginTransmission(Magnetometer); // transmit to device Wire.write(Magnetometer_mZ0); Wire.endTransmission(); Wire.requestFrom(Magnetometer,1); if(Wire.available()<=1) { mZ1 = Wire.read(); } //---- X-Axis mX1=mX1<<8; mX_out =mX0+mX1; // Raw data // From the datasheet: 0.92 mG/digit Xm = mX_out*0.00092; // Gauss unit //* Earth magnetic field ranges from 0.25 to 0.65 Gauss, so these are the values that we need to get approximately.</p><p> //---- Y-Axis mY1=mY1<<8; mY_out =mY0+mY1; Ym = mY_out*0.00092;</p><p> //---- Z-Axis mZ1=mZ1<<8; mZ_out =mZ0+mZ1; Zm = mZ_out*0.00092; // ============================== //Calculating Heading heading = atan2(Ym, Xm); // Correcting the heading with the declination angle depending on your location // You can find your declination angle at: http://www.ngdc.noaa.gov/geomag-web/ // At my location it's 4.2 degrees => 0.073 rad declination = 0.073; heading += declination; // Correcting when signs are reveresed if(heading <0) heading += 2*PI;</p><p> // Correcting due to the addition of the declination angle if(heading > 2*PI)heading -= 2*PI;</p><p> headingDegrees = heading * 180/PI; // The heading in Degrees unit</p><p> // Smoothing the output angle / Low pass filter headingFiltered = headingFiltered*0.85 + headingDegrees*0.15;</p><p> //Sending the heading value through the Serial Port to Processing IDE Serial.println(headingFiltered);</p><p> delay(50); }</p>

Step 3: Processing IDE Source Code

After we have uploaded the previous Arduino sketch we need to receive the data into the Processing IDE and draw the Digital Compass. The compass is composed of an background image, fixed image of the arrow, and an rotating image of the body of the compass. So the values for the eart magnetic field calculated with the Arduino are used to rotate the compass.

Here's the source code of the Processing IDE:

<p>/*   Arduino Compass <br> *      
 *  by Dejan Nedelkovski, 
 *  <a href="http://www.HowToMechatronics.com">  www.HowToMechatronics.com

import processing.serial.*;
import java.awt.event.KeyEvent;
import java.io.IOException;</p><p>Serial myPort;
PImage imgCompass;
PImage imgCompassArrow;
PImage background;</p><p>String data="";
float heading;</p><p>void setup() {
  size (1920, 1080, P3D);
  imgCompass = loadImage("Compass.png");
  imgCompassArrow = loadImage("CompassArrow.png");
  background = loadImage("Background.png");
  myPort = new Serial(this, "COM4", 115200); // starts the serial communication
}</p><p>void draw() {
  image(background,0, 0); // Loads the Background image
  translate(width/2, height/2, 0); // Translates the coordinate system into the center of the screen, so that the rotation happen right in the center
  rotateZ(radians(-heading)); // Rotates the Compass around Z - Axis 
  image(imgCompass, -960, -540); // Loads the Compass image and as the coordinate system is relocated we need need to set the image at -960x, -540y (half the screen size)
  popMatrix(); // Brings coordinate system is back to the original position 0,0,0
  image(imgCompassArrow,0, 0); // Loads the CompassArrow image which is not affected by the rotateZ() function because of the popMatrix() function
  text("Heading: " + heading,40,40); // Prints the value of the heading on the screen</p><p>  delay(40);
}</p><p>// starts reading data from the Serial Port
 void serialEvent (Serial myPort) { 
   data = myPort.readStringUntil('\n');// reads the data from the Serial Port and puts it into the String variable "data".
  heading = float(data); // Convering the the String value into Float value

I hope you will like this project. If it's so you can also visit my website for more cool projects.

<p>This is really cool! I've always been intrigued by digital compasses. </p>

About This Instructable




Bio: Founder and Author of www.HowToMechatronics.com, an education website in the area of Mechanical, Electrical and Computer Engineering. Tutorials, Tips, Tricks, How It Works ... More »
More by Dejan Nedelkovski:LED Heart Photo Frame - Make a Perfect Valentine's or Birthday Present Arduino Digital Compass Project 
Add instructable to: