Hello an welcome back! The purpose of this week’s assignment is to produce a tutorial on how to use the DHT-11 temperature and humidity sensor with the Raspberry pi. In this tutorial I will:
- describe the purpose of the activity,
- introduce the components needed,
- describe how to arrange and mount said components on a breadboard,
- present a very small and useful python library to make use of the sensor and
- provide a python script that reads and presents sensor data.
Additionally, I will also discuss the SPI interface on the Raspberry Pi.
Description
The primary purpose of this tutorial is getting the Raspberry Pi to read temperature and humidity data captured by the DHT-11 sensor. In order to to this, we will run a Python script on the Pi which will be connected to the sensor using its GPIO pins.
Components
For this tutorial you will need:
- A Raspberry Pi
- A DHT-11 temperature and humidity sensor
- A breadboard
- 1 x 10KΩ resistor
- Jumper wires
- A breakout board and 40-pin ribbon cable
The breakout board and 40-pin ribbon cable are not mandatory, but having them might make building the circuit a little bit easier. Also, if like me you bought the Oddwires IoT Kit 1.1, you already have a DHT-11 sensor.
The Setup

DHT11 schematic
The DHT-11 is a very convenient, inexpensive and easy to implement sensor. As you can see from the schematic, for it to work you must connect VDD to 3.3V-5.0V, GND to ground and DATA to an input pin. Also, you must connect a 4.7KΩ-10KΩ resistor from the DATA pin to VCC.
THIS Adafruit tutorial serves as a good starting point for the understanding and setup of the components and the circuit. My setup can be observed in the following schematic and diagram, and the implementation can be observed in the subsequent picture. Notice that the DATA pin of the sensor is wired to the GPIO pin #4 (or pin #9).

Setup diagram

Setup schematic
Go ahead and put your setup together. You should end up with something equivalent to this:

My implementation
NOTE: notice that in my implementation of the circuit, the Vilros breakout board has a 3V3+/- wing of which I’m using the +3.3V pin, but I am wiring the DHT-11’s GND pin directly to the breakout board’s pin 9 (GND) on row 5 of the breadboard. I did this because I wasn’t sure if the -3.3V pin on the 3V3+/- wing corresponded to ground as well.
By the way, both the diagrams and the schematics I presented above (and the ones I will show below) were produced using Fritzing, a free and open-source software. Feel free to download!
DHT-11 Python Library
Being usual amongst low cost sensors, the output format of the DHT-11 is not SPI, I2C or 1-Wire compatible (some common sensor formats, more on SPI later). Instead, the data from the sensor must be polled continuously by the Raspberry pi. For this, Adafruit provides a library in the tutorial mentioned in the previous section, but I decided to use a small Python library I found on Github, written by Zoltán Szarvas (@szazo). It can be downloaded HERE. Opposed to the library provided by Adafruit, Zoltán’s library will work ONLY WITH THE DHT-11.
The library is basically a .py file containing the definition of the classes DHT11 and DHT11Result, which making use of the RPi.GPIO and the time python libraries read the binary data from the sensor.
[sourcecode language=”python” wraplines=”false” collapse=”false”]
#Source code taken from https://github.com/szazo/DHT11_Python/blob/master/dht11.py
import time
import RPi.GPIO as GPIO
class DHT11Result:
‘DHT11 sensor result returned by DHT11.read() method’
ERR_NO_ERROR = 0
ERR_MISSING_DATA = 1
ERR_CRC = 2
error_code = ERR_NO_ERROR
temperature = -1
humidity = -1
def __init__(self, error_code, temperature, humidity):
self.error_code = error_code
self.temperature = temperature
self.humidity = humidity
def is_valid(self):
return self.error_code == DHT11Result.ERR_NO_ERROR
class DHT11:
‘DHT11 sensor reader class for Raspberry’
__pin = 0
def __init__(self, pin):
self.__pin = pin
def read(self):
GPIO.setup(self.__pin, GPIO.OUT)
# send initial high
self.__send_and_sleep(GPIO.HIGH, 0.05)
# pull down to low
self.__send_and_sleep(GPIO.LOW, 0.02)
# change to input using pull up
GPIO.setup(self.__pin, GPIO.IN, GPIO.PUD_UP)
# collect data into an array
data = self.__collect_input()
# parse lengths of all data pull up periods
pull_up_lengths = self.__parse_data_pull_up_lengths(data)
# if bit count mismatch, return error (4 byte data + 1 byte checksum)
if len(pull_up_lengths) != 40:
return DHT11Result(DHT11Result.ERR_MISSING_DATA, 0, 0)
# calculate bits from lengths of the pull up periods
bits = self.__calculate_bits(pull_up_lengths)
# we have the bits, calculate bytes
the_bytes = self.__bits_to_bytes(bits)
# calculate checksum and check
checksum = self.__calculate_checksum(the_bytes)
if the_bytes[4] != checksum:
return DHT11Result(DHT11Result.ERR_CRC, 0, 0)
# ok, we have valid data, return it
return DHT11Result(DHT11Result.ERR_NO_ERROR, the_bytes[2], the_bytes[0])
def __send_and_sleep(self, output, sleep):
GPIO.output(self.__pin, output)
time.sleep(sleep)
def __collect_input(self):
# collect the data while unchanged found
unchanged_count = 0
# this is used to determine where is the end of the data
max_unchanged_count = 100
last = -1
data = []
while True:
current = GPIO.input(self.__pin)
data.append(current)
if last != current:
unchanged_count = 0
last = current
else:
unchanged_count += 1
if unchanged_count > max_unchanged_count:
break
return data
def __parse_data_pull_up_lengths(self, data):
STATE_INIT_PULL_DOWN = 1
STATE_INIT_PULL_UP = 2
STATE_DATA_FIRST_PULL_DOWN = 3
STATE_DATA_PULL_UP = 4
STATE_DATA_PULL_DOWN = 5
state = STATE_INIT_PULL_DOWN
lengths = [] # will contain the lengths of data pull up periods
current_length = 0 # will contain the length of the previous period
for i in range(len(data)):
current = data[i]
current_length += 1
if state == STATE_INIT_PULL_DOWN:
if current == GPIO.LOW:
# ok, we got the initial pull down
state = STATE_INIT_PULL_UP
continue
else:
continue
if state == STATE_INIT_PULL_UP:
if current == GPIO.HIGH:
# ok, we got the initial pull up
state = STATE_DATA_FIRST_PULL_DOWN
continue
else:
continue
if state == STATE_DATA_FIRST_PULL_DOWN:
if current == GPIO.LOW:
# we have the initial pull down, the next will be the data pull up
state = STATE_DATA_PULL_UP
continue
else:
continue
if state == STATE_DATA_PULL_UP:
if current == GPIO.HIGH:
# data pulled up, the length of this pull up will determine whether it is 0 or 1
current_length = 0
state = STATE_DATA_PULL_DOWN
continue
else:
continue
if state == STATE_DATA_PULL_DOWN:
if current == GPIO.LOW:
# pulled down, we store the length of the previous pull up period
lengths.append(current_length)
state = STATE_DATA_PULL_UP
continue
else:
continue
return lengths
def __calculate_bits(self, pull_up_lengths):
# find shortest and longest period
shortest_pull_up = 1000
longest_pull_up = 0
for i in range(0, len(pull_up_lengths)):
length = pull_up_lengths[i]
if length < shortest_pull_up:
shortest_pull_up = length
if length > longest_pull_up:
longest_pull_up = length
# use the halfway to determine whether the period it is long or short
halfway = shortest_pull_up + (longest_pull_up – shortest_pull_up) / 2
bits = []
for i in range(0, len(pull_up_lengths)):
bit = False
if pull_up_lengths[i] > halfway:
bit = True
bits.append(bit)
return bits
def __bits_to_bytes(self, bits):
the_bytes = []
byte = 0
for i in range(0, len(bits)):
byte = byte << 1
if (bits[i]):
byte = byte | 1
else:
byte = byte | 0
if ((i + 1) % 8 == 0):
the_bytes.append(byte)
byte = 0
return the_bytes
def __calculate_checksum(self, the_bytes):
return the_bytes[0] + the_bytes[1] + the_bytes[2] + the_bytes[3] & 255
[/sourcecode]
There are two major advantages to using this dht11.py library. One is that we get builtin error code handling, and the other is that we get access to the sensor data just by using the GPIO number as a function’s argument!
[sourcecode language=”python” wraplines=”false” collapse=”false”]
#Example usage of the dht11.py library by @szazo
…
instance = dht11.DHT11(pin = 14)
result = instance.read()
if result.is_valid():
print(result.temperature)
print(result.humidity)
else:
print("Error: " % result.error_code)
…
[/sourcecode]
Python Script and Execution
First of all, turn off your Raspberry Pi, and put the whole setup together using the 40-pin ribbon cable to join the GPIO pins with the T Cobbler Breakout board:

Complete setup diagram

My complete setup

Pi running with DHT-11 setup connected
Connect your mouse, keyboard, screen, ethane cable/WiFi and power to the Pi and let it boot normally. Download the library from Github using the terminal and the git command:
[code]
$ git clone https://github.com/szazo/DHT11_Python
[/code]
NOTE: Having downloaded the library, make sure to place a copy the file dht11.py it in the same folder as the python script in which you want to make use of it:

The file dht11.py in the same folder as my script tutorial.py.
Once you have done that, open IDLE3 and create a new script with the following contents:
[sourcecode language=”python” wraplines=”false” collapse=”false”]
#Import RPi.GPIO to make use of the GPIO Pins
import RPi.GPIO
#Import Zoltan Szarvas’s DHT11 Python library (./dht11.py)
import dht11
#Initialize the GPIO in BCM Mode to access pins using their GPIO number
RPi.GPIO.setmode(RPi.GPIO.BCM)
#Read data using GPIO pin 4
instance = dht11.DHT11(pin = 4)
result = instance.read()
#Check if result is valid (provided by dht11 library)
if result.is_valid():
#print out temperature and humidity values
print("Temperature: %d C" % result.temperature)
print("Humidity: %d %%" % result.humidity)
#Error handling provided by dht11 library
else:
#print out error code
print("Error: %d" % result.error_code)
#Cleanup the GPIO pins before ending
RPi.GPIO.cleanup()
[/sourcecode]
Save the file using whatever filename you want. In my case I used the filename tutorial.py.

Terminal window executing the script shown on an IDLE3 window
Then execute the script on a terminal using sudo to get admin privileges, and wait to see the output produced by the script:
[sourcecode]
$ sudo python3 tutorial.py
[/sourcecode]

Script output showing temperature and humidity measurements
Eureka! If everything went well, you will see the output strings on the terminal window showing you the current temperature and humidity data sensed by the DHT-11, like in the above image.
If you see an error message like this:

Script output showing an ERROR
you might have a connection error with the circuit. This means the Pi is unable to receive signals from the DHT-11. I suggest you check your setup carefully using the diagrams and schematics presented in the previous sections.
NOTE: all the code I present here can also be found on THIS repository of my Github account.
The Raspberry Pi’s SPI Interface
SPI
SPI stands for Serial Peripheral Interface. It is a short-distance serial bus communication standard developed by Motorola primarily used in embedded systems. Since then, SPI has become one of the most commonly accepted and used serial communications standards. Some of the particularities about SPI are that it is a synchronous serial bus, implementing full-duplex protocol using a ‘master/slave‘ paradigm. This means that all communications are governed by a master system clock, and these communications can go both ways: from master to slave, or from slave to master. The following diagram illustrates the architecture:
If we detail the previous diagram, we can tell that each SPI component has a system clock I/O, a Master Output/Slave Input I/O, a Master Input/Slave Output I/O and a Slave Select I/O. Wether any of these signals is an Input or an Output is determined by the type of component. A master component will have SLCK, MOSI and SSn as Outputs and MISO as Input; a slave component will have an opposite configuration. It is also evident that more than one slave components can be connected to a master component, but for each one an extra Slave Select pin is needed in addition to SCLK, MOSI and MISO.
Another caveat of working with SPI is that you need to ‘send a byte to receive a byte‘. This is true because a master component communicates with a slave component through commands. In a oversimplified way, first it will send the request command to the slave, and the slave will respond with acknowledgement. Then the master will send the slave the address of the data block, and the slave will look up the data and respond with acknowledgement again. Finally the master sends the transfer command to the slave, and the it responds with the data. So as you see,communications take place synchronously. Also, depending on the slave component, specifications for command bytes, times and communications protocol might change, but all SPI components will hold to presented I/O signal scheme.
Raspberry Pi 2 Model B SPI Capabilities
If you detail the Pi’s pin diagram, you will see that GPIO11, GPIO09 and GPIO10 (pins 23, 19 and 21) correspond to SPI_CLK, SPI_MOSI and SPI_MISO respectively:
In this case, our Slave Select I/Os would be GPIO08, GPIO07 (pins 24 and 26) named SPI_CE0_N and SPI_CE1_N respectively. I believe you could also use any of the other unused GPIOs as SS I/Os, but you might have to program them yourself, as GPIO07 and GPIO08 are the only SS pint that are part of the Pi’s builtin SPI bus.
As we can see, the Raspberry Pi is readily equipped with SPI capabilities because it implements the necessary I/Os for the Interface. So, instead of using a temperature sensor like the DHT-11 (which is not very precise), we could use a SPI compatible high precision digital temperature sensor like the Adafruit BME280 (but that’s just a whole different story).
Alright! I hope you enjoyed this post and might have learned a thing or two. See you next week!
-J4D!
Sources
- http://www.micropik.com/PDF/dht11.pdf
- https://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus
- https://www.raspberrypi.org/documentation/hardware/raspberrypi/spi/README.md
- http://www.totalphase.com/support/articles/200349236-SPI-Background
- https://learn.sparkfun.com/tutorials/raspberry-pi-spi-and-i2c-tutorial
- https://projects.drogon.net/understanding-spi-on-the-raspberry-pi/
- http://raspberrypi-aa.github.io/session3/spi.html
- https://github.com/babetoduarte/dht11-tutorial/blob/master/dht11.py
Disclaimer: English is not my first language, though I do handle it well. Please excuse any typos and grammatical errors you might find. If you’re kind enough to point them out, I’ll make sure to correct them.
Thanks!
PelicanPlants
Seen a similar idea elsewhere but using a 1-wire device rather than the DHT-11, the 1-wire setup would seem to have the advantage that you can have multiple sensors on one GPIO pin which seems to me to be better – any comment?
Jorge A. Duarte G.
Hello and thanks for your comment! Well, I think that a 1-wire setup might have the advantage if you need to have the sensor wired farther apart from the Raspberry Pi, or use multiple sensors to average a reading. It would be more simple and advantageous to use only one GPIO pin for this, but note that even though communications between master and slave would be bidirectional, they would also be ‘half-duplex’. You might find a bit more information on that here: https://www.maximintegrated.com/en/app-notes/index.mvp/id/1796.
Daryl
How would I convert printout Temperature from Celsius to Fahrenheit in this code?
Jorge A. Duarte G.
Hello Daryl,
thank you for reading my post, and I hope these blog has been helpful.
In order to print out Fahrenheit units, you must convert the output in Celsius by using the formula:
Tf = Tc*(9/5) + 32.
This means you have to edit portion of the script which returns the temperature, into something like this:
[…]
if result.is_valid():
#print out temperature and humidity values
print(“Temperature: %d F” % (result.temperature * (9/5) + 32) )
print(“Humidity: %d %%” % result.humidity)
[…]
I hope this helps.
Best,
– Jorge