Skip to content

Instantly share code, notes, and snippets.

@ladyada
Last active April 7, 2024 18:32
Show Gist options
  • Star 15 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
  • Save ladyada/3151375 to your computer and use it in GitHub Desktop.
Save ladyada/3151375 to your computer and use it in GitHub Desktop.
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
GPIO.setmode(GPIO.BCM)
DEBUG = 1
# 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)
else:
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
SPICLK = 18
SPIMISO = 23
SPIMOSI = 24
SPICS = 25
# set up the SPI interface pins
GPIO.setup(SPIMOSI, GPIO.OUT)
GPIO.setup(SPIMISO, GPIO.IN)
GPIO.setup(SPICLK, GPIO.OUT)
GPIO.setup(SPICS, GPIO.OUT)
# 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)
if DEBUG:
print "trim_pot:", trim_pot
print "pot_adjust:", pot_adjust
print "last_read", last_read
if ( pot_adjust > tolerance ):
trim_pot_changed = True
if DEBUG:
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
if DEBUG:
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
time.sleep(0.5)
@ChickenProp
Copy link

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
Copy link

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

@naikrovek
Copy link

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
Copy link

ralfiii commented Apr 10, 2015

Has anyone ported the code to use the SPI interface?
Thank,
Ralf

@luxedo
Copy link

luxedo commented Nov 23, 2015

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

@haydenroche5
Copy link

haydenroche5 commented Aug 13, 2016

Reviving this because I think the issue ChickenProp raised in 2012 still remains. Reading 12 bits in that loop is indeed 1 too many, and a right shift or, equivalently, division by 2 is misleading, as is the comment that this is a "null" bit. As someone learning about SPI, this mysterious shift confused me for 2 solid days. The empty bit is already on the bus after the command, as ChickenProp states, so you only need to read out 11 bits: 1 null and 10 valid ADC bits. Sure, you can read the additional bit and shift it out after, but what's the point?

@chiques
Copy link

chiques commented Jun 1, 2017

You have to follow these instructions or else the account blocks the script.

"> If the tips above didn't help, visit https://www.google.com/accounts/DisplayUnlockCaptcha and follow the steps on the page."

@thijstriemstra
Copy link

Can you make the print statements compatible with Python 2.7/3.x?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment