Problem with minimum temperature, factor of 100 out.

Hi,

I have written a code to sample the temperature from a TM35 and following smoothing output the current temperature, lowest and highest temperature since reset to an LCD display and the serial port.

However the temp and max temp are reported correctly the min temp is reported as 100x smaller than the temp and I cannot see why.

I also want to set up a routine so that the LCD display only updates at a preset interval, i.e 10 seconds, but the serial continues to output in real time.

Hopefully someone can tell me what's wrong.

Output from serial

Temp =18.97 Min Temp =0.19 Max Temp =26.03
Temp =18.97 Min Temp =0.19 Max Temp =26.03
Temp =18.97 Min Temp =0.19 Max Temp =26.03
Temp =18.97 Min Temp =0.19 Max Temp =26.03
Temp =18.97 Min Temp =0.19 Max Temp =26.03
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// Define the number of samples to keep track of.  The higher the number,
// the more the readings will be smoothed, but the slower the output will
// respond to the input.  Using a constant rather than a normal variable lets
// use this value to determine the size of the readings array.
const int numReadings = 100;

int readings[numReadings];      // the array readings from the analog input
int index = 0;                  // the index of the current reading
float totaltemp = 0;            // the running total
float averagetemp = 0;          // the average temperature
float maxtemp = -255.0;             // the max temperature
float mintemp = 255.0;              // the min temperature
float temp;                       // the current temperature
char buffer[64];

int tempPin = A0;


// Set up the LCD
// set the LCD address to 0x27 for a 16 chars 2 line display
// Set the pins on the I2C chip used for LCD connections:
//                    addr, en,rw,rs,d4,d5,d6,d7,bl,blpol
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address


void setup()
{
  // initialize serial communication with computer:
  Serial.begin(9600);                   
  // initialize all the readings to 0 for gettemp 
  for (int thisReading = 0; thisReading < numReadings; thisReading++)
    readings[thisReading] = 0;  

  lcd.begin(16,2);   // initialize the lcd for 16 chars 2 lines, turn on backlight
  lcd.backlight();   // turnbacklight on intially      
}

void loop()
{

  gettemp();
  delay(2);        // delay in between reads for stability    
  // check to see if the currenttemp is the lowest temperature since reset
  // if it is then set lowtemp to currenttemp
  if (temp < mintemp) mintemp = temp;
  // check to see if the current temp is the highest temperature since reset
  // if it is then set hightemp to currenttemp
  if (maxtemp < temp) maxtemp = temp;
  Serial.print("Temp =");
  Serial.print(temp);
  Serial.print("Min Temp =");
  Serial.print(mintemp);
  Serial.print("Max Temp =");
  Serial.print(maxtemp);
  Serial.println("");
  updatedisplay();
}



int gettemp() // routine the get the average temp from the sensor
{

  // subtract the last reading:
  totaltemp= totaltemp - readings[index];         
  // read from the sensor:  
  readings[index] = ((5.0 * analogRead(tempPin) * 100.0)/1024); 
  // add the reading to the total:
  totaltemp= totaltemp + readings[index];       
  // advance to the next position in the array:  
  index = index++;                    

  // if we're at the end of the array...
  if (index >= numReadings)              
    // ...wrap around to the beginning: 
    index = 0;                           

  // calculate the average:
  temp = totaltemp / numReadings;         
  // send it to the computer as ASCII digits
  delay (2);
  return 1;
}


// routine to update the LCD display if the displayfreq period has been reached
 void updatedisplay()
 {
 // print the outputs to the LCD display
 lcd.setCursor (0,0);
 //sprintf(buffer,"Current: %i",temp);
 lcd.print (temp);
 lcd.setCursor (0,1);  // move to the next row
 //sprintf(buffer,"Min %i\337C Max %i\337C",mintemp, maxtemp);
 lcd.print (mintemp);
 lcd.setCursor (6,1);
 lcd.print (maxtemp);
 // reset the display timer back to zero
 //starttime = millis();
 }

This line :
readings[index] = ((5.0 * analogRead(tempPin) * 100.0)/1024);

could be simplified to:
readings[index] = ((analogRead(tempPin) * 500.0)/1024);

But since readings[] is of type int, its range is from -32768to +32767 .
So if temp goes over 65 , (=> 65*500 ) it rolls over.

So either use long int, or precalculate the constant 500.0/1024=0.48828 .

why do you have a buffer of readings?

You should check if the temp reading is valid, so if the minimum drops more than lets say 1 or 2 degrees you have a really fast cooling down (not realistic?), so you should ignore it or do a reread?.
In fact I think if the current reading differs more than 2 degrees of the previous you should be careful to use it.

The problem is that the if statement that checks the mintemp is a factor of 100 out as you can see in the serial output and there seems no reason for this.

I had a simpler check but went with the smoothing example from here to smooth the results hence the array.

apollinaire:
This line :
readings[index] = ((5.0 * analogRead(tempPin) * 100.0)/1024);

