Temperature measurement using a NTC thermistor and an AVR microcontroller

Author: Pogrebnyak Dmitry

Russia, Samara, 2013.

Внимание. Сей опус является неубедительной моей попыткой перевести собственное творчество на ломанный английский язык. Поэтому, если вам русский язык кажется чем-то родным и близким, то прочтите, пожалуйста, оригинал этого манускрипта на родном языке автора. Впрочем, если вы при этом знакомы с языком, претерпевшем много боли ниже по тексту, и обладаете крепкими нервами и здоровым чувством юмора, то, как говорится, ю а вэлкам.

Note. This is a weak translation of an article originally written in Russian. So, if you famous with Russian language, it is preferred to read it in the original language.

One of methods to measure temperature is to use a thermal resistor (or a thermistor for short). It has significant value of the thermal coefficient, i.e. changing of resistance with changing of temperature (at level of 2-10% per Kelvin).

There are two types of thermistors – Positive Temperature Coefficient (PTC) which increases their resistance when temperature is growing up; and Negative Temperature Coefficient (NTC) which decreases resistance when temperature is growing. This article describes usage of a NTC only in link with an AVR MCU for purposes of temperature measurement.

NTC thermistor characteristics

Thermistors have a lot of parameters that characterize them, such as: maximum allowed current, resistance tolerance, resistance at particular temperature (usually 25°С). One of them is a coefficient of temperature sensitivity or the B parameter. This parameter is calculated, when measuring two resistance values for two different temperatures. In most cases it is 25°С and 100°С. Usually, temperatures that was used to calculate B is printed after the B letter, for example: B25/100. The B parameter measured in Kelvins and can be calculated, using the formula:

B = (ln(R1) – ln(R2)) / (1 / T1 - 1 / T2) [1],

R1 and R2 - the values of resistance for temperatures T1 and T2 respectively, expressed in Kelvins.

There are the reverse formulas:

R1 = R2 * e(B * (1 / T1 - 1 / T2)) [2].


T1 = 1 / ((ln(R1) – ln(R2)) / B + 1 / T2) [3].

Calculating the temperature

Parameters of thermistors have high level of non-linearity. The B parameter has different values, when measurement based on different temperatures. Two different models of thermistors with same B25/100 value could change their resistance in different ways over a temperature range.

Thus formula [3] can be used only for a rough estimation of temperature. In addition, this formula requires heavy calculation, which requires much of CPU time. Since the CPU of the MC usually has no floating point math support, these calculations are not applicable.

A better way is to store a table in the memory, whose cells will contain precalculated ADC values, corresponding to temperatures.

For memory economy it is possible to store ADC values only for several temperature points (given with some step), then to find it using binary search and to calculate resulting temperature, using linear interpolation.

To measure ambient temperature in range from -30°С to +70°С with accuracy up to 0.3°С, it is require to store 20 values (with step of 5°С). If every value will fit into 16 bits, it will require only 40 bytes of the flash-memory – that is much less, than memory required to store floating math routines.

Decreasing table step down to 2°С, it is possible to take accuracy at level of 0.1°С on wide measurement range.

Vendors of thermistors usually give tables of R/T characteristics, which show how resistance changed over temperature. For example, Siemens-EPCOS gives those tables with step of 5°С. To calculate intermediate values, it is possible to interpolate them with high accuracy, using formulas [1] and [2].

Schemes of connection

Connecting of thermistor

Scheme A
Scheme A
Scheme B
Scheme B
Scheme C
Scheme C
Scheme D
Scheme D

The simplest way to connect thermistor to a MCU (or an ADC IC) is Scheme A.

To minimize measurement error, the RA value should be close to thermistor resistance value in the measurement range – that makes ADC values changing closer to linear, and consequently, allows to minimize error while linear interpolation.

When selecting values of thermistor resistance and RA, it is should be taking into account, that current that flow thru the thermistor, will provoke its heating, and, as consequence, distortion of the measured value. It is desirable, than dissipated power on thermistor never excess 1mW. Thus, if input voltage U0 = 5V, then values of RA and thermistor resistance at measured range should be at least 10 kiloOhms each.

Scheme B is used to minimize power dissipated on thermistor.

Schemes C and D – is reverse for A and B. They are to use, if required to measure low temperatures, when ADC referent voltage (Uref) is lower than U0.

Connecting to ADC of MCU ATmega

