Built-in project · Arduino Uno R3

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.

The circuit runs here once the page has loaded.

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.

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 pinConnects to
Six-axis IMU, LSM6DSOX pin SCLArduino Uno R3 pin SCL
Six-axis IMU, LSM6DSOX pin SDAArduino Uno R3 pin SDA
Six-axis IMU, LSM6DSOX pin GNDArduino Uno R3 pin GND
Magnetometer, LIS3MDL pin VINArduino Uno R3 pin 5V
Magnetometer, LIS3MDL pin GNDArduino Uno R3 pin GND
Magnetometer, LIS3MDL pin SDAArduino Uno R3 pin A4
Magnetometer, LIS3MDL pin SCLArduino Uno R3 pin A5
Six-axis IMU, LSM6DSOX pin VIN5 V

The code

The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.

sketch.ino · Arduino Uno R3
// 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.