Averaging Acceleration on x,y,z-axis using LIS3DH Breakout Board with Mkr Zero

Hello!
I want to average each axis separately while doing different actions. I need this data for my overall code which will use these different accelerations as switch cases for different actions to happen. I have configured this in the I2C setup not SPI.

The problems I’m having:
I have been trying to read just the X acceleration into an array and then averaging those results. I’ve looked at many examples but all were for analog inputs.
This is how the data is being read.

void get_Acceleration( void )
{
  lis.read(); //reading X, Y, and Z

  lis.getEvent(&event);

  accel_X = event.acceleration.x/9.81;
  accel_Y = event.acceleration.y/9.81;
  accel_Z = event.acceleration.z/9.81;
  
  magnitude = sqrt(sq(accel_X) + sq(accel_Y) + sq(accel_Z));
  if (magnitude < 0)
     magnitude = 0;
}

My latest attempt to getting average: (definitions are defined globally, not shown)

void get_AverageX ( void )
{
  get_Acceleration(); //I don't think I need this
  array_X[j++] = accel_X;     // populate FIFO array

  if( j >= array_size)
      j=0;
   for (i = 0; i < array_size; i++)
   {
    sumX = sumX + array_X[i];
   }
   avgX = sumX / array_size;
}

My main issue is that I can get my code to run but I am getting all zeros for the array and the average. The x acceleration (accel_X) is outputted fine.

I need help figuring out how to get the array to hold about 20 values of X-acceleration and then average it.
(I’ve seen the running average and smoothing examples but all are using analog pins, so I’m confused how I can change that to my type of output)

Thanks!

helpme_impoor:
(I’ve seen the running average and smoothing examples but all are using analog pins, so I’m confused how I can change that to my type of output)

That’s a little bit strange. Such routines are purely mathematical and, if written by other than a monkey, are completely agnostic about how the data is produced or used. Here is an example:

// exponential filter (lossy integration)
unsigned int smooth(unsigned int newVal) {
  static unsigned int oldVal = 0;
  unsigned long sum;
  //  sum = (oldVal * 16 + newVal) / 16;
  sum = ( (oldVal << 4) - oldVal + newVal) >> 4;
  oldVal = sum;
  return oldVal;
}

What you’re saying seems like, “I went to buy a drinking cup for my apple juice, but all the cups were for water”.

Thanks for the reply.
When I was looking at the smooth examples they were using a potentiometer that was connect to the Arduino through analog pins.
Smoothing Code Below

const int numReadings = 10;

int readings[numReadings];      // the readings from the analog input
int readIndex = 0;              // the index of the current reading
int total = 0;                  // the running total
int average = 0;                // the average

int inputPin = A0;

void setup() {
  // initialize serial communication with computer:
  Serial.begin(9600);
  // initialize all the readings to 0:
  for (int thisReading = 0; thisReading < numReadings; thisReading++) {
    readings[thisReading] = 0;
  }
}

void loop() {
  // subtract the last reading:
  total = total - readings[readIndex];
  // read from the sensor:
  readings[readIndex] = analogRead(inputPin);
  // add the reading to the total:
  total = total + readings[readIndex];
  // advance to the next position in the array:
  readIndex = readIndex + 1;

  // if we're at the end of the array...
  if (readIndex >= numReadings) {
    // ...wrap around to the beginning:
    readIndex = 0;
  }

  // calculate the average:
  average = total / numReadings;
  // send it to the computer as ASCII digits
  Serial.println(average);
  delay(1);        // delay in between reads for stability
}

I am unsure how I could change this to my accel_X readings and plus how I can add the section you commented as well.

Post ALL the code, using code tags, and forum members can help you integrate a sensible moving average into it.

It's easy, you just pass incoming values into smooth(). The return value is the averaged value. So it's like:

average_X = smooth(accel_X);

Except that you are using floating point math, the function I gave you is for unsigned integers. The function can easily be converted to floating point:

// exponential filter (lossy integration)
float smooth(float newVal) {
  static float oldVal = 0.0;
  oldVal = (oldVal * 16 + newVal) / 16;
  return oldVal;
}

jremington:
Post ALL the code, using code tags, and forum members can help you integrate a sensible moving average into it.

//From the Accel Program
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_LIS3DH.h>
#include <Adafruit_Sensor.h>

// For I2C Wiring Config.
Adafruit_LIS3DH lis = Adafruit_LIS3DH();

double magnitude, accel_X, accel_Y, accel_Z; 
float avg_X, avg_Y, avg_Z, avg_M;
const int numReadings = 100;
float readingsX[numReadings], readingsY[numReadings], readingsZ[numReadings];
int readIndex = 0;
float totalx, totaly, totalz;

float inputX = accel_X, inputY = accel_Y, inputZ= accel_Z;

  sensors_event_t event;
  
