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 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 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 | - GRDThe voltage, Vtherm is calculated using voltage division;
(1) Vtherm = Rtherm / (R1 + Rtherm) * 5.0When using a ten bit A/D, the voltage Rtherm is calculated as;
(2) Vtherm = ADVal / 1024 * +5 VDCUsing (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.15Note 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)