Saturday, 30 January 2016

Raspberry PI - Adding analogue inputs using MCP3008, MCP3004, MCP3208, MCP3204


















These chips are analogue to digital converters with 8 inputs, they can read values from POT's, NTC sensors and other resistive passive sensors. The MCP3008 is 10 bit resolution and the MCP3208 is 12 bit resolution. There is also MCP3004 and MCP3204 these are the same but with 4 inputs. To connect to the Raspberry PI you need to enable the SPI (Serial Peripheral Interface) chip interface. The chip reads eight parallel inputs and converts them to a serial data stream. It is possible to chain more than one chip using this interface to increase the amount of inputs or use the another chip for outputs (74HC595) or digital inputs (74HC165). 

Install and enable the SPI interface

  1. First make sure the system is up to date
    sudo apt-get update
    sudo apt-get upgrade
    sudo reboot
  2. To install the development software that includes spi_dev type the following command. Note:Raspberry PI needs internet connection for this to work.
    sudo apt-get install python-dev python3-dev
    The command will indicate the package size before installing. Then install the spidev for Pyhon with the below commands.
    cd ~
    git clone https://github.com/doceme/py-spidev.git
    cd py-spidev
    make
    sudo make install
  3. Enable the SPI interface 
  4. sudo raspi-config select Advanced the enable SPI and set to load by default.
  5. Reboot the Raspberry PI
  6. sudo reboot
  7. To check that the interface is enabled type lsmod and check that spi_bcm2835 is listed.

Chip connections


















MCP3004/ MCP3204
MCP3008/ MCP3208
Signal
Description
1
1
CH0
Analogue Input 1
2
2
CH2
Analogue Input 2
3
3
CH2
Analogue Input 3
4
4
CH3
Analogue Input 4
-
5
CH4
Analogue Input 5
-
6
CH5
Analogue Input 6
-
7
CH6
Analogue Input 7
-
8
CH7
Analogue Input 8
5
-
NC
No Connection
6
-
NC
No Connection
7
9
DGND
Digital Ground
8
10
CS/SHDN
Chip Select/Shut Down
9
11
DIN
Digital Serial Input
10
12
DOUT
Digital Serial Output
11
13
CLK
Clock
12
14
AGND
Analogue Input Ground
13
15
VREF
Reference Voltage 
14
16
VDD
Positive Supply Voltage


Chip Communication 


















The input values are read and stored in chip addresses continuously. CLK is the clock for communication timing. The CS pin starts high then pulled low to enable to chip data to be read. The address of the input to be read is sent to DIN (MSB IN) and the requested data is sent to DOUT (MSB OUT) after a null data bit. The output of the MCP3008/4 will be 0 to 3FF hex and the MCP3208/4 output will be form 0 to FFF hex. For more details download the detailed data sheets MCP3004MCP3008, MCP3204MCP3208.

Wiring Connections























The wiring drawing shows the MCP3008 connections. Connections to all the chips are listed in table below:-


MCP3004/ MCP3204
MCP3008/ MCP3208
Signal
Description
GPIO
RPI Pin
1
1
CH0
10K NTC Sensor
-
-
2
2
CH1
10K POT
-
-
7
9
DGND
Digital Ground
GND
6
8
10
CS/SHDN
Chip Select/Shut Down
CS0
24
9
11
DIN
Digital Serial Input
MISI
19
10
12
DOUT
Digital Serial Output
MISO
21
11
13
CLK
Clock
SCLK
23
12
14
AGND
Analogue Input Ground
GND
6
13
15
VREF
Reference Voltage 
3.3 Volts
1
14
16
VDD
Positive Supply Voltage
3.3 Volts
1

The drawing shows just two inputs connected. A 10KΩ POT is connected to GND and 3.3 volts and the wiper is connected to the analogue input 2. Wiring a sensor requires a resistor of the same value as the sensors reference value in this case 10K. The sensor is connected to analogue input 1 and GND and the resistor is also connected to input 2 and 3.3 volts.


Python Code

import spidev
import time
import math

spi = spidev.SpiDev()
spi.open(0,0)

#Read SPI Sensor input 0 to 7 for MCP3008
def ReadInput(Sensor):
  adc = spi.xfer2([1,(8+Sensor)<<4,0])
  data = ((adc[1]&3) << 8) + adc[2]
return data


#Convert Voltage to Resistance
def VoltToOhm(V):
  Rd = 10000
    Rd_effective =(Rd * 16800.0) / (Rd + 16800.0)
  Ohm = (3.3 - V) / V * Rd_effective
return Ohm

