Category Archives: Developing a TEMP ℃ chain

A string of underwater temperatures using one-wire DS18b20s

How to make Resistive Sensor Readings with DIGITAL I/O pins

AN685 figure 8: The RC rise-time response of the circuit allows microcontroller timers to be used to determine the relative resistance of the NTC element.

For more than a year we’ve been oversampling the Arduino’s humble ADC to get >16bit ambient temperature readings from a 20¢ thermistor. That pin-toggling method is simple and delivers solid results, but it requires the main CPU to stay awake long enough to capture multiple readings for the decimation step. (~200 miliseconds @ 250 kHz ADC clock) While that only burns 100 mAs per day with a typical 15 minute sample interval, a read through Thermistors in Single Supply Temperature Sensing Circuits hinted that I could ditch the ADC and read those sensors with a pin-interrupt method that would let me sleep the cpu during the process. An additional benefit is that the sensors would draw no power unless they were actively being read.

Given how easy it is drop a trend-line on your data, I’ve never understood why engineers dislike non-linear sensors so much. If you leave out the linearizing resistor you end up with this simple relationship.

The resolution of time based methods depends on the speed of the clock, and Timer1 can be set with a prescalar = 1; ticking in step with the 8 mHz oscillator on our Pro Mini based data loggers. The input capture unit can save Timer1’s counter value as soon as the voltage on pin D8 passes a high/low threshold. This under appreciated feature of the 328p is more precise than typical interrupt handling, and people often use it to measure the time between rising edges of two inputs to determine the pulse width/frequency of incoming signals.

You are not limited to one sensor here – you can line them up like ducks on as many driver pins as you have available.  As long as they share that common connection you just read each one sequentially with the other pins in input mode. Since they are all being compared to the same reference resistor, you’ll see better cohort consistency than you would by using multiple series resistors.

Using 328p timers is described in detail at Nick Gammons Timers & Counters page, and I realized that I could tweak the method from ‘Timing an interval using the input capture unit’ (Reply #12) so that it recorded only the initial rise of an RC circuit.  This is essentially the same idea as his capacitance measuring method except that I’m using D8’s external pin change threshold at 0.66*Vcc rather than a level set through the comparator. That’s almost the same as one RC time constant (63%) but the actual level doesn’t matter so long as it’s consistent between the two consecutive reference & sensor readings. It’s also worth noting here that the method  doesn’t require an ICU peripheral – any pin that supports a rising/ falling interrupt can be used. (see addendum for details) It’s just that the ICU makes the method more precise which is important with small capacitor values. (Note that AVRs have a Schmitt triggers on  the digital GPIO pins. This is not necessarily true for other digital chips. For pins without a Schmitt trigger, this method may not give consistent results)

Using Nicks code as my guide, here is how I setup the Timer1 with ICU:

#include <avr/sleep.h>              //  to sleep the processor
#include <avr/power.h>            //  for peripherals shutdown
#include <LowPower.h>            //

float referencePullupResistance=10351.6;   // a 1% metfilm measured with a DVM
volatile boolean triggered;
volatile unsigned long overflowCount;
volatile unsigned long finishTime;

ISR (TIMER1_OVF_vect) {  // triggers when T1 overflows: every 65536 system clock ticks

ISR (TIMER1_CAPT_vect) {     // transfers Timer1 when D8 reaches the threshold
unsigned int timer1CounterValue = ICR1;       // Input Capture register (datasheet p117)
unsigned long overflowCopy = overflowCount;

if ((TIFR1 & bit (TOV1)) && timer1CounterValue < 256){    // 256 is an arbitrary low value
overflowCopy++;    // if “just missed” an overflow

if (triggered){ return; }  // multiple trigger error catch

finishTime = (overflowCopy << 16) + timer1CounterValue;
triggered = true;
TIMSK1 = 0;  // all 4 interrupts controlled by this register are disabled by setting zero

void prepareForInterrupts() {
noInterrupts ();
triggered = false;                   // reset for the  do{ … }while(!triggered);  loop
overflowCount = 0;               // reset overflow counter
TCCR1A = 0; TCCR1B = 0;     // reset the two (16-bit) Timer 1 registers
TCNT1 = 0;                             // we are not preloading the timer for match/compare
bitSet(TCCR1B,CS10);           // set prescaler to 1x system clock (F_CPU)
bitSet(TCCR1B,ICES1);          // Input Capture Edge Select ICES1: =1 for rising edge
// or use bitClear(TCCR1B,ICES1); to record falling edge

// Clearing Timer/Counter Interrupt Flag Register  bits  by writing 1
bitSet(TIFR1,ICF1);         // Input Capture Flag 1
bitSet(TIFR1,TOV1);       // Timer/Counter Overflow Flag

bitSet(TIMSK1,TOIE1);   // interrupt on Timer 1 overflow
bitSet(TIMSK1,ICIE1);    // Enable input capture unit
interrupts ();

With the interrupt vectors ready, take the first reading with pins D8 & D9 in INPUT mode and D7 HIGH. This charges the capacitor through the reference resistor:

//========== read 10k reference resistor on D7 ===========

power_timer0_disable();    // otherwise Timer0 generates interrupts every 1us

pinMode(7, INPUT);digitalWrite(7,LOW);     // our reference
pinMode(9, INPUT);digitalWrite(9,LOW);     // the thermistor
pinMode(8,OUTPUT);digitalWrite(8,LOW);  // ground & drain the cap through 300Ω
LowPower.powerDown(SLEEP_30MS, ADC_OFF, BOD_ON);  //overkill: 5T is only 0.15ms

pinMode(8,INPUT);digitalWrite(8, LOW);    // Now pin D8 is listening

set_sleep_mode (SLEEP_MODE_IDLE);  // leaves Timer1 running
prepareForInterrupts ();
noInterrupts ();
DDRD |= (1 << DDD7);          // Pin D7 to OUTPUT
PORTD |= (1 << PORTD7);    // Pin D7 HIGH -> charging the cap through 10k ref

interrupts ();
sleep_cpu ();                //sleep until D8 reaches the threshold voltage
noInterrupts ();
}while(!triggered);      //trapped here till TIMER1_CAPT_vect changes value of triggered

sleep_disable();           // redundant here but belt&suspenders right?
interrupts ();
unsigned long elapsedTimeReff=finishTime;   // this is the reference reading

Now discharge and then repeat the process a second time with D7 & D8 in INPUT mode, and D9 HIGH to charge the capacitor through the thermistor:

//==========read the NTC thermistor on D9 ===========

pinMode(7, INPUT);digitalWrite(7,LOW);     // our reference
pinMode(9, INPUT);digitalWrite(9,LOW);     // the thermistor
pinMode(8,OUTPUT);digitalWrite(8,LOW);  // ground & drain the cap through 300Ω
LowPower.powerDown(SLEEP_30MS, ADC_OFF, BOD_ON);

pinMode(8,INPUT);digitalWrite(8, LOW);    // Now pin D8 is listening

set_sleep_mode (SLEEP_MODE_IDLE);
prepareForInterrupts ();
noInterrupts ();
DDRB |= (1 << DDB1);          // Pin D9 to OUTPUT
PORTB |= (1 << PORTB1);    // set D9 HIGH -> charging through 10k NTC thermistor

interrupts ();
sleep_cpu ();
noInterrupts ();

interrupts ();
unsigned long elapsedTimeSensor=finishTime;   //this is your sensor reading

Now you can determine the resistance of the NTC thermistor via the ratio:

unsigned long resistanceof10kNTC=
(elapsedTimeSensor * (unsigned long) referencePullupResistance) / elapsedTimeReff;

pinMode(9, INPUT);digitalWrite(9,LOW);pinMode(7, INPUT);digitalWrite(7,LOW);
pinMode(8,OUTPUT);digitalWrite(8,LOW);  //discharge the capacitor when you are done

The integrating capacitor does a fantastic job of smoothing the readings and getting the resistance directly eliminates 1/2 of the calculations you’d normally do with a thermistor. To figure out your constants, you need to know the resistance at three different temperatures. These should be evenly spaced and at least 10 degrees apart with the idea that your calibration covers the range you expect to use the sensor for. I usually put loggers in the refrigerator & freezer to get points with enough separation from normal room temp with the thermistor taped to the surface of a si7051.  Then plug those values into the thermistor calculator provided by Stanford Research Systems.

Just for comparison I ran a few head-to-head trials against my older dithering/oversampling method:

°Celcius vs time [1 minute interval]  si7051 reference [0.01°C 14-bit resolution, 10.8 msec/read ] vs. Pin-toggle Oversampled 5k NTC vs. ICUtiming 10k NTC.   I’ve artificially separated these curves for visual comparison, and the 5K was not in direct contact with the si7051 ±0.1 °C accuracy reference, while the 10k NTC was taped to the surface of chip – so some of the 5ks offset is an artifact. The Timer1 ratios delver better resolution than 16-bit (equivalent) oversampling in 1/10 the time.

There are a couple of things to keep in mind with this method:

si7051 reference temp (C) vs 10k NTC temp with with a ceramic 106 capacitor. If your Ulong calculations overflow at low temperatures like the graph above, switch to doing the division before the multiplication or use larger ‘long-long’ variables. Also keep in mind that the Arduino will default to 16bit calculations unless you set/cast everything to longer ints Or you could make you life easy and save the raw elapsedTimeReff & elapsedTimeSensor values and do the calculations later in Excel. Whenever you see a sudden discontinuity where the result of a calculation suddenly takes a big jump to larger or smaller values – then you should suspect a variable type/cast error.

1) Because my Timer1 numbers were small with a 104 cap I did the multiplication before the division. But keep in mind that this method can easily generate values that over-run your variables during calculationUlong MAX is 4,294,967,295  so the elapsedTimeSensor reading must be below 429,496 or the multiplication overflows with a 10,000 ohm reference. Dividing that by our 8mHz clock gives about 50 milliseconds. The pin interrupt threshold is reached after about one rise-time constant so you can use an RC rise time calculator to figure out your capacitors upper size limit. But keep in mind that’s one RC at the maximum resistance you expect from your NTC – that’s the resistance at the coldest temperature you expect to measure as opposed to its nominal rating.  (But it’s kind of a chicken&egg thing with an unknown thermistor right?  See if you can find a manufacturers table of values on the web, or simply try a 0.1uF and see if it works).  Once you have some constants in hand Ametherm’s Steinhart & Hart page lets you check the actual temperature at which your particular therm will reach a given resistance.  Variable over-runs are easy to spot because the problems appear & then disappear whenever some temperature threshold is crossed. I tried to get around this on a few large-capacitor test runs by casting everything to float variables, but that lead to other calculation errors.

(Note: Integer arithmetic on the Arduino defaults to 16 bit & never promotes to higher bit calculations, unless you cast one of the numbers to a high-bit integer first. After casting the Arduino supports 64-bit “long long” int64_t & uint64_t integers for large number calculations but they do gobble up lots of program memory space – typically adding 1 to 3k to the compiled size. Also Arduino’s printing function can not handle 64 bit numbers, so you have to slice them into smaller pieces before using any .print functions

2) This method works with any kind of resistive sensor, but if you have one that goes below ~200 ohms (like a photoresistor in full sunlight) then the capacitor charging could draw more power than you can safely supply from the D9 pin.  In those cases add a ~300Ω resistor in series with your sensor to limit the current, and subtract that value from the output of the final calculation. At higher currents you’ll also have voltage drop across the mosfets controlling the I/O pins (~40Ω on a 3.3v Pro Mini), so make sure the calibration includes the ends of your range.

There are a host of things that might affect the readings because every component has  temperature, aging, and other coefficients, but for the accuracy level I’m after many of those factors are absorbed into the S&H coefficients. Even if you pay top dollar for reference resistors it doesn’t necessarily mean they are “Low TC”. That’s why expensive resistors have a temperature compensation curve in the datasheet. What you’re talking about in quality references is usually low long-term drift @ a certain fixed temperature (normally around 20 ~ 25°C) so ‘real world’ temps up at 40°C are going to cause accelerated drift.

The ratio-metric nature of the method means it’s almost independent of value of the capacitor, so you can get away with a cheap ceramic cap even though it’s value changes dramatically with temperature. (& also with DC bias ) In my tests thermal variation of a Y5V causes a delta in the reference resistor count that’s about 1/3 the size of the delta in the NTC thermistor.  Last year I also found that the main system clock was variable to the tune of about 0.05% over a 40°C range, but that shouldn’t be a problem if the reference and the sensor readings are taken immediately after one another. Ditto for variations in your supply. None of it matters unless it affects the system ‘between’ the two counts you are using to make the ratio.

The significant digits from your final calculation depend on the RC rise time, so switching to 100k thermistors increases the resolution, as would processors with higher clock speeds.  You can shut down more peripherals with the PRR to use less power during the readings as long as you leave Timer1 running with SLEEP_MODE_IDLE.  I’ve also found that cycling the capacitor through an initial charge/discharge cycle (through the 300Ω on D8) improved the consistency of the readings. That capacitor shakedown might be an artifact of the ceramics I was using but you should take every step you can to eliminating errors that might arise from the pre-existing conditions. I’ve also noticed that the read order matters, though the effect is small.

Code consistency is always important with time based calibrations no matter how stable your reference is. Using a smaller integrating capacitor makes it more likely that your calibration constants will be affected by variations in code execution time. Any software scheme is going to show some timing jitter because both interrupts and the loop are subject to being delayed by other interrupts. Noise on the rail from a bad regulator will directly affect your readings. Using a larger 1uF (105) capacitor is a safer option than a 104. This method bakes a heap of small systemic errors into those NTC calibration constants, and this approach works because most of those errors are thermal variations too. However code-dependent variations mess with the fit of the thermistor equation as they tend to be independent of temperature, so make sure the deployment uses EXACTLY the same code that you calibrated with. We are passing current through the thermistor to charge the capacitor so there will inevitably be some self heating – if your calibration constants were derived with 1-pass, then your deployment must also read only one pass. If you calibrate with a 104 cap & then switch to a 105, then the temps recorded with the larger 105 will be offset to higher than actual. Oversampling works fine to boost resolution, but since it leverages multiple passes that also cause much more self heating.

Will this method replace our pin-toggled oversampling?  Perhaps not for something as simple as a thermistor since that method has already proven itself in the real world, and I don’t really have anything better to do with A6 & A7. And oversampling still has the advantage of being simultaneously available on all the analog inputs, while the ICU is a limited resource.  Given the high resolution that’s potentially available with the Timer1/ICU combination, I might save this method for sensors with less dynamic range.  I already have some ideas there and, of course, lots more testing to do before I figure out if there are other problems related to this new method.  I still haven’t determined what the long-term drift is for the Pro Mini’s oscillator, and the jitter seen in the WDT experiment taught me to be cautious about counting those chickens.

Addendum:   Using the processors built in pull-up resistors

After a few successful runs, I realized that I could use the internal pull-up resistor on D8 as my reference; bringing it down to only three components. Measuring the resistance of the internal pull-up is simply a matter of enabling it and then measuring the current that flows between that pin and ground. I ran several tests, and the Pro Mini’s the internal pullups were all close to 36,000 ohms, so my reference would become 36k + the 300Ω resistor needed on D8 to safely discharge of the capacitor between cycles. I just have to set the port manipulation differently before the central do-while loop:

PORTB |= (1 << PORTB0);     //  enable pullup on D8 to start charging the capacitor

A comparison of the two approaches:

°Celcius vs time: (Post-calibration, 0.1uF X7R ceramic cap)  si7051 reference [±0.01°C] vs. thermistor ICU ratio w 10k reference resistor charging the capacitor vs. the same thermistor reading calibrated using the rise time through pin D8’s internal pull-up resistor. The reported ‘resistance’ value of the NTC themistor was more than 1k different between the two methods, with the 10k met-film reference providing values closer to the rated spec. However the Steinhart-Hart equation constants from the calibration were also quite different, so the net result was indistinguishable between the two references in the room-temperatures range.

In reality, the pull-up “resistor” likely isn’t a real resistor at all, but an active device made out of transistor(s) which looks like a resistor when operated in its active region. I found the base-line temperature variance to be about 200 ohms over my 40°C calibration range. And because you are charging the capacitor through the reference and through the thermistor, the heat that generates necessarily changes those values during the process.  However when you run a calibration, those factors get absorbed into the S&H coefficients provided you let the system equilibrate during the run.

As might be expected, all chip operation time affects the resistance of the internal pull-up, so the execution pattern of the code used for your calibration must exactly match your deployment code or the calibration constants will give you an offset error proportional to the variance of the internal pull-up caused by the processors run-time. Discharging the capacitor through D8, also generates some internal heating so those (~30-60ms) sleep intervals also have to be consistent.  In data logging applications you can read that reference immediately after a long cool-down period of processor sleep and use the PRR to reduce self-heating while the sample is taken.

Another issue was lag because that pull-up is embedded with the rest of the chip in a lump of epoxy. This was a pretty small, with a maximum effect less than ±0.02°C/minute and I didn’t see that until temperatures fell below -5 Celsius.  Still, for situations where temperature is changing quickly I’d stick with the external reference, and physically attach it to the thermistor so they stay in sync.

Addendum:   What if your processor doesn’t have an Input Capture Unit?

With a 10k / 0.1uF combination, I was seeing Timer1 counts of about 5600 which is pretty close to one 63.2% R * C time constant for the pair.  That combination limits you to 4 significant figures and takes about 2x 0.7msec per reading on average.  Bumping the integrating capacitor up to 1uF (ceramic 105) multiplies your time by a factor of 10 – for another significant figure and an average of ~15msec per paired set readings.  Alternatively, a 1uF or greater capacitor allows you to record the rise times with micros()  (which counts 8x slower than timer1) and still get acceptable results. (even with the extra interrupts that leaving Timer0 running causes…) So the rise-time method could be implemented on processors that lack an input capture unit – provided that they have Schmitt triggers on the digital inputs like the AVR which registers a cmos high transition at ~0.6 * Vcc.

void d3isr() {
triggered = true;

pinMode(7, INPUT);digitalWrite(7,LOW);     // reference resistor
pinMode(9, INPUT);digitalWrite(9,LOW);     // the thermistor
pinMode(3,OUTPUT);digitalWrite(3,LOW);  // ground & drain the cap through 300Ω
LowPower.powerDown(SLEEP_30MS, ADC_OFF, BOD_ON);  // 5T is only 1.5ms w 10k

pinMode(3,INPUT);digitalWrite(3, LOW);    // Now pin D3 is listening

triggered = false;
set_sleep_mode (SLEEP_MODE_IDLE);  // leave micros timer0 running for micros()
unsigned long startTime = micros();
noInterrupts ();
attachInterrupt(1, d3isr, RISING); // using pin D3 here instead of D8
DDRD |= (1 << DDD7);          // Pin D7 to OUTPUT
PORTD |= (1 << PORTD7);    // Pin D7 HIGH -> charging the cap through 10k ref

interrupts ();
sleep_cpu ();
noInterrupts ();

interrupts ();
unsigned long D7riseTime = micros()-startTime;

Then repeat the pattern shown earlier for the thermistor reading & calculation. I’d probably bump it up to a ceramic 106 for the micros method just for some extra wiggle room. The method doesn’t really care what value of capacitor you use, but you have to leave more time for the discharge step as the size of your capacitor increases. Note that I’m switching between relatively slow digital writes (~5 µs each) outside the timing loop, and direct port manipulation (~200 ns each) inside the timed sequences to reduce that source of error.

Addendum 20191020:

After running more tests of this technique, I’m starting to appreciate that even on regulated systems, you always have about 10-30 counts of jitter in the Timer1 counts, even with the 4x input capture filter enabled.  I suspect this is due to the Shimitt triggers on the digital pins also being subject to noise/temp/etc and because other system interrupts find a way to sneak in. A smaller 104 integrating capacitor you make your readings 10x faster, but the fixed jitter error is a correspondingly larger percentage of that total reading (100nF typically sees raw counts in the 3000 range for the 10k reference). By the time you’ve over-sampled 104 capacitor readings up to a bit-depth equivalent of the single-pass readings with the 105 ceramic capacitor ( raw counts in the 60,000 range for the same 10k ref), you’ve spent about the same amount of run-time getting to that result. (Keep in mind that even with a pair of single-pass readings using the 10k/104 capacitor combination; raw counts of ~3500 yield a jitter-limited thermistor resolution of about 0.01C)

So, as a general rule of thumb, if your raw Timer1 counts are in the 20,000-60,000 range, you get beautiful smooth results no matter what you did to get there. This translates into about 2.5 – 7.5 milliseconds per read, and this seems to be a kind of ‘sweet-spot’ for ICU based timing methods because the system’s timing jitter error is insignificant at that point. With 5 significant figures in the raw data, the graphs are so smooth they make data from the si7051 reference I’m using look like a scratchy mess.

Another thing to watch out for is boards using temperature compensated oscillators for their system clock. ICU methods work better with the crappy ceramic oscillators on clone boards because their poor thermal behavior just gets rolled into the thermistors S&H constants during calibration. However better quality boards like the 8mhz Ultra from Rocket Scream have compensation circuits that kick in around 20C, which puts a weird discontinuity into the system behavior which can not be gracefully absorbed by the thermistor constants.  So the net result is that you get worse results from your calibration with boards using temperature compensation on their oscillators.

The thermistor constants also neatly absorb the tempco and even offset errors in the reference resistor. So if you are calibrating a thermistor for a given logger, and it will never be removed from that machine, you can set your reference resistor in the code to some arbitrary perfect value like 10,000 ohms, and just let the calibration push any offset between the real reference and your arbitrary value into the S&H constants. This lets you standardize the code across multiple builds if you are not worried about  ‘interchangeability’.

And finally, this method is working well on unregulated systems with significant battery supply variations as I test the loggers down to -15C in my freezer.  In addition to battery droop, those cheap ceramic caps have wicked tempcos, so the raw readings from the reference resistor are varying dramatically during these tests, but the ‘Ratio/Relationship’ of the NTC to this reference is remaining stable over a 30-40C range, with errors in the ±0.1°C range, relative to the reference. (Note: si7051 itself has ±0.13°C, so the net is probably around ±0.25°C)

Addendum 20201011:

Re: the temperature coefficient of resistance (TCR) of your reference resistor:

“Using a thin film resistor at ±10 ppm/°C would result in a 100 ppm (0.01%) error if the ambient changes by only 10°C. If the temperature of operation is not close to the midpoint of the temperature range used to quantify the TCR at ±10 ppm/°C, it would result in a much larger error over higher temperature ranges. A foil resistor would only change 0.0002% to 0.002% over that same 10°C span, depending upon which model is used (0.2 ppm/°C to 2 ppm/°C.) And for larger temperature spans, it would be even more important to use a resistor with an inherently low TCR.”

During calibration, I usually bake the reference resistor’s tempco into the thermistor constants by simply assuming the resistor is a ‘perfect 10k ohm’ during all calculations. (this also makes the code more portable between units)  However this does nothing to correct long-term drift in your reference.  If you want to tackle that problem with a something like a $10 Vishay Z-foil resistor (with life stability of ± 0.005 %) then it’s probably also worth adding Plastic film capacitors which have much better thermal coefficients: Polyphenylene sulfide (PPS ±1.5%) or Polypropylene (CBB or PP ±2.5%)A quick browse around the Bay shows those are often available for less than $1 each, and the aging rate (% change/decade hour) for both of those dielectrics is listed as negligible. The trade off is that they are huge in comparison to ceramics, so you are not going to just sneak one in between the pins on your pro-mini. Be sure to check the rated voltage – and don’t order them if they are rated >100v as the common 650v film caps are too big to be practical on small logger builds.  For the coup de grâce you could correct away the system clock variation by comparing it to the RTC

Addendum 2020-03-31:  Small capacitors make this method sensitive to a noisy rail

After a reasonable number of builds I have finally identified one primary cause of timing jitter with this technique: a noisy regulator. To get sleep current down I replace the stock MIC5205’s on clone ProMini boards with MCP1700’s and I noticed a few from a recent batch of loggers were producing noisy curves on my calibration runs. One of them was extreme, creating >0.5°C of variation in the record:

ICU based Thermistor readings VS si7051 reference. Sensors in physical contact with each other. Y axis = Celsius

But in the same batch, I had others thermistors with less noise than the si7051’s I was using as a reference. All were using small 104 ceramic capacitors for the integration, producing relatively low counts (~3500 clock ticks) on the 10k reference resistor.

For the unit shown above I removed the regulator, and re-ran the calibration using 2x Lithium AA batteries in series to supply the rail voltage. No other hardware was changed:

Same unit, after regulator was removed. Samples taken at 1 minute interval on both runs.

In hindsight, I should have guessed a bad regulator was going to be a problem, as few other issues can cause that much variation in the brief interval between the reference resistor & thermistor readings. Reg. noise/ripple translates instantaneously into a variation in the Schmitt trigger point on the input pin – which affects the ICU’s timer count. It’s possible that this issue could be eliminated with more smoothing so I will try a few caps across the rails on the less problematic units. (~1000µF/108 Tantalums can be found for 50¢ on eBay but I will start with a 10µF/106 & work up from there)

Addendum 2020-04-05:  106 ceramic across rails increased ICU reading noise (w bad reg)

Adding a cheap 106 (Y5V ceramic) across the rails more than doubled the noise in the readings of the NTC with this ICU technique.  This is interesting, as it goes completely against what I was expecting to happen.  Possibly that 10µF cap was just badly sized for this job or had some other interaction via inductance effects that actually accentuated the noise? I probably need a smaller, faster cap for the job.

Changing the sampling capacitor from a 104 (0.1µF) , to a 105 (1µF) dramatically reduced the problem. Not surprising as the rail noise from the regulator is relatively consistent, while the reference timer counts change from ~3500 with a 104 capacitor to ~60,000 with the larger 105. So the jitter is still there, but it is proportionally much smaller. I’m currently re-running the thermistor calibration with that larger capacitor. If the gods are kind, the S&H thermistor constants will be the same no matter what sampling capacitor is used.

It’s worth noting that this issue only appeared with the most recent crop of crappy eBay regulators. But if you are sourcing parts from dodgy vendors, you’d best go with a 105 sampling capacitor right from the start to smooth out that kind of noise

Addendum 2020-04-06:  Re-use old S&H constants after changing the sample capacitor?

After discovering the reg. issue, I re-ran a few thermistor calibrations once the sampling capacitor had been changed from a 104 to a 105:  This reveals that the thermistor constants obtained with a 104 sampling capacitor, still work, but it depends on the tolerance you are aiming for: with the older 104 cap constants drifting over a 40°C range by about ±0.3 Celsius. The extra resolution provided by the larger 105 cap is only useful if you have the accuracy to utilize it (ie: It doesn’t matter if the third decimal point is distinguishable of the whole number is wrong) I generally aim for a maximum of ±0.1°C over that range, so for our research loggers that’s a complete do-over on the calibration. From now on I will only use 105 caps (or larger) with this ICU technique on regulated systems. The battery-only units were smooth as silk with smaller 104 caps because the rail had zero noise.

Addendum 2020-05-21: Using the ADS1115 in Continuous Mode for Burst Sampling

For single resistive sensors, it’s hard to beat this ICU method for elegance & efficiency. However there’s still one sensor situation that forces me to go to an external ADC module: Differential readings on bridge sensors.  In those cases I use an ADS1115 module, which can also generate interrupt alerts for other tricks like ‘event’ triggered sampling.

Addendum 2021-01-24        Don’t sleep during the do-while loop with regular I/O pins.

I’ve been noodling around with other discharge timing methods and come across something  that’s relevant to using these methods on other digital pins. Here’s the schematic from the actual 328 datasheet, with a bit of highlighting added. The green path is PINx.  It’s always available to the databus through the synchronizer (except for in SLEEP mode?) The purple path is PORTx.   Whether or not it is connected to PINx depends on the state of DDRx (which is the yellow path.)

As shown in the figure of General Digital I/O, the digital input signal can be clamped to ground at the input of the Schmitt Trigger. The signal denoted SLEEP in the figure, is set by the MCU Sleep Controller in Power-down mode and Standby mode to avoid high power consumption if some input signals are left floating, or have an analog signal level close to VCC/2.

When sleeping, any GPIO that is not used an an interrupt input has its input buffer disconnected from the pin and in clamped LOW by the MOSFET.

Clearly the ICU on D8 must make it one of those ‘interrupt exceptions’ or the cap charging method would have been grounded out when entering the sleep state.  If you use a similar timing method on normal digital IO pins you can’t sleep the processor in the central do-while loop.

The datasheet also warns that on pins where this is overridden, such as external interrupts, you must ensure these inputs don’t float (page 53). The implication being that when you put anything near 1/2 of the rail voltage on the input’s Schmitt trigger this will cause current to flow into or out of the pin. Yet another source of error that gets rolled into those catch-all S&H constants.

Addendum 2022-03-09: Adding a second resistive sensor

The LDR goes to very large resistance at night, so we cap the reading at one timer overflow.

We usually integrate this ICU method with our low-power 2-part loggers as they have only a single Cr2032 as their power supply. Once the pins are pulled low these resistive sensors add nothing to the sleep current. With the stability of battery power, a 104 is sufficient for stable readings of about 6000 counts for 10k ohms.
For more details see: Powering a ProMini logger for one year on a coin cell.

Field Report 2016-03-27: Progress on the 1-Wire DS18B20 Temperature Strings

Happy to report the successful deployment of three more temperature strings:


and I think it’s fair to say the first two protoypes have almost run their course:

Logger/Sensors Time/Max.Depth Comment
#45  (19 x 25cm)
[1st build]
Full data from Temp chain, Qsil problem on pressure sensor
         (19 x 25cm) 201508-12
Wire break on 1st segment before deployment: total read failure on temps, pressure data OK
         (11 x 25cm) 201512-1603
Full record including pressure. Segment wire broke during retrieval. Brought home for refurb.
#46  (20 x 50cm)
[2nd build]
Full temp record. Pressure record problem.
         (20 x 50cm) 201508-12
Full temp record. Pressure OK after Qsil removed.
        (9 x 50cm) 201512-1603
3rd segment wire break during deployment dive. Nine sensors report OK for duration. Pressure OK. Failed segment removed & unit re-deployed.
#78  (24 x 100cm)
Complete data record. Unit redeployed.
#79  (24 x 25cm,  & 10m extension)
Wire break during deployment dive. Full logs saved but no data. Failed segment removed & unit re-deployed w 18 nodes

These internal wire failures did not compromise the integrity of the outer jacket.

Fortunately none of these internal wire failures compromised the integrity of the outer jacket. These are only 7-strand wires, and I will be hunting for more flexible 19 strand replacements.

While it’s hard to see all that work deliver only a few successful deployments, I’m happy to note the failures were all at physical pinch points in the cable, with no other apparent problems on the housings or electronics.  And even when the wires break, the logger itself keeps chugging along: saving logs full of 1360 & -1 read errors.  Sensor problems like this often take a whole logger down, but the MS5803’s delivered data through every deployment, so failures on the one wire bus seem to be isolated from the rest of the sensors on the logger.


Though it was always part of the plan, actually pulling off segment swaps in real time was pretty cool…

For existing builds, I reenforced the weak points with mastic tape

For the units in the field, I’ve re-enforced the weak points with mastic tape & cable ties to limit bending.

This is good news for me because physical problems are generally the easiest ones to fix…

Up to this point I’ve been using soft silicone jacket cable which is lovely to work with, but I will have to build the next set from sterner stuff.

Of course that means we might have to develop a new set of deployment procedures,  because handling 24m of stiff cable could be a challenge.  These deployments have already been some of the toughest dives on the project, especially when you get into the low-visibility hydrogen sulfide soup that Trish is so fond of…


The second generation builds also used less power than the first:

Much better power performance on the second generation of temp strings.

The battery curve is still a bit crunchy, but it looks like it might settle to about 100 mV drop per month at a 15 minute interval on 2x3AA’s.  That’s nearly the same as the first two loggers which had twice as many cells.  One of the things you notice about cheap DS18b20’s from ebay, is that they can draw dramatically different sleep currents.  This may be compounded by the fairly aggressive 2k2 pullup I use on the bus.


Spotted in Tulum:


Just finding a problem doesn’t always tell you how to fix it…

DS18B20 1-Wire calibration with Arduino: Finally nailed it!

Dry well insert for DS18b20 calibrations

The Thermapen tip fits reasonably well into a 9/64″ hole, and that size also makes a good pilot hole for the 1/4 inch DS18 wells. The travel on my little bench-top drill press is only two inches, but that turned out to be the perfect depth.  The whole job took about 4 hours, with less colorful language than I was expecting.

The high residuals I was seeing up at 40°C, and the dubious waterproofing on those cheap DS18b20‘s convinced me that I needed a dry well for the calibrations. Since lead has a thermal conductivity 100x better than water, my first though was to populate a tin can with short lengths of copper pipe and fill the spaces in between from my solder pot.  But I was unable to find exactly the right internal diameter of piping for the tight fit I wanted around those sensors, and the charts indicated that aluminum would give me 1/2 an order of magnitude more thermal distribution.  So despite the fact that I had no experience working metal like this, I ordered a 4″ piece of lathe bar stock from eBay (~$15).




Go to a 530GPH pump if you have a larger water bath. The aluminum and stainless steel contact point suffered a bit of galvanic corrosion at first, but I separated them with a plastic plate and it seemed fine after that.

It took me a while figuring out how to lubricate the bits: basically you can never have too much cutting fluid at small diameters, but you can use the big 1/4″ bits ‘dry’ once the pilot holes are in. Just lift the bits frequently to clear the shavings so things don’t heat up and bind. And yes, new high-temp drill bits will cost you more than the aluminum rod – but it’s worth it. The 3″ diameter rod gave me enough room for 38 DS18’s with a few different locations for the Reference Thermapen probe. The next addition to the process was a 1200L/h aquarium pump (~$8), which created a vigorous circulation in the five liter reservoir.  So now I have a setup were the clunky old water bath provides a big lump of passively cooling thermal mass, while the sensors and my reference thermometer are held together by the conductivity of the aluminum block.

The full DS18b20 calibration rig, with ~10x1m sensors in what is essentially a star configuration via the intermediate breadboard. 2k2 pullup resistor.

With those new additions, re-running the 100-ish sensors I had on hand produced curves so smooth that it was easy to spot my own procedural errors ( when I did not wait long enough for the Thermapen to equilibrate ) because the DS18 curves had considerably less of the random scatter I had seen in the water-only runs:


After removing the bad reference readings I generated the same series of y=mx+b fit equations as before, only this time I did not use a graph trend line to generate the constants (described in the previous post) but instead used the slope & intercept functions in excel. This lets you copy and paste the equations from one data set to the next with a macro which dramatically speeds up the process when you are chugging through more than thirty sensors at a time. It also adjusts & recalculates dynamically when you delete a row. Convert your reference thermoenter readings from Celsius into the equivalent 12-bit DS18b20 reading by dividing by the DS18’s resolution of 0.0625C

Now that the sensors were really were being exposed to the same temperature, I could use the raw and corrected residuals to prune the reference data even more by rejecting any single reading anomalies that made it through the first pass: ( residual(s) = reference – DS18 reading)


It’s quite an improvement to be able to use the DS18 output to polish the set because in previous tests (without the dry well) the high temperature residuals were so variable that it would have completely masked this kind of problem.

Using the groomed reference points,  it’s easy to see which sensors make the cut:

Criterion used to select sensors for DS18b20 sensor strings

With LSB points on the y axis of those graphs, the corrected residuals for a good sensor fall into a tight band between ±0.5 LSB. The bad sensor on the right has a huge spread, and was immediately tossed in the garbage.  In theory at least, I’m achieving calibration accuracy better than the sensor resolution of ±0.0625°C.  It would be a bit bravo to claim that, but I am confident that we reached the ±0.1°C target that I wanted for our field sensors, without doing a full laboratory level temperature sweep

Grouping the DS18b20 sensors into more refined categories

With only one or two exceptions, these sensors under-read. Of about 100, the largest group was (-3 to -5) LSB points with 12 sensors, followed by (-4 to -6) with 11 sensors. A small handful of sensors had consistent offsets with no slope, and only two sensors had positive slopes in their raw residuals. Overall, I rejected about 30% of them for one reason or another. To get really sweet sets, it’s a good idea to start your project with about twice as many sensors as you think you are going to need, from 3 or 4 different vendors. (though the ones from Electrodragon provided the highest yield…)

Another benefit of all this work is that I can now group my sensors using the raw residual trend-line values at both 20 & 30°C. (see top left graph for #328 above) This captures both the offset and the slope information, and by matching slopes I can assemble strings of sensors who’s uncorrected behavior does not diverge.  With the calibration corrections already in hand this might seem unnecessary, but experience has shown that being able to quickly spot trends in the raw data can be very handy in the field when you need to make on-the-spot decisions about a deployment.  I can also add that the sensors from different suppliers showed strong clustering in these groups, implying that production run biases would introduce an offset into group normalization methods (ie: without a reference thermometer to compare them to) unless you bought them from several sources.

So our temperature chain system is maturing nicely, and in a pattern that is becoming familiar, it took three generations to reach the point where I think my DIY builds could be compared to commercial kit without looking too shabby:

As time goes on I am reducing the number of interconnects, but even with longer chain segments I will probably stick with only 24 sensors per logger.

As time goes on I am reducing the number of interconnects to improve reliability. But even with the chain segments sporting more sensors, I only put twenty four nodes on a given logger body.    As my wife already calls the all white flow sensors ‘storm-troopers’, I added a touch of ‘vader black to spruce up the housing.   Hopefully, the force is strong in this one  🙂

Of course these guys still have to prove they can last a year under water, so for now we will continue with dual deployments (which we usually use for new prototypes) in case one drops out on us.  

I’ve only got solid power usage data from one of my 24 sensor chains at the moment as everything else is still out on deployment. On those early units, I had no idea what the total usage would be so I just packed in 12 AA’s. (4 banks x 3 in series) After the over-voltage burn down, the discharge curve settled to a steady 100mV drop per month. That unit had 24 sensors, taking 12-bit samples every 15 minutes. My current builds have only 2x 3AA banks, so I expect them to drop by about 200 mV per month. A low input cutoff of about 3.4v on the regulator means that I will probably get 5-6 months out of a fresh set of six AA’s.  Driving the DS18’s at their maximum resolution (ie: 24 x 1.5mA x 750ms =~27 mAs /sample), I think we would only make a year on 3AA’s if I moved the interval out to once per hour.

With respect to quality, the real test will come in post deployment testing when we find out how much these sensors drift over time.  And I am really keen to see how the units we deployed across that hydrogen sulfide layer performed, as that chemistry is far from benign.  If they prove hardy enough, we might even bury a few and see how that goes.  In harsh environments, every choice you make in the build is a cost/time-to-failure trade-off.

Addendum 2016-03-07

[jboyton] over at the sensors forum pointed out that you could save the $200 cost of the Thermapen by using an Ultra Precision Thermistor from U.S.Sensor that offers ±0.05°C accuracy right out of the box. These 10K NTC units can be purchased from Digikey for ~$10 each. You could epoxy that sensor into the same stainless steel sleeves used on the DS18’s and be ready to go. The cool thing about this idea is that the reference readings could then be taken on a logger, avoiding those mistake-prone manual readings. Definitely going to look into this …

Addendum 2016-03-10

I’m doing post assembly water bath checks on the T-chains now. I had some lingering questions about whether epoxy contraction, or the heat I used in the process of making the string nodes, would throw off the calibration:

#74 was included in this group by accident....grrr

#74 was included in this group by accident…

You can see how closely matching the 20 & 30°C offsets groups the raw readings into a fairly tight 3-point band even though the sensors are now distributed around the water bath.   Applying the linear correction reduces that to 2pts, and shifts them all up towards true temp.  With the spread approaching the bit-toggling level I suspect I’ve reached the point of diminishing returns, so I’m calling this set ready to deploy without further normalization.

Addendum 2016-03-11

Segments 19 & 20 were constructed with some of the most stable sensors from that round of calibration, at least with regard to the corrected residuals.  But I did not have any other groups large enough to build the next few sets from DS18’s with identical performance,  so I loosened the criterion out to ±1 LSB, and started combining different offset groups on the same chain (mixing the sensors randomly so no trends would be introduced…)

The mix ended up producing graphs like this:                        (click images to enlarge)

Cave Pearl data loggersThe raw variability now crosses 10 points, and after you apply linear calibration you are still left with a 3-4 point spread depending on where you are on the curve.  So while the group as a whole should still be accurate, I now face a question of precision. Having those sensors flop around by as much as ±0.15°C could mask some of the more subtle signals we are looking for, and this forces me to apply a round of group-normalization to get them into the kind of tight band I saw from the prime sensors:

Cave Pearl data loggers

This second Y=mx+b correction is based on the average of the post calibration readings (so should have no effect on the accuracy) and you can still see some spaghetti squiggles from the inherent noise in these b-class sensors. I was hoping that the new method would save me from having to bang ’em together like that, but apparently it depends on how fiercely you apply your selection criterion before getting out the epoxy.

Which reminds me that automating the calibration run (ie: using a high accuracy thermistor as the reference) would let me test many more sensors and build those matched sets with less time playing button-monkey. Of course, I’d also have to ensure that I’m not introducing other errors in the process with the way I handle the thermistor readings…


Addendum 2016-06-29

Now that I have several more of these temp. chain loggers under my belt, another issue has popped up that could be taken care of easily at the calibration stage: Sleep current.  Sensors from some of the eBay vendors draw significantly more than the 750nA standby listed in the spec sheet.  A ‘good’ set of DS18B’s doesn’t add significantly to your loggers sleep current, but a couple of lemons in the batch can raise a 12-node segment to between 0.1-0.2 mA for the sensors alone.  This might explain how these cheapies get onto the grey market in the first place.

Addendum 2016-07-03

With the next generation of temp strings in production, I am still wrestling with the “diverse” sets assembled from sensors with widely varying behaviors.  These continue to need a post-calibration normalization round to bring the group behavior together:

Cave Pearl data loggers

What’s twisting my melon is that it’s hard to know how much of the post-cal normalization is actually beneficial, and how much of it is just compensating for differences in water bath, since the post epoxy sensors are no longer pinned together by that block of aluminum…?

Addendum 2016-11-16

Just a quick note to suggest another step to overall procedure: Measure the sleep current of your DS18b20! Good ones auto-sleep at about 1μA, but I’ve had a few batches now that constantly draw between 40-60 uA each.  This flaw was definitely vendor specific, so only buy a dozen or so from each to test them out before buying a large bunch. So far, I’ve had the best sleep currents with the $2.50 sensors from electrodragon, though their “waterproof” epoxy job is utterly pathetic, and the raw 20 & 30°C offsets are often just barely within the ±0.5°C spec. Also I have modified the script I use to gather the serial numbers so that it automatically sets the sensors to 12 bit every time it runs. That is the default resolution, but I found that one or two sensors from a given batch arrive set for lower bit depths. And finally, watch out for sensors that require parasite power turned on at the end of the conversion to operate properly even when they are being powered by all three lines. (ie: needing oneWire.write(0x44 ,1);  rather than oneWire.write(0x44); )  I have never gotten parasite and normal mode DS18b20s to work properly as a mixed bunch. Stick with normal mode.

Addendum 2016-12-04

Usually I do these calibration runs with sets of 32-34 sensors, so all the holes in the aluminum block are filled. But I recently tried to calibrate a small batch of eight “naked” DS18b20s with the same method, and ended up with slope and offset calibration factors that were 1-2 degrees Celsius too low, as compared to my reference.   I re-ran the cooling ramp with tape over the unused holes, and sure enough, the adjustments became much smaller, with the raw readings much closer to the reference.  Air was getting in and cooling the center of the block where the sensors were. So if you use this method on a smaller numbers, use other DS18’s to plug up all the unused holes, and put tape over the used ones to prevent air circulation from depressing the readings for raw sensors.  I hadn’t thought about it earlier, but the sensors themselves form part of the thermal mass of the system.  Once you start calibrating, you will have more than enough ‘duds’ (with the cables cut off) to plug up those wells when doing a runs with smaller numbers of sensors.

Addendum 2017-01-18

I just stumbled across a paper at MIT that makes it pretty clear that I’ve been reproducing a standard method already on the books. I’m still digesting the information, and there is lots of stuff in there about handling sensor frequency issues, but I figure most of that has been damped out by the oversampling in the DS18, and by the epoxy around the sensor nodes.

Addendum 2017-01-24

Using shower combs to separate sesors in the calibration bath

Shower combs from the local dollar-store worked well to keep the sensors separated.

We retired a few early generation temp. strings during the last round of fieldwork, and many of those had been deployed before I had a good calibration process. But we had months of data from them, so I now had the challenge of calibrating these sensors after the strings has been assembled.  With 24 sensors per chain, they amounted to  pretty hefty lump, and it would take some work to make sure they were all exposed to the circulating water bath properly. Some large combs provided the needed separation, and I mounted a 330Gph circulation pump near the center of the mass.

Cave Pearl data loggersSo now I could use the waters cooling curve to do a decent  normalization, but calibration with the was going to be tricky because of the thermal inertia of the epoxy around those sensors. I found a dead sensor from one of my early experiments, and drilled it out as a mount for the Reference Thermapen probe, providing it with approximately the same amount of lag as the sensors on the strings.

postdeploymentcalibrationThe loggers recorded readings every 5min over 24 hours, for the group normalization (y=mx+b) coefficients . I did manual readings with the Thermapen every 30minutes which provided at least one reading per degree as the bath dropped from 35°C to 20°C.  For that T-pen subset, I produced a second set of y=mx+b coefficients that corrected the group average used for normalization,  into the reference temps.  So the order of operations is reversed when compared to procedures that start with calibration of the raw DS18b20’s, but I’m happy to report that the process worked rather well, turning some jagged old data sets into smooth temperature profiles that compare very well to Hydrolab drop profiles from those same sites.  In fact it worked so well, that I think I will do it with every chain that comes home, with a careful eye on the chains that had a decent calibration as individual sensors.  I’ve seen plenty of pressure related problems in my other temp sensors, so I do wonder if the contraction of the epoxy is enough to hurt those initial calibrations?

Addendum 2017-04-28

While I like the $200 reference Thermapen I used for this calibration (and the NIST certificate that comes with it…) that ±0.04°C tool is probably out of reach for many DIY builders.  I recently discovered the Silicon Labs Si7051 at Tindie for $9, which gives you 14-bit resolution of 0.01°C at ±0.1°C accuracy. I’ve been using these I2C sensors to calibrate 5% thermistors  to better than ±0.2°C by baking all the system voltage errors into synthetic Steinhart-hart constants. That’s pretty good considering that the eBay thermistor and the reference thermometer together, cost less than a high quality thermistor.  I still have other details to work out before I’m building temperature chains with thermistors, but I don’t think I’ll be able to resist that temptation for long…

Addendum 2017-09-04

Just noticed a nice DIY dry bath over at Though I’m not sure its accurate enough for calibration, you could still use it on the “natural cooling curve” side like I do with my wet/dry method. I’m sure there will be more good calibration kit coming out of the bio-hacking boys soon and they did some beautiful DS18b20 calibrations over at using the melt plateau of 99.99% gallium.  And here’s an interesting “exposed” epoxy mounting of DS18b20’s in a liquid-cooled system.

Addendum 2020-04-07

Given how hard we have to work to bring DS18b20’s into line, I’m quite surprised to see someone using them as a reference standard. Also had no idea that K-type thermocouple sensors were so darned noisy – much worse than plain old thermistors. I’d be oversampling the heck out of those things, along with extremely vigorous circulation in that bath. Air calibrations? Fuhgeddaboutit.

Improving the Accuracy of 1-Wire DS18b20 Temperature Sensor Groups

Copper pipe for positioning the thermapen

Without the dryer lint stuck in the end, the last digit on the Thermapen sometimes does not settle. I use the same thermometer for individual sensor calibration later on, but in that case it’s the DS18B20 probe that moves in and out of the copper sleeve, while the Thermapen stays in the Calibration Bath most of the time. The somewhat annoying folding off/on feature of the Thermopen design forces you to take it out of the bath every 10 minutes as the unit times out.

As I dive into another batch of cheap DS18B20’s for a new set of temperature strings, I thought I should post a note about the pre-filtering I do with these guys before investing effort to properly calibrate and epoxy them. After attaching crimp pins & assigning each sensor a serial number,  I put several groups of them together in the water bath by binding them around a short length 1/2″ copper tube that has a felt plug at the bottom. The copper tube lets me keep the tip of a Reference Thermapen right in the center of the bunch. (The Thermapen has a resolution of 0.01°C  & accuracy of±0.04°C  – so not quite the ±0.01°C accuracy I should be using for my ±0.1°C target, but more affordable at ~$200 and it has a NIST traceable calibration certificate.)  I cover the whole thing with towels for insulation, turn off the bath heater and, manually take reference readings every 1/2 hour as the water cools from about 40°C to around 10°C.  I ignore the first few high temp readings as the bath convection stabilizes, and focus in on the curve at my temperature band of interest, which for the next deployment will be about 24°C.  On a batch of about forty sensors, you see a spread something like this:

Cave Pearl data loggers

( Note: 12-bit values on left axis = 0.0625°C/LSB.  The Thermapen reference line is the one with round markers, and has been converted to equivalent integer values)

These sensors usually  tend to have stable offsets to within 1 LSB, so they group together naturally into ‘bands’ of  2pt high, 1pt high, 1pt low, etc. although they tend to toggle up or down over the length of the curve.  I right-click on the lines and label the nodes with their behavior in Excel.


Once a sensor is labeled I hide it from view (by un-checking the box), so that I can click & label the remaining sensors.  Once they are all roughly classified I bring up the sensors from each designated band to check if the high/low behavior of all the units in the group is stable over a larger section of the curve:


You can see here that some of the units that were 1pt low between 350-380 change over to 1pt high down around 300. And sometimes I move the sensors into a different bucket after looking a bit more closely at their behavior in Excel.

Those are the raw 12-bit integer readings, so the graph above covers from 18.75 – 37.5°C, and over that range you typically get about half of your sensors toggling within one LSB of the reference, with a further subset of about 8-10 that lie right on the reference line. If I can live with a spread of ±2 LSB (ie: ±0.12°C), my yield from this batch of sensors goes up to 31 out of the original 43.  Given that these guys cost about $2.50 each, I just toss the rest into the bin before I do any further calibration.  Occasionally, I get a bad batch where I triage around 50%. But the thing is, I invest so much time mounting these sensors, that I would still be doing a suite of tests like this no matter where I bought them.  Time is the real cost when you build a logger with so many sensors connected to it.

Addendum 2016-02-15

If you want to do more than just eyeballing those graphs, a simple first pass is to subtract the RAW sensor reading from the 12-bit equivalent of your reference reading. Then take the average of those residuals as your bin category. (excluding high temperature outliers) This is still a pretty broad brush approximation of your sensor behavior though.

Of course once you get that far, you might as well plot the reference temps (Y axis) and the raw data (X axis) together and use Excel’s trend-line function to give you a correction equation that converts the sensor output into the reference:

Binning DS18b20 temperature sensors by checking residuals

Note: The spreadsheet above (Click to embiggen)  is in sensor output equivalent  numbers rather than Celsius or Fahrenheit. It’s just faster to translate the reference data into sensor equivalent units, rather than change all the other numbers in the spreadsheet into stanard temperatures. But the units you decide to use does not change the method, just the constants you get for your trend-line equation.

Since the 20-30°C range is usually the flattest part of the DS18b20 response curve, a linear trend-line usually brings my average residuals below the sensors LSB resolution of 0.0625°C.  In essence, this is a simple multi point calibration method that you could do with any reference thermometer and an insulated bucket of warm water.

If you have to go to a polynomial to get your residuals down in this temp range, you probably have a sensor that is going to be a pain in the backside to deal with.  I always have bigger residuals at 30-40°C than I do at 20-30°C but that is also the part of the curve where the bath is cooling more quickly, so I suspect that I have hot and cold spots cropping up in the physical system.  I’m now looking at adding a circulation pump, and perhaps making a dry well insert to put in the center of the bath.

This cooling ‘temperature ramp’ procedure is much different from the two point calibrations I was doing earlier, and I am still trying to determine which one gives better results when I combine it with the normalization that I usually apply to these sensor strings after they are epoxied into permanent sets.  So far, the actual measured ice point offsets have not been agreeing very well with the intercepts predicted by regression on data from 20-30°C and that has me wondering whether it’s even worth the effort to calibrate these sensors over a temperature range that they won’t see in the field.

DS18b20 normalisation bath

I insulate the heck out of the thing with towels, etc. so the normalization cooling takes at least 24 hours (usually closer to 2 days). I still take occasional Thermapen readings, but only to compare with the resulting average that I generate with the sensor data.  Ideally, the corrected average should already have very good agreement with the reference readings… or something has gone wrong with my procedure…

For the  ‘normalization’, I repeat the cooling water bath procedure above with the completed temperature chains assembled as they will be for a deployment. But instead of the Thermapen readings, I use the corrected average (using the equations from above) of all the sensor readings on the Y axis, to generate a second correction equation to apply to each sensors raw data to normalize them to each other. Unlike Craig at Yosemite Foothills, I use a second order polynomial for on this step. After that second correction is applied, any differences I see between sensors on the same chain should only be due only to a real differences in temperature.  

Addendum 2016-02-16

Well it serves me right for counting my chickens. After a few more days putting these sensors through their paces, 11 more units have died or they have stopped reading temperatures below 15°C.  I’ve been drying them on the radiators each night, and though we have a hot water system, I suspect the combination of daily soaking, and night time roasting, loosened the seals between the metal caps and the epoxy or did some other kind of damage. So now this latest batch is falling towards a 50% yield of sensors worth putting into the chain. Looking on the bright side, perhaps selecting sensors that can take more abuse is a good idea in the long run, even if it was done accidentally.

Addendum 2016-02-29

NY platform

An air filled unit from eBay vendor nyplatform. Looking on the bright side, it’s very easy to extract the raw sensor if you wanted to.

After having a few more units die in the middle of testing it started to really get under my skin. So I decided to take a bunch of these sensors apart and soon discovered that many of them were poorly put together (no big surprise there…) I had assumed that the metal casings were completely filled with epoxy, but I soon discovered that many were sealed only with a bit of heat shrink tubing. So the thermal cycling I put them through on the radiators loosened it enough for them to soak through on their next dunking.  Even when they had adhesive (like the unit shown on the right) they sometimes exhibited other strange problems as time went on such as not reading temperatures below 10°C so I have to wonder if the solder joints are also suffering from thermal expansion problems.  Although the nyplatform sensors were the cleanest looking units inside, they suffered the highest failure rate, and had the largest raw residuals, often outside the ±0.5°C spec.  So just being clean, does not necessarily mean best quality…

Things got even uglier as I moved on to the other vendor’s:

Smelly bad

That just can’t be good…

Though the units from Electrodragon had at least some epoxy inside, they were still easy to simply pull away from the stainless sheath, and when I did that I discovered some nasty smelling chemistry going on in there.  The frustrating thing about this is that during calibration runs these sensors were by far the best performers in terms of offsets & accuracy. But after seeing inside I have to wonder how long they are going to last.  I have started pulling all of these units out of the metal and cleaning them with 90% IPA so that I can mount them directly inside the epoxy. Hopefully this treatment will halt that creeping decay and keep them running longer than they would have if I just left them as they were.

DS18b20 temp sensor embedded in E30CL epoxy.

DS18b20 after cleaning & embedding in epoxy.

So I guess the take home of all this is that these cheap sensors should only be considered notionally waterproof in the same way that the round red things you buy at the supermarket in the middle of February are notionally tomatoes. Of course, once I strip them from the casing, they could be subjected to more pressure at depth, which had bad effects on my other temp sensors…Argh!

Addendum 2016-07-27

WordPress does not let me move comments from one post to another, so I am transposing this reader question over as an addendum:

João Farinha asks:
Great Work, I’ve a question if you don’t mind. The length of probes DS18B20 are small and several times I had to make extensions, used single-wire cable (Ethernet cable), sometimes the probes stop to work as the union between cables is made with solder, did this ever happened to you? if I just wrap the wire to the other and put duct tape around works well but is not as solid.

I solder everything, and so far I have not had any problems with signal bounce on chains up to 25 m in length. But I do not use Ethernet cable, I use M12C Series Extension Cables from  as these are rugged enough for my underwater application.   However I should mention that I have ruined several sensors in the past by having my soldering iron too hot when I tried to put the jumpers on the legs of naked DS18b20’s.  I simply cooked them by taking too long.  Wire wrapping could work if your leads were clean, but it takes practice, and a good tool for the job. 

Generally I buy the waterproof units that already have 1-2m of wires connected (as pictured in the post above) since I need at at least short term waterproof capability to run the sensors through the overall calibration procedure  before soldering them into a chain.

Field Report 2015-12-15: New DS18B20 1-Wire Sensor Chains Installed

A typical DS18b20 temperature string deployment

A typical deployment

The August deployment produced both success and failure from our 1st-generation temperature strings. We still managed to get both of those older units back on their feet with fresh batteries, and we add two beta units to the set.  I really had to scramble to get the new chains ready in time because we are spending more time on testing and calibration as I try to squeeze the best possible performance out of these humble DS18b20’s. It takes me about a day to solder and epoxy a  section with 8-12 sensors, so these instruments also represent a significant amount of build time. (note: the length of the wires in between nodes does not affect that time very much)

Although both of the alpha loggers passed the overnight tests following their first run, the shorter (25cm) chain developed a reading problem as soon as it was powered up. The fact that this error occurred before the unit went near the water tells me that it was either a sensor failure, or a problem with the connectors. I have been using Deans 1241 micro connectors between the segments because they seem really robust, but my gut tells me those break points could also add some signal reflection problems.

Here I am 'prospecting' for thermals by dangling a 24m chain from a life jacket and moving it around the cenote. I thik I will put a display screen on one of the next units to make this task easier.

We went hunting for potential deployment locations by dangling a 24m chain from a life jacket and moving it around the cenotes to generate profiles. I think I will put a display screen on one of the next units to make these ‘prospecting’ trips easier in the future.

The logger itself ran for the duration, but the log data was a string of the dreaded 85C (ie: 1360)  and ‘-1’ read errors.  Since these numbers are fairly distinctive, I will put an error check in code on startup to see if I can intercept this kind of problem in the future.  At least the pressure record from the MS5803 on the housing survived intact, and that sensor seems to be working again now that I have removed the Qsil silicone coating that I had over top of the sensor on the previous deployments.

I isolated the read fault to the first segment of the temperature sensor chain, and when that section was removed the rest of the sensors ran well enough. We decided to re-deploy the parts that were still running  (although the chain is now less than four meters long) and I brought the dodgy section home for some forensic testing. I am suspicious of the U-09LV urethane that I used on a few of the nodes, thinking that it’s higher moisture resistance might not compensate for the stiffness and overall durability of E-30CL.

Fortunately, the longer chain that we deployed in the deeper inland site performed well, giving us another record with sensors spanning the halocline:

raw data

Two months of raw DS18b20 output, Logger 46  (10m cable with 20 sensors). Note: the warmer temps shown at the top of this graph are from sensors deeper in the water column, while the cooler temperatures are from shallow sensors in fresh water

Even with relatively long 50 cm spacing, the large rain events of the season pushed the fresh/salt boundary around so much that several sensors (indicated here with 48pt moving averages) switched from the saline, to the fresh water, and then back again. It will be interesting to see if those bands tighten up, or spread out, after we apply our normalization factors.

New DS18b20 Temp strings ready to deploy.

This 6m x 24node chain has a 10m extension, allowing us to change sensor positions in the water column by simply tying off the excess.

After several meetings to obtain permission from the landowners, we managed to install our new set of DS18b20 temperature strings.  We decided to co-deploy a combination of high and low spatial resolution chains, so that we still have a good chance to get data, if one loggers dies.  Due to memory limitations, etc. I built them with twenty four sensor nodes per logger, and even with those spread out over 24m of cable, 3k3 pullup resistors are enough for the one wire communications. That’s aggressive enough to give me some concern about self-heating if I was doing multiple readings, but I figure that with the bus at 3.3v it probably just comes out in the wash.

This deployment site had significant amounts of hydrogen sulfide at depth which forms a visible layer that is shown well by these photos from Angelita.  It will be interesting to see how the chemistry affects our sensors. It certainly had an effect on me, as I was a little worse for wear after that dive.

<— Click here to continue reading the story—>

Field Report 2015-08-12: Success with DS18B20 1-Wire Temperature Chains!

OMG! It worked. Woot!

Yep, that’s me grinning like a fool. It might not be an X-prize, but it worked! IT WORKED!

We were already happy with the flow & drip sensor data from this trip because, despite the TMP102 problems, most of the units had performed brilliantly.  But for me, the real prize of the season was going to come from the underwater temperature strings that began their first ‘real-world’ trials on the last trip. Because the cave they were in presented some challenges, we didn’t pull those units until we had a few good dives under our belts. Back in March, I had just changed over to slimmer builds in 2″ PVC pipe,  and the flow meters at this site were the deepest deployment so far for those new housings. So every unit in this cave was testing something important, and I hoped to take plenty of photos despite the fact that we were right at our little cameras limit.

Trish had line duty (for this part of our dive), and she captured a reasonable shot of me inspecting first flow meter from there. Though I had only been at the sensor for a few moments, you can already see a ball of dust starting to form overhead:

After that we installed a new ceiling anchor, with a descender rod to put a Pearl in the deeper saline flow.  Unfortunately, all that faffing perc’d out the site, so I only managed a tiny clip of one temperature string in-situ before the cloud of pea soup drifted over:

That logger supported a fairly short 5m sensor chain, with 19 nodes spaced at 25cm. It was installed across the halocline, and I admit that I was concerned that (with a minimum increment of 0.06°C) those humble DS18B20’s would not have enough resolution to track the fresh/salt water interface. In addition, I had assembled the string in segments that were linked by my new diy underwater connectors, so these builds had more potential failure points than I even wanted to think about.  It’s probably a good thing that that our dive schedule was so full that I didn’t have time to look for LED heartbeat pips while we were still under water.

Following that long dive, we had a bumpy crawl back to the main road which put more than a few new scratches onto the rental car. After one bone-shaker, Trish observed a logger going through it’s startup sequence on the indicator LED.  A power blip like that during an SD write could toast the card, destroying all our data!  I asked her to cradle the new babies till we got back to the paved highway. When we reached Tulum, we returned our tanks, stowed the gear, and bolted down a couple of tacos in record time. I might even have exceeded the speed limit a bit on the way back to the room…

But after some tough dives, and months of waiting, this was the result from #045:

Cave Pearl data loggers - DS18b20 Temperature string
Note: I inverted the temperature axis (left side) to match the physical situation: the saline water was warmer at depth, with a cooler fresh cap layer. The black traces are 96 point (1 day) moving averages.

The deeper saline water was a full degree warmer than the shallow fresh water, giving plenty of spread for the DS18’s.  And with 25cm spacing we managed to plant one sensor right in the middle of the halocline, capturing its cycles of expansion and shearing away. And that’s just the raw data!  Even without the calibration corrections it was easy to see that we nailed it. Unit #046 gave an equally complete log, but with its larger 50cm spacing the sensors straddled that fresh/salt boundary, so we simply have an empty gap on the plot. Of course to a karst hydrologist, knowing the limits of mixing zone is also useful information

After the initial excitement over that temperature data died down, I proceeded through my usual set of post deployment checks. I was keen to compare the power curves from the two loggers we had deployed:TempLoggerPowerCurves

I knew those sensors were going to pull a substantial amount of juice during 12-bit conversions, but putting twelve AA batteries in the housing was still something of a shot in the dark for me back in March. While both curves looked smooth , there was something odd about #046 using less power than #045, because it had one more sensor (total of 20) and they were stretched out over 10 meters of cable so #046 also had a more aggressive pull-up resistor on the bus.  A bit more poking around and I noticed that I had accidentally reversed a cell in one of the banks  (those with sharp eyes probably  spotted that in the photo above) so #045 had actually run on only three sets of AA batteries. Shottky’s isolate each bank against battery failures, but it’s nice to know that they also protect the little loggers from my own dumb mistakes. With 46’s full complement of 4x3AA batteries only loosing 0.5 v over almost four months, I’m confident these loggers could approach my one-year operating target on a fresh set.

The marine heat shrink tubing adhesive after four months

After four months under water the adhesive on that marine-grade heat shrink looked a bit flaky, but there is ECL30 potting the wires inside the adapter so I’m not worried about the seal.

Of course that’s predicated on everything staying water-tight, so I examined each temperature sensor very carefully: looking for evidence of water damage. Most of the nodes were filled with hard epoxy (E-30CL), and for some I was trying out a more flexible urethane. (U-09LV )  They were all remarkably clean, with only one node showing yellowing, and that one was a botch where I had split the original sheathing under the heat gun, and had to re-wrap it. I was also pleased to see that my DIY underwater connectors proved to be robust. They were all were bone dry inside, with no hint of oxidation on the contacts. It was looking like we would be able to re-deploy these units right away!

While I was examining the hardware, Trish had been chewing on the data from both of the loggers. At one point she started making funny “Hmmm…” noises which I know she only makes when she disagrees with something, but is being too polite to say so.  (You hear that kind of thing a lot at academic parties…)  When I asked her what was wrong, she showed me the pressure log from one of the MS5803’s:


Damn! Even with surface barometric corrections it was obvious that the rising trend in this record was out of sync with our other water level recorders.  And with 1 millibar of pressure being approximately equivalent to 1 cm of water depth, the implied 4m delta is simply ridiculous. I immediately suspected that the Qsil 216 I had put over the pressure sensor was doing something weird. I’ll need to do some homework to sort out what actually happened, but I’m guessing the silicone started absorbing moisture at depth since the stable cave environment is unlikely to cause problems from thermal expansion.

#046 was ready to roll the next morning.

#046: ready to roll the next morning.

So we didn’t get a hat-trick on these first builds, but by 2 am I had new batteries in place, the clocks updated, and I had carefully peeled away the silicone over the MS5803s. (hopefully without damaging the factory gel caps). If the overnight run looked good, we hoped to install #046 in deeper cave (~24m) the next day.

<— Click here to continue reading the story—>

Calibrating DS18B20 1-Wire Sensors with Ice & Steam point measurement

You will need to crimp the ends and give each sensor a serial number, but don't label the sensor itself as I have in this photo or the will fall off during the steam point testing.

Give each sensor a serial number, but don’t label the sensor itself as I have in this photo or the labels will just fall off during the steam point testing. After adding crimp pins to the wire ends it becomes easy to gang them together on a breadboard for testing. Despite Maxim’s warnings, I had star configurations above 20 sensors reading well with them close together like this.

I’m probably not the first person to note that sensor calibration is one of the big differences between the mountains of data coming from the citizen science movement and that produced by research professionals. (…mea culpa…) After opening this can of worms, I think I am beginning to understand why: Accuracy calibration rapidly gets complicated, or expensive, and often it’s both at the same time. By the time you have what you need to do the job, the difference between a $0.30 sensor, and a $30 sensor, is pretty insignificant. So it’s no surprise that few people work on calibration methods for low cost sensors, or why normalization approaches are used instead.

But I am already spending far too much on this little hobby, so despite knowing that the folks over at Leighton Telescope managed to get their DS18b20’s to about ±0.01°C with a NIST traceable thermometer, I thought I would see how far I could get on my own.  I suppose if I was an alpha geek,  I would make my own platinum RTD  and calibrate the sensors against that.  But I’m not quite there yet. I should also point out that numbers are not my strong suit, so there could be some significant errors in what I have cobbled together here and I appreciate any feedback to help correct those…

The first thing that occurs to me is: Can you read the temperature more accurately  by averaging a bunch of these sensors together?  If the readings from the sensors have a mean and standard deviation, then as the number of sensors increases then the standard deviation should decrease…right?  The data sheet gives you a sense of how far you can get with that approach, because I assume that Maxim/Dallas used a very large number of sensors to derive their typical performance curve:


But if I understand what people say about this graph, the only reason the 3 sigma spread on that graph looks better than ±0.5 at 20°C is because the errors in the sensors used to derive that curve were truly random, and had a nice Gaussian distribution around that mean. However, since the actual batch of sensors I am holding in my hand is likely from the same production run, it is subject to systematic errors that don’t cancel each other out so nicely. And since I bought them on eBay, there is also a chance that they might be fake DS18b20’s.  So I could have no idea how my mean error line was related to the one on Maxim’s graph.

But there are still useful things you can do with this kind of averaging:


The front temperature display on this clunky old Fischer Scientific was off by more than 2°C, and it was missing a foot. While it’s hardly a temperature chamber,  the insulation and covering lid produced a slow cooling curve, so I could be reasonably confident the sensors were being exposed to the same temperatures. Don’t use data from the rapid heating cycle, because temperatures are likely to be unevenly distributed in the bath.

First of all you can get rid of the bad sensors by selecting a group that has a consistent behavior over the temperature range you are looking at, with readings that fall within the manufacturer’s specifications.  To get enough data for this kind of assessment, I needed to run at least 10 sensors at the same time so that the average had some statistical weight. For this testing I picked up an old five Litre isotemp bath (you can find them for $25-$50 on eBay) but you could just as easily do this with hot water in a styrofoam cooler. With about 20 sensors on a breadboard in a star configuration (4.7k pullup), I brought the water bath up to a stable 40°C, and then moved the entire thing out into the fridge and left it logging during the cool down. The lid was on, and I had several towels over top to make the process go as slowly as possible.  It took 12 to 24 hours for each batch of sensors to reach ~5°C.

With this data in hand, I looked at the residuals by subtracting each sensors raw reading from the average of all the sensor readings.  This exercise sent one DS18 straight into the bin, as it was more than 2.5°C away from the rest of the herd for its entire record.  Another was triaged due to a strange “hockey stick” bend in it’s residual around 25°C.  I threw out the data from those two duds, and recalculated the average & residuals again.  Just to be on the safe side I decided not to epoxy any sensors into a long chain if they were more than 0.3°C away from the average. (although I am still wondering whether eyeballing residuals like this is enough to exclude the right outliers?)

You can then normalize the sensors to each other by fitting a quadratic equation to a graph of each sensor & the overall average line. Excel can generate these coefficients for you with the linest function, or it can solve the quadratic with Goal Seek.  But the easiest method I found was make a 2nd order (but not higher) fit with the chart tool’s trendline function. Make a scatter plot of the data with the averages on the Y axis, and data from one individual sensor on the X axis.  Then right click on the data points to select them, and choose ( Add Trendline ) from the pull down menu, with the [ ] Display equation on chart tick box checked.      (here is an example of the technique using an older version of Excel)

The equation you see displayed will convert that particular sensors output into corresponding temperatures on the average line. With this transformation, each sensor will yield the same reading if it is in the same thermal environment, and you can accept that any differences between two sensors in the chain represent real differences in temperature.

This kind of normalization is as far as most people go. However for the reasons I outlined above, we can’t be sure that we were using a valid sample for that mean data. In my tests it looked like I did not have an equal distribution of sensors above and below the average line, so I still didn’t really have a handle on whether this was improving the absolute accuracy. (I will post some example graphs of this later…)

That brought me to calibrating the DS18b20’s against intrinsic physical standards which rely on the fact that during a phase change  (melting, freezing or boiling)  adding and removing heat causes no change in temperatureIn fact those heating curve plateaus are known so precisely that they use them at NIST to calibrate the expensive thermometers that I am trying to avoid buying.  Today they do this with Gallium‘s triple point (29.7666 °C) and the triple point of water (0.010 °C), but they used to use Gallium’s melting point plateau. (29.7646 °C)  Gallium sells for less than a buck a gram on Amazon and a density of about five grams per cubic centimeter means a block big enough to surround one of the DS18’s is almost within a DIY’ers budget. (100 grams will make a disk about two inches across and a quarter inch thick) But considering that commercial Ga melt cells cost about three grand, either that stuff is nasty enough to get me into trouble, or you need allot more more of it, at higher purities than you can buy on eBay to build one.  Then there is the significant time it would take to refreeze the block again for every single sensor. And finally, all exposed metal must be carefully lacquered as Gallium will form an amalgam with many metals, and any dissolved metals will compromise the purity of the bath, shifting the melting point. And you would probably have to cover everything with Argon wine preserver.

So I went hunting for other substances I could use for a mid range calibration point and found several good boiling points such as: Ether (35 °C), Pentane (36.1°C), Acetone (56 °C), and Methanol (66 °C). Despite my enthusiasm over coffee the next morning,  all of them were summarily rejected by my wife, who strongly suggested that I look for calibration procedures that do not create large amounts of highly explosive vapor. Given how unstoppable she usually is in the pursuit of  good data, I was not expecting this outburst of common sense 🙂

So I looked at the other primary standard used to calibrate pt100’s. Turns out it is possible to make your own triple point cell, and if that’s not good enough for you,  Mr. Schmermund also produced plans for a freezing point of mercury cell (–34.8 °C) (See: “Calibrating with Cold”, Shawn Carlson, Scientific American, Dec. 2000 issue).  However the local 7-11 was fresh out of liquid nitrogen when I checked, and I had this gut feeling that risking mercury induced brain damage was not going to pass the cost/benefit analysis either. If I actually did need sub zero calibration I think I would try using Galinstan, (−19 °C) which is now replacing mercury in glass thermometers.

If you can pre-chll the sensors in one corner of the bath, the whole process goes much faster.

Pre-chilling the sensors in one corner of the bath makes the process much faster. Hold the sensors by the cable, not the metal sheath, or heat from your hands will affect the readings.

It was looking like calibrating against anything other than distilled water was going to take a substantial amount of effort compared to what I was seeing in the NIST and EPA videos. Most sources indicated that the ice point and steam points were at least an order of magnitude more accurate than my ±0.1°C target, making them suitable for the exercise.

While the overall procedure is pretty easy, it did help to practice a few times to get a sense of when I could trust the readings. Checking that you have just the right amount of water in your ice bath makes a big difference, and don’t run the sensors at full tilt or they will self heat. (I left 15 seconds between readings) Since errors on my part would cause the sensor to be warmer than the true ice point, I took the lowest reading, while stirring, as my final reading. The difference between stirring, and not stirring was usually 1-2 integer points on the sensors raw output (0.0625-0.13 C) and this was consistent for all the sensors.

If these sensors were linear then reading the ice point was a direct measure of the b in y=mx+b. And this got me wondering if one point calibration was enough all by itself.  But once again my wet blanket science adviser assured me that nothing on those graphs told me if the offset was constant over the sensors range. Hrmph! (Although according to Thermoworks, ice point alone can be a good way to check for drift, because the most common error in electronic temperature sensors is a shift in the base electrical value)

I found a silicone vegetable steamer lid for the calibration that had three DS18B20 sized holes in it already.

I found a silicone vegetable steamer lid for the calibration that had three DS18B20 sized holes in it already.  Getting the right pace for your slow rolling boil is important, and this lid sheds the condensed water back into the pot reasonably well. Alligator clips also help speed the process.

So I moved on to measuring the steam point. Water’s boiling point is not necessarily at 100°C and the only factor that is really involved in the variance is atmospheric pressure. Altitude is often given as an alternative when pressure information is not directly available and there are plenty of places to look up elevation and barometric pressure data (& converters) for the necessary corrections.

I already had some MS5805-02 sensors on hand, so with the help of Luke Millers library, I could read my local atmospheric pressure for the correction directly.  The accuracy of my pressure sensor was ±2.5 mbar (similar to  the more common BMP180) with the B.Pt adjustment equation being: Corrected B.Pt.=100 (°C)+((PressureReading-1013.25mbar)/30)  So the 5 mbar total error range in the pressure sensor could change the adjusted boiling point by up to 0.166°C.  This means that the error in my pressure sensor measurement is at least as significant as the other aspects of this procedure. Better than the default ±0.5°C, but it puts a limit on how accurate I will can get with my steam point measurement.

Doing multiple sensors at once saves significant time, but be carefull or you will pay the piper with a couple of burn fingers

Cutting down the stacks on the Fred steamer lid allowed me to do multiple sensors at once. This saves time, but be careful or you will pay the piper with a few burned fingers when you change them out.

With each sensor just under the surface of the boiling water (since the evaporation process happens a little bit above 100°C) each one took about 5 minutes to warm up to reading temperature with the water on a slow to medium boil (and it was easy to see that on the serial monitor) I didn’t consider the test done till I saw at least a full minute of stable output (reading the sensor every 10 seconds) Since errors in my technique would produce readings on the cold side, I took the highest ‘frequently repeated’  number as the final reading. Most sensors settled nicely while some of them toggled back and forth by one integer point from one reading to the next.

In comparison to the steam point procedure, I trust the Ice point as more reproducible because it does not suffer from any pressure information dependency. Perhaps more important is the fact that 100°C is far away from my 20-30°C target range, leaving the possibility for significant errors if the sensors have a non-linearity problem.

With the ice and steam readings in hand, I could construct a two-point calibration for each of my DS18B20’s with slope M=Δy/Δx, and B=(the ice point reading).  (explained here, and that left you in the dust there are lots of fill-in-the-blank spreadsheet templates on the web)

At this point I am still doing tests & chewing on numbers, but the standard deviations around the mean line are being reduced by this ice&steam point calibration. The problem is that even after I apply the resulting slope and intercept I still have significant residuals from a mean that is derived with the corrected numbers. I thought that the two point calibration would make the graphs the individual sensors line up very closely with one another, and that they would have nearly identical slopes(?) I am left wondering if larger sensor errors up at 100°C mean that I need to apply some additional process to normalize my sensors to each other in the 30°C range after doing the two point calibration.  But using the process I described above would generate ‘b’ value corrections, and I am very reluctant to modify my y intercept numbers because I think using the ice point to measure that offset is robust. These doubts about accuracy of the steam point, the sensors linearity, and my lack of a nice “mid-range” standard to calibrate against, have me hunting for a method which would gracefully combine a single (ice) point calibration with normalization. And the dip in the datasheet’s mean error curve between 20-30°C implies that even after applying ice point corrections my average line will still be 0.05°C lower than actual (?)

Another important observation is that the means generated from the uncorrected data were within 0.14-0.16°C of the means calculated after applying the two point calibration. Either my sensors actually did have a reasonably normal distribution of error, or I might have missed something important.  The implication in that first case is that normalization alone should improve your overall accuracy, but I still need to get my hands on a calibrated pt100 to know for sure….Argh!

Addendum 2015-05-18

Bil Earl just posted a beautifully written article on sensor calibration, which puts everything here into context. A great job once again by the folks over at Adafruit!

Addendum 2016-02-12

Just adding a quick link to a small post on the pre-filtering I do with these sensors, which I only posted because no one else seems to bother posting data on the ‘typical quality’ you see with the cheep eBay sensors. And after splashing out on a Thermapen reference thermometer ($200), I can try a multi-point calibration for these sensors that is closer to my target temperature range.

Addendum 2016-03-05

Just put the finishing touches on a new calibration approach which, compared to this ice & steam point method, was an order of magnitude faster to do. If you are calibrating a large number of sensors, the reference thermometer is definitely worth the investment.


Using multiple 1-Wire DS18B20’s for a DIY Temperature Sensor Chain

Temperature is a fundamental physical parameter which can be used to track how water moves and interacts with the rest of the environment. Most waterways in the world are multi use, and temperature sensors are a simple way to monitor rivers and streams, to see how they are being affected by urban pollutionagricultural runoff,  solar heating, groundwater exchange, etc.  In larger water bodies, we also want to track the cycles of temperature stratification.  Sometimes we deliberately disrupt thermoclines with aeration to preserve game fish, but at other times we depend on metalimnion stability to provide a barrier between the E. coli we dump in, and the water we take out for drinking.  Monitoring hydrothermal plumes can even shed light on geological activity under the ocean floor.

1st attempt: epoxy & 1/2" pipe

My first attempt with epoxy & lengths of 1/2″ pvc was successful enough to keep me going. For shallow water deployments, a simple treatment like this would probably be good enough to last for many months. Epoxy is the most expensive component, so each node cost ~ $4.00 with the benefit that each sensor is very robust afterward. You can also check out Luke Miller’s sensor waterproofing on his blog.

Temperature is also a key metric of ecological integrity because it exerts a profound influence on aquatic creatures. Many species time their reproduction and migration according to seasonal water temperatures and, as temperature increases, the capacity of water to hold dissolved oxygen is reduced. In coastal environments, temperature and salinity are very strongly associated so you can use it as a reliable proxy to record tidal cycles, and saline intrusion into fresh water habitats.

All of these applications require simultaneous sampling at different depths and locations, so arrays of temperature sensors are a standard tool for addressing water quality issues. Today, many researchers are looking to the open source movement to help make these large installations more affordable.  One obvious candidate is the inexpensive DS18b20 temperature sensor because it’s one wire protocol makes it possible to string them together in a daisy-chain configuration.  Electronics hobbyists have taken note, and are putting them all over the place with Cat5 network cables. Reading those pages convinced me to take a closer look at the DB’s from the perspective of the Cave Pearl Project: Would it be possible to turn these humble band-gap temperature sensors into something like a research quality thermistor string?

A review of typical commercial options gives you a sense of how much it actually costs to build a network of temperature sensors:

Sensor Cost/Node Precision ±Accuracy Comments:
iButton $25.00
(stand alone)
0.10°C ±0.70 Thermochrons have made a new class of low resolution networks possible,  with large #’s of sensors (Note: You need to coat these guys in PlastiDip if you are deploying them outside, as they are not really waterproof)
Hobo Water Temperature Pro v2 $129.00
(stand alone)
0.02°C ±0.21 Here is an interesting USDA study using TidbiTs which have identical cost & spec.
 NexSens T-Node FR $250/node + $150/cable 0.01°C ±0.075 Typical of high end sensors used in large well funded projects at NOAA, etc

The iButtons are basically disposable, and I don’t think it would be worth anyone’s time to make them from scratch if all you need is 0.10±0.7°C.  But if you need more precise information, a simple 10 node string puts you around $1500 in the mid range, and $4000+ at the geotechnical high end.  Factory calibration of 0.0625 ±0.5°C puts the DS18B20s somewhere between the Hobo loggers and the low end Themochrons. But with each sensor on the same chain, you would be able to keep all the sensors synchronized better than stand alone units.  After seeing the elegant pro-level system, I knew I also wanted something that could be assembled from interchangeable segments, to customize the chain for each installation.

I googled and grazed my way through various YouTube videosinstructables, etc. looking at how others had water-proofed these sensors. After digesting that, I produced a some prototypes using a modified version of my underwater connector idea that have the DS18b20 potted in an irrigation pipe coupling:

all it took was a slight modification of those underwater connectors

~ $9 each for materials. The barbs are tapped to aid adhesion, JB Plasticweld forms a plug to hold the epoxy in.

The 1-Wire Address Finder and the library from Miles Burton work well with this changeable configuration. I preferred the code over at Paul Stoffregen’s site, as it gives you the raw integer reading, the ROM address, and the temp in °C as soon as you connect a new sensor.  But like most of the scripts that uses address arrays, it will read them in numerical, rather than physical order.  So you will eventually end up hard coding the Rom addresses unless you switch over to something like the DS28EA00 that supports sequence detection through a chain mode function. This allows you to discover the registration numbers according to the physical device location in a chain, and if the DS28 also had better resolution/accuracy than the dirt cheap DS18’s, I’d be converted.

The one-wire guide at the Arduino playground has a most valuable tip for driving a string of sensors like this:

“The master can address all devices on the bus simultaneously without sending any ROM code information. For example, the master can make all DS18B20s on the bus perform simultaneous temperature conversions by issuing a Skip ROM [CCh] command followed by a Convert T [44h] command.”

This means that I can minimize the number of times the Arduino has to wake-up for each set of readings. But these sensors are still going to pull about 1.5 mA each during the 750ms 12-bit conversions, so there is going to be a substantial power demand during each set of sensor readings even if I put the μC to sleep while it waits for the data. (and you can only sleep if you are not using parasite power) Thirty or more sensors on the bus will also generate allot of communication and data buffering. I have no idea yet how much juice all that is going to take so I built some larger housings, with room for twelve AA batteries, to drive these long daisy-chains:

This chain is 13.5m long, with 27 nodes (1m, 0.5m and 0.25m lengths)

This prototype is 14m long, with 1m, 0.5m and 0.25m cables.  I’ve had no data reading errors so far…

Maxim has guidelines for reliable long line 1-wire networks which suggests that longer networks need 100 Ω resistors at each node for distributed impedance matching or you could run into timing/reflection issues. As I am working in reasonably restricted cave environments where the floor to ceiling distances rarely exceed 20m, I did not add these resistors. If I start seeing the dreaded 85°C error after I load the bus, I will try a lower value pullup resistor, or perhaps a barrier diode. There is even an I2C to one wire bridge out there that can adjust the strength of the pull-up dynamically as your network grows, although since the one-wire bus is so easy to get running, I would only cobble the two networks together if I had an unusual situation.

these are using deans xyz - the trick is toget the wires to fold the right way

I am using Deans 1241 Micro 4R plugs, which just barely fit inside the pipe. These connectors are really solid, but the trick is to get the wires to fold without pinching when you mate the o-ring to the seat on the opposite side of the connector.

The sharp eyed will note that there are four conductors on that interconnect.  I used four conductor cable for added strength because silicone jacket cables are so soft and floppy that the sheath really provides no support at all. And this offers the potential for a second one-wire network that is separate from the first if communication errors start to appear. Alternatively, I could add other one wire devices to that second line for more functionality. I have not figured out what this might be yet, but perhaps I could include some kind of leak detector to make the system more robust. I am also keeping an eye on the one-wire weather station crowd to see if an interesting sensor pops up there.  And finally, I wanted a four wire connector that could also be used with I2C breakouts, as most of my other builds use sensors with that protocol.

I think this is approaching the largest string I would want to deploy on a dive.

With 27 nodes, this is approaching the largest string I would want to deploy on a dive.  I need to put some thought into how to handle this massive tangle hazard safely.

I have replaced the cable that came with the “waterproof” DS18B20 sensors, as the insulation was far too thin for the rough handling I expect to see and the 28awg wires would add significant resistance over a long run.  But silicone jacket cable is expensive and I am still searching for an affordable 24awg, 4-conductor option. (If you have a suggestion, please pop it in the comments!)  The PVC and Poly-urethane jacket cables I have tried so far are just too stiff to “hang right” under water without a weight on the line, and this would put strain the data connections.  The standard solution is to run a suspension wire alongside the thermistor string with the data lines connected to this armored stiffener. While this works great off a boat or a buoy, I would prefer not to have to deal with those extra components on a cave dive.

After addressing the issues with the physical build, I still have an elephant standing in the corner.  While an accuracy of ±0.5°C might be good enough to track the refrigerator in the garage,  ±0.1 °C is about as coarse as you want to go for research applications. So a lot of the burden of making this thing really functional will be the calibration. If you replace the Arduino’s 10-bit ADC, then there are thermistors that give you ±0.1°C right out of the box, and I would like to get these DS18b20’s into that ball park before I say the newest addition to the Cave Pearl family is ready to deploy.

Maxim has a document describing how to curve fit the error of a band-gap based digital temperature sensor with 2nd order polynomials to achieve accuracies in the 0.02-0.04°C range,  but they make the assumption that you already have a NIST traceable platinum RTD to determine what the errors actually are. But what if you don’t have that $5-600 piece of kit just lying around? (+another $150/year for the required annual re-calibration) Is there any other way to calibrate temperature sensors like this to improve their accuracy?

Actually, there are verification procedures that use the ice and steam points of water, and I will detail my attempts to use these “old-school” calibration methods in the next post. In theory at least, these intrinsic standards can bring our DS18b20’s well within a mid-range accuracy target of  ±0.2°C.  At the end of it all I will try to borrow a certified pt100 to see how close I actually got.

Addendum 2015-02-23

After handling that long chain for a few days of calibration, I realized that I needed to mount those sensors with a much smaller physical profile or they were going to be a pain the backside under water. So I came up with a simple combination of heat shrink tubing and epoxy that keeps the sensor much closer to the cable than a hard-sided mould:


Using clear heat shrink will let me monitor leaks, aging, etc. There is heat shrink tubing on those solder joins, but it became invisible in the clear epoxy.


Heat from the bottom and as the tubing shrinks the epoxy “flows” up to the other end. When you seal the upper end leave some excess epoxy trapped in the tube. Apply another ring of shrink to cap it off, and with both ends fully sealed & cooled down, gently heat the entire surface. As the leftover wrinkles disappear the heat shrink tube turns into a tension structure creating a smooth rounded profile.

The trick is to seal one end of the clear tubing first, and then inject the epoxy into that from the open end.  Once the tube is about 2/3 full 0f epoxy, shrink the upper open end of the tubing down to it’s minimum diameter. Very gently heat several spots along the tube  – as those areas contract it pushes the epoxy up toward the open end.  Then wipe away any excess so the meniscus is level, and seal the upper end of the outer tube to the cable with a short section of adhesive lined heat shrink tubing, making sure that you trap as few bubbles as possible. I usually use adhesive lined heat shrink for those two end tubes, and I let the upper ring completely cool so that if forms a good seal before heating the rest of the clear heat shrink tube.  I usually use Loctite E-30CL  epoxy as this sets much more quickly after the reheating process, often becoming hard in about 30-45 minutes. This approach to mounting the sensors would also work with ‘naked’ Ds18b20’s, but having the sensors already mounted in the stainless steel sleeve makes it much easier to do the ice & steam point calibrations before you commit to actually using a particular sensor.  At only $1.50 each, you should expect to triage at least some of them for being out of spec.

Completely encasing the sensor like this will induce some thermal lag, if you are really worried about that you could make the tubing shorter and not seal the upper end, leaving the metal sleeve exposed.  If field handling indicates that the hard epoxy is too brittle, I will hunt around for a flexible amine curing silicone, or a low durometer clear urethane, to fill the nodes (typical hardware store silicone gives off acetic acid while curing which is bad for electronics)

Addendum 2015-03-01

With more handling, those epoxy filled tubes did end up feeling a little fragile at the thin ends, so I reinforced them with a few more wraps of adhesive lined 3:1 heat shrink tubing:

I also moved the join so that it overlaps the sensor, so the metal sleeve acts as stiffener

I also moved the solder joins so that they overlap the sensor, this lets the metal sleeve acts as stiffener for the whole unit.

In addition, I decided to make the chain in 2m segments, with sensors at fixed distances along each segment. This reduces the total number of joins in the cable, while still allowing for custom configurations. In the photo (belowyou can see that I used a longer piece of pipe in the segment connectors than was necessary. My hope is that if I distribute some buoyancy throughout the chain there will be less strain on the segment nearest to the datalogger. Those segments will be bearing the weight of the whole sensor string because the logger will most likely be mounted on the ceiling of the cave passages. The real test will be when we actually install them, as there is always some unforeseen factor that comes into play under water.

Addendum 2015-03-02

I just came across a great write up by someone using DS18b20 sensors to track ocean temperatures over in New Zealand. (I knew I couldn’t be the only one…)   Interestingly, he uses a star configuration for the sensor connections. While this is much faster to connect than the daisy-chain approach I have taken, it looks like he ran into some problems with his long cable runs and had to use a very strong 480Ω pullup resistor. There are plenty of photos, and a good explanation of how he used normalization to smooth out between-sensor variations. While this is helpful for tracking relative changes in temperature, it does not necessarily improve the accuracy of these sensors.

Addendum 2015-03-06

The second prototype with 30 DS18b20 sensor nodes, 12 m of cable length

The second prototype with 30 DS18b20 sensor nodes distributed over 12 m of cable with 7 inter connections

I have been putting more segments together for a second prototype. For the newer nodes I used Loctite U-09FL urethane. This stuff is less brittle than the epoxy after curing, however it sets almost immediately when you apply the heat to the shrinkable tubing. The result is that the nodes didn’t “smooth out” like they did with the epoxy, and so they ended up looking like clear Jello raisins.  You can’t be too aggressive with the heat gun or the outer sheath splits, making a bit of a mess.

The hardest part is pulling the sensor wires free of the old cable as it tends to be filled with black epoxy. You will loose a few sensors by accidentally cutting them in the process.

The hardest part is pulling the sensor wires free of the stock cable as it tends to be filled with black epoxy. Expect to loose a few sensors by accidentally cutting wires in the process. Most sources I found recommended using unshielded cables to reduce capacitive coupling, and warned against grounding unused wires for the same reason. Some folks also suggest adding 100-120 Ω resistors to the data leg of each sensor to reduce the load on the data bus, but I have not tested that yet to see if this really works.

Once I had enough segments ready I did some tests to see how far the chain could be extended before the coms failed.  Line capacitance appears to be the controlling factor on these kinds of networks and most try to keep it low for maximum distances, or better performance. But I am packing quite a few DS18’s close together, so the sensors themselves are more likely to limit my system. With 15 minute sample intervals being typical for an environmental monitoring application,  I could even look into slowing down the bus, rather than speeding it up.

With a standard 4.7 kΩ pullup, I was able to get to 30 sensors responding  well on 12m of 4x 26awg silicone jacket cable.  Changing the pullup to 3k3, allowed me to add 15 more sensors on an extra 8m of cabling. And taking the bus to a fairly aggressive 2k2 let me add 19 more sensors and an additional 7m of cable. So my add-hock test reached a total of 63 daisy chained nodes on 27 m of cable before the readings became unstable. I did not see 85°C errors, but when the network approached the R-C rise time limit, the readings started looking like this:       (in a room at 20°C)

2015/03/06 16:58 Cycle: 1, InternalV= 3293
Probe 0 Temp Raw: 268 Temp C: 16.238
Probe 1 Temp Raw: 288 Temp C: 18.0
Probe 2 Temp Raw: 16 Temp C: 1.0
Probe 3 Temp Raw: 322 Temp C: 20.125
Probe 4 Temp Raw: 332 Temp C: 20.238
Probe 5 Temp Raw: 113 Temp C: 7.62
Probe 6 Temp Raw: 323 Temp C: 20.187
Probe 7 Temp Raw: 280 Temp C: 17.244
Probe 8 Temp Raw: 324 Temp C: 20.250
Probe 9 Temp Raw: 17 Temp C: 1.62
Probe 10 Temp Raw: 256 Temp C: 16.0
Probe 11 Temp Raw: 320 Temp C: 20.0
Probe 12 Temp Raw: 66 Temp C: 4.125
Probe 13 Temp Raw: 24 Temp C: 1.244
Probe 14 Temp Raw: 32 Temp C: 2.0
Probe 15 Temp Raw: 268 Temp C: 16.238
Probe 16 Temp Raw: 320 Temp C: 20.0

Adding a couple more nodes after that caused the reads to completely fail. The numbers then become zeros because I use  memset(rawTemps,0,sizeof(rawTemps));  to clear the temperature reading array after the data has been transferred to the eeprom buffer. Doing this between readings makes it much easier to spot when sensors drop out.

Thirty sensors is a bit tight for my application, so I will remove the 4.7K resistors I have on the loggers now and make an in-line adapter that lets me change the pull-up on the fly if a particular installation starts giving me grief.  But going much larger than 10m & 30 nodes might be putting too much data at risk if we have a point failure. Another limitation on the practical side is that my FTDI USB adapter can only source about 50 mA, so I can’t use the skip ROM command to trigger temperature conversions on more that about 33 sensors before I risk hurting the chip during tethered test runs. 

Addendum 2015-04-05

DIY Cave Pearl data loggers based on Arduino MicrocontrollersWe recently had a chance to test the first prototype (with the separate individual nodes) out in open water. To make sure the connections were up to the challenges of “normal” fieldwork, I let students handle & install the unit, with no specific instructions other than letting the last sensor just rest on the bottom. (yes, I cringed a few times…) The lowest sensor was at 6.5m depth, and the unit gathered temperature data in a saline water outflow for about 7 hours with no leaks or problems.  On the same trip we also deployed two “clear epoxy tube” chains in a different cave system spanning depths from 7 to 14m. I don’t have any photos as that deployment was significantly deeper, and beyond the reach of our little point & shoot.  Both of those carried 12 x AA batteries, and the plan is to leave them running in-situ till mid year, at which point I should have a pretty good record of power consumption of these long sensor strings. 

Addendum 2015-04-20

I mentioned using E00CL epoxy earlier, but have since gotten back some cave deployed RH sensors that all failed due to moisture permeability on the E05CL epoxy I used to build them. And those humidity sensors were not even under water!  So for future builds, I will stick with E30CL. It’s gooey to work with, but I have several units that have been under water for more than a year using that epoxy. 

Addendum 2015-07-08

The single sensor chain in the photo above ended up coming home after the field trip, and since it was just sitting there, I set it up on the bookshelf for a power drain test:

Multi DS18b20 power drain test.

Because the DS18’s run on fumes when they are not doing a conversion, this logger slept at 0.11 mA. The units currently in the field have more sensors and longer cables, but seeing that less than half of a three cell battery’s capacity was used on this two month run gives me confidence that the 12 cell loggers we deployed had far more power than they needed. So the only real question is how long they remained water tight* with the crummy epoxy used on some of the nodes. If I am lucky, the marine heat shrink tubing I used for physical re-enforcement will have provided a water tight seal on those ends.

P.S. * Those deployed units survived their first underwater test and worked brilliantly. For more details click HERE

Addendum 2015-10-30

Just an update to that 20-node DS18B20 sensor chain’s power test:

Power drain test with 20 dS18b20 nodes on 3xAA cells

These units save 200 records (~2 days worth of data) to an onboard 32k Eeprom buffer before doing a flush to the SD cards.

So we have already done five months on 3xAA battteries, confirming that those DS18B20s are very nicely behaved sensors in terms of their overall power consumption.  But that P.S. curve is bumpier than I am used to seeing from alkaline batteries read with a 2x 4.7M ohm divider,  so I might go digging on what causes that to happen.  It could just be that simultaneous temperature conversons ( 20 sensors x 1.5 mA x 750ms ) is enough of a load that the batteries feel that hit.

Addendum 2018-12-01

Just an update regarding a problem we ran into on some deeper deployments of 24m x 24 node DS18b20 chains.  I’d been running those with 3k3 pullups and they passed all run tests at the surface – but those strings suffered a slow progressive read failure after about five months between 20-50m depth.  This was a slow bus failure, and the tidal cycle showed up clearly as the sensors fell off, and then rejoined the set. My best guess is that the soft silicone jacket on the cable became progressively more compressed over time, and this raised the bus capacitance just enough to push the timing out of spec.  Adding a second pull-up to the end of the bus brought all the sensors back on line, so my recommendation is to stick with ~2K pullups if you build longer chains like this. Harder PUR insulation cable jackets might also have prevented this very tricky problem.

A Simple DIY Underwater Connector System made from plumbing parts

Up to this point, the Cave Pearls have been self-contained units. But this means that the sensors must be mounted directly on the housings, and the batteries must fit inside.  I already have ideas for new sensors that would require me to overcome these two limitations, so I need to address the issue of how to make electrical connections that are not merely IP68 waterproof, but rugged enough to withstand pressure at depth for a year or more.

I wet-sand the ends (indicated by the red arrows) of that nipple with 600 & 800 grit to smooth away any casting seams.

Wet-sand the ends of that nipple (arrows) with 600 & 800 grit to remove any casting seams. Smooth all o-ring seats.

Use a couple of sizes of heat shrink tube to seal the cable to the pex adapter

On 1/2″ barbs, use a couple of sizes of heat shrink to step down (from Ø12/6 tubing) to the diameter of the cable you are using before sealing the connection with epoxy. Adhesive lined tubing  helps the seal. You can also buy 1/2 NPT x 3/8 PEX adapters for thinner cables, but for some reason they cost twice as much as the larger diameter 1/2 x 1/2″ adapters (?) 

As a diver, I had already seen debates about which connectors are the best on the scuba forums, and a quick Google search quickly finds many suppliers for that market. Most of these are “wet-plugable” connectors encased in delrin/rubber, and they are workhorses in many industrial and military applications. High profile companies like Seacon making a bewildering array of solutions, but their cheapest ones come in around twenty five dollars per socket, so a complete connection will set you back at least $50.



Remove the cone washer before filling adapter with epoxy

Remove the cone washer before filling with epoxy. I score the inside of these 1/2″ barbs with an old 8×125 tap to promote epoxy bonding.

Many of these commercial connectors are rated for deep ocean deployments, able to withstand thousands of psi – far more than I will subject them to in the shallow cave systems. And they often give you a short little pig-tail under the assumption that you will be using it with a cable gland , or a bulkhead connector on a nearby housing ( or at the very least a couple of layers of marine grade adhesive lined heat shrink tubing)  I needed some decent cable runs so I went looking for other applications with longer lines at shallower depths . The pool & underwater lighting folks often use  Bulgin electrical connectors, and the 400 series   Buckaneers  (rated to 10m) occasionally come up on eBay in the $10 range, But again you need to buy two sockets (male&female), and you need to buy the pins which are sold separately. So you still end up around $25 per connection.


Make sure your wires are long enough to extend past the nipple, or you wont be able to make the connection!

Make sure your wires extend all the way through the nipple, or you can’t make the connection! And use soft flexible silicone wires so they fold back into the connector easily.

With all that as background, I went hunting once again through the plumbing section at the local hardware store.  I reasoned that anything that could hold water in, could also hold water out, right?  And I think I have come up with a solution using Nibco pex swivel adapters that is pretty cheap and can be adapted to many different applications. These plumbing adapters are rated to withstand 100 psi, which is roughly equivalent to 230 feet under water.  And that is pressure from the inside out, so my gut feeling is that these things will be able to withstand slightly greater pressure in the other direction, where the forces act to increase the compression of the cone washer.  The thing I like about the swivel adapter mechanism is that it applies pressure to a hard plastic lip on the other side of the washer, so as you tighten the nut there is no rotational force being applied to the parts forming the water tight seal.

And it even looks good!

~$6 for parts, and it looks good too.

These adapters come in a variety of larger & smaller diameters allowing you to use different cable thicknesses, and you can change the length of the pvc riser pipe in the middle to make more space for the internal connectors. This also gives you a way to adjust the amount of air/buoyancy along the run and with a string of connectors this might be a good way to reduce strain on the cable.  I suspect that the schedule 80 tubes in the middle are the weakest point in the system, but filling the internal space with mineral oil would get these connectors to significant depth, as would a filling of paraffin wax, though that would have to be heated again to undo the joint. 



Don’t forget to smooth the seat on that male hose barb side if it has bad casting seams. You want that connection to be as clean as possible. You need to use small connectors for this M-F design.

After building a few of these, I realized that it was possible to make them even simpler if the electrical connectors were small enough.  In the picture here, one side has a Nibco PEX swivel adapter, while the other has a male thread NPT to PEX  adapter. These are both polymer, though it is hard to find an epoxy that will bond to it with an applicator fine enough to put the adhesive into the barb cavity. These fittings are also available in brass for the same price, and the o-ring seats are much cleaner on those than the polymer adapters because there are casting seams on the plastic parts. But I don’t know how well the brass will fare in marine environments. Some of my sensors have stainless shells, so I worry about galvanic effects in salt water?  

After this, pull the cable through so the

Pull these joins into the connector so they are embedded in the epoxy.

In the photo above I’ve used a three wire PC fan connector, and it “just barely” fits inside the cavity of that m/m hose barb. I will use smaller JST connectors in future, or perhaps Dean’s Micro 3pin or 4pin for something more robust.  The internal diameter of the 1/2 pipe is a little over 12mm, so if you need to squeeze more connections in there you could try a couple of “mini micro” JST’s, but I find that soldering all those wires so close together is a bit irritating because it’s easy to accidentally melt the plastic, loosening the tiny pins. 

I also found that the wire inside my cables were too stiff to fold neatly into this much smaller space, so I had to add some flexible 26 AWG silicone wires to the ends. After the jumpers are attached, pull the cable through so that the solder joins get embedded in the epoxy. This has the added benefit of providing a break in the insulation around the wires, so that if you do get a cut in the cable, water can’t work it’s way through the connector by wicking along the copper strands.  I am still hunting for a good supplier of multi-conductor 22-24 awg cable that has a good “handling weight” for underwater applications. It’s hard to shop for something on the internet when what you are really after is something that “feels right” when you hold it in your hands.

A dual connection cap for one of our 2″ underwater housings. We now use harder PUR sheath cables because softer silicone jackets were too easily damaged during deployment dives. For a less expensive option,  Luke Miller has had success using USB cables with his underwater sensors.  A 3/4″ adapter from the pex connector system gives you a way to mount pressure sensors under oil.

I should add the usual provisos here about this being another of my completely experimental ideas so use this at your own risk.  Pex tubing is generally rated to  ~100psi (at temperatures below 74°F) and if the o-rings can withstand that then they should “theoretically” be good to about 60m depth. Translating that into the real world, I expect these connectors to be trustworthy to about 40m, which covers most of our cave deployments.

Addendum 2015-01-30

I just stumbled across a different solution to the expensive underwater connector problem. His method for waterproofing connectors using 3D printed silicone molds is beyond my current capabilities, but its nice to see it explained with such clear documentation.

Addendum 2015-02-01

If those pex adapters don’t have enough room for your cables, I found another great underwater connector project which might do the job for ya 😉

Addendum 2017-01-23

As time goes on I am reducing the number of interconnects, but even with longer chain segments I will probably stick with only 24 sensors per logger.

As time goes on I am reducing the number of interconnects, but even with longer chain segments I will probably stick with only 24 sensors per logger.

Just though I should add an update to mention that quite a few of these connectors have been in service for more than a year on temperature chain deployments. None of them have failed on relatively shallow deployments from 5-15m depth. The only problem I’ve had  is the length of the connector itself can be challenging when you are trying to pack one of those long strings into the mesh bag for an underwater deployment.


Addendum 2018-12-05

I’ve posted a video showing how I build those underwater connectors and use them with epoxy potted sensors:   ( part of our 2017 screw terminal logger series) 

It’s also worth mentioning that you can improve the fit of various parts by taking advantage of the fact that they are thermoplastics:

Rotate & heat the inside corner edge of the tube until ~ 0.5mm of the material ‘softens back’. Don’t over-do it! you don’t want to alter the tube diameter or hurt the threads.

Quickly press the mating o-ring onto the seat while the PVC is still soft enough to conform to the shape.


Before & After heat treatment: the o-rings now form a better seal. This is faster than sanding away any rough casting seams on the parts

Addendum 2020-04-06

These connectors & sensor mounts are part of the housing system we’ve been developing since 2015. You can see the latest underwater housing build @ DIY data-logger Housing from PVC parts

Using a DS18B20 Temperature Sensor “without” a dedicated Arduino library

New housings in production

New housings in production. The sensor wells on top will be filled with epoxy to seal the hull.  Note: some of these housings have unnecessary “chunks” of PVC on the outside, as I made them for latch clamps which I am no longer using in the design.

I haven’t had time to post to the blog lately, as I am now in full-on production mode: trying to get five full units running for deployment next week.  Between all the cutting and sanding of the new underwater housings, I have been investigating sensors, and thinking about how to make the entire unit modular enough to allow quick field repairs.  The I2C bus architecture becomes very attractive here, because so many sensors are available for it, and I found a nifty I2C hub from Seed studios, which gives me a standard plug for connectivity. But most of the sensors I found want a steady 3.3 volt supply, and that is not available on the Tiny Duino  (the lack of a 3.3v rail is a weakness of the platform but I was the one who wanted to run with no voltage regulator in the first place..) So I started designing a power supply module around a NCP1402-3.3V Step-Up Breakout from Sparkfun.  I knew this was going to waste 25% of my power for this deployment, but I figured I could use lithium AA’s to make up for the loss, and look around for a more efficient voltage regulator later.

No luck with the voltage regulator even though it seemed to be working fine...

It seemed to be working fine…

Well, a frustrating day or two later and I still had no joy from the 3.3v regulator. I don’t know if it was an inrush current brownout, or transient spikes, or output ripple…but for some reason the tiny stack simply will not run from this regulator – all I get is one little pip out of the on-board led and that’s it.  Grrr! There goes about half of sensors I planned to use with the nice I2C breakout boards I had just purchased, unless I can somehow power the sensors “separately” from the main mcu stack. (And there is no guarantee I won’t have the same kind of gremlins pestering me there…)

Time to make lemonade: I had a few DS18B20 one wire temperature sensors, and they are not too choosy about input voltage, so I figured I would give them a try, while just running the Tiny’s from unregulated AA’s (…again).  DS18B20’s are common as dirt, and almost as cheap, and there are libraries for them everywhere. So it should not take long to get them going…right?

Well what I thought was going to take me 30 minutes has actually taken me a day of digging to sort out, so I am posting the result here, to hopefully save someone else the trouble.  The standard approach is to install a Dallas control library, and a one wire library to run this sensor. Most sources suggested the library written by Miles Burton, and the one wire library over at PJRC.  And after a few grumbles, like finding out that zip file created directories with the wrong names  ( “dallas-temperature-control” from the zip extraction needs to be renamed to “DallasTemperature” for it to work) I did get the test code running…sort of. Now don’t get me wrong, Miles Burton has created a veritable swiss army knife of a library, but a tiny script for this one sensor weighed in at 8,772 bytes. That’s almost a third of the available program memory, and I already knew my data logger script was around 22 k (with accelerometer) , so that was not going to leave much space for the other sensors I want to add.

Logger units with I2C hub for sensor and RTC connections.

Logger units with I2C hub for sensor and RTC connections.

And while I was sorting all that out, I discovered another problem with the DS18b20: Every once and a while it was throwing out a spurious reading of 85 degrees, but it was frustratingly intermittent. More run tests with different settings showed that the error never happened when I had the sensor set to run at 9-bit, but it popped up more frequently as I raised the bit depth. Back to digging through the forums, which revealed that this is a pretty common issue with the DS18b20. The “default” setting of the registers produces the 85, which is what you get if you read the sensor too soon after a reset. If you sift through the datasheet, you find that when you ask the sensor for the full 12 bits of resolution, you need to wait at least 750 ms for the sensor to embed the temperature data in it’s eeprom before you can read it out. So although the sensor only draws 1.5mA during the conversion, and it goes into standby mode right afterwards,  it was going to hold the whole system in limbo for that conversion time, doing some serious damage my the overall power budget.

More googling, and I came across a great little project blog called BitKnitting, where someone managed to use the sensor without a dedicated sensor library. So it was possible! However, they were only using the integer part of the 12 bit register, and I wanted all of the information, as the temperature variations in cave system is often only fractions of a degree C. I found a floating point capture demonstrated over at the Bildr forum. Combining that with a 1 second Watch Dog Timer sleep (to save power while the mcu waits for the sensor’s temperature conversion) produced this little script which weighs in at 5988 bytes. Not much savings on memory, because of the addition of the wdt library, but hopefully much lighter on the projects power budget. I will be weaving this into the main logger code later. I also have a feeling that I can go rooting through those libraries and delete a few functions I am not using to make them lighter, when I have time.

Addendum 2015-02-25

I think this is approaching the largest string I would want to deploy on a dive.

Addendum 2015: I think this is approaching the largest senor string I would want to deploy on a dive.

I recently started making long chains of these sensors, in an attempt to build a poor mans thermistor string. Here is a link to the post about those experiments.


The DS18B20 is only supposed to draw around 1μA when in standby, and not being interrogated. So it hardly seems worth the bother of turning them on and off to me, but there are a few people doing so by powering the units directly from the digital pins of the Arduino.  I might investigate that later if I have not used the pins for something more productive.


I have also noticed that this sensor seems to warm itself if you have it doing a continuous reads at 12 bit as the script below does (which has the unit at its maximum 1.5 mA current the entire time). So just be warned that if you are using the ‘waterproof’ models, which are encased in a metal sleeve filled with epoxy, you can’t drive the sensor full tilt without internal heat building up & affecting the readings. 

// adapted from
//  and
//  sleep from

#include <avr/sleep.h>
#include <avr/wdt.h>

#include <OneWire.h>
const byte DS18B20_PIN=4;  //sensor data pin
OneWire ds(DS18B20_PIN);
byte addr[8];
float DS18B20float;

void setup() {


//Set up Temp sensor – there is only one 1 wire sensor connected
if ( ! {
Serial.println(F(“—> ERROR: Did not find the DS18B20  Temperature Sensor!”));
else {
Serial.print(F(“DS18B20 ROM address =”));
for(byte i = 0; i < 8; i++) {
Serial.write(‘ ‘);
Serial.print(addr[i], HEX);
delay (200);

void loop() {

DS18B20float = getTemp();
Serial.print(F(“FLOAT temp in celcius: “));
//Note: sensor defaults to a reading of 85 if you read it too soon after a reset!
delay (200);  //just a delay to boot out the coms

// watchdog interrupt
ISR (WDT_vect)
wdt_disable(); // disable watchdog
} // end of WDT_vect

// this returns the temperature from one DS18S20 in DEG Celsius using 12 bit conversion
float getTemp(){
byte data[2];
ds.write(0x44); // start conversion, read temperature and store it in the scratchpad

//this next bit creates a 1 second WDT delay during the DS18b20 temp conversion
//The time needed between the CONVERT_T command and the READ_SCRATCHPAD command has to be at least
//750 millisecs (but can be shorter if using a D18B20 type with resolutions < 12 bits)
MCUSR = 0; // clear various “reset” flags
WDTCSR = bit (WDCE) | bit (WDE); // allow changes, disable reset
// set interrupt mode and an interval
WDTCSR = bit (WDIE) | bit (WDP2) | bit (WDP1); //a 1 sec timer
wdt_reset(); // pat the dog
set_sleep_mode (SLEEP_MODE_PWR_DOWN);
sleep_cpu ();
sleep_disable(); // cancel sleep after wakeup as a precaution

byte present = ds.reset();  //now we can read the temp sensor data;
ds.write(0xBE); // Read Scratchpad
for (int i = 0; i < 2; i++) { // Only read the bytes you need? there is more there
data[i] =;
byte MSB = data[1];
byte LSB = data[0];
float tempRead = ((MSB << 8) | LSB); //using two’s compliment
float TemperatureSum = tempRead / 16; //this converts to C
return TemperatureSum;