Thermistor and Thermocouple Temperature Logger Part 2



This week in the epic adventure I like to call Thermistor and Thermocouple Temperature Logger Part 2, I took a look at the thermistor portion of my circuit.

Last week I used the MAX31855 thermocouple IC to interface to T-type thermocouples using the SPI interface on a PIC16F1789.  This week I wanted to interface the PIC16F1789 to two thermistors.  Normally you can find a thermistor’s equation from the datasheet that correlates resistance to temperature.  If you have a controller that can handle some math (natural log or exponential functions) you can use the equation.  An alternative to avoid the math is to use look-up tables that relate an analog measurement to temperature.

In this particular design I was using an off-the-shelf cooking thermometer.  Unfortunately, the exact thermistor used in the thermometer is not readily available from the manufacturer.   So I needed to determine how the resistance of this thermometer relates to temperature.

To do that I had to first read the resistance of the thermometer.  That begins by converting the resistance to an analog voltage.



The cooking thermometers (Thermoworks TW362XX) use stereo jacks to connect to measurement circuitry.  I used a standard voltage divider followed by an op-amp buffer to isolate the voltage divider impedance from the PIC16F1789’s analog-to-digital converters (ADC).  This is necessary as any impedance over a few 1000 ohms can cause errors in the PIC16F1789 ADC measurements.  I also placed 10Kohm resistors to 3.3V R8 and R9 in my schematic as placeholders.  The actual value I used for testing was 49.9K ohm, and that would probably change based on the results of my thermistor tests.

The final system will be a battery powered system so I’ve added a MOSFET (Q1) to isolate the thermistors from ground when I’m not taking measurements.  When the gate of Q1 is high the thermistors are connected to ground allowing me to take voltage measurements.  Otherwise the gate is kept grounded reducing the current to ground to the leakage current of the MOSFET’s body diode.

The firmware to take resistor measurements looks a little more complicated than it is.  Here’s an overview of some of the actions it takes.

1.  I take 16 samples of the ADC in a row and then store the average of the values.  If you have a noisy power supply this can tend to increase the measured value.  I haven’t yet worked on my battery charging system so I wanted to allow some smoothing of the measurements.  In the final system I may need to take these 16 samples at intervals that span any periodic noise the battery charger produces.

2.  I’m using floating point variables (IEEE 24-bit) for the math to calculate the resistor value and to use a y=mx+b equation to accommodate some calibration of resistance measurements.  However, I’m also using integer variables in my communication protocol.  Therefore I store my thermistor values as Resistance/16.  This gives me an upper-range resistor value of 16*32,767 = 524,272 ohms.  That’s 2x the 25C resistance of the cooking thermometers I’m testing.  In the end I might just convert everything to ASCII and be done with it.  For now though, that’s why you see the scaling of certain values by 16.  This also makes my resistance measurements no better than +/-16 ohms (which is much lower than the resolution I expect to need).

3.  To convert from an ADC count to resistance I use the voltage divider equation and solve for Rthermistor.  Rth = R1*(Vin-Vout)/Vout .

4.  I use measurements of a known 49.9K ohm and 0 ohm resistor to determine the gain and offset values for a y=mx+b equation.  These are stored in non-volatile registers NV.Ints.Therm1Cal50Kohm (49.9K ohm) and NV.Ints.Therm1Cal0ohm (0 ohm).

5.  My code will be able to return either a resistance or a temperature when I’m done.  Since I’m still working on calculating the temperature from the thermistor resistance values it will only return the resistance values now.

6.  I have an additional ADC input (shown in the schematic above) that I can use to calibrate the ADC if needed.

/* Read_Thermistors:
 Reads the thermistor values and converts them to ohms or temperature
void Read_Thermistors(void)
    // Connect thermistor voltage dividers to ground
    // before measurement
    ENTHERM = 1;

    sample_time(25);  // allow some time for voltage divider to settle

    float Offset;    // resistor measurement offset
    float Gain;      // ratio of ideal 49900 resistor and actual 49900 plug
    float ADCWork;
    float Resistor = 49900;  // nominal value of voltage divider high resistor
    float Vin = 3.3;         // nominal VIN voltage
    float Vout;              // holder for calculated voltage at divider

    unsigned long ADCSum;
    char i;

    // thermistor 1
    Offset = (float)NV.Ints.Therm1Cal0Ohm*16;  // 0 ohm plug value  
    Gain = (float)NV.Ints.Therm1Cal50kOhm*16;  // 49.9K ohm plug value 
    Gain = 49900/Gain;             // gain is ideal value / measured value 

    ADCON0bits.CHS = 0x01;  // select correct channel of ADC
    sample_time(AQUTIME);   // wait for channel change
    ADCSum = 0;             // clear summation register
    for (i=0;i<16;i++)      // perform 16 samples
        ADCON0bits.ADGO = 1;
        while(ADCON0bits.ADGO == 1);
        ADCSum+=(ADRES>>4);  // ADC is 12-bit, left justified, so right shift
        sample_time(25);  // allow some time between samples, ~25uS
    ADCSum = (ADCSum>>4);  // divide by 16 to get average 

    ADCRaw[1] = (unsigned int)ADCSum; // keep the raw value
    ADCWork = (float)ADCRaw[1];  // convert ADC to a floating point variable
    Vout = ADCWork*0.000806;  // convert ADC to volts
    ADCWork = (Resistor*Vout)/(Vin-Vout);  // convert volts to resistance
    ADCWork = Gain*ADCWork - Offset; // y = mx + b equation
    ADCWork = ADCWork/16;  // store resistor as resistor / 16 so it fits in int16 register
    if (ADCWork < 0)  // if offset takes us below 0 then fix it
        ADCWork = 0;
    if (ADCWork > 32767) // if value is greater than max in register positive value then cap it
        ADCWork = 32767;
    if (MODE.Bits.ReportOhms == 1)
        NV.Ints.Thermistor1 = (int)ADCWork;
    else  // this is where to convert resistance to temperature
        NV.Ints.Thermistor1 = (int)ADCWork; // add code to convert to temperature

Using the code above I can take measurements in the +/- 200 ohm range.  That’s not too spectacular (just compare it to a multimeter).  One serious issue with my measurements is that I don’t have any battery in my battery circuit (which powers my 3.3V regulator).  I’m hoping to get better results when I can insert the Lithium-Ion battery into my system next week.


But at this point in the design I’m able to measure temperature with the thermocouple interface (+/- 2C at 0.25C resolution).  And I can measure resistance of the thermistors (+/- 200 ohm at 16 ohm resolution).  Using these two measurements, a pan of water, some ice, and I hot plate I was able to get a representation of the cooking thermometer’s resistance to temperature function.  I configured my communication software to log the data from a thermistor channel and a thermocouple channel every second, and then plotted degrees C with resistance.  The curve and equation are shown below.

The equation to convert thermistor resistance to temperature is y = -21.38ln(x) + 287.61 (from MS Excel).

There are several accuracy issues I still need to delve into.  It is also not a very good idea to use your own design as a test instrument like I did here.  But the resulting equation and logged data do fit the form of a thermistor’s temperature function.  I’ll need to run this same test with an independent temperature sensor and data logger to verify my results.

Speak Your Mind