Measuring Temperature using an NTC Thermistor using an Abbreviated Lookup Table

copyright, Peter H Anderson, Baltimore, MD, June, '05


Introduction.

This discussion deals with the use of a 10K negative temperature coefficient (NTC) thermistor to measure and calculate temperature. I have specifically focused on the Vishay / BC Components 2381-640-66103 bead type thermistor with axial leads and 2381-633-83103 glass thermistor with radial leads. However, the general approach may be used for any NTC thermistors.

Note that Digikey stocks these as Digikey part numbers BC1482 and BC1491. These are 2322 devices as opposed to 2381. I am uncertain if the 2381 devices may be new devices which have yet to make it into the supply chain. However, the thermal characteristics are identical.

NTC thermistors are relatively inexpensive, stable and even with a 10-bit A/D converter are capable of resolving temperature to within 0.1 degrees C over most of the temperature range. The disadvantage is the calculation of temperature is beyond the capability of many processors.

In this discussion, I focus on how the calculation may be quite accurately performed with an abbreviated table lookup and interpolation. In this example, less than 256 reference points (bytes) are used with an added interpolation of four points between each reference. The result is a resolution of nominally 0.1 Degees C over the range of 0.0 to 37.0 degrees C and 0.2 degrees over the range of -20.0 to 65 degrees C. My feeling is that this table lookup approach is as good as using a processor which has floating point calculations and can handle the log function and raise numbers to powers and it uses a tiny fraction of the program memory.

Note that the number of reference points might be reduced to nominally 128, 64, 32 or even 16 reference points admittedly with some loss of accuracy.

Details

Consider the following configuration;


   +5 VDC
   |
   R1 = 10.0K
   |
   ---------------------------------- PICAXE A/D input
   |                          +
   10K NTC thermistor         Vtherm
   |                          -
   GRD
The voltage, Vtherm is calculated using voltage division;

(1)   Vtherm = Rtherm / (R1 + Rtherm) * 5.0
When using a ten bit A/D, the voltage Rtherm is calculated as;
(2)   Vtherm = ADVal / 1024 * +5 VDC
Using (1) and (2);
(3)   Rtherm = R1 / (1024 / ADVal - 1.0)
where R1 is 10.0K.

Note that for an 8-bit A/D, there are 256 quantizing bands and the above expression is;

(4)  Rtherm = R1 / (256 / ADVal - 1.0)
The temperature in degrees K is then calculated using a four point model as;
(5) Tkelvin = 1.0 / (A_const + B_const * ln(Rtherm/10.0e3)
              + C_const * ln(Rtherm/10.0e3)^2 + D_const * ln(Rtherm/10.0)^3)
This expression is discussed in greater detail below.

The temperature in degrees C is then;

(6) Tcelcius = Tkelvin - 273.15
Note that for each value of ADVal, other than zero, there is a corresponding temperature and thus, one implementation might be a 1024 word (2 byte) lookup table. That is, the value of temperature for each of the 1024 values of ADVal. This is well beyond the capability of any PICAXE.

I used Microsoft Excel to calculate the Tcelcius at 256 points. A portion of the spreadsheet is shown below. The complete spreadsheet is shown here.

ADVal	Rtherm	Tkelvin	Tcelc   Diff	Diff * 100