could be simplified to:
readings[index] = ((analogRead(tempPin) * 500.0)/1024);

But since readings[] is of type int, its range is from -32768to +32767 .
So if temp goes over 65 , (=> 65*500 ) it rolls over.

So either use long int, or precalculate the constant 500.0/1024=0.48828 .

Not correct, the assigned to the int is the last step in evaluating the formula. (but it could have been)

the first step is: 5.0 * analogRead(tempPin) which makes the intermediate value a float as one of the operands is float.
Next is: * 100.0 resulting in a float
Next is: / 1024 resulting in a float
Then the assignment, which "truncates" the value to an int.

Why does getttemp() return a value? The only value that is ever returns is 1, so it's useless to have it return a value. It's also useless to return a value when you don't use the returned value.

This is the problem:

  // calculate the average:
  temp = totaltemp / numReadings;

The first time that is executed, there has only been one reading made so total was apparently 19 at that point. Temp becomes 0.19 and is captured in mintemp, as you observe.

Run the program again and look at the first few lines of serial data, you'll see that temp is 0.19 too I suspect.

ushills:
The problem is that the if statement that checks the mintemp is a factor of 100 out as you can see in the serial output and there seems no reason for this.

I had a simpler check but went with the smoothing example from here to smooth the results hence the array.

There is no factor 100 in the mintemp related code lines. So also not in the IF statement.

Q1: , is it after every start that the mintemp fails?
Q2: do the values on serial match those on LCD?
Q3: if you comment the line // updateDisplay(); and oonly use Serial?

If you have a loose wire the analogRead() can fluctuate either to some very low value or to some high value, resulting in an faulty value for temp. However the value 0.19 is very strange as that would imply an analogRead value on about 0.38 which cannot be read.

root cause is
temp = totaltemp/ numreadings.
In the beginning there are no numreadings causing a minimum value to be 100x too low.

minor issue.
The line index = index++; is syntactical correct, but normally written as index++; only

update: You beat me wildbill :wink:

wildbill:
This is the problem:

  // calculate the average:

temp = totaltemp / numReadings;




The first time that is executed, there has only been one reading made so total was apparently 19 at that point. Temp becomes 0.19 and is captured in mintemp, as you observe.

Run the program again and look at the first few lines of serial data, you'll see that temp is 0.19 too I suspect.

Thanks, this makes perfect sense as it worked before I added the smoothing routine.

I'm not sure that routine is correct, however, as it divides by 100 samples this must be where the error lies.

Any ideas how I can get the update display routine to only trigger every x seconds.

This should help to get it right

// ADD THIS GLOBAL VAR
int sampleCount = 0;

void gettemp()
{
  // subtract the last reading:
  totaltemp= totaltemp - readings[index];

  // read from the sensor:
  readings[index] = ((5.0 * analogRead(tempPin) * 100.0)/1024);

  // add the reading to the total:
  totaltemp= totaltemp + readings[index];

  // advance to the next position in the array:
  index = index++;

  // ADD THIS
  if (sampleCount < numReadings) 
    sampleCount++
    
  // if we're at the end of the array...
  if (index >= numReadings)
    // ...wrap around to the beginning:
    index = 0;

  // calculate the average:
  // CHANGE THIS
  temp = totaltemp / sampleCount;
}

A very crude fix would simply be to call getTemp in a loop in setup to fill up your array.

As to the periodic update, as ever, take a look at the blink without delay example to see how to use millis to control when things are done.

RobTillaart, it seems our paths cross again :wink:

wildbill:
A very crude fix would simply be to call getTemp in a loop in setup to fill up your array.

As to the periodic update, as ever, take a look at the blink without delay example to see how to use millis to control when things are done.

RobTillaart, it seems our paths cross again :wink:

Thanks regarding the setup loop, I think this is the problem as the array is not filled correctly, filling it the setup loop would stop this error.

With the periodic update of the display I was thinking of something like the following, would it be the best way.

void setup ()
{
update = 20 // set the update time in seconds
starttimetime = milis();
}

void loop ()
{
elapsedtime = millis() - starttime;
if (elapsedtime >= (update * 1000)) updatedisplay (); // check if elapsed time equals updatetime
}

void updatedisplay ()
{
// run the code to update display
starttime = millis(); // reset the starttime 
delay (1); // prevent startime equalling updatetime immediately
}

More or less. Make sure that all time variables are unsigned long and ensure that your constants such as 20 have a UL suffix to ensure that the compiler doesn't assume that they're int. As you have it now, it doesn't matter but if you were to change the 20 to 40, there's the possibility of integer overflow.

Another overflow issue concerns millis. After ~49 days it will wrap around back to 0. At that time you may get unexpected effects. However, if you use subtraction to do your check, the issue goes away:

if (millis()-startTime >= (update * 1000UL)) 
   updatedisplay ();

the program is to fast after power up wait a few seconds until sensor is powered up.