How to average ?

Hello !

First, I’d like to apologize for my english, it’s not my native language. And I’m quite a beginner.

I’m currently working on a little project.

Here, my goal is to average several values in coming from a hearth beat sensor.
I’d like to measure the heart beat every seconds, for 10 seconds (for instance, actually it will be an average on 12 hours).

So I did this :

#include <Wire.h>
long svalues = 0;
int average = 0;
int nbmeasures = 0;
long time;

void setup() {
  temps = millis();
  pinMode(5, OUTPUT);
  Serial.begin(9600);
  Serial.println("heart rate sensor:");
  Wire.begin();
  setDestination();
  lapping();
}
void loop() {
  Wire.requestFrom(0xA0 >> 1, 1);    // request 1 bytes from slave device 
  while(Wire.available()) {          // slave may send less than requested
    unsigned char c = Wire.read();   // receive heart rate value (a byte)
    Serial.println(c, DEC);         // print heart rate value
  }
  delay(500);
}



// Xbee configuration
void setDestination() {
  Serial.print("ATRE\r");
  Serial.print("ATDL0\r"); // sent to xbee 0
  Serial.print("ATMY1\r"); // this xbee is 1
  Serial.print("ATID1111\r");
  Serial.print("ATCN\r");
}
void lapping() {
    
  Wire.requestFrom(0xA0 >> 1, 1);    // request 1 bytes from slave device 
  while(Wire.available()) {          // slave may send less than requested
     while (nbmeasures < 10){
     if((millis() - time) > 1000)
        {
     unsigned char value = Wire.read();   // receive heart rate value (a byte)
     svalues = svalues + values;
     nbmesures++;
     temps = millis(); 
        }
  digitalWrite(5, HIGH);     // 
  delay(10);  
     }
  average = svalues / nbmeasures;
  Serial.println("average");
  Serial.println(average);
  digitalWrite(5, LOW); 
 
}
}

But, I never had a correct average, even if I initialize “nbvalues” at 1 or 0… How can I fix it ?

Thank you for your answers !

You have to initialize svalues to zero, every time you start to collect a new set of samples.

It seems likely that Wire.read() is going to return to you a character, not an integer number. For example, if the heartbeat is 123 beats per minute, you will receive the characters: '1' '2' '3'. These are ASCII characters, so the the values are:

'1' = 49
'2' = 50
'3' = 51

These characters need to be converted to an integer value if you want to perform an arithmetic average on them. There are several algorithms that you can use to make the conversion. The easiest is to convert the ASCII characters to a string by saving them in a character array, add a null termination character ('\0') to the array, and then use the atoi() function to make the conversion.
,

econjack:
It seems likely that Wire.read() is going to return to you a character, not an integer number. For example, if the heartbeat is 123 beats per minute, you will receive the characters: '1' '2' '3'. These are ASCII characters, so the the values are:

'1' = 49
'2' = 50
'3' = 51

These characters need to be converted to an integer value if you want to perform an arithmetic average on them. There are several algorithms that you can use to make the conversion. The easiest is to convert the ASCII characters to a string by saving them in a character array, add a null termination character ('\0') to the array, and then use the atoi() function to make the conversion.
,

I don't think so, or maybe it's converted automatically because the values returned on the serial monitor are integer numbers. Also, if i average a single value, it's working.

I'll try the solution of jremington ! But I think I didn't really understand it, you mean I should add "svalues=0" in the while loop ?

I tried to initialize svalues, but I still have a wrong average. ( Always twice a normal value or a half of it )

Post you code again with those changes please...

Actually, as I didn’t understand the advice I tried to put it everywhere…

But, there it is :

void lapping() {
    
  Wire.requestFrom(0xA0 >> 1, 1);    // request 1 bytes from slave device 
  while(Wire.available()) {          // slave may send less than requested
  svalues =0;   
  while (nbmeasures < 10){
     if((millis() - time) > 1000)
        {
     unsigned char value = Wire.read();   // receive heart rate value (a byte)
     svalues += value;
     nbmesures++;
     temps = millis(); 
        }
  digitalWrite(5, HIGH);     // 
  delay(10);  
     }
  average = svalues / nbmeasures;
  Serial.println("average");
  Serial.println(average);
  digitalWrite(5, LOW); 
 
}
}

Here is one method to average 10 readings:

