Raspberry Pi with MCP3008, TMP36, SQLite and lighttpd

Introduction

I’ve found a few posts out there that cover connecting a TMP36 (analogue temperature sensor) to the Raspberry Pi via the MCP3008 ADC (Analogue to Digital Converter) but I wanted to expand on the thing that I found confusing – the associated Python code that interfaces with the MCP3008.  By using the MCP3008 datasheet, some Python help pages and the official documentation for the interface library I got to the point where I could write & understand the code and what it was doing (sometimes posts can just be followed without really knowing what key pieces of a circuit or code do which really restricts you taking the concepts and building on them).

In addition to the basic operations of the MCP3008 with a TMP36 I’ll cover installing SQLite & Lighttpd, outputting temperature related data to a SQLite database and displaying this on a simple webpage.

Parts

You’ll need the following parts for this circuit:

  • Raspberry Pi (with latest Raspbian)
  • Breadboard
  • Male-female jumper leads (Raspberry Pi to Breadboard)
  • Breadboard jumper wires.
  • MCP3008 ADC chip (a few GBP £)
  • TMP36GT9Z analogue temperature sensor (there are a few TMP36 variants, just ensure it’s the TO-92 package i.e. the 3 pin plastic header style)
  • Two 100nF ceramic capacitors

Raspberry Pi with MCP3008, TMP36, SQLite and lighttpd

Pi Preparation

We need to set up a couple of things before we start – enable the hardware SPI interface and install a python module to help with interfacing to the MCP3008.

NOTE: The documentation for the python module is listed in the references but it details the steps required to complete the following tasks.

All the steps below (and all further steps in blue) can be run via an SSH session to your Raspberry Pi.

Run through the three steps below to make sure your Raspberry Pi is up to date:

$ sudo apt-get update

$ sudo apt-get upgrade

$ sudo reboot

Ensure you have Python Dev package (and install it if not):

$ sudo apt-get install python-dev

Open the current kernel module blacklist file and place a hash ‘#’ in front of the line blacklist spi-bcm2708.

$ sudo nano /etc/modprobe.d/raspi-blacklist.conf

Save and exit with Ctrl+X, Y to confirm and then Enter.  Reboot the Pi:

$ sudo reboot

If you want to confirm the module has loaded on restart, you can list the status of the modules in the kernel:

$ lsmod | grep spi_bcm2708

You should see one result back with spi_bcm2708  at the start of the line.

Lastly we need to install the SpiDev Python module:

$ mkdir python-spi

$ cd python-spi

$ wget https://raw.github.com/doceme/py-spidev/master/setup.py

$ wget https://raw.github.com/doceme/py-spidev/master/spidev_module.c

$ sudo python setup.py install

The SPI interface on the Raspberry Pi is enabled and the spidev module is available to Python.

Circuit Details

The Raspberry Pi pin requirements are as follows:

Pin 1 – 3.3v
Pin 6 – Ground
Pin 19 – MOSI (Master In Slave Out)
Pin 21 – MISO (Master Out Slave In)
Pin 23 – CLK (Clock)
Pin 24 – CE0 (Select 0)

The TMP36 pin three pin assignments are shown below (from datasheet):

The detailed point to point connections can be found in the table below.  A couple of notes about the MCP3008 pins:

  • AGND is Analogue Ground and DGND is Digital Ground.
  • CH0 is the first of 8 channels on the MCP3008 ADC.

 

MCP3008

The MCP3008 datasheet (linked in the references) has many sections that are useful for this small project but a few are definitely worth reading to help understand the Python code later (which I’ll expand on then).  These are table 5.2 on page 19 and all of section 6.1.  The table details the configure bits we will send to select our mode (single channel) and which channel will be used.  Section 6.1 details the use of the MCP3008 with Microcontroller SPI ports (which we use on the Raspberry Pi) – specifically the three 8 bit sequences that are transmitted from the Pi and sequences that are returned with data.

TMP36

The TMP36 can detect temperatures in the range -40 °C to +125 °C.  Each degree Celsius is 10mV and because it can detect temperatures below zero it has a 500mV reference voltage (0°C).  If you open the data sheet (linked in the references) and go to page 5 figure 6 (line b) you’ll see 0V is actually -50°C (but this can’t be detected).  The guide for the sensor is 750mV at 25°C (which makes sense: 25 x 10mV + 500mV reference = 750mV).

