Saturday, November 15, 2014

66: Analog Input using an Arduino
- revised 11/18/14 -

After my frustration with the MCP3008 (post 65) I decided to try using an Arduino. I bought a new Nano from Sainsmart ($14). Here's the Arduino setup:

Note that the Nano plugs directly into the breadboard
(very handy)

I use the Arduino.app on my iMac to write and debug the Arduino program (over the USB cable). Then I move the cable to a USB port on my Pi. (Note: the Arduino uses Flash memory, so the program starts again.)

Here's the Arduino "sketch" (as programs are called) in C language:
// Rheostat has 3 pins L=GND, R=5v (or 3.3V), Mid to A1
// Readings connected to 3.3V: 0 to 730, to 5V: 0 to 1023
#define RheoPin 1
#define spl(x) Serial.println(x)

void setup() {
    Serial.begin(9600);
}

void loop() {
    int i, r;
  
    while(!Serial.available()) delay(100); // Wait for request
    while(Serial.available() && i < 10) {
          Serial.read(); // discard input
    }
    r = analogRead(RheoPin);
    spl(r); // Writes number as ASCII to the USB
}

Meanwhile, on the other end of the USB cable -- the Pi Python program:
import serial # I had to install "pySerial" for this
import time
import RPi.GPIO as GPIO
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)

ser = serial.Serial("/dev/ttyUSB0", baudrate=9600, timeout=1)
ser.open()

while True:
        ser.write("1") # Ask the Arduino for a reading
        time.sleep(.2)
        ct = ser.inWaiting()
        if ct > 0:
                s = ser.read(ct)
                i = int(s.rstrip()) # ASCII to Int
                print i
        time.sleep(.5)

There's good news and bad news about this approach:
GOOD: 
* No GPIO pins used (a USB port, instead).
* My Arduino program worked the first time.
* Because the Arduino only runs one thing at a time, results can be more accurate.
BAD:
* More complicated, two kinds of programming, etc.
* The cheap Nano still costs 3X the MCP3008 chip.
* The request-to-response turn-around is about 1 second, minimum.

Overall, I like using the Arduino. Better yet, the Pi model B+ should have had analog pins.

REVISED:
I have successfully added other sensors to the Arduino end of the setup: 
1. A photosensor.
2. An HC-SR04 rangefinder.
And I have checked out adding --
3. DS18B20 temperature and DHT22 temperature/humidity sensors (although they work fine on the Pi).
4. Output to a 2x16 LCD character display (saves 4 GPIOs on the Pi).

Saturday, November 8, 2014

65: Analog Devices

So, the RPi B+ has 14 more digital GPIO pins. Big deal.

From my perspective a few analog pins would have made a better "+" model. I have 3 Pi's and an Arduino Micro. Well, the Pi may have a nice OS but analog input is painful on the Pi and it is a chinch on the Arduino.

You can fake analog for devices like a photocell by programming a tight loop, e.g.:

# Python
import RPi.GPIO as GPIO, time, os      
GPIO.setmode(GPIO.BCM)
PHOTO_pin = 22 

def Ptime (pin):
        reading = 0
        GPIO.setup(pin, GPIO.OUT)
        GPIO.output(pin, GPIO.LOW)
        time.sleep(0.1)

        GPIO.setup(pin, GPIO.IN)
        while (GPIO.input(pin) == GPIO.LOW): # Eat the CPU!
                reading += 1
        return reading

 ## read by:
        print Ptime(PHOTO_pin)

Earlier, in Posts 50 and 54 I complained about the inherent inaccuracy of faking analog for an ultrasonic rangefinder (because of Linux scheduling other processes).  And I've noted that the DS18B20 temperature sensor (C language) code uses a lot of CPU cycles to achieve an analog output.

Supposedly, you only need only add am MCP3008 chip to the Pi and, voila, you can trade 4 GPIOs for 8 analog ports. Well, I haven't had much joy with that. I bought 2 chips from Adafruit (anyone besides me irritated by their exorbitant shipping fees?). I've downloaded 4 different sets of 3008 code and the first 3 didn't work for me.

Adafruit's wiring used BCM ports 18, 23, 24 and 25 -- all of which I was already using -- so I first tried the setup described at--

http://www.raspberrypi-spy.co.uk/2013/10/analogue-sensors-on-the-raspberry-pi-using-an-mcp3008/

--which used BCM 10, 9, 11 and 8 (ones I was not using). These are AKA as SP10 pins MOSI, MISO, SCLK AND CE0. This involves activating SPI and downloading stuff. I did this but (as I mentioned) it didn't work.

Finally, code that sort of works:
https://github.com/adafruit/Adafruit-Raspberry-Pi-Python-Code/blob/master/Adafruit_MCP3008/mcp3008.py

but my GPIO pins are:
SPICLK = 11
SPIMISO = 9
SPIMOSI = 10
SPICS = 8

MCP3008 CH0 photo sensor
 CH1 potentiometer
 CH2 - CH7 not connected

Trimmed down Python========

import RPi.GPIO as GPIO, time, os
GPIO.setmode(GPIO.BCM)

# 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 /= 2       # first bit is 'null' so drop it
return adcout

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

# set up the SPI interface pins 
GPIO.setup(SPICLK, GPIO.OUT)
GPIO.setup(SPIMISO, GPIO.IN)
GPIO.setup(SPIMOSI, GPIO.OUT)
GPIO.setup(SPICS, GPIO.OUT)

print " #0  #1  #2  #3  #4  #5  #6  #7"
print "-----------------------------------------------------"
while True:
print "|",
for adcnum in xrange(0, 8):
ret = readadc(adcnum, SPICLK, SPIMOSI, SPIMISO, SPICS)
print ret,"\t",
print "|"
time.sleep(2)

=================(sorry, columns won't line up)==================

$ sudo python mcp.py 
 #0   #1   #2   #3   #4   #5   #6   #7
------------------------------------------------------------
 992  0 3 4 2 2 0 0      dark, rheostat closed
 991 0 12 14 14 15 18 31 
 990 0 7 7 4 3 1
 990 0 3 7 8 9 10 22 
...
 983 269 4 4 1 1 0 0      rheostat opening
 982 269 8 11 11 13 14 26 
...
 983 1023 2 1 0 0 0 1     rheostat  way open
 985 1023 13 15 16 17 19 33 
...
 509 663 17 16 12 10 8 13   flashlight on photocell
 445 663 0 5 6 9 9 21 
...

====================================

Note the extraneous readings on the non-connected ports.

After all of this trouble you'd hope that the MCP3008 would save a lot of CPU time getting analog readings but my half-assed tests don't show much reduction (on the photocell, only).