int sum = 0;
for (i=0; i< 10; i++) {
int value = get_input();
sum = sum + value;
}
int average=sum/10;

You can't have wire.available() outside a while loop and wire.read() inside the loop. That way, the test for available is only performed for the first character.

Thank you for your replies !

So, aware of your advices I made this, but it’s still not working. Honestly, I don’t really understand why.

There is the code :

    void lapping(){

     Wire.requestFrom(0xA0 >> 1, 1);                        // request 1 bytes from slave device 
     const int number_of_values = 10;
     int tab_values[number_of_values];
     for (unsigned int i=0; i <= number_of_values; i++) { tab_values[i] = 0; }
     unsigned int cursor = 0;
     
     for(cursor=0; cursor < number_of_values; cursor++){
     if((millis() - temps) > 1000)
        {
     while(Wire.available()) {
     int new_value = Wire.read();     // receive heart rate value (a byte)
     tab_values[cursor] = new_value;
     temps = millis();                      
     }
     if (cursor == number_of_values)  { cursor =0; }
        }
  digitalWrite(5, HIGH);     
  delay(10);  
     }
  unsigned int sum_of_values = 0;
  for ( unsigned i=0;  i<= number_of_values; i++) { sum_of_values += tab_values[i]; }
  byte average;
  average = sum_of_values / number_of_values;
  Serial.println("average");
  Serial.println(average);
  digitalWrite(5, LOW); 
 
}

And there is what we can see on the serial monitor :

heart rate sensor:
ATRE
ATDL0
ATMY1
ATID1111
ATCN
average
0
87
88
88
88
88

Plus, we can see that the time function is wrong, the function lapping() does not last 10 seconds, so I guess that there is no 10 values either.

So I also tried whithout the time() function, using a simple delay. So the duration seems consistent but i have an average of 5…

void lapping(){

     Wire.requestFrom(0xA0 >> 1, 1);                        // request 1 bytes from slave device 
     const int number_of_values = 10;
     int tab_values[number_of_values];
     for (unsigned int i=0; i <= number_of_values; i++) { tab_values[i] = 0; }
     unsigned int cursor = 0;
     
     for(cursor=0; cursor < number_of_values; cursor++){
    
     while(Wire.available()) {
     int new_value = Wire.read();     // receive heart rate value (a byte)
     tab_values[cursor] = new_value;
                           
     }
     if (cursor == number_of_values)  { cursor =0; }
        
  digitalWrite(5, HIGH);     
  delay(10); 
delay(1000); 
     }
  unsigned int sum_of_values = 0;
  for ( unsigned i=0;  i<= number_of_values; i++) { sum_of_values += tab_values[i]; }
  byte average;
  average = sum_of_values / number_of_values;
  Serial.println("average");
  Serial.println(average);
  digitalWrite(5, LOW); 
 
}

With some tests I saw where the problem is. Actually, the tab is not filling up. Only the first value is filled up with a value of the heart beat. So the sum is equal to a single value ( like 50 ), and when the program averages the values, it divises the sum by 10, so i have an average of 5... Why the tab does not fills up ?

with a value of the hearth beat.

A hearth is that rocky area in front of a fireplace.

A heart is that thing that pumps blood.

I seriously doubt that you are measuring a hearth beat.

Who said that my sensor is not underground ?

I edited :slight_smile:

HRadar:
I don't think so, or maybe it's converted automatically because the values returned on the serial monitor are integer numbers. Also, if i average a single value, it's working.

If you type in the digit 1 into the Serial monitor, what's the value you see printed? My guess is that it is 49, which is what I said in my first post. Also, if you "average a single value", I'm not surprised that it appears to be working: the average of a single value is the value.

You need to google "ASCII Codes" and understand what is presented there before you can even hope to tackle this problem.

you actually have a few issues in you code, and if you properly format it, you may see them:

like this problem here:

while (Wire.available()) 
    {
      int new_value = Wire.read();     // receive heart rate value (a byte)
      tab_values[cursor] = new_value;
    }

and what are you doing here? as cursor should NEVER increment in the for-loop past number_of_values minus one:

  if (cursor == number_of_values)  
    {
      cursor = 0;
    }

PROPERLY FORMATTED CODE:

