Digital Compass using Arduino

Programming, Tutorial, Coding, New Design, and/or any project for your Arduino and Raspberry Pi can post your topic /suggestion here.
User avatar
dondon pramis
Inventor
Inventor
Contact:
Location: Philippines
Posts: 51
Joined: Sun Feb 12, 2017 8:28 am

Digital Compass using Arduino

Sat Nov 24, 2018 4:45 am

Materials Required
Arduino Pro mini
HMC5883L Magnetometer sensor
LED lights - 8Nos
470Ohm Resistor – 8Nos
Barrel Jack
A reliable PCB manufacturer like PCBgogo
FTDI Programmer for mini
PC/Laptop

The Circuit Diagram:
Circuit-Diagram-for-Digital-Compass-using-Arduino-and-HMC5883L-Magnetometer.png
Circuit-Diagram-for-Digital-Compass-using-Arduino-and-HMC5883L-Magnetometer.png (71.26 KiB) Viewed 51 times
Difference between HMC5883L and QMC5883L
There is a common confusion revolving around these sensors for many beginners. This is because some vendors (actually most) sell the QMC5883L sensors instead of the original HMC5883L from Honeywell. It is mostly because the QMC5883L is way cheaper than the HMC5883L module. The sad part is that the working of these two sensors is slightly different and the same code cannot be used for both. This is because the I2C address of both the sensors is not the same. The code give in this tutorial will work only for QMC5883L the commonly available sensor module.
QMC5883L-Magnetometer-sensor.jpg
QMC5883L-Magnetometer-sensor.jpg (29.97 KiB) Viewed 51 times
To know which model of sensor you are having, you just have to look up closely at the IC itself to read what is written on top of it. If it is written something like L883 then it is the HMC58836L and if it is written something like DA5883 then it is the QMC5883L IC. Both the modules are shown in picture below for easy understating.
Difference-between-HMC5883L-and-QMC5883L.png
Difference-between-HMC5883L-and-QMC5883L.png (69.92 KiB) Viewed 51 times
The Sensor module has 5 pins out of which the DRDY (Data Ready) is not used in our project since we are operating the sensor in continuous mode. The Vcc and ground pin is used to power the Module with 5V from the Arduino board. The SCL and SDA are the I2C communication bus lines that are connected to the A4 and A5 I2C pins of the Arduino Pro mini respectively. Since the module itself has a pull high resistor on the lines, there is no need to add them externally.

To indicate the direction we have used 8 LEDs all of which are connected to the GPIO pins of the Arduino through a current limiting resistor of 470 Ohms. The Complete circuit is powered by a 9V battery through the barrel Jack. This 9V is provided directly to the Vin pin of the Arduino where it is regulated to 5V using the on-board regulator on Arduino. This 5V is then used to power the sensor and the Arduino as well.

Programming the Arduino
Now that our hardware is ready, let us look into the program that has to be uploaded into our Arduino board. The purpose of the code is to read the data from the QMC5883L magnetometer sensor and convert it into degree (0 to 360). Once we know the degree, we have to turn on a the LED pointing a specific direction. The direction I have used in this program is north. So irrespective of where you are there will only be one LED glowing on your board and the direction of the LED will indicate the NORTH direction. Once could later calculate the other direction is one direction is known.

As told earlier we are using the QMC5883L IC, to communicate with the IC we need to know the I2C address of its registers which can be found in its datasheet. But lucky for us all of that is already done and is packaged as a library by a guy called keepworking on Github. So all you have to do is simply download the Library for QMC5883L by clicking on the link to get a ZIP file. This ZIP file can then be added into your Arduino IDE by following Sketch -> Include Library -> Add .ZIP library.

Download Library for QMC5883L

After the Library is added, we can proceed with our program. We begin the program by including the required library files as shown below. The wire library is used to enable I2C communication and the MechaQMC5883 is the one that we just added to Arduino. This library holds all the information on how to talk with the EMC5883L sensor.
#include <Wire.h> //Wire Library for I2C communication
#include <MechaQMC5883.h> //QMC5883 Library is added since mine is QMC583 and not HMC5883
In the next line, we create an object name for the sensor we are using. I have used the name qmc but it can be anything you like.
MechaQMC5883 qmc; //Create an object name for the snsor, I have named it as qmc
Next, we get into the global variable declarations. Here since we have 8 Led as outputs, it is hard to refer to each one through pin name, so we are using the array option to refer to all the LEDs. The name of the array is ledPins and the variable led_count is the number of led we have. It starts with 0.
int ledPins[] = {2,3,4,5,6,7,8,9}; //Array of output pin to which the LED is connected to
char led_count = 7; //Total number of LED pins
Inside the void setup function, we initialize the I2C communication, Serial communication and the sensor as well. Then we declare all the LED pins as output pins. Since we have used an array, it is easy to refer to all the pins by using a for loop and navigating through the for loop as shown below.
void setup() {
Wire.begin(); //Begin I2C communication
Serial.begin(9600); //Begin Serial Communication
qmc.init(); //Initialise the QMC5883 Sensor

for (int thisPin=0; thisPin <= led_count; thisPin++){ //Navigate through all the pins in array
pinMode(ledPins[thisPin],OUTPUT); //Declare them as output
}
}
In the main loop which is an infinite one, we have to get the values of x,y and z from the sensor and calculate the degree the sensor is currently facing. To read the values of x,y and z use the following line

