NTC thermistor temperature reading jumping ±4°C — ADC noise or something in the circuit?
Asked by stale_biscuit_03 ·
I've got an NTC thermistor (10 kΩ at 25°C, standard B = 3950) in a voltage divider with a 10 kΩ fixed resistor, top rail 3.3 V, thermistor to GND. Output of the divider goes to one of the ESP32's ADC1 pins. I'm reading the ADC, converting to resistance, then applying the B-parameter equation to get temperature.
At room temperature with the thermistor sitting still (not touching it, no airflow), the readings jump around by ±3–5°C. I'm not doing anything aggressive — just reading the ADC once per second in the main loop.
I tried averaging 100 samples before converting. That helped a bit but I'm still seeing ±1–2°C variation, which is too much for what I'm trying to do (monitor ambient temperature in a small enclosure).
My PCB has the thermistor on the edge connector, so the analog trace runs about 80 mm from the divider output back to the ESP32 ADC pin. No ground pour on the board, just a 2-layer with digital signals routing all over the place.
Is this level of noise normal for an NTC? I assumed 12-bit ADC would give me sub-1°C resolution. Where should I be looking first?
3 Replies
±4°C is not normal for an NTC thermistor. Let's work through why you're seeing it.
At 25°C with a 10k/10k divider, the midpoint voltage is around 1.65 V and the divider sensitivity is roughly 13–15 mV/°C. For ±4°C of reading error you'd need ±55–60 mV of noise on that ADC input. On a 3.3 V / 12-bit ADC, 1 LSB = 0.8 mV — so 60 mV is about 75 LSBs of noise. That's a lot. You have multiple error sources stacking up.
1. AVDD decoupling on the ESP32
This is usually the biggest culprit, especially if you're running Wi-Fi. The ESP32 ADC and its internal reference share a supply pin (often labelled VDDA or VDD3P3_RTC depending on the module). Every time the Wi-Fi PA fires — even if you're not in an active TX burst — there are transient load spikes on the 3.3 V rail that couple into the ADC reference. If you don't have a 100 nF ceramic capacitor placed right at that VDDA pin (not 10 mm away, right at the pin), those spikes read as noise.
Add a 100 nF C0G or X7R ceramic directly at the ADC supply pin. Add a 10 µF bulk cap nearby on the same rail. Then check your 3.3 V supply decoupling more broadly — if your regulator output cap is far from the ESP32 or is a large electrolytic without a parallel ceramic, you'll see this problem regardless of Wi-Fi state.
2. No filter capacitor across the thermistor
Right now your thermistor divider is presenting a Thevenin source impedance of roughly 5 kΩ (the parallel combination of 10 kΩ and 10 kΩ) at the ADC input. That 5 kΩ source impedance, combined with the ADC's sample-and-hold capacitor (~1 pF for the ESP32), forms a charging time constant that can cause problems at higher sampling rates, but more importantly, 5 kΩ × 80 mm of trace acts as an antenna and a noise integrator.
Add a 100 nF ceramic capacitor across the thermistor (between the divider output and GND). This does two things: it forms a low-pass RC filter with the Thevenin source resistance (f_c = 1 / (2π × 5 kΩ × 100 nF) ≈ 320 Hz), and it reduces the impedance that the 80 mm trace is driving at high frequency. For temperature sensing — a signal that changes over seconds or minutes — a 320 Hz cutoff is no constraint at all.
3. The 80 mm analog trace
An 80 mm unshielded trace on a 2-layer board with no ground pour is an efficient pickup antenna for anything switching near it. At 12-bit resolution and 5 kΩ source impedance, that trace will couple mains-frequency interference, digital edge transients, and Wi-Fi harmonics into the ADC reading.
The cap in point 2 helps a lot here. Additionally: if you can add a ground pour on the top layer adjacent to the analog trace, do it. Or use a bottom-layer GND pour directly below it. Either creates a low-impedance return path that reduces the effective loop area for noise pickup.
4. ESP32 ADC calibration and oversampling
The ESP32 ADC has a known non-linearity (see the ESP-IDF ADC calibration documentation).
For 1°C accuracy you should be using the calibrated ADC API — esp_adc_cal_get_voltage()
rather than raw adc1_get_raw(). The calibrated path compensates for the internal
reference offset and gain error, which can be several mV by themselves.
For oversampling: the ESP-IDF ADC multisampling API or a simple firmware loop averaging 64 or 256 readings (with conversion after averaging, not per-sample) is more effective than averaging 100 raw integers. More samples, combined with the 100 nF cap already doing pre-filtering, will get you to ±0.2–0.3°C real noise in a normal environment.
In short: add 100 nF to VDDA, add 100 nF across the thermistor, route carefully, and use the calibrated ADC API. The sensor signal conditioning page covers the full picture of ADC input filtering and reference decoupling. The thermistor page has the divider circuit and self-heating considerations if you haven't read it already.
adc_accuracy_alice has covered the main circuit fixes. I want to add some context on the layout side, because 80 mm of unprotected trace is doing real damage here.
When you route an analog signal trace without a ground return path nearby, the effective noise pickup area is the loop formed by the signal trace and the nearest available return path — which, on a board with no ground pour, might be a digital signal on the other side of the board or a power trace several centimetres away. That loop area picks up magnetic field noise proportional to its area. A 80 mm × 20 mm loop can pick up enough 50 Hz mains interference alone to account for a significant fraction of your variation, depending on what's nearby in the enclosure.
Adding ground pour adjacent to the analog trace reduces that loop area dramatically. If the analog trace is on the top layer, a top-layer GND pour on either side (stitched to GND with vias) reduces the loop to roughly the trace-to-pour spacing. If the digital signals on the board are creating too much copper congestion for a pour, even routing a dedicated GND trace running parallel to and close to the analog trace helps.
One thing worth knowing: the 100 nF cap across the thermistor that Alice recommended doesn't just filter electrical noise. Because the thermistor's resistance changes with temperature, and temperature changes slowly, that RC filter's 320 Hz cutoff is still ten thousand times faster than any real temperature transient you'll see. It only blocks electrical interference — you don't lose any temperature measurement bandwidth at all.
The voltage divider page has a section on loading effects worth reviewing if you adjust the resistor values — higher-value resistors give more noise sensitivity at the divider output but reduce self-heating current through the thermistor. There's a real trade-off there.
Once you've fixed the circuit and layout issues above, there's one more accuracy thing worth setting expectations on: the B-parameter equation with a generic B = 3950 value.
The B-parameter is a simplification of the Steinhart-Hart relationship. It's reasonably accurate over a narrow temperature range (typically ±10–15°C around the calibration point, usually 25°C), but the actual B value for your specific thermistor lot varies — typically ±1% in the datasheet tolerance, which translates directly into a temperature error that looks like noise but isn't: it's a systematic offset that varies with temperature.
For monitoring ambient temperature in an enclosure — say 15–40°C — the B-parameter approach with the manufacturer's nominal value usually gets you within ±0.5–1°C if the rest of the signal chain is clean. That's probably acceptable for your use case.
If you need better, the options are: two-point calibration against a reference thermometer (gives you a corrected B for your specific batch at two temperatures), or switching to a digital temperature IC for absolute accuracy without calibration effort. The digital temperature sensor page covers the MCP9808 and TMP117, both of which give ±0.1–0.2°C accuracy out of the box on I2C — useful comparison for deciding whether the NTC approach is right for your accuracy target.
The bottom line: after fixing the decoupling and layout issues, you should comfortably reach ±0.5°C variation in a quiet environment. If you're seeing more than that, the B-parameter calibration is the next thing to investigate.
Related Discussions
MAX31865 reading fault bit on every conversion — VBIAS enabled but still faulting
Spent most of yesterday trying to get a MAX31865 talking to my STM32F4 and I'm stuck on the same problem no matter what I try: the fault bit
ICM-42688-P INT1 never fires after FIFO setup — FIFO count stuck at 0x0000 but WHO_AM_I reads back correctly
Working on a motion data logger — ICM-42688-P on an STM32F4 via SPI4 at 4 MHz. SPI seems fine: WHO_AM_I at register 0x75 reliably returns 0x
STM32 ADC readings bouncing ±50 LSB — CubeMX default sampling time the culprit?
Running into a wall with STM32G4 ADC. 12-bit ADC reading a 0–3.3 V analog signal from a simple potentiometer — nothing exotic, just wiper to
Op-amp output oscillating at ~200 kHz — only happens when I add the anti-aliasing cap
Building a signal conditioning board for a resistive sensor — non-inverting amplifier stage at gain of 6 (51 kΩ / 10 kΩ feedback), running o