ADXL345 Quickstart Guide


Description:

The ADXL345 is a small, thin, low power, 3-axis accelerometer with high resolution (13-bit) measurement at up to ±16 g. Digital output data is formatted as 16-bit twos complement and is accessible through either a SPI (3- or 4-wire) or I2C digital interface. The ADXL345 is well suited to measure the static acceleration of gravity in tilt-sensing applications, as well as dynamic acceleration resulting from motion or shock. Its high resolution (4 mg/LSB) enables measurement of inclination changes less than 1.0°. Several special sensing functions are provided. Activity and inactivity sensing detect the presence or lack of motion and if the acceleration on any axis exceeds a user-set level. Tap sensing detects single and double taps. Free-fall sensing detects if the device is falling. These functions can be mapped to one of two interrupt output pins. An integrated, patent pending 32-level first in, first out (FIFO) buffer can be used to store data to minimize host processor intervention. Low power modes enable intelligent motion-based power management with threshold sensing and active acceleration measurement at extremely low power dissipation.

Features:

  •     2.0-3.6VDC Supply Voltage
  •     Ultra Low Power: 40uA in measurement mode, 0.1uA in standby@ 2.5V
  •     Tap/Double Tap Detection
  •     Free-Fall Detection
  •     SPI and I2C interfaces

Downloads:

Hooking it Up:

This section of the guide illustrates how to connect an Arduino to the ADXL345 breakout board. The following is a table describing which pins on the Arduino should be connected to the pins on the accelerometer:

Arduino Pin ADXL345 Pin
10 CS
11 SDA
12 SDO
13 SCL
3V3 VCC
Gnd

GND

Here is a diagram in case you like pictures!

http://sparkfun.com/tutorial/ADXL/ADXL_Hookup.png

Beginner Sample Code:

Let's look at a sketch to get the ADXL345 up and running with an Arduino. You can download the sketch in its entirety here. Below we'll examine what the different sections of this code does.

         
//Add the SPI library so we can communicate with the ADXL345 sensor
#include <SPI.h>

//Assign the Chip Select signal to pin 10.
int CS=10;

//This is a list of some of the registers available on the ADXL345.
//To learn more about these and the rest of the registers on the ADXL345, read the datasheet!
char POWER_CTL = 0x2D;    //Power Control Register
char DATA_FORMAT = 0x31;
char DATAX0 = 0x32;   //X-Axis Data 0
char DATAX1 = 0x33;   //X-Axis Data 1
char DATAY0 = 0x34;   //Y-Axis Data 0
char DATAY1 = 0x35;   //Y-Axis Data 1
char DATAZ0 = 0x36;   //Z-Axis Data 0
char DATAZ1 = 0x37;   //Z-Axis Data 1

//This buffer will hold values read from the ADXL345 registers.
char values[10];
//These variables will be used to hold the x,y and z axis accelerometer values.
int x,y,z;

This is the initialization section of the sketch. It's pretty basic really. Here's what's happening.

  1. The SPI.h library is added to the sketch. SPI is a communication protocol used by Arduino to communicate to the ADLX345.
  2. A variable named CS is created to store the pin number of the Chip Select signal.
  3. Variables are created for several of the ADXL345 registers. These variables store the address of the indicated registers and are used to set and retrieve values from the accelerometer. The datasheet shows all of the registers available on the ADXL345 and their addresses.
  4. Variables are created that will hold information that has been retrieved from the accelerometer.
         
void setup(){ 
  //Initiate an SPI communication instance.
  SPI.begin();
  //Configure the SPI connection for the ADXL345.
  SPI.setDataMode(SPI_MODE3);
  //Create a serial connection to display the data on the terminal.
  Serial.begin(9600);
  
  //Set up the Chip Select pin to be an output from the Arduino.
  pinMode(CS, OUTPUT);
  //Before communication starts, the Chip Select pin needs to be set high.
  digitalWrite(CS, HIGH);
  
  //Put the ADXL345 into +/- 4G range by writing the value 0x01 to the DATA_FORMAT register.
  writeRegister(DATA_FORMAT, 0x01);
  //Put the ADXL345 into Measurement Mode by writing 0x08 to the POWER_CTL register.
  writeRegister(POWER_CTL, 0x08);  //Measurement mode  
}

