Skip to content
Create a gist now

Instantly share code, notes, and snippets.

Raspbery Pi Analog Input with MCP3008
#!/usr/bin/env python
# Written by Limor "Ladyada" Fried for Adafruit Industries, (c) 2015
# This code is released into the public domain
import time
import os
import RPi.GPIO as GPIO
# read SPI data from MCP3008 chip, 8 possible adc's (0 thru 7)
def readadc(adcnum, clockpin, mosipin, misopin, cspin):
if ((adcnum > 7) or (adcnum < 0)):
return -1
GPIO.output(cspin, True)
GPIO.output(clockpin, False) # start clock low
GPIO.output(cspin, False) # bring CS low
commandout = adcnum
commandout |= 0x18 # start bit + single-ended bit
commandout <<= 3 # we only need to send 5 bits here
for i in range(5):
if (commandout & 0x80):
GPIO.output(mosipin, True)
GPIO.output(mosipin, False)
commandout <<= 1
GPIO.output(clockpin, True)
GPIO.output(clockpin, False)
adcout = 0
# read in one empty bit, one null bit and 10 ADC bits
for i in range(12):
GPIO.output(clockpin, True)
GPIO.output(clockpin, False)
adcout <<= 1
if (GPIO.input(misopin)):
adcout |= 0x1
GPIO.output(cspin, True)
adcout >>= 1 # first bit is 'null' so drop it
return adcout
# change these as desired - they're the pins connected from the
# SPI port on the ADC to the Cobbler
SPICS = 25
# set up the SPI interface pins
# 10k trim pot connected to adc #0
potentiometer_adc = 0;
last_read = 0 # this keeps track of the last potentiometer value
tolerance = 5 # to keep from being jittery we'll only change
# volume when the pot has moved more than 5 'counts'
while True:
# we'll assume that the pot didn't move
trim_pot_changed = False
# read the analog pin
trim_pot = readadc(potentiometer_adc, SPICLK, SPIMOSI, SPIMISO, SPICS)
# how much has it changed since the last read?
pot_adjust = abs(trim_pot - last_read)
print "trim_pot:", trim_pot
print "pot_adjust:", pot_adjust
print "last_read", last_read
if ( pot_adjust > tolerance ):
trim_pot_changed = True
print "trim_pot_changed", trim_pot_changed
if ( trim_pot_changed ):
set_volume = trim_pot / 10.24 # convert 10bit adc0 (0-1024) trim pot read into 0-100 volume level
set_volume = round(set_volume) # round out decimal value
set_volume = int(set_volume) # cast volume as integer
print 'Volume = {volume}%' .format(volume = set_volume)
set_vol_cmd = 'sudo amixer cset numid=1 -- {volume}% > /dev/null' .format(volume = set_volume)
os.system(set_vol_cmd) # set volume
print "set_volume", set_volume
print "tri_pot_changed", set_volume
# save the potentiometer reading for the next loop
last_read = trim_pot
# hang out and do nothing for a half second

When I run this I frequently get half-integer values, e.g. 1023.5. I think this part is incorrect:

        adcout = 0
        # read in one empty bit, one null bit and 10 ADC bits
        for i in range(12):
                GPIO.output(clockpin, True)
                GPIO.output(clockpin, False)
                adcout <<= 1
                if (GPIO.input(misopin)):
                        adcout |= 0x1

We already cycled the clock after sending the command, so the empty bit (allowed to be anything) is on MISO. First time through the loop, we cycle the clock again, so first time we actually read MISO it's guaranteed to be null. Then we read another 11 bits and store them in adcout, which is one too many. When you read too many bits, the 3008 starts sending them in reverse order; it only sends the LSB twice, so the final bit we read is going to be the same as the bit two before that, which is the second-least significant bit of the actual value. (This is what the datasheet says should happen, and I put in a debug print to confirm it.)

We later divide by two, but this apparently assumes the final bit read would be 0, which isn't guaranteed. Replacing the /= 2 with >>= 1 would fix it, but it's cleaner to remove that line completely and only loop in range(11) when setting up adcout.

Muuo commented Feb 11, 2013

I'm assuming that

adcout >>= 1 # first bit is 'null' so drop it

is a erroneously commented correction for the mistake mentioned by ChickenProp above


Why aren't you using the SPI hardware? raspi-config can enable it, so it's not hard. It's easy to use from C, and I HATE C. Bit banging SPI doesn't teach anyone how to use SPI, IMHO.

ralfiii commented Apr 10, 2015

Has anyone ported the code to use the SPI interface?


Hey guys!
take a look at my library that uses SpiDev to communicate with the chip.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.