home |  electronics |  toolbox |  science club |  tuxtalk |  photos |  e-cards |  online-shop



Raspberry PI, reading temperatures with a DS18S20 sensor

My daughter owns a Raspberry PI and this is a little project I did with her during spring break. She enjoyed exploring this little computer and it's a perfect tool for young children to get started. You get a much better understanding what a computer actually is in terms of hardware since you can hold the bare board in your hands. We went first over the board of the Raspberry PI to discuss what the different chips on the board do and then we started with this little temperature measurement project.

DS18S20 and DS18B20

The DS18S20 or DS18B20 are digital temperature sensors that are already calibrated. It means that this 3 pin sensor itself has already a kind of computer inside that converts the output of the actual sensor (the one on the chip inside the DS18S20) into digital data (numbers). The sensor data is then read via a 1-wire bus, a bi-directional digital communication bus that requires only one wire. The computer sends a command to the DS18S20 and tells it to start a measurement cycle. The DS18S20 replies then back with the data. The good thing about the Raspberry PI is that it has some linux kernel modules which know this 1-wire protocol already. To read one or more sensors you just read a file under "/sys/bus/w1/devices/" and the Raspberry PI will then read at that very moment the sensor.
pi@raspberrypi:~ $ ls /sys/bus/w1/devices
10-0008014fd32f  10-000801cb40f0  w1_bus_master1

pi@raspberrypi:~ $ more /sys/bus/w1/devices/10-000801cb40f0/w1_slave 
29 00 4b 46 ff ff 08 10 eb : crc=eb YES
29 00 4b 46 ff ff 08 10 eb t=20250

pi@raspberrypi:~ $ more /sys/bus/w1/devices/10-0008014fd32f/w1_slave 
3d 00 4b 46 ff ff 08 10 aa : crc=aa YES
3d 00 4b 46 ff ff 08 10 aa t=30250

The hex numbers you see in that file is the actual data received from the sensor and linux converts this into more human readable temperature values. The value behind "t=" is °C*1000 (milli degree celsius).

Connecting the sensors

You can use either DS18S20 or DS18B20 sensors. They differ a bit in accuracy but for most use cases this is really irrelevant. Any of them is more accurate than any analog thermometer you can find in the households. The kernel modules of the Raspberry PI handling the digital communication with the sensors can handle both. The sensors look like little transistors in a plastic case. Polarity is important. One of the outer pins is ground and the other one is +3.3V DC. The center pin is the data pin.

a ds18s20 sensor

a ds18s20 sensor


To connect this little sensor to the Raspberry PI you will just need one other component: A 4.7 KOhm resistor. Each sensor has a unique identifier burned into the chip during production and this is how they will be identified under /sys/bus/w1/devices. The same sensor will always have the same number (e.g 10-0008014fd32f) but you don't know up front which number a given sensor has. It's not printed onto the case of the sensor. You can connect multiple sensors to the same Raspberry PI since 1-wire is a bus. That is: it can handle several sensors on the same cable. You just connect the sensors in parallel. It's still only one 4K7 pull-up resistor for all sensors on the bus. Here is how to connect the sensor(s) to the Raspberry PI.

ds18s20 sensor to Raspberry PI connection

How to connect a ds18s20 sensor to a Raspberry PI. You can build this on a little bread board. No soldering required.


In older Raspberry PIs you can simply load manually the required kernel modules w1_gpio and w1_therm with the command modprobe. Newer Raspbian OS versions (2016 and later) have introduced a GUI to load those modules and loading just the kernel modules with modprobe does not seem to result in proper sensor readings. The kernel modules load but no sensor files are found under /sys/bus/w1/devices.

pi@raspberrypi:~ $ more /etc/os-release 
PRETTY_NAME="Raspbian GNU/Linux 8 (jessie)"
NAME="Raspbian GNU/Linux"
VERSION_ID="8"
VERSION="8 (jessie)"
ID=raspbian
ID_LIKE=debian
HOME_URL="http://www.raspbian.org/"
SUPPORT_URL="http://www.raspbian.org/RaspbianForums"
BUG_REPORT_URL="http://www.raspbian.org/RaspbianBugs"


The GUI to load the kernel modules can be found here:

Raspberry PI config menu

Go to the Raspberry PI config menu

Raspberry PI config menu dialog window

The Raspberry PI config men dialog window


Enable 1-wire and after that shut down the Raspberry PI, connect the sensor and power it up again.

As an alternative to the GUI you can possibly edit the file /boot/config.txt and add the line "dtoverlay=w1-gpio,gpiopin=4" at the end of the file but we did not try that. We used the GUI.

After those changes and booting up the Raspberry PI again you should see a directory under /sys/bus/w1/devices/ (type in a terminal: ls /sys/bus/w1/devices/). In that directory should be a file called w1_slave and it contains the temperature reading. In other words the command
more /sys/bus/w1/devices/*/w1_slave
Will read all your DS18S20 (or DS18B20) sensors one after the other. You will notice that it's a bit slow to read those virtual w1_slave files because the 1-wire protocol bus speed is rather low to avoid problems with electromagnetic interference even without special cables or shielding.

A script to print the temperature sensor values in human readable format

Next we wrote a shell script. This is an opportunity get hands on experience with the linux bash shell and to see that one can actually write programs with it. We called the script tempread.sh
#!/bin/bash
# 
if [ -n "$1" -a "$1" = "-h" ]; then
	echo "USAGE: $0 [-h]"
	echo "read all 1wire temperature sensors form /sys/bus/w1/devices/"
	exit 0
fi
sensdir=/sys/bus/w1/devices
for s in $sensdir/*-*/w1_slave; do
	if [ ! -r $s ]; then
		echo "ERROR: can not read sensor $s"
	else
		#  we are looking for a line with t= 
		# 41 00 4b 46 ff ff 07 10 38 t=32312
		awk '/t=/{sub(/^.+t=/, "");c=$0/1000;print c " C"}' $s
	fi
done

Copy paste this into the text editor and save it as tempread.sh. Go back to the shell and type "chmod 755 tempread.sh" to make it executable. After that you can run

pi@raspberrypi:~ $ ./tempread.sh


... and it will print the temperature readings of all your sensors in °C.

Temperature reading with any web browser

To play with the hardware, to write the script and to get it all to work was something that my daughter enjoyed but to be able to read the temperature on any device connected to the home WIFI network was really "cool".

I mentioned already that reading the sensors are a bit slow (about 1/2 sec to read the data of one sensor). It's slow but OK if you are just connecting this to your home wifi network. The firewall in your router will block by default access for anybody outside your home network. Thus the amount of connections per seconds that you have to handle is not an issue. The following little web server is a simple perl script and you can just copy/paste it. No software installation required. If you want to make all this available on the open internet such that anybody in the world can read the temperature then I recommend a different strategy: install a real web server on the raspberry PI and do not read the sensors directly. Instead run the script to read the sensors periodically (e.g every 5min) and write the output (and some html code) to a file. The web server can the just serve that file as fast as possible and it does not need to wait for the slow 1-wire bus.

#!/usr/bin/perl -w
# vim: set sw=4 ts=4 si et:
# Simple single threaded web server that displays the output of ./tempread.sh
#
# Usage: perlweb.pl [portnumber] [ip-to-bind-to]
# Example: ./perlweb.pl
# The above example will answer http requests at port 8000 
# on any interface that this machine has.
# Try:
# curl -v http://localhost:8000
#
use strict;
use Socket;
#
my $listenport = $ARGV[0] || 8000;
socket (Server, PF_INET, SOCK_STREAM, 6) || die ("Error socket: $!"); # protocol 6 is tcp
setsockopt(Server, SOL_SOCKET,SO_REUSEADDR,1) || die ("Error setsockopt: $!");
my $sockaddr = sockaddr_in($listenport, $ARGV[1] ? inet_aton($ARGV[1]) : INADDR_ANY) || die ("Error sockaddr_in: $!");
bind(Server,$sockaddr) || die ("Error bind: $!");
listen(Server,SOMAXCONN) || die ("Error listen: $!");

my $caddr;
my $buffer;
my $tmpincelsius="unknown";
while ($caddr = accept(Client, Server)) {
    recv(Client,$buffer,1000,0);
    if ($buffer && $buffer=~/^GET /){
        print Client "HTTP/1.1 200 OK\r\n";
        print Client "Content-Type: text/html\r\n";
        print Client "Server: perlweb/1.0\r\n";
        print Client "Connection: close\r\n";
        print Client "\r\n";
        print "client:\n$buffer\n";
        # now read the sensor, this is a bit slow because it it in real time
        print Client "<h1>Temperature in my room</h1>\n";
        open(SF,"./tempread.sh |")||die "ERROR: can not read from pipe\n";
        while(<SF>){
            print Client $_ . "<br>";
        }
        close SF;
    }
    close Client;
}
#
__END__ 

Copy the above script into a text editor and save it under the name perlweb.pl in the same directory where you have the tempread.sh script. Make the script executable by running in the command "chmod 755 perlweb.pl" and then start it with ./perlweb.pl This little web server will try to run ./tempread.sh and display the output. In other words you have to be in the directory where perlweb.pl and tempread.sh are when you start it as ./perlweb.pl.
pi@raspberrypi:~ $ ./perlweb.pl &


Next we need to find the IP address of the Raspberry PI. For this run the command ifconfig:
pi@raspberrypi:~ $ ifconfig 
eth0      Link encap:Ethernet  HWaddr b8:27:eb:42:f4:cf  
          inet addr:10.0.0.12  Bcast:10.0.0.255  Mask:255.255.255.0
          inet6 addr: fe80::e2c7:cb8:86d5:f27c/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:15607 errors:0 dropped:9 overruns:0 frame:0
          TX packets:7233 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:1736206 (1.6 MiB)  TX bytes:1023444 (999.4 KiB)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:360 errors:0 dropped:0 overruns:0 frame:0
          TX packets:360 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1 
          RX bytes:47276 (46.1 KiB)  TX bytes:47276 (46.1 KiB)

wlan0     Link encap:Ethernet  HWaddr b8:27:eb:17:a1:9a  
          inet6 addr: fe80::c56b:6c07:e267:4e04/64 Scope:Link
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:7173 errors:0 dropped:7173 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:1666950 (1.5 MiB)  TX bytes:0 (0.0 B)

If you have it connected via a cable to the network then you will will find its IP under eth0 as shown above (10.0.0.12) otherwise if you use wifi then it will be under wlan0 in the ifconfig printout.

Now take another computer or iPad, whatever,... and use the URL http://10.0.0.12:8000 where you replace 10.0.0.12 with the IP from your Raspberry PI.
Raspberry PI as a web server for temperature readings

Reading the temperature via the web browser of another device.


Cool stuff!


Back to: "No preservatives added"



Copyright © 2004-2018 Guido Socher