The formulae to calculate the heading in degree is shown below. Since we are not going to rotate the compass along the z axis we do not take that value into account. This formulae can be used only if the IC flat surface is facing up like it is in our set-up. Once heading is calculated, the value will be in range -180 to 180 which we have to convert to 0 to 360 like we would find in all digital compasses.
int heading=atan2(x, y)/0.0174532925; //Calculate the degree using X and Y parameters with this formulae
//Convert result into 0 to 360
if(heading < 0)
heading+=360;
heading = 360-heading;
The final step is to glow the LED pointing in the NORTH direction. To do that we have series of if conditions statements where we check in what range the degree is currently in and turn on the LED according to that. The code is show below:

Code: Select all

//Based on the value of heading print the result for debugging and glow the respective LED.
  if (heading > 338 || heading < 22)
  {
    Serial.println("NORTH");
    digitalWrite(ledPins[0],HIGH);
  }

  if (heading > 22 && heading < 68)
  {
    Serial.println("NORTH-EAST");
    digitalWrite(ledPins[7],HIGH);
  }

  if (heading > 68 && heading < 113)
  {
    Serial.println("EAST");
    digitalWrite(ledPins[6],HIGH);
  }

  if (heading > 113 && heading < 158)
  {
    Serial.println("SOUTH-EAST");
    digitalWrite(ledPins[5],HIGH);
  }

  if (heading > 158 && heading < 203)
  {
    Serial.println("SOUTH");
    digitalWrite(ledPins[4],HIGH);
  }

  if (heading > 203 && heading < 248)
  {
    Serial.println("SOTUH-WEST");
    digitalWrite(ledPins[3],HIGH);
  }

  if (heading > 248 && heading < 293)
  {
    Serial.println("WEST");
    digitalWrite(ledPins[2],HIGH);
  }

  if (heading > 293 && heading < 338)
  {
    Serial.println("NORTH-WEST");
    digitalWrite(ledPins[1],HIGH);
  }
The logic behind the code values can be understood by looking at the table below:
Specs.png
Specs.png (28 KiB) Viewed 51 times
The final part of the program is to set how fast the result has to be updated. I have create a delay for 500 milliseconds and then made all the LED to turn off to start again from the first inside the void loop. But if you need faster updates you can reduce the delay further down.
delay(500); // update position of LED for every alf seconds
//Turn off the all the LED
for (int thisPin=0; thisPin <= led_count; thisPin++){
digitalWrite(ledPins[thisPin],LOW);
}
Complete Code:

Code: Select all

/*
 * Program for Arduino Digital Compass using QMC5883
 * Project by: dohangout.com
 * Dated: 1-11-2018
 * Website: dohangout.com
 * Lib. from https://github.com/keepworking/Mecha_QMC5883L
 * WARNING: This code works only for QMC5883 Sensor which is commonly being sold as HMC5883
 */

#include <Wire.h> //Wire Library for I2C communication 
#include <MechaQMC5883.h> //QMC5883 Library is added since mine is QMC583 and not HMC5883

MechaQMC5883 qmc; //Create an object name for the sensor, I named it as qmc

int ledPins[] = {2,3,4,5,6,7,8,9}; //Array of output pin to which the LED is connected to
char led_count = 7; //Total number of LED pins 

  
void setup() {
  Wire.begin(); //Begin I2C communication 
  Serial.begin(9600); //Begin Serial Communication 
  qmc.init(); //Initialise the QMC5883 Sensor 

  for (int thisPin=0; thisPin <= led_count; thisPin++){ //Navigate through all the pins in array 
    pinMode(ledPins[thisPin],OUTPUT); //Declare them as output 
  }

}

void loop() { //Infinite Loop
  int x,y,z;
  qmc.read(&x,&y,&z); //Get the values of X,Y and Z from sensor 
  
  int heading=atan2(x, y)/0.0174532925; //Calculate the degree using X and Y parameters with this formulae 

 //Convert result into 0 to 360
  if(heading < 0) 
  heading+=360;
  heading = 360-heading;
  
  Serial.println(heading); //Print the value of heading in degree for debugging 

//Based on the value of heading print the result for debugging and glow the respective LED.
  if (heading > 338 || heading < 22)
  {
    Serial.println("NORTH");
    digitalWrite(ledPins[0],HIGH);
  }
  if (heading > 22 && heading < 68)
  {
    Serial.println("NORTH-EAST");
    digitalWrite(ledPins[7],HIGH);
  }
  if (heading > 68 && heading < 113)
  {
    Serial.println("EAST");
    digitalWrite(ledPins[6],HIGH);
  }
  if (heading > 113 && heading < 158)
  {
    Serial.println("SOUTH-EAST");
    digitalWrite(ledPins[5],HIGH);
  }
  if (heading > 158 && heading < 203)
  {
    Serial.println("SOUTH");
    digitalWrite(ledPins[4],HIGH);
  }
  if (heading > 203 && heading < 248)
  {
    Serial.println("SOTUH-WEST");
    digitalWrite(ledPins[3],HIGH);
  }
  if (heading > 248 && heading < 293)
  {
    Serial.println("WEST");
    digitalWrite(ledPins[2],HIGH);
  }
  if (heading > 293 && heading < 338)
  {
    Serial.println("NORTH-WEST");
    digitalWrite(ledPins[1],HIGH);
  }

  delay(500); // update position of LED for every alf seconds 
//Turn off the all the LED 
    for (int thisPin=0; thisPin <= led_count; thisPin++){
     digitalWrite(ledPins[thisPin],LOW);
  }
}


Return to “ARDUINO AND RASPBERRY PI”

Links

In total there are 8 users online :: 1 registered, 0 hidden and 7 guests
Registered users: Google [Bot]
Most users ever online was 156 on Sun Jun 17, 2018 7:42 am
Total posts 460
Total topics 339
Total members 69
Our newest member svetikvow
No birthdays today