Sunday 25 November 2012

More accurate thermistor tables

A couple of weeks ago I wanted to create some thermistor tables for Marlin. At that time it had a copy of createTemperatureLookup.py, which I think was written by Zach Smith based on my article "Measuring temperature the easy Way". It uses the simple two constant thermistor equation based on the resistance at 25°C and beta.

The two constant formula was adequate for my own software because I have separate constants for each thermistor that I use and I calibrate them against a thermocouple at the working temperature and room temperature. Marlin however has a thermistor table for each type of thermistor, so if you use the same thermistor for the bed and the hot end they share a table. The problem with the simple equation is that beta is not very constant and depends on temperature. There are several values given on the datasheet for different temperature ranges
, none of them very applicable to our application. If you have beta correct for the hot end at say 250°C it is about 7°C out at the bed temperature, say 130°C.

I decided to make a new script which uses the three constant Steinhart–Hart equation. The graph shows the difference between the two equations over a large temperature range: -
The two constant equation is only accurate around the two temperatures the constants are calculated at (in this case 25°C and 256°C). When these are at opposite ends of the thermistors range the error in the middle is quite large.

The script I made is MakeTempTable.py. Its parameters are resistances at three temperatures. The tables is makes look like this: -

    {     344,       300     }, // r=   101 adc=  21.47
    {     369,       295     }, // r=   108 adc=  23.08
    {     397,       290     }, // r=   117 adc=  24.83
    {     428,       285     }, // r=   126 adc=  26.75
    {     461,       280     }, // r=   136 adc=  28.84
    {     498,       275     }, // r=   147 adc=  31.12
    {     538,       270     }, // r=   160 adc=  33.63
    {     582,       265     }, // r=   173 adc=  36.37
    {     630,       260     }, // r=   188 adc=  39.38
    {     683,       255     }, // r=   205 adc=  42.69
    {     741,       250     }, // r=   223 adc=  46.32
    {     805,       245     }, // r=   243 adc=  50.31
    {     875,       240     }, // r=   266 adc=  54.71
    {     953,       235     }, // r=   290 adc=  59.55
    {    1038,       230     }, // r=   318 adc=  64.88
    {    1132,       225     }, // r=   349 adc=  70.77
    {    1236,       220     }, // r=   384 adc=  77.26
    {    1351,       215     }, // r=   423 adc=  84.42
    {    1477,       210     }, // r=   466 adc=  92.32
    {    1617,       205     }, // r=   515 adc= 101.05
    {    1771,       200     }, // r=   570 adc= 110.68
    {    1941,       195     }, // r=   632 adc= 121.30
    {    2128,       190     }, // r=   702 adc= 133.01
    {    2335,       185     }, // r=   782 adc= 145.91
    {    2562,       180     }, // r=   872 adc= 160.11
    {    2811,       175     }, // r=   975 adc= 175.70
    {    3085,       170     }, // r=  1092 adc= 192.81
    {    3384,       165     }, // r=  1225 adc= 211.53
    {    3711,       160     }, // r=  1378 adc= 231.95
    {    4066,       155     }, // r=  1554 adc= 254.15
    {    4451,       150     }, // r=  1756 adc= 278.21
    {    4866,       145     }, // r=  1989 adc= 304.15
    {    5312,       140     }, // r=  2258 adc= 331.99
    {    5787,       135     }, // r=  2570 adc= 361.68
    {    6290,       130     }, // r=  2934 adc= 393.15
    {    6820,       125     }, // r=  3357 adc= 426.25
    {    7373,       120     }, // r=  3852 adc= 460.80
    {    7945,       115     }, // r=  4433 adc= 496.54
    {    8531,       110     }, // r=  5116 adc= 533.16
    {    9125,       105     }, // r=  5921 adc= 570.31
    {    9722,       100     }, // r=  6875 adc= 607.60
    {   10314,        95     }, // r=  8007 adc= 644.61
    {   10895,        90     }, // r=  9356 adc= 680.92
    {   11458,        85     }, // r= 10968 adc= 716.13
    {   11998,        80     }, // r= 12903 adc= 749.86
    {   12509,        75     }, // r= 15234 adc= 781.80
    {   12987,        70     }, // r= 18051 adc= 811.66
    {   13428,        65     }, // r= 21469 adc= 839.27
    {   13832,        60     }, // r= 25635 adc= 864.50
    {   14197,        55     }, // r= 30732 adc= 887.30
    {   14523,        50     }, // r= 36995 adc= 907.68
    {   14811,        45     }, // r= 44725 adc= 925.72
    {   15064,        40     }, // r= 54309 adc= 941.52
    {   15284,        35     }, // r= 66249 adc= 955.23
    {   15472,        30     }, // r= 81195 adc= 967.02
    {   15633,        25     }, // r=100000 adc= 977.08
    {   15769,        20     }, // r=123783 adc= 985.58
    {   15883,        15     }, // r=154025 adc= 992.71
    {   15978,        10     }, // r=192694 adc= 998.64
    {   16057,         5     }, // r=242427 adc=1003.54
    {   16121,         0     }, // r=306773 adc=1007.56


The ADC values in the table are multiplied by 16 because Marlin uses oversampling to give four more bits of precision. The old tables just multiplied the integer ADC value by 16 but I multiply it before rounding it to an integer so the table has the same precision as the oversampled ADC reading.

The script can also take ADC values as parameters instead of resistances. This allows you to calibrate a thermistor in situ. If you set the temperature to a value in an existing table and let it settle and then measure it with a thermocouple you know that the ADC value for the measured temperature is the value in the table for the set temperature. You can then produce a new more accurate table.

Two days after I put it on Github ErikZalm added a new script to the official version of Marlin to do exactly the same thing: createTemperatureLookupMarlin.py, amazing coincidence! It is different code but I think it uses exactly the same maths to find the three coefficients using simultaneous equations that I lifted from here