MCP3002 example code for Raspberry Pi (ADC through SPI)

26 September, 2012

I’ve been tinkering with my Raspberry Pi for quite a few weeks now. It’s a great little thing. Like many, though, I’ve been a little frustrated with the lack of analogue inputs into the Pi. I was for a while using a barebones arduino to service serial comms from the Pi but this had it’s own limitations. (One day I’ll post the code I used for that on this blog!). Adafruit industries have been doing a great job of promoting the use of the Pi to hobbyists and showing examples of hardware control. One of these was to control the SPI controlled 8 channel ADC chip known as the MCP3008 from Microchip. Find it here!

In my naivety I thought that the control would be exactly the same for its 2 channel sibling, the MCP3002. I was wrong! It’s still an SPI chip and when I started to delve into the datasheets it was relatively straight forward to modify the code. I thought I’d post that here so others could make use of it. You’ll notice I haven’t changed the meat of the code and all credit goes to Adafruit for their hard work on which I build!

The code:

#!/usr/bin/env python

# just some bitbang code for testing the 2 channels

import RPi.GPIO as GPIO, time, os

DEBUG = 1
GPIO.setmode(GPIO.BCM)

# read SPI data from MCP3002 chip, 2 possible adc's (0 thru 1)
def readadc(adcnum, clockpin, mosipin, misopin, cspin):
    if ((adcnum > 1) or (adcnum < 0)):
        return -1
	if (adcnum == 0):
            commandout = 0x6
        else:
            commandout = 0x7
	GPIO.output(cspin, True)

	GPIO.output(clockpin, False)  # start clock low
	GPIO.output(cspin, False)     # bring CS low

	#commandout = 0x6  #start bit and 1, 0 to select single ended ch0
	commandout <<= 5    # we only need to send 3 bits here
	for i in range(3):
		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 /= 2       # first bit is 'null' so drop it
	return adcout

# change these as desired
SPICLK = 11
SPIMOSI = 9
SPIMISO = 10
SPICS = 18

# 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)
adcnum = 0

# Note that bitbanging SPI is incredibly slow on the Pi as its not
# a RTOS - reading the ADC takes about 30 ms (~30 samples per second)
# which is awful for a microcontroller but better-than-nothing for Linux

while True:
    print "------------"
    for adcnum in range(2):
        ret = readadc(adcnum, SPICLK, SPIMOSI, SPIMISO, SPICS)
        print adcnum, ": ",ret
    print "------------"

Wifly Working at Last!

25 September, 2012

After several attempts to get my Wifly-rn-xv working I’ve finally succeeded. The documentation seems good at first glance but when you get into it you realise information is missing. To be fair I got the initial connection working with a combination of the command datasheet and the following blog: http://www.tinkerfailure.com/2012/02/setting-up-the-wifly-rn-xv/

Thanks Tinker! Check out his other posts including using PHP to read the results. That’s exactly what I wanted to do. Take an analogue input voltage from a temperature sensor and record it using the web server. I struggled a lot though until I read the following posts on the sparkfun forum: https://forum.sparkfun.com/viewtopic.php?f=13&t=32719

It turns out there are some subtleties to all this, including an unseen resistor network before the reading is measured and least significant bits being stripped in the GET request. In the end, inspired by one of the posters I decided to use a multimeter and the measurement command and do my own linear regression. This actually worked out pretty well and the measurements are very accurate now.

So I had input voltages ranging from 0 to 1V in ~100mV increments and had the decimal value at the command line of 34,500 (0×08698 -> ~0x086A via HTTP GET) to 183,800 (0x2cf3e), supposedly in uVs. That gave me a slope and offset to play with. It turns out the readings were thankfully very linear.

I used the Sparkfun named pin ADC0, which is pin 20, but called SEN 2 at the wifly. At the command line, to read this input use the command: “show q 2″.

I hope this makes sense to someone else who is having a bit of a struggle!

One day I might try the UART data via HTTP option – but not until I can face it!


Raspberry Pi, TextStar LCD, and Python

4 July, 2012

I have my raspberry pi and have been trying to get it to talk to a serial LCD via the uart ports on the gpio header. This post aims to demonstrate simple comms with bash (the command line) and then with programmatically using python. You can download this code if it’s of any use.

The LCD

I purchased a serial LCD from Coolcomponents called the TextStar from Cat’s Whiskers Technologies (their product page). It’s a tiny display but incredibly easy to use. It even has four buttons on the sides to use as inputs to your project.

Read the rest of this entry »


Android Apps!

8 September, 2011

Today I have published my second Android application, Gas It Right Pro. It’s a paid version of an ad supported app, Gas It Right, but with lots more features. Please take a look and support my new hobby!

The idea of Gas It Right is to help you choose the correct amount of fuel/gas into a hire car that was delivered without a full tank (eg 3/4 full). This happens to me all the time and I invariably overfill “to be safe”. I wrote this to save money. Please do the same and download it now!

Gas it Right Pro (Android v2.2+)

Gas it Right (original, Android v1.6+)

Gas it Right Pro Screenshot

Home page of Gas it Right Pro


Cooperative Swarm of Robots

18 August, 2011

This is an amazing achievement!


A scanning range finder using Arduino and Processing

30 March, 2010

I’ve really been unfocused recently with my robotics hobby. I have so much I want to play with. This post is to document playing with a couple of new toys all together. These are: a sharp GP2D12 range finding sensor (10 to 80cm), an Arduino (actually a seeduino), and the programming language Processing.

Plotting range with angle

A big thanks to David Barnes (of the excellent Quotient Robotics blog) for giving me my first Arduino (an iDuino). I got the bug and have since bought a Seeduino which is the same thing but a bit more of a conventional layout for shields and such.

