Using a Raspberry Pi distance sensor (ultrasonic sensor HC-SR04)

25. January 2017
5576 Views
30 Comments

For many (outdoor) projects a distance measurement is necessary or advantageous. These small modules are available starting at 1-2 bucks and can measure the distance up to 4-5 meters by ultrasound and are suprisingly accurate. This tutorial shows the connection and control.

 

Hardware

  • HC-SR04 Module (US / UK)
  • Resistors: 330Ω and 470Ω (US / UK)
  • Jumper wire (US / UK)

Wiring

There are four pins on the ultrasound module that are connected to the Raspberry:

  • VCC to Pin 2 (VCC)
  • GND to Pin 6 (GND)
  • TRIG to Pin 12 (GPIO18)
  • connect the 330Ω resistor to ECHO.  On its end you connect it to Pin 18 (GPIO24) and through a 470Ω resistor you connect it also to Pin6 (GND).

We do this because the GPIO pins only tolerate maximal 3.3V. The connection to GND is to have a obvious signal on GPIO24. If no pulse is sent, the signal is 0 (through the connection with GND), else it is 1. If there would be no connection to GND, the input would be undefined if no signal is sent (randomly 0 or 1), so ambiguous.

Here is the structure as a circuit diagram:

ultraschall_Steckplatine
 

Script for controlling

First of all, the Python GPIO library should be installed

To use the module, we create a new script

sudo nano ultrasonic_distance.py

with the following content:

After that we run:

sudo python ultrasonic_distance.py

So every second, the distance will be measured until the script is cancelled by pressing CTRL + C.
That‘s it. You can use it many fields, but who still want to measure larger distances would have to rely on laser measuring devices, which, however, are much more expensive.

