If you need to measure temperature with high resolution on a budget, pairing an NTC thermistor and Arduino is the most reliable bench-tested method. The direct answer for a standard room-to-boiling range build: use a Vishay NTCLE100E3103 (10kΩ, 1% tolerance, Beta 3950) wired as the lower leg of a voltage divider with a 10kΩ 1% metal film pull-up resistor, read the midpoint on Analog Pin A0, and convert the ADC raw value using the Steinhart-Hart equation in C++.

This guide targets the Arduino Uno R3 (and is fully compatible with the R4 Minima and Nano v3). We will cover the exact voltage divider math, provide a copy-pasteable code block with division-by-zero protection, and debug the most common serial monitor errors.

Difficulty Rating: Beginner-Intermediate (2/5)
Estimated Time: 20 minutes wiring, 10 minutes coding/calibration

NTC Thermistor Specs and Voltage Divider Data

NTC (Negative Temperature Coefficient) thermistors drop in resistance as they heat up. Because the Arduino's ADC (Analog-to-Digital Converter) measures voltage, not resistance, we must use a voltage divider to translate the changing resistance into a changing voltage.

By placing the fixed 10kΩ resistor between the 5V VCC and A0, and the thermistor between A0 and GND, the voltage at A0 drops as temperature rises. Below is the expected data-dense profile for a standard 10kΩ Beta 3950 thermistor on a 5V Uno R3 reference. According to the Vishay NTCLE100 datasheet, these resistance values assume a 5V reference and a 10-bit ADC (0-1023).

Temperature (°C) Thermistor Resistance (Ω) Voltage at A0 (V) Expected ADC Raw Value (10-bit)
0°C (Ice Bath) 27,279 Ω 3.658 V 748
25°C (Room Temp) 10,000 Ω 2.500 V 511
50°C (Hot Tap Water) 3,893 Ω 1.401 V 286
85°C (Near Boiling) 1,205 Ω 0.537 V 110
100°C (Boiling) 805 Ω 0.372 V 76

Exact Parts List and Pin Mapping

Do not use the cheap, unmarked epoxy-coated thermistors from bulk assortment kits if you need accuracy better than ±2°C. The 1% tolerance glass-bead or silicone-coated variants are worth the extra $1.50 per unit.

  • Microcontroller: Arduino Uno R3 (ATmega328P) or Uno R4 Minima
  • Sensor: Vishay NTCLE100E3103 (10kΩ, 1%, 3950 Beta, silicone-coated leads)
  • Pull-up Resistor: 10kΩ 1/4W 1% Metal Film Resistor (brown, black, black, red, brown bands)
  • Hardware: Half-size breadboard, 22 AWG solid core jumper wires
  • Optional: 100nF (0.1µF) ceramic capacitor between A0 and GND for hardware noise filtering
Component Pin / Leg 1 Pin / Leg 2 Notes
10kΩ Fixed Resistor Arduino 5V Pin Arduino A0 Pin Acts as the upper pull-up leg
10kΩ NTC Thermistor Arduino A0 Pin Arduino GND Pin Polarity does not matter for NTCs
100nF Capacitor (Optional) Arduino A0 Pin Arduino GND Pin Smooths out high-frequency ADC jitter

Step-by-Step Wiring Procedure

  1. De-energize the board: Unplug the Arduino USB cable before inserting components into the breadboard to prevent accidental shorts on the 5V rail.
  2. Insert the pull-up resistor: Place one leg of the 10kΩ metal film resistor into the 5V rail and the other leg into row 10 of the breadboard.
  3. Insert the thermistor: Place one leg of the NTC thermistor into row 10 (sharing the node with the pull-up resistor) and the other leg into the GND rail.
  4. Wire the ADC input: Run a jumper wire from row 10 (the midpoint node) directly to the Arduino A0 pin.
  5. Add the filter capacitor (highly recommended): Insert the 100nF capacitor across row 10 and the GND rail. As noted in All About Circuits' thermistor guide, the high impedance of a 10kΩ divider makes the ADC susceptible to electromagnetic interference; this capacitor acts as a low-pass filter.
  6. Verify with a multimeter: Before plugging in USB, set your DMM to continuity mode. Probe the 5V rail and GND rail to ensure there is no dead short (the resistance should read roughly 20kΩ at room temperature, not 0Ω).

Complete Arduino Code with Steinhart-Hart Error Handling

The simple Beta parameter equation is fine for a 10°C window, but across a 0-100°C range, it introduces up to 1.5°C of error. The Steinhart-Hart equation uses three coefficients (A, B, C) to model the thermistor's curve accurately across a wide range. The code below includes explicit error handling to prevent the Arduino from crashing or outputting garbage data if the ADC saturates.

#include <math.h>

// Pin Definitions
const int THERMISTOR_PIN = A0;

// Circuit Constants
const float SERIES_RESISTOR = 10000.0; // 10k Ohm pull-up resistor

