Skip to content

Arduino ADXL345: Calibrate and Read 3-Axis Data | ShillehTek

October 30, 2023

Video Tutorial (Optional)

Watch first if you want to see the ADXL345 wiring and calibration flow in real time.

Project Overview

In this project, you connect an Arduino to an ADXL345 accelerometer over I2C, then run Arduino code to read X/Y/Z acceleration and optionally calibrate offsets for more accurate measurements.

The ADXL345 is a popular 3-axis accelerometer used in robotics, gaming, and wearable projects to measure motion and orientation.

  • Time: 20 to 40 minutes
  • Skill level: Beginner
  • What you will build: An Arduino I2C setup that reads ADXL345 3-axis acceleration and (optionally) writes calibration offsets

Parts List

From ShillehTek

External

  • Arduino board (example used: Arduino Nano)
  • ADXL345 accelerometer module (any ADXL345 breakout)
  • 4 jumper wires (for I2C + power)
  • Breadboard (optional, for easier wiring)
  • USB cable for your Arduino

Note: This tutorial uses I2C with the ADXL345 at address 0x53. Match SDA/SCL to your specific Arduino board’s I2C pins.

Step-by-Step Guide

Step 1 - Wire the ADXL345 to the Arduino over I2C

Goal: Create a working I2C connection so the Arduino can read acceleration registers from the ADXL345.

What to do: Connect the ADXL345 to your Arduino using four wires: power, ground, SDA, and SCL. The example shown uses an Arduino Nano, but any Arduino works as long as you match the correct I2C pins.

Arduino Nano wired to an ADXL345 accelerometer module using I2C (SDA and SCL) plus 3.3V/GND power
Example I2C wiring between an Arduino Nano and an ADXL345 module.

Expected result: The sensor is powered and connected on I2C (SDA/SCL). You are ready to upload code.

Step 2 - Upload the Arduino sketch and open Serial Monitor

Goal: Configure the ADXL345, read raw acceleration registers, convert them to g values, and print X/Y/Z to Serial Monitor.

What to do: Upload the following sketch to your Arduino. Then open the Serial Monitor at 9600 baud to view the output.

Code:

#include <Wire.h>

int ADXL345 = 0x53; // The ADXL345 sensor I2C address
float X_out, Y_out, Z_out; // Outputs
int X_offset = 0, Y_offset = 0, Z_offset = 0; // Offset values

void setup() {
  Serial.begin(9600);
  Wire.begin();
  configureADXL345(); // Configure the sensor
  // Note: You should calibrate upon re-powering the sensor
  // Uncomment if you want to calibrate
  // calibrateADXL345(); // Calibrate the sensor
}

void calibrateADXL345() {
  // You should calibrate with the positive axis pointed upwards against gravity
  // You should calibrate each axis separately and change the offset constant,
  // You need to run this code three times if you want to calibrate all three axes
  float numReadings = 500;
  // float xSum = 0;
  // float ySum = 0;
  float zSum = 0;
  Serial.print("Beginning Calibration");
  Serial.println();
  for (int i = 0; i < numReadings; i++) {
    Serial.print(i);
    Serial.println();
    Wire.beginTransmission(ADXL345);
    Wire.write(0x32); // Start with register 0x32 (ACCEL_XOUT_H)
    Wire.endTransmission(false);
    Wire.requestFrom(ADXL345, 6, true);
    X_out = (Wire.read() | Wire.read() << 8);
    Y_out = (Wire.read() | Wire.read() << 8);
    Z_out = (Wire.read() | Wire.read() << 8);
    // xSum += X_out;
    // ySum += Y_out;
    zSum += Z_out;
  }
  //// 256 is the raw value for 1g, (we calibrate at 1g hence why we take the difference)

  // X_offset = (256 - xSum / numReadings) / 4;
  // Serial.print("X_offset= " );
  // Serial.print(X_offset);
  // Y_offset = (256 - ySum / numReadings) / 4;
  // Serial.print("Y_offset= " );
  // Serial.print(Y_offset);

  Z_offset = (256 - (zSum / numReadings)) / 4;
  Serial.print("Z_offset= " );
  Serial.print(Z_offset);
  Serial.println();
  delay(1000);
  // We need to add it to the offset channel respectively =
  // Once you write to the offset channel you do not need to do it again,
  // unless you unpower and power the device
  // Each offset address is different for X, Y, and Z

  // Wire.beginTransmission(ADXL345);
  // Wire.write(0x1E);
  // Wire.write(X_offset);
  // Wire.endTransmission();
  // Wire.beginTransmission(ADXL345);
  // Wire.write(0x1F);
  // Wire.write(Y_offset);
  // Wire.endTransmission();
  Wire.beginTransmission(ADXL345);
  Wire.write(0x20);
  Wire.write(Z_offset);
  Wire.endTransmission();
}

void configureADXL345() {
  Wire.beginTransmission(ADXL345);
  Wire.write(0x2D); // Access/ talk to POWER_CTL Register - 0x2D
  Wire.write(8); // Enable measurement (D3 bit high)
  Wire.endTransmission();
  delay(10);
}

void loop() {
  Wire.beginTransmission(ADXL345);
  Wire.write(0x32); // Start with register 0x32 (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(ADXL345, 6, true);
  X_out = (Wire.read() | Wire.read() << 8);
  X_out = X_out / 256;
  Y_out = (Wire.read() | Wire.read() << 8);
  Y_out = Y_out / 256;
  Z_out = (Wire.read() | Wire.read() << 8);
  Serial.print(Z_out);
  Z_out = Z_out / 256;

  Serial.print("Xa= ");
  Serial.print(X_out);
  Serial.print("   Ya= ");
  Serial.print(Y_out);
  Serial.print("   Za= ");
  Serial.println(Z_out);
  delay(500);
}

Expected result: The Serial Monitor continuously prints Xa, Ya, and Za values about every 500 ms.

Step 3 - (Optional) Calibrate one axis and write the offset

Goal: Improve accuracy by calculating an offset when a single axis is pointed upward against gravity (1g), then writing that offset to the ADXL345.

What to do: In setup(), uncomment the calibration call:

  • Uncomment calibrateADXL345();

Calibrate with the positive axis you want to calibrate pointed upward and held as steady as possible. The provided calibration code is set up to compute and write the Z axis offset by averaging 500 readings and using 256 as the expected raw value for 1g.

To calibrate X or Y, you will need to switch the commented sections in the calibration function so it sums that axis and writes to the correct offset register address (shown in the comments). You need to run calibration separately for each axis if you want all three calibrated.

Expected result: The Serial Monitor prints the computed offset (for example, Z_offset), and the sketch writes that offset to the sensor so measurements are corrected while the sensor remains powered.

Step 4 - Return to measurement mode

Goal: Read acceleration normally after calibration.

What to do: After you finish calibrating (and confirming the offset values), comment out the calibration call again so the Arduino boots straight into continuous readings without re-running calibration each time.

Expected result: The Arduino prints steady, repeatable X/Y/Z acceleration readings, with improved accuracy if you applied offsets.

Conclusion

You connected an Arduino to the ADXL345 accelerometer over I2C, enabled measurement mode, and streamed 3-axis acceleration data to the Serial Monitor. You also learned how the included calibration routine averages readings and writes an offset (one axis at a time) to improve accuracy while the sensor stays powered.

Want the exact parts used in this build? Grab them from ShillehTek.com. If you want help customizing this for your application (robotics, wearables, orientation sensing) or building a complete prototype, check out our IoT consulting services.

Subscribe: Youtube

Support: https://www.buymeacoffee.com/mmshilleh