Help with TMP37 temperature sensor

I recently received a few free samples of this device from analog.com because I wanted to try to modify my old analog thermostat and make it digitally controlled. The device has 3 pins: Vs, out and GND, and it operates between 2.7 and 5.5V. Here's the datasheet:

http://www.analog.com/static/imported-files/data_sheets/TMP35_36_37.pdf

The 37 in particular reads in 20mV/°C, is designed for the 5°-100°C range and has a reading of 500mV at 25°C. Initially I simply plugged in 5V to the Vs, GND to the ground pin and the out pin to A0 on the Arduino, then I read the analog input, adjusted it to convert the 0-1023 analog reading to a mV value like this:

int reading = analogRead(A0);
float mv = (reading * 5.0) / 1024.0;

Then subtracted 500 and divided by 20 to get degrees C. This is where I'm not sure I'm doing it right. Basically, I don't believe what the sensor is saying the temperature is in my room. Right now it's saying it's 77.07° F (25.04° C). I know that it's hotter than that (it gets really hot in my room). Here's my current sketch. I'm using 3.3V aref and printing to an LCD.

#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 10, 5, 4, 3, 2);
#define aref_voltage 3.3

int tempPin = 1;
int tempReading;
 
void setup(void) {
  analogReference(EXTERNAL);
  lcd.begin(16,2);
  lcd.clear();
  lcd.setCursor(0,0);
}

void loop(void) {
  tempReading = analogRead(tempPin);
  float voltage = tempReading * aref_voltage;
  voltage /= 1024.0;

  float temperatureC = (voltage - 0.5) * 200 ;
  float temperatureF = (temperatureC * 9.0 / 5.0) + 32.0;
  lcd.clear();
  lcd.print(temperatureC);
  lcd.print(" C");
  lcd.setCursor(0, 1);
  lcd.print(temperatureF);
  lcd.print(" F");
  delay(1000);
}

The article I was reading to help me figure out how to set this thing up was using a TMP36, which is slightly different than the 37. From the datasheet:

The TMP36 is specified from ?40°C to +125°C, provides a 750 mV output at 25°C, and operates to 125°C from a single 2.7 V supply. [...] Both the TMP35 and TMP36 have an output scale factor of 10 mV/°C.

The TMP37 is intended for applications over the range of 5°C to 100°C and provides an output scale factor of 20 mV/°C. The TMP37 provides a 500 mV output at 25°C.

The difference I see is that the 36 reads 500mV at 0°C whereas the 37 reads 500mV at 25°C. My tiny tired brain can't work out what I'm supposed to do to account for this.

I'm hoping someone here who is less tired and has a bigger brain can help me figured the math out for this.

int reading = analogRead(A0);
float mv = (reading * 5.0) / 1024.0;

this code results in volts...

try this

int raw = analogRead(tempPin);
float milliVolts = raw * 4.887586;        // faster than * 5.0/1023      ==> 0 .. 5000
float tempC = (milliVolts - 500) * 0.05;  // faster than / 20            ==> -500 .. 4500 ==> / 20 ==> -25 .. +225
float tempH = tempC * 1.8 + 32;

That still doesn't explain why my readings seem to be off. Applying your math to an example analog value from the sensor of 200:

200 * 4.887586 = 977.52
(977.52 - 500) * 0.05 = 23.88 (°C)
23.88 * 1.8 + 32 = 74.98 (°F)

And my math:

200 * (5000/1024) = 976.56
(976.56 - 500) / 20 = 23.83 (°C)
(23.83 * 9 / 5) + 32 = 74.89 (°F)

The difference is .05°C (and I did my math above assuming I was using 5V as the Vs, I'm actually using 3.3V with aref so it's more precise and less noisy). I don't think the way I'm doing the math itself is wrong, I'm just wondering why the number coming out seems wrong. Right now it says it's 72.43°F in my room, but it feels a lot hotter to me. I have no way of verifying the actual temperature in the room.

Your math is sound, the difference of 0.05 is because of rounding errors you have with floats. The 0.05 is probably within the limits of the accuracy of the sensor.
Note that the Arduino float only has 7 digits of precision, and every float operation adds rounding errors.

long has 9 digits of precision, and is faster

But part of the temperature you "experience/feel" has to do with humidity.
A high temperature with low humidity feels very different from same temp with high humidity.

Time for the next sensor (one that reads rH)

