NTC thermistor/Using it/08. A steadier reading
Using it · 08 of 12

A steadier reading

Every ADC flickers in its last count or two, and near room temperature one Uno count is about a tenth of a degree. Averaging 32 readings before converting takes a few milliseconds and holds the printed temperature still.

The last count flickers

Leave the block alone on a desk and print the count many times a second. It will not sit on one number. The last count or two wanders up and down: some of that is electrical noise the capacitor did not catch, and some is the ADC itself deciding which side of a step a voltage falls on. Every ADC does it.

Near room temperature on an Uno, one count is about 0.09 °C. So a flicker of three counts makes the printed temperature jump by a quarter of a degree while nothing has changed.

A steadier reading
Readings averaged
Averaged
1
Spread
6.0 counts
In degrees
0.53 °C
One reading at a time. The temperature is not moving, but the last count or two flickers, as it does on any ADC, and the printed value jumps by up to 0.5 °C. That is not the room changing.

The noise in the figure is drawn, not recorded, but the arithmetic is general. Averaging N readings shrinks random flicker by about the square root of N: 8 readings make it nearly three times steadier, 32 nearly six.

Averaging in the sketch

The change is in thermistorOhms() and nowhere else. Instead of one analogRead, it takes SAMPLES of them, adds them up and divides, and only then works out the resistance. An Uno's analogRead takes about a tenth of a millisecond, so 32 cost about 3 ms, once a second.

The sketch now prints two decimal places, because now the second one means something. It does not mean the answer is right to a hundredth of a degree. How far to trust it is about the difference between steady and correct.

The code

The counts-to-degrees sketch, with one change: thermistorOhms() takes SAMPLES readings and averages them before doing any arithmetic.

ntc_averaged.ino
/*
  NTC Thermistor - a steadier reading                    TK12 / /p/tk12

  Wiring. Count from the square pad on the TinkerBlock board, parts
  up, header at the bottom:

    GND    -> GND
    VCC    -> 5V on an Uno; 3V3 on an ESP32, ESP32-S3 or Pico
              (SIGNAL is a fraction of VCC, so it stays in range)
    NC     -> nothing   (unconnected on the board)
    SIGNAL -> A0 on an Uno, GPIO 34 on an ESP32, GPIO 4 on an
              ESP32-S3, GP26 on a Raspberry Pi Pico

  Arduino IDE
    Tools > Board                 your board, e.g. Arduino Uno
    Tools > Port                  the one that appears when you plug in
    Tools > USB CDC On Boot       Enabled   (ESP32-S3 only)
    No library needed.
*/

#include <math.h>

// The analog pin SIGNAL is wired to.
// Uno: A0. ESP32: 34. ESP32-S3: 4. Pico: 26.
const int SENSOR_PIN = A0;

const int SAMPLES = 32;          // readings averaged per answer
const float R_FIXED = 10000.0;   // the 10 kOhm from VCC to SIGNAL
const float R25 = 10000.0;       // the thermistor at 25 C: the "103"
const float B = 3950.0;          // its B value: the "3950"
const float T0 = 298.15;         // 25 C in kelvin
const float VCC_MV = 3300.0;     // ESP32 only: your 3V3 pin, measured

float thermistorOhms() {
  float sum = 0;
#if defined(ARDUINO_ARCH_ESP32)
  for (int i = 0; i < SAMPLES; i++) sum += analogReadMilliVolts(SENSOR_PIN);
  float mv = sum / SAMPLES;
  if (mv <= 0 || mv >= VCC_MV) return NAN;
  return R_FIXED * mv / (VCC_MV - mv);
#else
  for (int i = 0; i < SAMPLES; i++) sum += analogRead(SENSOR_PIN);
  float n = sum / SAMPLES;
  if (n <= 0 || n >= 1023) return NAN;
  return R_FIXED * n / (1023.0 - n);
#endif
}

float celsius(float ohms) {
  float invT = 1.0 / T0 + log(ohms / R25) / B;
  return 1.0 / invT - 273.15;
}

void setup() {
  Serial.begin(115200);
}

void loop() {
  float r = thermistorOhms();

  if (isnan(r)) {
    Serial.println("SIGNAL at 0: check VCC. At the top: check GND.");
  } else {
    Serial.print(celsius(r), 2);
    Serial.println(" C");
  }
  delay(1000);
}

32 readings on an Uno take about 3 ms. On an ESP32 each analogReadMilliVolts call is slower but the total is still a few milliseconds, once a second. VCC_MV only matters on an ESP32.

When it does not work

Should I average the counts or the temperatures?

The readings, before converting. It is one conversion instead of 32, and over the few counts of flicker involved the curve is straight enough that the two give the same answer to well under a tenth of a degree.

How many readings should I average?

16 to 64 is plenty. Averaging N readings cuts random flicker by about the square root of N, so 32 is nearly six times steadier than one, and 128 only twice as steady again. Past a point you are polishing a number the parts cannot promise.

It still jumps by a whole degree now and then.

That is not ADC flicker. Look for a real cause: a draught, a hand near it, or on a classic ESP32 an ADC2 pin with Wi-Fi on. Averaging hides small random noise; it will not fix a wrong pin or a warm neighbour.

Can I use a running average instead?

Yes: keep the last N readings and print their mean each time a new one arrives. It gives a new value every loop instead of every N. The simple block average here is easier to read and is what the figure shows.

Where this goes next

A TK01 that lights when it gets warm, and does not flicker at the line.

A temperature alarm

Edit this page — content/books/ntc-thermistor/a-steadier-reading.mdx

Community

Questions about this product

See what other owners have asked, and read their solutions.

Ask a question ↗

NTC Thermistor

Loading discussions…

Discuss this article

Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.

Browse Modules and blocks on the forum