Devember 2021[COMPLETED]- ultrasonic garage parking sensor

Full time drug dealer, part-time family network/server admin :rofl:

The problem:

Recently bought a house with a supposed 1 car garage, we lovingly refer to it as our 0.8 car garage as our Camry fits, but just barely. Between the back wall of the garage, a metal post protecting the water heater, and the garage door itself there is little room for error when parking the car. Due to the low margin for error the tried-and-true tennis ball from the ceiling method is not good enough. So Devember to the rescue, and as Ayn Rand said “If it’s worth doing, it’s worth overdoing”.

The Goal:

  • Create an ultrasonic sensor array paired with a LED Matrix display to help guide the driver into the garage confidently and to park without hitting anything.
  • Improve upon my entry level python skills, while also making something functional for my house.

The Plan: Version 1.0

The system is currently planned to be run off of a raspberry pi zero w, with HC-SR04 Ultrasonic Modules for measurements, and an 8x32 led matrix for display.

  • 2 ultrasonic sensors
    • 1 sensor for left and right
    • 1 sensor for depth
  • LED Matrix
    • Arrows for left and right guidance
    • Distance count down with a final “Stop” signal when correct garage depth is reached

Then Plan: Version 2.0

  • Integrate display and sensor activation to the garage door status through home assistant to decrease idle power draw.
  • Add two additional sensors to the garage door opening to help guide the car into the garage as mirror clearance is also tight. (Requested by the wife)
6 Likes

It would be interesting to test the 3mm accuracy claim if you have time. I have one of these sensors but haven’t had time to play with it. Good luck on your project!

“This is the HC-SR04 ultrasonic distance sensor. This economical sensor provides 2cm to 400cm of non-contact measurement functionality with a ranging accuracy that can reach up to 3mm.”

Just a heads up, ultrasonic sensors are very finickey and not fun to work with. I’ve done a lot of working with them. They’re… a challenge…

1 Like

Kindly do elaborate on you experience.

Im curious because i find the project interesting as well.

Thanks for the heads up! I had originally looked into using Lidar sensors, but the range was a bit short on the ones in my budget range. However depending on how challenging the ultrasonics end up that may be the way I end up going.

You might consider Time-of-Flight laser sensors like this one or similar.

One thing i want to point out is that with time the arrows become invisible, our brain get used to it and legit you dont even notice them anymore.

If possible i would put a randomizer for the visual representation to make your brain more alert with the subtle changes.
Also i would focus on maximizing the optimal angle for attack to save the mirrors, im a mirror terrorist, im really good at gaging depht and terrible with wide awareness on my apartment we have a shared garage spot that is awful but i have down to science how to enter it because i really really suck at it, is not even funny.

Hey I’ve used that sensor before, they’re quite decent but the connection is a bit sensitive and the accuracy is around 0.5cm-0.25cm won’t be a problem for your application.
Also it might be cheaper to use microcontroller like esp or arduino, raspberry pi seems to be a bit overkill.

Thanks for the input on the sensors. As for the pi being overkill, it’s just a zero W I had laying arown from a previous project.

Hello world!

I have made progress! was able to get the time of flight sensor working over Ethernet wire which will allow me to more easily place the sensors where they are needed in the garage. this first attempt was not pretty, but was more of a proof of concept over a 10 ft run of cable to make sure everything would run without issue.

Also this last week while at work I ran across a post on the Home Assistant forum where someone integrated a custom RPi python program into their MQTT integration in Home Assistant which i think i should be able to use as a basis to have the sensors only run when the status of the garage door is open in home assistant!

hopefully i will be able to get the led matrix set up next and be able to start working on printing sensors output to the matrix this weekend.



1 Like

So it’s working… kind of. I now have the distance sensor working and am able to display the measured distance on the LCD matrix. I have also been able to make the program loop indefinitely with a fair amount of accuracy, or at least enough consistency to keep my us from hitting anything while trying to park the car.

So what’s next on the to do list? MQTT and Home assistant intigration. As I have been working on getting the sensors to play nice and setting up the display it has become apparent that without an integration to detect an open or closed garage door the whole system would have to be triggered manually. This is a no go as the wife wouldn’t want to take the extra time(and I don’t blame her) to remotely start/stop the program just to park her car.


import board
import neopixel
from adafruit_pixel_framebuf import PixelFramebuffer, VERTICAL
import RPi.GPIO as GPIO
import time

pixel_pin = board.D18
pixel_width = 32
pixel_height = 8 

pixels = neopixel.NeoPixel(
	pixel_pin,
	pixel_width * pixel_height,
	brightness=0.1,
	auto_write=False,
)

