Built-in project · Arduino Uno R3

Tilt level on an Arduino Uno

An ADXL345 accelerometer read with the Adafruit library: the three axes in m/s^2 and a spirit level that says which way to tip. The whole build, an Arduino Uno and 1 more part, 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 15 minutes to build and run. Runs in your browser. Free, and no account needed.

The parts retail for about $33. On Mokxi they're free.

Tilt level · Arduino Uno R3live0.000 s 0.00x
Click to open it in the editor
The circuit itself, running here on the simulator. Press what can be pressed; click anything else to open it in the editor.

What you'll learn

  • Reading an accelerometer over I2C
  • Gravity as an acceleration of about 9.8 m/s^2
  • Raw counts versus real units
  • Setting a measurement range
  • A threshold that decides what counts as level
  • Difficulty: Intermediate
  • Time: About 15 minutes
  • Board: Arduino Uno R3
  • Parts: 3

How it works

The ADXL345 accelerometer measures gravity on three axes in meters per second squared. Lying flat, all of gravity is on Z. Tip it, and some moves onto X or Y. The Uno reads it over I2C on A4 and A5 with the Adafruit library.

The sketch prints X, Y and Z, and the raw count on X. It then acts as a spirit level: more than 13 raw counts on X says nose up or down, a big Y says roll it level, and otherwise it says level. That is about half a meter per second squared.

Parts list

3 parts, plus the jumper wires. They retail for about $33; on Mokxi they're free.

Kits that can build it

This starter kit has every part in the circuit. Each kit's page lists what else it can build.

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
Accelerometer, ADXL345 pin VCCArduino Uno R3 pin 5V
Accelerometer, ADXL345 pin GNDArduino Uno R3 pin GND
Accelerometer, ADXL345 pin SDAArduino Uno R3 pin A4
Accelerometer, ADXL345 pin SCLArduino Uno R3 pin A5

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
// Tilt level: an ADXL345 accelerometer on I2C, read with the Adafruit
// unified-sensor library, as a spirit level that says which way to tip.
//
//   ADXL345 (GY-291)   SDA -> A4   SCL -> A5   VCC -> 5 V   GND -> GND
//                      CS, SDO, INT1 and INT2 left as the board pulls them
//
// An accelerometer at rest measures gravity: 9.8 m/s^2 straight down. Lying
// flat that is all on Z; tipped, some of it moves onto X or Y, and the share
// says how far. The sketch prints the three axes in m/s^2 and the raw counts
// on X (256 a g at full resolution), and calls it level when X and Y are both
// under half a meter per second squared, about three degrees.
//
// The chip starts in standby and does not measure until it is told to:
// begin() does that, and setRange() picks 16 g at full resolution.
//
// Drag the board to tip it.

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_ADXL345_U.h>

Adafruit_ADXL345_Unified accel = Adafruit_ADXL345_Unified(12345);

void setup() {
  Serial.begin(9600);
  if (!accel.begin()) {
    Serial.println("No ADXL345 detected");
    while (1) delay(10);
  }
  accel.setRange(ADXL345_RANGE_16_G);
  Serial.println("Tilt level: ADXL345 ready");
}

void loop() {
  sensors_event_t event;
  accel.getEvent(&event);
  int16_t rawX = accel.getX();

  Serial.print("X: ");
  Serial.print(event.acceleration.x);
  Serial.print("  Y: ");
  Serial.print(event.acceleration.y);
  Serial.print("  Z: ");
  Serial.print(event.acceleration.z);
  Serial.print(" m/s^2  raw X ");
  Serial.print(rawX);

  // Half a m/s^2 is 13 counts at 3.9 mg a count.
  int16_t rawY = accel.getY();
  if (rawX > 13) {
    Serial.println("  tip it nose up");
  } else if (rawX < -13) {
    Serial.println("  tip it nose down");
  } else if (rawY > 13 || rawY < -13) {
    Serial.println("  roll it level");
  } else {
    Serial.println("  level");
  }
  delay(300);
}

Code walkthrough

The key functions of the sketch, and what each one does.

setup sketch.ino

Opens the serial port and calls accel.begin(). If the chip is not found, it prints a message and stops in a loop. Otherwise it sets the range to 16 g with setRange and prints that it is ready.

void setup() {
  Serial.begin(9600);
  if (!accel.begin()) {
    Serial.println("No ADXL345 detected");
    while (1) delay(10);
  }
  accel.setRange(ADXL345_RANGE_16_G);
  Serial.println("Tilt level: ADXL345 ready");
}

loop sketch.ino

Gets an event and the raw X and Y counts, then prints X, Y and Z in m/s^2 and raw X. If raw X is over 13 it says nose up, below -13 nose down. A big raw Y says roll it level. Otherwise it says level. It waits 300 ms.

void loop() {
  sensors_event_t event;
  accel.getEvent(&event);
  int16_t rawX = accel.getX();

  Serial.print("X: ");
  Serial.print(event.acceleration.x);
  Serial.print("  Y: ");
  Serial.print(event.acceleration.y);
  Serial.print("  Z: ");
  Serial.print(event.acceleration.z);
  Serial.print(" m/s^2  raw X ");
  Serial.print(rawX);

  // Half a m/s^2 is 13 counts at 3.9 mg a count.
  int16_t rawY = accel.getY();
  if (rawX > 13) {
    Serial.println("  tip it nose up");
  } else if (rawX < -13) {
    Serial.println("  tip it nose down");
  } else if (rawY > 13 || rawY < -13) {
    Serial.println("  roll it level");
  } else {
    Serial.println("  level");
  }
  delay(300);
}

Troubleshooting

The usual mistakes with this circuit, and the fix for each.

  • The monitor says No ADXL345 detected This project
    Check SDA on A4, SCL on A5, and 5V and ground to the sensor.
  • It is never exactly level This project
    The limit is 13 raw counts on each axis, about three degrees. You can change that number.
  • The sensor is not found on I2C. Accelerometer
    The ADXL345 address is 0x53 with the ALT ADDRESS pin low, or 0x1D with it high. Wire SDA and SCL with pull-ups. It is a 3.3 V part, so do not feed the bus 5 V logic directly.
  • Readings stay at zero or show nonsense. Accelerometer
    It starts in standby. Set the POWER_CTL measure bit (0x08) in register 0x2D. Choose the range in DATA_FORMAT. At rest, the Z axis should read about 1 g, and the numbers are raw counts to scale.
  • 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.