Jumpnow Technologies

home code consulting contact

Using DS18B20 1-wire Temp Sensors with the Raspberry Pi

24 Apr 2017

The DS18B20 one-wire thermometer is fairly easy to get working with the Raspberry Pi.

The Raspberry Pi doesn’t have a dedicated hardware controller for 1-wire devices (some SOCs do), but 1-wire is a very slow bus and it doesn’t take much to bitbang the communication using a GPIO pin.

Linux has a standard driver to do this called w1-gpio.ko.

There is also a w1-therm.ko slave driver to handle the specifics of DS18*20 devices.

Both drivers are enabled as modules in the standard RPi kernels.

The drivers can be loaded by a device tree overlay already provided with the official RPi kernels

w1-gpio-overlay.dtb  

The overlay is the only requirement to use the DS18B20. There is no need for third-party libraries.

I’m testing with Buildroot and Yocto built systems.

Raspbian systems include the w1-gpio-overlay and should work the same.

There are a couple of ways to wire the DS18B20 to the RPi. You need to provide slightly different parameters to the w1-gpio overlay depending on the method used.

Parasitic Powering

Wiring
DS18B20          RPi
-------          ---
VDD (red)        GND
GND (black)      GND
DQ (yellow)      GPIO  -- 4.7k -- 3.3v
config.txt
dtoverlay=w1-gpio,pullup=1

Non-Parasitic Powering

Wiring
DS18B20          RPi
-------          ---
VDD (red)        3.3v
GND (black)      GND
DQ (yellow)      GPIO -- 4.7k -- 3.3v
config.txt
dtoverlay=w1-gpio

The w1-gpio-overlay defaults to using GPIO_4 for the data pin. That’s pin 7 on the header.

To specify a different GPIO pin, add a gpiopin=<pin> argument to the dtoverlay line.

For example, to use GPIO_22 (pin 15) on a parasitic setup, you would use the following

dtoverlay=w1-gpio,pullup=1,gpiopin=22

Remember to use the RPi GPIO pin number, not the header pin number.

With the DS18B20 connected and the appropriate dtoverlay statement in config.txt you should see the w1_gpio, w1_therm and wire drivers loaded

# lsmod
Module                  Size  Used by    Not tainted
ipv6                  405858 24 [permanent]
w1_therm                6401  0                   <--- this
brcmfmac              222226  0
brcmutil                9156  1 brcmfmac
cfg80211              543594  1 brcmfmac
rfkill                 20944  1 cfg80211
bcm2835_gpiomem         3900  0
w1_gpio                 4818  0                   <--- this
wire                   32616  2 w1_therm,w1_gpio  <--- this
cn                      5889  1 wire
uio_pdrv_genirq         3923  0
uio                    10332  1 uio_pdrv_genirq
fixed                   3285  0

The interface to the DS18B20 shows up here

# ls /sys/bus/w1
devices            drivers            drivers_autoprobe  drivers_probe      uevent

# ls /sys/bus/w1/devices
28-0000071cbc72  w1_bus_master1

The 28-0000071cbc72 directory represents this particular thermometer.

The 28 identifies a DS18B20 device.

The 0000071cbc72 represents the unique device id for this particular thermometer. This allows you to have multiple thermometers on the same data line.

Read the thermometer value by reading from 28-0000071cbc72/w1-slave

# cat /sys/bus/w1/devices/28-0000071cbc72/w1_slave
73 01 4b 46 7f ff 0d 10 41 : crc=41 YES
73 01 4b 46 7f ff 0d 10 41 t=23187

The second line t=23187 is the temp in Celsius multiplied by 1000.

Here’s a simple python script that will read the temp and convert to Fahrenheit.

#!/usr/bin/env python3

import glob

def celsius_to_fahrenheit(c):
    return (c * 1.8) + 32.0


def find_devices():
    return glob.glob('/sys/bus/w1/devices/28-*')


def read_temp(path):
    lines = []

    with open(path + '/w1_slave') as f:
        lines = f.readlines()

    if len(lines) != 2:
        return False, 0

    if lines[0].find('YES') == -1:
        return False, 0

    d = lines[1].strip().split('=')

    if len(d) != 2:
        return False, 0

    return True, int(d[1])


if __name__ == '__main__':

    devices = find_devices()

    for device in devices:
        valid, raw = read_temp(device)

        if valid:
            c = raw / 1000.0
            f = celsius_to_fahrenheit(c)
            print('%0.2f F' % (f))