Saturday 25 July 2015

Thermistors as thermometers


 

I am taking a break from work of my microscope eyepiece project, and have been playing around with sensors using the Arduino embedded systems.

The device that is the most idiotically complicated to get meaningful information from is the humble thermistor.


Negative Coefficient (NTC) thermistors are used because of their large resistance change with temperature - as the temperature of the device increases, the resistance of the device decreases.

Unfortunately, the relationship between resistance and temperature is far from linear.

Indeed, the Steinhart-Hart Thermistor Equation is the kind of thing that causes nightmares in undergraduate students. Three coefficients of uncertain origin, natural logs of resistances and complicated denominators do not make for a happy user.

Happily, there is a solution.

For the Arduino user, there is a simple circuit to build (right) which is a thermistor analog breakout, and can be made on a thumbnail sized scrap of stripboard. You will need to select a resistor for R2 that matches (approximately, at least) the room-temperature resistance of the thermistor.

The 0V and +5V connections go to the power rails of the microcontroller circuit, and the Vout connection to one of the analog inputs (I use A1 for this).

What you will be doing is measuring the resistance of the thermistor by measuring the voltage drop across R2.


The real work is done in code. I built a simple test program around the code below, and ended up with a thermometer that is both fairly accurate and fairly stable.

Electrical noise throws out a series of fluctuations in the recorded value of Vout  - noise that may be masked by averaging a number of readings, and then using a time-weighted average for the output. This has the effect of making the thermometer slow to react to sudden changes in temperature, but overall, I was happy with the results.

A common mistake with using the Arduino analog ports is due to the nature of the way in which the port reports the voltage - as an integer from zero to 1023. Many users use the formula  -


V = AnalogValue *  Vref  / 1024

This is, of course, incorrect, and should read:


V = AnalogValue *  Vref  / 1023

I have added a pair of calibration constants into the program in order to allow for small shifts in the calibration of the thermistor - slope and offset. The linearity of the output is beyond the average user's facility to correct. The three coefficients in the Steinhart-Hart Equation are represented by three magic numbers that are typical for most modern NTC thermistors - mucking about with these (or using atypical components) will certainly lead to frustration and inevitably to tears before bedtime.



The code, which is probably what you are interested in follows ...


// thermistor.h


// ####################################################################

// ####################################################################

// ##

// ##  thermistor.ino (thermistor.h)

// ##  version 1.00.00

// ##  date    25-Jul-2015

// ##  author  Alysson Rowan (AlyssonR)

// ##

// ##  Copyright (C) 2015, Alysson Rowan

// ##   alyssonrowan @ gmail.com

// ##

// ####################################################################

// ####################################################################

// ##

// ##  This program is provided on an as-is basis without warranty.

// ##  No claim is made as toward operability or fitness for purpose

// ##  whatsoever. No liability can be accepted for any loss or damages

// ##  howsoever caused in respect of this software.

// ##

// ##  This software is made freely available under the terms of the

// ##  GNU General Public License V2. In short, you are allowed to do

// ##  anything with this program except sell it and hide the

// ##  source code.

// ##

// ##  In addition, this program is "postcard ware" – if you find it

// ##  useful then please send me an e-mail and tell me about your

// ##  application.

// ##

// ##  Your comments and suggestions are much appreciated.

// ##

// ####################################################################

// ####################################################################
 




#define thermistorPort A1          // analog input pin assignment

#define thermistorRefR 9630        // resistance of buffer resistor

#define thermistorNRes 10000       // Nominal resistance of thermistor

#define thermistorCorr 1           // Calibration scaling factor

#define thermistorOffs 0           // Calibration offset



// Steinhart-Hart coefficients

// – these magic numbers are fairly close for most modern NTC Thermistors

#define thermistorConA 0.001129148     // coefficient a

#define thermistorConB 0.000234125     // coefficient b

#define thermistorConC 0.0000000876741 // coefficient c



float tempAvge; // Temperature averaged over time







// Temperature Conversions:

//

// To convert Kelvin to Celsius

// Celsius = Kelvin – 273.15;

//

// To convert Celsius to Farenheit

// Farenheit = (Celsius * 1.8000) + 32;



// set up analog thermistor

void analogThermistorEnable()

    {

    analogReference(DEFAULT);

    pinMode(thermistorPort, OUTPUT);

    digitalWrite(thermistorPort, LOW);

    pinMode(thermistorPort, INPUT);

    analogRead(thermistorPort);

    }



// Noise reduction using time-weighted averaging

void thermistorGetTemp(int tempWeight)

  // tempWeight controls the weight that the current  reading has

  // in tempAvge.

  //    tempAvge = 0 produces "current temperature only"

  //    tempAvge = 1 => 50% weighting (standard weighting)

  //    tempAvge = 2 => 33.33% weighting etc.

  // NB: at tempAvge = 4, it takes about 30 readings for the temperature to

  //     stabilise on initialisation and after a sudden change in temperature

  {

  float temperature;

  temperature = (analogTemp(analogThermistorRead())-273.15);

  tempAvge = ((tempAvge * tempWeight) + temperature) / (tempWeight+1);

  }



// read analog thermistor

float analogThermistorRead()

    {

    float thermistorRaw;

    thermistorRaw = analogRead(thermistorPort); 
                   // integer value in range 0 – 1023

    // NB: noise results in periodic variation of reading of up to 0.75 degrees

    // noise is reduced markedly by averaging a number of readings

    for( int count = 1; count < 8; count++)

      {

      thermistorRaw = (thermistorRaw + analogRead(thermistorPort))/2;

      }

    return thermistorRaw;

    }



//  calculate temperature from measured voltage

float analogTemp(float voltageRaw)

    {

    float tempLn; // Natural log of temperature

    float tempKelvin

    tempLn = log(ThermistorRefR * ((1023.0/voltageRaw) - 1); 
             // 1023 intervals not 1024 counts on ADC!!!



    tempKelvin = 1 / (thermistorConA + (thermistorConB * tempLn) + (thermistorConC * tempLn * tempLn * tempLn));

    return tempKelvin;

    }


Note: There is one line that breaks over the width of the column (tempKelvin assignment), so please don't be tempted to put in a line-break. The code is presented as a header file, but it can be as easily copy-pasted into your own code rather than #included

No comments:

Post a Comment

Comments and suggestions are warmly welcomed.