Measuring Temperature Without an ADC: The RC Charge-Time Thermistor Trick
August 29, 2026
Measuring Temperature Without an ADC: The RC Charge-Time Thermistor Trick
The Problem
In the early days of microcontrollers, ADCs were expensive or simply not available on the part you could afford. You still needed to measure an analog quantity like temperature. The trick: use the timing of an RC charge instead of a voltage conversion.
You have exactly two digital outputs and one digital input:
- OUT1 — switches the supply into the charging resistor.
- OUT2 — discharges the capacitor.
- IN — a single digital input that reads one voltage threshold (its “logic 1” level).
No analog divider, no successive-approximation register, no ADC.
The Circuit
The measurement loop:
- Discharge — close OUT2 (OUT2 ON). The capacitor dumps to ~0 V. This guarantees every measurement starts from the same known state.
- Charge — open OUT2 (OUT2 OFF), close OUT1 (OUT1 ON), and start the timer. Current flows from +24 V through R1 and the thermistor into the capacitor.
- Time it — watch IN. When V_C crosses the logic-1 threshold V_L1, stop the timer. The elapsed time is your measurement.
- Discharge again — open OUT1 (OUT1 OFF), close OUT2 (OUT2 ON). The capacitor discharges back to ~0 V, readying the next measurement cycle.
The two outputs alternate: OUT1 and OUT2 are never on at the same time. OUT1 charges, OUT2 discharges. The IN pin is the observer.
The Physics
Charge phase
This is a first-order RC circuit. The capacitor charges toward the supply V_S through the total series resistance R_total = R1 + R_T(T):
Time to reach the logic-1 threshold
The IN pin trips when V_C reaches the logic-1 level V_L1. Solve for the time t_L:
Combine with τ:
Discharge phase
When V_C reaches V_L1, OUT1 opens and OUT2 closes. The capacitor now discharges through OUT2 to ground. With the supply disconnected, the circuit is a simple RC decay:
where is the voltage at the start of discharge (approximately ). The time to discharge from to a near-zero reset level is:
With the component values at 25 °C and a reset threshold of 1 V: s. The discharge is slower than the charge because the cap starts at 12 V and must decay almost to zero, whereas the charge only needs to climb from 0 to 12 V.
The same ODE governs both phases — transfer functions assume zero initial conditions, so the discharge (which is a zero-input response driven by the initial voltage ) is solved directly from the ODE with and , giving and the exponential decay above. One transfer function, two modes — only the input changes.
The thermistor
An NTC thermistor’s resistance falls as temperature rises. Using the B-equation (a simplified Steinhart–Hart with one beta constant), with T in kelvin, reference temperature T₀ = 298.15 K, and resistance R₀ at T₀:
The combined measurement equation
Put the thermistor into t_L:
The Circuit as a Transfer Function
The math above is easy to lose track of. The clean way an engineer reads this circuit: the charge phase is just a first-order RC lag, and the capacitor is literally the integrator.
The capacitor is 1/s
The capacitor’s defining relation is . Solve for the voltage:
The capacitor voltage is the integral of its charging current — an integrator, exactly scaled by .
Close the loop through the resistor
The charging current comes through the total series resistance: . As a voltage divider in the s-domain (with ):
That is the whole charge phase: a unity-gain first-order lag. Its step response is , and setting recovers — the same result as the physics derivation, now in one line.
The state-space form
One state is enough:
With the component values at 25 °C ( s): , . OpenBlockSim’s StateSpace block takes exactly these four arrays.
The catch: the plant is temperature-dependent
changes with temperature, so the plant is linear time-varying (LTV): the dynamics are linear, but the parameter is not. That’s why the block model doesn’t just drop a single TransferFunction block with fixed coefficients and call it done — it computes through the thermistor chain and feeds it into the ODE live. Freeze the temperature (a fixed constant) and the LTV plant collapses to the LTI lag above.
Why You Need a Lookup Table
Here is the key insight that drives the whole design.
- t_L is linear in R_T. Charging to a fixed fraction of V_S always gives t_L ∝ τ ∝ R_total. Doubling the resistance doubles the time. That part is clean.
- R_T is exponential in 1/T. The NTC thermistor resists exponentially, not linearly, with inverse temperature.
So even though time is a linear function of resistance, resistance is a highly nonlinear function of temperature. There is no closed-form inverse you can solve analytically — the exponential sits inside a linear map and cannot be algebraically unwrapped.
The standard solution: calibrate empirically. During commissioning, record t_L at several known temperatures, build a t_L → T table, store it in memory, and interpolate between entries at runtime. The table is the inverse you can’t solve for.
Component values used in this model
| Symbol | Value | Meaning |
|---|---|---|
| V_S | 24 V | supply |
| V_L1 | 12 V | logic-1 threshold |
| R1 | 10 kΩ | current-limiting resistor |
| C | 100 µF | capacitor |
| R₀ | 10 kΩ | thermistor resistance at 25 °C |
| B | 3435 K | thermistor beta constant |
| T₀ | 298.15 K | (25 °C) reference temperature |
With these, at 25 °C: τ = 2.0 s, and t_L = τ · ln(2) = 1.39 s.
The calibration curve
Because R_T is exponential, the t_L(T) curve is strongly curved. A small change in hot temperatures (where R_T is small) yields a small change in time, while a large change in cold temperatures yields a big change in time. The table must be denser where the curve is steepest — usually spanning the full expected operating range with more points at the cold/cold-resistance end.
Temperature (°C) R_T (Ω) t_L (s)
0 28,704 2.683
10 18,410 1.969
25 10,000 1.386
40 5,759 1.092
60 2,981 0.900
(Values from the model: hotter → lower R_T → shorter t_L, monotonically decreasing.)
Block Simulation Model
I built this as a runnable block diagram in OpenBlockSim, loaded from the Examples gallery under “Thermistor RC Charge/Discharge (2-OUT/1-IN)”. Comment annotations sit next to each block so you can read the diagram like the circuit.
The model uses the transfer function with s (frozen at 25 °C) as the RC plant, and a feedback loop that automatically switches between charge and discharge phases:
Signal flow
- Switch block — routes either +24 V (OUT1, charge) or 0 V (OUT2, discharge) into the TF plant input, based on the relay output. When the relay is off (V_C below threshold), the switch passes +24 V. When the relay fires (V_C above threshold), the switch passes 0 V.
- TransferFunction block — , the RC plant. Its output is V_C, the capacitor voltage. With +24 V input it charges; with 0 V input it discharges.
- Relay block — the IN pin. Trips at V (switchOn) and resets at 1 V (switchOff). The hysteresis band prevents chatter at the threshold. Output: 1 = charged, 0 = discharged.
- Feedback edge — relay output feeds back to the switch control input, closing the loop: V_C rises → relay fires → switch flips to 0 V → V_C falls → relay resets → switch flips back to 24 V → repeat.
Two Scope blocks display V_C (the charge/discharge waveform) and the relay output (the logic-1 pulse train).
What you see when you run it
The V_C scope shows a sawtooth-like waveform: exponential rise from 0 to 12 V (charge, s), then exponential decay from 12 V back to 1 V (discharge, s), then repeat. The relay scope shows a square wave that goes high when V_C crosses 12 V and low when V_C drops below 1 V.
One transfer function for both charge and discharge
The RC plant is the same physical circuit in both phases — the same resistor, the same capacitor. The only thing that changes is what drives the input. During charge, the input is V. During discharge, the input is 0 V (OUT2 shorts the cap to ground). The transfer function describes the capacitor’s response to whatever input you give it:
- Charge: input = , output rises as
- Discharge: input = 0, output decays as
Both are the same first-order ODE , just with different input . One transfer function, two operating modes — the Switch block handles the mode selection.
How the Switch block represents the digital outputs
The Switch block has three inputs and one output. It works like a digital selector:
| Switch port | Connected to | Circuit role |
|---|---|---|
| input[0] | gnd (0 V) | OUT2 ON — discharge path |
| input[1] | relay output | IN pin (digital input) — the control signal |
| input[2] | vs (24 V) | OUT1 ON — charge supply |
The relay (IN pin) is the control input. When the relay output is 0 (V_C below threshold), the switch passes input[2] = 24 V — this is OUT1 ON, OUT2 OFF (charge). When the relay output is 1 (V_C reached V_L1), the switch passes input[0] = 0 V — this is OUT1 OFF, OUT2 ON (discharge).
In the real microcontroller, the firmware would do this with two separate digital output pins. In the block diagram, the Switch block combines both outputs into a single signal-routing element: it is the OUT1/OUT2 selector, driven by the IN pin’s threshold detection. The Relay block acts as the IN pin — a digital input that reads V_C and outputs a 1 or 0 depending on whether V_C has crossed the logic-1 threshold.
| Block(s) | Circuit element / role |
|---|---|
vs (Constant = 24) | OUT1: +24 V supply (charge) |
gnd (Constant = 0) | OUT2: 0 V discharge path |
sw (Switch) | OUT1/OUT2 routing — relay state selects which source feeds the plant |
tf (TransferFunction 1/(2s+1)) | The RC plant: , s at 25 °C |
relay (Relay @ 12 V) | IN pin — logic-1 threshold detector with hysteresis (switchOff = 1 V) |
scope_v, scope_r | Oscilloscope views: V_C charge/discharge waveform, relay output |
Verifying the model against the math
- Charge time — the first rising edge crosses 12 V at s, matching the analytic s to within 1%.
- Discharge time — the first falling edge drops from 12 V to 1 V in s, matching s.
- Periodicity — the cycle repeats with a period of s (), confirming the feedback loop is stable.
This is a useful pattern generally: after building any simulation, verify it against a closed-form or hand calculation before trusting it. A simulation that runs but disagrees with the equations is just a confident wrong answer.
Practical Notes
- Discharge phase. The model includes the discharge explicitly: when V_C reaches V_L1, the relay fires and the switch routes 0 V into the TF plant, causing V_C to decay exponentially. The relay’s switchOff hysteresis (1 V) ensures the cap discharges well below the charge threshold before the next cycle begins. In the real circuit, the OUT2 discharge path has its own resistance (the ON-resistance of the output driver), which sets the discharge time constant. If that resistance is significant compared to R1 + R_T, the discharge won’t follow the same τ as the charge — model it separately if precision matters.
- Logic-1 threshold. The real IN pin’s threshold V_L1 is the device’s actual switching level, not necessarily a round number. Measure it or specify it precisely — it directly multiplies into t_L. A slight error here shifts every calibration entry.
- R1 is a safety resistor. It limits charging current and sets a floor on the minimum time constant so the microcontroller can measure it, even when the thermistor’s resistance collapses at high temperature.
- Noise and jitter. Because you’re timing an analog threshold crossing with a digital counter, timer resolution and input hysteresis add small measurement uncertainty. Calibration absorbs the fixed part; the random part bounds your achievable precision.
Why This Still Matters
Even now, this two-output-one-input technique shows up in cost-critical projects and in any place where you’d rather not spend an ADC channel. It also teaches the general lesson that measurement can be done in the time domain: instead of resolving a voltage to N bits, you count the time it takes to cross a fixed threshold, and let the nonlinearity of your transducer become a calibration table rather than a math problem.