How can I average a TMP-36 reading?

Okie doke, I had an issue in another thread unrelated to this specific region of my project, so I felt a new thread was in order- Not to mention I just thought of this…

My project is simple; displaying the temperature from a TMP-36 on an oLED display.
My goal is to be able to average 5 readings and display the calculated average from that.

int sensorPin = 0; //the analog pin the TMP36's Vout (sense) pin is connected to
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);

#define NUMFLAKES 10
#define XPOS 0
#define YPOS 1
#define DELTAY 2


#define LOGO16_GLCD_HEIGHT 16 
#define LOGO16_GLCD_WIDTH  16 

#if (SSD1306_LCDHEIGHT != 64)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif

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

  // by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3C (for the 128x64)
  // init done

  
display.clearDisplay();     
display.setTextColor(WHITE);
display.setTextSize(2);
display.setCursor(32,0); 
display.print("Temp:");
display.setTextSize(1);
display.setCursor(0,0); 
display.print("TMP36");
display.display();
}


void loop() {
 int reading = analogRead(sensorPin);  
 
 // converting that reading to voltage, for 3.3v arduino use 3.3
 float voltage = reading * 5.0;
 voltage /= 1024.0; 
 
 // print out the voltage
// Serial.print(voltage); Serial.println(" volts");
 
 // now print out the temperature
 float temperatureC = (voltage - 0.5) * 100 ;  //converting from 10 mv per degree wit 500 mV offset
                                               //to degrees ((voltage - 500mV) times 100)
// Serial.print(temperatureC); Serial.println(" degrees C");
 
 // now convert to Fahrenheit
 float temperatureF = (temperatureC * 9.0 / 5.0) + 32.0;
 
display.setTextColor(WHITE, BLACK);
display.setTextSize(3);
display.setCursor(8,18); 
display.print(temperatureF);
display.print("F");
display.setTextSize(1);
display.setCursor(0,40); 
display.print(temperatureC);
display.print("C");
display.display();
Serial.print(temperatureF); Serial.println(" degrees F");
delay(750);
}

Is my current code, I don’t know how to store the values in my Arduino in order to do the calculation.

I’ve looked around and I can’t find all that much on this. Thanks everyone, I’m slowly learning!

#define NO_OF_READINGS 5
float readings[NO_OF_READINGS]={0.0,0.0,0.0,0.0,0.0};


void save_reading( float new_reading )
{
  // move old readings down to make room for new one
  for( int i=1; i<NO_OF_READINGS; i++ )
  {
    readings[i-1] = readings[i];
  }
  // add new reading
  readings[NO_OF_READINGS - 1]= new_reading;
}

float average_readings()
{
  float total= 0.0;
  for( int i=0; i<NO_OF_READINGS; i++ )
  {
    total += readings[i];
  }
  return total / (float)NO_OF_READINGS;

}

Yours,
TonyWilk

Here’s one simple way, google “Arduino analog smoothing” for many more.

void setup()
{
  Serial.begin(9600);
}
void loop()
{
  int total = 0;
  for(int i = 0;i < 16;i++) // accumulate 16 readings in total
    total += analogRead(A0);
  Serial.println(total / 16); // divide by 16 and print
  delay(1000);  
}

outsider:
Here’s one simple way, google “Arduino analog smoothing” for many more.

void setup()

{
  Serial.begin(9600);
}
void loop()
{
  int total = 0;
  for(int i = 0;i < 16;i++) // accumulate 16 readings in total
    total += analogRead(A0);
  Serial.println(total / 16); // divide by 16 and print
  delay(1000); 
}

Can you break this down in detail for me? What does each operator do? Thanks, appreciate it.

Is this good enough? :slight_smile:

void setup()
{
  Serial.begin(9600);
}
void loop()
{
  int total = 0; // declare a 16 bit signed integer (int)
                 // named "total"
  for(int i = 0;i < 16;i++) // the "for" loop does the next line 16 times
    total += analogRead(A0); // Get an analog input reading from pin A0
                             // and add to total
  Serial.println(total / 16); // print the results of total divided by 16
  delay(1000); // wait one second
}

outsider:
Is this good enough? :slight_smile:

void setup()

{
  Serial.begin(9600);
}
void loop()
{
  int total = 0; // declare a 16 bit signed integer (int)
                // named “total”
  for(int i = 0;i < 16;i++) // the “for” loop does the next line 16 times
    total += analogRead(A0); // Get an analog input reading from pin A0
                            // and add to total
  Serial.println(total / 16); // print the results of total divided by 16
  delay(1000); // wait one second
}

How does int i = 0;i < 16;i++ work? I’d like to familiarize myself with this type of loop as I see it sometimes, but don’t quite understand it.

Thanks again.

I'd like to familiarize myself with this type of loop

Have a look at:

Reference > Language > Structure > Control structure > For

or

https://www.cprogramming.com/tutorial/c/lesson3.html

