# ADXL345 – The Universal Accelerometer – Part 2

This is part 2 of my article about the ADXL345 accelerometer and my associated library. It builds on the first part, in which I discussed the general features and the first seven sketches.

This part focuses on:

• Activity / Inactivity
• here I also catch up with the Auto Sleep function
• Single and Double Tap
• FIFO functions

Further notes:

• The circuit used for the example sketches is the same as that of the first part.
• Again, you’ll only find the shortened sample sketches. You get the original sketches with the library. They contain considerably more comments, especially the options for the function parameters.
• I found a few bugs after I posted the first part. So install the updates via the Arduino IDE or keep an eye out for the latest version on GitHub!

## Activity – Inactivity – Auto Sleep

The Activity interrupt is triggered when a certain acceleration value is exceeded on the specified axes. The Inactivity interrupt is triggered when the values for the axes involved fall below a certain acceleration value for a certain time.

Both interrupts can be set up independently of each other. Because of their relationship, I treat both in one example sketch. In addition, the effect of the link bit can be best shown this way.

In the last post I covered the interrupts of the ADXL345 in general.

Here I use the following new functions:

• setActivityParameters(mode, axes, g-threshold)
• mode: is DC or AC
• DC: the limit (threshold) applies absolutely.
• AC: the limit is the delta to the starting value.
• axes: the participating axes.
• g-threshold: the limit in g.
• setInactivityParameters(mode, axes, g-threshold, time-threshold)
• mode, axes, g-threshold: as above.
• time-threshold: the time limit in seconds (max. 255).
• setLinkBit(true/false)
• I’ll explain that below.
• getActTapStatusAsString()
• Returns as a string which axis/axes was/were responsible for the Activity interrupt. Also used for the Tap functions.
• getActTapStatus()
• Returns the content of the Act Tap Status register as byte. The meaning of the returned value can be understood from the data sheet and the library files. For advanced users.

In the sketch you will find the permissible parameters. In addition, you need to activate the interrupts with setInterrupt().

For Activity and Inactivity interrupt, I set the DC mode, selected the x and y axis, 0.5 g as the g limit, and chose 10 seconds as the time limit for inactivity.

Since an interrupt can be both an Activity and an Inactivity interrupt, the content of the interrupt register is read with intSource = readAndClearInterrupt() and then checkInterrupt(intSource, interruptType) checks if interruptType was the trigger.

#include<Wire.h>
const int int2Pin = 2;
volatile bool in_activity = false; // in_activity: either activity or inactivity interrupt occured

*/

void setup() {
Wire.begin();
Serial.begin(9600);
pinMode(int2Pin, INPUT);
Serial.println("ADXL345_Sketch - Activity and Inactivity Interrupts");
Serial.println();
if (!myAcc.init()) {
}

/* Insert your data from ADXL345_calibration.ino and uncomment for more precise results */
// myAcc.setCorrFactors(-266.0, 285.0, -268.0, 278.0, -291.0, 214.0);

attachInterrupt(digitalPinToInterrupt(int2Pin), in_activityISR, RISING);

/* Three parameters have to be set for activity:
1. DC / AC Mode:
ADXL345_DC_MODE - Threshold is the defined one (parameter 3)
ADXL345_AC_MODE - Threshold = starting acceleration + defined threshold
2. Axes, that are considered:
ADXL345_000  -  no axis (which makes no sense)
3. Threshold in g
*/

/* Four parameters have to be set for in activity:
1. DC / AC Mode:
see activity parameters
2. Axes, that are considered:
see activity parameters
3. Threshold in g
4. Inactivity period threshold in seconds (max 255)
*/

/* Setting the link bit only makes sense if both activity and inactivity are used
*  If link bit is not set:
*   - activity interrupt can be triggered any time and multiple times
*   - inactivity can be triggered if inactivity param. are met, independent of activity param. (if used)
*  If link bit is set:
*   - activity interrupt can only be triggered after inactivity interrupt
*   - only one activity interrupt can be triggered until the next inactivity interrupt occurs
*/

/* You can choose the following interrupts:
Variable name:             Triggered, if:
ADXL345_WATERMARK    -   the number of samples in FIFO equals the number defined in FIFO_CTL
ADXL345_FREEFALL     -   acceleration values of all axes are below the thresold defined in THRESH_FF
ADXL345_INACTIVITY   -   acc. value of all included axes are < THRESH_INACT for period > TIME_INACT
ADXL345_ACTIVITY     -   acc. value of included axes are > THRESH_ACT
ADXL345_DOUBLE_TAP   -   double tap detected on one incl. axis and various defined conditions are met
ADXL345_SINGLE_TAP   -   single tap detected on one incl. axis and various defined conditions are met

Assign the interrupts to INT1 (INT_PIN_2) or INT2 (INT_PIN_2). Data ready, watermark and overrun are
always enabled. You can only change the assignment of these which is INT1 by default.

You can delete interrupts with deleteInterrupt(type);
*/

}

