Contents[Hide]

Measure Arduino chip temperature
Measure Arduino chip temperature

Intro

Here we describe a class for reading out the temperature sensor inside the ATmega328. This class is inspired on the library “InternalTemp” from SpikedCola and marcello.romani. The class ChipTemp is kept as small and simple as possible.

Details

  • Resolution 1 degree or 0.1 degree
  • Accuracy >=1°C
  • Requires between 166 and 204 bytes program memory
  • Both the offset and the gain can be calibrated and set in decimals

Restrictions

The temperature sensor inside the ATmega328 has many restrictions. The accuracy without calibration is very poor. An accuracy of >=1% can be achieved but requires a complicated two point calibration. Even than, the accuracy will be influenced by the chip self heating and ADC noise. The internal temperature sensor is useful when just an indication of the temperature is required. But because ChipTemp only uses about 200 bytes, it never hurts to add it to the software to monitor the chip temperature.

Mostly it is preferable to use special IC’s such as the analog LM335 or the digital LM75 temperature sensors which cost less than $2.

Example graph

Graph °Celsius and °deci-Celsius
Graph °Celsius and °deci-Celsius

Temperature sensor

The sensor is a diode that produces a temperature dependent voltage. This voltage is measured with the 10 bit ADC. The voltage has a linear relationship to temperature of about 1.06 mV/°C, whereby 1 LSB = 1mV.

Test software

Connect an ATmega328 Arduino board and run ChipTempTest.pde to print the internal temperature in degrees Celsius, Fahrenheit, deci-Celsius and deci-Fahrenheit to the screen. Deci means that the temperature resolution is 0.1 degree.

#include <avr/pgmspace.h>

#include "ChipTemp.h"

ChipTemp chipTemp;

void setup()
{ Serial.begin(9600);
//Serial.println(offsetFactor); Serial.println(divideFactor);
  Serial.println("Celsius deciCelsius Fahrenheit deciFahrenheit");
}

void loop()
{ delay(1000);
  Serial.print(chipTemp.celsius()); Serial.print(" ");
  Serial.print(chipTemp.deciCelsius()); Serial.print(" ");
  Serial.print(chipTemp.fahrenheit()); Serial.print(" ");
  Serial.println(chipTemp.deciFahrenheit());
}

ChipTemp class

Place the files ChipTemp.cpp and ChipTemp.h in a new library subfolder \libraries\ChipTemp. For troubleshooting see HERE.

ChipTemp.cpp

#if ARDUINO >= 100 

#include "Arduino.h"
#else
#include "WProgram.h"
#endif

#include "ChipTemp.h"

ChipTemp::ChipTemp()
{
}

inline void ChipTemp::initialize()
{ ADMUX = 0xC8; // select reference, select temp sensor
  delay(10); // wait for the analog reference to stabilize
  readAdc(); // discard first sample (never hurts to be safe)
}

inline int ChipTemp::readAdc()
{ ADCSRA |= _BV(ADSC); // start the conversion
  while (bit_is_set(ADCSRA, ADSC)); // ADSC is cleared when the conversion finishes
  return (ADCL | (ADCH << 8)); // combine bytes
}

int ChipTemp::deciCelsius()
{ long averageTemp=0;
  initialize(); // must be done everytime
  for (int i=0; i<samples; i++) averageTemp += readAdc();
  averageTemp -= offsetFactor;
  return averageTemp / divideFactor; // return deci degree Celsius
}

int ChipTemp::celsius()
{ return deciCelsius()/10;
}

int ChipTemp::deciFahrenheit()
{ return (9 * deciCelsius()+1600) / 5;
}

int ChipTemp::fahrenheit()
{ return (9 * deciCelsius()+1600) / 50; // do not use deciFahrenheit()/10;
}

ChipTemp.h

#ifndef ChipTemp_H
#define ChipTemp_H

// ATmega328 temperature sensor interface
// Rev 1.0 Albert van Dalen www.avdweb.nl
// Based on "InternalTemp"
// Requires 166 ... 204 bytes program memory
// Resolution 0.1 degree

// Calibration values, set in decimals
static const float offset = 335.2; // change this by calibration result
static const float gain = 1.06154;

static const int samples = 1000; // must be >= 1000, else the gain setting has no effect

// Compile time calculations
static const long offsetFactor = offset * samples;
static const int divideFactor = gain * samples/10; // deci = 1/10

class ChipTemp
{
public:
  ChipTemp();
  int deciCelsius();
  int celsius();
  int deciFahrenheit();
  int fahrenheit();

private:
  inline void initialize();
  inline int readAdc();
};

#endif

Notes to the software

  • The initialisation has to be done each time, because ADMUX can be overwritten in meantime.
  • To get a usefull accuracy we have to take the average of 1000 samples. This reduces the ADC noise. Also the resolution is increased to 0.1°, but is not the reason because the accuracy can never be 0.1°.
  • Some calculations are done during compile time and thus uses no program memory.
  • The accuracy at optimal circumstances can be 1%, which is higher than the resolution of 0.1 degree.
  • Small modifications make ChipTemp also useful as interface for external analog temperature sensors.

Rounding

In mathematically respect, rounding is a tricky business. The method round half up is widely used in many disciplines. This is the lidfunction Celsius () with using rounding half up:

int ChipTemp::celsius()
{ (return deciCelsius()+5)/10;
}

See here the result:

Rounding half up error
Rounding half up error

We see that the rounding is unsymmetrical. Therefore we don’t use the round half up method.

Accuracy

The diode voltage is highly linear, but due to process variations the temperature sensor output voltage varies from one chip to another. Also, the internal voltage reference used as the ADC reference voltage varies with temperature. Calibration is therefore mandatory to achieve useful results. Also the ADC Noise Reduction Mode can be used to increase accuracy.

Calibration

For a detailed calibration description, see the Atmel application note AVR122: Calibration of the AVR's internal temperature reference. Two calibration methods are useful:

  • One-point calibration. Offset removal using room temperature as reference gives +/-5°C accuracy from -30°C to 100°C and +/-3°C accuracy between 0°C and 85°C.
  • Two-point calibration. Here, +/-1°C accuracy from 5°C to 95°C can be achieved.

One-point calibration

Set the offset in ChipTemp.h to:
offset’ = deciCelsius * gain / 10 + offset – gain * T

Definitions
offset’ = the new offset value for ChipTemp.h

gain = 1.06154
offset = is the offset in ChipTemp.h (335.2)
T = room temperature

Formula distraction
deciCelsius = 10 * (ADC - offset) / gain
ADC = gain’ * T + offset’
deciCelsius = (gain’ * T + offset’ – offset) * 10 / gain
if offset’ = offset and gain’ = gain
deciCelsius = 10T
Assume gain’ = gain
offset’ = deciCelsius * gain / 10 + offset – gain * T
Set offset in ChipTemp.h to offset’

To be done

The ADC Noise Reduction Mode can be used to increase accuracy but this has not yet been implemented.

Do you have any comments? Please let me know.
Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.