I just can't believe that humidity is making up that much of a difference. Right now the device is reading 55.03°F. There is no possible way it's 55° in my room, the rest of the house is much cooler and the AC (in the living room) is set to turn on at 75°. I unplugged and replugged the Arduino from USB and it's now reading 53.87°F. I also removed the sensor from the breadboard and put in another one (I have two of the same kind), then unplugged and replugged the Arduino and it started at 65° and dropped down (quickly at first, then slower) to around 56°. It is not that cold in here, it never gets that cold in here, or anywhere else in the house for that matter, I hate cold.

I always consider Google to be my friend :slight_smile:

I am not sure where this equation came from

float temperatureC = (voltage - 0.5) * 200 ;

Not sure why you are subtracting the 0.5, nor why you are multiplying by 200 but I must admit to not looking too closely.

Have a read of http://shop.moderndevice.com/products/temperature-sensor or Arduino TMP37 Centigrade Temperature Sensor Tutorial and, there is also a library available Google Code Archive - Long-term storage for Google Code Project Hosting.

Hope this helps.

To be honest, I'm not completely sure why I'm subtracting .5 either...I just thought about it. The article I was reading was about the TMP36, which reads 500mV at 0°C, so that's why they subtracted .5 (.5V = 500mV), but the TMP37 reads 500mV at 25°C, and has an interval of 20mV/°C, 500/20 = 25, which means that at 0°C, the reading is 0mV, so I should just take the mV reading and divide by 20.

The reason I'm multiplying by 200 is because, in the sketch I'm using, the voltage is calculated to actual volts, not millivolts (.65 instead of 650, for example), but even now that doesn't make sense to me...I've confused myself.

I think I need to change my sketch to calculate actual mV and not volts.

int reading = analogRead(A0);
double mv = reading * ([input voltage] / 1024); // To convert the analog 0-1023 value to a mV value relative to the input voltage
double tempC = mv / 20;
double tempF = (tempC * 9 / 5) + 32;

Edit: Read through the second link you posted (the one from bristolwatch.com) and the sketch used in that example is awful. Tons of errors that needed to be fixed, and when I finally got it working it says it's 481°F in my room. Also looked at the library, also bad, said that it was like 302°F (and I did use setARefVoltage(3.3), so it wasn't that)). I'm not sure what the heck the problem is here, but I'm starting to get frustrated because I know that it's not 62°F in my room, which is what my modified code (from above in this post) is saying.

What result does this sketch provide (from the first link above)

/*TMP37.pde  
 * Arduino Sketch to read a TMP37 Temperature Sensor
 * Paul Badger
 * 2010
 * Sensor works between 5 degrees & 100 degrees C
 * 20 mV / degree C
 * Can be used with TMP35 & TMP36 by changing voltsPerDegree
 */

float voltsPerDegree = 0.02; // change to 0.01 for TMP35 & 36

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

void loop() {
    int sensorValue;
    float volts;
    float celsius;
    float farenheit;

    sensorValue = analogRead(0);
    volts = sensorValue * 5.0 / 1024.0;            // convert AD units to volts
    // convert volts to celsius
    celsius = (sensorValue * 5.0 / 1024.0) / voltsPerDegree;
    // standard conversion from celsius to farenheit
    farenheit = (((sensorValue * 5.0 / 1024.0) / voltsPerDegree)  * 9.0 / 5.0) + 32;

    Serial.print(sensorValue, DEC);
    Serial.print(" A/D units   ");
    Serial.print(volts);
    Serial.print(" volts   ");
    Serial.print(celsius);
    Serial.print(" degrees C   ");
    Serial.print(farenheit);
    Serial.println(" degrees F");
}

I think I've figured it out. My math for the C to F conversion was wrong (backwards), so I was getting the right C temperature but the wrong F temp.

Since the TMP37 reads 500mV at 25° and has a scale of 20mV/°C, that means that 0°C is 0mV, so I didn't need to subtract the .5 (500 mV), just divide the calculated mV by 20. Right now it's reading 30.29°C/86.53°F in my room, which feels right (I said before I really hate cold). Final sketch:

#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 10, 5, 4, 3, 2);
#define aref_voltage 3.3

int tempPin = 1;
long tempReading;
float avgTemp;
 
void setup(void) {
  analogReference(EXTERNAL);
  lcd.begin(16,2);
  lcd.clear();
  lcd.setCursor(0,0);
}

void loop(void) {
  for(int i = 0; i < 400; i++) {
    tempReading += analogRead(tempPin);
    delay(1);
  }
  
  avgTemp = tempReading / 400;
  
  double mv = avgTemp * ((aref_voltage * 1000) / 1024);
  double tempC = mv / 20;
  double tempF = (tempC / 5 * 9) + 32.0;
  lcd.clear();
  lcd.print(tempC);
  lcd.print(" C");
  lcd.setCursor(0, 1);
  lcd.print(tempF);
  lcd.print(" F");
  tempReading = 0;
  delay(1000);
}

