GPIO

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

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.

, , , , , , ,


17 Responses

  1. sudeep says:

    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()
  2. Prafnik says:

    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

  3. Alejandra says:

    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

  4. Nick says:

    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.

  5. Roger says:

    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?

    • Felix says:

      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()
  6. Drew says:

    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?

    • Felix says:

      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.

    • Drew says:

      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!

    • Felix says:

      You’re welcome 🙂

  7. Teddy says:

    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.

    • Felix says:

      50cm is not that much. In my experiments it worked to about 2-3m.

    • Teddy says:

      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.

Leave a Reply

Your email address will not be published. Required fields are marked *