OverviewFirst, I will explain how the sensor work and how to read the data from it, and then using the Processing development environment, we will make a 3D visualization of the accelerometer orientation.

Arduino and ADXL345 Accelerometer Tutorial

How ADXL345 Accelerometer Works

To begin with, let’s take a look how the ADXL345 sensor works. This is a 3-axis accelerometer which can measure both static and dynamic forces of acceleration. The earth gravitational force is a typical example of static force, while dynamic forces can be caused by vibrations, movements and so on.

ADXL345 Acceleromter How It Works

The unit of measurement for acceleration is meter per second squared (m/s^2). However, accelerometer sensors usually express the measurements in “g” or gravity. One “g” is the value of the earth gravitational force which is equal to 9.8 meters per second squared.

So, if we have an accelerometer positioned flat, with its Z-axis pointing upwards, opposite to the gravitational force, the Z-axis output of the sensor will be 1g. On the other hand, the X and Y outputs will be zero, because the gravitational force is perpendicular to these axes and doesn’t affect them at all.

ADXL345 3 axis acceleromter output data

If we flip the sensor upside down, then the Z-axis output will be -1 g. This means that the outputs of the sensor due to its orientation to gravity can vary from -1g to +1g.

adxl345 z axis output

So according to this data and using some trigonometry math, we can calculate the angle at which the sensor is positioned.

How to Read ADXL345 Accelerometer Data with Arduino

Ok, now let’s see how we can read the ADXL345 accelerometer data using the Arduino. This sensor uses the I2C protocol for communication with the Arduino so we need only two wires for connecting it, plus the two wires for powering it.

Arduino and ADXL345 Accelerometer Circuit Diagram

You can get the components needed for this Arduino Tutorial from the links below:

*Please note: These are affiliate links. I may make a commission if you buy the components through these links. I would appreciate your support in this way!

ADXL345 Accelerometer Arduino Code

Here’s the Arduino code fo reading the ADXL345 accelerometer data.


  1. /*
  2. Arduino and ADXL345 Accelerometer Tutorial
  3. by Dejan, https://howtomechatronics.com
  4. */
  5. #include <Wire.h> // Wire library – used for I2C communication
  6. int ADXL345 = 0x53; // The ADXL345 sensor I2C address
  7. float X_out, Y_out, Z_out; // Outputs
  8. void setup() {
  9. Serial.begin(9600); // Initiate serial communication for printing the results on the Serial monitor
  10. Wire.begin(); // Initiate the Wire library
  11. // Set ADXL345 in measuring mode
  12. Wire.beginTransmission(ADXL345); // Start communicating with the device
  13. Wire.write(0x2D); // Access/ talk to POWER_CTL Register – 0x2D
  14. // Enable measurement
  15. Wire.write(8); // (8dec -> 0000 1000 binary) Bit D3 High for measuring enable
  16. Wire.endTransmission();
  17. delay(10);
  18. }
  19. void loop() {
  20. // === Read acceleromter data === //
  21. Wire.beginTransmission(ADXL345);
  22. Wire.write(0x32); // Start with register 0x32 (ACCEL_XOUT_H)
  23. Wire.endTransmission(false);
  24. Wire.requestFrom(ADXL345, 6, true); // Read 6 registers total, each axis value is stored in 2 registers
  25. X_out = ( Wire.read()| Wire.read() << 8); // X-axis value
  26. X_out = X_out/256; //For a range of +-2g, we need to divide the raw values by 256, according to the datasheet
  27. Y_out = ( Wire.read()| Wire.read() << 8); // Y-axis value
  28. Y_out = Y_out/256;
  29. Z_out = ( Wire.read()| Wire.read() << 8); // Z-axis value
  30. Z_out = Z_out/256;
  31. Serial.print(“Xa= “);
  32. Serial.print(X_out);
  33. Serial.print(” Ya= “);
  34. Serial.print(Y_out);
  35. Serial.print(” Za= “);
  36. Serial.println(Z_out);
  37. }

Description: So first we need to include the Wire.h library which is used for the I2C communication. If you want to learn more on how the I2C communication works and how to use it with Arduino you can check my other detailed tutorial for it.

