Gabriele De Cosimo
Published © CC0

Using an Old CPU as a Thermometer

Every CPU hides a temperature sensor. Read it with one microcontroller, two wires and zero extra components.

IntermediateProtip3 hours23
Using an Old CPU as a Thermometer

Things used in this project

Hardware components

Intel Pentium 4 631
×1
Raspberry Pi Pico
Raspberry Pi Pico
×1
DS18B20 Programmable Resolution 1-Wire Digital Thermometer
Maxim Integrated DS18B20 Programmable Resolution 1-Wire Digital Thermometer
×1
Resistor 4.75k ohm
Resistor 4.75k ohm
×1

Software apps and online services

Thonny

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free
Multimeter

Story

Read more

Schematics

CPU_THERMOMETER

Code

calibration.py

MicroPython
# calibration.py - one-point calibration of the CPU thermal diode.
# Put the DS18B20 on the CPU heat spreader, don't touch anything and run this script:
# it waits SETTLE_MIN minutes so CPU and DS18B20 reach the same temperature,
# takes one averaged measurement and builds the full Vf(T) curve from diode physics.
from machine import Pin, ADC
import onewire, ds18x20, time, json, math

VREF = 3.300                     # <-- your 3V3 rail measured with a multimeter
DIODE_PIN = 27                   # GP27 / ADC1
DS_PIN = 22                      # DS18B20 DQ
SETTLE_MIN = 5                   # minutes to wait before measuring
READINGS = 30                    # readings averaged into the calibration point
SAMPLES = 1000                   # ADC samples per reading

VG0 = 1205.0                     # silicon bandgap voltage extrapolated to 0 K (mV)
ETA = 3.0                        # temperature exponent term of Vbe(T)
K_Q = 0.08617                    # Boltzmann constant / electron charge (mV/K)

adc = ADC(DIODE_PIN)
Pin(DIODE_PIN, Pin.IN, Pin.PULL_UP)   # after ADC(): the pull-up biases the diode

ds = ds18x20.DS18X20(onewire.OneWire(Pin(DS_PIN)))
sensors = ds.scan()
if not sensors:
    raise RuntimeError("DS18B20 not found")

def vf_mV():
    total = 0
    for _ in range(SAMPLES):
        total += adc.read_u16()
    return total / SAMPLES * VREF / 65535 * 1000

def ds_temp():
    ds.convert_temp()
    time.sleep_ms(750)
    return ds.read_temp(sensors[0])

def solve(A, b):
    m = len(b)
    M = [A[i][:] + [b[i]] for i in range(m)]
    for c in range(m):
        p = max(range(c, m), key=lambda r: abs(M[r][c]))
        M[c], M[p] = M[p], M[c]
        for r in range(c + 1, m):
            k = M[r][c] / M[c][c]
            for j in range(c, m + 1):
                M[r][j] -= k * M[c][j]
    sol = [0.0] * m
    for r in range(m - 1, -1, -1):
        sol[r] = (M[r][m] - sum(M[r][j] * sol[j] for j in range(r + 1, m))) / M[r][r]
    return sol

# ---------- checks ----------
v = vf_mV()
print("Initial Vf: {:.1f} mV".format(v))
if not 200 < v < 800:
    raise RuntimeError("Vf out of range: check the diode wiring (anode -> GP27, cathode -> AGND)")

# ---------- settle ----------
print("Don't touch anything. Waiting {} min for CPU and DS18B20 to reach the same temperature...".format(SETTLE_MIN))
for m in range(SETTLE_MIN, 0, -1):
    print("  {} min left   (DS18B20 {:.2f} C, Vf {:.1f} mV)".format(m, ds_temp(), vf_mV()))
    time.sleep(60 - 1)

# ---------- measure ----------
print("Measuring...")
ts, vs = [], []
for i in range(READINGS):
    ts.append(ds_temp())
    vs.append(vf_mV())
t_cal = sum(ts) / len(ts)
v_cal = sum(vs) / len(vs)
drift = ts[-1] - ts[0]
print("Calibration point: {:.2f} C  ->  {:.2f} mV".format(t_cal, v_cal))
if abs(drift) > 0.2:
    print("Warning: temperature drifted {:+.2f} C during the measurement.".format(drift))
    print("Wait longer and run the calibration again for best accuracy.")

# ---------- build the curve from diode physics ----------
# Vbe(T) = VG0 - (VG0 - Vbe0) * T/T0 - ETA * (kT/q) * ln(T/T0)
T0 = t_cal + 273.15
def vbe_model(t_c):
    T = t_c + 273.15
    return VG0 - (VG0 - v_cal) * T / T0 - ETA * K_Q * T * math.log(T / T0)

grid_t = [float(t) for t in range(0, 86)]
grid_v = [vbe_model(t) for t in grid_t]

# fit T = f(Vf) as a quadratic and save it in the format used by thermometer.py
x0 = sum(grid_v) / len(grid_v)
s = (max(grid_v) - min(grid_v)) / 2
us = [(x - x0) / s for x in grid_v]
A = [[sum(u ** (i + j) for u in us) for j in range(3)] for i in range(3)]
b = [sum(y * u ** i for u, y in zip(us, grid_t)) for i in range(3)]
c = solve(A, b)

print("Sensitivity at {:.1f} C: {:.3f} mV/C".format(t_cal, -(VG0 - v_cal) / T0 - ETA * K_Q))
with open("cal.json", "w") as f:
    json.dump({"x0": x0, "s": s, "c": c,
               "vmin": min(grid_v), "vmax": max(grid_v),
               "t_cal": t_cal, "v_cal": v_cal}, f)
print("Saved cal.json - calibration done. You can remove the DS18B20 and run thermometer.py")

cpu_thermometer.py

MicroPython
# thermometer.py - reads the CPU thermal diode and prints the temperature,
# using the calibration saved in cal.json by calibration.py
from machine import Pin, ADC
import json, time

VREF = 3.300                     # <-- same value used in calibration.py
DIODE_PIN = 27                   # GP27 / ADC1
SAMPLES = 1000                   # ADC samples averaged per reading
INTERVAL_S = 1                   # seconds between readings

try:
    with open("cal.json") as f:
        cal = json.load(f)
except OSError:
    raise RuntimeError("cal.json not found: run calibration.py first")

adc = ADC(DIODE_PIN)
Pin(DIODE_PIN, Pin.IN, Pin.PULL_UP)   # after ADC(): the pull-up biases the diode

def vf_mV():
    total = 0
    for _ in range(SAMPLES):
        total += adc.read_u16()
    return total / SAMPLES * VREF / 65535 * 1000

def temperature(v):
    u = (v - cal["x0"]) / cal["s"]
    return sum(c * u ** i for i, c in enumerate(cal["c"]))

while True:
    v = vf_mV()
    if not 200 < v < 800:
        print("Diode not detected (Vf {:.0f} mV): check wiring".format(v))
    else:
        t = temperature(v)
        note = "" if cal["vmin"] <= v <= cal["vmax"] else "  ! outside 0-85 C range"
        print("{:.2f} C{}".format(t, note))
    time.sleep(INTERVAL_S)

Credits

Gabriele De Cosimo
1 project β€’ 0 followers
Electronics enthusiast into retrofit, rapid prototyping and giving old hardware a second life.

Comments