void setup() 
{
  // put your setup code here, to run once:
   Serial.begin(115200);
   while (!Serial) 
    delay(10);     // will pause Zero, Leonardo, etc until serial console opens

  Serial.println("Starting");

  if (! lis.begin(0x18)) 
  {   // change this to 0x19 for alternative i2c address
    Serial.println("Couldnt start");
    while (1) yield();
  }
  lis.setRange(LIS3DH_RANGE_16_G);   //Choose 16G because of teseting for firing (shock)

  
  //Switch statement from the acceleration demo
  switch (lis.getDataRate()) {
    case LIS3DH_DATARATE_1_HZ: 
    break;
    case LIS3DH_DATARATE_10_HZ:  
    break;
    case LIS3DH_DATARATE_25_HZ: 
    break;
    case LIS3DH_DATARATE_50_HZ:  
    break;
    case LIS3DH_DATARATE_100_HZ:  
    break;
    case LIS3DH_DATARATE_200_HZ: 
    break;
    case LIS3DH_DATARATE_400_HZ:  
    break;
    case LIS3DH_DATARATE_POWERDOWN: 
    break;
    case LIS3DH_DATARATE_LOWPOWER_5KHZ:  
    break;
    case LIS3DH_DATARATE_LOWPOWER_1K6HZ:  
    break;
  }
  for (int thisReading = 0; thisReading < numReadings; thisReading++) {
    readingsX[thisReading] = 0;
    readingsY[thisReading] = 0;
    readingsZ[thisReading] = 0;
  }
  avg.begin();
}

void loop() {

  get_Acceleration();
  get_AverageX();
  get_AverageY();
  get_AverageZ();
  averageOutput_test();
  delay(100);
  totalx = 0;
  totaly = 0;
  totalz = 0;
}

void get_Acceleration( void )
{
  lis.read(); //reading X, Y, and Z

  lis.getEvent(&event);

  accel_X = event.acceleration.x/9.81;
  accel_Y = event.acceleration.y/9.81;
  accel_Z = event.acceleration.z/9.81;
  
  magnitude = sqrt(sq(accel_X) + sq(accel_Y) + sq(accel_Z));
  if (magnitude < 0)
     magnitude = 0;
}

void get_AverageX ( void )
{
 totalx = totalx - readingsX[readIndex];
 readingsX[readIndex] = inputX;
 totalx = totalx + readingsX[readIndex];
 readIndex = readIndex + 1;

 if (readIndex >= numReadings) 
 {
  readIndex = 0;
 }
 avg_X = totalx / numReadings;
}

void get_AverageY ( void )
{
 totaly = totaly - readingsY[readIndex];
 readingsY[readIndex] = inputY;
 totaly = totaly + readingsY[readIndex];
 readIndex = readIndex + 1;

 if (readIndex >= numReadings) 
 {
  readIndex = 0;
 }
 avg_Y = totaly / numReadings;
}

void get_AverageZ ( void )
{
 totalz = totalz - readingsZ[readIndex];
 readingsZ[readIndex] = inputZ;
 totalz = totalz + readingsZ[readIndex];
 readIndex = readIndex + 1;

 if (readIndex >= numReadings) 
 {
  readIndex = 0;
 }
 avg_Z = totalz / numReadings;
}

void averageOutput_test( void )
{
  Serial.print("\tX acceleration: ");
  Serial.print(accel_X);
  Serial.print(" \tX average: ");
  Serial.print(avg_X);
  Serial.print("\tY acceleration: ");
  Serial.print(accel_Y);
  Serial.print(" \tY average: ");
  Serial.print(avg_Y);
  Serial.print("\tZ acceleration: ");
  Serial.print(accel_Z);
  Serial.print(" \tZ average: ");
  Serial.print(avg_Z);
  Serial.println();
  delay(1000);
}

The accelerations were printing fine, the averages were 0.
I don’t need the values to be in float, they were originally doubles but was having issues with that so I changed to float.

they were originally doubles but was having issues with that so I changed to float.

What sort of issues? "float" and "double" variables should behave the same in an application like this.

float inputX = accel_X,

That doesn’t magically bind one variable to the other.

jremington:
What sort of issues? "float" and "double" variables should behave the same in an application like this.

I was able to change to double and didn't get errors. I believe I was getting errors when I was writing the arrays when I was trying to do a different average code. I was confused because I thought they were relatively the same thing.

Thanks!

TheMemberFormerlyKnownAsAWOL:

float inputX = accel_X,

That doesn't magically bind one variable to the other.

Thanks! I got rid of the input variable.

My question is do I need to fix the line:

readingsX[readIndex] = accel_X;

The example code used:

readings[readIndex] = analogRead(inputPin);

Since I'm not reading from a pin but that event created, is that line my problem?

I actually think I found the problem.

I created different readIndex for each axis.

Here is final code:

//From the Accel Program
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_LIS3DH.h>
#include <Adafruit_Sensor.h>

// For I2C Wiring Config.
Adafruit_LIS3DH lis = Adafruit_LIS3DH();

