The Sensing Principle: How NTC Thermistors Work

A thermistor (a portmanteau of "thermal" and "resistor") is a solid-state temperature sensor made from sintered semiconductor metal oxides. The most common variant in embedded electronics is the NTC (Negative Temperature Coefficient) thermistor. In an NTC device, electrical resistance drops non-linearly as temperature rises. This happens because thermal energy excites electrons in the semiconductor lattice, increasing the number of available charge carriers and thereby lowering the resistance. Unlike digital sensors (e.g., DS18B20) that output serial data, or thermocouples that generate a micro-voltage via the Seebeck effect, a thermistor is a purely passive, analog resistive component.

Because a thermistor only outputs variable resistance, it cannot be read directly by a microcontroller's GPIO or ADC pins. You must pass a known excitation current through it to create a measurable voltage drop. In practice, we do this by placing the thermistor in a voltage divider circuit with a fixed-precision resistor. The microcontroller's ADC then reads the analog voltage at the junction of the two resistors, which we later convert back into a physical temperature using logarithmic math. While PTC (Positive Temperature Coefficient) thermistors exist, they are primarily used as resettable fuses for overcurrent protection rather than precision temperature measurement.

Core Specifications and Hardware Wiring

Before wiring up a sensor, you need to understand its datasheet parameters. The hobbyist and prototyping standard is the 10K NTC 3950 glass-encapsulated thermistor. The "10K" refers to its nominal resistance at 25°C, and "3950" is its Beta (B) value, which defines the shape of its resistance-to-temperature curve. According to Omega Engineering's temperature sensor guides, glass encapsulation protects the semiconductor from moisture ingress, which can otherwise cause severe resistance drift over time.

Table 1: Typical Datasheet Specifications for a 10K NTC 3950 Thermistor
Parameter Symbol Typical Value Practical Impact on Your Circuit
Nominal Resistance (25°C) R25 10,000 Ω (10K) Determines the fixed resistor value needed for your voltage divider.
Beta Value (25/50°C) B 3950 K ± 1% Used in the Beta parameter equation to calculate temperature from resistance.
Resistance Tolerance ΔR ± 1% to ± 5% A 5% tolerance introduces roughly ±1.5°C error at room temp; 1% is preferred.
Dissipation Constant δ ~1.5 mW/°C Dictates self-heating error; keep excitation current low to avoid cooking the sensor.
Thermal Time Constant τ ~15 seconds Time to reach 63.2% of a step change in temp; limits your maximum sampling rate.
Operating Temperature Top -55°C to +125°C Glass bead survives high heat, but silicone lead wire insulation may melt >200°C.

Wiring the Voltage Divider

To interface this resistive output with an ESP32 or Arduino, we use a simple voltage divider. You supply a reference voltage (Vin), place a fixed pull-up resistor (Rseries), and ground the thermistor. The ADC reads the middle node (Vout).

Table 2: ESP32 / Arduino Thermistor Wiring Pinout
Microcontroller Pin Connection Target Notes & Supply Range Constraints
3V3 (or 5V) Fixed 10K Resistor (Leg 1) Use the 3.3V pin on ESP32 to match the ADC reference voltage range (0-3.1V).
GPIO 34 (ADC1_CH6) Junction of Resistor & Thermistor Use an ADC1 pin on ESP32. ADC2 pins are disabled when WiFi is active.
GND Thermistor (Leg 2) Keep ground return path short to avoid ground-loop noise injection.
N/A (Passive) Fixed 10K Resistor (Leg 2) Connects to the thermistor. Use a 1% metal film resistor for accuracy.
Bench Tip: Never power a thermistor voltage divider directly from a noisy microcontroller VCC rail if you need precision. For high-accuracy bench setups, drive the top of the divider from a dedicated 3.3V LDO (like an AMS1117-3.3) or switch the divider power via a MOSFET so the thermistor only draws current during the brief ADC sampling window.

Converting Raw ADC Readings to Celsius (The Math)

The most common point of failure in thermistor projects is conflating the raw ADC integer with a linear voltage, and then assuming voltage maps linearly to temperature. It does not. You must perform a three-step mathematical conversion: Raw ADC to Voltage, Voltage to Resistance, and Resistance to Temperature.

Step 1: Raw ADC to Voltage