2.00	78.74	500.14	226.99	#VALUE!	#VALUE!
3.00	118.58	462.25	189.10	37.89	3789.00
4.00	158.73	442.41	169.26	19.84	1984.00
5.00	199.20	429.23	156.08	13.19	1319.00
6.00	240.00	419.45	146.30	9.78	978.00
7.00	281.12	411.73	138.58	7.72	772.00
8.00	322.58	405.39	132.24	6.35	635.00
9.00	364.37	400.01	126.86	5.38	538.00
10.00	406.50	395.36	122.21	4.65	465.00
11.00	448.98	391.26	118.11	4.10	410.00
12.00	491.80	387.61	114.46	3.65	365.00
13.00	534.98	384.31	111.16	3.29	329.00
14.00	578.51	381.32	108.17	3.00	300.00
15.00	622.41	378.57	105.42	2.75	275.00
16.00	666.67	376.03	102.88	2.54	254.00	<<< Note
17.00	711.30	373.67	100.52	2.36	236.00
18.00	756.30	371.47	98.32	2.20	220.00
19.00	801.69	369.42	96.27	2.06	206.00
20.00	847.46	367.48	94.33	1.94	194.00
21.00	893.62	365.65	92.50	1.83	183.00
22.00	940.17	363.92	90.77	1.73	173.00
23.00	987.12	362.27	89.12	1.64	164.00
24.00	1034.48	360.71	87.56	1.57	157.00
25.00	1082.25	359.22	86.07	1.49	149.00
26.00	1130.43	357.79	84.64	1.43	143.00
27.00	1179.04	356.42	83.27	1.37	137.00
28.00	1228.07	355.11	81.96	1.31	131.00
29.00	1277.53	353.84	80.69	1.26	126.00
...
238.00	132222.22	248.74	-24.41	0.94	94.00
239.00	140588.24	247.77	-25.38	0.98	98.00
240.00	150000.00	246.75	-26.40	1.02	102.00
241.00	160666.67	245.68	-27.47	1.07	107.00
242.00	172857.14	244.55	-28.60	1.13	113.00
243.00	186923.08	243.36	-29.79	1.19	119.00
244.00	203333.33	242.10	-31.05	1.26	126.00
245.00	222727.27	240.75	-32.40	1.35	135.00
246.00	246000.00	239.31	-33.84	1.44	144.00
247.00	274444.44	237.75	-35.40	1.56	156.00
248.00	310000.00	236.06	-37.09	1.70	170.00
249.00	355714.29	234.19	-38.96	1.87	187.00
250.00	416666.67	232.11	-41.04	2.08	208.00
251.00	502000.00	229.76	-43.39	2.35	235.00
252.00	630000.00	227.04	-46.11	2.72	272.00	<<< Note
253.00	843333.33	223.79	-49.36	3.25	325.00
254.00	1270000.00	219.73	-53.42	4.06	406.00
255.00	2550000.00	214.23	-58.92	5.51	551.00

The first column is the the values of ADVal. Column B are the corresponding values of Rtherm which are calulated using equation (4). The Tkelvin is calculated using (5) with values of constants noted below. (More on this later).

   A_const = 3.354016e-3
   B_const = 2.569107e-4
   C_const = 2.626311e-6
   D_const = 0.675278e-7

This in itself, might be adequate in many situations. However, a byte is capable of storing unsigned values in the range of 0 - 255 or signed values of -127 to +128. It is indeed workable, but limits the temperature to one degree of resolution. I was looking for something better.

Thus, in the next column, I calculated the difference between two adjacent cells in the Tcelcius column and in the last column I multiplied this by 100.

Thus, if the ADVal is 2, the corresponding Tcelcius_100 (100 times Tcelcius) is 22699. If the ADVal is 3, the Tcelcius_100 is 22699 - 3789. If the ADVal is 6, the corresponding Tcelcius_100 is 22699 - 3789 - 1984 - 1319 - 978. That is, the Tcelcius_100 for any ADVAL may be calculated using a table consisting of the last column.

Well, there is more complexity as an unsigned byte is limited to values in the range of 0 to 255, not such values as 3789, 1984, etc.

However, scroll down to an ADVal of 15, where the temperature, Tcelcius_100 is 10542. If the ADVal is 16, the Tcelcius_100 is 10542 - 254. If the ADVal is 20, the Tcelcius_100 is 10542 - 254 - 236 - 220 - 194. Note that values such as 254, 236, etc may be stored in a byte.

Thus the Diff * 100 values in the last column over ADVals of 16 to 251 may be arranged in a table. If the ADVal is less than 15, the temperature is too hot. If the ADVal is above 251, the temperature is too cold. Otherwise, the start temperature is 10542 minus each value in the table until the ADVal is reached.

Program Therm1.Bas implements this technique.

Program Therm1.Bas

' Therm1.Bas - PICAXE-18X
'
' Illustrates an interface with an NTC thermistor.
'
' Measures voltage across the thermistor and uses a combination of table lookup
' and linear interpolation to calculate the temperature in degrees C.
'
' copyright, Peter H Anderson, Baltimore, MD, June, '05

  Symbol ADVal = W0
  Symbol TC_100 = W0	' ADVal not needed when TC_100 is calculated

  Symbol ADValHi8 = B2
  Symbol ADValLow2 = B3
  Symbol N = B4
  Symbol Diff = B5
  Symbol Whole = B6
  Symbol Fract = B7
  Symbol SignFlag = B4	' N is not need when this is used
  Symbol Dig = B5	' Diff not needed when this is used

Top:

  GoSub MeasTemp
  If TC_100 = $7fff Then OutofRange
  GoSub DisplayTemp
Top1:
  Wait 5  ' perform measurement every five seconds
  GoTo Top


