Friday 29 March 2013

Ultrasonic Distance Measurement. - HC-SR04 / Pi

Recently picked up a couple of Nokia 5110 displays, mounted on circuits with header pins suitable for connection to Arduino or Pi. - What inspired me to do so was this QRclock which works a treat, is very cool and contains all the setup for these displays - http://scruss.com/blog/2013/01/19/the-quite-rubbish-clock/
[ From a bare Raspian Wheezy install, I've put the details here just in case I need the wiring details later.
1. - sudo vi /etc/modprobe.d/raspi-blacklist.conf
2. - #blacklist spi-bcm2708
3. - sudo modprobe spi-bcm2708
4. - sudo apt-get install python-imaging python-imaging-tk python-pip python-dev git
5. - sudo pip install spidev
6. - sudo pip install wiringpi

7. - git clone git://github.com/mozillazg/python-qrcode.git
8. - cd python-qrcode/
9. - sudo python ./setup.py install

10. - Note to go by the pin functions rather than the numbers on the LCD - they may vary.


 LCD Pin       Function      Pi GPIO Pin #   Pi Pin Name
============= ============= =============== =============
 1 VCC         Vcc            1              3.3 V
 2 GND         Ground        25              GND
 3 SCE         Chip Enable   24              GPIO08 SPI0_CE0_N
 4 RST         Reset         11              GPIO17
 5 D/C         Data/Command  15              GPIO22
 6 DNK(MOSI)   Data In       19              GPIO10 SPI0_MOSI
 7 SCLK        Serial Clock  23              GPIO11 SPI0_SCLK
 8 LED         Backlight     12              GPIO18 PWM0

N.B. - See this link for details of the GPIO output pins - http://elinux.org/Rpi_Low-level_peripherals#General_Purpose_Input.2FOutput_.28GPIO.29
11. - Add in a start script to /etc/init.d so that it works on boot, and install it:-
 sudo insserv qrclock

Having got the display attached, I need to be able to measure distance for a project I'm working on. - I got a couple of very cheap ultrasonic sensors - HC-SR04 from ebay. - These were approx £5 for 2 delivered.


This was very simple to use in actual fact. - Connect Vcc pin to 5 volt output on the GPIO header, GND to ground, and the ECHO and TRIGGER pins to two of the addressable GPIO pins. (In my case, TRIGGER to pin 8, and ECHO to pin 10).  I reworked code from various sources on the net, and was able to create a distance measurement device which works tolerably well for my purposes. - It is never going to be accurate because of the way the Pi works - it's a multi-tasking OS, and the device relies on your code timing from the trigger being sent to the echo received, and you can't guarantee that the code handling the measurement is actually running at the time the echo is received. - Anyway, when there's nothing else running on the Pi, the measured distance seems to remain within about a 5% fluctuation.

Code:
#!/usr/bin/env python
# -*- coding: utf-8 -*-

#
# AJR - 28/March/2013
# Distance script. - Cobbled together from various sources.
# All credit to original authors, but sorry I don't know exactly
# which bits from where.
# Takes an average of 3 readings from the attached Ultrasonic echo sounder.
# and converts into reading in cm of distance.
# Note that it's not accurate because the Pi is a multitasking, scheduled OS
# and the ultrasonic relies on timing the echo accurately to work out
# the distance - but, hey - ho, it's probably good enough for measuring
# river water level.....
#

import time
import RPi.GPIO as GPIO
import nokiaSPI

noki = nokiaSPI.NokiaSPI()              # create display device
noki.led(900)
noki.cls()
noki.centre_word(0, 'Distance in CM')

# Which GPIO's are used [0]=BCM Port Number [1]=BCM Name [2]=Use [3]=Pin
# ----------------------------------------------------------------------
arrgpio = [(15,"GPIO15","Echo",10),(14,"GPIO14","Trig",8)]



# Set GPIO Channels
# -----------------
GPIO.setmode(GPIO.BCM)
GPIO.setup(arrgpio[0][0], GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(arrgpio[1][0], GPIO.OUT)
GPIO.output(arrgpio[1][0], False)


# A couple of variables
# ---------------------
EXIT = 0                        # Infinite loop
decpulsetrigger = 0.0001        # Trigger duration
inttimeout = 2100               # Number of loop iterations before timeout called


# Wait for 2 seconds to allow the ultrasonics to settle (probably not needed)
# ---------------------------------------------------------------------------
print "Waiting for 2 seconds....."
noki.centre_word(3, 'Waiting for 2 seconds...')
time.sleep(2)


# Go
# --
print "Running...."
noki.cls()
noki.centre_word(3, 'Running....')

def measure():
    # Trigger high for 0.0001s then low

    GPIO.output(arrgpio[1][0], True)
    time.sleep(decpulsetrigger)
    GPIO.output(arrgpio[1][0], False)

    # Wait for echo to go high (or timeout)

    intcountdown = inttimeout

    while (GPIO.input(arrgpio[0][0]) == 0 and intcountdown > 0):
        intcountdown = intcountdown - 1

    # If echo is high

    if intcountdown > 0:

        # Start timer and init timeout countdown

        echostart = time.time()
        intcountdown = inttimeout

        # Wait for echo to go low (or timeout)

        while (GPIO.input(arrgpio[0][0]) == 1 and intcountdown > 0):
            intcountdown = intcountdown - 1

        # Stop timer

        echoend = time.time()


        # Echo duration

        echoduration = echoend - echostart
        # Display distance

    if intcountdown > 0:
        intdistance = (echoduration*1000000)/58
        return intdistance
    else:
        print "Distance - timeout"
        noki.cls()
        noki.centre_word(3, 'Timeout')
        return 1000 ### This should indicate that
                    ### the distance has timed out.

    # Wait at least .01s before re trig (or in this case .5s)

    ###time.sleep(.5)

def measure_average():
    count = 1
    distance = 0
    while ( count <= 3 ):
        distance = distance + measure()
        time.sleep(0.1)
        count = count + 1
    distance = distance / 3
    return distance

###
### Main program. - Take the output of the measure routine and
### average over three tries.
###
try:
    # Never ending loop
    # -----------------
    while EXIT == 0:

        distance = measure_average()
        if distance > 500: # we have timed out...
            print "Timeout"

        ### print "Distance = " + str(distance) + "cm"
        print 'Distance = %.0f cm' % (int(round(distance))) # print to nearest CM
        noki.cls()
        noki.centre_word(1, 'Distance')
        ### noki.centre_word(3, str(distance) + 'cm')
        noki.centre_word(3, '%.0f ' % (int(round(distance))) + 'cm')
        time.sleep(0.5)

except KeyboardInterrupt:
  # User pressed CTRL-C
  noki.cls()
  noki.centre_word(1, 'Prog End')
  # Reset GPIO settings
  GPIO.cleanup()
Here is the finished distance measure. - It's lying on the table, so this is the distance to the ceiling.