double magnitude, accel_X, accel_Y, accel_Z; 
double avg_X, avg_Y, avg_Z, avg_M;
const int numReadings = 100;
double readingsX[numReadings], readingsY[numReadings], readingsZ[numReadings];
int readIndexX = 0, readIndexY = 0, readIndexZ = 0;
double totalx = 0, totaly = 0, totalz = 0;
  sensors_event_t event;
  
void setup() 
{
  // put your setup code here, to run once:
   Serial.begin(115200);
   while (!Serial) 
    delay(10);     // will pause Zero, Leonardo, etc until serial console opens

  Serial.println("Starting");

  if (! lis.begin(0x18)) 
  {   // change this to 0x19 for alternative i2c address
    Serial.println("Couldnt start");
    while (1) yield();
  }
  lis.setRange(LIS3DH_RANGE_16_G);   //Choose 16G because of teseting for firing (shock)
  //Switch statement from the acceleration demo
  switch (lis.getDataRate()) {
    case LIS3DH_DATARATE_1_HZ: 
    break;
    case LIS3DH_DATARATE_10_HZ:  
    break;
    case LIS3DH_DATARATE_25_HZ: 
    break;
    case LIS3DH_DATARATE_50_HZ:  
    break;
    case LIS3DH_DATARATE_100_HZ:  
    break;
    case LIS3DH_DATARATE_200_HZ: 
    break;
    case LIS3DH_DATARATE_400_HZ:  
    break;
    case LIS3DH_DATARATE_POWERDOWN: 
    break;
    case LIS3DH_DATARATE_LOWPOWER_5KHZ:  
    break;
    case LIS3DH_DATARATE_LOWPOWER_1K6HZ:  
    break;
  }
  for (int thisReading = 0; thisReading < numReadings; thisReading++) {
    readingsX[thisReading] = 0;
    readingsY[thisReading] = 0;
    readingsZ[thisReading] = 0;
  }
}

void loop() {

  get_Acceleration();
  get_AverageX();
  get_AverageY();
  get_AverageZ();
  averageOutput_test();
  delay(100);
}

void get_Acceleration( void )
{
  lis.read(); //reading X, Y, and Z

  lis.getEvent(&event);

  accel_X = event.acceleration.x/9.81;
  accel_Y = event.acceleration.y/9.81;
  accel_Z = event.acceleration.z/9.81;
  
  magnitude = sqrt(sq(accel_X) + sq(accel_Y) + sq(accel_Z));
  if (magnitude < 0)
     magnitude = 0;
}

void get_AverageX ( void )
{
 totalx = totalx - readingsX[readIndexX];
 readingsX[readIndexX] = accel_X;
 totalx = totalx + readingsX[readIndexX];
 readIndexX = readIndexX + 1;

 if (readIndexX >= numReadings) 
 {
  readIndexX = 0;
 }
 avg_X = totalx / numReadings;
}

void get_AverageY ( void )
{
 totaly = totaly - readingsY[readIndexY];
 readingsY[readIndexY] = accel_Y;
 totaly = totaly + readingsY[readIndexY];
 readIndexY = readIndexY + 1;

 if (readIndexY >= numReadings) 
 {
  readIndexY = 0;
 }
 avg_Y = totaly / numReadings;
}

void get_AverageZ ( void )
{
 totalz = totalz - readingsZ[readIndexZ];
 readingsZ[readIndexZ] = accel_Z;
 totalz = totalz + readingsZ[readIndexZ];
 readIndexZ = readIndexZ + 1;

 if (readIndexZ >= numReadings) 
 {
  readIndexZ = 0;
 }
 avg_Z = totalz / numReadings;
}

void averageOutput_test( void )
{
  Serial.print("\tX acceleration: ");
  Serial.print(accel_X);
  Serial.print(" \tX average: ");
  Serial.print(avg_X);
  Serial.print("\tY acceleration: ");
  Serial.print(accel_Y);
  Serial.print(" \tY average: ");
  Serial.print(avg_Y);
  Serial.print("\tZ acceleration: ");
  Serial.print(accel_Z);
  Serial.print(" \tZ average: ");
  Serial.print(avg_Z);
  Serial.println();
}

Let me know if you think there is a simpler way.
(Below I attached the monitor from the above code, the circuit was sitting on the table)

Let me know if you think there is a simpler way.

See replies #1 and #4 for a much simpler approach.

Well, there's your problem. At the bottom of loop() you have:

  totalx = 0;
  totaly = 0;
  totalz = 0;

So you are only totaling 1 reading and dividing the total by 100. DON'T clear the totals! The oldest values are being subtracted from the total when you do the averaging.

Thanks!
I found that was one of the problems.

johnwasser:
Well, there's your problem. At the bottom of loop() you have:

  totalx = 0;

totaly = 0;
  totalz = 0;





So you are only totaling 1 reading and dividing the total by 100. DON'T clear the totals! The oldest values are being subtracted from the total when you do the averaging.