Arduino average

I want to measure the temperature of the room at intervals of 1 minute, and average it every 5 minutes. But I don't know how to average it. What code should I add to average?

#include <DHT11.h>
const int pin  = A1;

DHT11 dht11(pin);

void setup() {
  Serial.begin(9600);
  Serial.println();

}

void loop() {
   int err;
   float humi, temp;
    if((err=dht11.read(humi, temp))==0){
    Serial.print("temp= "); Serial.print(temp); Serial.print("C");
    delay(60000);
    Serial.println();
   }
   
  
}

Add an array of length 5, a variable to point to one of the 5 elements, and use it as a circular buffer. Each minute you'd write a new value in the buffer, do the bookkeeping, then sum and average the 5 elements & use it.

1 Like
#include <DHT11.h>
const int pin  = A1;

DHT11 dht11(pin);

void setup() {
  Serial.begin(9600);
  Serial.println();
}

void loop() {
  int err;
  static float TEMP = 0.0;
  float humi, temp;
  if ((err = dht11.read(humi, temp)) == 0) {
    TEMP = (TEMP + temp) / 2.0;
    Serial.print("temp= "); Serial.print(TEMP); Serial.print("C");
    delay(20000);
    Serial.println();
  }
}

think about it how you would do it on a piece of paper by hand
writing down the 1st measuring
writing down the 2nd measuring
writing down the 3rd measuring
writing down the 4th measuring
writing down the 5th measuring

add them all
divide by 5

now there are two different methods

  1. start a completely new measuring period every five minutes then the above is sufficient

  2. moving average which means
    take measuring of 1st minute out add 6th measuring divide by 5

and this can be done by using an array
with 5 elements.
counting up the indexnumber
storing the new measuring in that array-element
sum up all 5 new divide by 5

Arrays are explained here. From the fact that arrays are paragraph 17 you can conclude you should know some basic things before learning arrays

If you have at least learn what variables are you can jump forward to learn arrays

Here is a code-version where you have to find out two things to make it work with your sensor and your Arduino-IDE
reactivating the dht-library
adjusting serial communication with IDE

This code is consequently structured in functions where each function is a senseful subunit of code
It uses non-blocking timing. You will have to adjust the timing-constant to 1 minute


//#include <DHT11.h>
const int pin  = A1;

unsigned long myMeasuringTimer;
const unsigned long MeasuringINterval = 2000;

byte IndexNr = 0;
float myTempartureArray[5]; // FIVE elements with index 0,1,2,3,4
float myTestData[5];        // FIVE elements with index 0,1,2,3,4
float myTempartureSum;
float myTempartureAverage;

//DHT11 dht11(pin);

void setup() {
  Serial.begin(115200);
  Serial.println("Setup-Start");
  for (IndexNr = 0; IndexNr < 5; IndexNr++) {
    myTempartureArray[IndexNr] = 0.0;
    printExplanation1(IndexNr, 0.0);
  }
  IndexNr = 0;

  myTestData[0] = 1.0;
  myTestData[1] = 2.0;
  myTestData[2] = 3.0;
  myTestData[3] = 4.0;
  myTestData[4] = 5.0;
  Serial.println("setup done");
  Serial.println();
}


void loop() {
  int err;
  float humi;
  float temp;

  if ( TimePeriodIsOver(myMeasuringTimer, MeasuringINterval) ) {

    //if ( (err = dht11.read(humi, temp)) == 0) {
    myTempartureArray[IndexNr] = myTestData[IndexNr];
    printExplanation1(IndexNr, myTestData[IndexNr]);

    calculateAverage();
    
    IndexNr++;
    if (IndexNr == 5) { // count 0,1,2,3,4 which are 5 elements
      IndexNr = 0;
    }
  }
}


// easy to use helper-function for non-blocking timing
boolean TimePeriodIsOver (unsigned long & startOfPeriod, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - startOfPeriod >= TimePeriod ) {
    // more time than TimePeriod has elapsed since last time if-condition was true
    startOfPeriod = currentMillis; // a new period starts right here so set new starttime
    return true;
  }
  else return false;            // actual TimePeriod is NOT yet over
}

void printExplanation1(byte p_Idx, float p_value) {
  Serial.print("assigning myTempartureArray[");
  Serial.print(p_Idx);
  Serial.print("] value ");
  Serial.println(p_value);
}


void printExplanation2(byte p_Idx, float p_value) {
  Serial.print("+ myTempartureArray[");
  Serial.print(p_Idx);
  Serial.print("] has value ");
  Serial.println(p_value);
}


void calculateAverage() {
  Serial.println("calculating average");
  myTempartureSum = 0.0;
  
  for (int i = 0; i < 5; i ++) {
    myTempartureSum += myTempartureArray[i];
    printExplanation2(i, myTempartureArray[i]);
  }
  Serial.print("myTempartureSum= ");
  Serial.println(myTempartureSum);

  myTempartureAverage = myTempartureSum / 5.0;

  Serial.print("myTempartureAverage= ");
  Serial.print(myTempartureAverage);
  Serial.print("C");
  Serial.println();
  Serial.println();
}

best regards Stefan

1 Like

use a running average (leaky integration)

avg += (samp - avg) / N

try an N of 8

1 Like

Use a low pass filter, colloquially referred to as leaky integration

  average = 0.9 * average + 0.1 * sample;

0.9, most of the old average, 0.1 a bit of influence from the new sample.

Later samples have more influence.

Just use constants that add to 1, like 0.9 and 0.1, or 0.8 and 0.2 and so forth.

Try 0.875 and 0.125, for example.

a7

1 Like

An N=8N=5 (oops, thx Alto777) or 0.8 & 0.2 would give the same 20% weight to the most recent observation as the 1 minute out of 5 in the OP.

Those two algorithms are equivalent, and are sometimes called an "Exponentially Weighted Moving Average", since the i-th older observations in to the past are weighted (1/N)^i or alpha^i in the average. The EWMA as a low pass filter is a good procedure, since it is simple and requires minimum memory (1 state variable) and processing (1 multiplication per observation), and with the right weight parameter, can produce the statistically "optimal" estimate for many underlying processes.

avg reaches ~95% of input after 3*N samples (like RC time constant)

Check my maths. N = 8 would be 0.875 and 0.125 to make the two expressions equivalent.

But yes, the two statements do the same thing. The more verbose less efficient expression shows the process better, and is, indeed, what one might write after seeing a graphic representation of the low pass filter:


Exponential_Averaging_FIGURE1


Where alpha is the (small) part of the sample and (1 - alpha) is the (large) part of the old average.

Neither is a running average.

a7

1 Like

@gcjr One can "short-circuit" the 3RC "filltime" by seeding the algorithm - assign a single reading to the average in setup(). Then just start with whatever averaging strategy is preferred.
Clearly, depends on the noise type and magnitude, but often useful.

2 Likes

You are right. I was aiming for the OP's sample five and average post and mistyped.

There's lots of low pass filters, but the easy one in electronics is the RC network which has exponetial decay.

The common one folks think of on a spreadsheet or in code is a block mean (like the OP) or rolling window moving average (like my first post) or a rolling window median, or other central tendency statistic. (ETA: Oh, and there's the simple running averages based on all the data (sum(x)/n), or year-year-to-date, both of which are worse forecasters than the EWMA)

I'm partial to the EWMA/RC/lowpass filter since I spent a couple years on a thesis applying them to certain processes--The most recent observation is the best forecast, unless there's some benefit from considering deeper history, (but that has diminishing returns the deeper into the past you dig.)

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