Wikijunior:Raspberry Pi/Raspberry Pi Python GPIO Zero Distance Sensor

Tutorial by Jonathan Teague - Public Domain
28th Jan 2017 – www.cotswoldjam.org

This tutorial will cover using the popular HC-SR04 ultrasonic distance sensor, the Raspberry Pi and the gpiozero library to measure distances.

The kit edit

 

In your bag you will find the following components:

  • ×4 M-F Jumper leads (pin to socket)
  • ×1 R1 680Ω (blue grey brown) resistor
  • ×1 R2 470Ω (yellow violet brown) resistor
  • ×1 Mini-breadboard
  • ×1 HC-SR04 ultrasound sensor

Using a breadboard edit

 

Breadboards are used to prototype and test electronic components without needing to solder them.

Each hole is identified with a notation system using numbers horizontally across the breadboard and letters vertically down the breadboard.

Using the Raspberry Pi's GPIO pins edit

 

The Raspberry Pi has two ways of labelling the General Purpose Input-Output (GPIO) pins:

  • Board numbering – just starts from the bottom-left at 1, and works its way up and right, through to 40.
  • BCM numbering (Broadcom numbering) – is the way the Raspberry Pi's processor sees the pin connections.

Putting it together edit

Step 0: Make sure your Pi is shut down and the power lead disconnected.

Step 1: Take 1 jumper lead and plug the pin (male end) into B1 on the breadboard and the socket (female end) into pin 6 (GND) on the Pi.

   

Step 2: Take 1 jumper lead and plug the pin into B3 on the breadboard and the socket into pin 3 (GPIO 20) on the Pi. This is the connector that the Pi will use to trigger the distance measurer to take a measurement.

   

Step 3: Take the R1 680Ω (blue grey brown) resistor – the one with a blue colour line on and insert the legs into holes E1 and I2 on the breadboard – it doesn’t matter which way round the resistor is.

   

Step 4: Take the other resistor, the R2 470Ω (yellow violet brown) – with a yellow colour line on and insert the legs into holes D2 and H2 on the breadboard – again it doesn’t matter which way round.

   

Step 5: Gently bend the R1 resistor away from R2 so the legs in E1 and D2 won't touch each other (if they do it won’t damage anything, it just won’t work).

 

Step 6: Take 1 jumper lead and plug the pin into J2 on the breadboard and the socket into pin 1 (GPIO 21) on the Pi. This is the connector that the Pi will use to listen for the measurement.

   

Step 7: Take the last jumper lead and plug the pin into B4 on the breadboard and the socket into pin 2 (5V).

   

Step 8: Finally, plug in the HC-SR04. It has 4 pins and these need to go into A1, A2, A3 and A4. The back of the sensor with all the black ICs on should be facing the breadboard and the two silver sensors pointing away from the breadboard.

 

The program edit

Power up your Raspberry Pi. From the desktop menu, select Programming – Python 3 (IDLE). Then use File, New File to create a new program.

Type in the following program then use File, Save to save your program as the name of your choice (don’t forget to put .py on the end) in the ~/python/distance folder and then run it with Run menu, Run Module.

#!/usr/bin/python

from gpiozero import DistanceSensor
from time import sleep

sensor = DistanceSensor(echo=21, trigger=20, max_distance=2.0)

while True:

   distance = sensor.distance * 100
   print("Distance : %.1f" % distance)
   sleep(1)

What does the program do?

#!/usr/bin/python