void loop(){
  //Reading 6 bytes of data starting at register DATAX0 will retrieve the x,y and z acceleration values from the ADXL345.
  //The results of the read operation will get stored to the values[] buffer.
  readRegister(DATAX0, 6, values);

  //The ADXL345 gives 10-bit acceleration values, but they are stored as bytes (8-bits). To get the full value, two bytes must be combined for each axis.
  //The X value is stored in values[0] and values[1].
  x = ((int)values[1]<<8)|(int)values[0];
  //The Y value is stored in values[2] and values[3].
  y = ((int)values[3]<<8)|(int)values[2];
  //The Z value is stored in values[4] and values[5].
  z = ((int)values[5]<<8)|(int)values[4];
  
  //Print the results to the terminal.
  Serial.print(x, DEC);
  Serial.print(',');
  Serial.print(y, DEC);
  Serial.print(',');
  Serial.println(z, DEC);      
  delay(10); 
}

The main section of the sketch is split into two parts: the setup and the loop. The setup section is used to configure different aspects of the Arduino to communicate with the ADXL345. At the end of the setup section two functions are used to put the accelerometer into the mode we want to use (+/-4g detection, and enable measurement mode). The functions will be discussed in the next section of the sketch.

The loop does most of the work in the sketch. Here's what's going on in the loop:

  1. Arduino reads 6 register values from the ADXL345 using the readRegister function. This command reads the x, y and z acceleration values from the accelerometer and stores them in the buffer named "values" that was created in the initialization section of the sketch.
  2. When values are read from the accelerometer they are returned in bytes. A byte is only 8 bits, but the accelerometer reports acceleration with up to 10 bits! In order to see the correct acceleration value we have to concatenate two bytes together. The x, y and z acceleration values are determined by concatenating the bytes from the values buffer.
  3. The acceleration values are printed to the serial terminal
  4. The sketch waits for 10ms before executing the loop again. This will allow the loop to run at roughly 100 hz.
 
//This function will write a value to a register on the ADXL345.
//Parameters:
//  char registerAddress - The register to write a value to
//  char value - The value to be written to the specified register.
void writeRegister(char registerAddress, char value){
  //Set Chip Select pin low to signal the beginning of an SPI packet.
  digitalWrite(CS, LOW);
  //Transfer the register address over SPI.
  SPI.transfer(registerAddress);
  //Transfer the desired register value over SPI.
  SPI.transfer(value);
  //Set the Chip Select pin high to signal the end of an SPI packet.
  digitalWrite(CS, HIGH);
}

//This function will read a certain number of registers starting from a specified address and store their values in a buffer.
//Parameters:
//  char registerAddress - The register addresse to start the read sequence from.
//  int numBytes - The number of registers that should be read.
//  char * values - A pointer to a buffer where the results of the operation should be stored.
void readRegister(char registerAddress, int numBytes, char * values){
  //Since we're performing a read operation, the most significant bit of the register address should be set.
  char address = 0x80 | registerAddress;
  //If we're doing a multi-byte read, bit 6 needs to be set as well.
  if(numBytes > 1)address = address | 0x40;
  
  //Set the Chip select pin low to start an SPI packet.
  digitalWrite(CS, LOW);
  //Transfer the starting register address that needs to be read.
  SPI.transfer(address);
  //Continue to read registers until we've read the number specified, storing the results to the input buffer.
  for(int  SPI.transfer(0x00);
  }
  //Set the Chips Select pin high to end the SPI packet.
  digitalWrite(CS, HIGH);
}

The readRegister command is used to read values from the ADXL345. When using the readRegister function three variables must be given to the function: the registerAddress where reading should start from, the number of registers that should be read in sequence, and the buffer where the values should be stored. When reading a register from the ADXL345 the address must be modified so that the 8th bit is set high. If more than 1 register is to be read the 7th bit must also be set high. The function accomplishes this task in the first two lines. After that the function is very similar to the writeRegister function. The CS pin is set low to start an SPI sequence, then the address is transferred. After that a loop is used to read the specified number of registers from the accelerometer and the values are stored in the specified buffer. Finally the CS pin is set high to end the SPI sequence and the function is complete.

Once you've hooked the ADXL345 up to an Arduino as specified in the Hook Up section of the guide, download the sketch. After opening the sketch in the Arduino IDE just load it to your board and open the terminal from Arduino. You'll see the X,Y and Z acceleration values start scrolling in the terminal window.

Advanced Sample Code:

Once you understand how to read and write from registers to the ADXL345 there are some really cool functions that you might want to experiment with. In this section we'll look at a sketch that can detect single and double taps, and will convert the x,y and z acceleration values to Gs (a unit of measurement corresponding to 1g). This project will use both an Arduino sketch to interface with the ADXL345 and a Processing sketch so that we can interpret the data. You can download the Arduino sketch here and the Processing sketch here. In order for the sketch to work properly you'll need to add a wire from the INT1 pin on the ADXL345 to pin D2 on the Arduino. Let's look at some of the special parts of this sketch.

 
//Create an interrupt that will trigger when a tap is detected.
  attachInterrupt(0, tap, RISING);
  
  //Put the ADXL345 into +/- 4G range by writing the value 0x01 to the DATA_FORMAT register.
  writeRegister(DATA_FORMAT, 0x01);

  //Send the Tap and Double Tap Interrupts to INT1 pin
  writeRegister(INT_MAP, 0x9F);
  //Look for taps on the Z axis only.
  writeRegister(TAP_AXES, 0x01);
  //Set the Tap Threshold to 3g
  writeRegister(THRESH_TAP, 0x38);
  //Set the Tap Duration that must be reached
  writeRegister(DURATION, 0x10);
  
  //100ms Latency before the second tap can occur.
  writeRegister(LATENT, 0x50);
  writeRegister(WINDOW, 0xFF);
  
  //Enable the Single and Double Taps.
  writeRegister(INT_ENABLE, 0xE0);  
  
  //Put the ADXL345 into Measurement Mode by writing 0x08 to the POWER_CTL register.
  writeRegister(POWER_CTL, 0x08);  //Measurement mode
  readRegister(INT_SOURCE, 1, values); //Clear the interrupts from the INT_SOURCE register.

This code has been added to the setup() section of the sketch. It's a bit different from the setup() section of the ADXL345_Basic example because we're setting the ADXL345 up to detect a single and double tap event. When the accelerometer detects a tap or a double tap we want the INT1 pin to go high, then when the event is handled the INT1 pin will go back low.

When the INT1 pin goes high on the ADXL345 we want to the Arduino to generate an interrupt. In order to do this we use the attachInterrupt() function; we tell the function that we want an interrupt to occur on the Arduino Interrupt 0 (pin D2) when the pin goes from low to high, and we want the Arduino to execute the tap function when this occurs. We'll look at the tap() function later on.

After the interrupt is created we need to configure the ADXL345 to recognize single and double tap events. In order to make this configuration we must change the values in the following registers: INT_MAP, TAP_AXES, THRESH_TAP, DURATION, LATENT and WINDOW. You can read the 'TAP DETECTION' section of the datasheet to see why these values were assigned to their registers. Basically, though, the ADXL345 has been configured so that if a single or double tap is detected the INT1 pin will go from low to high; once the interrupt is handled the INT1 pin will go back low.

 
//Convert the accelerometer value to G's. 
  //With 10 bits measuring over a +/-4g range we can find how to convert by using the equation:
  // Gs = Measurement Value * (G-range/(2^10)) or Gs = Measurement Value * (8/1024)
  xg = x * 0.0078;
  yg = y * 0.0078;
  zg = z * 0.0078;

In the final project we'll want two types of outputs. If a single tap is detected the Arduino will output the raw accelerometer values for x,y and z; just like in the ADXL345_Basic example. However if the ADXL345 detects a double tap we want the Arduino to output the actual G values of the x,y and z axis. In order to find the G values from the raw accelerometer data we must scale the original values. To find the scale we need to know two things: the number of bits used to represent the original values and the range of acceleration values that can be represented. The ADXL345 is set up to measure the values with 10 bits. We've set the range to +/- 4 g, or an 8g range. The scale is found by dividing the total range by the number that can be represented in 10 bits.

http://sparkfun.com/tutorial/ADXL/ADXLScale.jpg

Once we know the scale all we have to do is multiply the raw accelerometer data by the scale to find the number of gs. The variables xg, yg and zg are float variables so that they can hold decimal numbers.

 
if(tapType > 0)
  {
    if(tapType == 1){
      Serial.println("SINGLE");
      Serial.print(x);
      Serial.print(',');
      Serial.print(y);
      Serial.print(',');
      Serial.println(z);
    }
    else{
      Serial.println("DOUBLE");
      Serial.print((float)xg,2);
      Serial.print("g,");
      Serial.print((float)yg,2);
      Serial.print("g,");
      Serial.print((float)zg,2);
      Serial.println("g");
    }
    detachInterrupt(0);
    delay(500);
    attachInterrupt(0, tap, RISING);
    intType=0;    
  }