Each device that uses the I2C communication has a unique I2C address, and this address can be found in the datasheet of the sensor (ADXL345 Datasheet). So, once we define the address and the variables for the three outputs, in the setup section, first, we need to initialize the wire library and then set the accelerometer in measuring mode. In order to do that, if we take a look at the datasheet again, we can see that we need to set the bit D3 of the POWER_CTL register HIGH.

adxl345 power register - enabling measuring mode

So, using the beginTransmission() function we start the communication, then using the write() function we tell which register we want to access, and again using the write() function we set the D3 bit HIGH, by writing the number 8 in decimal which correspond to setting the bit D3 HIGH.


  1. // Set ADXL345 in measuring mode
  2. Wire.beginTransmission(ADXL345); // Start communicating with the device
  3. Wire.write(0x2D); // Access/ talk to POWER_CTL Register – 0x2D
  4. // Enable measurement
  5. Wire.write(8); // (8dec -> 0000 1000 binary) Bit D3 High for measuring enable
  6. Wire.endTransmission();

In the loop section now we read the data from the sensor. The data for each axis is stored in two bytes or registers. We can see the addresses of these registers from the datasheet.

adxl345 acceleromter x y z data registers

In order to read them all, we start with the first register, and the using the requestionFrom() function we ask to read the 6 registers. Then using the read() function, we read the data from each register, and because the outputs are twos complements we combine them appropriately to get the correct values.


  1. // === Read acceleromter data === //
  2. Wire.beginTransmission(ADXL345);
  3. Wire.write(0x32); // Start with register 0x32 (ACCEL_XOUT_H)
  4. Wire.endTransmission(false);
  5. Wire.requestFrom(ADXL345, 6, true); // Read 6 registers total, each axis value is stored in 2 registers
  6. X_out = ( Wire.read()| Wire.read() << 8); // X-axis value
  7. X_out = X_out/256; //For a range of +-2g, we need to divide the raw values by 256, according to the datasheet
  8. Y_out = ( Wire.read()| Wire.read() << 8); // Y-axis value
  9. Y_out = Y_out/256;
  10. Z_out = ( Wire.read()| Wire.read() << 8); // Z-axis value
  11. Z_out = Z_out/256;

The output values from the sensor actually depend on the selected sensitivity, which can vary from +- 2g to +-16g. The default sensitivity is +-2g so that’s why we need to divide the output by 256 in order to get values from -1 to +1g. The 256 LSB/g means that we have 256 counts per g.

adxl345 sensitivity range

Depending on the application we can select the appropriate sensitivity. In this case, for tracking orientation, +-2g sensitivity is fine, but for application where we need to sense higher acceleration force from like sudden movements, shocks and so on, we can choose some of the other sensitivity ranges using the DATA_FORMAT register and its D1 and D0 bits.

adxl345 sensitivity range registers and truth table

ADXL345 Acceleromter Calibration

Nevertheless, once we read the data, we can simply print it on the serial monitor to check whether the values are as expected. In my case, the values I was getting were not exactly as they should be, especially the Z-axis which had a noticeable error of 0.1g.

adxl345 accelerometer calibration

To solve this issue, we need to calibrate the accelerometer using the 3 offset calibration registers, and here’s how we can do that. So, we need to position the sensor flat, and print the RAW values without dividing them by 256.

adxl345 accelerometer calibration process

From here now we can notice the how much the outputs are off, in my case, the Z output was around 283. That’s difference of 27 in positive. Now we need to divide this value by 4, and that will give use the number that we need to write to the Z-axis offset register. If we upload the code now, the Z-axis output will be exactly 256, or 1g as it should be.


  1. // This code goes in the SETUP section
  2. // Off-set Calibration
  3. //X-axis
  4. Wire.beginTransmission(ADXL345);
  5. Wire.write(0x1E); // X-axis offset register
  6. Wire.write(1);
  7. Wire.endTransmission();
  8. delay(10);
  9. //Y-axis
  10. Wire.beginTransmission(ADXL345);
  11. Wire.write(0x1F); // Y-axis offset register
  12. Wire.write(-2);
  13. Wire.endTransmission();
  14. delay(10);
  15. //Z-axis
  16. Wire.beginTransmission(ADXL345);
  17. Wire.write(0x20); // Z-axis offset register
  18. Wire.write(-7);
  19. Wire.endTransmission();
  20. delay(10);