#Convert resistance to temperature
def OhmToCelsius(ohms):
    #NTC Sensor Characteristics
    A = 0.00335401643468053
    B = 0.0002744032
    C = 0.000003666944
    D = 0.0000001375492
    r = math.log(ohms/10000.0)
    temp = 1.0 / (A + B*r + C*r**2 + D*r**3)
    temp = temp - 273.15
    return temp

while True:
    for i in range(0,2):
    RawValue = ReadInput(i)    #Read the raw input from the chip
    percent = (RawValue/10.24) #Convert to a percentage
  Voltage = (percent /100.0)*3.3 #Convert % to a 0 to 3.3 Volts
  if (Voltage > 0): #Skip If 0 to avoid divide by zero error
  Ohms = VoltToOhm(Voltage) #Convert Voltage to resistance
DGC = OhmToCelsius(Ohms) #Convert to temp in Celsius
  else:
  Ohm = 0.0
  DGC =-50.0
  #Format values to 2 decimal places
PCTStr = ",PCT={0:.2f}".format(percent)+"%"
VoltsStr = ",Volts={0:.2f}".format(Voltage)+"V"
OhmsStr = ",Ohms={0:.2f}".format(Ohms)+"ohms"
DGCStr = ",Temperature={0:.2f}".format(DGC)+"DGC"
print "Sensor No "+str(i),"Raw="+str(RawValue), PCTStr, VoltsStr, OhmsStr, DGCStr
time.sleep(2) #Delay to slow down display for use humans

The SPIDEV function is imported to communicate with the MCP3008. I have also imported time for delays and math for the conversion to temperature. The SPI interface is defined and opened on channel 0 at start-up. The ReadInput function reads an input from the chip and returns the raw value. The VoltagetoOhm function converts the calculated voltage to a resistance value. OhmToCelsius converts the resistance to a temperature. In the main program the for loop reads the first two sensors (could be changed depending on the inputs used and chip used). Percentage, Voltage etc.. are calculated and displayed for the two inputs. 


The program output

















NTC Sensor Characteristics

The sensor characteristics can sometimes be obtained from the supplier but not always. Here is a table of the sensors I have found and there characteristics. There is no guarantee but this helped me to get sensors reading reliably. When testing I would recommend testing values around the target temperatures against a know sensor. The value may need an offset or +/- 10 on the final value. The below are for 10K NTC sensors if you don't know the NTC sensor number just try each one until you get the best fit.


NTC Sensor
A
B
C
D
3490
0.003354016
0.000292425
3.45753E-06
2.33526E-07
3590
0.003354016
0.000255056
1.62764E-06
2.21525E-07
3740
0.003354016
0.000274403
3.66694E-06
1.37549E-07
3977
0.003354016
0.000256985
2.62013E-06
6.38309E-08
3435
0.003354016
0.000307404
1.01915E-05
9.09371E-07
3960
0.003354016
0.000255056
1.62764E-06
2.21525E-07
3610
0.003354016
0.000282875
2.98965E-06
4.84086E-08
3570
0.003354016
0.000286452
3.25226E-06
4.5945E-08
3940
0.003354016
0.000257077
1.89389E-06
1.89729E-07
3430
0.003354016
0.000303944
6.31344E-06
-6.85454E-08
3984
0.003354016
0.000256524
2.60597E-06
6.32926E-08
4090
0.003354016
0.000249339
2.2881E-06
7.98311E-08
3435
0.003354016
0.000300131
5.08516E-06
2.18765E-07

6 comments:

  1. Is your pin 15 supposed to go to ground? I thought that was supposed to go to positive voltage.

    ReplyDelete
  2. Is there a difference between spidev (3.2) and the library 'python-periphery', or the 'py-libbcm2835' library? They all seem to do spi, however not sure if they have different applications or if they are just deprecated libraries?

    Interestingly, the spidev documentation says you can set the number of bits per word(transfer) , 8..16, however according to the pi documentation, the spi peripheral connected to the header only supports 8/9! So perhaps spidev is a generic driver whose functionality may or may not be fully supported by the rpi?

    BTW, doesn't a python list ([0x90,0,0,0,0]) consist of ints and not bytes? Does the driver take this into account and convert it to 8-bit bytes before sending?

    ReplyDelete
  3. You worked with 3008, Can yopu help me with 3208?
    Please respond asap

    ReplyDelete
  4. Did you already find a solution??

    ReplyDelete
  5. What would I need to change to read a 12bit MCP3208? Something in the `def ReadInput` `spi.xfer2`? Right now I'm topping out at 1025, but I should be able to get a value of 4095 with those other two bits, correct?

    ReplyDelete

Thank you fro reading my page.
Let me know if there is anything that I could add or change.
Please let me know if this information is helpful.