Go Down

Topic: Strange results from TMP36 Thermistor (Read 758 times) previous topic - next topic

vagulus

I am having trouble getting coherent results from a TMP36 Thermistor.  My circuit is

wired up as

using this code
Code: [Select]

// general constants
// we tie 3.3V to ARef
#define AREF_VOLTAGE 3.3
//the analog pin the TMP36's Vout
#define  TEMP_PIN 1
// debug info switch
#define DEBUG true
// 15 minutes between data collections (ms)
#define INTERVAL 2000 // 900000


// general variables
// last time readings were taken
unsigned long previousMillis = millis();
// present moment
long currentMillis;
// the analog reading from the sensor
int tempReading;
// voltage
float voltage;
// temperature in Celsius
float temperatureC;
// temperature in Fahreinheit
float temperatureF;



/*==========================================*/


void setup()
{
// send debugging information via the Serial monitor
Serial.begin(9600);

// initialize for output
Serial.println("CLEARDATA");
Serial.println
("LABEL,Current Time,Raw Data,Voltage,Centigrade,Fahreinheit");
 
// read into TEMP_PIN
pinMode(TEMP_PIN, INPUT);

// set the aref to something other than 5v
analogReference(EXTERNAL);
}  // end setup()


/*==========================================*/


void loop()
{
// get the present moment
currentMillis = millis();

// see if it is time to work
if (currentMillis - previousMillis >= INTERVAL)
{
    // save the current time
    previousMillis = currentMillis;
   
// get the reading from the sensor and show it
tempReading = analogRead(TEMP_PIN);
if (DEBUG)
{
Serial.print("Temp reading = ");
Serial.print(tempReading);
}

// converting that reading to voltage, which is based off
//  the reference voltage and s
voltage = tempReading * AREF_VOLTAGE;
voltage /= 1024.0;
if (DEBUG)
{
Serial.print(" which equates to ");
Serial.print(voltage);
Serial.println(" volts");
}

// convert from 10 mv per degree with 750 mV offset
//  to degrees ((voltage - 750 mV) times 100)
//  and show it
temperatureC = (voltage - 0.75) * 100;
if (DEBUG)
{
Serial.print(temperatureC);
Serial.println(" degrees C");
}

// now convert to Fahrenheight and show it
temperatureF = (temperatureC * 9.0 / 5.0) + 32.0;
if (DEBUG)
{
Serial.print(temperatureF);
Serial.println(" degrees F");
Serial.println();
Serial.println();
}

// export the data to PLX-DAQ
Serial.print("DATA,TIME,");
Serial.print(tempReading);
Serial.print(',');
Serial.print(voltage);
Serial.print(',');
Serial.print(temperatureC);
Serial.print(',');
Serial.println(temperatureF);
}  //  end if time check
}


The code was adapted from this adafruit tutorial and originally used a voltage offset of 0.5 V.  This gave the following overnight output

which shows, effectively, no temperature variation (and the temperature higher than it was,

When I checked the TMP36 Datasheet I found the Output Voltage listed as 0.75 V so I adjusted the code as shown above.  Sadly, this gives the following output
Code: [Select]

Opening port
Port open
CLEARDATA
LABEL,Current Time,Raw Data,Voltage,Centigrade,Fahreinheit
Temp reading = 221 which equates to 0.71 volts
-3.78 degrees C
25.20 degrees F


DATA,TIME,221,0.71,-3.78,25.20
Temp reading = 221 which equates to 0.71 volts
-3.78 degrees C
25.20 degrees F


DATA,TIME,221,0.71,-3.78,25.20
Temp reading = 221 which equates to 0.71 volts
-3.78 degrees C
25.20 degrees F


DATA,TIME,221,0.71,-3.78,25.20
Temp reading = 222 which equates to 0.72 volts
-3.46 degrees C
25.78 degrees F


DATA,TIME,222,0.72,-3.46,25.78
Temp reading = 221 which equates to 0.71 volts
-3.78 degrees C
25.20 degrees F


This is Perth, Western Australia in high summer and I can assure you that it is not that cold :)

How do I get an accurate output?
Thanks

"Answers are easy;
               it's asking the right questions
                                                      which is hard."
 The Doctor (Dr Who: The Face of Evil (1977))

Wawa

#1
Jan 12, 2018, 12:37 am Last Edit: Jan 12, 2018, 12:37 am by Wawa
temperatureC = (voltage - 0.75) * 100;

The TMP36 outputs 0.5volt at zero Celcius, not 0.75volt.
Leo..

outsider

The TMP36 offset is 500 mV, @ 25 C the output should be 500 + (10 mV / degree * 25 = 250) = 750 mV, why not use the 1.1V internal reference?
Here's a simple sketch to play with.   :)
Code: [Select]