30 Responses

  1. i am using 4 US-sensors to connect to my PI2….but i am getting variable readings from last 3 sensors….whereas 1st sensor is working fine….when i run each sensor individually it is working…with the same connection….so i think there might be mistake in python script can anyone please let me know what the mistake is?

    I have attached my code here:

    import RPi.GPIO as GPIO
    import time
    
    #GPIO Mode (BOARD / BCM)
    GPIO.setmode(GPIO.BCM)
    GPIO.setwarnings(False)
    
    #set GPIO Pins
    GPIO_TRIGGER1 = 23
    GPIO_ECHO1 = 24
    
    GPIO_TRIGGER2 = 4
    GPIO_ECHO2 = 17
    
    GPIO_TRIGGER3 = 27
    GPIO_ECHO3 = 22
    
    #GPIO_TRIGGER4 = 5
    #GPIO_ECHO4 = 6
    
    #set GPIO direction (IN / OUT)
    GPIO.setup(GPIO_TRIGGER1, GPIO.OUT)
    GPIO.setup(GPIO_ECHO1, GPIO.IN)
    
    GPIO.setup(GPIO_TRIGGER2, GPIO.OUT)
    GPIO.setup(GPIO_ECHO2, GPIO.IN)
    
    GPIO.setup(GPIO_TRIGGER3, GPIO.OUT)
    GPIO.setup(GPIO_ECHO3, GPIO.IN)
    
    #GPIO.setup(GPIO_TRIGGER4, GPIO.OUT)
    #GPIO.setup(GPIO_ECHO4, GPIO.IN)
    
    def distance():
    	# set Trigger to HIGH
    	GPIO.output(GPIO_TRIGGER1, True)
    	GPIO.output(GPIO_TRIGGER2, True)
    	GPIO.output(GPIO_TRIGGER3, True)
    	#GPIO.output(GPIO_TRIGGER4, True)
    
    	# set Trigger after 0.01ms to LOW
    	time.sleep(0.5)
    	GPIO.output(GPIO_TRIGGER1, False)
    	GPIO.output(GPIO_TRIGGER2, False)
        	GPIO.output(GPIO_TRIGGER3, False)
       	#GPIO.output(GPIO_TRIGGER4, False)
    
    
    
    	StartTime1 = time.time()
    	StopTime1 = time.time()
    
    	StartTime2 = time.time()
    	StopTime2 = time.time()
    
    	StartTime3 = time.time()
    	StopTime3 = time.time()
    
    	#StartTime4 = time.time()
    	#StopTime4 = time.time()
    
    	# save StartTime
    	while GPIO.input(GPIO_ECHO1) == 0:
    		StartTime1 = time.time()
    		#print ("Start ",StartTime)
    
    	while GPIO.input(GPIO_ECHO2) == 0:
    		StartTime2 = time.time()
    		#print ("Start ",StartTime)
    
    	while GPIO.input(GPIO_ECHO3) == 0:
    		StartTime2 = time.time()
    		#print ("Start ",StartTime)
    
    	#while GPIO.input(GPIO_ECHO4) == 0:
    		#StartTime2 = time.time()
    		#print ("Start ",StartTime)
    
    	# save time of arrival
    	while GPIO.input(GPIO_ECHO1) == 1:
    		StopTime1 = time.time()
    		#print ("Stop ",StopTime)
    
    	while GPIO.input(GPIO_ECHO2) == 1:
    		StopTime2 = time.time()
    		#print ("Stop ",StopTime)
    
            while GPIO.input(GPIO_ECHO3) == 1:
    		StopTime1 = time.time()
    		#print ("Stop ",StopTime)
    
    	#while GPIO.input(GPIO_ECHO4) == 1:
    	#	StopTime1 = time.time()
    		#print ("Stop ",StopTime)
    
    	# time difference between start and arrival
    	TimeElapsed1 = StopTime1 - StartTime1
    	TimeElapsed2 = StopTime2 - StartTime2
    	TimeElapsed3 = StopTime3 - StartTime3
    	#TimeElapsed4 = StopTime4 - StartTime4
    	# multiply with the sonic speed (34300 cm/s)
    	# and divide by 2, because there and back
    	distance1 = (TimeElapsed1 * 34300) / 2
    	distance2 = (TimeElapsed2 * 34300) / 2
    	distance3 = (TimeElapsed3 * 34300) / 2
    	#distance4 = (TimeElapsed4 * 34300) / 2
    
    	print ("Measured Distance 1 = %.1f cm" % distance1)
    	print ("Measured Distance 2 = %.1f cm" % distance2)
    	print ("Measured Distance 3 = %.1f cm" % distance3)
    	#print ("Measured Distance 1 = %.1f cm\n\n" % distance4)
    
    	return
    
    if __name__ == '__main__':
    	try:
    		while True:
    			dist = distance()
    			#print ("Measured Distance = %.1f cm" % dist)
    			time.sleep(1)
    
    		# Reset by pressing CTRL + C
    	except KeyboardInterrupt:
    		print("Measurement stopped by User")
    		GPIO.cleanup()
    Reply
  2. It’s probably a runtime issue..you are waiting on a response of sensors 1:
    # save time of arrival
    while GPIO.input(GPIO_ECHO1) == 1:
    StopTime1 = time.time()
    #print (“Stop “,StopTime)

    …and after that you process the other responses…so basically its “time = processing and wait time for sensor echo 1 + response time echo 2″…

    You should use threads for every sensor.

    Regards, Prafnik

    Reply
  3. Hi I was trying this project out but when I run it, it says RuntimeWarning: This channel is already in use, continuing anyway. Use GPIO.setwarnings(FALSE) to disable warnings.

    Please I need help ASAP! Thanks

    Reply
  4. Would you mind explaining how to use a multimeter to ensure that the voltage coming out of the voltage splitter to the GPIO 24 pin is actually being dropped to ~3.3v? I’m still learning this stuff and that one piece of information is eluding me.

    Reply
  5. I am wanting to use the Ultrasonic sensor to let me know when a container only has 1/4 of a bulk solid in it. can someone point me in the right direction to wright a code for this?

    Reply
    • Hi Roger,
      the code is not tested, but I think you’ll get the point:

      ... # import, distance, etc
      if __name__ == '__main__':
          dist_full = 30 # e.g. the sensor is 30cm above the container
          dist_half = 70
          dist_1_4  = 100
          try:
              while True:
                  dist = distance()
                  if dist > dist_1_4:
                      # less than 1/4 of the container is filled
                      # do sth.
                      print("less than 1/4 is filled")
                  time.sleep(60) # wait 60 seconds
                  # Reset by pressing CTRL + C
          except KeyboardInterrupt:
              print("Measurement stopped by User")
              GPIO.cleanup()
      Reply
  6. Thank you for this nice article.
    I’m beginning with embedded and I have two questions.
    1)
    Can a pull_up_down=GPIO.PUD_DOWN on GPIO_ECHO be enough so we don’t have to wire the Echo to the ground?
    2)
    I have a “8-Way level conversion board – TXS0108E” from AdaFruit and I’m wondering if that could totally replace the the voltage divider you made with the resistors? If that the case I’m not too sure of the TXS0108E wiring. I’ve connected Rpi 3.3v pin to VA and the Rpi 5v pin to VB as well as to the level conversion board VCC. Then, Trig is directly wired from the Rpi to the “HC-SR04”. And finally, the “HC-SR04” Echo hopefully gets converted from 5v to 3.3v through the TXS0108E board. I do not yet have a scope to verify everything is setup properly. Do you have any hint?

    Reply
    • Hi Drew,
      you have to connect ECHO also to ground, because if there is no voltage, the input is not defined (displays randomly high/low).
      I have not worked with the TXS0108E, but as written in the datasheet, it is just a voltage translator. The resitors are just use to have always an accurate measurement. You can check this by reading an Input GPIO when nothing is wired to it. Sometimes it will be one, sometimes zero. On the other hand, using a high resitor connected to ground will ensure that the input is not random.

      Reply
    • Hi FELIX,

      Thank you so much for such a quick reply!
      I did try to read GPIO when nothing is wired and it’s indeed random, except when I use something that seems to be a built-in Rpi pull down feature.
      GPIO.setup(GPIO_ECHO, GPIO.IN, GPIO.PUD_DOWN)

      Thank you again for clarifying!

      Reply
  7. I have set up my HC-SR04 just as you explained and it seems to be working. However, It will only work up to about 50cm. I have tried multiple sensors. Is this a voltage issue? Any ideas?

    I am using gpiozero for the code and a Pi 3.

    Reply
    • Exactly. I can’t figure out why my range is so limited. I suppose I’ll have to just accept these results if I can’t solve this.

      If you have any ideas or advice, I’m all ears.

      Reply
  8. I’m not getting any distance measurements yet, and don’t understand the following code:

        StartTime = time.time()
        StopTime = time.time()
     
        # save StartTime
        while GPIO.input(GPIO_ECHO) == 0:
            StartTime = time.time()
     
        # save time of arrival
        while GPIO.input(GPIO_ECHO) == 1:
            StopTime = time.time()

    It makes more sense to me to write the following:

      # Once the trigger pulse has been transmitted, record the StartTime. 
       StartTime = time.time()
     
        # the echo pin will be low == 0 until the echo is received
        # don't do anything until that happens
        while GPIO.input(GPIO_ECHO) == 0:
            pass
    
        # when the echo is received, save the StopTime
        while GPIO.input(GPIO_ECHO) == 1:
            StopTime = time.time()

    Can you help me understand why my rewrite is wrong? And why it’s necessary to keep updating StartTime = time.time() until GPIO.input(GPIO_ECHO) == 1?
    Thanks very much for your time

    Reply
  9. First of all, thank you for the code. its the most understanding code ever in internet. however i want to integrate buzzer also. i want buzzer to work like if the obstacle is approaching towards device then the buzzer should beep loud. if obstacles is move away the sound of the buzzer should be faded slowly. can anyone please help me with code? thank you

    Reply
  10. # save StartTime
        while GPIO.input(GPIO_ECHO) == 0:
            StartTime = time.time()
     
        # save time of arrival
        while GPIO.input(GPIO_ECHO) == 1:
            StopTime = time.time()

    I am not sure what is happening but the e GPIO.input(GPIO_ECHO) is 1 when it comes in to this section therefor it does not exicute the trigger so it just loops through the stop time section.
    The circuit seems to match

    Thanks in advance for your help.

    Reply
  11. I work with a raspberry B+ and this programm don’t work. Mesurments are false.
    Does the raspberry have an alimentation with micro USB ? or maybe is better with a real
    alimentation ?
    Thanks

    Reply
    • You should power it by a 5V (2A) microUSB power adapter. As there are different versions of the HC-SR04 on the market, you can try a 10k ohm resistor instead of the 430 ohm.

      Reply
      • wesh frere; it doesn’t work with 10 k the voltage will be 4.8 volt. Bye bye raspberry pie b + :(.

      • I only have two types of resistors, one of 10 k and an other of 330 ohm. i will be able to make the build or my will my raspberry burn ?

  12. Thank you for posting this code; I was thinking about using the little ultra-sonic sensor for a way to tell how far to pull into my garage, when my wife parks her car.

    I added an if/then to test for a distance, then another output to power an LED.

    Reply
  13. I was facing to the problem, that my CLI prints all the time a distance of 0.2 or 0.1 cm…. After changing the “set Trigger” from 0.00001 to 0.0000001, it works very well!

    Reply
  14. It keeps saying Traceback (most recent call last):
    File “/home/pi/Finale project.py”, Line 14, in
    NameError: name ‘GPIO_ECHO’ is not defined
    Please help asap!

    Reply
  15. It keeps saying Traceback (most recent call last):
    File “/home/pi/Finale project.py”, Line 14, in
    NameError: name ‘GPIO_ECHO’ is not defined

    Please help asap!

    Reply

Leave a Comment

Your email address will not be published.

Subscribe to Raspberry Pi Tutorials and don't miss any new Tutorial!