/* In the main loop some checks are done:
getActTapStatus() returns which axes are responsible activity interrupt as byte (code in library)
getActTapStatusAsString() returns the axes that caused the interrupt as string
readAndClearInterrupts(); return the interrupt type as byte (code in library)
checkInterrupt(intSource, type) returns if intSource is type as bool
*/

void loop() {
if ((millis() % 1000) == 1) {
xyzFloat g = myAcc.getGValues();
Serial.print("g-x   = ");
Serial.print(g.x);
Serial.print("  |  g-y   = ");
Serial.print(g.y);
Serial.print("  |  g-z   = ");
Serial.println(g.z);
}

if(in_activity == true) {
//byte actTapSource = myAcc.getActTapStatus();
//Serial.println(actTapSource, BIN);
String axes = myAcc.getActTapStatusAsString();
Serial.print("Activity at: ");
Serial.println(axes);
}

Serial.println("Inactivity!");
}

delay(1000);
in_activity = false;
}
}

void in_activityISR() {
in_activity = true;
}

#### Output of the Activity / Inactivity Sketch

If the criterion for an activity interrupt is met, it is triggered. Since I have set the limits for Activity and Inactivity equal, an Activity interrupt sets the timer for the Inactivity interrupt back to zero.

Uncomment line 63 (setLinkBit(true)) and restart the sketch. Activity and Inactivity are now coupled. An Activity interrupt – one at a time – can only be triggered if an Inactivity interrupt has been triggered before. Try it best.

Here we now catch up with Auto Sleep mode. When enabled, the ADXL345 goes into Sleep mode when an Inactivity interrupt has been triggered. An Activity interrupt, on the other hand, wakes up the ADXL345. As in normal Sleep mode, the ADXL345 periodically wakes up briefly at a selectable frequency of 1 to 8 Hz even without an Activity interrupt.

For Auto Sleep mode, the link bit must be set. This is done automatically by the library in the background. Accordingly, Activity and Inactivity interrupt are interdependent.

The following features are new:

• setAutoSleep(true/false, frequency)
• true/false is the on/off switch.
• frequency sets the wake-up frequency.
• setAutoSleep(true/false)
• Turn the Auto Sleep function on/off without changing the wake-up frequency.
#include<Wire.h>
const int int2Pin = 2;
volatile bool in_activity = false;

*/