int adcnt;
float adcv;
float tempC, tempF;
int adavg[8];

void setup()
{
  Serial.begin(9600);
  analogReference(INTERNAL);
  for(int i = 0;i < 8;i++)
    adavg[i] = analogRead(0);
}

void loop()
{
  for(int j = 0;j < 8;j++)
  {
    adcnt = 0;
    analogRead(0);
    for(int i = 0;i < 16;i++){
      adcnt += analogRead(0);
      delay(2);
    }  
    adavg[j] = adcnt / 16;
    Serial.print(adavg[j]); Serial.print("  ");
    adcnt = 0;
    for(int k = 0;k < 8;k++)
      adcnt += adavg[k];
    adcnt /= 8;  
    adcv = adcnt * 1.1 / 1023 - 0.5;
    tempC = adcv  * 100; tempF = tempC * 9 / 5 + 32;
    
    Serial.print(adcv,3); Serial.print("  ");
    Serial.print(tempC,1); Serial.print("  ");
    Serial.println(tempF,1);
    delay(1000);
  }  
}

vagulus

The TMP36 outputs 0.5volt at zero Celcius, not 0.75volt.
Thanks, Leo.  I finally found that in fig. 6 on page 5 of the datasheet.

Neither of the tutorials I checked referred to zero Celsius as a benchmark.
"Answers are easy;
               it's asking the right questions
                                                      which is hard."
 The Doctor (Dr Who: The Face of Evil (1977))

vagulus

Here's a simple sketch to play with.   :)
I wish I could understand your code.  I always struggle with traditional 'C' programming style which is why you probably found my code a bit strange.  Please, what do the cryptic identifiers adcnt, adcv and adavg mean, why the loop (for(int i = 0;i < 16;i++)) inside loop(), and what does line 19 (analogRead(0);) do?

The TMP36 offset is 500 mV, @ 25 C the output should be 500 + (10 mV / degree * 25 = 250) = 750 mV, why not use the 1.1V internal reference?
If I run your code with the 3.3 V input to AREF is get
Code: [Select]

221  -0.263  -26.3  -15.4
221  -0.263  -26.3  -15.4
221  -0.263  -26.3  -15.4
220  -0.263  -26.3  -15.4
221  -0.263  -26.3  -15.4
221  -0.263  -26.3  -15.4
221  -0.263  -26.3  -15.4
221  -0.263  -26.3  -15.4
221  -0.263  -26.3  -15.4
221  -0.263  -26.3  -15.4
221  -0.263  -26.3  -15.4
221  -0.262  -26.2  -15.2
221  -0.262  -26.2  -15.2
221  -0.262  -26.2  -15.2
221  -0.262  -26.2  -15.2
221  -0.262  -26.2  -15.2
221  -0.262  -26.2  -15.2
221  -0.262  -26.2  -15.2
221  -0.262  -26.2  -15.2

and if I disconnect that I get
Code: [Select]

672  0.223  22.3  72.1
673  0.223  22.3  72.1
672  0.223  22.3  72.1
672  0.223  22.3  72.1
673  0.223  22.3  72.1
672  0.223  22.3  72.1
673  0.223  22.3  72.1
672  0.223  22.3  72.1
673  0.223  22.3  72.1
672  0.223  22.3  72.1
672  0.223  22.3  72.1
672  0.223  22.3  72.1
672  0.223  22.3  72.1
672  0.223  22.3  72.1
672  0.223  22.3  72.1
672  0.223  22.3  72.1
672  0.223  22.3  72.1
672  0.223  22.3  72.1
672  0.223  22.3  72.1
672  0.223  22.3  72.1
672  0.223  22.3  72.1
672  0.223  22.3  72.1


Neither set of data output makes any sense.  It is not that cold, and it's not that hot  :smiley-neutral:   All I have changed in your code is the analog pin reference.
Code: [Select]

int adcnt;
float adcv;
float tempC, tempF;
int adavg[8];

void setup()
{
  Serial.begin(9600);
  analogReference(INTERNAL);
  for(int i = 0;i < 8;i++)
    adavg[i] = analogRead(1);
}

void loop()
{
  for(int j = 0;j < 8;j++)
  {
    adcnt = 0;
    analogRead(0);
    for(int i = 0;i < 16;i++){
      adcnt += analogRead(1);
      delay(2);
    } 
    adavg[j] = adcnt / 16;
    Serial.print(adavg[j]); Serial.print("  ");
    adcnt = 0;
    for(int k = 0;k < 8;k++)
      adcnt += adavg[k];
    adcnt /= 8; 
    adcv = adcnt * 1.1 / 1023 - 0.5;
    tempC = adcv  * 100; tempF = tempC * 9 / 5 + 32;
   
    Serial.print(adcv,3); Serial.print("  ");
    Serial.print(tempC,1); Serial.print("  ");
    Serial.println(tempF,1);
    delay(1000);
  } 
}


