Saturday 30 August 2014

RaspberryPi + DS18B20 Temperature Sensor

The DS18B20 is a low cost, simple to use, 1-wire temperature sensor which is ideally suited for use with the Raspberry Pi.


Up to 10 of these sensors can be wired in parallel to a single Raspberry Pi GPIO pin, each giving a reasonably accurate indication of temperature.


The current Raspbian image includes support for these devices, so its quite easy to get them up and running.

 I ordered a pack of 5 "waterproof" sensors with 1metre cables from China via Amazon, which cost less than £5.

The only slight complication for me is that I wanted to use a Raspberry Pi that has a PiMote fitted to the GPIO connector.

Hmm! ...maybe I should buy a RaspberryPi B+


So instead of connecting the device to the default GPIO pin 4, I had to find an input which was not connected to the PiMote circuitry, and then solder wires to the back of the PiMote connector.

Configuration


Although the Raspbian includes the required modules, they are not enabled by default. So we need to open /etc/modules (as root) and add the following 2 lines at the end of this file:-

w1_gpio
w1_therm

In my case I need to use physical connector pin 12, which corresponds to GPIO pin 18.

An additional 4.7k resistor is required between supply & data wires


The default RaspberryPi pin for the DS18B20 data connection can be changed by editing /boot/cmdline.txt (as root) to include an extra command like this:-

bcm2708.w1_gpio_pin=18

Note that each command in this file must be separated from the next one by a space.

After a re-boot, there should be a directory in /sys/bus/w1/devices with a unique identity like this:-

28-000006270a42

EDIT Nov 2015: Important! Please see note about Device Tree at end of post.


The first 2 digits (28) are the device family code, and the remainder is a unique serial number. Each connected sensor can be addressed using its own identity code.

The path /sys/bus/w1/devices/28-000006270a42 includes a symbolic link, so the real path is: /sys/devices/w1_bus_master1/28-000006270a42

Within this folder is the W1_slave file which can be accessed to read the current temperature with a command like this:-

sudo cat /sys/devices/w1_bus_master1/28-000006270a42/w1_slave

...which may give an output similar to:-

5e 01 4b 46 7f ff 02 10 8d : crc=8d YES
5e 01 4b 46 7f ff 02 10 8d t=21875


The temperature is given in milli'C, so in this example the reading is: 21.875'C

The resolution of the DS18B20 can be set from 9 to 12 bits, but I don't think the current drivers allow this to be changed. So with the RaspberryPi this is always set to 12bits, and conversion time is fairly slow at 750ms.

Reading the DS18B20 with Gambas


It is easy to create a simple Gambas program to read this sensor. Since the data from w1_slave contains the adjacent characters "t=" I've used this reference point to isolate the temperature reading.

In a new Gambas project I added a timer and a listbox. My test code looks like this:-

' Gambas class file
 

Const SENSOR As String = "/sys/devices/w1_bus_master1/28-000006270a42/w1_slave"

Public Sub Form_Open()
  Timer1.Delay = 5000   'take a reading every 5 seconds
  Timer1.Start
End

Public Sub Timer1_Timer()
Dim strData As String
Dim fTemp As Float

  Exec ["cat", SENSOR] To strData
  If InStr(strData, "t=") > 0 Then
    Me.Text = "found temperature"
    strData = Mid(strData, InStr(strData, "t=") + 2, 5)   'strip out the temp
    fTemp = CFloat(strData) / 1000 
   'scale in degrees C
    lstTemp.Add(CStr(ftemp), 0)   'add latest reading to top of list
  Else
    Me.Text = "no temp data"
  Endif
End


This code example can be modified to add a time-stamp and tidy the readings:-

' Gambas class file
 

Const SENSOR As String = "/sys/devices/w1_bus_master1/28-000006270a42/w1_slave"
 

Public Sub Form_Open()
  Timer1.Delay = 5000
  Timer1.Start
End

Public Sub Timer1_Timer()
Dim strData As String
Dim fTemp As Float

  Exec ["cat", SENSOR] To strData
  If InStr(strData, "t=") > 0 Then
    Me.Text = "found temperature"
    strData = Mid(strData, InStr(strData, "t=") + 2, 5)
    fTemp = CFloat(strData) / 1000
    fTemp = Round(ftemp, -1)
    lstTemp.Add(Format(Now(), "hh:nn:ss    ") & CStr(ftemp) & String.Chr(176) & "C", 0)
  Else
    Me.Text = "no temp data"
  Endif
End


What next?


So now my RaspberryPi web server is controlling my back-yard light and monitoring room temperature. My experimental web page looks like this:-

The web page dished up by my web server

With the recent local spate of power-cuts (and the subsequent requirement to reset timers) I've decided to control our central heating using a RaspberryPi. The main reasons for wanting to do this are:-
  1. when the power goes off during the night, the boiler does not start on time in the morning (so no hot water or heating)
  2. battery powered devices (like intelligent thermostats & timers) eventually need new batteries, and then reprogramming
  3. a RaspberryPi based system (if it proves to be reliable) can use an RTC + internet to update its time-keeping
  4. 'cos I can

So I think I've found my next project!


*****Edit: Device Tree*****

My system broke late 2015 when I decided to do a system update.
Since kernel 3.18 (early 2015) Device Tree support has been included, which means the above method won't work. In my case I just turned Device Tree off, which you can do by running from terminal:-

sudo raspi-config

...then select Advanced > Device Tree and disable it.

Your alternatives include reading this long thread:-

https://www.raspberrypi.org/forums/viewtopic.php?f=28&t=97314

...or searching for a more recent write up on how to configure your system.

2 comments:

  1. Tanks a lot! This helped me with reassigning 1-wire to another pin.
    And to compress the changes needed from kernel 3.18 and onward, just modify /boot/config.txt to this (and reenable device tree):
    # 1-wire on custom pin with kernel 3.18 or higher:
    dtoverlay=w1-gpio,gpiopin=18
    bcm2708.w1_gpio_pin=18

    ReplyDelete
    Replies
    1. Many thanks for the feedback. This looks like a much better approach than disabling device tree.

      Delete