An ADXL345 on an Arduino: X, Y, Z and a spirit level
Intermediate
Or see every lesson.
- 505parts on the bench
- 45boards running now
- 1.00xreal time, on every board
The ADXL345 is a small three-axis accelerometer from Analog Devices. It sits on the GY-291 breakout in many sensor kits, talks to an Arduino over I2C, and at rest it measures one thing very well: which way is down. That makes it the chip behind tilt alarms, spirit levels and the "which way up is my phone" trick.
The circuit above is an Uno reading an ADXL345 with Adafruit’s unified sensor library. Three times a second it prints the three axes in meters per second squared, the raw count on X, and a word of advice: level, tip it nose up, tip it nose down, or roll it level. Press Run, then drag the board on the canvas to tip it and watch the numbers move.
What you need
- An Arduino Uno
- An ADXL345 breakout (GY-291)
- Four jumper wires
- A breadboard (optional)
Wiring
| Part and pin | Goes to | Note |
|---|---|---|
| ADXL345 VCC | 5 V | The GY-291 takes 3.3 V or 5 V |
| ADXL345 GND | GND | |
| ADXL345 SDA | A4 | The Uno’s I2C data pin |
| ADXL345 SCL | A5 | The Uno’s I2C clock pin |
| CS, SDO, INT1, INT2 | Nothing | The board pulls CS high for I2C and SDO low for address 0x53 |
What an accelerometer reads at rest
An accelerometer measures acceleration, and gravity counts. Lying flat on a desk, the ADXL345 feels 1 g pulling straight down through its Z axis and nothing on X or Y. Tip it, and part of that 1 g moves onto X or Y. How much moves tells you how far it is tipped: at 30 degrees, half of it is on the tipped axis, because the sine of 30 degrees is one half.
The chip turns each axis into a number. At full resolution one count is 3.9 thousandths of a g, so 1 g is about 256 counts. It sits at I2C address 0x53 with its SDO pin low, which is how the GY-291 ships, and at 0x1D with SDO tied high.
One detail trips up people who write their own code: the chip powers up in standby and does not measure anything until it is told to. Until then every axis reads zero. The library’s begin() does that for you.
The Adafruit ADXL345 library
Most ADXL345 tutorials use Adafruit_ADXL345_U.h, which needs Adafruit_Sensor.h beside it. In the Arduino IDE you install "Adafruit ADXL345" from the Library Manager and it pulls in the Unified Sensor library. In Mokxi both are built in, so the includes work with nothing to add.
begin() checks that the chip answers with its ID and wakes it up. If nothing answers, this sketch prints "No ADXL345 detected" and stops. setRange() picks the 2, 4, 8 or 16 g range; for tilt any of them works, and the sketch uses 16 g at full resolution.
getEvent() reads all three axes in one go and hands them back in meters per second squared. getX(), getY() and getZ() give the raw counts instead, which is what the sketch compares to decide which way to tip.
#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);
}Open this sketch in the editorThe code alone: the editor places the board and the parts it can read from the sketch.
What it prints, and why flat reads 10.04
Press Run and the first line after "Tilt level: ADXL345 ready" is all zeros: "X: 0.00 Y: 0.00 Z: 0.00 m/s^2 raw X 0 level". The sketch asked before the chip had finished its first sample. A third of a second later it reads "X: 0.00 Y: 0.00 Z: 10.04 m/s^2 raw X 0 level".
Why 10.04 and not 9.81? The library turns every count into 4 thousandths of a g, a round number close to the chip’s real 3.9. So the 256 counts of 1 g come out as 1.024 g, or 10.04 m/s^2. Real boards show the same thing, plus a little noise and offset of their own. For tilt it does not matter, because what counts is the share on each axis.
Drag the board 30 degrees nose up and the line becomes "X: -5.02 Y: 0.00 Z: 8.71 m/s^2 raw X -128 tip it nose down": half of 256 counts, with a minus sign, and the sketch says how to fix it. Tip it 30 degrees the other way and you get "raw X 128 tip it nose up". Roll it 20 degrees sideways and Y takes 3.45 m/s^2, with "roll it level". At 2 degrees of roll Y reads 0.35, under the 13 count limit, and the sketch calls it level.
I2C or SPI
The ADXL345 can also talk SPI: hold CS low and it ignores I2C and listens on SDA, SDO and SCL as a four-wire SPI bus. Some tutorials use that for faster reads. For a tilt sensor or a step counter I2C is plenty, and it needs only two wires that other sensors can share.
Mokxi models the I2C side only. With CS held low the simulated chip goes quiet on I2C, as a real one does, but it does not answer SPI. If your project needs SPI, test that wiring on a real board.
There is also a second built-in that skips the library and reads the chip’s registers with Wire, byte by byte. It is worth a look if you want to see what begin() and getEvent() actually send.
Common mistakes
Reading zeros on every axis. A hand-written driver that never writes 0x08 to the POWER_CTL register leaves the chip in standby. Use begin(), or write that byte yourself.
The wrong address. With SDO tied to 5 V the chip moves to 0x1D, and a sketch looking at 0x53 finds nothing. Wire SDO high here and the sketch prints "No ADXL345 detected", the same line you get with SDA pulled out.
Expecting 9.81 lying flat. With this library flat reads about 10.04, and a real board also has a small offset on each axis. Calibrate against your own flat reading if you need exact numbers.
Mixing up the axes. The arrows printed on the GY-291 show X and Y. Tip the board and watch which number moves before you write the code that reacts to it.
Open this circuit in the editor. Arduino Uno: an ADXL345 as a spirit level, wired as it runs at the top of this page. Change a part or a value and run it again.
Open the circuitQuestions
Which library do I need for the ADXL345 on Arduino?
Adafruit ADXL345 (Adafruit_ADXL345_U.h) and Adafruit Unified Sensor (Adafruit_Sensor.h), both from the Library Manager. In Mokxi they are built in.
What is the ADXL345 I2C address?
0x53 with SDO low or left open, which is how the GY-291 ships, and 0x1D with SDO tied high.
How do I wire an ADXL345 to an Arduino Uno?
VCC to 5 V, GND to GND, SDA to A4 and SCL to A5. Leave CS, SDO and the two interrupt pins as the board pulls them.
Why does my ADXL345 read 10 instead of 9.8 m/s^2?
The Adafruit library scales each count as 4 thousandths of a g, slightly more than the chip’s 3.9, so 1 g reads about 10.04. Real boards add their own small offsets on top.
Can I use the ADXL345 over SPI?
The chip supports it with CS held low. Mokxi simulates the I2C side only, so try SPI wiring on real hardware.
Keep going
Where the facts on this page come from
Build this for real
Open the editor, change a value and watch the number move with it. Nothing to install, and no account needed.