Using arrays and functions + plus general feedback on project!!

Okay, so I've made this sketch that reads values from an analog accelerometer and prints it to serial. The way it operates is that it first reads the raw values from accelerometer and averages them, calibrates these readings to g's and finally prints them to serial. The values are stored as x,y,z-accelerations in an array.

Sketch works fine however for the purpose of learning to use functions and getting a "nicer" sketch I want to move these operations into functions, something like explained below, however I can't get it to work!

What I want:

void loop() {
accRead();
calibrate();
printResults(accvals[]);


int readAccelerometer() {
..
..
return accRead[];
}

int calibrate() {
..
..
return accvals[];
}

void printResults(){
//print results
}

What I have so far:

/*
Arduino script to read accelerations from ADXL335 chip and averaging results before printing to serial.
 Values are offset and scaled by assuming linear response between +g to -g. 
 
 */

// Pins defined in void loop() (j = 0, 1, 2);
// xpin = A0
// ypin = A1
// zpin = A2

void setup(){
  analogReference(EXTERNAL); //ADXL input voltage is 3,3V
  Serial.begin(9600);
  Serial.println("Accelerometer ADXL335");
  Serial.println("acc x, acc y, acc z, magnitude");
  delay(2000);
}

void loop(){
  // Reading sensor
  float accRead[] = {0,0,0};  // Array for storing accelerometer values {x,y,z}
  int n = 50;      // Number of samples before averaging
  for (int i = 0; i < n; i++){
    for (int j = 0; j < 3; j++) {
    accRead[j] = accRead[j]+analogRead(j)/float(n);
    delay(0);  // Delay between readings, probably obsolete
    }
  }

  // Values for calibrating
  float zeroG[] = {
    503.25, 504.50, 528.50          }; // See below
  float sens[] = {
    103.25, 105.50, 101.50          };  // See below

  /*
   zeroG[] values are determined by 6 measurments where chip is placed
   on horizontal surface and getting acc. value for vertical
   axis "accUP", then turning chip upside down and getting new 
   value "accDOWN", and then averaging those two values for each axis:
   zeroG = (accUP + accDOWN)/2
   
   
   sens[] values are found by dividing the difference between accUP
   and accDOWN by 2G: sens = (accUP-accDOWN)/2.
   */

  // Calibrating sensor readings and calculating magnitude
  float magnitude = 0;
  float accval[] = {0,0,0};
  for (int i=0; i < 3; i++) {
    accval[i] = ((accRead[i] - zeroG[i])/sens[i]); // calibrating value
    magnitude = magnitude + accval[i]*accval[i];
  }
  magnitude = sqrt(magnitude);
  delay(1);


  // Printing results
  for (int i = 0; i < 3; i++) {
    if (accval[i] >= 0.00){
      Serial.print(" ");
    }
    Serial.print(accval[i]);
    Serial.print('\t');
  }
  Serial.println(magnitude);
  delay(100); // Delay between prints
}

You need to look into pointers ' * '. You can't return an array like that but you can return a pointer to the array.

HazardsMind:
You need to look into pointers ' * '. You can't return an array like that but you can return a pointer to the array.

Okay, I'm entirely blank there. Does it have anything to do with "&" (did a google search :))? I've seen it used in codes somewhere, but never understood how it worked.

Ok, after looking at your code, this is what I came up with. There are some things I added, mainly to get my sensor to work on my Mega.

/*
Arduino script to read accelerations from ADXL335 chip and averaging results before printing to serial.
 Values are offset and scaled by assuming linear response between +g to -g. 
 
 */

// Pins defined in void loop() (j = 0, 1, 2);
 byte xpin = A15;
 byte ypin = A14;
 byte zpin = A13;
 byte Power = 53;

float accRead[] = {0,0,0};  // Array for storing accelerometer values {x,y,z}
float accAvg[] = {0,0,0};
int n = 50;      // Number of samples before averaging

void setup(){
  pinMode(Power, OUTPUT);
  //analogReference(EXTERNAL); //ADXL input voltage is 3,3V
  Serial.begin(9600);
  digitalWrite(Power, HIGH);
  Serial.println("Accelerometer ADXL335");
  Serial.println("acc x, acc y, acc z, magnitude");
  delay(2000);
}

void loop(){
 read_Accelerometer();
/*Serial.print(accAvg[0]);
 Serial.print(" ");
 Serial.print(accAvg[1]);
 Serial.print(" ");
 Serial.println(accAvg[2]);*/
 CalibrateValues(accAvg); 
}

float read_Accelerometer() {
//Collect the values first
  for (int i = 0; i < n; i++)
  {
    for (int j = 0; j < 3; j++) 
    { 
    accRead[j] += analogRead(A13 + j); //A13 + j = A13, A14, A15, Same can be done for A0,A1,A2
    delay(1);  // Delay between readings, probably obsolete
    }
  }
  
  for (int k = 0; k < 3; k++) { // average the values
    accAvg[k] = accRead[k]/n;
  }
  for (int k = 0; k < 3; k++) { // Clear accRead for new values
    accRead[k] = 0;
  }
return *accAvg; //return pointer to accAvg
}

float CalibrateValues(float *accAvg)
{
  // Values for calibrating
  float zeroG[] = {
    503.25, 504.50, 528.50          }; // See below
  float sens[] = {
    103.25, 105.50, 101.50          };  // See below

  /*
   zeroG[] values are determined by 6 measurments where chip is placed
   on horizontal surface and getting acc. value for vertical
   axis "accUP", then turning chip upside down and getting new 
   value "accDOWN", and then averaging those two values for each axis:
   zeroG = (accUP + accDOWN)/2
   
   
   sens[] values are found by dividing the difference between accUP
   and accDOWN by 2G: sens = (accUP-accDOWN)/2.
   */

  // Calibrating sensor readings and calculating magnitude
  float magnitude = 0;
  float accval[] = {0,0,0};
  for (int i=0; i < 3; i++) {
    accval[i] = ((accAvg[i] - zeroG[i])/sens[i]); // calibrating value
    magnitude = magnitude + accval[i]*accval[i];
  }
  magnitude = sqrt(magnitude);
  delay(1);

  // Printing results
  for (int i = 0; i < 3; i++) {
    if (accval[i] >= 0.00){
      Serial.print(" ");
    }
    Serial.print(accval[i]);
    Serial.print('\t');
  }
  Serial.println(magnitude);
  delay(100); // Delay between prints
}

If you plan on adding anything else, I would takeout the delays. You can keep them in for this sketch, but in the future, look into the example "Blink Without Delay".

HazardsMind:
Ok, after looking at your code, this is what I came up with. There are some things I added, mainly to get my sensor to work on my Mega.

/*

Arduino script to read accelerations from ADXL335 chip and averaging results before printing to serial.
Values are offset and scaled by assuming linear response between +g to -g.

*/

// Pins defined in void loop() (j = 0, 1, 2);
byte xpin = A15;
byte ypin = A14;
byte zpin = A13;
byte Power = 53;

float accRead[] = {0,0,0};  // Array for storing accelerometer values {x,y,z}
float accAvg[] = {0,0,0};
int n = 50;      // Number of samples before averaging

void setup(){
  pinMode(Power, OUTPUT);
  //analogReference(EXTERNAL); //ADXL input voltage is 3,3V
  Serial.begin(9600);
  digitalWrite(Power, HIGH);
  Serial.println("Accelerometer ADXL335");
  Serial.println("acc x, acc y, acc z, magnitude");
  delay(2000);
}

void loop(){
read_Accelerometer();
/Serial.print(accAvg[0]);
Serial.print(" ");
Serial.print(accAvg[1]);
Serial.print(" ");
Serial.println(accAvg[2]);
/
CalibrateValues(accAvg);
}

float read_Accelerometer() {
//Collect the values first
  for (int i = 0; i < n; i++)
  {
    for (int j = 0; j < 3; j++)
    {
    accRead[j] += analogRead(A13 + j); //A13 + j = A13, A14, A15, Same can be done for A0,A1,A2
    delay(1);  // Delay between readings, probably obsolete
    }
  }
 
  for (int k = 0; k < 3; k++) { // average the values
    accAvg[k] = accRead[k]/n;
  }
  for (int k = 0; k < 3; k++) { // Clear accRead for new values
    accRead[k] = 0;
  }
return *accAvg; //return pointer to accAvg
}

float CalibrateValues(float *accAvg)
{
  // Values for calibrating
  float zeroG[] = {
    503.25, 504.50, 528.50          }; // See below
  float sens[] = {
    103.25, 105.50, 101.50          };  // See below

/*
   zeroG[] values are determined by 6 measurments where chip is placed
   on horizontal surface and getting acc. value for vertical
   axis "accUP", then turning chip upside down and getting new
   value "accDOWN", and then averaging those two values for each axis:
   zeroG = (accUP + accDOWN)/2
   
   
   sens[] values are found by dividing the difference between accUP
   and accDOWN by 2G: sens = (accUP-accDOWN)/2.
   */

// Calibrating sensor readings and calculating magnitude
  float magnitude = 0;
  float accval[] = {0,0,0};
  for (int i=0; i < 3; i++) {
    accval[i] = ((accAvg[i] - zeroG[i])/sens[i]); // calibrating value
    magnitude = magnitude + accval[i]*accval[i];
  }
  magnitude = sqrt(magnitude);
  delay(1);

// Printing results
  for (int i = 0; i < 3; i++) {
    if (accval[i] >= 0.00){
      Serial.print(" ");
    }
    Serial.print(accval[i]);
    Serial.print('\t');
  }
  Serial.println(magnitude);
  delay(100); // Delay between prints
}




If you plan on adding anything else, I would takeout the delays. You can keep them in for this sketch, but in the future, look into the example "Blink Without Delay".

Thank you very much for taking your time helping me with this, it's highly appreciated! :slight_smile:

I will have to look through this for a moment, as well as the example you referred to (I recall having seen it a long time ago, but never thought any more of it). The disadvantage of delay() in this project, from my understanding, is that it halts the program entirely instead of doing anything useful like for instance doing readings and averaging results, am I right?

Just a quick question, the reason for using 'byte' as data type instead of 'int', is that to save memory?
Also I don't immediately see the reason for the array accAvg[], is it to reduce the number of computations?

int to byte does save memory and its a good practice when the value of the variable is within 0 - 255.

I added accAvg[] because I needed to clear accRead and set is back to zero, otherwise it will just continue to add to accRead and it will not give you the correct output.

HazardsMind:
int to byte does save memory and its a good practice when the value of the variable is within 0 - 255.

I added accAvg[] because I needed to clear accRead and set is back to zero, otherwise it will just continue to add to accRead and it will not give you the correct output.

Good advice!

Btw. I tried to run the sketch you provided, but it gave different results, approx 20% off, but I haven't had time to check it thoroughly yet. (I guess less time to do hobby stuff is the price of having small children :)).

but it gave different results, approx 20% off

How so? What values are you expecting?

HazardsMind:

but it gave different results, approx 20% off

How so? What values are you expecting?

With the offset and scale parameters I used I got 0.00 on horizontal axes and 1.00 on vertical axis, and magnitude equal to 1.00, with the new sketch it was 1.20 and 0.80 or something like that. Could it be because the "n" variable vas int instead of float?

It could be, but if you were to change it, just do it in the average values for loop. Use (float)n to cast it as a float value .

HazardsMind:
It could be, but if you were to change it, just do it in the average values for loop. Use (float)n to cast it as a float value .

Okay, so not when defining the variable? I guess that has to do with the memory as well?

I'll try this and get back to you. Thanks again for helping :slight_smile:

HazardsMind:
It could be, but if you were to change it, just do it in the average values for loop. Use (float)n to cast it as a float value .

Okay, I made some changes to the sketch, that is adding a function to average the readings and another for printing. What I don't understand is that if I don't remove the "float accVal[] = {0,0,0,0};" inside calibrate_Values() {}, the prints will be 0,0,0,0.

Oh, and I found why the prints from the sketch you altered gave different results than mine; I forgot to include the analogReference. After that it was correct :slight_smile:

What remains I guess is to get rid of the delays and change some of the int's to byte's.

Another thing is that maybe the averaging would be better if I stored all the raw-values in separate arrays and 'smooth' them as in this example: http://arduino.cc/en/Tutorial/Smoothing.

Anyway, here is the current sketch:

float accRead[] = {0,0,0};  // Array for storing accelerometer values {x,y,z}
float accAvg[] = {0,0,0};   // Averaged readings
float accVal[] = {0,0,0,0}; // Acc X,Y,Z + magnitude
float n = 50;      // Number of samples before averaging

void setup(){

  analogReference(EXTERNAL); //ADXL input voltage is 3,3V
  Serial.begin(9600);

  Serial.println("Accelerometer ADXL335");
  Serial.println("acc x, acc y, acc z, magnitude");
  delay(2000);
}

void loop(){
  read_Accelerometer();
  average_Values(accRead);
  calibrate_Values(accAvg);
  print_Acc(accVal); 
}

float read_Accelerometer() {
  //Collect the values first
  for (int i = 0; i < n; i++) 
  {
    for (int j = 0; j < 3; j++) // 
    { 
      accRead[j] += analogRead(j);
      delay(1);  // Delay between readings, probably obsolete
    }
  }
  return *accRead;
}

float average_Values(float *accRead) {
  for (int k = 0; k < 3; k++) { // average the values
    accAvg[k] = accRead[k]/float(n);
  }
  for (int k = 0; k < 3; k++) { // Clear accRead for new values
    accRead[k] = 0;
  }

  return *accAvg; //return pointer to accAvg
}


float calibrate_Values(float *accAvg)
{
  // Values for calibrating
  float zeroG[] = {503.25, 504.50, 528.50}; // See below
  float sens[] = {103.25, 105.50, 101.50};  // See below

  /*
   zeroG[] values are determined by 6 measurments where chip is placed on horizontal surface and getting acc. value for vertical axis "accUP", then turning chip upside down and getting new value "accDOWN", and then averaging those two values for each axis: zeroG = (accUP + accDOWN)/2
   sens[] values are found by dividing the difference between accUP and accDOWN by 2G: sens = (accUP-accDOWN)/2.
   */

  // Calibrating sensor readings and calculating magnitude
 float magnitude = 0;
 //float accVal[] = {0,0,0,0};
  for (int i=0; i < 3; i++) {
    accVal[i] = ((accAvg[i] - zeroG[i])/sens[i]); // calibrating value
    magnitude = magnitude + accVal[i]*accVal[i];
  }
  magnitude = sqrt(magnitude);
  accVal[3] = magnitude;
  return *accVal;
}


void print_Acc(float *accVal) {
  // Printing results
  for (int i = 0; i < 3; i++) {
    if (accVal[i] >= 0.00){
      Serial.print(" ");
    }
    Serial.print(accVal[i]);
    Serial.print('\t');
  }
  Serial.println(accVal[3]);
  delay(100); // Delay between prints
}