Assuming a 12-bit ADC (like the ESP32's) and a 3.3V reference, the raw reading (0-4095) converts to voltage:

V_out = Raw_ADC * (3.3 / 4095.0)

Step 2: Voltage to Resistance

Using the voltage divider rule, where Rseries is your fixed 10,000Ω pull-up resistor, we solve for the thermistor's current resistance (Rntc):

R_ntc = R_series * ((V_in / V_out) - 1.0)

Note: If you wired the thermistor to VCC and the fixed resistor to GND, the math inverts to R_ntc = R_series / ((V_in / V_out) - 1.0).

Step 3: Resistance to Temperature (Beta Equation)

To translate resistance into Celsius, we use the Beta parameter equation, a simplified version of the Steinhart-Hart equation that is highly accurate between -20°C and +100°C. As detailed in Ametherm's NTC theory documentation, the formula requires converting your nominal values to Kelvin first:

1 / T_kelvin = (1 / T_0) + (1 / B) * ln(R_ntc / R_0)

  • T_0: Nominal temperature in Kelvin (25°C = 298.15 K)
  • B: Beta value from datasheet (e.g., 3950)
  • R_0: Nominal resistance (10,000 Ω)
  • R_ntc: Calculated resistance from Step 2

Finally, convert back to Celsius: T_celsius = T_kelvin - 273.15.

Interference Sources, Calibration, and ESP32 Implementation

Even with perfect math, real-world physics will corrupt your data if you ignore interference. Here are the three primary error sources and how to mitigate them:

  1. Self-Heating Error: Passing current through the thermistor generates heat (P = I²R). If your dissipation constant is 1.5 mW/°C and your circuit dissipates 3 mW, the sensor will read 2°C higher than ambient. Fix: Use a higher value pull-up resistor (e.g., 47K or 100K) to limit current, or only power the divider during the read cycle.
  2. ADC Non-Linearity (ESP32 Specific): The ESP32's internal SAR ADC is notoriously non-linear at the extreme ends of its range (below 0.1V and above 3.0V). Fix: Design your voltage divider so the expected temperature range keeps Vout between 0.5V and 2.5V. According to the Espressif ADC Oneshot Driver docs, applying software multi-sampling and ADC calibration curves via the ESP-IDF API drastically reduces this noise.
  3. Lead Wire Resistance & EMI: For a 10K thermistor, a few ohms of copper wire resistance is negligible (causing <0.01°C error). However, long unshielded wires act as antennas for 50/60Hz mains EMI. Fix: Use twisted-pair wire for the thermistor leads and place a 100nF ceramic bypass capacitor directly across the ADC input pin and GND to filter high-frequency noise.

Calibration Protocol

Off-the-shelf 3950 thermistors often have a B-value tolerance of ±2%, which translates to roughly ±1.5°C error at 80°C. If your application requires higher precision, perform a two-point calibration. Submerge the sealed sensor in a stirred ice-water bath (0.0°C) and record the resistance. Then submerge it in boiling water (adjusting for your local barometric pressure/altitude) and record the resistance. Use these two points to calculate the actual B-value of your specific component using the inverse Beta formula.

Complete ESP32 Arduino Implementation

The following code implements the voltage divider math, the Beta equation, and a 16-sample rolling average to smooth out ESP32 ADC jitter. Wire the sensor to GPIO 34 as outlined in Table 2.

// Thermistor Pin Definitions
#define THERMISTOR_PIN 34      // ESP32 ADC1_CH6
#define SERIES_RESISTOR 10000  // 10K Ohm fixed pull-up resistor
#define VCC_VOLTAGE 3.3        // 3.3V reference

// Thermistor Nominal Values
#define NOMINAL_RESISTANCE 10000 // R0 = 10K
#define NOMINAL_TEMPERATURE 25   // T0 = 25C
#define B_COEFFICIENT 3950       // Beta = 3950

void setup() {
  Serial.begin(115200);
  analogReadResolution(12);    // Set ESP32 ADC to 12-bit (0-4095)
  analogSetAttenuation(ADC_11db); // Full range up to ~3.1V
}

void loop() {
  // Multi-sample to mitigate ESP32 ADC noise
  float adc_sum = 0;
  for(int i = 0; i < 16; i++) {
    adc_sum += analogRead(THERMISTOR_PIN);
    delayMicroseconds(500);
  }
  float adc_avg = adc_sum / 16.0;

  // Step 1: ADC to Voltage
  float v_out = adc_avg * (VCC_VOLTAGE / 4095.0);
  
  // Prevent divide-by-zero if thermistor is disconnected (V_out = 0)
  if(v_out < 0.05) {
    Serial.println("Error: Thermistor open circuit or shorted.");
    delay(2000);
    return;
  }

  // Step 2: Voltage to Resistance
  float r_ntc = SERIES_RESISTOR * ((VCC_VOLTAGE / v_out) - 1.0);

  // Step 3: Resistance to Temperature (Beta Equation)
  float t_kelvin = 1.0 / ((1.0 / (NOMINAL_TEMPERATURE + 273.15)) + 
                          (1.0 / B_COEFFICIENT) * log(r_ntc / NOMINAL_RESISTANCE));
  float t_celsius = t_kelvin - 273.15;
  float t_fahrenheit = (t_celsius * 1.8) + 32.0;

  Serial.printf("Raw ADC: %.1f | R_ntc: %.0f Ohms | Temp: %.2f C (%.2f F)\n", 
                adc_avg, r_ntc, t_celsius, t_fahrenheit);

  delay(1000); // Respect thermal time constant
}

By understanding the underlying physics of the semiconductor lattice and rigorously applying the Beta parameter math, you can extract laboratory-grade temperature data from a component that costs less than twenty cents.