If you need to measure temperature with an Arduino, the default pick is a 10K NTC 3950 B-value glass bead thermistor paired with a 10K 1% metal film pull-down resistor. This combination offers the best balance of ADC resolution, low self-heating, and widespread library support for the ATmega328P's 10-bit analog-to-digital converter.
While digital sensors like the DS18B20 are popular, analog thermistors react faster (often under 2 seconds) and cost pennies. However, getting accurate Celsius readings requires a proper voltage divider circuit and the Steinhart-Hart equation in your firmware. Below is the exact hardware selection, wiring procedure, and production-ready C++ code to get reliable readings without the dreaded nan serial errors.
The Quick Decision Path: 10K vs 100K NTC Thermistor
Not all thermistors are created equal. The base resistance (10K vs 100K) and the encapsulation material drastically change how you wire and code the sensor. Use this decision matrix to select the right component for your specific build environment.
| Application Scenario | Recommended Variant | Why This Wins |
|---|---|---|
| General purpose, dry air, -40°C to +125°C | 10K NTC 3950 Glass Bead | Matches standard 10K pull-up/down resistors; glass survives high heat; fast thermal response. |
| High humidity, liquid-adjacent, 0°C to +70°C | 10K NTC 3950 Epoxy Coated | Thick epoxy head prevents moisture ingress which causes resistance drift in bare glass beads. |
| Battery pack monitoring, ultra-low power | 100K NTC 3950 Glass Bead | Draws 10x less current through the voltage divider, minimizing self-heating and battery drain. |
| Long wire runs (>2 meters) | 100K NTC (or switch to DS18B20) | Higher base resistance makes the parasitic resistance of long copper wires mathematically negligible. |
Parts List and Exact Specifications
A thermistor is a variable resistor. To read it with an Arduino, we must place it in a voltage divider circuit with a fixed resistor. The tolerance of that fixed resistor directly dictates your temperature accuracy. Do not use a standard 5% carbon film resistor for the fixed leg.
| Component | Exact Specification / Model | Estimated Cost (2026) |
|---|---|---|
| Microcontroller | Arduino Uno R3 (or Nano v3) - ATmega328P | $15.00 - $22.00 |
| Thermistor | NTC 10K Ohm, B-Value 3950, Glass Encapsulated | $0.50 (in 10-packs) |
| Fixed Resistor | 10K Ohm, 1/4W, 1% Tolerance Metal Film | $0.10 |
| Filter Capacitor (Optional) | 0.1µF (100nF) Ceramic Disc Capacitor | $0.05 |
Difficulty Rating: 2/5 (Basic breadboarding and fundamental C++ math).
Wiring the Thermistor to Arduino
We are wiring the thermistor to the 'high' side of the voltage divider. This means as temperature increases, the thermistor's resistance drops, and the voltage at the analog pin increases. This configuration is slightly more intuitive for debugging than the reverse.
Pin Mapping Table
| Component Leg | Destination | Notes |
|---|---|---|
| Thermistor Leg 1 | Arduino 5V Pin | Polarity does not matter for NTC thermistors. |
| Thermistor Leg 2 | Arduino A0 Pin AND Fixed Resistor Leg 1 | This is the analog sense node. |
| Fixed Resistor Leg 2 | Arduino GND Pin | Completes the voltage divider to ground. |
Step-by-Step Wiring Procedure
- Insert the 10K metal film resistor into your breadboard, spanning the center trench.
- Insert the glass bead thermistor into the same row as one leg of the fixed resistor. (NTC thermistors are unpolarized; either leg works).
- Run a jumper wire from the Arduino 5V pin to the empty leg of the thermistor.
- Run a jumper wire from the shared junction (where the thermistor and fixed resistor meet) to the Arduino A0 analog pin.
- Run a jumper wire from the empty leg of the fixed resistor to the Arduino GND pin.
- Optional Noise Filtering: Insert the 0.1µF capacitor in parallel with the fixed resistor (one leg in the A0 junction row, the other in the GND row). This creates a low-pass RC filter that eliminates 60Hz mains hum and ADC jitter.
Complete Steinhart-Hart C++ Code
This code targets the Arduino Uno R3 (ATmega328P) and its 10-bit ADC (0-1023 range). It uses the Beta parameter equation (a simplified Steinhart-Hart equation) which is highly accurate for the 3950 B-value thermistors between 0°C and 70°C.
Crucially, this code includes error handling for ADC saturation. If a wire breaks or shorts, the ADC reads exactly 0 or 1023. Passing these values into the logarithmic math functions will crash the math engine and output nan (Not a Number). We intercept this before the math executes.
// Target: Arduino Uno R3 / Nano v3 (ATmega328P)
// Sensor: NTC 10K 3950 Glass Bead Thermistor
const int THERMISTOR_PIN = A0;
const int OVERSAMPLE_COUNT = 16; // Averaging 16 reads for 12-bit effective resolution
// Circuit Constants
const float SERIES_RESISTOR = 10000.0; // 10K Ohm fixed resistor (use 1% tolerance)
const float ADC_MAX = 1023.0; // 10-bit ADC maximum value
// Thermistor Datasheet Coefficients
const float NOMINAL_RESISTANCE = 10000.0; // Resistance at 25°C
const float NOMINAL_TEMPERATURE = 25.0; // 25°C in Celsius
const float B_COEFFICIENT = 3950.0; // Beta value (25/85 standard)
void setup() {
Serial.begin(115200);
analogReference(DEFAULT); // 5V reference on Uno R3
while (!Serial) { ; } // Wait for serial port to connect
Serial.println("Thermistor Initialized. Reading...");
}
void loop() {
// 1. Read ADC with Oversampling to reduce noise
long adcSum = 0;
for (int i = 0; i < OVERSAMPLE_COUNT; i++) {
adcSum += analogRead(THERMISTOR_PIN);
delayMicroseconds(200); // Allow ADC sample-and-hold cap to settle
}
float adcAverage = (float)adcSum / OVERSAMPLE_COUNT;
// 2. Error Handling: Prevent Math Crashes (NaN/Inf)
if (adcAverage <= 1.0) {
Serial.println("ERROR: ADC reads 0. Check for short to GND or broken 5V wire.");
delay(2000);
return;
}
if (adcAverage >= ADC_MAX - 1.0) {
Serial.println("ERROR: ADC reads 1023. Check for broken GND wire or short to 5V.");
delay(2000);
return;
}
// 3. Convert ADC to Resistance
// Formula for 5V -> Thermistor -> A0 -> Resistor -> GND
float resistance = SERIES_RESISTOR * ((ADC_MAX / adcAverage) - 1.0);
resistance = SERIES_RESISTOR / ((ADC_MAX / adcAverage) - 1.0); // Corrected divider math
// 4. Steinhart-Hart (Beta Equation) Math
float steinhart;
steinhart = resistance / NOMINAL_RESISTANCE; // (R/Ro)
steinhart = log(steinhart); // ln(R/Ro)
steinhart /= B_COEFFICIENT; // 1/B * ln(R/Ro)
steinhart += 1.0 / (NOMINAL_TEMPERATURE + 273.15); // + (1/To)
steinhart = 1.0 / steinhart; // Invert
steinhart -= 273.15; // Convert Kelvin to Celsius
// 5. Output
Serial.print("Resistance: ");
Serial.print(resistance);
Serial.print(" Ohms | Temp: ");
Serial.print(steinhart);
Serial.println(" °C");
delay(1000);
}
Note on ADC Math: The formula R_therm = R_series / ((ADC_MAX / adc) - 1) is specifically derived for the wiring topology where the thermistor is on the 5V side. If you swap the physical positions of the thermistor and the fixed resistor, you must change the code to R_therm = R_series * ((ADC_MAX / adc) - 1).
Debugging: Fixing 'nan' and '-127.00' Readings
When working with analog thermistors, the serial monitor throwing strange values is a rite of passage. If your serial output displays nan, inf, or a hardcoded fallback like -127.00, follow this ranked troubleshooting path.
The First Three Things to Check
- ADC Saturation (The 'nan' Generator): The
nan(Not a Number) error occurs when the Arduino attempts to calculate the natural log of zero or a negative number (log(0)). This happens if your ADC reads exactly0or1023.- Fix: Verify your voltage divider wiring. If A0 reads 1023, the thermistor is shorted to 5V or the GND leg of the fixed resistor is disconnected. If A0 reads 0, the thermistor is disconnected from 5V.
- Pull-Down Resistor Mismatch: If your temperatures read wildly incorrectly (e.g., room temp reads as 65°C), you likely used the wrong fixed resistor.
- Fix: Measure your fixed resistor with a multimeter. If you are using a 10K thermistor, the fixed resistor must be 10K. Using a 100K resistor with a 10K thermistor will shift the voltage divider curve entirely, breaking the Steinhart-Hart coefficients in the code.
- Analog Pin Misaddressing: A common beginner mistake is defining the pin as
0instead ofA0, or physically plugging the sense wire into Digital Pin 0 (RX) instead of Analog Pin 0.- Fix: Ensure the code defines
const int THERMISTOR_PIN = A0;and the physical wire is in the Analog header bank.
- Fix: Ensure the code defines
For a deeper mathematical breakdown of how the Beta parameter equation models the non-linear resistance curve of NTC semiconductors, refer to the Ametherm Steinhart-Hart technical guide. If you need to verify your specific board's ADC behavior, consult the official Arduino analogRead() documentation.
Extending and Simplifying the Build
Depending on your end goal, you can either strip this project down to its bare essentials or scale it up for industrial-grade stability.
How to Simplify (The 'Quick Prototype' Route)
If you do not want to wire a voltage divider or calculate math, purchase a pre-packaged module like the KY-013 Analog Temperature Sensor. This is a small PCB that already includes the NTC thermistor and the 10K pull-up resistor wired to VCC, GND, and a Signal pin. Warning: Many cheap KY-013 modules ship with 5% carbon film resistors, which limits your absolute accuracy to roughly ±2°C. It is fine for triggering a cooling fan, but poor for a weather station.
How to Extend (The 'High Precision' Route)
If you need lab-grade stability (±0.2°C accuracy), implement these three hardware and software upgrades:
- Software Oversampling: The code provided above already includes a 16-sample oversampling loop. By reading the ADC 16 times and averaging, you mathematically gain 2 bits of resolution, effectively turning the Uno's 10-bit ADC into a 12-bit ADC (0-4095 range). This smooths out the 'stepping' effect in the serial plotter.
- Hardware RC Filtering: As mentioned in the wiring steps, soldering a 0.1µF ceramic capacitor directly across the fixed resistor creates a low-pass filter. This eliminates high-frequency EMI noise from nearby switching power supplies or WiFi modules (like an ESP32 sharing the same breadboard).
- Use the Full Steinhart-Hart Equation: The Beta equation used in our code is a simplification. For extreme temperature ranges (-40°C to +150°C), replace the Beta math with the full 3-coefficient Steinhart-Hart equation ($1/T = A + B\ln(R) + C(\ln(R))^3$). You can extract the A, B, and C coefficients directly from your specific thermistor's manufacturer datasheet.
For practical implementation examples of analog sensors in embedded environments, the Adafruit Thermistor Tutorial remains an excellent visual reference for breadboard layouts.
By sticking to the 10K NTC 3950 glass bead, a 1% metal film resistor, and the oversampled C++ code provided above, you will achieve fast, stable, and highly accurate temperature readings without relying on expensive digital sensor ICs.