// Steinhart-Hart coefficients for standard 10k NTC (Beta 3950)
// Derived from manufacturer datasheet R-T tables
const float A = 1.009249522e-3;
const float B = 2.378405444e-4;
const float C = 2.019202697e-7;

void setup() {
  Serial.begin(115200);
  pinMode(THERMISTOR_PIN, INPUT);
  
  // Allow ADC reference to stabilize
  analogRead(THERMISTOR_PIN); 
  delay(100);
  
  Serial.println("Thermistor Logger Initialized.");
}

void loop() {
  // Read the ADC (10-bit resolution on Uno R3: 0 to 1023)
  int rawADC = analogRead(THERMISTOR_PIN);
  
  // Error Handling: Check for ADC saturation (open or short circuit)
  if (rawADC <= 0 || rawADC >= 1023) {
    Serial.println("Error: ADC saturated. Check voltage divider wiring.");
    delay(2000);
    return;
  }
  
  // Calculate thermistor resistance using voltage divider math
  // R_th = R_series * ((1023 / ADC) - 1)
  float resistance = SERIES_RESISTOR * ((1023.0 / (float)rawADC) - 1.0);
  
  // Error Handling: Prevent math domain error in log()
  if (resistance <= 0.0) {
    Serial.println("Error: Negative resistance calculated. Hardware fault.");
    delay(2000);
    return;
  }
  
  // Steinhart-Hart Equation: 1/T = A + B*ln(R) + C*(ln(R))^3
  float logR = log(resistance);
  float tempK = 1.0 / (A + (B * logR) + (C * logR * logR * logR));
  float tempC = tempK - 273.15;
  
  // Output to Serial Monitor
  Serial.print("Raw ADC: ");
  Serial.print(rawADC);
  Serial.print(" | Resistance: ");
  Serial.print(resistance, 1);
  Serial.print(" Ohms | Temperature: ");
  Serial.print(tempC, 2);
  Serial.println(" C");
  
  delay(1000);
}

Debugging: Fixing 'nan' and '-273.15°C' Serial Errors

When working with analog sensors and logarithmic math, the Serial Monitor will occasionally throw specific error strings. If you see Temperature: nan °C or Temperature: -273.15 °C, do not rewrite the code. The math is failing because the input data is physically impossible. Here are the first three things to check, ranked by probability:

  1. Check for ADC Saturation (Wiring Fault): The nan (Not a Number) error happens when the log() function receives a zero or negative value. This occurs if rawADC reads exactly 0 (thermistor shorted to GND) or 1023 (thermistor disconnected/open circuit). Use your multimeter to measure the voltage directly at pin A0. It should be between 0.5V and 4.5V at room temperature. If it reads 0.00V or 5.00V, re-seat your breadboard jumper wires.
  2. Measure USB VCC Sag: The Arduino Uno R3 assumes a perfect 5.00V reference. If your PC's USB port is sagging to 4.6V under load, your ADC readings will skew, and the calculated resistance will be wrong. Measure the 5V pin to GND with a DMM. If it is below 4.8V, switch to a powered USB hub or use the Arduino's internal 1.1V reference (requires code modification to analogReference(INTERNAL) and a different pull-up resistor value).
  3. Verify the Pull-Up Resistor Value: If the temperature reads consistently off by 5-10°C (e.g., reading 15°C in a 22°C room), you likely grabbed a 10kΩ 5% carbon film resistor instead of a 1% metal film resistor, or you misread the color bands. Measure the fixed resistor out-of-circuit with your multimeter and update the SERIES_RESISTOR constant in the code to the exact measured value (e.g., 9850.0).

Extending and Simplifying the Build

Depending on your project requirements, you may want to strip this build down to its bare essentials or scale it up for data logging.

How to Simplify (If You Don't Need Raw Math)

If you only need to know if a liquid has exceeded a specific threshold (e.g., a boiler over-temp alarm), ditch the analog pin and the Steinhart-Hart math entirely. Buy a LM393 Thermistor Module (usually $2 for a pack of five). These boards feature a built-in potentiometer and comparator. You wire the digital out (DO) pin to any Arduino digital pin. It outputs a clean HIGH/LOW signal when the temperature crosses the physical dial's threshold, requiring zero math in your sketch.

How to Extend (For Precision Data Logging)

If you are building a sous-vide controller or a 3D printer hotend monitor, the 10-bit ADC on the Uno R3 might not provide enough granularity. Extend the build by:

  • Software Oversampling: Read the ADC 16 times in a tight loop, sum the values, and bit-shift right by 4 (divide by 16). This effectively increases your ADC resolution from 10-bit to 12-bit, eliminating the ±0.2°C jitter seen on the serial monitor.
  • Hardware Upgrade: Move to an Arduino Nano Every or an ESP32. The ESP32 features a 12-bit ADC (0-4095), which quadruples your temperature resolution. Note that the ESP32 ADC is notoriously non-linear above 3.0V, so for ESP32 builds, swap the 10kΩ pull-up for a 3.3kΩ pull-up to keep the midpoint voltage in the ESP32's linear ADC range.