How to build a compass with a LSM303DLHC Compass and Accelerometer

Project Overview

This project connects a magnetometer and an RGB LED ring to create a compass that will light up the LED on the ring facing North.

Materials

How does the LSM303 Work?

The LSM303 is a triple-axis accelerometer/magnetometer compass module. Inside are two sensors, one is a 3-axis accelerometer, which measures the force acting on the object. The other sensor is a magnetometer that can sense where the direction and strength of a magnetic field.

The triple-axis accelerometer reports readings in the X, Y, and Z directions in meters per second squared. At rest, the sensor should report no acceleration except that due to gravity (about 9.8 meters/second squared). By calculating the angle of the gravity vector with respect to the X, Y and Z axis, the device can be repurposed into a tilt sensor.

The tripe-axis magnetometer reports readings in the X, Y and Z axis in micro-Teslas. In the absence of any strong local magnetic fields, the sensor readings should reflect the magnetic field of the earth (between 20 and 60 micro-Teslas). When the sensor is held level, by calculating the angle of the magnetic field with respect to the X and Y axis, the device can be used as a compass.

You can read more here.

Wiring

Connecting the LSM303

  • Connect board VCC(3v3) to Arduino 5V
  • Connect board GND to Arduino GND
  • Connect board SDA to Arduino SDA(or A4)
  • Connect board SCL to Arduino SCL(or A5)

Connecting the NeoPixel Ring

  • Connect Data Input to Arduino D6
  • Connect 5V to Arduino 5V
  • Connect GND to Arduino GND

Code

This code is based off the Adafruit LSM303DLH Mag compass example. If you would like to skip ahead, you can go ahead and open the example:

Open up File -> Examples -> Adafruit LSM303DLH Mag-> compass and upload to your Arduino wired up to the sensor.

Upload the sketch to your board and open up the Serial Monitor (Tools -> Serial Monitor) at 115200 baud. You will see heading calculations printed out to the serial monitor. If you rotate the sensor as it runs you can see the heading change.

Installation and Initialization

Magnetometer + Helper Libraries

First we need to make sure we have the necessary libraries installed.

  • Adafruit_LSM303DLH_Mag
  • Adafruit BusIO
  • Adafruit Unified Sensor

NeoPixel Library

  • Adafruit NeoPixel

After you have these libraries installed, we can include them in our sketch.

#include <Adafruit_LSM303DLH_Mag.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>
#include <Adafruit_NeoPixel.h>

We also want to set macros for the data input pin for the NeoPixel as well as the number of pixels in the ring(we’ll use this later). In this example, we’re using a 16 LED NeoPixel ring.

#define NEOPIXEL_PIN 6
#define NUM_PIXELS 16

Next, we want to initialize the magnetometer and the NeoPixel ring

Adafruit_LSM303DLH_Mag_Unified mag = Adafruit_LSM303DLH_Mag_Unified(12345);
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUM_PIXELS, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800);

Full initialization and macros

#include <Adafruit_LSM303DLH_Mag.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>
#include <Adafruit_NeoPixel.h>

#define NEOPIXEL_PIN 6
#define NUM_PIXELS 16
Adafruit_LSM303DLH_Mag_Unified mag = Adafruit_LSM303DLH_Mag_Unified(12345);
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUM_PIXELS, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800);

Setup Block

In the setup block, we want to do a few things:

  • Initialize a serial monitor for debugging purposes
  • Initialize the magnetometer
  • Initialize the NeoPixel

Here is the code for that below:

void setup(void) {
  Serial.begin(115200);
  Serial.println("Magnetometer Test");
  Serial.println("");

  /* Initialise the sensor */
  if (!mag.begin()) {
    /* There was a problem detecting the LSM303 ... check your connections */
    Serial.println("Ooops, no LSM303 detected ... Check your wiring!");
    while (1)
      ;
  }
  pixels.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
  pixels.setBrightness(100);
}

Loop Block

In the loop block, first we want to declare a sensor event, and then put the input data from the mag sensor into that variable.

sensors_event_t event;
mag.getEvent(&event);

Next we want to calculate the heading(north direction).

float heading = (atan2(event.magnetic.y, event.magnetic.x) * 180) / Pi;

There’s a few interesting things happen here. First, given an x and y axis reading, we can convert this into an angle by using the atan2() function. Since this returns the result in radians, we also have to convert it to degrees.

Additionally, since this returns a degree value between -180 to 180, we want to normalize it to return a value between 0 and 360.

if (heading < 0) {
  heading = 360 + heading;
}

That’s it! Now given whatever orientation the magnetometer is facing, it will return an angle that points north.

We can visualize this by using a NeoPixel ring. To do this, we need to map this degree value to an index from 0 to NUM_PIXELS, which in this case is 16. A helper function is provided below:

int mapDegToPixel(float deg) {
  float degStep = 360 / NUM_PIXELS;
  return floor(deg / degStep);
}

Finally we want to light up an LED of the NeoPixel at that index.

pixels.clear();
pixels.setPixelColor(mapDegToPixel(heading), 0, 255, 0);
pixels.show();

We can also write out our values to the serial as a sanity check and for debugging purposes.

Serial.print("Compass Heading: ");
Serial.print(heading);
Serial.print(", Pixel: ");
Serial.println(mapDegToPixel(heading));

And that’s it! Full code is provided below:

#include <Adafruit_LSM303DLH_Mag.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>
#include <Adafruit_NeoPixel.h>

#define NEOPIXEL_PIN 6
#define NUM_PIXELS 16
Adafruit_LSM303DLH_Mag_Unified mag = Adafruit_LSM303DLH_Mag_Unified(12345);
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUM_PIXELS, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800);

void setup(void) {
  Serial.begin(115200);
  Serial.println("Magnetometer Test");
  Serial.println("");

  /* Initialise the sensor */
  if (!mag.begin()) {
    /* There was a problem detecting the LSM303 ... check your connections */
    Serial.println("Ooops, no LSM303 detected ... Check your wiring!");
    while (1)
      ;
  }
  pixels.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
  pixels.setBrightness(100);
}

void loop(void) {
  /* Get a new sensor event */
  sensors_event_t event;
  mag.getEvent(&event);

  float Pi = 3.14159;

  // Calculate the angle of the vector y,x
  float heading = (atan2(event.magnetic.y, event.magnetic.x) * 180) / Pi;

  // Normalize to 0-360
  if (heading < 0) {
    heading = 360 + heading;
  }
  Serial.print("Compass Heading: ");
  Serial.print(heading);
  Serial.print(", Pixel: ");
  Serial.println(mapDegToPixel(heading));

  pixels.clear();
  pixels.setPixelColor(mapDegToPixel(heading), 0, 255, 0);
  pixels.show();
  delay(500);
}

int mapDegToPixel(float deg) {
  float degStep = 360 / NUM_PIXELS;
  return floor(deg / degStep);
}

Posted

in

by

Tags:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *