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.
You’ll need the following parts for this circuit:
- Raspberry Pi (with latest Raspbian)
- 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
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.
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.
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.
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).
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
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
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:
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.
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 and spidata is used in this line and basically refers to the second and third value in the three values returned (the first would be spidata)
First byte (spidata) – never referenced as figure 6.1 shows us it’s not needed.
Second byte (spidata & 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 (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) – 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