Tuesday, November 17, 2015

DS18b20 temperature sensor calibration and correction

I recently bought a couple of DS18b20 temperature sensors in stainless steel probes.  Although I prefer to do AVR development using the Atmel AVR toolchain, I decided to test out the temperature sensors using the Arduino IDE.  I found a OneWire library that will use the AVR internal pullup resistor, so all I had to do was solder on some header pins and plug into one of my Pro Mini boards.

I soldered a 2-pin male header on one of the sensors, with the red power and black ground connected for parasitic power mode.  On the 2nd sensor, I connected a 3-pin header so I could use it in powered mode. I flashed NoPullupTester (after modifying it to use pin 2 for the onewire bus) to the Pro Mini, opened the serial monitor at 9600 baud, and started getting readings.  At first I thought there was something wrong, as I was getting readings in the 67-68 range.  Then I realized the output was in Fahrenheit; some Americans still dance to a different tune!  I removed the Fahrenheit conversion from the program, re-flashed the Pro Mini, and started getting results in familiar Celsius units.

I used some ice cubes and water in a coffee mug to check the calibration of the sensors.  The first problem I noticed was that the temperature readings would rise by about 0.2C once the sampling started.

I believe this was due to self-heading.  I reduced the sampling interval to every 10 seconds in order to reduce the change of self-heating.  Even then, my lowest readings were 0.06C, which seemed a bit high.  The datasheet shows a mean error of -0.14C at 0C, and I couldn't get either of my sensors to read below 0.06C.

While it is possible that both of my sensors were off from the mean error by about +0.2C, I suspected my ice/water mixture was a bit warmer than 0C.  Water's maximum density is at 3.8C, so I thought I could be getting some stratification with the water at the bottom of the mug slightly warmer than the water and ice at the top.  I then set out to find the best way to make a 0C temperature reference bath, and hit the jackpot with one of Measurement Canada's lab standards.

From previous experience with Mr. Boston, I knew that chopping ice cubes could ruin a cheap blender.  I didn't have a readily available supply of distilled water either, so I came up with another solution.  I chipped enough frost out of the freezer to fill a blender jug.  The frost is not as hard as ice, so it would be less likely to damage the blender. and since the frost forms from sublimation of water vapor, it should have few impurities.

After about 15 minutes of adding water and blending, I had a bunch of slush about the consistency of sorbet.  I drained the cold water and saved it to pre-chill the temperature probes, and transferred the slush into a dewar's flask.  After another 10 minutes I tested the temperature of the slush:

The other sensor gave similar results stabilizing at -0.19C.  I suspect my ice bath was a bit too cold.  I had added (chlorinated) municipal water to the frost in order to make the slush, which would add impurities.  Those impurities would lower the bath temperature by several millikelvins as noted by Measurement Canada.  I also hadn't maintained a full immersion of all the ice in water while blending, so some of the ice slush may not have come in contact with water long enough to raise it's temperature to 0C.  The results were good enough though; making the perfect 0.00C reference can wait for another day.

Not quite ready to finish with the temperature sensors, after looking at the mean error graph from the datasheet, I realized I could improve the accuracy in software.  The mean error looks like part of a parabolic curve, so by flattening the curve the error would be reduced.  Although a quadratic equation would be ideal, for simplicity I used a linear formula which reduces the mean error to under 0.05C from -25C to +65C:
float correctTemp(float temperature) {
  return temperature + 0.2 - (abs(temperature - 20) * .005);
}

I modified the test program to output the raw and corrected temperature,  As expected, with a raw temperature of 20.0C, the corrected temperature was 20.2C.

While the DS18B20 sensors are cheap and reasonably accurate, they have some drawbacks.  The -55C to +125C temperature range is less than diode temperature sensors that work from -65C to +200C.  The Arduino libary is not very efficient since it converts the native 7.8 bit fixed-point readings into 32-bit float.  It's quite simple to use, and given the low cost of the sensors, I think they are a great solution for remote temperature sensing.