Average calculations and arrays

Hello everyone

I wrote this sketch to learn and test how to send a multidimensional array to a function and return the average:

float total = 0;
int numReadings = 10;
 
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600); // for debugging

  float test [3][6] {
  {7.5, 3.6, 4.7, 2.8, 2.9, 1.2 }, // 22.7, average 3.78
  {2.1, 1.2, 7.2, 6.8, 1.9, 9.2 }, // 28.4, average 4.73
  {1.5, 8.6, 3.7, 5.8, 3.9, 9.2 } // 32.7, average 5.45
  };

for ( int i = 0; i < 3; i++) {
  float resultArray[i];
  resultArray[i] = averageMath(test);
    Serial.print("result ");
    Serial.print(i);
    Serial.print(": ");
    Serial.println(resultArray[i]);
}//for
}//setup

float averageMath(float Readings[3][6]){

  for (int i = 0; i < 3; i++){
    static float average[3];
    total = 0;
    for (int j = 0; j < 6; j++ ){
    total = total + Readings[i][j];
Serial.print("total ");
Serial.print(i);
Serial.print("-");
Serial.print(j);
Serial.print(": ");
Serial.println(total);
    
    }//for j
        average[i] = total / 6;
Serial.print("average ");
Serial.print(i);
Serial.print(": ");
Serial.println(average[i]);
            return average[i];
    }//for i
  }//averageMath


void loop() {
  // put your main code here, to run repeatedly:

}

So far everything worked perfectly.

Then I wanted to make it more general to be able to send what ever mulidimensional array. I found this site to give me an idea on how to do it:

I wanted to work with method 2.

I ended with this code:

float total = 0;
int numReadings = 10;
 
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600); // for debugging

  int m = 3;
  int n = 6;

  float test [m][n] {
  {7.5, 3.6, 4.7, 2.8, 2.9, 1.2 }, // 22.7, average 3.78
  {2.1, 1.2, 7.2, 6.8, 1.9, 9.2 }, // 28.4, average 4.73
  {1.5, 8.6, 3.7, 5.8, 3.9, 9.2 } // 32.7, average 5.45
  };

for ( int i = 0; i < m; i++) {
  float resultArray[i];
  resultArray[i] = averageMath((float*)test, m, n);
    Serial.print("result ");
    Serial.print(i);
    Serial.print(": ");
    Serial.println(resultArray[i]);
}//for
}//setup

float averageMath((float *)readings, int a, int b){
    //  static float average[3];
  for (int i = 0; i < a; i++){
    static float average[3];
    total = 0;
    for (int j = 0; j < b; j++ ){
    total = total + readings[i][j];
Serial.print("total ");
Serial.print(i);
Serial.print("-");
Serial.print(j);
Serial.print(": ");
Serial.println(total);
    
    }//for j
        average[i] = total / b;
Serial.print("average ");
Serial.print(i);
Serial.print(": ");
Serial.println(average[i]);
            return average[i];
    }//for i
  }//averageMath


void loop() {
  // put your main code here, to run repeatedly:

}

As to my understanding I just changed the parameters for the sizes of the arrays and I changed the variable Readings to readings. But now all of a sudden I get the “averageMath was not declared in this scope” error:

Arduino: 1.8.9 (Windows 10), Board: "Arduino/Genuino Uno"

R:\Arduino\Terrarium\0_Test_Average_OneArray_2_bidimen_pointers\0_Test_Average_OneArray_2_bidimen_pointers.ino: In function 'void setup()':

0_Test_Average_OneArray_2_bidimen_pointers:19:50: error: 'averageMath' was not declared in this scope

   resultArray[i] = averageMath((float*)test, m, n);

                                                  ^

R:\Arduino\Terrarium\0_Test_Average_OneArray_2_bidimen_pointers\0_Test_Average_OneArray_2_bidimen_pointers.ino: At global scope:

0_Test_Average_OneArray_2_bidimen_pointers:27:28: error: 'readings' was not declared in this scope

 float averageMath((float *)readings, int a, int b){

                            ^

0_Test_Average_OneArray_2_bidimen_pointers:27:38: error: expected primary-expression before 'int'

 float averageMath((float *)readings, int a, int b){

                                      ^

0_Test_Average_OneArray_2_bidimen_pointers:27:45: error: expected primary-expression before 'int'

 float averageMath((float *)readings, int a, int b){

                                             ^

0_Test_Average_OneArray_2_bidimen_pointers:27:51: error: expected ',' or ';' before '{' token

 float averageMath((float *)readings, int a, int b){

                                                   ^

exit status 1
'averageMath' was not declared in this scope

Invalid library found in R:\Arduino\IDE\libraries\libraries: no headers files (.h) found in R:\Arduino\IDE\libraries\libraries
Invalid library found in R:\Arduino\IDE\libraries\Terrarium_forum: no headers files (.h) found in R:\Arduino\IDE\libraries\Terrarium_forum

This report would have more information with
"Show verbose output during compilation"
option enabled in File -> Preferences.

I looked through the different functions and I could not find why this error occurs.

Thank you all for the help.

moses

That error often means you have got your braces wrong.

}//for

The very fact that you have labelled this brace indicates that you are having difficulty with braces.

Use the auto-format. It will indent your code properly. If it appears to scramble the code rhen the code was wrong.

Try using " float averageMath((float *)readings, int a, int b)" after you tell the compiler how to handle it. I think the compiler is complaining, because the variables are not equal to a known value.

Your function declaration is wrong, so the Arduino IDE doesn’t add a prototype for it at the top of the sketch. This means that you cannot use the function before where it is defined.

On top of that, you cannot pass multidimensional arrays to functions like that, because the compiler has to know the dimensions of the array in order to calculate the memory offsets (offset = column + row * number of columns).

The easiest way is to just use templates, and let the compiler figure out the sizes for you:

[color=#5e6d03]template[/color] [color=#434f54]<[/color][color=#00979c]class[/color] [color=#000000]T[/color][color=#434f54],[/color] [b][color=#d35400]size_t[/color][/b] [color=#000000]N[/color][color=#434f54]>[/color] [color=#000000]T[/color] [color=#000000]average[/color][color=#000000]([/color][color=#00979c]const[/color] [color=#000000]T[/color] [color=#000000]([/color][color=#434f54]&[/color][color=#00979c]array[/color][color=#000000])[/color][color=#000000][[/color][color=#000000]N[/color][color=#000000]][/color][color=#000000])[/color] [color=#000000]{[/color]
  [color=#000000]T[/color] [color=#000000]sum[/color][color=#000000]{}[/color][color=#000000];[/color]
  [color=#5e6d03]for[/color] [color=#000000]([/color][color=#000000]T[/color] [color=#000000]element[/color] [color=#434f54]:[/color] [color=#00979c]array[/color][color=#000000])[/color]
    [color=#000000]sum[/color] [color=#434f54]+=[/color] [color=#000000]element[/color][color=#000000];[/color]
  [color=#5e6d03]return[/color] [color=#000000]sum[/color] [color=#434f54]/[/color] [color=#000000]N[/color][color=#000000];[/color]
[color=#000000]}[/color]

[color=#5e6d03]template[/color] [color=#434f54]<[/color][color=#00979c]class[/color] [color=#000000]T[/color][color=#434f54],[/color] [b][color=#d35400]size_t[/color][/b] [color=#000000]R[/color][color=#434f54],[/color] [b][color=#d35400]size_t[/color][/b] [color=#000000]C[/color][color=#434f54]>[/color] [color=#000000]T[/color] [color=#000000]average[/color][color=#000000]([/color][color=#00979c]const[/color] [color=#000000]T[/color] [color=#000000]([/color][color=#434f54]&[/color][color=#000000]array2D[/color][color=#000000])[/color][color=#000000][[/color][color=#000000]R[/color][color=#000000]][/color][color=#000000][[/color][color=#000000]C[/color][color=#000000]][/color][color=#000000])[/color] [color=#000000]{[/color]  
  [color=#000000]T[/color] [color=#000000]sum[/color][color=#000000]{}[/color][color=#000000];[/color]
  [color=#5e6d03]for[/color] [color=#000000]([/color][color=#00979c]auto[/color] [color=#434f54]&[/color][color=#000000]row[/color] [color=#434f54]:[/color] [color=#000000]array2D[/color][color=#000000])[/color]
    [color=#000000]sum[/color] [color=#434f54]+=[/color] [color=#000000]average[/color][color=#000000]([/color][color=#000000]row[/color][color=#000000])[/color][color=#000000];[/color]
  [color=#5e6d03]return[/color] [color=#000000]sum[/color] [color=#434f54]/[/color] [color=#000000]R[/color][color=#000000];[/color]
[color=#000000]}[/color]
template <class T, size_t N> T average(const T (&array)[N]) {
  T sum{};  // sum = 0
  for (T element : array)
    sum += element;
  return sum / N;
}

template <class T, size_t R, size_t C> T average(const T (&array2D)[R][C]) {
  T sum{};  // sum = 0
  for (auto &row : array2D)
    sum += average(row);
  return sum / R;
}

void setup() {
  Serial.begin(115200);
  while (!Serial);

  constexpr size_t m = 3;
  constexpr size_t n = 6;

  float test[m][n] = {
    {7.5, 3.6, 4.7, 2.8, 2.9, 1.2 }, // 22.7, average 3.78
    {2.1, 1.2, 7.2, 6.8, 1.9, 9.2 }, // 28.4, average 4.73
    {1.5, 8.6, 3.7, 5.8, 3.9, 9.2 }, // 32.7, average 5.45
  };

  for (auto &row : test) {
    for (auto element : row) {
      Serial.print(element, 1);
      Serial.print(", ");
    }
    Serial.print("→ average = ");
    Serial.println(average(row));
  }

  Serial.print("Total average = ");
  Serial.println(average(test));
}

void loop() {}

I don’t really understand your code. Why is “average” on line 30 static? Why do you return from the function on line 47 after only one iteration? Why is “total” a global variable?

Pieter

PieterP:
Your function declaration is wrong, so the Arduino IDE doesn’t add a prototype for it at the top of the sketch. This means that you cannot use the function before where it is defined.

So explain why it is wrong.

I looked up a couple of methods of passing multidimensional arrays. The one that is closest to what moserroger started with should look like this…

float total = 0;
int numReadings = 10;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600); // for debugging

  const int m = 3;
  const int n = 6;

  float test [m][n] {
    {7.5, 3.6, 4.7, 2.8, 2.9, 1.2 }, // 22.7, average 3.78
    {2.1, 1.2, 7.2, 6.8, 1.9, 9.2 }, // 28.4, average 4.73
    {1.5, 8.6, 3.7, 5.8, 3.9, 9.2 } // 32.7, average 5.45
  };

  for ( int i = 0; i < m; i++) {
    float resultArray[i];
    resultArray[i] = averageMath((float *)test, m, n);
    Serial.print("result ");
    Serial.print(i);
    Serial.print(": ");
    Serial.println(resultArray[i]);
  }//for
}//setup

float averageMath(float *readings, int a, int b) {
  //  static float average[3];
  for (int i = 0; i < a; i++) {
    static float average[3];
    total = 0;
    for (int j = 0; j < b; j++ ) {
      total = total + readings[i*b + j];
      Serial.print("total ");
      Serial.print(i);
      Serial.print("-");
      Serial.print(j);
      Serial.print(": ");
      Serial.println(total);

    }//for j
    average[i] = total / b;
    Serial.print("average ");
    Serial.print(i);
    Serial.print(": ");
    Serial.println(average[i]);
    return average[i];
  }//for i
}//averageMath


void loop() {
  // put your main code here, to run repeatedly:

}

The problem with the function declaration was the brackets () around float*. That makes it a cast. It doesn’t specify the type. Removing the brackets made that work.

Then I had to make the size of the array constant.

Then I changed the addressing on the array inside the function. The notation for multidimensional arrays is just a convenience but it means the compiler must know in advance how big the dimensions are. The function can work just fine by using [i*b + j]

Of course there are other problems with this code. Pieter pointed out that the function exits after averaging the first row of the array. It looks like you intended to return a reference to the array that’s declared inside the function. That’s normally not a good idea as you should never be accessing local storage for a function or object from outside that object.

Unfortunately I am unexperienced. Therefore I don't see the best and most efficient methods (yet).

MorganS: That error often means you have got your braces wrong.

The very fact that you have labelled this brace indicates that you are having difficulty with braces.

Use the auto-format. It will indent your code properly. If it appears to scramble the code rhen the code was wrong.

Thank you MorganS - I saw the labeling of the braces once and found it a good idea. According to your comment I would think that it is not advisable?

I had a look for the braces but couldn't find any problem. May be I overlooked something? I will also try auto-format. Haven't tried that yet.

Gates: not equal to a known value.

Thank you Gates - What do you mean by that?

PieterP: I don't really understand your code. Why is "average" on line 30 static? Why do you return from the function on line 47 after only one iteration? Why is "total" a global variable?

Thank you PieterP - I can declare the total locally, it's probably better. The static comes from the idea that local variables don't exist anymore after the end of the function. Therefore I read in the following post about three ways how to declare it to be able to handle it after the end of the function:

https://www.geeksforgeeks.org/return-local-array-c-function/

MorganS: The problem with the function declaration was the brackets () around float*. That makes it a cast. It doesn't specify the type. Removing the brackets made that work.

Then I had to make the size of the array constant.

Then I changed the addressing on the array inside the function. The [][] notation for multidimensional arrays is just a convenience but it means the compiler must know in advance how big the dimensions are. The function can work just fine by using [i*b + j]

Of course there are other problems with this code. Pieter pointed out that the function exits after averaging the first row of the array. It looks like you intended to return a reference to the array that's declared inside the function. That's normally not a good idea as you should never be accessing local storage for a function or object from outside that object.

Thank you again MorganS - after that I understood how to pass a multidimensional array with the sizes for the rows and columns, I wanted to change the code to be able to pass differently sized arrays to the same function. This seems to be possible with your input?

As MorganS and Pieter point out I am confused on how and where to return the average[]. I would like to be able to return a single dimensional array and use it in the original function. But then again I don't understand where to declare the array, where and how to store the values to it in my function and especialy how to write the code to return it.

Thank you all!

moses

Declare the return array in global scope. Then you don't need to return it.

Try Changing the braces,because the erroe is for that I think.

mayur63690: Try Changing the braces,because the erroe is for that I think.

You think? Can you give us any clues which braces?

I did compile and test my code on actual hardware before posting it. That is a complete, working program although calling it "working" is a bit optimisitic due to the inherent misunderstanding on what should be returned by the function.

MorganS: inherent misunderstanding on what should be returned by the function.

This is a test function to understand the handling of arrays in function calls and returns for the following setting: 1. mesured temperatures and humidities shall be calculated to a running average in the averageMath function. Sensor data, and parameters for the array are passed to the function. 2. the function returns the values of three sensons in one array.

In a subsequent function: 3. calculating overall average 4. daytime average 5. nighttime average

For the last three I will probably need inputs, as I haven't yet understood a concept, that would help to do this.

MorganS: Declare the return array in global scope. Then you don't need to return it.

Therefore I would declare it as a global array and pass it to the function ? How come no need to return it? What happens if I declare it in the function that calls the function that will calculate the average?

MorganS:       total = total + readings[i*b + j];

Could anyone explain to me [i*b+j]?

I multiply the current number of the row with the original number of columns and add the number of the current column? Sorry wouldn't know other words how to describe it.

When I have 3 sensors that need averaging then I call my average function 3 times. Rarely do all sensors need the same average period.

If the array is global then you don't pass it to the function. Another way to do it is if you want m averages from an m×n array, you define an array of length m and pass a pointer to that array to the big average function.

[i*b+j]? This is how you calculate [j] when the inner dimension of the array has b elements.

Hi MorganS

I have 3 temperature and 3 humidity sensors that need to be compared and that I want to average. But it's true I could just call the function once for every sensor. I often get carried away trying to optimize the code.

I wanted to work with the "smoothing code" proposed by arduino.cc:

https://www.arduino.cc/en/Tutorial/Smoothing

I guess I would have to store the array and total in the calling function. Otherwise the data would be lost?

What do you mean by:

"define an array of length m and pass a pointer to that array to the big average function"?

Thank you

moses

moserroger:
What do you mean by:

“define an array of length m and pass a pointer to that array to the big average function”?

As evidenced by the multiple responses, there are many ways to approach this problem. Here’s one:

void average(const float *array2D, size_t nRows, size_t nCols, float *results);

void setup() {
  float myArray[][6] = {
    {7.5, 3.6, 4.7, 2.8, 2.9, 1.2 }, // 22.7, average 3.78
    {2.1, 1.2, 7.2, 6.8, 1.9, 9.2 }, // 28.4, average 4.73
    {1.5, 8.6, 3.7, 5.8, 3.9, 9.2 } // 32.7, average 5.45
  };

  const size_t numRows = sizeof(myArray) / sizeof(myArray[0]);
  const size_t numCols = sizeof(myArray[0]) / sizeof(myArray[0][0]);

  float averageValues[numRows];

  Serial.begin(115200);
  delay(1000);
  Serial.println("Starting");
  Serial.println();

  average((float *)myArray, numRows, numCols, averageValues);
  for (size_t row = 0; row < numRows; row++) {
    Serial.print("Average for Row ");
    Serial.print(row);
    Serial.print(" = ");
    Serial.println(averageValues[row]);
  }
}

void loop() {
}


void average(const float *array2D, size_t nRows, size_t nCols, float *results) {
  float sum;
  for (size_t row = 0; row < nRows; row++) {
    sum = 0.0;
    for (size_t col = 0; col < nCols; col++) {
      sum += *(array2D + row * nCols + col);
    }
    *(results + row) = sum / nCols;
  }
}

If you want a running average of sensor measurements, use either a Simple Moving Average or an Exponential Moving Average. There is no need to save them in an array like that and recalculate the average each time, just keep it updated.

It’s a good idea to create a class for moving averages, so you can create an instance for each sensor. No passing around of pointers required.

For example:

[color=#5e6d03]template[/color] [color=#434f54]<[/color][color=#00979c]uint8_t[/color] [color=#000000]N[/color][color=#434f54],[/color] [color=#00979c]class[/color] [color=#000000]input_t[/color] [color=#434f54]=[/color] [color=#00979c]uint16_t[/color][color=#434f54],[/color] [color=#00979c]class[/color] [color=#000000]sum_t[/color] [color=#434f54]=[/color] [color=#00979c]uint32_t[/color][color=#434f54]>[/color]
[color=#00979c]class[/color] [color=#000000]SMA[/color] [color=#000000]{[/color]
  [color=#00979c]public[/color][color=#434f54]:[/color]
    [color=#000000]input_t[/color] [color=#00979c]operator[/color][color=#000000]([/color][color=#000000])[/color][color=#000000]([/color][color=#000000]input_t[/color] [color=#000000]input[/color][color=#000000])[/color] [color=#000000]{[/color]
        [color=#000000]sum[/color] [color=#434f54]-=[/color] [color=#000000]previousInputs[/color][color=#000000][[/color][color=#000000]index[/color][color=#000000]][/color][color=#000000];[/color]
        [color=#000000]sum[/color] [color=#434f54]+=[/color] [color=#000000]input[/color][color=#000000];[/color]
        [color=#000000]previousInputs[/color][color=#000000][[/color][color=#000000]index[/color][color=#000000]][/color] [color=#434f54]=[/color] [color=#000000]input[/color][color=#000000];[/color]
        [color=#5e6d03]if[/color] [color=#000000]([/color][color=#434f54]++[/color][color=#000000]index[/color] [color=#434f54]==[/color] [color=#000000]N[/color][color=#000000])[/color]
            [color=#000000]index[/color] [color=#434f54]=[/color] [color=#000000]0[/color][color=#000000];[/color]
        [color=#5e6d03]return[/color] [color=#000000]([/color][color=#000000]sum[/color] [color=#434f54]+[/color] [color=#000000]([/color][color=#000000]N[/color] [color=#434f54]/[/color] [color=#000000]2[/color][color=#000000])[/color][color=#000000])[/color] [color=#434f54]/[/color] [color=#000000]N[/color][color=#000000];[/color]
    [color=#000000]}[/color]

  [color=#00979c]private[/color][color=#434f54]:[/color]
    [color=#00979c]uint8_t[/color] [color=#000000]index[/color]             [color=#434f54]=[/color] [color=#000000]0[/color][color=#000000];[/color]
    [color=#000000]input_t[/color] [color=#000000]previousInputs[/color][color=#000000][[/color][color=#000000]N[/color][color=#000000]][/color] [color=#434f54]=[/color] [color=#000000]{[/color][color=#000000]}[/color][color=#000000];[/color]
    [color=#000000]sum_t[/color] [color=#000000]sum[/color]                 [color=#434f54]=[/color] [color=#000000]0[/color][color=#000000];[/color]
[color=#000000]}[/color][color=#000000];[/color]

[color=#5e6d03]template[/color] [color=#434f54]<[/color][color=#00979c]uint8_t[/color] [color=#000000]K[/color][color=#434f54],[/color] [color=#00979c]class[/color] [color=#000000]uint_t[/color] [color=#434f54]=[/color] [color=#00979c]uint16_t[/color][color=#434f54]>[/color]
[color=#00979c]class[/color] [color=#000000]EMA[/color] [color=#000000]{[/color]
  [color=#00979c]public[/color][color=#434f54]:[/color]
    [color=#000000]uint_t[/color] [color=#00979c]operator[/color][color=#000000]([/color][color=#000000])[/color][color=#000000]([/color][color=#000000]uint_t[/color] [color=#000000]x[/color][color=#000000])[/color] [color=#000000]{[/color]
        [color=#000000]z[/color] [color=#434f54]+=[/color] [color=#000000]x[/color][color=#000000];[/color]
        [color=#000000]uint_t[/color] [color=#000000]y[/color] [color=#434f54]=[/color] [color=#000000]([/color][color=#000000]z[/color] [color=#434f54]+[/color] [color=#000000]([/color][color=#000000]1[/color] [color=#434f54]<<[/color] [color=#000000]([/color][color=#000000]K[/color] [color=#434f54]-[/color] [color=#000000]1[/color][color=#000000])[/color][color=#000000])[/color][color=#000000])[/color] [color=#434f54]>>[/color] [color=#000000]K[/color][color=#000000];[/color]
        [color=#000000]z[/color] [color=#434f54]-=[/color] [color=#000000]y[/color][color=#000000];[/color]
        [color=#5e6d03]return[/color] [color=#000000]y[/color][color=#000000];[/color]
    [color=#000000]}[/color]

  [color=#00979c]private[/color][color=#434f54]:[/color]
    [color=#000000]uint_t[/color] [color=#000000]z[/color] [color=#434f54]=[/color] [color=#000000]0[/color][color=#000000];[/color]
[color=#000000]}[/color][color=#000000];[/color]
template <uint8_t N, class input_t = uint16_t, class sum_t = uint32_t>
class SMA {
  public:
    input_t operator()(input_t input) {
        sum -= previousInputs[index];
        sum += input;
        previousInputs[index] = input;
        if (++index == N)
            index = 0;
        return (sum + (N / 2)) / N;
        static_assert(
            sum_t(0) < sum_t(-1),  // Check that `sum_t` is an unsigned type
            "Error: sum data type should be an unsigned integer, otherwise, "
            "the rounding operation in the return statement is invalid.");
    }

  private:
    uint8_t index             = 0;
    input_t previousInputs[N] = {};
    sum_t sum                 = 0;
};

template <uint8_t K, class uint_t = uint16_t>
class EMA {
  public:
    uint_t operator()(uint_t x) {
        z += x;
        uint_t y = (z + (1 << (K - 1))) >> K;
        z -= y;
        return y;
    }

  private:
    uint_t z = 0;
};

void setup() {
  Serial.begin(115200);
  while (!Serial);
}

void loop() {
  static SMA<64, uint16_t, uint32_t> sma;
  static EMA<4, uint32_t> ema;
  uint16_t sensorValue = analogRead(A0);
  Serial.print(sma(sensorValue));
  Serial.print('\t');
  Serial.print(ema(sensorValue));
  Serial.println();
  delay(10);
}

Full explanation of the SMA code: Simple Moving Average C++ Implementation
Theory behind the SMA: Simple Moving Average Filter
Full explanation of the EMA code: Exponential Moving Average C++ Implementation
Theory behind the EMA: Exponential Moving Average Filter

Note how the EMA converges to the new value exponentially, while the SMA converges linearly.

Thank you all for the replies.

The thing I am still unsure of is how to call a function with several sensors. Lets take the following example just for me to get the point:

  • 3 sensors
  • each one calling the same average function once every second
  • the average will be returned after a hour

do I need to store the average or totale or any value in between in the function which is calling the average function to prevent the values to be overwritten?

Thank you all,

moses

If you just need the average once per hour, there’s no need to keep all readings in an array (which is impossible, because you would need at least 10.8 KB of RAM, and the Arduino UNO only has 2.048 KB).

Just sum 3600 samples each time, then divide the sum by 3600.

void setup() {
  Serial.begin(115200);
}

constexpr unsigned int interval = 1000;  // milliseconds

void loop() {
  static unsigned long previousUpdate = millis() - interval;
  
  if (millis() - previousUpdate >= interval) {
    update();  // Update every `interval` = every second
    previousUpdate += interval;
  }
}

constexpr size_t numberOfSensors = 3;

void update() {
  static unsigned int count = 0;
  static float sensorSums[numberOfSensors] = {};
  
  for (size_t i = 0; i < numberOfSensors; ++i)
    sensorSums[i] += getSensorMeasurement(i);
  ++count;
  
  if (count >= 3600) { // 3600 * interval = 3,600,000 ms = 1 hour
    for (size_t i = 0; i < numberOfSensors; ++i) {
      float average = sensorSums[i] / count;
      Serial.print("Sensor ") + Serial.print(i);
      Serial.print(" average = ") + Serial.println(average, 7);
      sensorSums[i] = 0;
    }
    Serial.println();
    count = 0;
  }
}

float getSensorMeasurement(uint8_t sensor) {
  return 42.0 * sensor;  // Your implementation
}

You cannot “store a value in the function”, as it would be overwritten indeed. If you need a stateful function that you have to call on different instances of data, you have to wrap it in a class or struct, as demonstrated in my previous post.

Hello PieterP

Thank you for the message.

I am too much of a newby to know how to use classes. So far I have been working with structs, which I have learned to use by reading about it and in the forum.

As you mentioned I have changed my strategy a bit. My goal is now to have a total and a counter. The counter shall allow me to call the function at any point and be able to calculate the average. I want to divide the averages into day and night time. The total average of each will be saved at the end. This is what it could look like (without the code for time or rtc):

float average(structTEMPControl control, value)
if (time 8:00 - 20:00) {
    daytime = true; //indicate that daytime has started
    control.counter = 0; //for new period set counter to zero
    control.total = 0; // for new period set total to zero
    control.total = control.total + value;
    control.counter++;
else if (daytime == true) { // at the end of daytime
    daytime = false; // indicate end of daytime
    average = total / counter
    if (average < predefined average value)
        start some action;
}
}
if (time 20:00 - 8:00) {
    nighttime = true; //indicate that nighttime has started
    counter = 0; //for new period set counter to zero
    total = 0; // for new period set total to zero
    float value = sensor;
    total = total+value;
    counter++;
    if (sensor < predefined average value)
        average = total / counter;
        if (average < predefined average value)
        start some action;
    }
else if (nighttime == true) { // at the end of nighttime
    nighttime = false; // indicate end of nighttime
    average = total / counter
    if (average < predefined average value)
        start some action;
}
}

I want to show how I have been declaring structs and how I have passed them to functions:

structTEMPControl grTEMPControl[] =
{
    {
    // Sensor1
    .value = Sensor1, // just to show, in sketch refered to sensor
    .total = 0,
    .float = 0
    .counter,
    }, // Sensor1
    {
    // Sensor2
    .value = Sensor2, // just to show, in sketch refered to sensor
    .total = 0,
    .average = 0
    .counter,
    }, // Sensor2

        if ( (someFunction (grTEMPControl[Sensor1]) //calling the function

        float someFunction(structTEMPControl control)

So far I usually passed the struct by reference. Which I would not be able to do in the case of several sensors as the original variable value must be changed.

Now the thing I dont understand at all is:
What to do if I have to different structs and they should both call the same function. How to passe the struct?

Lets say I have structTEMPControl and structLIGHTControl. How to declare the data type of the variable for the called function? Is it because I used deftype so far that I have trouble understanding?

Thank you for the help!

moses

Hello all

I was wondering if it is possible to pass two different structs if the are nested in an other struct? Then the outer struct should be defining the datatype (which would be the same for both) and then the two inner structs can be passed?

struct TEMPControl
{
    variable1 ;
    variable2 ;
    variable3 ;
    variable4 ;
};
struct HUMControl
{
    variable1 ;
    variable2 ;
    variable3 ;
    variable4 ;
};
struct MAINControl
{
    TEMPControl Temperature ;
    HUMControl Humidity ;
} Main;

So I would pass Main.Temperature.variable1 and Main.Humidity.variable4.

The are both of type MAINControl?

Is it necessary to define Main? Or could I use MAINControl.Temperature.variable1 MAINControl.Humidity.variable4 instead? Which would still be of type MAINControl?

Thank you!

moses

I don’t think I understand your question. “MAINControl” is a type, and “Main” is an instance/object of that type. They are fundamentally different things.

Compare it to integers:

int i = 42;

“int” is a type, and “i” is an instance of that type.

Thank you PieterP

Ohhhh, yes of course! Sorry, I got confused because I haven't seen a nested struct up to now. I thought something was weird. But couldn't see and understand that all of what I posted yesterday were just struct definitions. Now I see what you mean and see that instances of the defined struct must be declared. Now I see the parallel to simple structs better.

But it means also that all of the struct has the same datatype MAINControl and therefore I would be able to pass its different members including the nested structs to a function?

Cheers,

moses