Raspberry Pi 2: SPI and MCP3008

The GPIOs on the Raspberry are good for signaling binary information. In cases where analog signal information is to be obtained a A/D Converter like MCP3008 is required. In this blog post, I'm going to describe how this setup got working for me.

Looking around in the net for a description on attaching the MCP3008, there seem to be two approaches: 

  • Using RPi.GPIO: This approach does a direct communication over GPIO Ports. The sequence of actions implements the low level communication.  The source code samples  in the net use different ports compared to the SPI approach (18, 23, 24 and 25). Due to the low level handling of bits and bytes any typo in the source code can make the code stop working. Thus getting it to work may be more difficult.
  • Using SPI: Information on how to use SPI was a little bit spread over the net. I'm trying to assemble the relevant stuff here. The code is shorter (and has less room for typos).
The RPi.GPIO approach lets you choose the GPIOs. If you wire the MCP3008 using the GPIO default for SPI (see below), it possible to use either approach.


Step 1: Enable SPI 


  • In /boot/config.txt uncomment "dtparam=spi=on". You need to reboot after this change. 
  • Check whether the kernel module "spi_bcm2835" is loaded using "lsmod". If not, load it using "modprobe". I did not have to add it to "/etc/modules" in order to make this change lasting. 
  • For the latest raspbian version (wheezy), there is no kernel module "spidev" needed or loaded.
  • In case you are using python, you need to install "python-spidev". 


You may verify your installation steps by doing a reboot and check with "dmesg". You should find two lines in the output like this:

[    6.122996] spi spi0.0: setting up native-CS0 as GPIO 8 
[    6.125109] spi spi0.1: setting up native-CS1 as GPIO 7 

This means that SPI is configured to talk with two SPI "devices". The cable select on GPIO 8 and 7 address these two devices.


Step 2: Proper wiring 

Using the built in SPI, requires to use the following GPIO ports: 


  • GPIO8 = Pin 24 on Raspberry => CS = Pin 10 on MCP3008 
  • GPIO10 = Pin 19 on Raspberry => Din = Pin 11 on MCP3008 
  • GPIO9 = Pin 21 on Raspberry => Dout = Pin 12 on MCP3008 
  • GPIO11= Pin 23 on Raspberry => SCLK = Pin 13 on MCP3008 

The following picture hopefully also helps:


As mentioned above this wiring allows for the choice between RPi.GPIO communication and the SPI approach. For RPi.GPIO the ports need to be declared properly:

ADC_Channel = 0
SCLK = 11
MOSI = 10
MISO = 9
CS = 8

Source code and tutorial for RPi.GPIO can be found here:





Step 3: Python Code

The code to read values from the A/D converter is really simple: 

import spidev
import time

spi=spidev.SpiDev()
spi.open(0,0)
while True:
      resp=spi.xfer([1,128,0])
      if 0<= resp[1]<=3: 
          w=((resp[1]*256)+resp[2])* 0.0032 
          print w, " V"
      time.sleep(1) 

If you want to use an A/D connected to Cable Select on GPIO 7, then spi.open(0,1) needs to be used. For changing the channel, the middle element of the tuple in spi.xfer needs to be changed: 

       resp=spi.xfer([1, (8+channel)<<4,0]) # 0<=channel<=7

The multiplication with 0.0032 is a result of the resolution of the A/D (10 bit, 1024 values) and the reference voltage (3.3V). In case you apply a different voltage to Pin 15 (Vref), the factor needs to be different.