Getting dynamic movement with ADXL345

Hello all, I am playing with the ADXL345 accelerometer. I found various sample code on the web and have the sensor working. I have pasted the code I am using below. However, my question is not specifically about the code. Maybe I don't fully understand this device but I read in the datasheet that it will measure static data such as from gravity and dynamic data such as movement. What I am trying to get is acceleration reading when the sensor is moving. For example, if I place it on the roof of my car so "x" axis is facing front and rear and I start driving forward, I would expect to see the data for x moving positive as I accelerate. I understand that if I hit steady/constant speed, this value may drop back to 0 as it is only measuring the acceleration. Then, as I decelerate, I would expect to get a negative number.

However, what I am seeing with the code below is more of a value change when I change the tilt of each axis. For example, if I hold x axis level, I get 0. If I tilt x axis up in the air, I get a positive number. If I tilt it towards ground, I get a negative number. So, it seems to be working and reading out but acting more like a tilt sensor. I assume this is because I am reading out the static gravity data.

When I try to move along the x axis, I don't get data changing. Even if I shake or bump/jolt it. Again, seems to only really change on tilt.

What am I doing wrong here? Is there a register setting I should change to get it to read my movement out?

Thanks!

#include <Wire.h>
#define DEVICE (0x53)    //ADXL345 device address
#define TO_READ (6)        //num of bytes we are going to read each time (two bytes for each axis)

byte buff[TO_READ] ;    //6 bytes buffer for saving data read from the device
char str[512];                      //string buffer to transform data before sending it to the serial port



void setup() {
  // put your setup code here, to run once:
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(9600);  // start serial for output
  
  //Turning on the ADXL345
  writeTo(DEVICE, 0x2D, 0);      
  writeTo(DEVICE, 0x2D, 16);
  writeTo(DEVICE, 0x2D, 8);
  writeTo(DEVICE, 0x31, 8);
}

void loop() {
  // put your main code here, to run repeatedly:
  int regAddress = 0x32;    //first axis-acceleration-data register on the ADXL345
  int16_t x, y, z;
  
  readFrom(DEVICE, regAddress, TO_READ, buff); //read the acceleration data from the ADXL345
  
   //each axis reading comes in 10 bit resolution, ie 2 bytes.  Least Significat Byte first!!
   //thus we are converting both bytes in to one int
  x = (((int)buff[1]) << 8) | (int)buff[0];   
  y = (((int)buff[3])<< 8) | (int)buff[2];
  z = (((int)buff[5]) << 8) | (int)buff[4];
  
  //we send the x y z values as a string to the serial port
  sprintf(str, "%d %d %d", x, y, z);  
  Serial.print(str);
  Serial.write(10);
  
  //It appears that delay is needed in order not to clog the port
  delay(15);
}

void readFrom(int device, byte address, int num, byte buff[]) {
  Wire.beginTransmission(device); //start transmission to device 
  Wire.write(address);        //sends address to read from
  Wire.endTransmission(); //end transmission
  
  Wire.beginTransmission(device); //start transmission to device (initiate again)
  Wire.requestFrom(device, num);    // request 6 bytes from device
  
  int i = 0;
  while(Wire.available())    //device may send less than requested (abnormal)
  { 
    buff[i] = Wire.read(); // receive a byte
    i++;
  }
  Wire.endTransmission(); //end transmission
}

void writeTo(int device, byte address, byte val) {
   Wire.beginTransmission(device); //start transmission to device 
   Wire.write(address);        // send register address
   Wire.write(val);        // send value to write
   Wire.endTransmission(); //end transmission
}

Where did you find that code ?
The Wire.requestFrom() does not need a Wire.beginTransmission() nor a Wire.endTransmission().

You are using the raw accelerometer values, that is okay.
Measuring the earth gravity (tilt sensor) is the same as measuring the acceleration of a car.

Suppose you combine x,y,z into a vector and a strength. When the car is not moving, the vector is pointing downwards because of earth gravity. When the car is accelerating, the vector is moving more backward because the overall pulling force has changed direction.
The sensor is like a plumb bob : Plumb bob - Wikipedia

If you use a tablet or phone with a spirit level app, you can test that against your Arduino. They should both be able to detect the earch gravity and acceleration/deceleration.

Hello, thank you for the reply. Ultimately I found the code I used and posted here:

Understood that there seem to be extra calls there that aren't needed.

I tried the app on my Android phone like you recommended. First, it seems to behave a little better or more like I would expect than what I am seeing from the Arduino/ADXL345. This makes me wonder if there is a code issue or I am not setting/understanding the sensor properly.

What I want to do is basically what I said, I want to be able to use the sensor on a robot that mainly only uses on axis (only goes back and forth). I understand that directly coupled sensors like encoders on the wheels, etc... would be best and easiest but I am going for non-contact or non-coupled way of detecting the amount of distance traveled in feet. Think of it as a pedometer for a person walking using an accelerometer instead of GPS.

I am thinking that I can use filtering (granted, none of it is in my code now) to filter out noise and zero. Then, when I get acceleration on my axis that I care about, I start a timer and keep track of the amount of acceleration each time it changes. Then, I will see decelleration and when I get back to zero, I can do a simple time and velocity calculation to get the distance traveled. I know it will not be 100% accurate, don't need it to be. I just really need approximate, relative distance traveled.

Thanks!

That code is wrong :o

I took "adxl345_arduino_processing_02.zip" and rewrote the "readFrom" function. To request data, only the "Wire.requestFrom()" is used and the "Wire.beginTransmision()" and "Wire.endTransmission()" are removed, and I use "Wire.readBytes()" as an extra.

//reads num bytes starting from address register on device in to buff array
void readFrom(int device, byte address, int num, byte buff[]) {
  Wire.beginTransmission(device); //start transmission to device 
  Wire.write(address);        //sends address to read from
  Wire.endTransmission(); //end transmission
  
  int n = Wire.requestFrom(device, num);    // request 6 bytes from device
  if( n == num)
  {
    Wire.readBytes(buff, n);
  }
}

To calculate the distance is not accurate at all. Many have tried it, and it can be accurate for only a short time. But those sensors (MEMS sensors MEMS - Wikipedia ) are not very stable and they drift.

A tablet or phone uses a filter for the accelerometer. If you use the raw values in the Arduino, they will be jumpy like crazy.

I'm afraid that you started a project that uses an accelerometer in a way that will not work very well.
For more accuracy, a fixed sample rate is needed. For example 100 times a second. And a software filter is needed. Perhaps a better accelerometer is needed.
Kalman filter guide : Guide to gyro and accelerometer with Arduino including Kalman filtering - Sensors - Arduino Forum
Better accelerometer : Adafruit 9-DOF Absolute Orientation IMU Fusion Breakout - BNO055 : ID 2472 : $34.95 : Adafruit Industries, Unique & fun DIY electronics and kits