If needed we should calibrate the other axis using the same method. And just a quick note that this calibration is not permanently written to the registers. We need to do write these values to the registers at each power up of the sensor.

Once we are done with the calibration, we can now finally calculate the Roll and Pitch, or the rotation around the X-axis and the rotation around the Y axis in degrees, using these two formulas.

  1. // Calculate Roll and Pitch (rotation around X-axis, rotation around Y-axis)
  2. roll = atan(Y_out / sqrt(pow(X_out, 2) + pow(Z_out, 2))) * 180 / PI;
  3. pitch = atan(-1 * X_out / sqrt(pow(Y_out, 2) + pow(Z_out, 2))) * 180 / PI;

For more details how these formulas work, you can check this Freescale Semiconductor application note.

Arduino and ADXL345 Accelerometer Orientation Tracking – 3D Visualization

Ok, let’s make the accelerometer 3D visualization example now.

Arduino and ADXL345 Accelerometer Orientation Tracking - 3D Visualization

So, we are using the same code, which sends the Roll and Pitch values through the serial port. Here’s the complete Arduino code:


  1. /*
  2. Arduino and ADXL345 Accelerometer – 3D Visualization Example
  3. by Dejan, https://howtomechatronics.com
  4. */
  5. #include <Wire.h> // Wire library – used for I2C communication
  6. int ADXL345 = 0x53; // The ADXL345 sensor I2C address
  7. float X_out, Y_out, Z_out; // Outputs
  8. float roll,pitch,rollF,pitchF=0;
  9. void setup() {
  10. Serial.begin(9600); // Initiate serial communication for printing the results on the Serial monitor
  11. Wire.begin(); // Initiate the Wire library
  12. // Set ADXL345 in measuring mode
  13. Wire.beginTransmission(ADXL345); // Start communicating with the device
  14. Wire.write(0x2D); // Access/ talk to POWER_CTL Register – 0x2D
  15. // Enable measurement
  16. Wire.write(8); // Bit D3 High for measuring enable (8dec -> 0000 1000 binary)
  17. Wire.endTransmission();
  18. delay(10);
  19. //Off-set Calibration
  20. //X-axis
  21. Wire.beginTransmission(ADXL345);
  22. Wire.write(0x1E);
  23. Wire.write(1);
  24. Wire.endTransmission();
  25. delay(10);
  26. //Y-axis
  27. Wire.beginTransmission(ADXL345);
  28. Wire.write(0x1F);
  29. Wire.write(-2);
  30. Wire.endTransmission();
  31. delay(10);
  32. //Z-axis
  33. Wire.beginTransmission(ADXL345);
  34. Wire.write(0x20);
  35. Wire.write(-9);
  36. Wire.endTransmission();
  37. delay(10);
  38. }
  39. void loop() {
  40. // === Read acceleromter data === //
  41. Wire.beginTransmission(ADXL345);
  42. Wire.write(0x32); // Start with register 0x32 (ACCEL_XOUT_H)
  43. Wire.endTransmission(false);
  44. Wire.requestFrom(ADXL345, 6, true); // Read 6 registers total, each axis value is stored in 2 registers
  45. X_out = ( Wire.read() | Wire.read() << 8); // X-axis value
  46. X_out = X_out / 256; //For a range of +-2g, we need to divide the raw values by 256, according to the datasheet
  47. Y_out = ( Wire.read() | Wire.read() << 8); // Y-axis value
  48. Y_out = Y_out / 256;
  49. Z_out = ( Wire.read() | Wire.read() << 8); // Z-axis value
  50. Z_out = Z_out / 256;
  51. // Calculate Roll and Pitch (rotation around X-axis, rotation around Y-axis)
  52. roll = atan(Y_out / sqrt(pow(X_out, 2) + pow(Z_out, 2))) * 180 / PI;
  53. pitch = atan(-1 * X_out / sqrt(pow(Y_out, 2) + pow(Z_out, 2))) * 180 / PI;
  54. // Low-pass filter
  55. rollF = 0.94 * rollF + 0.06 * roll;
  56. pitchF = 0.94 * pitchF + 0.06 * pitch;
  57. Serial.print(rollF);
  58. Serial.print(“/”);
  59. Serial.println(pitchF);
  60. }