After originally posting this guide I found that adding a capacitor across Pin 1 (+Vs) and Pin 3 (GND) and across Pin 2 (VOUT) and Pin 3 (GND) stopped a small voltage drop that was occurring on the way to the MCP3008 and stopped the readings being affected by other elements of the circuit (touching/moving wires, adding additional components).

Python

I’ll list out the basic code first then go through it in detail. This code will read the temperature and display the data every 1 second:

$ sudo nano <yourfile>.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/usr/bin/python
import spidev
import time
spi = spidev.SpiDev()
spi.open(0,0)
def read_spi(channel):
    spidata = spi.xfer2([1,(8+channel)<<4,0])
    print("Raw ADC:      {}".format(spidata))
    data = ((spidata[1] & 3) << 8) + spidata[2]
    return data
try:
    while True:
        channeldata = read_spi(0)
        voltage = round(((channeldata * 3300) / 1024),0)
        temperature = ((voltage - 500) / 10)
        print("Data (dec)    {}".format(channeldata))
        print("Data (bin)    {}".format(('{0:010b}'.format(channeldata))))
        print("Voltage (mV): {}".format(voltage))
        print("Temperature:  {} degC".format(temperature))
        print("-----------------")
        time.sleep(1)
except KeyboardInterrupt:
    spi.close()

Save and exit nano (Ctrl+X then y).  You should be able to run this code as is and get the following output:

$ sudo python <yourfile>.py

Raw ADC: [0, 0, 213]
Data (dec) 213
Data (bin) 0011010101
Voltage (mV): 687.0
Temperature: 18.7 degC
—————–

The decimal data value above is 213.  As the MCP3008 is a 10bit ADC it will have 1024 possible values (0-1023).

Line 1, 3 and 4

The shebang for python (allowing you to make this script executable without calling python first) is at the top and then the import of spidev (the python module installed above) and time (so we can use the sleep function).

Lines 6 and 7

Initiate the SPI interface and open 0,0 (this is CE0 on the Raspberry Pi – if you wanted to use CE1 you would use 0,1).  You can see the spidev devices on your system:

$ ls -al /dev/spidev*

crw-rw—T 1 root spi 153, 0 Jan 1 1970 /dev/spidev0.0
crw-rw—T 1 root spi 153, 1 Jan 1 1970 /dev/spidev0.1

Line 9

This defines the function read_spi and receives an input variable that it’ll assign to the name channel (we’ll get to this shortly)

Line 10 and 11

Here we are transferring data to the MCP3008 so that we get the required data back.  The spidev documentation for the xfer2 function states:

Syntax: xfer2([values])
Returns: [values]

So we transfer values inside ([ and ]) and we’ll receive values back.  These values are in decimal format for our benefit but within the MCP3008 they obviously exist and operate in binary.  Let’s break down line 12:

spidata is the variable we want to store the returned data.

We then send three comma separated values to the MCP3008 .  These values are each one byte (8 bits) and are built up from figure 6.1 (page 21) of the MCP3008 datasheet (mentioned earlier)

First byte (1) – The first 8 bits transmitted should be the start bit which is just 1 or 00000001 in binary

Second byte ((8+channel)<<4) – The second 8 bits must contain the control bit selections as shown in table 5.2 of the MCP3008 datasheet.  We’re in single mode and using channel 0 (as mentioned above there are 8 channels, 0-7) so we need to use 1000 (1 for the Singe/Diff, 0 for D2, 0 for D1 and 0 for D0).  This value (1000) is 8 in binary and we then add the channel to this, but the channel is 0 so it stays at 8.

NOTE: If the channel was 1 the value here would become 9 or 1001 in binary – checking the table this is the combination needed for CH1 (channel 1).

Finally we need to perform a bitwise operation on the value 8 (1000) because the second byte (8 bits) needs to have control bit selections at the beginning  (in the most significant bits).  The value (8) is really 00001000 in 8 bit binary and we need to shift our control bit selections to the left 4 places to give us 1000000.  This is what the <<4 does – it shifts the value left 4 places.  This is a useful trick to ensure our bits are in the location they should be without additional numerical calculation on our part.

