Counts to degrees
Two lines of arithmetic: the count back to the thermistor's resistance, and the resistance to degrees with the B equation. They are exact at 25 °C by construction and good across the range. A straight map() is exact at two temperatures and wrong everywhere else.
Two steps
A count is not a temperature. Between them are two conversions, and each is one line.
Count to resistance. On an Uno the count is SIGNAL as a fraction of VCC, out of 1023, and the divider says what the thermistor must be to give that fraction:
R_ntc = 10000 × count / (1023 − count)Resistance to degrees. The B model from the second article, solved for T:
1/T = 1/298.15 + ln(R_ntc / 10000) / 3950T comes out in kelvin; subtract 273.15 for Celsius.
Slide the count and follow the orange curve: every count has exactly one temperature, and the curve bends at both ends. At 512, halfway, the thermistor is 10 kΩ and the answer is 25 °C, which is the B model's fixed point.
Why not map()
The grey line is map() told that the count at 0 °C means 0 and the count at
50 °C means 50. It is exact at those two points and up to about 2 °C off
between them. Outside them it has no way to bend: by 75 °C it reads more than
10 °C low, and by 100 °C about 30. Choosing different points moves the error;
it does not remove it.
What you should see
The serial monitor prints the resistance and the temperature once a second:
10751 ohm 23.4 C
10751 ohm 23.4 C
10793 ohm 23.3 C
8499 ohm 28.7 CThe last line is a finger on the thermistor. If the line reads SIGNAL at 0
or At the top, the arithmetic is fine and a wire is missing: the sketch
refuses to take the logarithm of a reading that cannot be a temperature.
The code
The whole chain: read SIGNAL, work out the thermistor's resistance, and turn that into degrees with the B equation. On an ESP32 the first step reads millivolts instead of a count, for the reason in the article after this one.
/*
NTC Thermistor - counts to degrees 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 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
// The thermistor's resistance, or NAN if SIGNAL is at either end.
float thermistorOhms() {
#if defined(ARDUINO_ARCH_ESP32)
// Own reference: calibrated millivolts, over what VCC is.
float mv = analogReadMilliVolts(SENSOR_PIN);
if (mv <= 0 || mv >= VCC_MV) return NAN;
return R_FIXED * mv / (VCC_MV - mv);
#else
// Uno, Pico: the count is already a fraction of VCC.
float n = analogRead(SENSOR_PIN);
if (n <= 0 || n >= 1023) return NAN;
return R_FIXED * n / (1023.0 - n);
#endif
}
// The B equation, backwards: resistance to degrees Celsius.
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(r, 0);
Serial.print(" ohm ");
Serial.print(celsius(r), 1);
Serial.println(" C");
}
delay(1000);
}R25 and B are the 103 and the 3950 from the thermistor's part number. VCC_MV only matters on an ESP32: measure the 3V3 pin with a multimeter and put the reading in, and the answer improves. On an Uno and a Pico there is nothing to measure.
The same chain in MicroPython. On an ESP32 read_uv returns calibrated microvolts; on a Pico read_u16 is already a fraction of the 3.3 V the block runs from.
"""
NTC Thermistor - counts to degrees, MicroPython TK12 / /p/tk12
Wiring. Count from the square pad on the TinkerBlock board, parts
up, header at the bottom:
GND -> GND
VCC -> 3V3 (SIGNAL is a fraction of VCC)
NC -> nothing (unconnected on the board)
SIGNAL -> GPIO 34 on an ESP32, GPIO 4 on an ESP32-S3,
GP26 on a Raspberry Pi Pico
Thonny
Run > Configure interpreter MicroPython (ESP32) or
MicroPython (Raspberry Pi Pico)
Save it to the board as main.py to run it on every power-up.
Nothing to install: machine, math, sys and time are built in.
"""
import math
import sys
import time
from machine import ADC, Pin
# The GPIO number SIGNAL is wired to. ESP32: 34. ESP32-S3: 4. Pico: 26.
SENSOR_PIN = 4
R_FIXED = 10000 # the 10 kOhm from VCC to SIGNAL
R25 = 10000 # the thermistor at 25 C: the "103"
B = 3950 # its B value: the "3950"
T0 = 298.15 # 25 C in kelvin
VCC = 3.3 # ESP32 only: your 3V3 pin, measured
adc = ADC(Pin(SENSOR_PIN))
ESP = sys.platform == "esp32" # ESP32 and ESP32-S3
if ESP:
adc.atten(ADC.ATTN_11DB) # the full range, to about 3.1 V
def thermistor_ohms():
if ESP:
ratio = adc.read_uv() / 1000000 / VCC # own reference
else:
ratio = adc.read_u16() / 65535 # already a fraction
if ratio <= 0 or ratio >= 1:
return None # SIGNAL at either end
return R_FIXED * ratio / (1 - ratio)
def celsius(ohms):
return 1 / (1 / T0 + math.log(ohms / R25) / B) - 273.15
while True:
r = thermistor_ohms()
if r is None:
print("SIGNAL at 0: check VCC. At the top: check GND.")
else:
print("%.0f ohm %.1f C" % (r, celsius(r)))
time.sleep(1)read_uv is in recent MicroPython releases for the ESP32 family; if yours says it has no such method, update the firmware. VCC is only used on an ESP32: measure the 3V3 pin and put the reading in. Stop it with Ctrl-C.
When it does not work
The resistance line is upside down: it was written for a board with the thermistor on top. On this block the thermistor is the lower half, so it is 10000 × n / (1023 − n). Swap it and warming it will raise the number.
The count was 0 or the maximum, so the resistance came out as zero or infinite, and a logarithm of either is not a number. That is VCC or GND missing, not arithmetic. The sketch here catches both and prints which wire to check.
It follows a thermistor's curve more closely than the B model, but it needs three constants fitted to the part's own resistance table, and this part's table was not available. With only 10 kΩ and B = 3950 to go on, the B equation is what the numbers support.
Float. The logarithm and the division by B produce small fractions that an int rounds to zero. An Uno's float is slower than an int but still does this in well under a millisecond, once a second.
Its own reference, a range that stops short of 3.3 V, and millivolts instead of counts.
Reading it on an ESP32 →Edit this page — content/books/ntc-thermistor/counts-to-degrees.mdx
Questions about this product
See what other owners have asked, and read their solutions.
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.