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 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.