The first line is a comment (it starts with #) and tells the operating system what language the program is written in, as you will run the program from Python yourself this is redundant (but good practice).

from gpiozero import DistanceSensor
from time import sleep

The from lines tell the computer to learn about new things. Computers can learn from programs that other people have written; we call these other programs "libraries". Our program needs the function called DistanceSensor from the gpiozero library which it will use to talk to the sensor and the function sleep from the time library so that we can insert pauses. A function is just some code located somewhere else that we can make use of to do something.

sensor = DistanceSensor(echo=21, trigger=20, max_distance=2.0)

We then create an object we call sensor using the imported function DistanceSensor. The parameters that we pass say that the trigger to invoke the sensor will be on GPIO pin 20; the echo to listen for the reply will be on GPIO pin 21 and that the maximum distance that should be returned is 2.0 metres.

while True:

This while True: tells the program to run forever in a loop.

   distance = sensor.distance * 100
   print("Distance : %.1f" % distance)
   sleep(1)

Then we get the current sensor reading by calling sensor.distance, multiply this by 100 (as the returned value is in mm); print out the value with only one digit after the decimal point, and finally go to sleep for a second before the while True: makes us go around again.

Additional notes edit

These notes are additonal to the main tutorial and explain the use of two resistors to make a "voltage divider" and give an example program that directly controls the sensor.

The Voltage Divider edit

The Raspberry Pi is a 3.3V device. The sensor used in this tutorial, an HC-SR04, is a 5V device. This means that the sensor needs 5V to power it and, crucially for the Pi, that the voltage levels that the sensor returns the results to the Pi are also at 5V. Connecting the sensor output directly to the Pi would almost certainly result in damage to the Pi.

This sort of situation is often found in the real world, you have two devices you wish to connect to each other but they operate at different voltages. There are several ways to solve this but one of the simplest, and the one used here, is to drop the voltage from 5V to just under the safe value of 3.3V by making a "voltage divider" out of two resistors.

The two resistors are placed in series across the source voltage and zero volts and the voltage at the point between the two resistors will be reduced in proporation with the values of the two resistors.

  Vout =VinR1/R1 + R2

So substituting in the values for 5V as the Vin and the two resistors we have used, R1 = 680Ω and R2 = 470Ω we get:

  Vout =Vin680/470 + 680 = 2.95V

This 2.95V is then safe to connect to the Pi.

A program to interface to the HC-SR04 directly edit

In the tutorial, we don't talk directly to the HC-SR04 but leave all that to the DistanceSensor functionality of the GPIO Zero library. This is convenient, but sometimes it's useful to know exactly what is going on and to be able to understand how to control devices directly.

In the ~/python/distance folder you will find a file rpigpio_DistanceSensor.py which shows how to trigger and read the HC-SR04 sensor directly and which uses the RPi.GPIO library.

Here is the important bit that triggers the HC-SR04 to take a measurement and gets the result back, it's all contained in a function called measure():

def measure():
   # This function measures a distance
   GPIO.output(GPIO_TRIGGER, True)
   time.sleep(0.00001)
   GPIO.output(GPIO_TRIGGER, False)
 
   start = time.time()
   while GPIO.input(GPIO_ECHO)==0:
      start = time.time()
    
   while GPIO.input(GPIO_ECHO)==1:
      stop = time.time()
 
   elapsed = stop-start
   distance = (elapsed * 34300)/2
    
   return distance

 

What does this do? edit

Setting the GPIO_TRIGGER to True, sleeping for 10uS then setting the GPIO_TRIGGER to False triggers the sensor to take a reading.

The code then sets a variable called start to the current time and goes into a tight loop while GPIO_ECHO is 0 (= low), every time round the loop it resets the variable start.

Once GPIO_ECHO is 1 (= high) this first look will stop. At this point start contains the time when the Echo pin went high.

Now the code goes into a tight loop while GPIO_ECHO is high, every time round the loop setting the variable stop.

Finally, when the GPIO_ECHO goes low again the loop exits. At this point, start contains the time when the Echo went high and stop the time when it went low again.

Now it's just some maths to work out how long the Echo pin was high for, multiply this by the speed of sound (the 34300) and divide by 2 (because the sound from the sensor went out and back) and we get the distance value in centimetres (cm).

Files edit

Distancesensor-gpiozero.pdf edit

The original PDF for this tutorial is on Wikicommons: Distancesensor-gpiozero.pdf

Distancesensor-gpiozero-additional.pdf edit

The original PDF for this tutorial is on Wikicommons: Distancesensor-gpiozero-additional.pdf

rpigpio_DistanceSensor.py edit

#!/usr/bin/python

import time
import RPi.GPIO as GPIO

# -----------------------
# Define measure function
# -----------------------
def measure():
  # This function measures a distance
  GPIO.output(GPIO_TRIGGER, True)
  time.sleep(0.00001)
  GPIO.output(GPIO_TRIGGER, False)
  start = time.time()

  while GPIO.input(GPIO_ECHO)==0:
    start = time.time()

  while GPIO.input(GPIO_ECHO)==1:
    stop = time.time()

  elapsed = stop-start
  distance = (elapsed * 34300)/2

  return distance

# -----------------------
# Main Script
# -----------------------

# Use BCM GPIO references instead of physical pin numbers
GPIO.setmode(GPIO.BCM)

# Define GPIO to use on Pi
GPIO_TRIGGER = 20
GPIO_ECHO    = 21

# Set trigger as output and echo as input
GPIO.setup(GPIO_TRIGGER,GPIO.OUT)  # Trigger
GPIO.setup(GPIO_ECHO,GPIO.IN)      # Echo

# Set trigger to False (Low)
GPIO.output(GPIO_TRIGGER, False)

# Wrap main content in a try block so we can
# catch the user pressing CTRL-C and call the
# GPIO cleanup function.
try:
  while True:
    distance = measure()
    print("Distance : %.1f" % distance)
    time.sleep(1)

except KeyboardInterrupt:
  # User pressed CTRL-C, reset GPIO settings
  GPIO.cleanup()

gpiozero_DistanceSensor.py edit

#!/usr/bin/python

# -----------------------
# Import required Python libraries
# -----------------------
from gpiozero import DistanceSensor
from time import sleep

# -----------------------
# Main Script
# -----------------------

sensor = DistanceSensor(echo=21, trigger=20, max_distance=2.0)

while True:

  distance = sensor.distance * 100
  print("Distance : %.1f" % distance)
  sleep(1)