Hiking compass on an Arduino Uno
An LSM303AGR gives the compass heading and the tilt, and an LTR-390 the sun’s UV index, with a warning at 3. The whole build, an Arduino Uno and 2 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.
Teaching? Assign it to your class in one click.
Intermediate About 20 minutes to build and run. Runs in your browser. Free, and no account needed.
The parts retail for about $42. On Mokxi they're free.
What you'll learn
- Compass heading from two magnetic axes
- Tilt from an accelerometer
- Reading raw sensor registers over I2C
- A UV index threshold as a simple warning
- Difficulty: Intermediate
- Time: About 20 minutes
- Board: Arduino Uno R3
- Parts: 4
How it works
An LSM303AGR holds an accelerometer and a magnetometer. The sketch reads the magnetometer's two level axes directly over I2C and turns them into a compass heading with atan2. The accelerometer's reading gives the tilt. This heading is only right while the board is flat.
An LTR-390 on A4 and A5 reads the sun's UV index. Every half second or so the Uno prints the heading, tilt and UV index to the serial monitor.
When the UV index reaches 3 or more, it also prints "Sun cream".
Parts list
4 parts, plus the jumper wires. They retail for about $42; on Mokxi they're free.
- 1 × Arduino Uno R3In Starter Kit, Claw Machine Kit, Vending Machine Kit.
- 1 × Accelerometer and compass, LSM303AGR
- 1 × Power, 5 V
- 1 × UV and light, LTR-390
Wiring table
Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.
| Part and pin | Connects to |
|---|---|
| Accelerometer and compass, LSM303AGR pin SCL | Arduino Uno R3 pin SCL |
| Accelerometer and compass, LSM303AGR pin SDA | Arduino Uno R3 pin SDA |
| Accelerometer and compass, LSM303AGR pin GND | Arduino Uno R3 pin GND |
| UV and light, LTR-390 pin VIN | Arduino Uno R3 pin 5V |
| UV and light, LTR-390 pin GND | Arduino Uno R3 pin GND |
| UV and light, LTR-390 pin SDA | Arduino Uno R3 pin A4 |
| UV and light, LTR-390 pin SCL | Arduino Uno R3 pin A5 |
| Accelerometer and compass, LSM303AGR pin VIN | 5 V |
The code
The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.
// Hiking compass: an LSM303AGR reads which way is down and which way is
// north, and an LTR-390 reads the sun's UV index, two things worth knowing
// on a hill.
//
// LSM303AGR SDA -> SDA SCL -> SCL
// LTR-390 SDA -> A4 SCL -> A5
//
// The compass heading comes from the magnetometer's two level axes, so it is
// only right while the board is flat; the accelerometer says how far it is
// tipped. A UV index of 3 or more wants sun cream.
//
// Things to try. Drag the heading slider round. Tip the board with the
// roll slider. Drag the UV slider past 3.
#include <Wire.h>
#include <Adafruit_LSM303_Accel.h>
#include "Adafruit_LTR390.h"
const uint8_t MAG = 0x1E;
Adafruit_LSM303_Accel_Unified accel(1);
Adafruit_LTR390 uv;
void setup() {
Serial.begin(9600);
Wire.begin();
if (!accel.begin()) {
Serial.println("No LSM303");
}
// The magnetometer half: continuous at 10 Hz.
Wire.beginTransmission(MAG);
Wire.write(0x60);
Wire.write(0x00);
Wire.endTransmission();
uv.begin();
uv.setMode(LTR390_MODE_UVS);
uv.setGain(LTR390_GAIN_18);
uv.setResolution(LTR390_RESOLUTION_18BIT);
Serial.println("Hiking compass");
}
void loop() {
delay(200);
int16_t m[3];
Wire.beginTransmission(MAG);
Wire.write(0x68);
Wire.endTransmission(false);
Wire.requestFrom(MAG, (uint8_t)6);
for (uint8_t i = 0; i < 3; i++) {
uint8_t lo = Wire.read();
m[i] = (int16_t)(Wire.read() << 8 | lo);
}
float heading = atan2((float)m[1], (float)m[0]) * 180 / PI;
if (heading < 0) heading += 360;
sensors_event_t event;
accel.getEvent(&event);
float uvi = uv.getUVI();
Serial.print("heading ");
Serial.print(heading, 0);
Serial.print(" tilt ");
Serial.print(atan2(event.acceleration.y, event.acceleration.z) * 180 / PI, 0);
Serial.print(" UV ");
Serial.println(uvi, 1);
if (uvi >= 3) {
Serial.println("Sun cream");
}
delay(300);
}
Code walkthrough
The key functions of the sketch, and what each one does.
setup sketch.ino
Starts serial and I2C and begins the accelerometer. It then writes a configuration to the magnetometer at address 0x1E to run continuously at 10 Hz. The UV sensor is set to UV mode with gain 18 and 18-bit resolution.
void setup() {
Serial.begin(9600);
Wire.begin();
if (!accel.begin()) {
Serial.println("No LSM303");
}
// The magnetometer half: continuous at 10 Hz.
Wire.beginTransmission(MAG);
Wire.write(0x60);
Wire.write(0x00);
Wire.endTransmission();
uv.begin();
uv.setMode(LTR390_MODE_UVS);
uv.setGain(LTR390_GAIN_18);
uv.setResolution(LTR390_RESOLUTION_18BIT);
Serial.println("Hiking compass");
}loop sketch.ino
Reads six bytes from the magnetometer starting at register 0x68 into three values. It takes atan2 of the second and first, turns radians into degrees, and adds 360 if the result is negative. Then it reads the accelerometer and UV index, prints all three, and prints "Sun cream" at a UV index of 3 or more.
void loop() {
delay(200);
int16_t m[3];
Wire.beginTransmission(MAG);
Wire.write(0x68);
Wire.endTransmission(false);
Wire.requestFrom(MAG, (uint8_t)6);
for (uint8_t i = 0; i < 3; i++) {
uint8_t lo = Wire.read();
m[i] = (int16_t)(Wire.read() << 8 | lo);
}
float heading = atan2((float)m[1], (float)m[0]) * 180 / PI;
if (heading < 0) heading += 360;
sensors_event_t event;
accel.getEvent(&event);
float uvi = uv.getUVI();
Serial.print("heading ");
Serial.print(heading, 0);
Serial.print(" tilt ");
Serial.print(atan2(event.acceleration.y, event.acceleration.z) * 180 / PI, 0);
Serial.print(" UV ");
Serial.println(uvi, 1);
if (uvi >= 3) {
Serial.println("Sun cream");
}
delay(300);
}Troubleshooting
The usual mistakes with this circuit, and the fix for each.
- The heading is wrong when the board is tilted This project
The sketch uses only the two level magnetic axes, so keep the board flat. It does not correct for tilt. - The serial monitor says No LSM303 This project
Check the compass chip's SDA and SCL, VIN and GND. - The chip does not respond. Accelerometer and compass
It is two devices in one: the accelerometer is at 0x19 and the magnetometer at 0x1E. Scan the I2C bus and use a library for the AGR, not older LSM303 versions. Run it at 3.3 V. - The compass heading is wrong. Accelerometer and compass
Calibrate the magnetometer by turning it through all directions. Keep it away from magnets, motors and metal. Use the accelerometer to tilt-compensate the heading. - The sensor is not found, or UV reads zero indoors. UV and light
The I2C address is 0x53, and it is a 3.3 V part, so do not feed the bus 5 V. UV needs real sunlight, since glass and most indoor lights give almost none. - UV readings look wrong. UV and light
Choose the UV mode or the ambient light mode, because the sensor measures only one at a time. The UV index is raw counts divided by sensitivity for the gain and resolution you set. Change gain for the light level. - Upload fails or nothing runs after wiring pins 0 and 1 to a module. Arduino Uno R3
Pins 0 and 1 are the USB serial port. Move the module to other pins, or unplug it while uploading. Also check the right board is selected. - Pins misbehave or the board resets when you power motors or servos from it. Arduino Uno R3
Each I/O pin gives about 20 mA, 40 mA max. Power motors and servos from their own supply and join its ground to the Uno's GND.
Change it and keep it
Open it in the editor, change the circuit or the code, and keep your version in a free account.