# Arduino thermistor thermometer – a tutorial

This project was designed ad-hoc as a learning exercise for a friend who ‘needed’ to acquaint himself with Arduino.

What better than a practical exercise that demonstrates some key advantages and disadvantages of the Arduino environment.

The project was a simple digital thermometer to display normal environment air temperature, say from -20° to 50° using common and inexpensive Arduino hardware with firmware developed on the free Arduino IDE, all using hardware that was on hand.

Key hardware elements:

• Arduino PRO 328 5V 16MHz board;
• USB / serial TTL interface, FTDI basic used in this article;
• LCD display, HD44780 compatible display (2×16 display used in this article);
• Thermistor and series R, NTC201 and 1kΩ used in this article.

***This is not necessarily an optimum solution, the parts used were parts on hand.

## Sensing temperature

There are many ways to sense temperature, each having strengths and weaknesses for different applications.

A negative temperature coefficient (NTC) thermistor was chosen for the project, partly because it will necessitate a little research on the constructor’s part, design of a suitable voltage sensing network and implementation of some calculations to convert input voltage to temperature.

The characteristic of an NTC thermistor is moderately complex, but it can be approximated by a simpler expression and the coefficients of that simpler expression are often published in the component datasheets. The simpler relationship is T=1/(1/To+1/B*ln(R/Ro)).

The part chosen was an NTC201 thermistor, specification has nominal R at 25° of 200Ω and B equal to 3100K.

Fig 1 shows the relationship between R and temperature.

The calculator Two point thermistor calibration provides a handy method of deriving the parameters of a thermistor from measurements.

## Conversion to voltage

The Arduino processor (ATMega328P in this case) cannot sense resistance, but it can sense a voltage using its Analogue to Digital Converter (ADC). So a circuit is needed that produces a voltage that varies with thermistor resistance (or temperature). Note that the equivalent source impedance needs to be less than 10kΩ, keep that in mind when choosing a thermistor for a working temperature range.

A simple voltage divider circuit using a fixed resistance from some reference voltage and the thermistor in series to ground will give rise to a thermistor voltage that varies with temperature.

An Excel spreadsheet was constructed to explore the response of such a network in terms of the required temperature range and the ADC capacity. A 1kΩ resistor and the ATMega328P’s 1.1V reference were used.

Fig 2 shows the relationship between Vin from this network and temperature. So what is needed is some code to read the voltage and calculate the temperature.

## Self heating

Before embarking on that, a small digression. One of the disadvantages of thermistors is that they are heated by the current that they conduct, so they will be a little hotter than their environment.

Fig 3 shows the temperature rise due to self heating in this circuit and in still air, again calculated from the datasheet figures and the circuit model. In this case, it is quite small (not by accident though, but partially by choice of the series resistor and reference voltage) but it can be corrected out of the final calculated temperature.

## Resolution

Any process that converts a continuous variable such as the input voltage into a digital value with discrete steps introduces some error, and a notional resolution in reading.

Fig 4 shows the calculated resolution for a single reading, and it varies with temperature being worst at higher temperatures.

To help to reduce the granularity of measurements and to improve stability overall, the ADC output can be averaged over a large number of observations. The effect of noise referred to the ADC input and averaging is to improve resolution and stability of readings, and averaging 50 readings gives a quite stable display with one decimal digit at 30°.

## The code

Here is the code.

```//Thermometer example
//Owen Duffy 2013/02/24

#define VREF 1.087
#define VCC VREF
#define R0 216
#define T0 (25.0+273.15)
#define Beta 3200
#define Rs 1000
#define AVG 50
#define DISSFACT 0.006

/*
The circuit:
* LCD RS pin to digital pin 12
* LCD Enable pin to digital pin 11
* LCD D4 pin to digital pin 5
* LCD D5 pin to digital pin 4
* LCD D6 pin to digital pin 3
* LCD D7 pin to digital pin 2
* LCD R/W pin to ground
* 20k pot: ends to +5V and ground wiper to LCD VO pin (pin 3)
* 2k2 from +5V to A0, NTC201 thermistor to GND
*/

// include the library code:
#include <LiquidCrystal.h>

int sensorPin=A0; // select the input pin for the thermistor
unsigned AdcAccumulator; // variable to accumulate the value coming from the sensor
float vin;
float rt;
float temperature;
float self;
int i;

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

void setup(){
analogReference(INTERNAL);
//set up the LCD number of columns and rows:
lcd.begin(16, 2);

// start serial port at 9600 bps and wait for port to open:
Serial.begin(9600);
while (!Serial) {;} // wait for serial port to connect. Needed for Leonardo only
}

void loop() {
for(i=AVG;i--;){
// read the value from the thermistor:
delay(100);
}
// calculate average vin
//calculate rt
rt=vin/((VCC-vin)/Rs);
//calculate self heating
self=vin*vin/rt/DISSFACT;
//calculate temperature
temperature=1/(log(rt/R0)/Beta+1/T0)-273.15-self;
//  temperature=rt;

// Print a message to the LCD.
lcd.clear();
lcd.print("Thermistor #1:");
lcd.setCursor(0, 1);
lcd.print("Temp: ");
lcd.print(temperature,1);
lcd.print("\xdf" "C");

if(Serial.available()<=0){
Serial.println(temperature); //send
}

}```

