Connect DS18B20 sensors to Raspberry Pi

RPI cpu temperature

Digital temperature sensor DS18B20 are very popular among hobbyist because they are quite cheap, provide satisfactory precision for number of applications and can be easily interfaced with microcontrollers or Raspberry Pi. In this article we are going to explain how to periodically measure temperature with DS18B20 sensors that are attached to Raspberry Pi. This is quite common situation in number of different applications (like house monitoring, drying process monitoring, etc.). The measured values are stored to MySql database for later examination.

DS18B20 has an operating temperature range of -55°C to +125°C and is accurate to ±0.5°C over the range of -10°C to +85°C. The most popular are those in TO-92 package as shown in image below. The sensors has three pins: VDD, GND and DQ (data input/output line). The power supply range is between 3.0 V and 5.5 V so can be easily interfaced to the 5V and 3.3V microcontrollers. In addition, it can be powered from DQ line (parasite power) so only two wires are needed. For communication with the sensor over DQ line 1-wire protocol is used. Each DS18B20 sensors has a unique 64-bit serial code. Therefore, you can connect number of them on same line.

Hardware

When connecting multiple sensors to device like microcontroller or Raspberry Pi, the sensors should be connected in a bus like fashion. Basically, this means that one cable with three wires is deployed and the sensors are attached to it at desired locations. Each sensor should be connected to the bus with wires as short as possible. In 1-wire communication, the bus is in idle state at high level. Therefore, you have to connect pull-up resistor (about 5k) between VDD and DQ line.

In our setup we have connected three DS18B20 sensors to Raspberry Pi. The bus consists of three wires which are connected to the 3.3V, GPIO4 and GND pin on Raspberry Pi GPIO header and to appropriate pins of each sensor. Pull-up resistor of 4.7k is connected between 3.3V and GPIO4.

Software

We wrote simple python script which gets sensor readings from DS18B20 sensors every 10 minutes and store the readings to MySql database. Let's first prepare the database. Install MySql and python module for accessing database by running the following command in shell:

sudo apt-get install mysql-server python-mysqldb

After completing the installation, log into MySql:

sudo mysql -u root -p

Inside MySql create database:

CREATE DATABASE TempLog;

and table which will have sensor ID, date stamp, time stamp and measured value columns:

USE TempLog;
CREATE TABLE DS18B20( measurement_id INT NOT NULL AUTO_INCREMENT,
                      sensor_id INT NOT NULL,
                      date DATE NOT NULL,
                      time TIME NOT NULL,
                      value varchar(50),
                      PRIMARY KEY ( measurement_id ));

Make one user which can write the sensor readings to this database:

GRANT INSERT,SELECT ON TempLog.* TO 'sensor_writer'@'localhost' IDENTIFIED BY 'password';
FLUSH PRIVILEGES;

Our Raspberry Pi with Raspbian OS acts like a master on a bus and gets the readings from sensors. Luckily, the 1-wire communication on GPIO4 is already implemented as kernel module in Raspbian which should be loaded before trying to read the sensors. Load modules with the following shell commands:

sudo modprobe w1-gpio
sudo modprobe w1-therm

If the sensors are properly connected to RPi GPIO header, the following command should return number of slaves on the bus:

cat /sys/bus/w1/devices/w1_bus_master1/w1_master_slave_count

and this command:

cat /sys/bus/w1/devices/w1_bus_master1/w1_master_slaves

returns unique ID of each sensor that is connected to the bus. In our case previous command gives the following output:

28-0000029f82cc
28-000005e791b9
28-000005e3816f

As can be seen, each sensor is represented with the unique ID (28-xxxxxxxxxxxx). If sensor ID does not start with "28" then you probably have some other temperature sensor from DS18x20 family.

Sensor reading can be performed by reading w1_slave link in /sys/bus/w1/devices/28-xxxxxxxxxxxx directory. For example by running the following command:

cat /sys/bus/w1/devices/28-000005e3816f/w1_slave

we get something like this:

aa 01 4b 46 7f ff 06 10 84 : crc=84 YES
aa 01 4b 46 7f ff 06 10 84 t=26625

In the first line we can see if the reading was successfull ("YES") and in the second line after "t=" is the temperature reading in mili degreees Celsius (26625). Howver, we want to automate this reading and store the read values to the database.

Our python script loops through all available sensors on the bus. If the reading is successfull, the reading is stored in MySql database with date and time stamp. If error occurs during sensor reading (CRC is not valid) or script cannot access database, the error is logged to the file (/home/pi/DS18B20_error.log). Here is the whole script:

import time
import os
import fnmatch
import MySQLdb as mdb
import logging
logging.basicConfig(filename='/home/pi/DS18B20_error.log', level=logging.DEBUG,
                    format='%(asctime)s %(levelname)s %(name)s %(message)s')
logger=logging.getLogger(__name__)

os.system('modprobe w1-gpio')
os.system('modprobe w1-therm')

#function for storing readings into MySql
def insertDB(IDs, temperature):

  try:

    con = mdb.connect('localhost', 'sensor_writer', 'password', 'TempLog');
    cursor = con.cursor()

    for i in range(0,len(temperature)):
      sql = "INSERT INTO DS18B20(sensor_id, date, time, value) \
      VALUES ('%s', '%s', '%s', '%s' )" % \
      (IDs[i], time.strftime("%Y-%m-%d"), time.strftime("%H:%M"), temperature[i])
      cursor.execute(sql)
      sql = []
      con.commit()

    con.close()

  except mdb.Error, e:
    logger.error(e)

#get readings from sensors every 10 minutes and store them to MySql
while True:

  temperature = []
  IDs = []

  for filename in os.listdir("/sys/bus/w1/devices"):
    if fnmatch.fnmatch(filename, '28-*'):
      with open("/sys/bus/w1/devices/" + filename + "/w1_slave") as fileobj:
        lines = fileobj.readlines()
        if lines[0].find("YES"):
          pok = lines[1].find('=')
          temperature.append(float(lines[1][pok+1:pok+6])/1000)
          IDs.append(filename)
        else:
          logger.error("Error reading sensor with ID: %s" % (filename))

  if (len(temperature)>0):
    insertDB(IDs, temperature)

  time.sleep(600)

Stored readings can be checked by running the following commands in MySql:

USE TempLog;
SELECT * FROM DS18B20;

Sources:

  • Maxim Integrated datasheet: DS18B20 Programmable Resolution 1-Wire Digital Thermometer
  • Application Note 162: Interfacing the DS18x20/DS1822 1-Wire Temperature Sensor in a Microcontroller Environment

 

Share:  Add to Facebook Tweet This Add to Delicious Submit to Digg Stumble This