OutofRange:
  SerTxD ("Out of Range", 10, 13)
  GoTo Top1

MeasTemp:

  ReadADC10 0, ADVal
  ADValHi8 = ADVal / 4    ' isolate the high 8 bits
  ADValLow2 = ADVal & $03 ' low two bits

  TC_100 = 10542 ' adjust this as required
  If ADValHi8 < 16 Then TooHot
  If ADValHi8 > 251 Then TooCold


  For N = 0 to ADValHi8 ' continue to subtract
     Read N, Diff
     TC_100 = TC_100 - Diff
  Next
  ' Now for the low two bits, a linear interpolation

  N = N + 1
  Read N, Diff
  Diff = Diff / 4 * ADValLow2

  TC_100 = TC_100 - Diff

MeasTemp_1:

  Return

TooHot:
TooCold:
   TC_100 = $7fff
   GoTo MeasTemp_1


DisplayTemp:
  SignFlag = Tc_100 / 256 / 128
  If SignFlag = 0 Then Positive
    TC_100 = TC_100 ^ $ffff + 1	' twos comp
    SerTxD ("-")

Positive:

  Whole = TC_100 / 100
  Fract = TC_100 % 100
  SerTxD (#Whole, ".")
  ' be sure the fractional is two digits
  Dig = Fract / 10
  SerTxD (#Dig)
  Dig = Fract % 10
  SerTxD (#Dig, 10, 13)

  Return


'''''''''''''''''''''''''''''''''''''''''''''''''''''''
 ' EEPROM locations 0 - 15 not used

  EEPROM 16, (254, 236, 220, 206, 194, 183, 173, 164)
  EEPROM 24, (157, 149, 143, 137, 131, 126, 122, 117)
  EEPROM 32, (113, 110, 106, 103, 100, 97, 94, 92)
  EEPROM 40, (89, 87, 85, 83, 81, 79, 77, 76)
  EEPROM 48, (74, 73, 71, 70, 69, 67, 66, 65)
  EEPROM 56, (64, 63, 62, 61, 60, 59, 58, 57)
  EEPROM 64, (57, 56, 55, 54, 54, 53, 52, 52)
  EEPROM 72, (51, 51, 50, 49, 49, 48, 48, 47)
  EEPROM 80, (47, 46, 46, 46, 45, 45, 44, 44)
  EEPROM 88, (44, 43, 43, 43, 42, 42, 42, 41)
  EEPROM 96, (41, 41, 41, 40, 40, 40, 40, 39)
  EEPROM 104, (39, 39, 39, 39, 38, 38, 38, 38)
  EEPROM 112, (38, 38, 37, 37, 37, 37, 37, 37)
  EEPROM 120, (37, 36, 36, 36, 36, 36, 36, 36)
  EEPROM 128, (36, 36, 36, 36, 36, 35, 35, 35)
  EEPROM 136, (35, 35, 35, 35, 35, 35, 35, 35)
  EEPROM 144, (35, 35, 35, 35, 35, 35, 35, 35)
  EEPROM 152, (35, 35, 35, 35, 35, 35, 35, 35)
  EEPROM 160, (36, 36, 36, 36, 36, 36, 36, 36)
  EEPROM 168, (36, 36, 36, 37, 37, 37, 37, 37)
  EEPROM 176, (37, 37, 38, 38, 38, 38, 38, 39)
  EEPROM 184, (39, 39, 39, 39, 40, 40, 40, 41)
  EEPROM 192, (41, 41, 42, 42, 42, 43, 43, 43)
  EEPROM 200, (44, 44, 45, 45, 46, 46, 47, 47)
  EEPROM 208, (48, 48, 49, 50, 50, 51, 52, 53)
  EEPROM 216, (53, 54, 55, 56, 57, 58, 59, 61)
  EEPROM 224, (62, 63, 65, 66, 68, 70, 72, 74)
  EEPROM 232, (76, 78, 81, 84, 87, 90, 94, 98)
  EEPROM 240, (102, 107, 113, 119, 126, 135, 144, 156)
  EEPROM 248, (170, 187, 208, 235)

Calibration.

In the above, the temperature corresponding to an ADVal of 15 is 105.42 (or Tcelcius_100 = 10542). This will result in a temperature of 25.00 (or Tcelcius_100 of 2500) at an ADVAL of 512, or at 128 in the above table. This is for an "ideal" thermistor.

Assume the above program is run in an environment where the temperature is 25.30 degrees C, but the reported temperature is 22.36 degrees. It is a simple matter to add 2530 - 2236 = 294 to the initial 10542 value. For example;

    TC_100 = 10542 + 294

Program Therm2.Bas.

This program illustrates a four channel temperature measurement system using four 10K - 10K thermistor divider networks on A/D channels 0, 1, 6 and 7. Note that in the example, I have used different calibration values for each of the thermistors.

In addition, I have implemented a four channel thermostat using outputs 0, 1, 2 and 3. Note that this would be somewaht more complex for negative temperatures and trip points.

' Therm2.Bas - PICAXE-18X
'
' Four channel temperature measurement using 10K NTC thermistors on A/D 0, 1, 6, 7.
'
' Measures voltage across the thermistor and uses a combination of table lookup
' and linear interpolation to calculate the temperature in degrees C.
'
' Sends temperature to PC (or similar).
'
' For each channel, operates or releases relays, or similar, on OUT0, 1, 2 or 3.  Note that
' this may be used to turn on a fan (hot alarm) or turn on a heater (cold alarm).
'
' Uses about 400 bytes of 2048 available.
'
' copyright, Peter H Anderson, Baltimore, MD, June, '05

  Symbol ADVal = W0
  Symbol TC_100 = W0	' ADVal not needed when TC_100 is calculated

  Symbol Channel = B2

  Symbol ADValHi8 = B3
  Symbol ADValLow2 = B4
  Symbol N = B5
  Symbol Diff = B6
  Symbol Whole = B7
  Symbol Fract = B8
  Symbol SignFlag = B5	' N is not need when this is used
  Symbol Dig = B6	' Diff not needed when this is used

Top:

  For Channel = 0 to 3
     GoSub MeasTemp
     GoSub DisplayTemp
     GoSub ThermostatSet
  Next

  Wait 5
  GoTo Top


MeasTemp:
  Branch Channel, (MeasTempCh0, MeasTempCh1, MeasTempCh2, MeasTempCh3)

MeasTempCh0:

  ReadADC10 0, ADVal
  ADValHi8 = ADVal / 4    ' isolate the high 8 bits
  ADValLow2 = ADVal & $03 ' low two bits

  TC_100 = 10542 + 234' adjust this as required.  Note this varies from channel to channel
  If ADValHi8  < 16 Then TooHot
  If ADValHi8 > 251 Then TooCold
  Goto MeasTempCalculate

MeasTempCh1:

  ReadADC10 1, ADVal
  ADValHi8 = ADVal / 4    ' isolate the high 8 bits
  ADValLow2 = ADVal & $03 ' low two bits

  TC_100 = 10542 - 123' adjust this as required.  Note this varies from channel to channel
  If ADValHi8  < 16 Then TooHot
  If ADValHi8 > 251 Then TooCold
  Goto MeasTempCalculate

MeasTempCh2:

  ReadADC10 4, ADVal ' Corresponds to IN6
  ADValHi8 = ADVal / 4    ' isolate the high 8 bits
  ADValLow2 = ADVal & $03 ' low two bits

  TC_100 = 10542 + 321' adjust this as required.  Note this varies from channel to channel
  If ADValHi8  < 16 Then TooHot
  If ADValHi8 > 251 Then TooCold
  Goto MeasTempCalculate

MeasTempCh3:

  ReadADC10 5, ADVal ' Correspond to IN7
  ADValHi8 = ADVal / 4    ' isolate the high 8 bits
  ADValLow2 = ADVal & $03 ' low two bits

  TC_100 = 10542 - 123' adjust this as required.  Note this varies from channel to channel
  If ADValHi8  < 16 Then TooHot
  If ADValHi8 > 251 Then TooCold
  Goto MeasTempCalculate


MeasTempCalculate:

  For N = 0 to ADValHi8 ' continue to subtract
     Read N, Diff
     TC_100 = TC_100 - Diff
  Next
  ' Now for the low two bits, a linear interpolation

  N = N + 1
  Read N, Diff
  Diff = Diff / 4 * ADValLow2

  TC_100 = TC_100 - Diff

MeasTemp_1:

  Return

TooHot:
TooCold:
   TC_100 = $7fff
   GoTo MeasTemp_1


DisplayTemp:
  SerTxD (#Channel, " ");
  If TC_100 = $7fff Then DisplayTempOutofRange
  SignFlag = Tc_100 / 256 / 128
  If SignFlag = 0 Then DisplayTempPositive
    TC_100 = TC_100 ^ $ffff + 1	' twos comp
    SerTxD ("-")

DisplayTempPositive:

  Whole = TC_100 / 100
  Fract = TC_100 % 100
  SerTxD (#Whole, ".")
  ' be sure the fractional is two digits
  Dig = Fract / 10
  SerTxD (#Dig)
  Dig = Fract % 10
  SerTxD (#Dig, 13, 10)
  Goto DisplayTempReturn

DisplayTempOutofRange:
  SerTxD ("Out of Range", 13, 10)
  Goto DisplayTempReturn

DisplayTempReturn:

  Return

ThermostatSet:

  Branch Channel, (ThermoStatSetCh0, ThermoStatSetCh1, ThermoStatSetCh2, ThermoStatSetCh3)

ThermoStatSetCh0:

  If TC_100 > 2900 Then TurnOn  ' Hot alarm 29.00, Off at 27.00
  If TC_100  < 2700 Then TurnOff
  Goto ThermoStatSetReturn

ThermoStatSetCh1:

  If TC_100 > 3200 Then TurnOn  ' Hot alarm 32.00.  Off at 27.00
  If TC_100  < 2700 Then TurnOff
  Goto ThermoStatSetReturn

ThermoStatSetCh2:

  If TC_100  < 400 Then TurnOn  ' Cold alarm 4.00.  Off at 10.00
  If TC_100 > 1000 Then TurnOff
  Goto ThermoStatSetReturn

ThermoStatSetCh3:

  If TC_100  < 2500 Then TurnOn  ' Cold alarm 25.00.  Off at 27.00
  If TC_100 > 2700 Then TurnOff
  Goto ThermoStatSetReturn

TurnOn:
  High Channel
  Goto ThermoStatSetReturn

TurnOff:
  Low Channel
  Goto ThermoStatSetReturn

ThermoStatSetReturn:
   Return


'''''''''''''''''''''''''''''''''''''''''''''''''''''''
 ' EEPROM locations 0 - 15 not used

  EEPROM 16, (254, 236, 220, 206, 194, 183, 173, 164)
  EEPROM 24, (157, 149, 143, 137, 131, 126, 122, 117)
  EEPROM 32, (113, 110, 106, 103, 100, 97, 94, 92)
  EEPROM 40, (89, 87, 85, 83, 81, 79, 77, 76)
  EEPROM 48, (74, 73, 71, 70, 69, 67, 66, 65)
  EEPROM 56, (64, 63, 62, 61, 60, 59, 58, 57)
  EEPROM 64, (57, 56, 55, 54, 54, 53, 52, 52)
  EEPROM 72, (51, 51, 50, 49, 49, 48, 48, 47)
  EEPROM 80, (47, 46, 46, 46, 45, 45, 44, 44)
  EEPROM 88, (44, 43, 43, 43, 42, 42, 42, 41)
  EEPROM 96, (41, 41, 41, 40, 40, 40, 40, 39)
  EEPROM 104, (39, 39, 39, 39, 38, 38, 38, 38)
  EEPROM 112, (38, 38, 37, 37, 37, 37, 37, 37)
  EEPROM 120, (37, 36, 36, 36, 36, 36, 36, 36)
  EEPROM 128, (36, 36, 36, 36, 36, 35, 35, 35)
  EEPROM 136, (35, 35, 35, 35, 35, 35, 35, 35)
  EEPROM 144, (35, 35, 35, 35, 35, 35, 35, 35)
  EEPROM 152, (35, 35, 35, 35, 35, 35, 35, 35)
  EEPROM 160, (36, 36, 36, 36, 36, 36, 36, 36)
  EEPROM 168, (36, 36, 36, 37, 37, 37, 37, 37)
  EEPROM 176, (37, 37, 38, 38, 38, 38, 38, 39)
  EEPROM 184, (39, 39, 39, 39, 40, 40, 40, 41)
  EEPROM 192, (41, 41, 42, 42, 42, 43, 43, 43)
  EEPROM 200, (44, 44, 45, 45, 46, 46, 47, 47)
  EEPROM 208, (48, 48, 49, 50, 50, 51, 52, 53)
  EEPROM 216, (53, 54, 55, 56, 57, 58, 59, 61)
  EEPROM 224, (62, 63, 65, 66, 68, 70, 72, 74)
  EEPROM 232, (76, 78, 81, 84, 87, 90, 94, 98)
  EEPROM 240, (102, 107, 113, 119, 126, 135, 144, 156)
  EEPROM 248, (170, 187, 208, 235)