You are on page 1of 9

Analogue Sensors On The Raspberry Pi Using An MCP3008

89
BY MATT ON OCTOBER 20, 2013PYTHON, SENSORS, TUTORIALS & HELP

The Raspberry Pi has no built in analogue inputs which means it is a bit of a pain
to use many of the available sensors. I wanted to update my garage security
system with the ability to use more sensors so I decided to investigate an easy
and cheap way to do it. The MCP3008 was the answer.

The MCP3008 is a 10bit 8-channel Analogue-to-digital converter (ADC). It is cheap, easy


to connect and doesnt require any additional components. It uses the SPI bus protocol
which is supported by the Pis GPIO header.

This article explains how to use an MCP3008 device to provide 8 analogue inputs which
you can use with a range of sensors. In the example circuit below I use my MCP3008 to
read a temperature and light sensor.

Here are the bits I used :

Raspberry Pi
MCP3008 8 channel ADC
Light dependent resistor (LDR)
TMP36 temperature sensor
10 Kohm resistor
The first step is enabling the SPI interface on the Pi which is usually disabled by default.

Please follow my Enabling The SPI Interface On The Raspberry Pi article to setup SPI and
install the SPI Python wrapper.
Circuit
The following list shows how the MCP3008 can be connected. It requires 4 GPIO pins on
the Pi P1 Header.

VDD 3.3V

VREF 3.3V

AGND GROUND

CLK GPIO11 (P1-23)

DOUT GPIO9 (P1-21)

DIN GPIO10 (P1-19)

CS GPIO8 (P1-24)

DGND GROUND

The CH0-CH7 pins are the 8 analogue inputs.

Here is my breadboard circuit :


It uses CH0 for the light sensor and CH1 for the TMP36 temperature sensor. The other 6
inputs are spare.
Here is a photo of my test circuit on a small piece of breadboard :
Light Dependent Resistor
I chose a nice chunky LDR (NORPS-12, datasheet). Under normal lighting its resistance is
approximately 10Kohm while in the dark this increases to over 2Mohm.
When there is lots of light the LDR has a low resistance resulting in the output voltage
dropping towards 0V.

When it is dark the LDR resistance increases resulting in the output voltage increasing
towards 3.3V.

TMP36 Temperature Sensor


The TMP36 temperature sensor is a 3 pin device (datasheet). You can power it with 3.3V
and the middle Vout pin will provide a voltage proportional to the temperature.
A temperature of 25 degrees C will result in an output of 0.750V. Each degree results in
10mV of output voltage.

So 0 degrees will give 0.5V and 100 degrees will give 1.5V.

Reading The Data Using a Python Script


The ADC is 10bit so it can report a range of numbers from 0 to 1023 (2 to the power of
10). A reading of 0 means the input is 0V and a reading of 1023 means the input is 3.3V.
Our 0-3.3V range would equate to a temperature range of -50 to 280 degrees C using the
TMP36.

To read the data I used this Python script :

1 #!/usr/bin/python
2
import spidev
3 import time
4 import os
5
6 # Open SPI bus
7 spi = spidev.SpiDev()
8 spi.open(0,0)
9
# Function to read SPI data from MCP3008 chip
10 # Channel must be an integer 0-7
11 def ReadChannel(channel):
12 adc = spi.xfer2([1,(8+channel)<<4,0])
13 data = ((adc[1]&3) << 8) + adc[2]
14 return data
15
# Function to convert data to voltage level,
16 # rounded to specified number of decimal places.
17 def ConvertVolts(data,places):
18 volts = (data * 3.3) / float(1023)
19 volts = round(volts,places)
return volts
20
21 # Function to calculate temperature from
22 # TMP36 data, rounded to specified
23 # number of decimal places.
24 def ConvertTemp(data,places):
25
26 # ADC Value
# (approx) Temp Volts
27 # 0 -50 0.00
28 # 78 -25 0.25
29 # 155 0 0.50
30 # 233 25 0.75
31 # 310 50 1.00
# 465 100 1.50
32 # 775 200 2.50
33 # 1023 280 3.30
34
35 temp = ((data * 330)/float(1023))-50
36 temp = round(temp,places)
return temp
37
38 # Define sensor channels
39 light_channel = 0
40 temp_channel = 1
41
42 # Define delay between readings
43 delay = 5
44
45
46
47
48
49
while True:
50
51 # Read the light sensor data
52 light_level = ReadChannel(light_channel)
53 light_volts = ConvertVolts(light_level,2)
54
55 # Read the temperature sensor data
56 temp_level = ReadChannel(temp_channel)
temp_volts = ConvertVolts(temp_level,2)
57 temp = ConvertTemp(temp_level,2)
58
59 # Print out results
60 print "--------------------------------------------"
61 print("Light: {} ({}V)".format(light_level,light_volts))
print("Temp : {} ({}V) {} deg C".format(temp_level,temp_volts,temp))
62
63 # Wait before repeating loop
64 time.sleep(delay)
65
66
67
68
69
Here is a screen-shot of the output :
You can download this script directly to your Pi using :

wget https://bitbucket.org/MattHawkinsUK/rpispy-
misc/raw/master/mcp3008/mcp3008_tmp36.py

It can then be run using :

sudo python mcp3008_tmp36.py

Alternatively if you are a Git fan you can clone my Raspberry Pi misc scripts repo using :

git clone https://bitbucket.org/MattHawkinsUK/rpispy-misc.git

Additional Explanation of spi.xfer2


Lots of people have asked about the spi.xfer2 line. This sends 3 bytes to the device. The
first byte is 1 which is equal to 00000001 in binary.
8+channel is 00001000 in binary (where channel is 0). <<4 shifts those bits to the
left which gives 10000000. The last byte is 0 which is 00000000 in binary.

So spi.xfer2([1,(8+channel)<<4,0]) sends 00000001 10000000 00000000 to the


device. The device then sends back 3 bytes in response. The data= line extracts 10
bits from that response and this represents the measurement.

The exact reason why you do the above is explained in the datasheet but that is outside
the scope of this article.

You might also like