pixel_framebuf = PixelFramebuffer(
	pixels,
	32,
	8,
	orientation=VERTICAL,
	rotation=2
)

try:


	GPIO.setmode(GPIO.BCM)

	PIN_TRIGGER = 4
	PIN_ECHO = 17

	GPIO.setup(PIN_TRIGGER, GPIO.OUT)
	GPIO.setup(PIN_ECHO, GPIO.IN)

	GPIO.output(PIN_TRIGGER, GPIO.LOW)

	time.sleep(0.5)

	GPIO.output(PIN_TRIGGER, GPIO.HIGH)

	time.sleep(0.00001)

	GPIO.output(PIN_TRIGGER, GPIO.LOW)

	while GPIO.input(PIN_ECHO)==0:
		pulse_start_time = time.time()
	while GPIO.input(PIN_ECHO)==1:
		pulse_end_time = time.time()

	pulse_duration = pulse_end_time - pulse_start_time

	distance_metric = pulse_duration * 17150

	distance_imperial = round(distance_metric / 2.54 )

	pixel_framebuf.fill(0x000000)
	pixel_framebuf.display()
	
	pixel_framebuf.text(str(distance_imperial), 10, 1, 0x00FF00)
	pixel_framebuf.display()

	time.sleep(2)

finally:
	print("exit")
	pixel_framebuf.fill(0x000000)
	pixel_framebuf.display()
	GPIO.cleanup()

Got any more pictures? This is cool

You can keep the sensors running all the time, maybe turn off the neopixels if there’s no change in distance for 2minutes (think kitchen scales).

1 Like

Looking forward to seeing what comes of this project. There’s a lot of things that can be done with proximity/distance sensing.

Though if anyone needs a quick low tech solution to “how far do I pull into the garage”, my grandfather, ever the genius, hung a chunk of foam with a rope from the ceiling so my grandmother would know she’s in far enough when it touches the windshield.

1 Like

Almost finished! Everything works great as far as pulling into the garage and stopping everything via MQTT and home assistant. However, having everything shut down after opening the garage and pulling the car out is causing an issue. I’ve been going at this for the better part of 5 hours at this point so I’m calling it a day and will take another crack at it this weekend.

Also as requested I have attached a few pictures of my demo set up in my office. I’m using cat 6 to connect to the US sensors as an easy way to run the cabling where ever I need and then just connecting the main board and sensor ends via female couplers. Also before I do the final install I’ll be redoing a lot of the wiring to make sure its secure and not likely to get dissconnected down the line.



#!/usr/bin/python3.9

import board
import neopixel
from adafruit_pixel_framebuf import PixelFramebuffer, VERTICAL
import RPi.GPIO as GPIO
import time
import paho.mqtt.client as mqtt
import random

# 8x32 led display innitialization 

pixel_pin = board.D18
pixel_width = 32
pixel_height = 8 

pixels = neopixel.NeoPixel(
	pixel_pin,
	pixel_width * pixel_height,
	brightness=0.1,
	auto_write=False,
)

pixel_framebuf = PixelFramebuffer(
	pixels,
	32,
	8,
	orientation=VERTICAL,
	rotation=2
)

def start_parking ():
	while True :
	
		GPIO.setmode(GPIO.BCM)

		PIN_TRIGGER = 4
		PIN_ECHO = 17

		GPIO.setup(PIN_TRIGGER, GPIO.OUT)
		GPIO.setup(PIN_ECHO, GPIO.IN)

		GPIO.output(PIN_TRIGGER, GPIO.LOW)

		time.sleep(0.5)

		GPIO.output(PIN_TRIGGER, GPIO.HIGH)

		time.sleep(0.00001)

		GPIO.output(PIN_TRIGGER, GPIO.LOW)

		while GPIO.input(PIN_ECHO)==0:
			pulse_start_time = time.time()
		while GPIO.input(PIN_ECHO)==1:
			pulse_end_time = time.time()

		pulse_duration = pulse_end_time - pulse_start_time

		distance_metric = pulse_duration * 17150

		distance_imperial = round(distance_metric / 2.54 )

		pixel_framebuf.fill(0x000000)
		pixel_framebuf.display()
	
		pixel_framebuf.text(str(distance_imperial), 10, 1, 0x00FF00)
		pixel_framebuf.display()

		if distance_imperial <=2:
			break

def stop_parking ():

	pixel_framebuf.fill(0x000000)
	pixel_framebuf.display()

def on_connect(client, userdata, flags, rc):
  print("Connected to MQTT broker")

def on_message(client, userdata, msg):
    print(msg.topic+" "+str(msg.qos)+" "+str(msg.payload))
    if msg.payload.decode() == "ON":
        print("Calling script to Start Parking...")
        start_parking()
    elif msg.payload.decode() == "OFF":
        print("Calling script to Stop Parking...")
        stop_parking()

def on_subscribe(client, userdata, mid, granted_qos):
    print("Subscribed to topic : " + str(mid) +" with QoS" + str(granted_qos))


client = mqtt.Client()

client.username_pw_set( "username" , "pw" )

client.connect( "ip", 1883, 60)

client.subscribe( "/parking/python" , qos=1)

client.on_connect = on_connect

client.on_message = on_message

client.loop_forever()

2 Likes

Success!!! All it needed was an additional IF statement and a loop count function and everything is good to go!

All that is left is to get it installed in the garage and adjust the start and stop distances to match what is actually needed based on where it’s placed! I’ll make sure to post pictures with the final install after its done this week!

def start_parking ():
	    count = 0
         while True :
	
		GPIO.setmode(GPIO.BCM)

		PIN_TRIGGER = 4
		PIN_ECHO = 17

		GPIO.setup(PIN_TRIGGER, GPIO.OUT)
		GPIO.setup(PIN_ECHO, GPIO.IN)

		GPIO.output(PIN_TRIGGER, GPIO.LOW)

		time.sleep(0.5)

		GPIO.output(PIN_TRIGGER, GPIO.HIGH)

		time.sleep(0.00001)

		GPIO.output(PIN_TRIGGER, GPIO.LOW)

		while GPIO.input(PIN_ECHO)==0:
			pulse_start_time = time.time()
		while GPIO.input(PIN_ECHO)==1:
			pulse_end_time = time.time()

		pulse_duration = pulse_end_time - pulse_start_time

		distance_metric = pulse_duration * 17150

		distance_imperial = round(distance_metric / 2.54 )

		pixel_framebuf.fill(0x000000)
		pixel_framebuf.display()
	
		pixel_framebuf.text(str(distance_imperial), 10, 1, 0x00FF00)
		pixel_framebuf.display()

		if distance_imperial <=2:
			break

        if distance_imperial >=50  and count >=60:
            break
4 Likes

I promise it is really done this time…Almost (just need to mount everything in the garage). I forgot about the second sensor, but I was able to get it figured out and adjusted the display outputs to account for the additional information for lateral guidance.

Lets take a look at my original plan to double check everything was achieved.

~ ~ ~ ~ ~ ~

Plan 1.0

The system is currently planned to be run off of a raspberry pi zero w, with HC-SR04 Ultrasonic Modules for measurements, and an 8x32 led matrix for display.

  • 2 ultrasonic sensors
    • 1 sensor for left and right
    • 1 sensor for depth
  • LED Matrix
    • Arrows for left and right guidance
    • Distance count down with a final “Stop” signal when correct garage depth is reached

Plan 2.0

  • Integrate display and sensor activation to the garage door status through home assistant to decrease idle power draw.
  • Add two additional sensors to the garage door opening to help guide the car into the garage as mirror clearance is also tight. (Requested by the wife)

~ ~ ~ ~

Looking at the original goals, all of plan 1.0 was achieved! Additionally I added vertical bars that will display when the car is at an acceptable lateral position in addition to the left and right arrows for guidance.

As for plan 2.0 I ended up with 1 of 2. Getting the program working with home assistant was a bit of a challenge as it was way beyond anything I had done previously, both with python and home assistant integration. As for the 2 additional sensors, this may be something that gets added in the future. However, for right now its more complicated from a wiring stand point than I want to deal with. This was my first real project using the GPIO pins on the raspberry pi and as far as I am aware there are only two 5v pins. Any guidance on the ability to split these out for additional sensors would be appreciated.

Final Thoughts

Up to this point my only coding experience has been structured python and SQL lessons on codecademy, and some VBA scripting in excel for an pharmacist intern project 6 years ago. This really pushed me beyond my previous abilities and increased my knowledge a ton, and I had a great time doing it as well.

Were I to start over from scratch I definitely would have included more notes in my code as I went to help to make any major adjustments, though I may still go back and do that so I don’t curse myself in the future.

The most difficult part of the whole process for me was getting the display working. This had nothing to do with how difficult it actually was to use the display, but more to do with the fact that 99% of the projects out there were for Arduino boards not the pi, and my lack of experience with python. However, after lots of googling, reformatting the pi’s SD card, and LOTS of trial and error it turns out that it isn’t actually that hard once you know what you need.

Finally the thing I’m most pleased with was the idea I had to use Ethernet to connect everything to the pi. The benefits of the standard rj45 plugs not only allow for easy positioning of sensors without having to worry about moving 10’s of feet of wiring attached to the end of them, but it also will let me more easily place the pi in a more secure location than the underside of my garage shelves.