void setup() {
Wire.begin();
Serial.begin(9600);
pinMode(int2Pin, INPUT);
Serial.println();
if (!myAcc.init()) {
}
/* Insert your data from ADXL345_calibration.ino and uncomment for more precise results */
// myAcc.setCorrFactors(-266.0, 285.0, -268.0, 278.0, -291.0, 214.0);

attachInterrupt(digitalPinToInterrupt(int2Pin), in_activityISR, RISING);

/* The following settings are similar to the settings in ADXL345_activity_inactivity_interrupt.ino */

/* Three parameters have to be set for activity:
1. DC / AC Mode:
ADXL345_DC_MODE - Threshold is the defined one (parameter 3)
ADXL345_AC_MODE - Threshold = starting acceleration + defined threshold
2. Axes, that are considered:
ADXL345_000  -  no axis (which makes no sense)
3. Threshold in g
*/

/* Four parameters have to be set for in activity:
1. DC / AC Mode:
see activity parameters
2. Axes, that are considered:
see activity parameters
3. Threshold in g
4. Inactivity period threshold in seconds (max 255)
*/

/* You can choose the following interrupts:
Variable name:             Triggered, if:
ADXL345_WATERMARK    -   the number of samples in FIFO equals the number defined in FIFO_CTL
ADXL345_FREEFALL     -   acceleration values of all axes are below the threshold defined in THRESH_FF
ADXL345_INACTIVITY   -   acc. value of all included axes are < THRESH_INACT for period > TIME_INACT
ADXL345_ACTIVITY     -   acc. value of included axes are > THRESH_ACT
ADXL345_DOUBLE_TAP   -   double tap detected on one incl. axis and various defined conditions are met
ADXL345_SINGLE_TAP   -   single tap detected on one incl. axis and various defined conditions are met

Assign the interrupts to INT1 (INT_PIN_1) or INT2 (INT_PIN_2). Data ready, watermark and overrun are
always enabled. You can only change the assignment of these which is INT1 by default.

You can delete interrupts with deleteInterrupt(type);
*/

/* Auto sleep is connected with activity and inactivity. The device goes in sleep when inactivity is
detected. The link bit must be set, if you want to use auto sleep. The library sets the link bit
automatically. When the ADXL345 goes into sleep mode it wakes up periodically (default is 8 Hz).

Choose the wake up frequency:

*/
// alternative: myAcc.setAutoSleep(true/false) without changing the wake up frequency.
}

void loop() {
if ((millis() % 300) == 1) {
xyzFloat g = myAcc.getGValues();
Serial.print("g-x   = ");
Serial.print(g.x);
Serial.print("  |  g-y   = ");
Serial.print(g.y);
Serial.print("  |  g-z   = ");
Serial.println(g.z);
}

if(in_activity == true) {
Serial.println("Activity!");
if(!myAcc.isAsleep()){        //check the asleep bit
Serial.println("I am awake!");
}
}

Serial.println("Inactivity!");
if(myAcc.isAsleep()){
Serial.println("I am sleeping...");
}
}

in_activity = false;
}
}

void in_activityISR() {
in_activity = true;
}

#### Output of the Auto Sleep sketch

Below you can see the excerpt of an output. I have moved the ADXL345 so that the acceleration values were below the activity limit. You can see that the ADXL345 is really asleep by the fact that the measured values are only updated once per second in Sleep mode (as with the Sleep example sketch). Since the values are retrieved every 300 ms, 3-4 consecutive values are identical.

## Single and Double Tap

A Single Tap event is basically an activity event, but with a defined maximum duration (DUR). If the event is longer than DUR, it is not considered a single tap. For example, vibrations can be distinguished from a longer acceleration. The next tap can only be detected after a latency period (latent) has elapsed. The latency starts after falling below the threshold.

A Double Tap consists of two Single Taps with same parameters. The additional condition is that the second tap must take place in a time window that starts after the latency of the first tap has expired. Sounds complicated, but it’s not when you look at it graphically:

For Single and Double tap detection you need relatively high measuring frequencies. For example, it would make no sense to specify a value for DUR in the millisecond range, but record measured values in the seconds range. That has to fit together.

You can set various parameters for the Single and Double Tap. The parameters that apply to both the Single and the Double Tap are defined by the following function:

• setGeneralTapParameters(axes, g-threshold, duration, latent)
• Axes are again the axes involved.
• g-threshold is the limit in g (this time without DC/AC mode).
• Vibrations usually cause high acceleration values and produce “echoes”, so the data sheet recommends 3 g as a starting value.
• duration is the maximum duration of the tap in milliseconds. The datasheet recommends values greater than 10 ms. The maximum is 159 ms.
• latent is the latency in milliseconds. It should be larger than 20 ms. The maximum is 318 ms.

You already know the function getActTapStatusAsString() from ADXL345_activity_inactivity.ino. Here it now returns the axis responsible for the tap interrupt or, if necessary, several.

#include<Wire.h>
const int int2Pin = 2;
volatile bool tap = false;