Is something else mashed up in transmission?

Thanks


"Answers are easy;
               it's asking the right questions
                                                      which is hard."
 The Doctor (Dr Who: The Face of Evil (1977))

Wawa

#5
Jan 12, 2018, 02:24 am Last Edit: Jan 12, 2018, 02:30 am by Wawa
Normally the internal 1.1volt Aref is used for the TMP36, but Adafruit is using external 3.3volt Aref to extend the temp range of the TMP36.
Temp range with the TMP36 on 1.1volt Aref is about -45C to +55C.
If you're happy with that, then try the attached sketch.
No smoothing and/or other gimmics in this simple sketch.
Make sure you DON't connect anything to the Aref pin when running the above sketch or this sketch.
Doing so, without setting the analogReference to EXTERNAL (Adafruit sketch), could damage the Aref pin.
Leo..
Code: [Select]
// LM35_TMP36 temp
// connect LM35 to 5volt A0 and ground
// connect TPM36 to 3.3volt A0 and ground
// calibrate temp by changing the last digit(s) of "0.1039"

float tempC; // Celcius
float tempF; // Fahrenheit

void setup() {
  analogReference(INTERNAL); // use internal 1.1volt Aref
  Serial.begin(9600);
}

void loop() {
  //tempC = ((analogRead(A0) * 0.1039)); // uncomment this line for an LM35
  tempC = ((analogRead(A0) * 0.1039) - 50.0); // uncomment this line for a TMP36
  tempF = tempC * 1.8 + 32.0; // C to F
  Serial.print("Temperature is  ");
  Serial.print(tempC, 1); // one decimal place
  Serial.print(" Celcius  ");
  Serial.print(tempF, 1);
  Serial.println(" Fahrenheit");

  delay(1000); // use a non-blocking delay when combined with other code
}

vagulus

Thanks Leo.  That gives a temperature which is much more in the ball park.  Not having a thermometer, I can't check but it looks about right. 

I am working with a TMP36 Thermistor.  Question is, "How did you derive the equation  tempC = (((analogRead(A0)* 0.1039) - 50.0);?"  That is quite different from the working used in the adafruit tutorial.
"Answers are easy;
               it's asking the right questions
                                                      which is hard."
 The Doctor (Dr Who: The Face of Evil (1977))

Wawa

0.1039 is a combined result of the internal 1.1volt reference voltage * 100 / 1024.

More info on this page.

Needs calibrating, because 1.1volt Aref  is slightly different on every Arduino.
Another Arduino I used needed 0.1049 to get the right temp.

The -50 part is offset temp of the TMP36. Not used for the LM35.
Leo..

vagulus

I hope you don't mind me coming back at this but I don't have clear in my head how to get a valid and consistent reading for temperature from a TMP36 Thermistor.

0.1039 is a combined result of the internal 1.1volt reference voltage * 100 / 1024.
According to my (student quality) calculator, 1.1*100/1024=0.1074 - not 0.1039.  Is this what you were referring to by
Needs calibrating, because 1.1volt Aref  is slightly different on every Arduino.
Another Arduino I used needed 0.1049 to get the right temp.
Does this mean that you set up your thermistor with the program and keep tweaking the 0.1074 until the output your program prints agrees with your desktop thermometer?  Should this be necessary?  The datasheet for the TMP36 proudly states, "The TMP35/TMP36/TMP37 are low voltage, precision centigrade temperature sensors. They provide a voltage output that is linearly proportional to the Celsius (centigrade) temperature. The TMP35/ TMP36/TMP37 do not require any external calibration to provide typical accuracies of ±1°C at +25°C   and ±2°C over the −40°C to +125°C temperature range."


The -50 part is offset temp of the TMP36. Not used for the LM35.
I cannot find a reference to an offset of 50 in the TMP36 datasheet.  I'd very much appreciate it if you would point out where it is.  Then I might know whet to look for next time an issue like this comes up. ;)

Since you say that analogReference(INTERNAL) varies from board to board, in my experimentation I have powered the thermistor from A Redwings Power Supply and I am thinking of feeding that 5 V into AREF with the definition #define AREF_VOLTAGE 5.0 and then analogReference(EXTERNAL); in setup().  Would this give me greater accuracy and consistency?  I see that there is second-to-second variation in readings using the UNO's internal aref.

I appreciate your patience.
"Answers are easy;
               it's asking the right questions
                                                      which is hard."
 The Doctor (Dr Who: The Face of Evil (1977))

Wawa