Raspberry Pi with MCP3008, TMP36, SQLite and lighttpd Diagram

Third byte (0) – This is zero as the MCP3008 doesn’t care what these last 8 bits are (this is the same with the last 4 bits of the second byte when the shifting operation occurs).  So 00000000 is sent.

Let’s recap what our final value being sent to MCP3008 would look like if we could see it:

([1,128,0]) = ([00000001,10000000,000000000]) (the 128 is the decimal version of our shifted bits in the second byte)

This is exactly what we need underlined above (look back at the MCU transmitted data in table 6.1)

When complete our variable spidata is going to contain 3 bytes of data so let’s print that out for now so we can see the raw output (line 11)

Line 12 and 13

These lines will manipulate the raw data in spidata so we just pull out the number we need (0-1023).  Let’s refer back to figure 6.1 (page 21) in the MCP3008 datasheet and look at the MCU received data and we’ll see the value we need (B0-B9 – 10 bits) is at the far right (the least significant bits).  Everything before that can be ignored.

The notation spidata[1] and spidata[2] is used in this line and basically refers to the second and third value in the three values returned (the first would be spidata[0])

First byte (spidata[0]) – never referenced as figure 6.1 shows us it’s not needed.

Second byte (spidata[1] & 3) – We know we need to keep B9 and B8 (figure 6.1) so we have to perform another bitwise operation – this time the AND operator is used (syntax is the ampersand ‘&’ here).  The number 3 in binary is 00000011 so let’s use an imaginary value of 131 (binary 10000011) in the second byte to show what is being done with this operator:

10000011 (let’s AND it with 3 – 00000011)
00000011 =
00000011 (look, we’ve eliminated all the other 1s from the input by using the AND operation)

( 0 AND 0 = 0, 0 AND 1 = 0, 1 AND 0 = 0 but 1 AND 1 = 1)  This is a way of masking off bits we don’t require.

So the second byte has the AND operator used to ensure we just keep the last two bits (the 2 least significant bits).  Then use the shift operator again but this time shift it 8 places so these two bits are on the left hand side of 8 zeros (1100000000).

Third byte (spidata[2]) – Finally the third byte is added to the result above to populate those remaining 8 bits – giving you 10 bits of information and a decimal number can then be calculated.

NOTE: Look again at the example output above (under the Python code) and you’ll see the raw ADC values are 0, 0 and 213.  The decimal value returned is 213 and binary value is 0011010101.  The second number (2 left most bits) will never have anything in it until you go over the value 255 (0011111111) e.g. 256 would be 0100000000.

The variable data is then returned from the function.

Line 15 and 16

Open a try and while loop so we can continually print the output to screen in the same script.

Line 17, 18 and 19

These call the function read_spi with the channel number then perform the calculation work turning the decimal value (0-1023) from the MCP3008 into a voltage (mV) and temperature (degrees celcius).

To obtain the voltage we use the calculation channeldata (our decimal value of 0-1023) multiplied by 3300 (3.3V converted to mV) then divided by the max sample size (1024 – 10bit – 210).

Now we have a voltage value in mV we can get the temperature as we know it’s 10mV per degree celcius and a reference voltage of 500mV (0 degrees).  To get the temperature we subtract 500 and then divide this value by 10.

Line 20, 21, 22, 23, 24 and 25

These just generate the output by printing the variables above and then sleep for 1 second.  The only noteworthy item being the binary  output (Data (bin)) formatting. 010b means pad the left with zeros, display a 10 bit sequence and convert to binary.

Line 27 and 28

These final two lines wait for a keyboard interrupt (as the script continuously runs displaying output every second) and when detected closes the SPI interface cleanly.

 

For more detail: Raspberry Pi with MCP3008, TMP36, SQLite and lighttpd


About The Author

Ibrar Ayyub

I am an experienced technical writer holding a Master's degree in computer science from BZU Multan, Pakistan University. With a background spanning various industries, particularly in home automation and engineering, I have honed my skills in crafting clear and concise content. Proficient in leveraging infographics and diagrams, I strive to simplify complex concepts for readers. My strength lies in thorough research and presenting information in a structured and logical format.

Follow Us:
LinkedinTwitter

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top