How to build a 5 milli Kelvin thermometer with an Arduino?
In this blog I will show how you can build a 5 milli Kelvin (5 mK) thermometer with an Arduino Uno, all you need is a 10 kilo Ohm NTC, a 16 bit ADC board from Adafruit, a Vellman probe and an Arduino proto shield.
Negative temperature coefficient (NTC) resistors appear in many electronic circuits. In the battery charger I use a 10k NTC to monitor the temperature within the case since the current regulator generates heat and we don’t want it to overheat. Once we reach 90 degrees Celsius close to the LM317 heat sink the microcontroller decides to temporarily stop charging. In other designs such as the oven crystal oscillator there is a thermostat to keep the temperature within a strict regime to guarantee optimal frequency stability. I demonstrated that 1 part per billion, or 1 milliherz relative to one Megahertz can be obtained mainly because of the quality of the thermostat. The NTC resistor is an amazingly simple passive component that comes in many different designs. The 10k resistor is supposed to have a reference value of 10000 Ohm at 20 degrees centigrade. But this is where the trouble starts; cheap NTC’s usually don’t come with a calibration curve, so I thought it could be a fun experiment to try to do this myself during the holidays.
The simplest circuit that allows you to measure the NTC resistance is to put a 10k resistor in series connected to 5 Volt, and to measure the voltage across the NTC to the ground. There is very little to design, you measure the voltage with an analog channel on the Arduino UNO and, pronto, it works. But soon you find out that a simple design is limited 3 issues:
- The voltage measure across the NTC has a resolution not better than 5 millivolt because the Analog to Digital converter is designed for 10 bit resolution (5000 mV / 1024 is approximately 5 mV)
- The measurement is susceptible to noise, because the 5000mV that comes from the power supply of the Arduino UNO fluctuates slightly, a standard deviation of 15 mV is rather typical.
- Since there is a current going through the NTC we slightly heat it up, the 10k NTC in series with a 10k resistor generates a current of 5000/20e3 = 250 microamperes. When multiplied times the 2.5 Volt across the NTC we dissipate 625 microWatt which slightly increases the temperature. The more we dissipate the more the temperature runs up, and the resistance decreases so that the current increases even more. To stabilize this problem you would need a constant current, or a large resistor in series.
The challenge is therefore to design something that works better. To tackle issue 1 you need a better ADC for which I got the ADS1115 16 bit ADC from Adafruit which has 4 channels (adc0 to adc3). You do not need anything better for this experiment. Issue 2: To do something against the supply voltage fluctuations you use two channels on the ADC, one channel monitors the voltage across the NTC, and another channel monitors the voltage of a potentiometer adjusted roughly at the voltage level across the NTC. The schematic contains therefore a so-called Wheatstone bridge. Issue 3: To circumvent the self-heating problem you have to reduce the current through the NTC, I’ve put therefore the possibility to switch between four different resistors. But be careful here, you can easily damage the ADS1115 by a voltage that exceeds the limit which is depends on the gain setting of the ADS1115. It has an internal amplifier that allows to set the gain between 2/3, 1, 2, 4, 8 and 16. With a 10k NTC and a 100k resistor in series a gain of 2 allows a maximum voltage of 2048 mV with a 300 mV tolerance. As long as we don’t put the board in very cold temperatures the ADC survives. The power dissipated in the NTC remains under 20 microWatt which is a factor 30 better than a 10k resistor in series.
Single-ended or Differential?
Figure 3 shows what is measured during the experiment, the lower graph displays channel adc1 and the upper graph shows in green the adc0 measurements. As you can see, the voltage across the potentiometer fluctuates by approximately 1 millivolt for a 341 mV average, across the 25k potentiometer we get typically 15 millivolt (5000/341) which is caused by fluctuations in the supply voltage. The Arduino UNO used in this experiment obtains the supply over the USB cable, and it has an internal regulator. The 15mV is quite normal, but it is rather large for our goal. The ADS1115 designed by Adafruit has an internal regulator and filters that take away to supply voltage fluctuations. To eliminate the supply voltage noise I take the difference between channel adc0 and adc1 on the ADC. This difference doesn’t have the disadvantage that it is affected by supply voltage fluctuations, but, differential measurements introduce a bias into our problem. To circumvent the latter one takes the average value over the adc1 channel, which is then added to the differential voltage, and this is shown as the blue continuous line in the upper graph. The blue line is clearly more accurate than the green stuff on channel adc0, the blue line is the best we can do for measuring temperatures. ADC terminology: Single-ended measurements on the ADC concern a direct voltage measurement on any channel, like adc0 or adc1 directly. Differential measurements on the ADC give a direct value for the adc0-adc1 voltage. The ADS1115 library has functions that allow you to do both. So the way to go is to take a differential measurement where the potentiometer setting is somewhere near the average NTC resistance, and to add the average, or a low pass filtered value of the reference channel adc1 back in to arrive at the blue curve in figure 3.
In the end you also want to know the relation between the voltage measured across the NTC and the ambient temperature. Now, this is an external effort because there is nothing set-up within our circuit that allows me to obtain an absolute temperature. But, there is a probe from Velleman which has a digital resolution of 0.1% which is probably its precision, but not its accuracy. The best I could do at home is to compare the NTC voltages against the external thermometer readings, the set-up of the experiment and the calibration analysis results are as follows:
The external calibration is somewhat affected by the self-heating of the Arduino, but as long as the tip of the Velleman probe is close to the grey 10k NTC then it may be assumed that both refer to the same ambient temperature. The following figure shows the relation between NTC voltage and external measurement, and it turns out that there is near perfect linear relation within the temperature interval of 25 to 29C, the relation is:
T=-0.0683 U + 29.8, with U in [10,70] and T in [25,29]
where U is given in millivolts and T is in degrees Celsius. In other words, a change of 1 millivolts is equivalent to 68.3 mK. And since the ADC resolution is 0.0625 millivolt we get the internal precision of 0.0625 * 68.3 = 4.26 mK. Since NTC voltage is obtained by a differential measurement one can wonder what the possible external accuracy could be. (External in the sense that the velleman probe tells the truth). The adc1 channel has 550 samples and the noise by sample is estimate around 1 milliVolt standard deviation, the bias that we account for in the differential mode has therefore a standard deviation of 1/sqrt(550) or 0.0426 mVolt, this adds a tiny amount of noise to the calibration, in fact, it should be added to the ADC noise so that the external accuracy becomes sqrt(0.0625^2 + 0.0426^2) * 68.3 = 5.17 mK. I thought it was a pretty amazing result for a DIY experiment, we have built ourselves a 5mK thermometer which is 20 times better than the 100 mK of the Velleman probe.
Precision thermometer version 2: on a circuit board
I moved away from the #arduino proto development shield to standard proto-board with its own LCD display. The reason is that I prefer to have a separate display, and I want to be able to move around the house to increase the temperature range. The living room is 3 to 4 degrees cooler than the second floor during this hot summer of 2014. To increase stability of the resistance measurement I now use 1% tolerance metal film resistors, and I added a separate 3,3V regulator to the Wheatstone bridge (it is a le33). The gain on the #adafruit ADC is set to 1, you don’t want to increase it to avoid damage when there is no NTC in the connector block in which case it should display the output voltage of the le33 (this can be set by a jumper left of the le33, the one on the north east of the le33 is a voltage check point for the multimeter). The top line on the photo shows the resistances for the left and the right NTC, and the bottom line shows the temperatures in centi-degrees Celsius for both corresponding thermistors. The resistance measurement is calibrated with a set of metal film reference resistors. Metal film resistors increase the thermal stability of the design, I don’t really care about their resistances, as long as they are stable and independent to the current going through the resistor. The resistances shown on the top line should be better than 0,1% and they were compared to several multimeters. My 30 year old Fluke 75 that is fortunately grey rather than the ugly yellow they go for these days, and the red fancy Velleman dvm893 which was so cheap in the shop that I couldn’t leave it there. Having two multimeters is always better than 1, ok, there is somewhere a very old analog monacor on the workbench, but that one is 40 years old, and I also have somewhere one of the very early digital ones from Sinclair that I don’t take too seriously. Interchannel biases on the precision thermometer v2 can be removed by exchanging a series of matching metal film reference resistor sets on both channels, eventually you get to the <0.1% accuracy measurement on the resistances. Actually, the #adafruit ADC outperforms ALL my multimeters, but I wouldn’t sell you it as the perfect multimeter since the ADC has no protection circuitry at all. The #Velleman probe has 0,1 degree C resolution, the manual states 1 degree accuracy, yet the precision level is really a lot better I think. (I’ve put the probe in a kettle of boiling water and it arrives to within 0.1 of a degree at 100C, and the same is true for melting ice cubes, compliments Velleman, this is a brilliant design). But other evidence for the accuracy of the Velleman probe comes from the following two graphs that consist of 10 datapoints for both thermistors (red is left and blue is right to keep up with the political system). The linear regression analysis was done in matlab.
I only had to edit out 1 datapoint which was taken apparently too early in the calibration measurement series so that the Velleman probe and the NTC’s did not match. You should allow for about two minutes in a flow free environment before you take any measurements. The regression coefficients for both NTC’s match to better than 10%, the only thing that really matters is the offset between them, but both parameters can easily be determined by means of regression. My conclusion is therefore that the Velleman probe is really better than 1C accuracy that they claim in the manual (not sure where that statement comes from). Perhaps they put it in there to avoid unjustified claims. But the sensitivity of precision thermometer v2 is amazing, if you stand close to the thermistors it senses your body temperature, 0.1 of a degree increase is very easily achieved. Yesterday I’ve put the thing in a policarbonate ‘case’, that is, two pieces of polycarbonate, you mount the circuit board on the bottom plate and, a connector on the bottom one, and four separators to the top plate. This is better than carrying around an unprotected bottom site of the circuit board so that you might introduce, or worse short circuit parts on the circuit board.
My external comparison to the Velleman pin probe gave me the following model parameters for these NTCs:
- Reference resistance = R0 = 9424 Ohm
- Left offset = Rl = 25.32 C
- Right offset = Rr = 24.51 C
- Slope = alpha = -0.00256117 C/Ohm
- Accel = beta = 1.30976e-07 C/Ohm^2
- RMS of fit = 0.2856 C (refers to observed – computed)
The model is:
- x = (R-R0) where R is the observed resistance of the either the left or the right NTC.
- Left temperature: Cl = Rl + alpha * x + beta * x * x
- Right temperature: Cr = Rr + alpha * x + beta * x * x;
Four datapoints were removed from the time series because the difference was too large, and the RMS of fit is here your helping hand. Eventually the NTC calibration parameters are determined by democracy in a least squares algorithm. I suspect that it is sufficient to keep the slope and the acceleration parameter as fixed for a series of thermistors, and to determine the offset once for each NTC in a series. And as you can see, that offset is apparently depending on how the NTC was produced.
Do we need a 16 bit ADC?
You could have done the same with a 10 bit ADC which is standard on the Arduino, but this reduces the resolution of the output.
Suppose that you take a 3.3V reference on the ADC with a 10 bit sampling, that would result in 3 mV steps, and probably 6 mV steps because of the noise on the ADC (you never get to the full bit range). Steps of 6 mV will result in 0.4 degree C jumps which is maybe a bit large for the precision thermometer but good enough for the standard kitchen thermometer.
In some designs I’ve included a tuned Wheatstone bridge and an LM358 opamp to focus on the temperature range that we most use, and this is perhaps the cheaper way to accomplish the same as in this experiment. Roughly a factor 10 of amplification on a differential opamp should do the job, but be careful in scaling down the output of the LM358 to the voltage range of the Arduino ADC.
Last update: 31-7-2014 E. Schrama