Connection of ATmega ADC
Connection of ATmega ADC

To minimize a noise, inducted by digital lines, MCUs ATmega use separate power supply pins for the ADC module. The datasheet recommends to connect these pins through a filter: the inductance L = 10µH, and the capacitor C2 = 0.1µF.

The ADC of the MCU can use as referent voltage either external voltage, connected to AREF pin, or internal 2.56V or 1.1V (depend on MCU model), or use AVCC supply voltage as referent value. When internal or AVCC voltage is used, an external capacitor must be connected between the analog ground and the AREF pin. The datasheet gives no exact recommendation for choosing this capacitance. I recommend to use a ceramic capacitor 0.1µF or more.

To minimize a noise, I recommend to use same filtered voltage, which is used for AVCC line, to supply thermistor scheme, and to use same AVCC as referent voltage.

Additionally, to suprres noises in wires, the capacitor C3 (1-100nF) could be installed.

Should be taken into account, that, besides of the ADC, the AVCC input used to supply digital levels on some pins (usually this is the same pins, where ADC inputs is located). Usage of those pins for digital input/output and connecting a load to it can produce an additional noise on the ADC.

To minimize measured ADC noises, and increase accuracy, I recommend to perform several consequent measurements, and to sum their results. ATmegas have 10-bit ADC. Sum of 64 measurements still be in range of 16-bit integer, thus not require additional memory to store table of values.

When making more measurements, it is possible to keep it in range of 16 bits, by dividing or shifting result.

Online table calculation

Here I brought for your comfort the online script for ADC values table calculation.

Calculation is performed by:

- Two values of temperature and corresponded to them values of thermistor resistance;

- One pair of temperature/resistance and B coefficient;

- Entered list of R/R1 values given with some step;

- One of preloaded R/T characteristics.

Now there are preloaded R/T characteristics for Siemens/EPCOS thermistors. Please, select one, which corresponds to yours. Preloaded characteristics are given with step of 5°С. When selecting the grid step less than 5°С, intermediate values are calculated using the formulas [1] and [2].

When the table is renewed, the source code under it is updating automatically.

Note! Due to high non-linearity of thermistor parameters, calculations based on two values are rough and then calculated value of temperature can be significant different from actual one for high and low temperature measurements.

To know what R/T characteristics correspond to your thermistor, please refer to vendor datasheets.

Here the table of commonly used thermistors Siemens/EPCOS is given. Press to an R/T characteristic code to load it into the calculation form below.

CodeResistance when 25°С, kOhmR/T characteristicB25/100, К
B57891S, though-hole 4.5mm (1.8’’) (datasheet, pdf)

B57891M, through-hole 3.5mm (1.4’’) (datasheet, pdf)

B57164K, through-hole 5.5mm (2.2’’) (datasheet, pdf)

B57540G, through-hole, glass "water drop" 0.8mm (0.3’’) (datasheet, pdf)
B57540G0502+000, +002584023497
B57540G1103+000, +0021083073492
B57540G1103+005, +0071070033625
B57540G0203+000, +0022084154006
B57540G1303+005, +0073070023988
B57540G0503+000, +0025084034006
B57540G1104+000, +00210083044092
B57540G0234+000, +00223084054264
B57540G0145+000, +002140084064581

B57551G, through-hole, glass "water drop" 1.8mm (0.7’’) (datasheet, pdf)
B57551G0202+000, +002284013436
B57551G1103+000, +0021083073492
B57551G1103+005, +0071070033625
B57551G1303+005, +0073070023988
B57551G1104+000, +00210083044092

B57621С5, SMD 1206 3.2х1.6mm (datasheet, pdf)

B57621С0, SMD 1206 3.2х1.6мм (datasheet, pdf)

B57703M, probe 10mm, with mounting plate 8.5x3.7mm with hole(datasheet, pdf)

Form for online calculating of ADC values

Table data
*Due to non-linearity of thermistor parameters, calculations based on two points are rough, and, when measuring high or low temperatures, calculated value will significantly differ from real one. For precise measuring in wide temperature range, please, select one of preloaded R/T characteristics, which corresponds to your NTC-thermistor, in drop-down list above.
T1 °С
R1, resistance when T1 kiloOhm
T2 °С
R2, resistance when T2 kiloOhm
Data for table:R/R1
starting from T2, with selected grid step.
Use comma to separate values.
BT1/T2 K
Scheme of thermistor connection
Resistor RA value kiloOhm
Resistor RB value kiloOhm
ADC resolution
Multiplier for ADC result
U0, source voltage В
Uref, ADC referent voltage В
Calculate from °С to  °С
Grid step

