Wednesday 4 December 2013

Raspberry Pi - Python & Temp Sensor DS18B20

I got a DS18B20 temperature sensor a little while back and I wanted to get it connected to a Raspberry Pi, so I could temperature in some of my data logging projects.  There are a couple of really good tutorials which describe how to get the sensor up and running (Cambridge University, Adafruit), but the code examples they provided didn't really fit my needs.

Setting up the sensor
The DS18B20 is a 1-wire digital sensor and is very easy to setup.  It has 3 pins, 3.3v in, data & ground and you will also need a 4.7K-10K resistor 'pull-up' the data line.

Looking at the sensor with the flat side facing you:
  • Pin 1 -> Raspberry Pi GND
  • Pin 2 -> Raspberry Pi GPIO 4
  • Pin 3 -> Raspberry Pi 3V
  • 4.7K resistor goes between Pin 2 & 3


Setup the software
In order to read data from the sensor I needed to install some modules using modprobe.  Once I had I could read the data from the sensor (including the current temperature) just like reading a file.

Install modules:

sudo modprobe w1-gpio
sudo modprobe w1-therm

The sensor appeared as a directory in /sys/bus/w1/devices directory.  The name of the directory is 28-########## with the hashes being the Id of the sensor:

cd /sys/bus/w1/devices
ls
cd 28-***********  (whatever the Id of your sensor)

I could then read the temperature data from the w1_slave file:

cat w1_slave


The data from the sensor looks like this:

f6 01 4b 46 7f ff 0a 10 eb : crc=eb YES
f6 01 4b 46 7f ff 0a 10 eb t=24437

The first line tells us whether the data read was successful with YES.
The second line displays the temperature as t=#####.

The temperature is returned in 1000's of a degress so 24437 is 24.437 centigrade.

Python program
I created a python module which would periodically sample the temperature from the sensor and allow a calling program to read the temperature from module as and when required, similar to the module I wrote for reading GPS data.

There is a more complete example of how to use the module in the code below but simply you use it like this:

#create temp sensor controller, passing Id of sensor and a time to wait between reads
tempcontrol = TempSensorController("28-000003aaea41", 1)

#start up temp sensor controller
tempcontrol.start()

#read temperature
print tempcontrol.temperature.C
print tempcontrol.temperature.F

#stop the controller
tempcontrol.stopController()

Temperature Sensor Controller code

import threading
import time

DEVICESDIR = "/sys/bus/w1/devices/"

#class for holding temperature values
class Temperature():
    def __init__(self, rawData):
        self.rawData = rawData
    @property
    def C(self):
        return float(self.rawData) / 1000
    @property
    def F(self):
        return self.C * 9.0 / 5.0 + 32.0

#class for controlling the temperature sensor
class TempSensorController(threading.Thread):
    def __init__(self, sensorId, timeToSleep):
        threading.Thread.__init__(self)
       
        #persist the file location
        self.tempSensorFile = DEVICESDIR + sensorId + "/w1_slave"

        #persist properties
        self.sensorId = sensorId
        self.timeToSleep = timeToSleep

#update the temperature
        self.updateTemp()
       
        #set to not running
        self.running = False
       
    def run(self):
        #loop until its set to stopped
        self.running = True
        while(self.running):
            #update temperature
            self.updateTemp()
            #sleep
            time.sleep(self.timeToSleep)
        self.running = False
       
    def stopController(self):
        self.running = False

    def readFile(self):
        sensorFile = open(self.tempSensorFile, "r")
        lines = sensorFile.readlines()
        sensorFile.close()
        return lines

    def updateTemp(self):
        data = self.readFile()
        #the output from the tempsensor looks like this
        #f6 01 4b 46 7f ff 0a 10 eb : crc=eb YES
        #f6 01 4b 46 7f ff 0a 10 eb t=31375
        #has a YES been returned?
        if data[0].strip()[-3:] == "YES":
            #can I find a temperature (t=)
            equals_pos = data[1].find("t=")
            if equals_pos != -1:
                tempData = data[1][equals_pos+2:]
                #update temperature
                self.temperature = Temperature(tempData)
                #update success status
                self.updateSuccess = True
            else:
                self.updateSuccess = False
        else:
            self.updateSuccess = False
       