#9
Jan 12, 2018, 08:55 am Last Edit: Jan 12, 2018, 09:00 am by Wawa
According to my (student quality) calculator, 1.1*100/1024=0.1074 - not 0.1039.

Does this mean that you set up your thermistor with the program and keep tweaking the 0.1074 until the output your program prints agrees with your desktop thermometer?

Should this be necessary?

I cannot find a reference to an offset of 50...

Since you say that analogReference(INTERNAL) varies from board to board,

I am thinking of feeding that 5 V into AREF...
Correct.

The TMP36 is not a thermistor, but an integrated circuit temp sensor.
Yes, you keep on tweaking/uploading until the temp is right.
Or measure the Aref pin with a good DMM, and use that value to calculate the magic number (less accurate).

Up to you.

A/D value, and so calculated temp, depends on two things.
1) Voltage from the sensor (which is calibrated).
2) Voltage of Aref (which is NOT calibrated).

The TMP36 outputs 500mV (10mV/degree C) at a temp of zero degrees C (positive/negative temp pivot point).

Yes, different for every board, but stable.

Did you read/understand the link I gave you.
Using a higher Aref than needed reduces resolution.
1.1volt Aref / 1024 is about 1mV per step = a temp resolution of 0.1C for a 10mV/C sensor.
5volt / 1024 is about 5mV per step = a temp resolution of 0.5C for a 10mV/C sensor.

One day you will discover the much easier to use digital DS18B20.
Leo..





vagulus

Thanks, Leo.

I'll post how I got on.
"Answers are easy;
               it's asking the right questions
                                                      which is hard."
 The Doctor (Dr Who: The Face of Evil (1977))

outsider

#11
Jan 13, 2018, 02:48 pm Last Edit: Jan 13, 2018, 03:24 pm by outsider
See if this makes more sense, don't connect anything to the AREF pin:
Code: [Select]
int adcnt; // reading from analog input pin
float adcv; // computed voltage
float tempC,
      tempF,
      aRef = 1.1; // adjust for calibration
int adavg[8]; // buffer for smoothing
byte counter;
char label[] = "\nADC avg  avg volts Celsius  Fahrenheit";

void setup()
{
  Serial.begin(9600);
  analogReference(INTERNAL); // use 1.1V internal reference
  for(int i = 0;i < 8;i++) // fill buffer with current reading
    adavg[i] = analogRead(1);
   Serial.println(label);
}

void loop()
{
  for(int j = 0;j < 8;j++)
  {
    adcnt = 0;
    analogRead(1);
    for(int i = 0;i < 16;i++){ // get total of 16 readings
      adcnt += analogRead(1);  // for smoothing
      delay(2);
    }
    adavg[j] = adcnt / 16; // get average of 16 readings
    if(++counter >= 15)
    {
      Serial.println(label);
      counter = 0;
    }
    Serial.print(adavg[j]); Serial.print("      ");
    adcnt = 0;
    for(int k = 0;k < 8;k++) // get average of last 8 averages
      adcnt += adavg[k];
    adcnt /= 8;
    adcv = adcnt * aRef / 1023 - 0.5; // compute voltage and
                                      // subtract 0.5V offset
    tempC = adcv  * 100; // multiply voltage by 100 to get tempC
    tempF = tempC * 9 / 5 + 32; // compute tempF
    Serial.print(adcv,3); Serial.print("     ");
    Serial.print(tempC,1); Serial.print("     ");
    Serial.println(tempF,1);
    delay(1000);
  }
}

vagulus

Thanks for that.  I like the smoothing operation - the TMP36 readings do bounce around a bit.  :smiley-confuse:

One question, "What is the purpose of line 24 analogRead(1);?"  Is it some sort of buffer clearing?  The value is not assigned to anything.
"Answers are easy;
               it's asking the right questions
                                                      which is hard."
 The Doctor (Dr Who: The Face of Evil (1977))

Wawa

analogRead(1); is the same as analogRead(A1);

The compiler assumes that you want to read an analogue pin if you use analogRead().
But it's good practise to add that 'A' to stop confusion.

You could also declare the pin at the start of the sketch.
const byte TEMP_PIN = A1;
then use analogRead(TEMP_PIN);
Only useful if you want to use it more than once in your sketch.

pinMode(TEMP_PIN, INPUT);
No need for that. Every pin is an input by default.
Leo..


vagulus

Thanks Leo but I knew those things.  What I want to know is why outsider wrote that line of code in the first place.  It will read the value at A1 but it does nothing with it.  To all intents and purposes it seems to be a wasted instruction. But ... there must be some reason for it being there.
"Answers are easy;
               it's asking the right questions
                                                      which is hard."
 The Doctor (Dr Who: The Face of Evil (1977))

Go Up