*/

void setup() {
Wire.begin();
Serial.begin(9600);
pinMode(int2Pin, INPUT);
Serial.println();
if (!myAcc.init()) {
}

/* Insert your data from ADXL345_calibration.ino and uncomment for more precise results */
// myAcc.setCorrFactors(-266.0, 285.0, -268.0, 278.0, -291.0, 214.0);

attachInterrupt(digitalPinToInterrupt(int2Pin), tapISR, RISING);

/* The following four parameters have to be set for tap application (single and double):
1. Axes, that are considered:
ADXL345_000  -  no axis (which makes no sense)
2. Threshold in g
It is recommended to not choose the value to low. 3g is a good starting point.
3. Duration in milliseconds (max 159 ms):
maximum time that the acceleration must be over g threshold to be regarded as a single tap. If
the acceleration drops below the g threshold before the duration is exceeded an interrupt will be
triggered. If also double tap is active an interrupt will only be triggered after the double tap
conditions have been checked. Duration should be greater than 10.
4. Latency time in milliseconds (maximum: 318 ms): minimum time before the next tap can be detected.
Starts at the end of duration or when the interrupt was triggered. Should be greater than 20 ms.
*/
}

/* In the main loop some checks are done:
getActTapStatus() returns which axes are resposible activity interrupt as byte (code in library)
getActTapStatusAsString() returns the axes that caused the interrupt as string
readAndClearInterrupts(); return the interrupt type as byte (code in library)
checkInterrupt(intSource, type) returns if intSource is type as bool
*/
void loop() {
if ((millis() % 1000) == 1) {
xyzFloat g = myAcc.getGValues();
Serial.print("g-x   = ");
Serial.print(g.x);
Serial.print("  |  g-y   = ");
Serial.print(g.y);
Serial.print("  |  g-z   = ");
Serial.println(g.z);
}

if(tap == true) {
//byte actTapSource = myAcc.getActTapStatus();
//Serial.println(actTapSource, BIN);
String axes = myAcc.getActTapStatusAsString();
Serial.print("TAP at: ");
Serial.println(axes);
}

delay(1000);
tap = false;
}
}

void tapISR() {
tap = true;
}

#### Output of the Single Tap sketch

For the output below, I first tapped the module several times in the direction of the x-axis and then in the direction of the y-axis. By the way, the measurement output has no deeper meaning. That’s just to make it not so boring in between.

For Double Tap detection, you first define the general parameters as for the Singles Taps. Then you set two more parameters for the Double Taps:

• setAdditionalDoubleTapParameters(suppress, window)
• suppress is true or false and determines whether the suppress bit is set. If the suppress bit is set, then a second tap within the latency of the first tap invalidates the Double Tap condition. All right? If not, then go to the data sheet. There is a graphic on page 29 which should make things clearer.
• window sets the time window in which the second tap must take place to be considered a double tap. The maximum is 318 ms.

In the example sketch I activated both the Single and the Double Tap Interrupt. Since there can be two different interrupts, it is necessary to check which interrupt was triggered.

The purpose of Single and Double tap detection is to look for characteristic shocks or vibrations. However, this requires a lot of trying out. Just play with the parameters.

#### Output of the Double Tap sketch

This is what an output might look like:

In this example, I tapped the ADXL345 twice in quick succession in the direction of the x-axis.

## FIFO Functions

The FIFO buffer is a kind of recording device for measured values. This allows you to track very fast events at a measurement frequency of 3200 Hz, for example. Or you can analyze what happens before, around or after a particular event (activity or tap).

The FIFO has space for up to 32 readings, i.e. 32 full x,y,z data sets. You can select the number. FIFO stands for “first in, first out”, i.e. the measured values that are stored first are read first. Reading of the FIFO works like normal reading of measured values. However, the data registers are not supplied with fresh readings, but from the FIFO.

There are four modes of operation:

• Bypass: FIFO is disabled, which is the default. The measured values are provided in the data register and new measurements overwrite the old ones.
• FIFO: you give the starting signal to fill the FIFO. If the buffer contains the number of values you have defined, the recording ends. The ADXL345 triggers the Watermark interrupt. It is a bit confusing that one of the FIFO modes is also called FIFO.
• Stream: as the name suggests, values are permanently written to the buffer. When the FIFO is full, new values are pushed in and the oldest ones fall out. You define the end of the measured value recording.
• Trigger: Measured values are permanently recorded as in Stream mode. If a selected event (= trigger, interrupt) occurs, only the number of values you have defined remains in the FIFO. Then, however, the FIFO is filled up with new readings up to 32. Then the process ends. So, you can set the middle of a measured value recording in the Trigger mode.

The following new functions are used:

• setFifoParameters(TriggerIntPin, number)
• The TriggerIntPin only comes into play for the trigger mode. In this example, it doesn’t matter which parameter you choose.
• number sets the maximum number of measured values in the FIFO.
• setFifoModes(mode)
• With mode, you set – guess what – the FIFO mode.

The setting of the FIFO mode is simple. The other settings that make sure that the whole thing works are a bit more complex. In particular, interrupts offer many possibilities for errors. It is important to follow the correct sequence.

A few notes for example:

• I set the measuring frequency at 6.25 Hz quite low. With 32 measured values it takes about 5 seconds until the FIFO is full. Enough time to tilt the ADXL345 slowly, for example.
• I deactivated all measurements until the beginning of the FIFO recording.
• The measurements are started with a countdown. Of course, you can also take something different as a “starting shot”, for example an interrupt.
• The Watermark interrupt is assigned to INT2. It signals the end of the measurement.
• After this I output 34 x,y,z value triple. This is more than the FIFO buffer contains. I just want to show that nothing changes after the 32nd value.
#include<Wire.h>
const int int2Pin = 2;
volatile bool event = false;

*/

void setup() {
Wire.begin();
Serial.begin(9600);
pinMode(int2Pin, INPUT);
Serial.println("ADXL345_Sketch - FIFO - Fifo Mode");
Serial.println();
if (!myAcc.init()) {
}

/* Insert your data from ADXL345_calibration.ino and uncomment for more precise results */
// myAcc.setCorrFactors(-266.0, 285.0, -268.0, 278.0, -291.0, 214.0);

/* Stop the measure mode - no new data will be obtained */
myAcc.setMeasureMode(false);
attachInterrupt(digitalPinToInterrupt(int2Pin), eventISR, RISING);

/* You can choose the following interrupts:
Variable name:             Triggered, if:
ADXL345_WATERMARK    -   the number of samples in FIFO equals the number defined in FIFO_CTL
ADXL345_FREEFALL     -   acceleration values of all axes are below the threshold defined in THRESH_FF
ADXL345_INACTIVITY   -   acc. value of all included axes are < THRESH_INACT for period > TIME_INACT
ADXL345_ACTIVITY     -   acc. value of included axes are > THRESH_ACT
ADXL345_DOUBLE_TAP   -   double tap detected on one incl. axis and various defined conditions are met
ADXL345_SINGLE_TAP   -   single tap detected on one incl. axis and various defined conditions are met

Assign the interrupts to INT1 (INT_PIN_1) or INT2 (INT_PIN_2). Data ready, watermark and overrun are
always enabled. You can only change the assignment of these which is INT1 by default.

You can delete interrupts with deleteInterrupt(type);
*/
myAcc.setInterrupt(ADXL345_WATERMARK, INT_PIN_2); // Interrupt when FIFO is full

/* The following two FIFO parameters need to be set:
1. Trigger Bit: not relevant for this FIFO mode.
2. FIFO samples (max 32). Defines the size of the FIFO. One sample is a set of an x,y,z triple.
*/

/* You can choose the following FIFO modes:
ADXL345_FIFO     -  you choose the start, ends when FIFO is full (at defined limit)
ADXL345_STREAM   -  FIFO always filled with new data, old data replaced if FIFO is full; you choose the stop
ADXL345_TRIGGER  -  FIFO always filled up to 32 samples; when the trigger event occurs only defined number of samples
is kept in the FIFO and further samples are taken after the event until FIFO is full again.
*/
}

