Arrays and average

Hello everybody,
I'm not an expert so maybe I'm doing some sacrilegious mistakes.
I am trying average the values of a CO2 sensor but the results are inconsistent.
Here you have a test code. After running for a while, it starts giving nonsense values.

float co2 = 3;
float sum = 0;
int i = 0;
float co2Arr[200];


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

void loop() {
  // put your main code here, to run repeatedly:
if(i<200){
  co2Arr[i] = co2;
  i++;
}
else{
  for(int j = 0; j<=200; j++){
    sum += co2Arr[j];
  }
  float average = suma/200;
  Serial.println("the average");
  Serial.println(average);
  memset(co2Arr, 0, 200);
  average = 0;
  sum = 0;
  i=0;
 }
}

Help will be appreciated!!!

are you using an Uno? Have you considered the size of a 200 element float array in relation to the MCU being used?

Here is a link to a moving average filter, have a look at the code.

This does not clear the data array:
memset(co2Arr, 0, 200);

This does:

for (int k=0; k<200; k++) co2Arr[k]=0.0;

But the way you are using the array, it doesn't matter whether you clear it.

After fixing this mistake,
for(int j = 0; j<=200; j++){
to:
for(int j = 0; j<200; j++){

And this mistake:
float average = suma/200;
to
float average = sum/200;

the program runs as expected, and one learns that the average of "3" is 3.00.

That puts you beyond the array. 0..199 are valid for 200 elements.

And would be 201 elements anyway, making

  float average = suma/200;

slightly wrong too!

File it sorta under "off by one".

a7

1 Like

I'd expect the number to be 3, except for this:

If it loops for 0, 200, and each number in between, that would 201 different numbers. Depending on whether the next 4 bytes in memory are zero or not, you'd get 3 or not.

( edit: Oops. I didn't read the comments -- What they said)

It seems at least probable that "the worst" that could go wrong is picking up a random number, but I think the act of reading at j == 200 means anything can happen.

a7

1 Like

have you considered leaky integration

// running average (leaky integration)

float  avg;
const float K = 0.125;

// -----------------------------------------------------------------------------
void
loop ()
{
    int samp = analogRead (A0);

    if (0 != avg)
        avg += (samp - avg) * K;
    else
        avg = samp;

    Serial.println (avg, 0.01);
    delay (250);
}

// -----------------------------------------------------------------------------
void
setup ()
{
    Serial.begin (9600);

Leaky integration? I learned that as an Exponentially Weighted Moving Average.

it's weighted in the sense that newer samples have greater weight.

the advantage is the average is updated with each sample and requires little memory

it's also a 1st order digital low-pass filter

Thanks you all for this quick response!!! I'll inform you on my progress.

No one has pointed out yet that the array is not needed at all, in @gbg_4 's current code. It uses 800B of ram memory for no reason.

It waits until 200 values have been accumulated in the array, then calculates the total and then the average, prints the average, then clears all values and starts over (or at least it will do when the errors pointed out have been corrected)

So the array is not needed. The total could be updated as the values arrive, without storing them in an array. Once 200 values have been received, the average can be calculated and printed as they are now.

1 Like

in other words

float co2 = 3;
float sum = 0;

int   n   = 0;
#define N   200

void loop()
{
    if (++n < N)
        sum += co2;
    else  {
        float average = sum / N;
        Serial.print   ("the average ");
        Serial.println (average);
        sum = n = 0;
    }
}

void setup() {
    Serial.begin(9600);
}
2 Likes

The reason it doesn't is that '200' is a size in bytes. The size of 200 floats is 800 bytes.

Use memset(co2Arr, 0, sizeof co2Arr); if you want to set every byte in the array to 0.

Note: There is no reason to store 200 samples if all you are going to do is add them up:

if (i<200) {
  sum += co2;
  i++;
}
else {
    Serial.println("the average");
    Serial.println(sum / 200);
    sum = 0;
    i = 0;
  }

Thanks you all, it works as espected now.
I have implemented the leaky integration and I have corrected the for loop's mistakes.

Thanks for the correction, I think that with all this answers I will be able to solve more than the current project!!!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.