Now in the Processing development environment we need to receive these values and use them to rotate the 3D object that we will create. Here’s the complete Processing code:


  1. /*
  2. Arduino and ADXL345 Accelerometer – 3D Visualization Example
  3. by Dejan, https://howtomechatronics.com
  4. */
  5. import processing.serial.*;
  6. import java.awt.event.KeyEvent;
  7. import java.io.IOException;
  8. Serial myPort;
  9. String data=””;
  10. float roll, pitch;
  11. void setup() {
  12. size (960, 640, P3D);
  13. myPort = new Serial(this, “COM8”, 9600); // starts the serial communication
  14. myPort.bufferUntil(‘\n’);
  15. }
  16. void draw() {
  17. translate(width/2, height/2, 0);
  18. background(33);
  19. textSize(22);
  20. text(“Roll: ” + int(roll) + ” Pitch: ” + int(pitch), -100, 265);
  21. // Rotate the object
  22. rotateX(radians(roll));
  23. rotateZ(radians(-pitch));
  24. // 3D 0bject
  25. textSize(30);
  26. fill(0, 76, 153);
  27. box (386, 40, 200); // Draw box
  28. textSize(25);
  29. fill(255, 255, 255);
  30. text(“www.HowToMechatronics.com”, -183, 10, 101);
  31. //delay(10);
  32. //println(“ypr:\t” + angleX + “\t” + angleY); // Print the values to check whether we are getting proper values
  33. }
  34. // Read data from the Serial Port
  35. void serialEvent (Serial myPort) {
  36. // reads the data from the Serial Port up to the character ‘.’ and puts it into the String variable “data”.
  37. data = myPort.readStringUntil(‘\n’);
  38. // if you got any bytes other than the linefeed:
  39. if (data != null) {
  40. data = trim(data);
  41. // split the string at “/”
  42. String items[] = split(data, ‘/’);
  43. if (items.length > 1) {
  44. //— Roll,Pitch in degrees
  45. roll = float(items[0]);
  46. pitch = float(items[1]);
  47. }
  48. }
  49. }

Description: So here, we need to include the serial library, define the serial port and the baud rate which needs to match we the baud rate of the uploaded Arduino sketch. Then we read the incoming data and put it into the appropriate roll and pitch variables. In the main draw loop, we use these values to rotate the 3D object, and in this case that’s a simple box with has a particular color and a text on it.

If we run the sketch, the 3D object will appear and it will track the orientation of the accelerometer sensor. We can notice here that the object is actually a bit shaky and that’s because the accelerometer captures not just the gravitational force, but also small forces generated by the movements of our hand. In order to get smoother result, we can use a simple Low-pass filter. Here I implemented such a filter in the Arduino code, which it takes 94% of the previous state and adds 6% of the current state or angle.


  1. // Low-pass filter
  2. rollF = 0.94 * rollF + 0.06 * roll;
  3. pitchF = 0.94 * pitchF + 0.06 * pitch;

With this filter, we can notice that the object moves a lot smoother now, but there is also a side effect and that’s slower responsiveness. We can also notice that we are missing the Yaw, or rotation around the Z-axis. Using only the 3-axis accelerometer data we are not able to calculate the Yaw.

In order to do that and improve the overall performance of our orientation tracking sensor, we actually need to include an additional sensor, a gyroscope, and fuse its data with the accelerometer.

adxl345 accelerometer and l3g4200d gyrscope or mpu6050 6dof module

So, we can either use the ADXL345 accelerometer in combination some gyroscope sensor, or use the MPU6050 IMU which has both 3-Axis accelerometer and 3-Axis gyroscope integrated on a single chip. You can find more detailed tutorial on this sensor in my next video.

I hope you enjoyed this tutorial and learned something new. Feel free to ask any question in the comments section below and don’t forget to check my collection of Arduino Projects.

Powered by WPeMatico

Share on facebook
Facebook
Share on twitter
Twitter
Share on reddit
Reddit
Share on linkedin
LinkedIn
Share on email
Email