Explanation of table fields:

Bold values of R/R1 and R columns are taken from preloaded array, or entered values. Non-bold values are obtained by calculations, using formulas.

ADC – the rounded value that will be taken from ADC output, with respect to multiplier. Values that fallen out of ADC range, are not displayed here.

I,µA - current in whole circuit.

P,mW - power, dissipating on thermistor.

E – heuristic estimation of a measured value error, when using the linear interpolation, taking into account a limited ADC accuracy. It enables to find parameters and scheme to minimize an error in a range of measured temperatures. This estimation does not taking into account an ADC noise, and a heating of thermistor due to power dissipation on it.

The error value could be lowered when selecting a lower grid step, selecting the ADC of higher resolution, averaging of many measurements, and by selection of resistors values.

Code, corresponding to table

#include <avr/io.h>
#include <avr/pgmspace.h>

// Value when sum of ADC values is more than first value in table
// Value when sum of ADC values is less than last value in table 
// Value corresponds to first entry in table
// Table step

// Type of each table item. If sum fits into 16 bits - uint16_t, else - uint32_t
typedef uint16_t temperature_table_entry_type;
// Type of table index. If table has more than 255 items, then uint16_t, else - uint8_t
typedef uint8_t temperature_table_index_type;
// Access method to table entry. Should correspond to temperature_table_entry_type
#define TEMPERATURE_TABLE_READ(i) pgm_read_word(&termo_table[i])

/* Table of ADC sum value, corresponding to temperature. Starting from higher value to lower.
   Next parameters had been used to build table:
const temperature_table_entry_type termo_table[] PROGMEM = {
  0 // Press "Build table" button on form above to fill this array

// This function is calculating temperature in tenth of degree of Celsius
// depending on ADC sum value as input parameter.
int16_t calc_temperature(temperature_table_entry_type adcsum) {
  temperature_table_index_type l = 0;
  temperature_table_index_type r = (sizeof(termo_table) / sizeof(termo_table[0])) - 1;
  temperature_table_entry_type thigh = TEMPERATURE_TABLE_READ(r);
  // Checking for bound values
  if (adcsum <= thigh) {
      if (adcsum < thigh) 
        return TEMPERATURE_UNDER;
  temperature_table_entry_type tlow = TEMPERATURE_TABLE_READ(0);
  if (adcsum >= tlow) {
      if (adcsum > tlow)
        return TEMPERATURE_OVER;

  // Table lookup using binary search 
  while ((r - l) > 1) {
    temperature_table_index_type m = (l + r) >> 1;
    temperature_table_entry_type mid = TEMPERATURE_TABLE_READ(m);
    if (adcsum > mid) {
      r = m;
    } else {
      l = m;
  temperature_table_entry_type vl = TEMPERATURE_TABLE_READ(l);
  if (adcsum >= vl) {
  temperature_table_entry_type vr = TEMPERATURE_TABLE_READ(r);
  temperature_table_entry_type vd = vl - vr;
  if (vd) {
    // Linear interpolation
    res -= ((TEMPERATURE_TABLE_STEP * (int32_t)(adcsum - vr) + (vd >> 1)) / vd);
  return res;

Example of usage

In example below, temperature is displayed on 7-segment indicator.

It utilizes the ledind_num() function that is described in my another article (in Russian).

Measured value is displayed on СС56-12GWA
Measured value is displayed on СС56-12GWA

  ADMUX = 0b01000111; // ref voltage - Vcc, input ADC7, right-edged result
  ADCSRA = 0b10000111; // 1/128 prescaler, ADC enabled

  while(1)  {
    temperature_table_entry_type summ = 0;
    for (uint8_t i = 0; i < 64; i++) {
      ADCSRA |= _BV(ADSC);
      loop_until_bit_is_clear(ADCSRA, ADSC);
      summ += ADC;
    int16_t t = calc_temperature(summ);
    ledind_num(t, 1, 0b01010011); // Output value with t-styled prefix

5 ms; mod: Sat, 06 May 2017 20:41:51 GMT; gen: Tue, 24 Oct 2017 05:32:51 GMT