Nine axes on an Arduino Uno
An LSM6DSOX accelerometer and gyroscope beside an LIS3MDL magnetometer: pitch, turn rate and compass heading. 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 25 minutes to build and run. Runs in your browser. Free, and no account needed.
The parts retail for about $47. On Mokxi they're free.
What you'll learn
- Accelerometer, gyroscope and magnetometer roles
- Pitch from gravity with
atan2 - Compass heading from magnetic X and Y
- Sharing one I2C bus between two sensors
- Using Adafruit sensor libraries
- Difficulty: Intermediate
- Time: About 25 minutes
- Board: Arduino Uno R3
- Parts: 4
How it works
Two sensor boards share the I2C bus. An LSM6DSOX has an accelerometer and a gyroscope. An LIS3MDL is a magnetometer. Together they give nine axes: which way is down, how fast it turns, and which way is north.
Every pass, the sketch reads acceleration, turn rate and the magnetic field. Pitch is worked out from the accelerometer alone with atan2. Heading comes from the magnetic X and Y, kept in the 0 to 360 range.
It prints pitch, turn rate in degrees per second and heading. Drag the sliders on the sensors to see the numbers change.
Parts list
4 parts, plus the jumper wires. They retail for about $47; on Mokxi they're free.
- 1 × Arduino Uno R3In Starter Kit, Claw Machine Kit, Vending Machine Kit.
- 1 × Six-axis IMU, LSM6DSOX
- 1 × Power, 5 V
- 1 × Magnetometer, LIS3MDL
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 |
|---|---|
| Six-axis IMU, LSM6DSOX pin SCL | Arduino Uno R3 pin SCL |
| Six-axis IMU, LSM6DSOX pin SDA | Arduino Uno R3 pin SDA |
| Six-axis IMU, LSM6DSOX pin GND | Arduino Uno R3 pin GND |
| Magnetometer, LIS3MDL pin VIN | Arduino Uno R3 pin 5V |
| Magnetometer, LIS3MDL pin GND | Arduino Uno R3 pin GND |
| Magnetometer, LIS3MDL pin SDA | Arduino Uno R3 pin A4 |
| Magnetometer, LIS3MDL pin SCL | Arduino Uno R3 pin A5 |
| Six-axis IMU, LSM6DSOX 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.
// Nine axes: an LSM6DSOX accelerometer and gyroscope with an LIS3MDL
// magnetometer beside it, the three sensors a drone or a head tracker
// needs: which way is down, how fast it turns, and which way is north.
//
// LSM6DSOX SDA -> SDA SCL -> SCL
// LIS3MDL SDA -> A4 SCL -> A5
//
// Pitch comes from the accelerometer alone; the gyroscope reads the turn
// rate only while the board is moving.
//
// Things to try. Drag the pitch slider on the LSM6DSOX. Drag the heading
// slider on the LIS3MDL, and the one on the LSM6DSOX to see the gyro move.
#include <Adafruit_LSM6DSOX.h>
#include <Adafruit_LIS3MDL.h>
Adafruit_LSM6DSOX imu;
Adafruit_LIS3MDL mag;
void setup() {
Serial.begin(9600);
if (!imu.begin_I2C()) {
Serial.println("No LSM6DSOX");
}
if (!mag.begin_I2C()) {
Serial.println("No LIS3MDL");
}
Serial.println("Nine axes");
}
void loop() {
delay(100);
float ax, ay, az, gx, gy, gz;
imu.readAcceleration(ax, ay, az);
imu.readGyroscope(gx, gy, gz);
sensors_event_t field;
mag.getEvent(&field);
float pitch = atan2(-ax, az) * 180 / PI;
float heading = atan2(field.magnetic.y, field.magnetic.x) * 180 / PI;
if (heading < 0) heading += 360;
Serial.print("pitch ");
Serial.print(pitch, 0);
Serial.print(" turn ");
Serial.print(gz, 0);
Serial.print(" dps heading ");
Serial.println(heading, 0);
delay(200);
}
Code walkthrough
The key functions of the sketch, and what each one does.
setup sketch.ino
It opens serial at 9600 and starts both sensors with begin_I2C. If one does not answer, it prints "No LSM6DSOX" or "No LIS3MDL". It then prints "Nine axes".
void setup() {
Serial.begin(9600);
if (!imu.begin_I2C()) {
Serial.println("No LSM6DSOX");
}
if (!mag.begin_I2C()) {
Serial.println("No LIS3MDL");
}
Serial.println("Nine axes");
}loop sketch.ino
It reads acceleration and rotation from the IMU and a magnetic event from mag. Pitch is atan2(-ax, az) turned into degrees. Heading is atan2 of the magnetic y and x, with 360 added if it comes out negative. It prints pitch, the gyro z rate and heading.
void loop() {
delay(100);
float ax, ay, az, gx, gy, gz;
imu.readAcceleration(ax, ay, az);
imu.readGyroscope(gx, gy, gz);
sensors_event_t field;
mag.getEvent(&field);
float pitch = atan2(-ax, az) * 180 / PI;
float heading = atan2(field.magnetic.y, field.magnetic.x) * 180 / PI;
if (heading < 0) heading += 360;
Serial.print("pitch ");
Serial.print(pitch, 0);
Serial.print(" turn ");
Serial.print(gz, 0);
Serial.print(" dps heading ");
Serial.println(heading, 0);
delay(200);
}Troubleshooting
The usual mistakes with this circuit, and the fix for each.
- The monitor says No LSM6DSOX or No LIS3MDL This project
Check SDA, SCL, power and ground on that sensor. Both sensors need power and a shared bus. - The turn rate reads about zero This project
That is normal. The gyroscope only reads a turn rate while the board is moving. - The chip is not found. The I2C address is 0x6A or 0x6B, set by the SDO pin, and it runs at 3.3 V. Six-axis IMU
Scan for the address and match the SDO pin level. Use 3.3 V power and logic, or a level shifter. Pull up SDA and SCL, and read WHO_AM_I, which is 0x6C. - Readings are zero or look wrong. The output rate is off after power up, and the raw 16 bit values are little endian and need a scale. Six-axis IMU
Set the output data rate and full scale range registers. Combine the low byte first, then the high byte, as a signed number. Multiply by the sensitivity for the range you set. - The sensor is not found. Magnetometer
The address is 0x1C or 0x1E, set by the SDO/SA1 pin. Check SDA, SCL, power and ground. Many boards need 3.3 V logic, so check yours. - The compass heading is wrong. Magnetometer
Keep the sensor away from magnets, motors and steel. Calibrate by turning it in a full circle to remove offsets. Hold it level, or tilt compensate with an accelerometer. - 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.