if __name__ == "__main__":

    #create temp sensor controller, put your controller Id here
    # look in "/sys/bus/w1/devices/" after running
    #  sudo modprobe w1-gpio
    #  sudo modprobe w1-therm
    tempcontrol = TempSensorController("28-000003aaea41", 1)

    try:
        print("Starting temp sensor controller")
        #start up temp sensor controller
        tempcontrol.start()
        #loop forever, wait for Ctrl C
        while(True):
            print tempcontrol.temperature.C
            print tempcontrol.temperature.F
            time.sleep(5)
    #Ctrl C
    except KeyboardInterrupt:
        print "Cancelled"
   
    #Error
    except:
        print "Unexpected error:", sys.exc_info()[0]
        raise

    #if it finishes or Ctrl C, shut it down
    finally:
        print "Stopping temp sensor controller"
        #stop the controller
        tempcontrol.stopController()
        #wait for the tread to finish if it hasn't already
        tempcontrol.join()
       
    print "Done"


14 comments:

  1. Great info! I followed the same pattern and also added a Java web application which the Raspberry Pi pythin script posts the temperature readings to via a REST interface. The web app stores the data in a CouchDB database and can show the temperature in diagrams and charts over time. If interested the code is free on GitHub. Keep up the good work!
    http://macgyverdev.blogspot.se/2014/01/weather-station-using-raspberry-pi.html

    ReplyDelete
  2. Nice code, thanks!

    Just a little typo in the conversion to F. 23.0 should be 32.0

    ReplyDelete
    Replies
    1. Thanks Denny.. You are the first person to mention this! Ive updated the post.

      Delete
    2. Yes, I'm in the US where we still us degrees F, so I noticed the temp was off right away.

      I'm just picking up python since I started using Pis for all kinds of things and I'm really liking the language. I particularly like the fact that you can do interrupts based on GPIO pin state changes.

      Your code is the first example I've seen that uses threading. I'd be interested in your thoughts about that method vs just reading the file directly if you have a minute. We can take this off-line if you would prefer.

      Thanks!

      Delete
    3. I wrote the class to use in my gps helmet cam http://www.stuffaboutcode.com/2014/01/raspberry-pi-gps-helmet-cam.html and for this it made sense to use a thread because i didnt want to delay the main program while it read the file directly, so by using threading i get around that issue. Its also a tidy way of monitoring a sensor, by running it in its own thread the calling program doesnt need to worry about how to do this or whether it needs to read the temperature, but it is a more processor intensive solution as im reading the value whether i need to or not.

      Slightly rambling but hopefully that makes sense.

      Delete
  3. This was super nice, intro to threading in python as well! Thanks!

    ReplyDelete
  4. This is very helpful, I'm really new at this. This is the first explanation I've found that actually works. Although the first example doesn't. This is probably more that I actually need. All I really need is for the reading to be placed on a GUI in tkinter.
    How would you make it replace the reading with the new reading instead of making a long list?

    ReplyDelete
    Replies
    1. Im not really sure what you mean? Each time you call tempcontrol.temperature.C you get the temperature at that time, you can do with that whatever you want, write it to the screen using print, assign it to a variable or put it to a GUI.

      Delete
  5. Hi Martin, Many thanks for this useful code. I am modifying your code for multiple sensors and it works first time the sensor prints the values, but the 2nd and subsequent times the remaining sensors do not update. Any ideas?

    import threading
    import time

    DEVICESDIR = "/sys/bus/w1/devices/"

    #class for holding temperature values
    class Temperature():
    def __init__(self, rawData):
    self.rawData = rawData
    @property
    def C(self):
    return float(self.rawData) / 1000
    @property
    def F(self):
    return self.C * 9.0 / 5.0 + 32.0

    #class for controlling the temperature sensor
    class TempSensorController(threading.Thread):
    def __init__(self, sensorId, timeToSleep):
    threading.Thread.__init__(self)

    #persist the file location
    self.tempSensorFile = DEVICESDIR + sensorId + "/w1_slave"

    #persist properties
    self.sensorId = sensorId
    self.timeToSleep = timeToSleep

    #update the temperature
    self.updateTemp()

    #set to not running
    self.running = False

    def run(self):
    #loop until its set to stopped
    self.running = True
    while(self.running):
    #update temperature
    self.updateTemp()
    #sleep
    time.sleep(self.timeToSleep)
    self.running = False

    def stopController(self):
    self.running = False

    def readFile(self):
    sensorFile = open(self.tempSensorFile, "r")
    lines = sensorFile.readlines()
    sensorFile.close()
    return lines

    def updateTemp(self):
    data = self.readFile()
    #the output from the tempsensor looks like this
    #f6 01 4b 46 7f ff 0a 10 eb : crc=eb YES
    #f6 01 4b 46 7f ff 0a 10 eb t=31375
    #has a YES been returned?
    if data[0].strip()[-3:] == "YES":
    #can I find a temperature (t=)
    equals_pos = data[1].find("t=")
    if equals_pos != -1:
    tempData = data[1][equals_pos+2:]
    #update temperature
    self.temperature = Temperature(tempData)
    #update success status
    self.updateSuccess = True
    else:
    self.updateSuccess = False
    else:
    self.updateSuccess = False

    if __name__ == "__main__":

    #create temp sensor controller, put your controller Id here
    # look in "/sys/bus/w1/devices/" after running
    # sudo modprobe w1-gpio
    # sudo modprobe w1-therm
    tempcontrol = TempSensorController("28-03168b14dbff", 1)
    tempcontrol2 = TempSensorController("28-0416935ab0ff", 2)
    tempcontrol3 = TempSensorController("28-051692c5bdff", 3)
    try:
    print("Starting temp sensor controller")
    #start up temp sensor controller
    tempcontrol.start()
    #loop forever, wait for Ctrl C
    while(True):
    print "Sensor 1", tempcontrol.temperature.C, tempcontrol.temperature.F
    print "Sensor 2", tempcontrol2.temperature.C, tempcontrol2.temperature.F
    print "Sensor 3", tempcontrol3.temperature.C, tempcontrol3.temperature.F
    time.sleep(10)
    #Ctrl C
    except KeyboardInterrupt:
    print "Cancelled by user"

    #Error
    except:
    print "Unexpected error:", sys.exc_info()[0]
    raise

    #if it finishes or Ctrl C, shut it down
    finally:
    print "Stopping temp sensor controller"
    #stop the controller
    tempcontrol.stopController()
    #wait for the tread to finish if it hasn't already
    tempcontrol.join()

    print "Done"

    ReplyDelete
    Replies
    1. You are only starting the first temperature controller.

      tempcontrol.start()

      You need to start the other 2 as well, i.e:

      tempcontrol.start()
      tempcontrol2.start()
      tempcontrol3.start()

      Its a good idea to stop them at the end of the program as well.


      Delete
    2. Scuse me, I'm new to Python, and rusty at other programming languages, but I was attempting to modify Mark's code to use five sensors. But even as the posted code doesn't work for me(even after proper indents were added)I was wondering if the Python version (2 or 3) made any difference.

      Delete
    3. Funny how as soon as you post something stupid, you find the answer to your woes? So I compared Martin's code, which worked fine to the code I modified from Mark, and once I corrected for all the indents, etc, plus my tinkering and modifications, I got something I can use.

      Delete

Note: only a member of this blog may post a comment.