Pictures of the final install to follow tomorrow afternoon!

#!/usr/bin/python3.9

import board
import neopixel
from adafruit_pixel_framebuf import PixelFramebuffer, VERTICAL
import RPi.GPIO as GPIO
import time
import paho.mqtt.client as mqtt
import random

# 8x32 led display innitialization 

pixel_pin = board.D18
pixel_width = 32
pixel_height = 8 

pixels = neopixel.NeoPixel(
	pixel_pin,
	pixel_width * pixel_height,
	brightness=0.1,
	auto_write=False,
)

pixel_framebuf = PixelFramebuffer(
	pixels,
	32,
	8,
	orientation=VERTICAL,
	rotation=2
)

def start_parking ():
	count = 0
	while True :
	
		GPIO.setmode(GPIO.BCM)

		PIN_TRIGGER = 4
		PIN_ECHO = 17

		PIN_TRIGGER_2 = 3
		PIN_ECHO_2 = 23

		GPIO.setup(PIN_TRIGGER, GPIO.OUT)
		GPIO.setup(PIN_ECHO, GPIO.IN)

		GPIO.output(PIN_TRIGGER, GPIO.LOW)

		time.sleep(0.5)

		GPIO.output(PIN_TRIGGER, GPIO.HIGH)

		time.sleep(0.00001)

		GPIO.output(PIN_TRIGGER, GPIO.LOW)

		while GPIO.input(PIN_ECHO)==0:
			pulse_start_time = time.time()
		while GPIO.input(PIN_ECHO)==1:
			pulse_end_time = time.time()

		GPIO.setup(PIN_TRIGGER_2, GPIO.OUT)
		GPIO.setup(PIN_ECHO_2, GPIO.IN)

		time.sleep(0.5)

		GPIO.output(PIN_TRIGGER_2, GPIO.HIGH)

		time.sleep(0.00001)

		GPIO.output(PIN_TRIGGER_2, GPIO.LOW)

		while GPIO.input(PIN_ECHO_2)==0:
			side_pulse_start_time  = time.time()
		while GPIO.input(PIN_ECHO_2)==1:
			side_pulse_end_time = time.time()

		pulse_duration = pulse_end_time - pulse_start_time

		distance_metric = pulse_duration * 17150

		distance_imperial = round(distance_metric / 2.54 )

		
		side_pulse_duration = side_pulse_end_time - side_pulse_start_time

		side_distance_metric = side_pulse_duration * 17150

		if 10 < side_distance_metric < 50:
			display = "|%s|"%distance_imperial
		if side_distance_metric < 10:
			display = " %s>"%distance_imperial
		if side_distance_metric > 50:
			display = "<%s "%distance_imperial
 
		pixel_framebuf.fill(0x000000)
		pixel_framebuf.display()
		
		if distance_imperial <= 2:
			pixel_framebuf.text("STOP", 4, 1, 0xFF0000) 	
			pixel_framebuf.display()
		else:
			pixel_framebuf.text(str(display), 4, 1, 0x00FF00)
			pixel_framebuf.display()
		
		count = count + 1

		if distance_imperial <=2:
			break
		
		if distance_imperial >=50 and count >= 10:
			break

def stop_parking ():

	pixel_framebuf.fill(0x000000)
	pixel_framebuf.display()

def on_connect(client, userdata, flags, rc):
  print("Connected to MQTT broker")

def on_message(client, userdata, msg):
    print(msg.topic+" "+str(msg.qos)+" "+str(msg.payload))
    if msg.payload.decode() == "ON":
        print("Calling script to Start Parking...")
        start_parking()
    elif msg.payload.decode() != "ON":
        print("Calling script to Stop Parking...")
        stop_parking()

def on_subscribe(client, userdata, mid, granted_qos):
    print("Subscribed to topic : " + str(mid) +" with QoS" + str(granted_qos))


client = mqtt.Client()

client.username_pw_set( "mqtt-user" , "c0up3rrx!" )

client.connect( "10.0.0.13", 1883, 60)

client.subscribe( "/parking/python" , qos=1)

client.on_connect = on_connect

client.on_message = on_message

client.loop_forever()

3 Likes

Final pictures as promised. Will definitely be looking into designing a 3d printed enclosure for the sensors and the pi to keep them from shifting around or getting bumped. But overall the whole install went smoothly.

The reason for it taking 30 seconds to park had nothing to do with the sensors or program. who would have thought that it would be so difficult to park a car and fill said park job at the same time.





6 Likes

NICE!

I love the big red !STOP!

Really hammers it home.

3 Likes

Great project came out awesome, good job