Saturday, November 14, 2015

Working with a Thermistor: a C Programming Example

Recently I worked on a project that needed to monitor temperature using a thermistor. A thermistor is a resistor that measures temperature: the resistance changes depending on how hot it is. They are used in all kinds of electronic devices to monitor temperature and keep components from oveheating.

I searched for a good, simple worked-out example for how to get an accurate reading from the thermistor, but had trouble finding something readable. I am not actually an electrical engineer and have never studied the math behind thermistors formally, but I was able to adapt the formulas from some existing sources, with a little help. I am sharing this example in the hope that it might be useful to someone trying to solve a similar problem. Please note the way I have adapted the general thermistor math to our particular part and circuit; unless you have an identical part and measurement circuit, you will probably not be able to use this example exactly "as-is."

As I understand it, most modern thermistor components are "NTC," which means that they have a "negative temperature coefficient," meaning that their resistance has an inverse relationship to temperature: higher temperature, lower resistance. Thermistors have highly non-linear response, and are usually characterized by the Steinhart-Hart equation. This equation is a general equation that can be parameterized to model the response curve associated with a specific thermistor device. The original form of the equation takes three coefficients, A, B, and C, and describes the relationship between thermistor resistance and temperature in degrees Kelvin (K). It turns out that the three-coefficient form is overkill for a lot of parts and their response curve can be characterized accurately with a single parameter, using a simplified version of the equation This single parameter is called "beta" and so the equation can be called the Beta Parameter Equation.

Reading a thermistor is complicated by the fact that in a typical application we are first using resistance as a measurment of, or proxy for, temperature; that's the basic thing a thermistor does. But in a circuit we don't read resistance directly; instead, we would typically read voltage as a measure of, or proxy for, resistance. To read the resistance from a thermistor we treat it like we would treat a variable resistor, aka potentiometer. We use a voltage divider. This consists of two resistors in series. In our case we place the thermistor after a fixed resistor, and tap the voltage in between. This goes to an ADC - and analog-to-digital converter. I'm going to assume that you already have a reasonably accurate ADC and working code to take a reading from it.

So now I'm going to describe how I took the general thermistor math and adapted it for a specific part and circuit. Our specific thermistor is a Murata NCP18XH103F03RB. So you can Google the vendor and part number and find a datasheet. You need to find out a few things from the datasheet, specifically the nominal resistance at the reference temperature, which is usually 25 degrees Celsius, or 298.15K (or if it is not, note the reference voltage). Also, the datasheet should specify the beta value for your part; in our case, it is 3380.

The beta parameter equation, solved for resistance, reads:

Rt = R0 * e^( -B * ( 1 / T0 - 1 / T ) )

Where Rt is the resistance as a proxy for temperature, e is the mathematical constant e, B is beta, T0 is the reference temperature in K, and T is the measured temperature in degrees Kelvin. We want temperature given resistance, so we can solve it for temperature, like so:

T = B / ln( R / ( R0 * e^( -B / T0 ) ) )

Plugging in our R0 = 10,000 ohms, B = 3380, and T0 = 298.15 K we get:

t = 10000 * e^( -3380 * ( 1 / 298.15 - 1 / T ) )

or

T = 3380 / ln( R / ( 10000 * e^( -3380 / 298.15 ) ) )

Now, we need to have something to plug in for R, given the fact that we're reading a voltage from a voltage divider. In our case, the fixed resistor in our voltage divider has the same resistance value in ohms as the nominal resistance for our thermistor at 25 C, 10 kohms (10,000 ohms). Our voltage going into the voltage divider is 2.5V. The standard formula for a voltage divider like this, arranged with the fixed resistor first in the series, before the thermistor, is:

V = 2.5 * ( R / ( 10000 + R ) )

If your thermistor comes before the fixed resistor, you will want to swap the two R values values (see the Wikipedia article on voltage dividers I mentioned above). To get resistance given voltage, we can solve the above for R:

R = 20000 * v / ( 5 - 2 * v )

Now we've got a formula that we can use to convert a voltage reading to a thermistor resistance reading R. We can actually plug the right hand side of that right into our beta parameter equation from above, replacing R:

T = 3380 / ln( ( 20000 * v / ( 5 - 2 * v ) ) / ( 10000 * e^( -3380 / 298.15 ) ) )

That looks kind of monstrous; it really seems like this ought to be simpler than that. But Wolfram Alpha could simplify it when my own algebra skills gave out. You can just go to the Wolfram Alpha site and paste in that equation, being careful to get the parentheses in the right place. You will want to change the T to a t so that Wolfram Alpha interprets it as a variable, rather than Tesla units. Here's the result. Note that Wolfram Alpha has provided a nicely simplified version of the equation, perfect for our needs:

t = 3380 / ln( 167665 * v / ( 5 - 2 * v ) )

That equation describes temperature, in K, as a function of the measured voltage from our specific thermistor and voltage divider circuit. Again, keep in mind that unless you have an identical part and circuit, you will not be able to use this formula as-is. Testing against a thermocouple and hand-held infrared thermometer suggests, so far, that our temperature readings seemed to be accurate to within a degree C. We have not tested it with extremely high or low temperatures yet, but I expect it to be reasonably accurate; for this application, which involves setting fan speed and determining if we need to shut down components, we don't need a high degree of accuracy.

Finally, remember that the results of this formula are in degrees Kelvin. A C programming language expression for converting degrees Kelvin to degrees Celsius is simply:

k - 273.15F

where k is a floating-point value representing degrees Kelvin. Similarly, you can convert to degrees Fahrenheit like so:

k * 9.0F / 5.0F - 459.67F

and the C expression to implement our voltage-to-temperature function is:

3380.0F / log( ( 167665.0F * v ) / ( 5.0F - ( 2.0F * v ) ) )

where v is a floating-point value representing the voltage from our voltage divider, and log is the C programming language's natural logarithm function, part of the C standard library of math functions.

Please take care when using expressions like this. Note that it contains floating-point division operations. You must check to make sure that the values you are dividing by are non-zero! If not, the result will be a floating-point "not-a-number" value (NaN). Depending on your platform, this may happen silently, without any sort of runtime error, and the result will then "poison" any downstream calculations carried out with this value.

I hope this has been helpful. Please leave a comment (note: comments are moderated, so you will not see them appear immediately) if you were able to adapt this approach to your project! If you have a question, you can leave a comment too, although keep in mind that I'm not actually an electrical engineer and so would be better at answering programming questions than circuit design questions. Happy measuring!

No comments: