1-Wire interface with DS18S20 temperature sensor

First published: 16th July 2013

Well, it turns out this is a really simple project that many people have tried. Sven O. Geggus has a nice summary of four methods of connecting 1-wire devices to a Raspberry Pi. I chose the GPIO method.

Hardware

A matrix board with a 26-pin DIL header socket provided the base for the construction. In the schematic, R1 is the pull-up resistor for the data line, other guides recommend a higher value resistor, 4.7KΩ, but I was intending to use a long network. D1/D2 is two Schottky diodes in a single package (BAT54S), providing some protection against voltage spikes on the network. The BAT54S is a tiny surface-mount component, about the size of a grain of rice, and the contacts are minuscule. Soldering it by hand is a challenge.

I used a 3.5mm stereo jack socket to connect the external network. On previous 1-wire networks, I used RJ45 connectors, but readily-available (cheap) RJ45 sockets have a tendency to allow the plug to slip out partway and loose contact, so I wanted to try something else. The advantage of 3.5mm jack connectors is that headphone extension cables with these are readily available. Also, headphone splitter plugs (two jack sockets with one jack plug) can be used to make a long daisy-chain of sensors. The big disadvantage that I had not considered is that adjacent contacts may be shorted together during plugging/unplugging. With the chosen arrangement of contacts, this can short the 5V supply to ground, rebooting the RPi. Do not plug or unplug the network when the RPi is on!

In the sensor package, R2 and the LED are just there to indicate power is present. C1 is a 10µF tantalum, it should make the power for the DS18S20 smoother on long cable runs. All the components can be crammed into the body of a metal 3.5mm jack plug - heat shrink tubing can be used to protect component leads. The metal body provides good thermal conduction for the DS18S20.

This network arrangement is working well with six sensors on a 10m network.

Software

Recent RPi packages have the working kernel modules installed, but not loaded. They can be loaded manually for initial testing:

sudo modprobe w1-gpio
sudo modprobe w1-therm

The modules create a filesystem for accessing the 1-wire devices:

cd /sys/bus/w1/devices
ls
10-0008029dbea3  w1_bus_master1

Each 1-wire device has a unique id, the first two characters are the family, 10 for the DS18S20, and the id is used as a directory name. The temperature can be read from the w1-slave file:

cd 10-0008029dbea3
cat w1_slave
4b 00 4b 46 ff ff 05 10 05 : crc=05 YES
4b 00 4b 46 ff ff 05 10 05 t=37437

When a temperature is read from the DS18S20, 9 bytes are read, the last is a checksum and the temperature reading is encoded in the rest (see the data sheet for the gory details). The w1-therm module does the tricky work of verifying the checksum and decoding the temperature, so, in the example above, the YES indicates the checksum was correct, and the t=37437 gives the temperature, in thousandths of a degree Celsius, in this case 37.437°C (it was a bit warm).

This fragment of Perl code does a bit more:

#!/usr/bin/perl
use warnings;
use strict;
use vars qw( $opt_d);

my @alldevs;
my $onewire= '/sys/bus/w1/devices';
opendir DEVDIR, $onewire or die "Cannot find 1-wire devices: $!";
@alldevs = readdir DEVDIR;
closedir DEVDIR;

foreach my $dev (@alldevs) {
  unless ($dev =~ /(\d{2})-([a-fA-F\d]{12})/) {
    print STDERR "Skipping one-wire device $dev\n";
    next;
  }
  my $fam= $1;
  if ($fam eq '10') {
    print "$dev ", DS18S20reader("$onewire/$dev/w1_slave"), "\n";
  } elsif ($fam eq '26') {
    print "$dev ", DS2438reader("$onewire/$dev/rw"), "\n";
  }
}

sub DS18S20reader {
  my ($devf) = @_;
  my $rtime = time;
  print STDERR "Reading DS18S20 $devf\n" if $opt_d;
  unless (open( DS18S20, $devf)) {
    print STDERR "Unable to open $devf $!\n";
    return;
  }
  undef $/;
  $_= <DS18S20>;
  close(DS18S20);
  $_ =~ /crc=[a-fA-F\d]+\s(\w+)\s.*?\st=(-?\d+)/s;
  print "CRC OK? $1\tTemp $2\nRaw: $_\n" if $opt_d;
  unless ($1 eq 'YES') {
    print STDERR "CRC $1 for $devf";
    return;
  }
  return $2/1000;
}

sub DS2438reader {
  # Do something interesting here
}

To be honest, I haven't tested this, I cut and modified it from a much longer script that stores the values in a MySQL database and does other stuff. However, if I didn't mess it up, it should read all (up to 10, that's the limit for the w1-gpio module) of the DS18S20 devices attached and print the temperature from each one. You can also see how to expand this for other 1-wire devices, such as the DS2438 battery monitor (which I am using to read from a humidity sensor, but that is another story).

So, how about some real data. On that page, probes 'Lounge', 'table temp' and 'table hum' are being read by a RPi.

Updated: 18th July 2013

I've added another extension cable to the network, now it has a group of five sensors at 10m, and another sensor at 20m. Still working fine.

Updated: 19th July 2013

Tried extending the network further, with two 9m and one 10m extension. Did not work, even with just one more extension. The sensors would report 0°C, and then disappear from the network. The problem is definitely related to the cable length, if I connected the sensors at 10m and 20m as before, and added a extra 10m with nothing at the end, the sensors would report 0°C. I interpret this being a problem of the cable capacitance, the pullup resistor can't raise the bus voltage fast enough to show logic 1 at the controller. Dallas has some information on active pullup drivers for a 1-wire network, I will have to take a closer look at that if I want a longer network.

I think there is a design fault with the 1-wire network:

For a temperature probe, a too-long network gives readings of 0°C, with no way of knowing this is a false reading. If room temperatures are being measured, then 0°C can be recognised as unlikely and rejected, but this is not the case for fridge or freezer temperatures. There could be a costly, or even dangerous, incident if these sensors are used for alerting when food-storage cooling fails.

The obvious fix would be to change the CRC polynominal, so that all-zero data does not produce an all-zero CRC. Dallas would need to reprogram at manufacture, and those sensors would be incompatible with current 1-wire networks that check the CRC.

Updated: 20th September 2016

Well, I did say that the code here was untested. Thanks to Gerd Mevissen for getting me to check it. The errors were:

  1. No declaration for $opt_d, added:

    use vars qw( $opt_d);
  2. Using angle brackets for file input got "eaten" by the HTML interpreter. Re-written using HTML entities:

    $_= <DS18S20>;
  3. Unnecessary close parenthesis before the semi-colon:

    print STDERR "CRC $1 for $devf";


Gallery

1-wire network schematic1-wire network schematic
Temperature probe on 3.5mm stereo jack plugTemperature probe on 3.5mm stereo jack plug
3.5mm stereo jack plug with temperature probe components inside.3.5mm stereo jack plug with temperature probe components inside.
Share