I'm sampling the sensor 400 times then averaging the reading, then converting that to a mV value by multiplying by ((aref_voltage * 1000) / 1024), then dividing by 20 to get °C. The reason I do aref_voltage * 1000 is because aref_voltage is 3.3, I need 3300. All seems well now.

Glad you got it sorted :smiley:

Magicj:
What result does this sketch provide (from the first link above)

/* TMP37.pde  
  • Arduino Sketch to read a TMP37 Temperature Sensor
  • Paul Badger
  • 2010
  • Sensor works between 5 degrees & 100 degrees C
  • 20 mV / degree C
  • Can be used with TMP35 & TMP36 by changing voltsPerDegree
    */

float voltsPerDegree = 0.02; // change to 0.01 for TMP35 & 36

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

void loop() {
    int sensorValue;
    float volts;
    float celsius;
    float farenheit;

sensorValue = analogRead(0);
    volts = sensorValue * 5.0 / 1024.0;            // convert AD units to volts
    // convert volts to celsius
    celsius = (sensorValue * 5.0 / 1024.0) / voltsPerDegree;
    // standard conversion from celsius to farenheit
    farenheit = (((sensorValue * 5.0 / 1024.0) / voltsPerDegree)  * 9.0 / 5.0) + 32;

Serial.print(sensorValue, DEC);
    Serial.print(" A/D units  ");
    Serial.print(volts);
    Serial.print(" volts  ");
    Serial.print(celsius);
    Serial.print(" degrees C  ");
    Serial.print(farenheit);
    Serial.println(" degrees F");
}

I know this is an old post and you might very likely have already figured out what I'm about to say but for future reference it might come in handy for others.

The code needs to take into account that TMP35, TMP36 and TMP37 not only differ in voltsPerDegree (Scale factor) but also in outputVoltage (Output voltage at 25°C).

So, you may need the above mentioned outputVoltage variable and set it up also depending on the type of temperature sensor you are using.
For a quick guide, TMP35 works with .25V, TMP36 with .75V and TMP37 with .5V outputVoltage at 25°C

Then, you can calculate the offset value for a specific sensor type by subtractring the value for the 25°C difference between 25°C and 0°C as the following:

float outputVoltage = .25; //.25 for TMP35, .75 for TMP36, .5 for TMP37
float offsetValue = outputVoltage - 25 * voltsPerDegree;

This gives you a calculation for temperature as:

celsius = (volts - offsetValue) / voltsPerDegree;

p.s. since you already calculate volts, there is no use calculating it again in celsius, hence the shorter version above.
similarly, fahrenheit can use the calculation from celsius and the following codeline can be used:

farenheit = (celsius * 9.0 / 5.0) + 32;

This way, the full code for temperature reading would be as:

/*TMP sensors  
 * Arduino Sketch to read a Temperature Sensor TMP35/36/37
 * Reworked by Barnabas Geller, 2019
 * Original code by Paul Badger, 2010
 * Sensor works between 5 degrees & 100 °C on average
 * Change voltsPerDegree and outputVoltage values according to the sensor used
 * Note that voltsPerDegree is generally provided as mV/°C which is divided by 1000 to get V/°C
 */

float voltsPerDegree = 0.02; // 0.02 for TMP37, 0.01 for TMP35/36 
float outputVoltage = 0.25; //0.25 for TMP35, 0.75 for TMP36, 0.5 for TMP37
float offsetValue;

void setup() {
    Serial.begin(9600);
    offsetValue = outputVoltage - 25 * voltsPerDegree;  //calculating the offset for 0 °C
}

void loop() {
    int sensorValue;
    float volts;
    float celsius;
    float farenheit;

    sensorValue = analogRead(0);
    volts = sensorValue * 5.0 / 1024.0;            // convert AD units to volts

    // convert volts to celsius
    celsius = (volts - offsetValue) / voltsPerDegree;

    // standard conversion from celsius to farenheit
    farenheit = (celsius * 9.0 / 5.0) + 32;

    Serial.print(sensorValue, DEC);
    Serial.print(" A/D units   ");
    Serial.print(volts);
    Serial.print(" volts   ");
    Serial.print(celsius);
    Serial.print(" degrees C   ");
    Serial.print(farenheit);
    Serial.println(" degrees F");
}