Yours,
TonyWilk

The other option is an Exponential Moving Average:

It's a type of IIR filter.

In the IDE menu bar, click HELP -> Reference:
https://www.arduino.cc/reference/en/language/structure/control-structure/for/
https://www.arduino.cc/en/Reference/HomePage

Ahh, I understand the loop now. All is going good! Thanks everyone for all the help! :smiley:

Actually, I just realized something.

138.88Favg
137.12 degrees F
136.24Favg
135.36 degrees F
133.60Favg
134.48 degrees F
130.96Favg
131.84 degrees F

I’m doing something wrong, but I can’t figure it out. I want a 30 second average, I heated up the sensor for just a second, if it’s an average, why would it basically reflect the current value?

My code so far

int sensorPin = 0; //the analog pin the TMP36's Vout (sense) pin is connected to
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);

#define NUMFLAKES 10
#define XPOS 0
#define YPOS 1
#define DELTAY 2


#define LOGO16_GLCD_HEIGHT 16 
#define LOGO16_GLCD_WIDTH  16 

#if (SSD1306_LCDHEIGHT != 64)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif

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

  // by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3C (for the 128x64)
  // init done

  
display.clearDisplay();     
display.setTextColor(WHITE);
display.setTextSize(2);
display.setCursor(32,0); 
display.print("Temp:");
display.setTextSize(1);
display.setCursor(0,0); 
display.print("TMP36");
display.display();
}


void loop() {
    int total = 0;
  for(int i = 0;i < 30;i++) // accumulate 5 readings in total
    total += analogRead(A0);
int avg = (total / 30); // divide by 30
//  Serial.println(avg); 

 int reading = analogRead(sensorPin);  
 /////////////////////////////////////////////////

  float voltageavg = avg * 5.0;
 voltageavg /= 1024.0; 
 
 // now print out the temperature
 float temperatureCavg = (voltageavg - 0.5) * 100 ;  //converting from 10 mv per degree wit 500 mV offset
                                               //to degrees ((voltage - 500mV) times 100)
// Serial.print(temperatureC); Serial.println(" degrees C");
 
 // now convert to Fahrenheit
 float temperatureFavg = (temperatureCavg * 9.0 / 5.0) + 32.0;

 Serial.print(temperatureFavg);
 Serial.println("Favg");
 ///////////////////////////////////////////////////

 float voltage = reading * 5.0;
 voltage /= 1024.0; 
 
 // now print out the temperature
 float temperatureC = (voltage - 0.5) * 100 ;  //converting from 10 mv per degree wit 500 mV offset
                                               //to degrees ((voltage - 500mV) times 100)
// Serial.print(temperatureC); Serial.println(" degrees C");
 
 // now convert to Fahrenheit
 float temperatureF = (temperatureC * 9.0 / 5.0) + 32.0;
 ///////////////////////////////////////////////////
 
display.setTextColor(WHITE, BLACK);
display.setTextSize(3);
display.setCursor(8,18); 
display.print(temperatureF);
display.setTextSize(2);
display.print("F");
display.setTextSize(1);
display.setCursor(0,40); 
display.print(temperatureC);
display.print("C");
display.display();
Serial.print(temperatureF); Serial.println(" degrees F");
delay(1000);
}

How many samples in 30 seconds?

outsider:
How many samples in 30 seconds?

It should be 30, with a 1000ms delay at the end of the loop, right?

  for(int i = 0;i < 30;i++) // accumulate 5 readings in total

What you think of this rude & crude start? Only ADC results, not yet converted to temperature.

unsigned long tStart;
int tEnd = 1000,
    reading;      
    
float temp,
      total,
      average;            
byte counter,
     samples = 30;


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

void loop()
{
  if(millis() - tStart > tEnd)
  {
    tStart += tEnd;
    reading = analogRead(A0);
    total += reading;
    average = total / samples;
    Serial.print(" reading #");
    Serial.print("  ");
    Serial.print(counter + 1);
    Serial.print(" \t");
    Serial.println(reading);
    if(++counter >= samples)
    {
      counter = 0;
      total = 0;
      Serial.println();
      Serial.print(" 30 second average = ");
      Serial.println(average);
      Serial.println();
    }    
  }
}

For a rolling average like this, I like to use a software low-pass filter.

double smoothed = some_sensible_initial_value;

void loop() {
  if(its time to take a sample) {
    double sample = take_a_sample();
    smoothed = smoothed * .75 + sample * .25; 
  }
}

If you tweak the .75/.25, you get a filter that responds quickly or slowly to fluctuations. If you make two filters - a quick one and a slow one - the difference can tell you something about how fast the temperature is changing. Eg, you could ignore the data temperature unless both values more-or-less agree, so filtering out transient spikes.

Slick, I’m going to try that on my current blunder project, an LM34 (Fahrenheit) thats being a real bear to smooth,
TNX & k++.