void loop() {
event = false;

countDown(); // as an alternative you could define any other event to start to fill the FIFO
myAcc.setMeasureMode(true); // this is the actual start
while(!event){}  // event = true means WATERMARK interrupt triggered -> means FIFO is full
myAcc.setMeasureMode(false);
Serial.println("FiFo full");

for(int i=0; i<34; i++){ // this is > 32 samples, but I want to show that the values do not change when FIFO is full
xyzFloat g = myAcc.getGValues();

Serial.print("g-x   = ");
Serial.print(g.x);
Serial.print("  |  g-y   = ");
Serial.print(g.y);
Serial.print("  |  g-z   = ");
Serial.println(g.z);
}
Serial.println("For another series of measurements, enter any key and send");

while(!(Serial.available())){}
Serial.println();
}

void eventISR() {
event = true;
}

void countDown(){
Serial.println();
delay(1000);
Serial.print("Fifo collection begins in 3, ");
delay(1000);
Serial.print("2, ");
delay(1000);
Serial.print("1, ");
delay(1000);
Serial.println("Now!");
}

#### Output of the FIFO FIFO Sketch

For the output shown below, I slowly rotated the ADXL345 around the y-axis (i.e. the x and z values vary).

The example sketch does not use any new functions. But a few comments:

• setFifoMode(ADXL345_STREAM) sets the stream mode.
• As the event that ends the measurement recording, I have selected an Activity interrupt: 0.6 g on the x- or y-axis.
• The 32 measured values before the event occurs are stored in the FIFO.
#include<Wire.h>
const int int2Pin = 2;
volatile bool event = false;

*/

void setup() {
Wire.begin();
Serial.begin(9600);
pinMode(int2Pin, INPUT);
Serial.println("ADXL345_Sketch - FIFO - Stream Mode");
Serial.println();
if (!myAcc.init()) {
}

attachInterrupt(digitalPinToInterrupt(int2Pin), eventISR, RISING);

/* Three parameters have to be set for activity:
1. DC / AC Mode:
ADXL345_DC_MODE - Threshold is the defined one (parameter 3)
ADXL345_AC_MODE - Threshold = starting acceleration + defined threshold
2. Axes, that are considered:
ADXL345_000  -  no axis (which makes no sense)
3. Threshold in g
*/

/* The following two FIFO parameters need to be set:
1. Trigger Bit: not relevant for this FIFO mode.
2. FIFO samples (max 32). Defines the size of the FIFO. One sample is a set of an x,y,z triple.
*/

Serial.println("Waiting for Activity Event - I propose to slowly turn the ADXL345");
}

/* In the main loop some checks are done:
getActTapStatus() returns which axes are responsible for activity interrupt as byte (code in library)
getActTapStatusAsString() returns the axes that caused the interrupt as string
readAndClearInterrupts(); returns the interrupt type as byte (code in library)
checkInterrupt(intSource, type) returns if intSource is type as bool
*/

void loop() {
if(event){
myAcc.setMeasureMode(false);
//byte actTapSource = myAcc.getActTapStatus();
//Serial.println(actTapSource, BIN);
String axes = myAcc.getActTapStatusAsString();
Serial.print("ACT at: ");
Serial.println(axes);
}
printFifo();
Serial.println("For another series of measurements, enter any key and send");
while(!(Serial.available())){}
Serial.println();
Serial.println("Waiting for Activity Event");

event = false;
myAcc.setMeasureMode(true);
}
}

void eventISR() {
event = true;
}

void printFifo(){
for(int i=0; i<32; i++){
xyzFloat g = myAcc.getGValues();

Serial.print("g-x   = ");
Serial.print(g.x);
Serial.print("  |  g-y   = ");
Serial.print(g.y);
Serial.print("  |  g-z   = ");
Serial.println(g.z);
}
}

#### Output of THE FIFO Stream Sketch

Again, I slowly turned the ADXL345 around the y-axis.

And now to the last example sketch. Here, the recording of FIFO data is controlled by a trigger. For this, I come back to the following function:

• setFifoParameters(TriggerIntPin, number)
• TriggerIntPin is set to INT1 or INT2. An interrupt on the selected pin is the trigger for the FIFO recording. You have to select an interrupt that you assign to this pin. Again, I have selected the Activity interrupt.
• number should be somewhere between 1 and 32. If you choose 32, that would be similar to Stream mode. If you choose 1, you might as well choose the FIFO FIFO mode. For this example, I chose 15.

The question arises as to how to detect the completion of the measurements. Basically, this would work with the Watermark interrupt. However, if we assign this to INT2, we may get confused with the Activity interrupt. And if we assign it to INT1, then there may be problems with the Data Ready interrupt, which, stupidly, cannot be turned off. A pragmatic solution would be a simple delay which length would have to be adapted to the measuring frequency. Fortunately, the number of values present in the FIFO can be queried. The getFifoStatus() function returns the content of the FIFO status register. Its bits 0 to 5 contain the number of values in the FIFO. If the bit 5 is set, 25= 32 readings are in the FIFO. This results in:

FIFO is full when (getFifoStatus() & 32) == true.

#include<Wire.h>
const int int2Pin = 2;
volatile bool event = false;

*/

void setup() {
Wire.begin();
Serial.begin(9600);
pinMode(int2Pin, INPUT);
Serial.println("ADXL345_Sketch - FIFO - Trigger Mode");
Serial.println();
if (!myAcc.init()) {
}

attachInterrupt(digitalPinToInterrupt(int2Pin), eventISR, RISING);

/* Three parameters have to be set for activity:
1. DC / AC Mode:
ADXL345_DC_MODE - Threshold is the defined one (parameter 3)
ADXL345_AC_MODE - Threshold = starting acceleration + defined threshold
2. Axes, that are considered:
ADXL345_000  -  no axis (which makes no sense)
3. Threshold in g
*/

/* The following two FIFO parameters need to be set:
1. Trigger Bit: the trigger is an interrupt at INT1 or INT2
ADXL345_TRIGGER_INT_1 - Trigger is an interrupt at INT1
ADXL345_TRIGGER_INT_2 - Trigger is an interrupt at INT2
2. FIFO samples (max 32). Defines the size of the FIFO. One sample is a set of an x,y,z triple.
*/

event = false;

Serial.println("Waiting for Activity Event");
}

/* In the main loop some checks are done:
getActTapStatus() returns which axes are responsible for activity interrupt as byte (code in library)
getActTapStatusAsString() returns the axes that caused the interrupt as string
readAndClearInterrupts(); returns the interrupt type as byte (code in library)
checkInterrupt(intSource, type) returns if intSource is type as bool
*/
void loop() {

if(event){
String axes = myAcc.getActTapStatusAsString();
Serial.print("Activity at: ");
Serial.println(axes);
}
while(!(myAcc.getFifoStatus() & 32)){}
printFifo();

Serial.println("For another series of measurements, enter any key and send");
while(!(Serial.available())){}
Serial.println();
myAcc.resetTrigger();
Serial.println("Waiting for Activity Event");
event = false;
}
}

void eventISR() {
event = true;
}

void printFifo(){
for(int i=0; i<32; i++){
xyzFloat g = myAcc.getGValues();

Serial.print("g-x   = ");
Serial.print(g.x);
Serial.print("  |  g-y   = ");
Serial.print(g.y);
Serial.print("  |  g-z   = ");
Serial.println(g.z);
}
}

#### Output of the FIFO Trigger Sketch

For the output below, I rotated the ADXL345 around the y-axis until the activity interrupt was triggered and then turned it back. You can see that the measured value responsible for the Activity interrupt is in the middle of the FIFO values.

At this measurement frequency, this is just a gimmick. It becomes interesting if you change to high measuring frequencies and analyze a tap event in more detail, for example.

## Final words

Creating the library and example sketches was a pretty tough job due to the versatility of this component. I hope it was worth it and some people use the library. Bugs are not only not excluded in such a project, but almost inevitable. If you discover something or if you miss features, please get in touch.

## 12 thoughts on “ADXL345 – The Universal Accelerometer – Part 2”

1. Super great job!

Just 2 questions…

According datasheet, use of the 3200 Hz and 1600 Hz output data rates is only recommended with SPI communication rates greater than or equal to 2 MHz (datasheet->page15) Did you take this into account?

I’m trying to guess which is the maximum number of readings that I can get with this module in one second…Could you shed some light on this?

Thank you!

1. Hi, first of all thanks for the feedback. You are right, you won’t be able to get the highest sample rates with I2C. Maybe I should mention this clearly. Not sure where the exact limit is. You can increase it by changing from 100 to 400 kHz using setWireClock(400000). I’ve implemented SPI, so you can change if you need. With SPI you should achieve the maximum as long as you don’t apply slow commands like Serial.print between the sample queries.

2. Do you have a simple sketch to wake a sleeping arduino with the adxl345. I have tried it a few different ways but keep coming up short.

Wonderful job on documenting and explaining your libraries! this has been incredibly helpful!!

1. Thanks for your kind feedback!

And to answer your question: you just need to set up an interrupt on the ADXL side (activity, inactivity, freefall, etc) and an interrupt on the Arduino side. Connect the interrupt pin of the ADXL345 with the interrupt pin of the Arduino. Don’t forget to pull it down (or up for active low setting). Interrupts are a source of many potential bugs! In this specific example it’s very important to clear the interrupt before the Arduino is sent to sleep.

Hope this helps. If you have questions regarding the code please ask!

#include <avr/sleep.h> #include<Wire.h> #include<ADXL345_WE.h> #define ADXL345_I2CADDR 0x53 const int int2Pin = 2; 

 ADXL345_WE myAcc = ADXL345_WE(ADXL345_I2CADDR); void setup(){ Wire.begin(); Serial.begin(9600); delay(100); if (!myAcc.init()) { Serial.println("ADXL345 not connected!"); } pinMode(int2Pin, INPUT); attachInterrupt(digitalPinToInterrupt(int2Pin), activityISR, RISING); myAcc.setActivityParameters(ADXL345_DC_MODE, ADXL345_XY0, 0.5); myAcc.setInterrupt(ADXL345_ACTIVITY, INT_PIN_2); } void loop(){ Serial.println("Going to sleep in 2 seconds..."); delay(2000); myAcc.readAndClearInterrupts(); set_sleep_mode(SLEEP_MODE_PWR_DOWN); sleep_mode(); // sleeping now and waiting for interrupt Serial.println("I woke up!"); } void activityISR(){ 

}

1. WOW! THANKS!

I was definitely over complicating it! haha!

If I have more questions I will reach out.

THANK YOU SO MUCH!!!

3. Hello,
How do I get the .h and .c files (library?):
#include
#include

Thank you,
Traian Morar

1. Dear Traian,
the easiest ways to get the library are:
1) If you use the Arduino IDE, go to the library manager. There you search for ADXL345_WE. Then click on install.
or:
2) Go to GitHub with this link:
There is a green button (“Code”). Click on it and choose download as ZIP. Extract the Zip file in your library folder.

If you still have problems, then google “install Arduino libraries ” or similar – There are lots of tutorials.
Good luck and have fun!
Best wishes, Wolfgang

4. Thank you for a well written article. The explanation off the interrupts was very good and made me switch from the 5060 to this module.
Which sample should I use to make the detection in one direction possible like the detection of braking and acceleration in combination with the interupts?

Thank you,
Peter

1. Hi Peter,
thanks for the feedback. Acceleration interrupts are covered in the example ADXL345_activity_inactivity_interrupt.ino. The key functions are setActivityParameters() and setInterrupt(). Activity interrupt is maybe not the ideal name, acceleration interrupt would be better, but I wanted to use the nomenclature of the data sheet. Have fun with your project!
Wolfgang

5. Hi Wolfgang, thank you so much for this post. Very informative and very clear. I have been trying to read the ADXL345 data sheet or should I say sheets for a long time now and I’ve looked at so many videos, instruction sites and forums that tell you exactly how easy it is but then do not quite show you how it’s done. Your style of instruction is very good and has helped me understand the ADXL345 a lot better. Thank you once again.

1. Thank you so much – this comment is very motivating for me!