Strange results from TMP36 Thermistor

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

wired up as

using this code

// 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](Using a Temp Sensor | TMP36 Temperature Sensor | Adafruit Learning System --) 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

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 :slight_smile:

How do I get an accurate output?
Thanks

Using TMP36 Thermistor-xlsx-640x382.png

temperatureC = (voltage - 0.75) * 100;

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

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. :slight_smile:

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);
  }  
}

Wawa:
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.

outsider:
Here's a simple sketch to play with. :slight_smile:

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?

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?

If I run your code with the 3.3 V input to AREF is get

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

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 :neutral_face: All I have changed in your code is the analog pin reference.

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

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..

// 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
}

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.

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..

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.

Wawa:
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

Wawa:
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."

Wawa:
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. :wink:

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.

vagulus:
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..

Thanks, Leo.

I'll post how I got on.

See if this makes more sense, don't connect anything to the AREF pin:

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);
  } 
}

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

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.

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..

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.

Two consecutive A/D readings are sometimes done to clear the A/D from a possible ghost charge from a previous reading. The line you're referring to is a dummy read before the real readings in the for loop.
Leo..

Thanks. That's what I suspected. Pity there is no Serial.purge(A0); command to clarify what the programmer is trying to do. :slight_smile:

A dummy read is to pre-charge the A/D sample cap to the value that is expected next.
Not to set the A/D value to zero.
Leo..

Thanks

I am still trying to get as accurate a temperature reading as possible from the TMP36. The current configuration is
1801141020-TMP36 Current Configuration-640x478.png
and the circuit is


and the code is

/*
-- filename   : usingTMP36temperatureSensor.ino
--
-- purpose    : demonstrate the interpretation of data from a TMP36
--               temperature sensor
--
-- derivation : https://learn.adafruit.com/tmp36-temperature-sensor/using-a-temp-sensor
--
-- version    : 0
--
-- date       : January 10, 2018
--
-- copyright  : this code is in the public domain
--
1234567890123456789012345678901234567890123456789012345678901234567890
_________1         2         3         4         5         6         7
*/

// general constants
// we tie 3.3V to ARef
#define AREF_VOLTAGE 3.27
//the analog pin the TMP36's Vout
#define SENSOR_PIN A0
// debug info switch
#define DEBUG true
// zero degree celsius voltage offset
#define OFFSET 0.53


// general variables
// the analog reading from the sensor
int sensorInput;
// 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);

 // tell 'em who we are, man!
 Serial.println("usingTMP36temperatureSensor.ino");
 Serial.println();

 // initialize for output to PLX-DAQ
 if (!DEBUG)
 {
 Serial.println("CLEARDATA");
 Serial.println
 ("LABEL,Current Time,Raw Data,Voltage,Celsius,Fahreinheit");
 }

 // read into SENSOR_PIN
 pinMode(SENSOR_PIN, INPUT);

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


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


void loop()
{
 // zero sensorInput
 sensorInput = 0;

 // get averaged reading from the sensor and show it
 for (int count = 0; count <= 31; count++)
 {
 sensorInput += analogRead(SENSOR_PIN);
 }
 sensorInput /= 32;
 if (DEBUG)
 {
 Serial.print("Sensor Input = ");
 Serial.print(sensorInput);
 }

 // converting that reading to voltage, which is based off 
 //  the reference voltage and show it
 voltage = sensorInput * AREF_VOLTAGE / 1024.0;

 if (DEBUG)
 {
 Serial.print(" which equates to ");
 Serial.print(voltage, 4);
 Serial.println(" volts");
 }

 // convert from 10 mv per degree with 500 mV offset
 // ( TMP36 outputs 0.5volt at zero Celcius)
 //  to degrees ((voltage - 500 mV) times 100)
 //  and show it
 temperatureC = (voltage - OFFSET) * 100;
 if (DEBUG)
 {
 Serial.print(temperatureC, 1);
 Serial.println(" degrees C");
 }

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

 // export the data to PLX-DAQ
 if (!DEBUG)
 {
 Serial.print("DATA,TIME,");
 Serial.print(sensorInput);
 Serial.print(',');
 Serial.print(voltage, 4);
 Serial.print(',');
 Serial.print(temperatureC, 1);
 Serial.print(',');
 Serial.println(temperatureF, 1);
 }

 // take a breather
 delay(3000); // (900000);
}

As you'll see in line 66 I am using EXTERNAL analog reference which is defined in line 21 as 3.27 V.

No, studying Fig 6 0n page 5 of the TMP36 Datasheet I noticed that the Vout (at 3 V Vin) for the TMP36 at 0 degrees Celsius is a little more that 0.5.


so I cast a TMP36 in ice and tweaked the offset to a value of 0.53 (line 27) which gave a temperature reading of zero degrees Celsius. Dipping the TMP36 in hot water less than a minute from boiling in the kettle gave me a reading of 98 degrees Celsius, so I decided the offset must be somewhere near right. What can possibly go wrong?

Running my program gives me a temperature of 25.9 C when my store-bought desktop thermometer (yesterday) tells me it's 27.9 C.

Who do I trust, and how do I adjust my program?

1801141020-TMP36 Current Configuration-640x478.png