How to Measure Temperature with an NTC Thermistor
2017-11-10 | By All About Circuits
License: See Original Project Resistors Arduino
Courtesy of All About Circuits
Do you ever wonder how some devices such as thermostats, car engines, overs, and 3D printer heat beds measure temperature? This project shows you how!
In many projects, knowing the temperature is a useful piece of data. Knowing temperature helps us to regulate room temperature, prevent engine overheating, ensure that a 3D printer bed is hot enough for materials like ABS to stick to its surface, and keep food from burning.
In this project, we focus on one type of sensor that can measure temperature: a thermistor.
A thermistor exhibits resistance with higher sensitivity to temperature compared to other types of resistors.
Using an Arduino, we can measure and process the thermistor’s readings and then convert them into more common temperature units.
Below is a picture of the thermistor we are going to use:
A bead thermistor. Image courtesy of Digi-Key.
BOM
- Arduino
- MEGA or Uno, or your favorite flavor of Arduino
- Some jumper wires
- Solder and soldering iron (these are just in case your thermistor doesn't fit easily into the Arduino headers
- Arduino IDE
The Theory Behind the Project
Typically, with applications using a resistor, you want to avoid having the resistance change as the temperature changes. While this isn’t exactly possible in real life, you can make it so that only a small change in resistance corresponds with a large change of temperature. If you couldn’t do that, resistors could really wreak havoc in circuits. For example, an LED that brightens and dims as the ambient temperature changes.
So what do you do if you actually want an LED’s brightness to be a function of temperature? Well, you’ll need a thermistor. Thermistors have a large change in resistance with just a small change of temperature. To illustrate this concept, see Figure 1 below, which shows the typical curve of a thermistor:
A thermistor can be easily tailored to different ranges, depending on the one you purchase. As you can see above, as the temperature rises, the resistance lowers. This is a main property of Negative Temperature Coefficient resistors (NTC).
Thermistors are also available as Positive Temperature Coefficient (PTC). PTCs work in a way that as the temperature rises, the resistance increases. However, note that PTC thermistors have a sort of tipping point and can greatly affect the resistance at some temperatures, making the PTC thermistor a bit more challenging with which to interface. Because of this, most low-cost temperature measurements utilize NTC thermistors.
That said, assume that we'll be referring to NTC type thermistors for the remainder of the article.
Finding a Curve-Fitting Formula: Four Approaches
Now that we covered the general behavior of thermistors, the next question on your mind may be how we could possibly measure temperature with an Arduino. The curve in the graph above is nonlinear, so a simple linear equation does not seem possible. (In reality, we can work out an equation. More on this later in the article.)
So what to do?
Before we continue, try to think about how you would do this with an Arduino or even just a circuit without a microprocessor component.
There are a few ways you can approach this problem. Four options are discussed below. This is far from an exhaustive list of every technique out there, but it covers some popular approaches.
Approach One
Some manufacturers provide an entire chart mapping out a specific integer range of the typical values for temperature and resistance. One such thermistor can be seen on this datasheet, written by the company Vishay.
But again, the question arises of how you could do this in the Arduino. All of these values would need to be hard coded into a huge lookup table or very long "switch…case" or "if…then" control structures.
And if the manufacturer doesn’t provide a lookup table, each point needs to be measured by you in order to generate the data. Sounds like a programming headache, right? However, this method does have its place. For instance, if your project at hand check only a few points or stay within a small range, this may be the way to go. An example would be if you only want to measure when the values fall within select temperature ranges with an LED that lights up to indicate each time it happened.
In this project, however, we want to measure a near-continuous range and send it to the serial monitor, rendering this method the wrong one for us.
Approach Two
Another approach is to “linearize” the response from the thermistor by adding external circuitry by connecting a resistor in parallel with the thermistor. Some ICs already to this for you.
Choosing the correct value and determining how to pick and linearize a region is an article in its own right. Approach two is a great fit if your microprocessor lack floating point precision (such as PICAXE) because it simplifies a range of temperature to a linear response, and also makes designing a circuit without a microprocessor much easier.
For this project, we have a microprocessor and will make use of the entire range, so again this approach won’t work for us.
Approach Three
If you’re a glutton for punishment, you could take the table data from the datasheet or, even more time-consuming, generate your own data you develop with independent measurements to recreate the plot in a program like Excel. From there, you could us the curve fitting feature to generate a curve formula. This provides you with a handy formula in your program, but it takes time and preprocessing of data.
We’d rather not be stuck analyzing all of this data, although approach three is a legitimate approach. Also, every thermistor is a bit different (not a huge issue if the the tolerance level is pretty low).
Approach Four
There exists a general curve fitting formula, known as the Steinhart-Hart equation, for devices like thermistors. There are different versions where the squared and cubed terms are both used. Here’s one version of the equation:
1/T = A + Bln(R) + C(ln(R))3
where R is the resistance of the thermistor at temperature T (in Kelvins).
This general curve fitting equation can accommodate all NTC type resistors, due to the approximation of the relationship of resistance and temperature is sufficient for most applications.
Note that the equation requires three constants: A, B, and C. These constants are different for each thermistor and have to be either given or calculated. With three unknowns, you need three measurements of resistance at a certain temperature. From there, these measurements create three equations to solve for these constants.
Even if you are an algebraic wizard, this is a lot of work.
Luckily, there is a simpler equation that is less accurate but has only one constant. The constant is denoted by β, and so the equation is known as the β equation.
1/T = 1/TO + (1/β) ⋅ ln (R/RO)
where RO refers to the resistance at your reference temperature TO (e.g., the resistance at room temperature).
β is usually provided in a datasheet. If it is not, you only need one measurement (one equation) to calculate for it.
And so we have found it. This is the equation, and the approach, that we will use for our thermistor interface, because it is pretty straightforward to code, while also being simplest one we have yet to find that doesn’t need to linearize the thermistor’s response.
Measuring Resistance with an Arduino
Since we’ve found the correct approach, we need to now figure out how to actually measure the resistance with our Arduino before we can plug it into the β equation. This can be done using a voltage divider:
Above is our interface circuit for our thermistor. Each time the thermistor senses a change in temperature, it will be reflected in the output voltage.
Generally, we use a voltage divider with the equation below:
Vout = Vs ⋅ (Rbalance / Rthermistor + Rbalance)
However, we do not want Vout as the answer - we want Rthermistor. We’ll solve for that with:
Rthermistor = Rbalance ⋅ (Vs/Vout − 1)
We’re about to perfect, but we need to measure our voltage output as well as the supply voltage. This is where the Arduino’s built-in ADC comes in handy.
We can represent the voltage as a digital number within a certain scale. So, our equation winds up as:
Rthermistor = Rbalance ⋅ (Dmax/Dmeasured − 1)
This works out mathematically due to the fact that no matter how we represent the voltage (in volts or in digital units), these units cancel out top and bottom in the fraction, leaving a dimensionless number. After that, multiply by a resistance to yield your answer in ohms.
Dmax for us will be 1023 because it is the highest number generated by our 10-bit ADC. Dmeasured will be set as our measured ADC value, ranging from be as low as zero to as high as 1023.
Now, let’s build!
Wiring It Up
We will use a 10K ohm thermistor, as well as a 10k ohm resistor for Rbalance in our voltage divider. No β was given, so that needs to be calculated.
Below you will find complete schematic.
Here's what the setup should end up looking like:
Arduino Code
The code below is laid out with helpful comments to help guide you through the logic.
The code measures the divider's voltage, calculates the temperature, and displays this in the serial terminal.
There are also some "if…then" statements sprinkled in for fun, to show how you can act upon a range of temperatures and a single data point.
/*
================================================================================
File........... Thermistor_Demo_Code
Purpose........ Thermistor demonstration code
Author......... Joseph Corleto
E-mail......... corleto.joseph@gmail.com
Started........ 7/25/2016
Finished....... 7/25/2016
Updated........ --/--/----
================================================================================
Notes
================================================================================
================================================================================
Updates
================================================================================
*/
//===============================================================================
// Header Files
//===============================================================================
//===============================================================================
// Constants
//===============================================================================
//Thermistor related:
/* Here we have a few constants that make editing the code easier. I will go
through them one by one.
A reading from the ADC might give one value at one sample and then a little
different the next time around. To eliminate noisy readings, we can sample
the ADC pin a few times and then average the samples to get something more
solid. This constant is utilized in the readThermistor function.
*/
const int SAMPLE_NUMBER = 10;
/* In order to use the Beta equation, we must know our other resistor
within our resistor divider. If you are using something with large tolerance,
like at 5% or even 1%, measure it and place your result here in ohms. */
const double BALANCE_RESISTOR = 9710.0;
// This helps calculate the thermistor's resistance (check article for details).
const double MAX_ADC = 1023.0;
/* This is thermistor dependent and it should be in the datasheet, or refer to the
article for how to calculate it using the Beta equation.
I had to do this, but I would try to get a thermistor with a known
beta if you want to avoid empirical calculations. */
const double BETA = 3974.0;
/* This is also needed for the conversion equation as "typical" room temperature
is needed as an input. */
const double ROOM_TEMP = 298.15; // room temperature in Kelvin
/* Thermistors will have a typical resistance at room temperature so write this
down here. Again, needed for conversion equations. */
const double RESISTOR_ROOM_TEMP = 10000.0;
//===============================================================================
// Variables
//===============================================================================
// Here is where we will save the current temperature
double currentTemperature = 0;
//===============================================================================
// Pin Declarations
//===============================================================================
//Inputs:
int thermistorPin = 0; // Where the ADC samples the resistor divider's output
//Outputs:
//===============================================================================
// Initialization
//===============================================================================
void setup()
{
// Set the port speed for serial window messages
Serial.begin(9600);
}
//===============================================================================
// Main
//===============================================================================
void loop()
{
/* The main loop is pretty simple, it prints what the temperature is in the
serial window. The heart of the program is within the readThermistor
function. */
currentTemperature = readThermistor();
delay(3000);
/* Here is how you can act upon a temperature that is too hot,
too cold or just right. */
if (currentTemperature > 21.0 && currentTemperature < 24.0)
{
Serial.print("It is ");
Serial.print(currentTemperature);
Serial.println("C. Ahhh, very nice temperature.");
}
else if (currentTemperature >= 24.0)
{
Serial.print("It is ");
Serial.print(currentTemperature);
Serial.println("C. I feel like a hot tamale!");
}
else
{
Serial.print("It is ");
Serial.print(currentTemperature);
Serial.println("C. Brrrrrr, it's COLD!");
}
}
//===============================================================================
// Functions
//===============================================================================
/////////////////////////////
////// readThermistor ///////
/////////////////////////////
/*
This function reads the analog pin as shown below. Converts voltage signal
to a digital representation with analog to digital conversion. However, this is
done multiple times so that we can average it to eliminate measurement errors.
This averaged number is then used to calculate the resistance of the thermistor.
After this, the resistance is used to calculate the temperature of the
thermistor. Finally, the temperature is converted to celsius. Please refer to
the allaboutcircuits.com article for the specifics and general theory of this
process.
Quick Schematic in case you are too lazy to look at the site :P
(Ground) ----\/\/\/-------|-------\/\/\/---- V_supply
R_balance | R_thermistor
|
Analog Pin
*/
double readThermistor()
{
// variables that live in this function
double rThermistor = 0; // Holds thermistor resistance value
double tKelvin = 0; // Holds calculated temperature
double tCelsius = 0; // Hold temperature in celsius
double adcAverage = 0; // Holds the average voltage measurement
int adcSamples[SAMPLE_NUMBER]; // Array to hold each voltage measurement
/* Calculate thermistor's average resistance:
As mentioned in the top of the code, we will sample the ADC pin a few times
to get a bunch of samples. A slight delay is added to properly have the
analogRead function sample properly */
for (int i = 0; i < SAMPLE_NUMBER; i++)
{
adcSamples[i] = analogRead(thermistorPin); // read from pin and store
delay(10); // wait 10 milliseconds
}
/* Then, we will simply average all of those samples up for a "stiffer"
measurement. */
for (int i = 0; i < SAMPLE_NUMBER; i++)
{
adcAverage += adcSamples[i]; // add all samples up . . .
}
adcAverage /= SAMPLE_NUMBER; // . . . average it w/ divide
/* Here we calculate the thermistor’s resistance using the equation
discussed in the article. */
rThermistor = BALANCE_RESISTOR * ( (MAX_ADC / adcAverage) - 1);
/* Here is where the Beta equation is used, but it is different
from what the article describes. Don't worry! It has been rearranged
algebraically to give a "better" looking formula. I encourage you
to try to manipulate the equation from the article yourself to get
better at algebra. And if not, just use what is shown here and take it
for granted or input the formula directly from the article, exactly
as it is shown. Either way will work! */
tKelvin = (BETA * ROOM_TEMP) /
(BETA + (ROOM_TEMP * log(rThermistor / RESISTOR_ROOM_TEMP)));
/* I will use the units of Celsius to indicate temperature. I did this
just so I can see the typical room temperature, which is 25 degrees
Celsius, when I first try the program out. I prefer Fahrenheit, but
I leave it up to you to either change this function, or create
another function which converts between the two units. */
tCelsius = tKelvin - 273.15; // convert kelvin to celsius
return tCelsius; // Return the temperature in Celsius
}
Download the code for this project here.
Next Steps
This project shows a pretty simple way of learning to measure temperature with a cheap thermistor. Want to improve upon the setup? Here are a couple of ideas:
- Put a small capacitor parallel to the output voltage. Doing so would stabilize the voltage and could even eliminate the need to average so many samples (as in the code).
- Use precision resistors (better than 1%) to give you a more consistent and predictable measurement. Note that if you need absolute critical temperature measurement, the self-heating of the thermistor can influence measurements. This particular project does not compensate for self-heating.
If you’ve completed this project, you should have a better idea on how you can measure temperature in your future projects!
Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.
Visit TechForum