Above is the example code. It is liberal with variables, and sets out the calculation in steps to be self explanatory.

# LCD waveforms

Fig 5 shows a logic analyser trace of the LCD E and RS control signals, and D5 and D4 pins being the LSB and next bit respectively. The analyser is a Logic Shrimp, and has only four channels so only two data bits are shown.

Fig 6 in an expanded view of Fig 5, and shows that the data bits are written one by one to output pins until all are ready then the E signal falling edge clocks the data into the LCD module.

Fig 7 shows demonstration hardware. The thermistor is the black bead just above the LCD module. There is a contrast trimpot soldered onto the LCD edge connector, it is standing up so not so easy to see. The whole thing is powered from the USB interface.

## I2C LCD

The following code uses an I2C converter (I2C – LCD interface – Type 2) to attach the HD44780 compatible LCD.

```//Thermometer example
//Owen Duffy 2013/02/24
//I2C LCD variation

#define VREF 1.087
#define VCC VREF
#define R0 216
#define T0 (25.0+273.15)
#define Beta 3200
#define Rb 1000
#define AVG 50
#define DISSFACT 0.006

/*
The circuit:
* 1k0 from AREF to A0, NTC201 thermistor to GND
*/

// include the library code:
//#include <LiquidCrystal.h>
//#define _BV(x) (1<<(x))
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

int sensorPin=A0; // select the input pin for the thermistor
unsigned AdcAccumulator; // variable to accumulate the value coming from the sensor
float vin;
float rt;
float temperature;
float self;
int i;

// initialize the library with the numbers of the interface pins
//LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
LiquidCrystal_I2C lcd(0x27,2,1,0,4,5,6,7,3,POSITIVE);  // Set the LCD I2C address

void setup(){
analogReference(INTERNAL);
//set up the LCD number of columns and rows:
lcd.begin(16, 2);

// start serial port at 9600 bps and wait for port to open:
Serial.begin(9600);
while (!Serial) {;} // wait for serial port to connect. Needed for Leonardo only
}

void loop() {
for(i=AVG;i--;){
// read the value from the thermistor:
delay(100);
}
// calculate average vin
//calculate rt
rt=vin/((VCC-vin)/Rb);
//calculate self heating
self=vin*vin/rt/DISSFACT;
//calculate temperature
temperature=1/(log(rt/R0)/Beta+1/T0)-273.15-self;
//  temperature=rt;

// Print a message to the LCD.
lcd.clear();
lcd.print("Thermistor #1:");
lcd.setCursor(0, 1);
lcd.print("Temp: ");
lcd.print(temperature,1);
lcd.print("\xb0" "C"); //ROM A02
//  lcd.print("\xdf" "C"); //ROM A01
//  lcd.print("C");

if(Serial.available()<=0){
Serial.println(temperature); //send
}

}```

## Calibration

The system can be calibrated by setting the value of R0 in the program to make the displayed value reconcile with measured value.

# Optimisation

Areas for optimisation are:

• better exploit the range of the ADC;
• improve single measurement resolution;
• reduce self heating.

Staying with an NTC thermistor, these goals could be achieved by using a higher resistance thermistor, eg one with R=5k at 25°, and a series resistor to suit (4.7k). So if you are buying parts to try this exercise, get an NTC5k thermistor (~\$0.35 each) and find an optimal series resistor (4.7k is a good start).

Working with the Arduino reference documentation will reveal frustrations in incomplete documentation of functions (eg parameter type are often not described), features not described.

# Further applications

This article describes a system to read an analogue input voltage, perform some calculations on the reading, and present the result on a common LCD display.

How would you change the design to use an LM35 or an LM335 temperature sensor? Could you use an ordinary silicon power diode as a temperature sensor?

The techniques could be applied to a host of projects, eg the voltage developed in the forward and reverse detectors in a VSWR meter could be sampled and Forward Power, Reflected Power, (net) Power, VSWR etc calculated and displayed.