When the tap() interrupt function runs (we'll cover this next) it assigns a value to the tagType variable. The variable is assigned '1' if a single tap was detected and '2' if a double tap was detected. If a single tap was detected the Arduino will write the word "SINGLE" to the terminal followed by the x,y and z accelerometer values. If a double tap was detected Arduino will write the word "DOUBLE" to the terminal followed by the g values for the x,y and z axis. After printing the values to the terminal we disable the interrupt for a little while; this prevents the Arduino from detecting any 'echoed' interrupts that may occur while the ADXL345 is still vibrating from the tap event.

 
void tap(void){
  //Clear the interrupts on the ADXL345
  readRegister(INT_SOURCE, 1, values); 
  if(values[0] & (1<<5))tapType=2;
  else tapType=1;;
}

Because of the way the attachInterrupt() function was called the tap() function will be used whenever Arduino sees an interrupt occur on pin D2. This function is fairly straightforward: Arduino reads the INT_SOURCE register on the ADXL345 and stores the value in the 'values' buffer. The INT_SOURCE register tells us if the interrupt came from a single tap or a double tap event. Depending on which kind of tap set of the interrupt we assign the tapType variable with a 1 or a 2.

Download the Arduino sketch and the Processing sketch. After you've connected the ADXL345 to the Arduino as specified in the Hooking It Up section, and added a wire from the INT1 pin on the ADXL345 to pin D2 on the Arduino, open Arduino and download the sketch to your board. Then open processing and run the ADXL345_Advanced processing sketch. You may have to change the serial port in the sketch to reflect which port your Arduino is plugged into. If everything has been done correctly the Processing window will output the words "Single Tap" to the screen along with the raw accelerometer values when you tap the ADXL345; likewise "Double Tap" and the G values will be displayed when you double tap the ADXL345.

Comments 30 comments

  • Do you have any i2c code examples for the ADXL345?

  • I’m having an issue with the advanced code. It keeps displaying the state of the interrupt and the accelerometer readings every time the loop runs. If I double tap it switches and continues to incrementally display. For example DOUBLE 0.06g,0.02g,0.98g DOUBLE 0.10g,0.12g,0.91g SINGLE 12,9,124 SINGLE 2,-1,46 ETC to infinity and beyond.

    This happens when running the code from this page in the digital configuration and it also does it when i have the board in the analog configuration. Is something wrong with my arduino?

  • Hi, When I am compiling the advanced code in which attachInterrupt() is used. I am getting the error and that error is : tap was not declared in the scope. And second error is tapType is not defined. Please, if somebody know the answer of these questions , so please tell me or share the correct code.

    Geetanjali

  • Hi, I’m using an Arduino Uno, with Arduino IDE, and I don’t understand how to use 2 sketch’s (Arduino sketch and the Processing sketch), compile Arduino then Processing?

    Best regards.

  • Im also interested how the read function in the beginners example is supposed to work, anyone who can explain perhaps?

    It reads: //Continue to read registers until we’ve read the number specified, storing the results to the input buffer. for(int SPI.transfer(0x00); }

  • Confusion re. Beginner Sample Code:

    1. How does the Arduino know to read/write pins 11 & 12? Only the CS (pin 10) is mentioned in code.
    2. Is the SDO pin needed in this example? Since it is using SDI 3-Wire, which means SDA is Serial Data Input and Output, according to specs.

    Newb, sorry if bonehead questions. Thx.

    • 11, 12, and 13 are the SPI pins on the ATMega328. When you use the SPI library it knows by default to use these pins. Also, the naming of the pins can be a bit confusing, this board will communicate over SPI or I2C, SDA is the I2C pin name, so if you were using I2C (on A4 and A5), you would only need SDA and SCL, but your code would be different as well.

  • I’ve implemented the simple sample code, and I’m getting some strange readings. As I approach +1g on any axis, things work normally until I’m really close, with readings approaching +1g. Then it will instantly start to give me a reading around -1g instead of the +1g I was expecting. I can’t find anything in the datasheet that would explain this behaviour, in fact on page 22, it says that I should expect a +1g reading when oriented one way, and -1g when oriented the other.

    Any ideas, guys?

    • If you look on page 18 of the Data sheet the data is in 2’s complement… This might explain it?

  • I am trying to use the Calibration Offset Registers on the ADXL345. Do you have to re-calibrate the offset if you change the range setting (e.g. from +-16g to +-2g)?

  • In your beginner code, in the readRegister function, I am seeing this:

    //Continue to read registers until we’ve read the number specified, storing the results to the input buffer.

    for(int i = 0; i < numB SPI.transfer(0x00);

    }

    //Set the Chips Select pin high to end the SPI packet.

    surely that for loop is not correct?

    • It reads:

      //Continue to read registers until we’ve read the number specified, storing the results to the input buffer. for(int SPI.transfer(0x00); }

      And I’m also scratching my head over that one. I certainly won’t compile.

  • Are there any additional considerations that need to be made when using an Arduino Pro-mini instead of the Arduino Duemilanove (as this tutorial appears to use)?

  • Line 130 of the full code, “intType=0;” shouldn’t it be “tapType=0;”? “intType” was not declared anywhere and gave me error..

  • Hey,

    I would really appreciate it if someone could let me know why this code does not work with xbee communications…..is it because :

    The TX and RX are occupied with the use of the accelerometer

    Thankyou

    • It does work with Xbee, there could be many reasons why it is not working for you. Also, accelerometers do not occupy Tx or Rx.

      I recommend checking your Xbee connection to Arduino, and check to see if both Xbee devices have same PAN number and configuration.

  • I’m trying to hook-up two ADXL345’s to a single ArduinoBT at the same time. Does anyone know what pins to use and how to change the above code to account for the second sensor?

    • If you use the I2C interface (which the above tutorial does not, but is very easy to do using the Arduino’s Wire library), I believe you can alter the I2C address using the SDO pin. See the datasheet for details.

  • Is this in c or c++?

    • The Arduino sketch is C++, but since it was written using the Arduino IDE there are some Arduino specific commands that are included by the IDE at compile time.
      The processing sketch is written in…processing. Processing is it’s own open source programming language.

  • Hi,
    I’ve used both example. The basic one runs very fast.
    But the advance one is MUCH more longer. Is there any way to detect a tap at the same speed than the reading of basic example? otherwise i can’t use that…too long.
    Thanks
    Fred

  • OK, nice!
    How will the G formula look if the Acc is set to full range? Then a feedback for the range is needed?

  • Can you list some example values that should be printing out in the Basic Code example? I want to make sure I am receiving the values correctly.

  • I have been trying to work with the ADXL345 and Arduino Uno to create some type of “fall detection device”. I have been trying to look at the codes here and I can’t find a program to open them.
    What i am trying to do is have the arduino read the data from the ADXL345 and add an algorithm (like a sum vector algorithm svm= sqrt((ax2)+(ay2)+(az2))). With that in mind I wanted to have thresholds set so once the threshold is reached, I would have a piezo transducer sound for a few seconds, and then turn off.
    I feel the advanced arduino code would help using the tap and double tap features. I was wondering if anyone could help me with this as soon as possible.
    Thanks

  • Your “basic Arduino code” at http://sparkfun.com/tutorial/ADXL/ADXL345_Basic.pde
    has a bug.
    You want the values[] array to be of type UNSIGNED CHAR, meaning you must change two declarations as below:
    unsigned char values[10];
    void readRegister(char registerAddress, int numBytes, unsigned char * values){
    otherwise, you are getting the low-order 8 bits interpreted as a signed char (byte) (incorrect) as well as the high-order bits (which are in fact signed). Combining those two gives you garbage whenever the low byte value is greater than 127.

  • ADXL345 Datasheet: Digital I/O: 3.6V “ABSOLUTE MAXIMUM”. Stresses above those listed under Absolute Maximum Ratings may cause permanent damage to the device.
    A 5V Arduino output pin can drive 40 mA current, which is putting stress on the I/O pin internal protection diodes on the ADXL345. Why not at least use a 1k series resistor to limit the current on the 5V outputs from Arduino to about 2 mA. It might save you the cost of another ADXL345 board.

  • Hey there. Would you mind helping me with displaying the three axis on a graph using Processing? The single and double tap stuff is cool, but I want to see these accelerations on a graph, instead of the serial monitor. I’m a newbee, so I’m not sure how to use Processing to accomplish this.
    Any help would be greatly appreciated. Thanks!

  • I notice your not doing any logic level conversion ( 3.3v to 5v ) for the SDA,SCL,SDO pins between the sensor and the Arduino pins.
    Is this because in your example you are using a 3.3v Arduino or because it’s not neccessary for an SPI interface?
    Thanks Marcus

    • I actually used a 5V Arduino in the example. Though this is ‘bad engineering’ it works fine. In the long run it may end up damaging the pins on the ADXL345, but I’ve yet to have this happen to me. If you’re nervous about using 5V signals to communicate with a 3.3V device you can certainly use the logic level shifter board to interface the two boards.