Note: I’m not claiming to be the first person to do this! The way I go about it may be different and may be useful to someone out there – that’s why I blog about it. My code is based on other examples which I will cite where appropriate.

Arduino Code – simple stuff. Move the servo take a measurement, and print both the angle and the range over serial. I used the example “Sweep” which is a kind of servos 101 example. Listing:

// Sweep servo, take range, send to serial
// by DMT195 <http://dmt195.wordpress.com> modified version of "sweep" 
// originally by BARRAGAN <http://barraganstudio.com> 

#include <Servo.h> 
Servo myservo;  // create servo object to control a servo                 
int pos = 0;    // variable to store the servo position 
char serialTemp[10];  //variable to create a serial string 
int rang;  //a variable to store the range value 
void setup() {  
  Serial.begin(9600);  
  myservo.attach(9);  // attaches the servo on pin 9 to the servo object 
} 
void loop() {   
  for(pos = 0; pos < 180; pos += 1)  // goes from 0 degrees to 180 degrees   
  {                                  // in steps of 1 degree     
   myservo.write(pos);    
   delay(15);                       // waits 15ms for the servo to reach the position     
   rang=analogRead(0);    
   Serial.print(rang);    
   Serial.print(",");    
   Serial.println(pos);   // reads the value at the analogue input and outputs this with position to the serial port  
  } 
 for(pos = 180; pos>=1; pos-=1)     // goes from 180 degrees to 0 degrees
   {                                    
   myservo.write(pos);    
   delay(15);                       // waits 15ms for the servo to reach the position     
   rang=analogRead(0);    
   Serial.print(rang);        
   Serial.print(",");    
   Serial.println(pos);   // reads the value at the analogue input and outputs this with position to the serial port  
   } 
}

I then used Processing to plot and visualise the output. I hadn’t come across it before but it seems very suited to this kind of thing. The language is very similar to the Arduino code. Again, I used existing code and modified it for my purposes. Standing on the shoulders of giants and all that. Listing:

// Graphing sketch
// This program takes ASCII-encoded strings
// from the serial port at 9600 baud and graphs them in a polar fassion.
// Original code Created 20 Apr 2005, by Tom Igoe
// Modified code by DMT195 and available free to all
// This example code is in the public domain.
import processing.serial.*;
Serial myPort;        // The serial portint
xPos = 1;         // anglefloat
deg2rad = PI/180; //conversion factor
float[] magn = new float[180]; //magnitude - somewhere to store the ranges with angle
int x1,x2,y1,y2;
void setup () {  // set the window size:  
size(800, 400);          
stroke(0,255,0);  // List all the available serial ports  
println(Serial.list());  // I know that the first port in the serial list on my mac  
// is always my  Arduino, so I open Serial.list()[0].  
// Open whatever port is the one you're using.  
myPort = new Serial(this, Serial.list()[0], 9600);  
// don't generate a serialEvent() unless you get a newline character:  
myPort.bufferUntil('\n');  
// set inital background:  
background(0);
}
void draw () {
  // everything happens in the serialEvent()
}
void serialEvent (Serial myPort) {  // get the ASCII string:  
  String inString = myPort.readStringUntil('\n');
  if (inString != null) {    // trim off any whitespace:
    inString = trim(inString);
    int[] inArray = int(split(inString, ','));
    // convert to an int and map to the screen height:
    int inPos = inArray[1];
    try{    //I don't claim to understand error handling that well but this seemed to stop the program from crashing
    magn[inPos] = map(getRange(inArray[0]), 0, 800, 0, height);
    }catch(Exception e){};
    // draw the thing:    
    if((inPos%5)==0){  //This says that for every angle divisible by 5 refresh the drawing
      background(0);
      plotWhiteBits();
    }
    x1 = int(width/2-magn[0]);
    y1 = height;
    for(int i=0; i<180; i=i+1){
      x2 = int(width/2-magn[i]*cos(i*deg2rad));
      y2 = int(height-magn[i]*sin(i*deg2rad));
      line(x1,y1,x2,y2);
      x1=x2;
      y1=y2;
    }
  }
}
int getRange(int volts){ //This is my fitting routine. I should check this works at some point!
  float rangeInMm = 1/(0.000016*volts+0.0004516);
  return int(rangeInMm);
}
void plotWhiteBits(){ //This is a function to draw the background reticules (lines and arcs)
  int leng=height*3;
  stroke(127,127,127);
  for(int j=0; j<180; j=j+15){   //A chose a 15 degree angle between lines
    line(int(width/2),int(height),int(width/2-1*leng*cos(j*deg2rad)),int(height-leng*sin(j*deg2rad)));
  }
  for(int j=1;j<9;j=j+1){  //And this is for the arcs
    int spacer=height/4;
    noFill();
    ellipse(width/2, height, j*spacer, j*spacer);
  }
  stroke(0,255,0);  //return the stroke colour back to the main one
}

I hope this is of use to someone. If you improve it please let me know!

Update: I’ve used XBee devices to do this wirelessly now! Works well.


New laser cutting service

2 February, 2010

It’s been nearly a year since I had real trouble trying to machine my own parts and failing miserably!

Since then I have been working tirelessly with others to set up a hobbyist friendly laser cutting service in for those in the UK.

I present you with ShapeMonkey!
ShapeMonkey Laser Cutting Service

Currently this offers a simple upload, pay, and receive service for acrylic and balsa wood. More materials and products will be offered in time. This is the kind of service I was looking for back then. Hopefully others reading this blog will find the service useful.

Please give as much feedback as you can. For example: What materials should be next?


Follow

Get every new post delivered to your Inbox.