void lapping() 
{
  Wire.requestFrom(0xA0 >> 1, 1);                        // request 1 bytes from slave device
  const int number_of_values = 10;
  int tab_values[number_of_values];
  for (unsigned int i = 0; i <= number_of_values; i++) 
  {
    tab_values[i] = 0;
  }
  unsigned int cursor = 0;
  for (cursor = 0; cursor < number_of_values; cursor++) 
  {
    while (Wire.available()) 
    {
      int new_value = Wire.read();     // receive heart rate value (a byte)
      tab_values[cursor] = new_value;
    }
    if (cursor == number_of_values)  
    {
      cursor = 0;
    }
    digitalWrite(5, HIGH);
    delay(10);
    delay(1000);
  }
  unsigned int sum_of_values = 0;
  for ( unsigned i = 0;  i <= number_of_values; i++) 
  {
    sum_of_values += tab_values[i];
  }
  byte average;
  average = sum_of_values / number_of_values;
  Serial.println("average");
  Serial.println(average);
  digitalWrite(5, LOW);

}

If I type 1, 1 is printed. I don’t understand exactly what you mean. Maybe I should have say that the sensor send a value already converted with the I2C protocol.

Moreover, the code given by the datasheet don’t have any converstion function ( I’m using this code, look at my first post ).

#include <Wire.h>
void setup() {
  Serial.begin(9600);
  Serial.println("heart rate sensor:");
  Wire.begin();
}
void loop() {
  Wire.requestFrom(0xA0 >> 1, 1);    // request 1 bytes from slave device 
  while(Wire.available()) {          // slave may send less than requested
    unsigned char c = Wire.read();   // receive heart rate value (a byte)
    Serial.println(c, DEC);         // print heart rate value
  }
  delay(500);
}

I am right ? Or I’m missing something ? As i said, the only problem seems to be that the tab is not filling up.

www.asciitable.com is very useful to see ASCII, decimal, HEX, and character codes all at once.

BulldogLowell:
you actually have a few issues in you code, and if you properly format it, you may see them:

like this problem here:

while (Wire.available()) 

{
      int new_value = Wire.read();    // receive heart rate value (a byte)
      tab_values[cursor] = new_value;
    }




and what are you doing here? as cursor should NEVER increment in the for-loop past number_of_values minus one:



if (cursor == number_of_values) 
    {
      cursor = 0;
    }





PROPERLY FORMATTED CODE:




void lapping()
{
  Wire.requestFrom(0xA0 >> 1, 1);                        // request 1 bytes from slave device
  const int number_of_values = 10;
  int tab_values[number_of_values];
  for (unsigned int i = 0; i <= number_of_values; i++)
  {
    tab_values[i] = 0;
  }
  unsigned int cursor = 0;
  for (cursor = 0; cursor < number_of_values; cursor++)
  {
    while (Wire.available())
    {
      int new_value = Wire.read();    // receive heart rate value (a byte)
      tab_values[cursor] = new_value;
    }
    if (cursor == number_of_values) 
    {
      cursor = 0;
    }
    digitalWrite(5, HIGH);
    delay(10);
    delay(1000);
  }
  unsigned int sum_of_values = 0;
  for ( unsigned i = 0;  i <= number_of_values; i++)
  {
    sum_of_values += tab_values[i];
  }
  byte average;
  average = sum_of_values / number_of_values;
  Serial.println(“average”);
  Serial.println(average);
  digitalWrite(5, LOW);

}

Thank you ! But even with this code, I still have an wrong average, only the fist tab box is filled. Did I missunderstood your message ? What are the errors ?

HRadar:
Thank you ! But even with this code, I still have an wrong average, only the fist tab box is filled. Did I missunderstood your message ? What are the errors ?

I didn't fix your code, I just tried to show you problems.

In this while loop, as long as there is available(), you fill the SAME element of the array:

while (Wire.available()) 
    {
      int new_value = Wire.read();     // receive heart rate value (a byte)
      tab_values[cursor] = new_value;
    }

you don't want that, I think...

BulldogLowell:
I didn't fix your code, I just tried to show you problems.

In this while loop, as long as there is available(), you fill the SAME element of the array:

while (Wire.available()) 

{
      int new_value = Wire.read();    // receive heart rate value (a byte)
      tab_values[cursor] = new_value;
    }




you don't want that, I think...

Just to try, I deleted the loop while(Wire.available()) but there is still only one element filled.
Just to remind you, please excuse me, I'm just a beginner :slight_smile: